interface RequestArguments { id?: string | undefined method: string; params?: unknown[] | object; } const listners = { accountsChanged: new Set<(p?: any) => void>(), connect: new Set<(p?: any) => void>(), disconnect: new Set<(p?: any) => void>(), chainChanged: new Set<(p?: any) => void>(), once: { accountsChanged: new Set<(p?: any) => void>(), connect: new Set<(p?: any) => void>(), disconnect: new Set<(p?: any) => void>(), chainChanged: new Set<(p?: any) => void>(), } } const promResolvers = new Map() const getListnersCount = (): number => { let count = 0 for(const key of Object.keys(listners)) { if(key === 'once'){ for(const onceKey of Object.keys(listners[key])) { count += (listners)[key][onceKey]?.length } }else { count += (listners)[key].length } } return count } const sendMessage = (args: RequestArguments, ping = false) => { if(Object.values(promResolvers).filter(r=> r).length < 10 ) { return new Promise((resolve, reject) => { const resId = crypto.randomUUID() promResolvers.set(resId, { resolve, reject }) const data = { type: "CLWALLET_CONTENT", data: args, resId}; if (ping) { data.type = 'CLWALLET_PING' } // console.log('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.")) }) } } class MetaMaskAPI { isMetaMask = true _state = {accounts: Array(1), isConnected: true, isUnlocked: true, initialized: true, isPermanentlyDisconnected: false} _sentWarnings = {enable: false, experimentalMethods: false, send: false, events: {}} // Deprecated - hardcoded for now, websites should not access this directly since is deprecated for a long time chainId = "0x89" // Deprecated - hardcoded for now, websites should not access this directly since is deprecated for a long time networkVersion = "137" selectedAddress = null autoRefreshOnNetworkChange = false // Internal Simulate Metamask _events = {} _eventsCount = 2 _jsonRpcConnection = {} _log = {} _maxListeners= 100 _metamask = new Proxy({ isUnlocked: () => { return Promise.resolve(true) }, requestBatch: () => { // empty }, }, {}) _rpcEngine = { _events: {}, _eventsCount: 0, _maxListeners: undefined, _middleware: Array(4) } isConnected() { return false } // for maximum compatibility since is cloning the same API enable() { return sendMessage({ method: 'eth_requestAccounts', params: Array(0)}) } request(args: RequestArguments): Promise { return sendMessage(args) } // Deprecated sendAsync (arg1: any, arg2: any): void { return this.send(arg1, arg2) as any } // Deprecated send (arg1: unknown, arg2: unknown): unknown { if( typeof arg1 === 'string' ) { return sendMessage({ method: arg1, params: arg2 as object }) } else if (arg2 === undefined) { console.error('Clear Wallet: Sync calling is deprecated and not supported') }else { sendMessage(arg1 as RequestArguments).then(result => { if (typeof arg2 === 'function'){ (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) } ) }) } } on (eventName: string, callback: () => void) { this.addListener(eventName, callback) return this } addListener (eventName: string, callback: () => void) { switch (eventName) { case 'accountsChanged': listners.accountsChanged.add(callback) break case 'connect': listners.connect.add(callback) sendMessage({ method: 'wallet_ready' }, true) break; case 'disconnect': case 'close': listners.disconnect.add(callback) break; // Deprecated - chainIdChanged -networkChanged case 'chainChanged': case 'chainIdChanged': case 'networkChanged': listners.chainChanged.add(callback) break; } return this } once (eventName: string, callback: () => void) { switch (eventName) { case 'accountsChanged': listners.once.accountsChanged.add(callback) break case 'connect': listners.once.connect.add(callback) sendMessage({ method: 'wallet_ready' }, true) break; case 'disconnect': case 'close': listners.once.disconnect.add(callback) break; // Deprecated - chainIdChanged -networkChanged case 'chainChanged': case 'chainIdChanged': case 'networkChanged': listners.once.chainChanged.add(callback) break; } return this } off (eventName: string, callback: () => void) { (this).removeListener(eventName, callback) return this } removeListener (eventName: string, callback: () => void) { switch (eventName) { case 'accountsChanged': listners.accountsChanged.delete(callback) break case 'connect': listners.connect.delete(callback) break; case 'disconnect': case 'close': listners.disconnect.delete(callback) break; // Deprecated - chainIdChanged -networkChanged case 'chainChanged': case 'chainIdChanged': case 'networkChanged': listners.chainChanged.delete(callback) break; default: return } return this } removeAllListeners() { listners.accountsChanged.clear() listners.chainChanged.clear() listners.disconnect.clear() listners.connect.clear() return this } getMaxListeners() { return 100 } _getExperimentalApi () { return this._metamask } eventNames () { return [] } listenerCount () { return getListnersCount() } listners() { return [] } rawListners() { return [] } // Internal Simulate Metamask _warnOfDeprecation() { return true } _rpcRequest() { return true } _handleAccountsChanged() { return true } _handleChainChanged() { return true } _handleConnect() { return true } _handleDisconnect() { return true } _handleStreamDisconnect() { return true } _handleUnlockStateChanged() { return true } _sendSync () { console.error('Clear Wallet: Sync calling is deprecated and not supported') } } const eth = new Proxy( new MetaMaskAPI(), { // set: () => { return true }, // get: function(target, name, receiver) { // if (typeof (target)[name] == 'function') { // return function (...args: any) { // console.dir({ call: [name, ...args] }); // return undefined; // } // } // let check = true // setTimeout(() => check = false, 400) // while(check){ // // igmore // } // }, deleteProperty: () => { return false }, }) const listner = function(event: any) { if (event.source != window) return; if (event.data.type && (event.data.type === "CLWALLET_PAGE")) { try { if(event?.data?.data?.error){ promResolvers.get(event.data.resId)?.reject(event.data.data); console.error(event?.data?.data) }else { promResolvers.get(event.data.resId)?.resolve(event.data.data); } promResolvers.delete(event.data.resId) } catch (e) { console.log('Failed to connect resolve msg', e) } } else if( event.data.type && (event.data.type === "CLWALLET_PAGE_LISTENER")) { if((event?.data?.data?.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; (eth).isConnected = () => true; } else if( listnerName === 'chainChanged' ) { console.log(event?.data?.data?.data); (eth).networkVersion = event?.data?.data?.data.toString(10) ?? '137'; (eth).chainId = event?.data?.data?.data ?? '0x89'; } else if ( listnerName === 'accountsChanged' ) { (eth).selectedAddress = event?.data?.data?.data?.address ?? 'dummy-string'; } listners[listnerName].forEach(listner => listner(event?.data?.data?.data)); listners.once[listnerName].forEach(listner => { listner(event?.data?.data?.data) listners.once[listnerName].delete(listner) }); } catch (e) { console.error(e) // ignore } } } } window.addEventListener("message",listner) const proxy1 = new Proxy({ // on: (event: any, callback:any) => { if (event === 'message') { // debugger; // callback(true, true) // } } }, { get: function(target, name, receiver) { if (typeof (target)[name] == 'function') { return function (...args: any) { console.dir({ call: [name, ...args] }); } } console.log('ETH', name.toString() , target, receiver); return undefined } }) const proxy2 = new Proxy({ // on: (event: any, callback:any) => { if (event === 'message') { // debugger; // callback(true, true) // } } }, { get: function(target, name, receiver) { if (typeof (target)[name] == 'function') { return function (...args: any) { console.dir({ call: [name, ...args] }); } } console.log('web3', name.toString() , target, receiver); return undefined } }) const web3Shim = { currentProvider: eth, __isMetaMaskShim__: true } const injectWallet = (win: any) => { Object.defineProperty(win, 'ethereum', { value: eth, }); Object.defineProperty(win, 'web3', { value: web3Shim }); sendMessage({ method: 'wallet_ready' }, true) console.log('Clear wallet injected', (window as any).ethereum, win) } injectWallet(this); // setTimeout(() => { // // console.log('Metamask clone test'); // // (window).ethereum.request({method: 'eth_requestAccounts', params: Array(0)}).then((res: any) => { console.log(res, '111111111')}); // // (window).ethereum.request({method: 'eth_accounts', params: Array(0)}).then((res: any) => { console.log(res, '111111111')}); // // (window).ethereum.request({method: 'eth_chainId', params: Array(0)}).then((res: any) => { console.log(res, '111111111')}); // // (window).ethereum.request({method: 'wallet_requestPermissions', params: [{eth_accounts: {}}]}).then((res: any) => { console.log(res, '111111111')}); // // (window).ethereum.request({method: 'net_version', params: []}).then((res: any) => { console.log(res, '111111111')}); // // (window).ethereum.request({method: 'wallet_switchEthereumChain', params: [{chainId: "0x89"}]}).then((res: any) => { console.log(res, '111111111')}); // // (window).ethereum2.request({method: 'wallet_switchEthereumChain', params: [{chainId: "0x89"}]}).then((res: any) => { console.log(res, '111111111')}); // // (window).ethereum.on('connect', ((a: any, b: any) => console.log('connect', a, b))); // // (window).ethereum.on('accountsChanged', ((a: any, b: any) => console.log('accountsChanged', a, b))); // // (window).ethereum.on('chainChanged', ((a: any) => console.log('chainChanged', a, typeof a))); // // console.log((window).ethereum.on('message', (a: any, b:any) => console.log(a,b))) // console.log((window).ethereum.toString()) // console.log((window).ethereum2.toString()) // console.log((window).ethereum.Symbold) // }, 3500) // console.log( (window as any).ethereum.request({method: 'eth_chainId'}))