From 2a9600cb9564c9eca80f4dbce945236f966844d5 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Fri, 28 Jun 2019 13:47:35 +0200 Subject: [PATCH 1/6] add networkChanged event to walletconnect --- connections/walletconnect.js | 29 +++++++++---- package-lock.json | 79 +++++++++++++++++++++++++++++------- package.json | 3 +- 3 files changed, 87 insertions(+), 24 deletions(-) diff --git a/connections/walletconnect.js b/connections/walletconnect.js index 26cb6b8..153b523 100644 --- a/connections/walletconnect.js +++ b/connections/walletconnect.js @@ -4,7 +4,7 @@ const dev = process.env.NODE_ENV === 'development' let WalletConnect, WCQRCode class WalletConnectConnection extends EventEmitter { - constructor (_WalletConnect, _WCQRCode, options) { + constructor(_WalletConnect, _WCQRCode, options) { super() WalletConnect = _WalletConnect WCQRCode = _WCQRCode @@ -13,12 +13,12 @@ class WalletConnectConnection extends EventEmitter { this.on('error', () => this.close()) setTimeout(() => this.create(options), 0) } - openQR () { + openQR() { WCQRCode.open(this.wc.uri, () => { this.emit('error', new Error('User close WalletConnect QR Code modal')) }) } - create (options) { + create(options) { if (!WalletConnect) this.emit('error', new Error('WalletConnect not available')) try { @@ -43,6 +43,8 @@ class WalletConnectConnection extends EventEmitter { this.accounts = accounts this.chainId = chainId + this.updateNetworkId(chainId) + // Emit connect event this.emit('connect') }) @@ -61,35 +63,46 @@ class WalletConnectConnection extends EventEmitter { // Check if chainId changed and trigger event if (this.chainId !== chainId) { this.chainId = chainId - this.emit('networkChanged', chainId) + this.emit('chainChanged', chainId) } + + // Check if networkId changed and trigger event + this.updateNetworkId(chainId) + }) this.wc.on('disconnect', (e, payload) => { if (e) return this.emit('error', e) this.onClose() }) } - onClose () { + onClose() { this.wc = null this.closed = true if (dev) console.log('Closing WalletConnector connection') this.emit('close') this.removeAllListeners() } - close () { + close() { if (this.wc) return this.wc.killSession() this.onClose() } - error (payload, message, code = -1) { + error(payload, message, code = -1) { this.emit('payload', { id: payload.id, jsonrpc: payload.jsonrpc, error: { message, code } }) } - send (payload) { + send(payload) { if (this.wc && this.wc.connected) { return this.wc.sendCustomRequest(payload) } else { return this.error(payload, 'Not connected') } } + async updateNetworkId(chainId) { + const networkId = convertChainIdToNetworkId(chainId) + if (this.networkId !== networkId) { + this.networkId = networkId + this.emit('networkChanged', networkId) + } + } } const opts = options => Object.assign({ walletConnectBridge: 'https://bridge.walletconnect.org', walletConnectQR: true }, options) diff --git a/package-lock.json b/package-lock.json index 3f9047a..846d733 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1595,6 +1595,22 @@ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, + "axios": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", + "requires": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" + } + } + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -2836,7 +2852,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -3492,6 +3507,14 @@ "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", "dev": true }, + "evm-chains": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/evm-chains/-/evm-chains-0.1.1.tgz", + "integrity": "sha512-gaB4WfMIKQmzA110WXsk4mKk04iGebR3Wuncd3XkZVSIVWZNoiAXpZPWjBcNf8OxSKFBB77U14QwX+WgM2ND8g==", + "requires": { + "axios": "^0.19.0" + } + }, "evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", @@ -3794,6 +3817,14 @@ "write": "^0.2.1" } }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -3864,7 +3895,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -3885,12 +3917,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3905,17 +3939,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -4032,7 +4069,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -4044,6 +4082,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4058,6 +4097,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4065,12 +4105,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -4089,6 +4131,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -4169,7 +4212,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -4181,6 +4225,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -4266,7 +4311,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -4302,6 +4348,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -4321,6 +4368,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -4364,12 +4412,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -5602,8 +5652,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "mute-stream": { "version": "0.0.7", @@ -8477,7 +8526,7 @@ "integrity": "sha512-VU6/DSUX93d1fCzBz7WP/SGCQizO1rKZi4Px9j/3yRyfssHyFcZamMw2/sj4E8TlfMXONvZLoforR8B4bRoyTQ==", "dev": true, "requires": { - "bignumber.js": "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934", + "bignumber.js": "git+https://github.com/frozeman/bignumber.js-nolookahead.git", "crypto-js": "^3.1.4", "utf8": "^2.1.1", "xhr2-cookies": "^1.1.0", diff --git a/package.json b/package.json index 803f7cc..710ebf6 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "author": { "name": "Jordan Muir", "email": "jordan@frame.sh", - "url": "https//frame.sh" + "url": "https://frame.sh" }, "license": "GPL-3.0", "repository": "github:floating/eth-provider", @@ -26,6 +26,7 @@ "@walletconnect/browser": "^1.0.0-beta.22", "@walletconnect/qrcode-modal": "^1.0.0-beta.22", "ethereum-provider": "0.0.6", + "evm-chains": "^0.1.1", "oboe": "2.1.4", "uuid": "3.3.2", "ws": "6.1.0", From 6e8885511133602235f49abf6901cd1685dc8674 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Fri, 28 Jun 2019 14:41:32 +0200 Subject: [PATCH 2/6] handle read calls with http connection and add network changed --- connections/walletconnect.js | 133 +++++++++++++++++++++++++++++++---- presets/index.js | 1 + 2 files changed, 119 insertions(+), 15 deletions(-) diff --git a/connections/walletconnect.js b/connections/walletconnect.js index 153b523..44bfeb0 100644 --- a/connections/walletconnect.js +++ b/connections/walletconnect.js @@ -1,8 +1,13 @@ +const { getChain } = require('evm-chains') +const { convertNumberToHex } = require('@walletconnect/browser') const EventEmitter = require('events') +const presets = require('../presets') const dev = process.env.NODE_ENV === 'development' let WalletConnect, WCQRCode +const XHR = typeof window !== 'undefined' && typeof window.XMLHttpRequest !== 'undefined' ? window.XMLHttpRequest : null + class WalletConnectConnection extends EventEmitter { constructor(_WalletConnect, _WCQRCode, options) { super() @@ -10,6 +15,7 @@ class WalletConnectConnection extends EventEmitter { WCQRCode = _WCQRCode this.bridge = options.walletConnectBridge this.qrcode = options.walletConnectQR + this.infuraId = options.walletConnectInfuraId this.on('error', () => this.close()) setTimeout(() => this.create(options), 0) } @@ -39,11 +45,11 @@ class WalletConnectConnection extends EventEmitter { if (this.qrcode) WCQRCode.close() // Close QR Code Modal const { accounts, chainId } = payload.params[0] // Get provided accounts and chainId - // Save accounts and chainId + // Save accounts this.accounts = accounts - this.chainId = chainId - this.updateNetworkId(chainId) + // Handle chain update + this.updateChain(chainId) // Emit connect event this.emit('connect') @@ -60,14 +66,8 @@ class WalletConnectConnection extends EventEmitter { this.emit('accountsChanged', accounts) } - // Check if chainId changed and trigger event - if (this.chainId !== chainId) { - this.chainId = chainId - this.emit('chainChanged', chainId) - } - - // Check if networkId changed and trigger event - this.updateNetworkId(chainId) + // Handle chain update + this.updateChain(chainId) }) this.wc.on('disconnect', (e, payload) => { @@ -89,19 +89,122 @@ class WalletConnectConnection extends EventEmitter { error(payload, message, code = -1) { this.emit('payload', { id: payload.id, jsonrpc: payload.jsonrpc, error: { message, code } }) } - send(payload) { + async send(payload) { + const signingMethods = [ + 'eth_sendTransaction', + 'eth_signTransction', + 'eth_sign', + 'eth_signTypedData', + 'eth_signTypedData_v1', + 'eth_signTypedData_v3', + 'personal_sign' + ] + const stateMethods = [ + 'eth_accounts', + 'eth_chainId', + 'net_version' + ] if (this.wc && this.wc.connected) { - return this.wc.sendCustomRequest(payload) + if (signingMethods.includes(payload.method)) { + return await this.wc.sendCustomRequest(payload) + } else if (stateMethods.includes(payload.method)) { + switch (payload.method) { + case 'eth_accounts': + return { + id: payload.id, + jsonrpc: payload.jsonrpc, + result: this.accounts + } + case 'eth_chainId': + return { + id: payload.id, + jsonrpc: payload.jsonrpc, + result: convertNumberToHex(this.chainId) + } + + case 'net_version': + return { + id: payload.id, + jsonrpc: payload.jsonrpc, + result: this.networkId + } + default: + break; + } + } else { + return await this.httpConnection.send(payload) + } } else { return this.error(payload, 'Not connected') } } - async updateNetworkId(chainId) { - const networkId = convertChainIdToNetworkId(chainId) + + async updateChain(chainId) { + if (this.chainId === chainId) { + return; + } + const chain = await getChain(chainId) + + // Check if chainId changed and trigger event + if (this.chainId !== chainId) { + this.chainId = chainId + this.emit('chainChanged', chainId) + } + + const { networkId } = chain + // Check if networkId changed and trigger event if (this.networkId !== networkId) { this.networkId = networkId this.emit('networkChanged', networkId) } + + // Handle rpcUrl update + this.updateRpcUrl(chain) + } + + updateRpcUrl(chain) { + const { chainId, rpc } = chain + if (rpc.length) { + if (this.infuraId) { + const matches = rpc.filter(rpcUrl => rpcUrl.inclues('infura.io')) + if (matches && matches.length) { + this.rpcUrl = matches[0].replace("${INFURA_API_KEY}", this.infuraId) + } else { + this.rpcUrl = rpc[0] + } + } + } else { + this.rpcUrl = this.getPresetRpcUrl(chainId) + } + // Handle httpConnection update + this.updateHttpConnection() + } + + updateHttpConnection = (options) => { + if (this.rpcUrl) { + this.httpConnection = new HTTPConnection(XHR, this.rpcUrl, options) + } + } + + getPresetRpcUrl(chainId) { + switch (chainId) { + case 1: + return presets.infura[1] + case 3: + return presets.infuraRopsten[1] + + case 4: + return presets.infuraRinkeby[1] + + case 5: + return presets.infuraGoerli[1] + + case 42: + return presets.infuraKovan[1] + default: + return '' + + } } } diff --git a/presets/index.js b/presets/index.js index 0b6c117..e8014d4 100644 --- a/presets/index.js +++ b/presets/index.js @@ -5,6 +5,7 @@ module.exports = { infura: ['wss://mainnet.infura.io/ws/v3/786ade30f36244469480aa5c2bf0743b', 'https://mainnet.infura.io/v3/786ade30f36244469480aa5c2bf0743b'], infuraRopsten: ['wss://ropsten.infura.io/ws/v3/786ade30f36244469480aa5c2bf0743b', 'https://ropsten.infura.io/v3/786ade30f36244469480aa5c2bf0743b'], infuraRinkeby: ['wss://rinkeby.infura.io/ws/v3/786ade30f36244469480aa5c2bf0743b', 'https://rinkeby.infura.io/v3/786ade30f36244469480aa5c2bf0743b'], + infuraGoerli: ['wss://goerli.infura.io/ws/v3/786ade30f36244469480aa5c2bf0743b', 'https://goerli.infura.io/v3/786ade30f36244469480aa5c2bf0743b'], infuraKovan: ['wss://kovan.infura.io/ws/v3/786ade30f36244469480aa5c2bf0743b', 'https://kovan.infura.io/v3/786ade30f36244469480aa5c2bf0743b'], walletconnect: ['walletconnect'] } From 251c3147ae7cb17702c9fa2417e86c5b13c37874 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Fri, 28 Jun 2019 14:44:05 +0200 Subject: [PATCH 3/6] handle missing rpcUrl error --- connections/walletconnect.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/connections/walletconnect.js b/connections/walletconnect.js index 44bfeb0..7f73672 100644 --- a/connections/walletconnect.js +++ b/connections/walletconnect.js @@ -164,20 +164,27 @@ class WalletConnectConnection extends EventEmitter { updateRpcUrl(chain) { const { chainId, rpc } = chain + let rpcUrl = '' if (rpc.length) { if (this.infuraId) { const matches = rpc.filter(rpcUrl => rpcUrl.inclues('infura.io')) if (matches && matches.length) { - this.rpcUrl = matches[0].replace("${INFURA_API_KEY}", this.infuraId) + rpcUrl = matches[0].replace("${INFURA_API_KEY}", this.infuraId) } else { - this.rpcUrl = rpc[0] + rpcUrl = rpc[0] } } } else { - this.rpcUrl = this.getPresetRpcUrl(chainId) + rpcUrl = this.getPresetRpcUrl(chainId) + } + if (rpcUrl) { + // Update rpcUrl + this.rpcUrl = rpcUrl + // Handle httpConnection update + this.updateHttpConnection() + } else { + this.emit('error', new Error(`No RPC Url avaialble for chainId: ${chainId}`)) } - // Handle httpConnection update - this.updateHttpConnection() } updateHttpConnection = (options) => { From 224ffee770c43d4110a9709bf8df55bcff58e1af Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Fri, 28 Jun 2019 14:50:32 +0200 Subject: [PATCH 4/6] cleaning up --- connections/walletconnect.js | 50 +++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/connections/walletconnect.js b/connections/walletconnect.js index 7f73672..dad5b42 100644 --- a/connections/walletconnect.js +++ b/connections/walletconnect.js @@ -108,29 +108,7 @@ class WalletConnectConnection extends EventEmitter { if (signingMethods.includes(payload.method)) { return await this.wc.sendCustomRequest(payload) } else if (stateMethods.includes(payload.method)) { - switch (payload.method) { - case 'eth_accounts': - return { - id: payload.id, - jsonrpc: payload.jsonrpc, - result: this.accounts - } - case 'eth_chainId': - return { - id: payload.id, - jsonrpc: payload.jsonrpc, - result: convertNumberToHex(this.chainId) - } - - case 'net_version': - return { - id: payload.id, - jsonrpc: payload.jsonrpc, - result: this.networkId - } - default: - break; - } + return await this.handleStateMethods(payload) } else { return await this.httpConnection.send(payload) } @@ -139,6 +117,32 @@ class WalletConnectConnection extends EventEmitter { } } + async handleStateMethods(payload) { + switch (payload.method) { + case 'eth_accounts': + return { + id: payload.id, + jsonrpc: payload.jsonrpc, + result: this.accounts + } + case 'eth_chainId': + return { + id: payload.id, + jsonrpc: payload.jsonrpc, + result: convertNumberToHex(this.chainId) + } + + case 'net_version': + return { + id: payload.id, + jsonrpc: payload.jsonrpc, + result: this.networkId + } + default: + break; + } + } + async updateChain(chainId) { if (this.chainId === chainId) { return; From d235d2f631fe50fdb9204b91822f93af3decebb2 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Fri, 28 Jun 2019 15:01:31 +0200 Subject: [PATCH 5/6] hook up httpconnection payload and error events --- connections/walletconnect.js | 37 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/connections/walletconnect.js b/connections/walletconnect.js index dad5b42..4ca80d9 100644 --- a/connections/walletconnect.js +++ b/connections/walletconnect.js @@ -106,11 +106,13 @@ class WalletConnectConnection extends EventEmitter { ] if (this.wc && this.wc.connected) { if (signingMethods.includes(payload.method)) { - return await this.wc.sendCustomRequest(payload) + const response = await this.wc.unsafeSend(payload) + this.emit('payload', response) } else if (stateMethods.includes(payload.method)) { - return await this.handleStateMethods(payload) + const response = await this.handleStateMethods(payload) + this.emit('payload', response) } else { - return await this.httpConnection.send(payload) + await this.httpConnection.send(payload) } } else { return this.error(payload, 'Not connected') @@ -118,29 +120,26 @@ class WalletConnectConnection extends EventEmitter { } async handleStateMethods(payload) { + const response = { + id: payload.id, + jsonrpc: payload.jsonrpc, + result + } switch (payload.method) { case 'eth_accounts': - return { - id: payload.id, - jsonrpc: payload.jsonrpc, - result: this.accounts - } + response.result = this.accounts + break; case 'eth_chainId': - return { - id: payload.id, - jsonrpc: payload.jsonrpc, - result: convertNumberToHex(this.chainId) - } + response.result = convertNumberToHex(this.chainId) + break; case 'net_version': - return { - id: payload.id, - jsonrpc: payload.jsonrpc, - result: this.networkId - } + response.result = this.networkId + break; default: break; } + return response } async updateChain(chainId) { @@ -194,6 +193,8 @@ class WalletConnectConnection extends EventEmitter { updateHttpConnection = (options) => { if (this.rpcUrl) { this.httpConnection = new HTTPConnection(XHR, this.rpcUrl, options) + this.httpConnection.on('payload', payload => this.emit('payload', payload)) + this.httpConnection.on('error', error => this.emit('error', error)) } } From 0f0f83c5fce326e0128303ac525b8341575b7eb5 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Fri, 28 Jun 2019 15:55:24 +0200 Subject: [PATCH 6/6] typo --- connections/walletconnect.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connections/walletconnect.js b/connections/walletconnect.js index 4ca80d9..406675e 100644 --- a/connections/walletconnect.js +++ b/connections/walletconnect.js @@ -1,5 +1,5 @@ const { getChain } = require('evm-chains') -const { convertNumberToHex } = require('@walletconnect/browser') +const { convertNumberToHex } = require('@walletconnect/utils') const EventEmitter = require('events') const presets = require('../presets') const dev = process.env.NODE_ENV === 'development'