diff --git a/subproviders/CHANGELOG.json b/subproviders/CHANGELOG.json index 2fa5763f34..353453e120 100644 --- a/subproviders/CHANGELOG.json +++ b/subproviders/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "6.4.0", + "changes": [ + { + "note": "Support `eth_signTypedData_v4` in `MetamaskSubprovider` and use the method name passed in", + "pr": 21 + } + ] + }, { "timestamp": 1610044030, "version": "6.3.2", diff --git a/subproviders/src/subproviders/metamask_subprovider.ts b/subproviders/src/subproviders/metamask_subprovider.ts index 3efb113ef2..5125d4203b 100644 --- a/subproviders/src/subproviders/metamask_subprovider.ts +++ b/subproviders/src/subproviders/metamask_subprovider.ts @@ -84,18 +84,21 @@ export class MetamaskSubprovider extends Subprovider { end(err); } return; + // Metamask supports different versions of the `eth_signTypedData` RPC method. case 'eth_signTypedData': case 'eth_signTypedData_v3': + case 'eth_signTypedData_v4': [address, message] = payload.params; try { - // Metamask supports multiple versions and has namespaced signTypedData to v3 for an indeterminate period of time. - // eth_signTypedData is mapped to an older implementation before the spec was finalized. - // Source: https://github.com/MetaMask/metamask-extension/blob/c49d854b55b3efd34c7fd0414b76f7feaa2eec7c/app/scripts/metamask-controller.js#L1262 - // and expects message to be serialised as JSON - const messageJSON = JSON.stringify(message); + // We accept either JSON-serialized or object messages. + const messageObject = typeof message === 'object' ? message : JSON.parse(message); const signature = await this._web3Wrapper.sendRawPayloadAsync({ - method: 'eth_signTypedData_v3', - params: [address, messageJSON], + method: payload.method, + // `eth_signTypedData` takes a raw object. + params: [ + address, + payload.method === 'eth_signTypedData' ? messageObject : JSON.stringify(messageObject), + ], }); signature ? end(null, signature) : end(new Error('Error performing eth_signTypedData'), null); } catch (err) { diff --git a/web3-wrapper/CHANGELOG.json b/web3-wrapper/CHANGELOG.json index 615f0cf568..1d860ddab3 100644 --- a/web3-wrapper/CHANGELOG.json +++ b/web3-wrapper/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "7.4.0", + "changes": [ + { + "note": "Remove `Web3Wrapper.signTypedDataV4Async()` and add backoff support for multiple versions of `eth_signTypedData` to `Web3Wrapper.signTypedDataAsync()`.", + "pr": 21 + } + ] + }, { "timestamp": 1610044030, "version": "7.3.2", diff --git a/web3-wrapper/src/web3_wrapper.ts b/web3-wrapper/src/web3_wrapper.ts index cd16799556..9549f3be94 100644 --- a/web3-wrapper/src/web3_wrapper.ts +++ b/web3-wrapper/src/web3_wrapper.ts @@ -342,26 +342,27 @@ export class Web3Wrapper { public async signTypedDataAsync(address: string, typedData: any): Promise { assert.isETHAddressHex('address', address); assert.doesConformToSchema('typedData', typedData, schemas.eip712TypedDataSchema); - const signData = await this.sendRawPayloadAsync({ - method: 'eth_signTypedData', - params: [address, typedData], - }); - return signData; - } - /** - * Sign an EIP712 typed data message with a specific address's private key (MetaMask's `eth_signTypedData_v4`) - * @param address Address of signer - * @param typedData Typed data message to sign - * @returns Signature string (as RSV) - */ - public async signTypedDataV4Async(address: string, typedData: any): Promise { - assert.isETHAddressHex('address', address); - assert.doesConformToSchema('typedData', typedData, schemas.eip712TypedDataSchema); - const signData = await this.sendRawPayloadAsync({ - method: 'eth_signTypedData_v4', - params: [address, JSON.stringify(typedData)], - }); - return signData; + // Try decreasing versions of `eth_signTypedData` until it works. + const methodsToTry = ['eth_signTypedData_v4', 'eth_signTypedData_v3', 'eth_signTypedData']; + let lastErr: Error | undefined; + for (const method of methodsToTry) { + try { + return await this.sendRawPayloadAsync({ + method, + // `eth_signTypedData` expects an object, whereas the others expect + // a JSON string. + params: [address, method === 'eth_signTypedData' ? typedData : JSON.stringify(typedData)], + }); + } catch (err) { + lastErr = err; + // If there are no more methods to try or the error says something other + // than the method not existing, throw. + if (!/(not handled|does not exist)/.test(err.message)) { + throw err; + } + } + } + throw lastErr; } /** * Fetches the latest block number