diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e611d3..4743bee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## Manifest Version 1.3.4 + +- bump fake Metamask version signature to 11.0.0 +- improved compatibility with older deprecated websites +- improved mimicking of Metamask API +- made the wallet compatible with fire extension on sending transaction( by mimicking new Metamask API) + ## Manifest Version 1.3.3 - improved eth_call and eth_blockNumber to be more compatible with older websites diff --git a/src/extension/content.ts b/src/extension/content.ts index e9a22ba..e541826 100644 --- a/src/extension/content.ts +++ b/src/extension/content.ts @@ -20,10 +20,13 @@ const allowedMethods = { 'eth_chainId': true, 'personal_sign': true, 'wallet_requestPermissions': true, + 'wallet_registerOnboarding': true, + 'wallet_revokePermissions': true, 'eth_gasPrice': true, 'eth_getBlockByNumber': true, 'eth_blockNumber': true, 'eth_estimateGas': true, + 'eth_syncing': true, 'eth_sign': true, 'net_version': true, 'eth_sendTransaction': true, @@ -53,30 +56,29 @@ window.addEventListener("message", (event) => { if (event.source != window) return; if (event?.data?.type === "CLWALLET_CONTENT") { - event.data.data.resId = event.data.resId - event.data.data.type = "CLWALLET_CONTENT_MSG" - event.data.data.website = document?.location?.href ?? '' - if ((event?.data?.data?.method ?? 'x') in allowedMethods) { - chrome.runtime.sendMessage(event.data.data, (res) => { + event.data.data.data.resId = event.data.resId + event.data.data.data.type = "CLWALLET_CONTENT_MSG" + event.data.data.data.website = document?.location?.href ?? '' + if ((event?.data?.data?.data?.method ?? 'x') in allowedMethods) { + chrome?.runtime?.sendMessage(event.data.data.data, (res) => { if (chrome.runtime.lastError) { console.warn("LOC1: Error sending message:", chrome.runtime.lastError); } - const data = { type: "CLWALLET_PAGE", data: res, resId: event.data.resId }; // console.info('data out', data) window.postMessage(data, "*"); }) } else { - const data = { type: "CLWALLET_PAGE", data: { error: true, message: 'ClearWallet: Unknown method requested ' + event?.data?.data?.method ?? '' }, resId: event.data.resId }; + const data = { type: "CLWALLET_PAGE", data: { error: true, message: 'ClearWallet: Unknown method requested ' + event?.data?.data?.data?.method ?? '' }, resId: event.data.resId }; window.postMessage(data, "*"); } } else if (event?.data?.type === "CLWALLET_PING") { - event.data.data.resId = event.data.resId - event.data.data.type = "CLWALLET_CONTENT_MSG" - event.data.data.method = "wallet_connect" - event.data.data.params = Array(0) - chrome.runtime.sendMessage(event.data.data, async (res) => { + event.data.data.data.resId = event.data.resId + event.data.data.data.type = "CLWALLET_CONTENT_MSG" + event.data.data.data.method = "wallet_connect" + event.data.data.data.params = Array(0) + chrome.runtime.sendMessage(event.data.data.data, async (res) => { if (chrome.runtime.lastError) { console.warn("LOC2: Error sending message:", chrome.runtime.lastError); } diff --git a/src/extension/inject.ts b/src/extension/inject.ts index f120ee8..0405ef9 100644 --- a/src/extension/inject.ts +++ b/src/extension/inject.ts @@ -69,18 +69,25 @@ const getListnersCount = (): number => { return count } -const sendMessage = (args: RequestArguments, ping = false) => { +const sendMessage = (args: RequestArguments, ping = false, from = 'request'): Promise => { if(Object.values(promResolvers).filter(r=> r).length < 10 ) { return new Promise((resolve, reject) => { const resId = [...`${Math.random().toString(16) + Date.now().toString(16)}`].slice(2).join('') promResolvers.set(resId, { resolve, reject }) - const data = { type: "CLWALLET_CONTENT", data: args, resId}; + const data = { + type: "CLWALLET_CONTENT", + data: { data: args, name: 'metamask-provider' }, + resId, + from, + target: 'metamask-contentscript' + } if (ping) { data.type = 'CLWALLET_PING' } // console.info('data in', data) window.postMessage(data, "*"); - }) + + }) } else { return new Promise((resolve, reject) => { reject(new Error("You have reached the maximum number of concurent wallet messeges.")) @@ -125,25 +132,26 @@ class MetaMaskAPI { } request(args: RequestArguments): Promise { - return sendMessage(args) + return sendMessage(args) as Promise } // Deprecated sendAsync (arg1: any, arg2: any): void | Promise { + // return this.send(arg1, arg2) as any if( typeof arg1 === 'string' ) { return sendMessage({ method: arg1, params: arg2 as object - }) + }, false , 'sendAsync') as Promise }else if (typeof arg2 === 'function'){ - sendMessage(arg1 as RequestArguments).then(result => { + ((sendMessage(arg1 as RequestArguments, false, 'sendAsync') as Promise).then(result => { (arg2 as (e?: any, r?: any) => any )(undefined, { id: (arg1 as RequestArguments)?.id, jsonrpc: '2.0', method: (arg1 as RequestArguments).method, result } - ) - }).catch( e => { + ) + }) as Promise).catch( e => { (arg2 as (er?: any, r?: any) => any )(new Error(e), { id: (arg1 as RequestArguments)?.id, jsonrpc: '2.0', @@ -152,45 +160,83 @@ class MetaMaskAPI { } ) }) - } + } else { + return sendMessage(arg1 as RequestArguments, false, 'sendAsync') as Promise + } } // Deprecated + // send (arg1: unknown, arg2: unknown): unknown { + // if (arg2 === undefined) { + // if( typeof arg1 === 'string' ) { + // return sendMessage({ + // method: arg1, + // params: undefined + // }) + // } else if (typeof arg1 === 'object') { + // return sendMessage(arg1 as RequestArguments) + // } else { + // console.error('ERROR: Clear Wallet: faulty request') + // } + // }else if( typeof arg1 === 'string' ) { + // return sendMessage({ + // method: arg1, + // params: arg2 as object + // }) + // }else if (typeof arg2 === 'function'){ + // sendMessage(arg1 as RequestArguments).then(result => { + // (arg2 as (e?: any, r?: any) => any )(undefined, { + // id: (arg1 as RequestArguments)?.id, + // jsonrpc: '2.0', + // method: (arg1 as RequestArguments).method, + // result + // } + // ) + // }).catch( e => { + // (arg2 as (er?: any, r?: any) => any )(new Error(e), { + // id: (arg1 as RequestArguments)?.id, + // jsonrpc: '2.0', + // method: (arg1 as RequestArguments).method, + // error: new Error(e) + // } + // ) + // }) + // } + // } + send (arg1: unknown, arg2: unknown): unknown { + const resultFmt = async (result: Promise) => { + return { + "id": 0, + "jsonrpc": "2.0", + result: await result + } + } if (arg2 === undefined) { if( typeof arg1 === 'string' ) { - return sendMessage({ + + return resultFmt(sendMessage({ method: arg1, params: undefined - }) - } else if (typeof arg1 === 'object') { - return sendMessage(arg1 as RequestArguments) + }, false, 'send')) } else { - console.error('ERROR: Clear Wallet: faulty request') + return resultFmt(sendMessage(arg1 as RequestArguments, false, 'send')) + } + } else if (typeof arg1 === 'object') { + if( typeof arg1 === 'string' ) { + return resultFmt(sendMessage(arg1 as RequestArguments, false, 'send')) + } else { + return resultFmt(sendMessage(arg1 as RequestArguments, false, 'send')) } }else if( typeof arg1 === 'string' ) { - return sendMessage({ + return resultFmt( sendMessage({ method: arg1, params: arg2 as object - }) + }, false, 'send')) }else if (typeof arg2 === 'function'){ - sendMessage(arg1 as RequestArguments).then(result => { - (arg2 as (e?: any, r?: any) => any )(undefined, { - id: (arg1 as RequestArguments)?.id, - jsonrpc: '2.0', - method: (arg1 as RequestArguments).method, - result - } - ) - }).catch( e => { - (arg2 as (er?: any, r?: any) => any )(new Error(e), { - id: (arg1 as RequestArguments)?.id, - jsonrpc: '2.0', - method: (arg1 as RequestArguments).method, - error: new Error(e) - } - ) - }) - } + return resultFmt( sendMessage(arg1 as RequestArguments, false, 'send')) + } else { + return resultFmt(sendMessage(arg1 as RequestArguments , false, 'send')) + } } on (eventName: string, callback: () => void) { this.addListener(eventName, callback) @@ -308,7 +354,7 @@ class MetaMaskAPI { _handleStreamDisconnect() { return true } _handleUnlockStateChanged() { return true } _sendSync () { - console.info('ERROR: Clear Wallet: Sync calling is deprecated and not supported') + console.warn('ERROR: Clear Wallet: Sync calling is deprecated and not supported') } } @@ -318,38 +364,41 @@ const eth = new Proxy( new MetaMaskAPI(), { const listner = function(event: any) { if (event.source != window) return; - - if (event?.data?.type === "CLWALLET_PAGE") { + const eventData = event?.data + const eventDataData = event?.data?.data + const eventDataDataData = event?.data?.data?.data + if (eventData?.type === "CLWALLET_PAGE") { try { - if(event?.data?.data?.error){ - promResolvers.get(event.data.resId)?.reject(event.data.data); + if(eventData?.error){ + promResolvers.get(eventData.resId)?.reject(eventData); // console.info('Error: ', event?.data?.data) }else { - promResolvers.get(event.data.resId)?.resolve(event.data.data); + promResolvers.get(eventData.resId)?.resolve(eventDataData); } promResolvers.delete(event.data.resId) } catch (e) { - // console.log('Failed to connect resolve msg', e) + // console.error('Failed to connect resolve msg', e) } - } else if(event?.data?.type === "CLWALLET_PAGE_LISTENER") { - if((event?.data?.data?.listner ?? 'x') in listners ) { + } else if(eventData?.type === "CLWALLET_PAGE_LISTENER") { + if((eventDataData?.listner ?? 'x') in listners ) { try { - const listnerName = event?.data?.data?.listner as ('accountsChanged' | 'connect' | 'disconnect' | 'chainChanged') - if( listnerName === 'connect' && event?.data?.data?.data) { - (eth).networkVersion = event?.data?.data?.data?.chainId?.toString(10) ?? '137'; - (eth).chainId = event?.data?.data?.data?.chainId ?? '0x89'; - (eth).selectedAddress = event?.data?.data?.address ?? null; + const listnerName = eventDataData.listner as ('accountsChanged' | 'connect' | 'disconnect' | 'chainChanged') + if( listnerName === 'connect' && eventDataData) { + (eth).networkVersion = eventDataDataData?.chainId?.toString(10) ?? '137'; + (eth).chainId = eventDataDataData?.chainId ?? '0x89'; + (eth).selectedAddress = eventDataData?.address?.[0] ?? null; + (eth).accounts = [eventDataData.address?.[0]] ?? []; (eth).isConnected = () => true; } else if( listnerName === 'chainChanged' ) { - // console.info(event?.data?.data?.data); - (eth).networkVersion = event?.data?.data?.data.toString(10) ?? '137'; - (eth).chainId = event?.data?.data?.data ?? '0x89'; + (eth).networkVersion = eventDataData?.toString(10) ?? '137'; + (eth).chainId = eventDataData ?? '0x89'; } else if ( listnerName === 'accountsChanged' ) { - (eth).selectedAddress = event?.data?.data?.data?.address ?? 'dummy-string'; + (eth).accounts = [eventDataData?.[0]] ?? []; + (eth).selectedAddress = eventDataData?.[0] ?? ''; } - listners[listnerName].forEach(listner => listner(event?.data?.data?.data)); + listners[listnerName].forEach(listner => listner(eventDataDataData)); listners.once[listnerName].forEach(listner => { - listner(event?.data?.data?.data) + listner(eventDataData) listners.once[listnerName].delete(listner) }); } catch (e) { @@ -362,37 +411,19 @@ const listner = function(event: any) { window.addEventListener("message",listner) - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const proxy1 = new Proxy(new MetaMaskAPI(), { - get: function (target: any, prop: any) { - // Intercept method calls and log them - if (typeof target[prop] === 'function') { - return function (...args: any[]) { - // console.log(`Calling ${prop} with arguments:`, args); - // eslint-disable-next-line prefer-spread - const result = target[prop].apply(target, args); - // console.log(`${prop} returned:`, result); - return result; - }; - } else { - // console.log(`Reading ${prop}`); - return target[prop]; - } - }, -}) -// const web3Shim = { -// currentProvider: eth, -// __isMetaMaskShim__: true -// } + +const web3Shim = { + currentProvider: eth, + __isMetaMaskShim__: true +} const injectWallet = (win: any) => { Object.defineProperty(win, 'ethereum', { value: eth, }); Object.defineProperty(win, 'web3', { - value: eth + value: web3Shim }); sendMessage({ method: 'wallet_ready' @@ -403,6 +434,29 @@ sendMessage({ injectWallet(this); loadEIP1193Provider(eth) + +// HELPERS TO CLONE METAMASK API + +// window.addEventListener("message" , (event) => { +// console.log('event', event) +// }) + +// setTimeout(() => { +// console.log('Metamask clone test'); +// console.log((window).ethereum.send({ +// "jsonrpc": "2.0", +// "method": "eth_accounts", +// "params": [], +// "id": 0 +// })) +// console.log((window).ethereum.request({ +// "jsonrpc": "2.0", +// "method": "eth_accounts", +// "params": [], +// "id": 0 +// })) +// }, 5000) + // setTimeout(() => { // // console.log('Metamask clone test'); // // (window).ethereum.request({method: 'eth_requestAccounts', params: Array(0)}).then((res: any) => { console.log(res, '111111111')}); diff --git a/src/extension/manifest.json b/src/extension/manifest.json index c859227..fbe0e80 100644 --- a/src/extension/manifest.json +++ b/src/extension/manifest.json @@ -3,8 +3,8 @@ "name": "__MSG_appName__", "description": "__MSG_appDesc__", "default_locale": "en", - "version": "1.3.3", - "version_name": "1.3.3", + "version": "1.3.4", + "version_name": "1.3.4", "icons": { "16": "assets/extension-icon/wallet_16.png", "32": "assets/extension-icon/wallet_32.png", diff --git a/src/extension/serviceWorker.ts b/src/extension/serviceWorker.ts index e76c879..e97fdc0 100644 --- a/src/extension/serviceWorker.ts +++ b/src/extension/serviceWorker.ts @@ -286,7 +286,7 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an } case 'eth_blockNumber': { try { - sendResponse(await getBlockNumber()) + sendResponse(numToHexStr(await getBlockNumber())) } catch (e) { sendResponse({ error: true, @@ -566,7 +566,7 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an break } case 'web3_clientVersion': { - sendResponse("MetaMask/v10.20.0") + sendResponse("MetaMask/v11.0.0") break } case 'wallet_getPermissions': @@ -585,6 +585,18 @@ const mainListner = (message: RequestArguments, sender:any, sendResponse: (a: an }]) break } + case 'wallet_revokePermissions': { + sendResponse(null) + break + } + case 'wallet_registerOnboarding': { + sendResponse(true) + break + } + case 'eth_syncing': { + sendResponse(false) + break + } case 'net_version': { const network = await getSelectedNetwork() const chainId = network?.chainId ?? 0 diff --git a/src/utils/wallet.ts b/src/utils/wallet.ts index 4a640b4..5064f8a 100644 --- a/src/utils/wallet.ts +++ b/src/utils/wallet.ts @@ -81,14 +81,23 @@ export const estimateGas = async ({to = '', from = '', data = '', value = '0x0' } export const evmCall = async (params: any[]) => { - const param1 = (params[0] ?? {to:'', from: '', data:'', value: '0x0', blockTag: 'latest' }) as {to: string, from: string, data: string, value: string, blockTag: string} - const param2 = (params[1] ?? 'latest') as string - param1.blockTag = param2 - - + const tx = {} as {to: string, from: string, data: string, value: string, blockTag: string} + const param1 = params[0] as any + if(param1.to) tx.to = param1.to + if(param1.from) tx.from = param1.from + if(param1.data) tx.data = param1.data + if(param1.value) tx.value = param1.value + const param2 = params[1] as string + if (param2.startsWith('0x')) { + tx.blockTag = param2 + } else { + tx.blockTag = 'latest' + } + const network = await getSelectedNetwork() const provider = new ethers.JsonRpcProvider(network.rpc) - return await provider.call(param1) + const result = await provider.call(tx) + return result } export const getTxByHash = async (hash: string) => { diff --git a/src/views/HomeTab.vue b/src/views/HomeTab.vue index fefc195..b6b70d1 100644 --- a/src/views/HomeTab.vue +++ b/src/views/HomeTab.vue @@ -96,7 +96,7 @@ class="exchange-btn" style="" /> -

+

This button does not contain any referral to maximize privacy.

@@ -330,10 +330,7 @@ export default defineComponent({ accounts.value.splice(0, 0, selectedAccount.value); const newAccounts = [...accounts.value]; await replaceAccounts(newAccounts); - triggerListner( - "accountsChanged", - newAccounts.map((a) => a.address) - ); + triggerListner("accountsChanged", [newAccounts.map((a) => a.address)?.[0]]); } accountsModal.value = false; loading.value = false;