aboutsummaryrefslogtreecommitdiffstats
path: root/packages/subproviders
diff options
context:
space:
mode:
authorBrandon Millman <brandon.millman@gmail.com>2018-10-10 08:04:54 +0800
committerBrandon Millman <brandon.millman@gmail.com>2018-10-10 08:04:54 +0800
commit8155d311af04339c105f1a29b74f1ddbced85197 (patch)
tree9b345f05561e806b6d89cec26b371aadafb109ed /packages/subproviders
parentcd8e6d9cdf7d6aa1e811bb7d9d7017da342907ed (diff)
parentbd06ebde8d1a5caf138ad3b36f9a5c5f255f2312 (diff)
downloaddexon-sol-tools-8155d311af04339c105f1a29b74f1ddbced85197.tar
dexon-sol-tools-8155d311af04339c105f1a29b74f1ddbced85197.tar.gz
dexon-sol-tools-8155d311af04339c105f1a29b74f1ddbced85197.tar.bz2
dexon-sol-tools-8155d311af04339c105f1a29b74f1ddbced85197.tar.lz
dexon-sol-tools-8155d311af04339c105f1a29b74f1ddbced85197.tar.xz
dexon-sol-tools-8155d311af04339c105f1a29b74f1ddbced85197.tar.zst
dexon-sol-tools-8155d311af04339c105f1a29b74f1ddbced85197.zip
Merge branch 'development' into feature/asset-buyer/api-tweaks
* development: Define bundlewatch.ci.repoBranchBase Stop accesing ethers private methods Fix merge conflicts Fix ethers build issue Upgrade ethers.js version Fix branch name Add max sizes Use bundlewatch instead of bundlesize Move Metamask Error to OrderErrors Update the exported types for the packages which touch RPC providers Throw and handle errors from Providers. Clarifies use of schemas outside of Javascript/TypeScript. Detect MM on signature validation failure. Return SignedOrder from signing utils. Update 0x.js Changelog Add eth_signTypedData support to our wallet subproviders Move SignTypedData to utils package Introduce Metamask Subprovider. Expose eth_signTypedData functionality for order signing
Diffstat (limited to 'packages/subproviders')
-rw-r--r--packages/subproviders/CHANGELOG.json13
-rw-r--r--packages/subproviders/package.json2
-rw-r--r--packages/subproviders/src/index.ts18
-rw-r--r--packages/subproviders/src/subproviders/base_wallet_subprovider.ts14
-rw-r--r--packages/subproviders/src/subproviders/eth_lightwallet_subprovider.ts37
-rw-r--r--packages/subproviders/src/subproviders/ledger.ts10
-rw-r--r--packages/subproviders/src/subproviders/metamask_subprovider.ts126
-rw-r--r--packages/subproviders/src/subproviders/mnemonic_wallet.ts28
-rw-r--r--packages/subproviders/src/subproviders/private_key_wallet.ts29
-rw-r--r--packages/subproviders/src/subproviders/signer.ts15
-rw-r--r--packages/subproviders/src/types.ts2
-rw-r--r--packages/subproviders/test/unit/eth_lightwallet_subprovider_test.ts21
-rw-r--r--packages/subproviders/test/unit/mnemonic_wallet_subprovider_test.ts21
-rw-r--r--packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts21
-rw-r--r--packages/subproviders/test/utils/fixture_data.ts31
15 files changed, 367 insertions, 21 deletions
diff --git a/packages/subproviders/CHANGELOG.json b/packages/subproviders/CHANGELOG.json
index 97f886f64..387207b01 100644
--- a/packages/subproviders/CHANGELOG.json
+++ b/packages/subproviders/CHANGELOG.json
@@ -1,5 +1,18 @@
[
{
+ "version": "2.1.0",
+ "changes": [
+ {
+ "note": "Add `MetamaskSubprovider` to handle inconsistent JSON RPC behaviour",
+ "pr": 1102
+ },
+ {
+ "note": "Add support for `eth_signTypedData` in wallets Mnemonic, Private and EthLightWallet",
+ "pr": 1102
+ }
+ ]
+ },
+ {
"version": "2.0.7",
"changes": [
{
diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json
index fad478349..ff3ab7ed5 100644
--- a/packages/subproviders/package.json
+++ b/packages/subproviders/package.json
@@ -45,7 +45,7 @@
"ethereum-types": "^1.0.11",
"ethereumjs-tx": "^1.3.5",
"ethereumjs-util": "^5.1.1",
- "ganache-core": "0xProject/ganache-core#monorepo-dep",
+ "ganache-core": "^2.2.1",
"hdkey": "^0.7.1",
"json-rpc-error": "2.0.0",
"lodash": "^4.17.5",
diff --git a/packages/subproviders/src/index.ts b/packages/subproviders/src/index.ts
index b5f9b3f90..9f4dac58b 100644
--- a/packages/subproviders/src/index.ts
+++ b/packages/subproviders/src/index.ts
@@ -27,6 +27,7 @@ export { Subprovider } from './subproviders/subprovider';
export { NonceTrackerSubprovider } from './subproviders/nonce_tracker';
export { PrivateKeyWalletSubprovider } from './subproviders/private_key_wallet';
export { MnemonicWalletSubprovider } from './subproviders/mnemonic_wallet';
+export { MetamaskSubprovider } from './subproviders/metamask_subprovider';
export { EthLightwalletSubprovider } from './subproviders/eth_lightwallet_subprovider';
export {
@@ -47,6 +48,19 @@ export {
LedgerGetAddressResult,
} from './types';
-export { ECSignature } from '@0xproject/types';
+export {
+ ECSignature,
+ EIP712Object,
+ EIP712ObjectValue,
+ EIP712TypedData,
+ EIP712Types,
+ EIP712Parameter,
+} from '@0xproject/types';
-export { JSONRPCRequestPayload, Provider, JSONRPCResponsePayload, JSONRPCErrorCallback } from 'ethereum-types';
+export {
+ JSONRPCRequestPayload,
+ Provider,
+ JSONRPCResponsePayload,
+ JSONRPCErrorCallback,
+ JSONRPCResponseError,
+} from 'ethereum-types';
diff --git a/packages/subproviders/src/subproviders/base_wallet_subprovider.ts b/packages/subproviders/src/subproviders/base_wallet_subprovider.ts
index 4342e47e9..409a0d330 100644
--- a/packages/subproviders/src/subproviders/base_wallet_subprovider.ts
+++ b/packages/subproviders/src/subproviders/base_wallet_subprovider.ts
@@ -23,6 +23,7 @@ export abstract class BaseWalletSubprovider extends Subprovider {
public abstract async getAccountsAsync(): Promise<string[]>;
public abstract async signTransactionAsync(txParams: PartialTxParams): Promise<string>;
public abstract async signPersonalMessageAsync(data: string, address: string): Promise<string>;
+ public abstract async signTypedDataAsync(address: string, typedData: any): Promise<string>;
/**
* This method conforms to the web3-provider-engine interface.
@@ -36,6 +37,8 @@ export abstract class BaseWalletSubprovider extends Subprovider {
public async handleRequest(payload: JSONRPCRequestPayload, next: Callback, end: ErrorCallback): Promise<void> {
let accounts;
let txParams;
+ let address;
+ let typedData;
switch (payload.method) {
case 'eth_coinbase':
try {
@@ -86,7 +89,7 @@ export abstract class BaseWalletSubprovider extends Subprovider {
case 'eth_sign':
case 'personal_sign':
const data = payload.method === 'eth_sign' ? payload.params[1] : payload.params[0];
- const address = payload.method === 'eth_sign' ? payload.params[0] : payload.params[1];
+ address = payload.method === 'eth_sign' ? payload.params[0] : payload.params[1];
try {
const ecSignatureHex = await this.signPersonalMessageAsync(data, address);
end(null, ecSignatureHex);
@@ -94,6 +97,15 @@ export abstract class BaseWalletSubprovider extends Subprovider {
end(err);
}
return;
+ case 'eth_signTypedData':
+ [address, typedData] = payload.params;
+ try {
+ const signature = await this.signTypedDataAsync(address, typedData);
+ end(null, signature);
+ } catch (err) {
+ end(err);
+ }
+ return;
default:
next();
diff --git a/packages/subproviders/src/subproviders/eth_lightwallet_subprovider.ts b/packages/subproviders/src/subproviders/eth_lightwallet_subprovider.ts
index 6afd71422..a1d93ac49 100644
--- a/packages/subproviders/src/subproviders/eth_lightwallet_subprovider.ts
+++ b/packages/subproviders/src/subproviders/eth_lightwallet_subprovider.ts
@@ -1,3 +1,4 @@
+import { EIP712TypedData } from '@0xproject/types';
import * as lightwallet from 'eth-lightwallet';
import { PartialTxParams } from '../types';
@@ -48,16 +49,16 @@ export class EthLightwalletSubprovider extends BaseWalletSubprovider {
// Lightwallet loses the chain id information when hex encoding the transaction
// this results in a different signature on certain networks. PrivateKeyWallet
// respects this as it uses the parameters passed in
- let privKey = this._keystore.exportPrivateKey(txParams.from, this._pwDerivedKey);
- const privKeyWallet = new PrivateKeyWalletSubprovider(privKey);
- privKey = '';
- const privKeySignature = await privKeyWallet.signTransactionAsync(txParams);
- return privKeySignature;
+ let privateKey = this._keystore.exportPrivateKey(txParams.from, this._pwDerivedKey);
+ const privateKeyWallet = new PrivateKeyWalletSubprovider(privateKey);
+ privateKey = '';
+ const privateKeySignature = await privateKeyWallet.signTransactionAsync(txParams);
+ return privateKeySignature;
}
/**
* Sign a personal Ethereum signed message. The signing account will be the account
* associated with the provided address.
- * If you've added the MnemonicWalletSubprovider to your app's provider, you can simply send an `eth_sign`
+ * If you've added this Subprovider to your app's provider, you can simply send an `eth_sign`
* or `personal_sign` JSON RPC request, and this method will be called auto-magically.
* If you are not using this via a ProviderEngine instance, you can call it directly.
* @param data Hex string message to sign
@@ -65,10 +66,26 @@ export class EthLightwalletSubprovider extends BaseWalletSubprovider {
* @return Signature hex string (order: rsv)
*/
public async signPersonalMessageAsync(data: string, address: string): Promise<string> {
- let privKey = this._keystore.exportPrivateKey(address, this._pwDerivedKey);
- const privKeyWallet = new PrivateKeyWalletSubprovider(privKey);
- privKey = '';
- const result = privKeyWallet.signPersonalMessageAsync(data, address);
+ let privateKey = this._keystore.exportPrivateKey(address, this._pwDerivedKey);
+ const privateKeyWallet = new PrivateKeyWalletSubprovider(privateKey);
+ privateKey = '';
+ const result = privateKeyWallet.signPersonalMessageAsync(data, address);
+ return result;
+ }
+ /**
+ * Sign an EIP712 Typed Data message. The signing address will associated with the provided address.
+ * If you've added this Subprovider to your app's provider, you can simply send an `eth_signTypedData`
+ * JSON RPC request, and this method will be called auto-magically.
+ * If you are not using this via a ProviderEngine instance, you can call it directly.
+ * @param address Address of the account to sign with
+ * @param data the typed data object
+ * @return Signature hex string (order: rsv)
+ */
+ public async signTypedDataAsync(address: string, typedData: EIP712TypedData): Promise<string> {
+ let privateKey = this._keystore.exportPrivateKey(address, this._pwDerivedKey);
+ const privateKeyWallet = new PrivateKeyWalletSubprovider(privateKey);
+ privateKey = '';
+ const result = privateKeyWallet.signTypedDataAsync(address, typedData);
return result;
}
}
diff --git a/packages/subproviders/src/subproviders/ledger.ts b/packages/subproviders/src/subproviders/ledger.ts
index 6ad5de2e2..ee8edde92 100644
--- a/packages/subproviders/src/subproviders/ledger.ts
+++ b/packages/subproviders/src/subproviders/ledger.ts
@@ -187,6 +187,16 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
throw err;
}
}
+ /**
+ * eth_signTypedData is currently not supported on Ledger devices.
+ * @param address Address of the account to sign with
+ * @param data the typed data object
+ * @return Signature hex string (order: rsv)
+ */
+ // tslint:disable-next-line:prefer-function-over-method
+ public async signTypedDataAsync(address: string, typedData: any): Promise<string> {
+ throw new Error(WalletSubproviderErrors.MethodNotSupported);
+ }
private async _createLedgerClientAsync(): Promise<LedgerEthereumClient> {
await this._connectionLock.acquire();
if (!_.isUndefined(this._ledgerClientIfExists)) {
diff --git a/packages/subproviders/src/subproviders/metamask_subprovider.ts b/packages/subproviders/src/subproviders/metamask_subprovider.ts
new file mode 100644
index 000000000..46fc2a9cd
--- /dev/null
+++ b/packages/subproviders/src/subproviders/metamask_subprovider.ts
@@ -0,0 +1,126 @@
+import { marshaller, Web3Wrapper } from '@0xproject/web3-wrapper';
+import { JSONRPCRequestPayload, Provider } from 'ethereum-types';
+import * as ethUtil from 'ethereumjs-util';
+
+import { Callback, ErrorCallback } from '../types';
+
+import { Subprovider } from './subprovider';
+
+/**
+ * This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine)
+ * subprovider interface and the provider sendAsync interface.
+ * It handles inconsistencies with Metamask implementations of various JSON RPC methods.
+ * It forwards JSON RPC requests involving the domain of a signer (getAccounts,
+ * sendTransaction, signMessage etc...) to the provider instance supplied at instantiation. All other requests
+ * are passed onwards for subsequent subproviders to handle.
+ */
+export class MetamaskSubprovider extends Subprovider {
+ private readonly _web3Wrapper: Web3Wrapper;
+ private readonly _provider: Provider;
+ /**
+ * Instantiates a new MetamaskSubprovider
+ * @param provider Web3 provider that should handle all user account related requests
+ */
+ constructor(provider: Provider) {
+ super();
+ this._web3Wrapper = new Web3Wrapper(provider);
+ this._provider = provider;
+ }
+ /**
+ * This method conforms to the web3-provider-engine interface.
+ * It is called internally by the ProviderEngine when it is this subproviders
+ * turn to handle a JSON RPC request.
+ * @param payload JSON RPC payload
+ * @param next Callback to call if this subprovider decides not to handle the request
+ * @param end Callback to call if subprovider handled the request and wants to pass back the request.
+ */
+ // tslint:disable-next-line:prefer-function-over-method async-suffix
+ public async handleRequest(payload: JSONRPCRequestPayload, next: Callback, end: ErrorCallback): Promise<void> {
+ let message;
+ let address;
+ switch (payload.method) {
+ case 'web3_clientVersion':
+ try {
+ const nodeVersion = await this._web3Wrapper.getNodeVersionAsync();
+ end(null, nodeVersion);
+ } catch (err) {
+ end(err);
+ }
+ return;
+ case 'eth_accounts':
+ try {
+ const accounts = await this._web3Wrapper.getAvailableAddressesAsync();
+ end(null, accounts);
+ } catch (err) {
+ end(err);
+ }
+ return;
+ case 'eth_sendTransaction':
+ const [txParams] = payload.params;
+ try {
+ const txData = marshaller.unmarshalTxData(txParams);
+ const txHash = await this._web3Wrapper.sendTransactionAsync(txData);
+ end(null, txHash);
+ } catch (err) {
+ end(err);
+ }
+ return;
+ case 'eth_sign':
+ [address, message] = payload.params;
+ try {
+ // Metamask incorrectly implements eth_sign and does not prefix the message as per the spec
+ // Source: https://github.com/MetaMask/metamask-extension/commit/a9d36860bec424dcee8db043d3e7da6a5ff5672e
+ const msgBuff = ethUtil.toBuffer(message);
+ const prefixedMsgBuff = ethUtil.hashPersonalMessage(msgBuff);
+ const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff);
+ const signature = await this._web3Wrapper.signMessageAsync(address, prefixedMsgHex);
+ signature ? end(null, signature) : end(new Error('Error performing eth_sign'), null);
+ } catch (err) {
+ end(err);
+ }
+ return;
+ case 'eth_signTypedData':
+ case 'eth_signTypedData_v3':
+ [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);
+ const signature = await this._web3Wrapper.sendRawPayloadAsync<string>({
+ method: 'eth_signTypedData_v3',
+ params: [address, messageJSON],
+ });
+ signature ? end(null, signature) : end(new Error('Error performing eth_signTypedData'), null);
+ } catch (err) {
+ end(err);
+ }
+ return;
+ default:
+ next();
+ return;
+ }
+ }
+ /**
+ * This method conforms to the provider sendAsync interface.
+ * Allowing the MetamaskSubprovider to be used as a generic provider (outside of Web3ProviderEngine) with the
+ * addition of wrapping the inconsistent Metamask behaviour
+ * @param payload JSON RPC payload
+ * @return The contents nested under the result key of the response body
+ */
+ public sendAsync(payload: JSONRPCRequestPayload, callback: ErrorCallback): void {
+ void this.handleRequest(
+ payload,
+ // handleRequest has decided to not handle this, so fall through to the provider
+ () => {
+ const sendAsync = this._provider.sendAsync.bind(this._provider);
+ sendAsync(payload, callback);
+ },
+ // handleRequest has called end and will handle this
+ (err, data) => {
+ err ? callback(err) : callback(null, { ...payload, result: data });
+ },
+ );
+ }
+}
diff --git a/packages/subproviders/src/subproviders/mnemonic_wallet.ts b/packages/subproviders/src/subproviders/mnemonic_wallet.ts
index 1495112b6..04a11c7be 100644
--- a/packages/subproviders/src/subproviders/mnemonic_wallet.ts
+++ b/packages/subproviders/src/subproviders/mnemonic_wallet.ts
@@ -1,4 +1,5 @@
import { assert } from '@0xproject/assert';
+import { EIP712TypedData } from '@0xproject/types';
import { addressUtils } from '@0xproject/utils';
import * as bip39 from 'bip39';
import HDNode = require('hdkey');
@@ -90,10 +91,10 @@ export class MnemonicWalletSubprovider extends BaseWalletSubprovider {
}
/**
* Sign a personal Ethereum signed message. The signing account will be the account
- * associated with the provided address.
- * If you've added the MnemonicWalletSubprovider to your app's provider, you can simply send an `eth_sign`
- * or `personal_sign` JSON RPC request, and this method will be called auto-magically.
- * If you are not using this via a ProviderEngine instance, you can call it directly.
+ * associated with the provided address. If you've added the MnemonicWalletSubprovider to
+ * your app's provider, you can simply send an `eth_sign` or `personal_sign` JSON RPC request,
+ * and this method will be called auto-magically. If you are not using this via a ProviderEngine
+ * instance, you can call it directly.
* @param data Hex string message to sign
* @param address Address of the account to sign with
* @return Signature hex string (order: rsv)
@@ -108,6 +109,25 @@ export class MnemonicWalletSubprovider extends BaseWalletSubprovider {
const sig = await privateKeyWallet.signPersonalMessageAsync(data, address);
return sig;
}
+ /**
+ * Sign an EIP712 Typed Data message. The signing account will be the account
+ * associated with the provided address. If you've added this MnemonicWalletSubprovider to
+ * your app's provider, you can simply send an `eth_signTypedData` JSON RPC request, and
+ * this method will be called auto-magically. If you are not using this via a ProviderEngine
+ * instance, you can call it directly.
+ * @param address Address of the account to sign with
+ * @param data the typed data object
+ * @return Signature hex string (order: rsv)
+ */
+ public async signTypedDataAsync(address: string, typedData: EIP712TypedData): Promise<string> {
+ if (_.isUndefined(typedData)) {
+ throw new Error(WalletSubproviderErrors.DataMissingForSignPersonalMessage);
+ }
+ assert.isETHAddressHex('address', address);
+ const privateKeyWallet = this._privateKeyWalletForAddress(address);
+ const sig = await privateKeyWallet.signTypedDataAsync(address, typedData);
+ return sig;
+ }
private _privateKeyWalletForAddress(address: string): PrivateKeyWalletSubprovider {
const derivedKeyInfo = this._findDerivedKeyInfoForAddress(address);
const privateKeyHex = derivedKeyInfo.hdKey.privateKey.toString('hex');
diff --git a/packages/subproviders/src/subproviders/private_key_wallet.ts b/packages/subproviders/src/subproviders/private_key_wallet.ts
index 9d6fc487e..e89c4c186 100644
--- a/packages/subproviders/src/subproviders/private_key_wallet.ts
+++ b/packages/subproviders/src/subproviders/private_key_wallet.ts
@@ -1,4 +1,6 @@
import { assert } from '@0xproject/assert';
+import { EIP712TypedData } from '@0xproject/types';
+import { signTypedDataUtils } from '@0xproject/utils';
import EthereumTx = require('ethereumjs-tx');
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
@@ -23,7 +25,7 @@ export class PrivateKeyWalletSubprovider extends BaseWalletSubprovider {
constructor(privateKey: string) {
assert.isString('privateKey', privateKey);
super();
- this._privateKeyBuffer = new Buffer(privateKey, 'hex');
+ this._privateKeyBuffer = Buffer.from(privateKey, 'hex');
this._address = `0x${ethUtil.privateToAddress(this._privateKeyBuffer).toString('hex')}`;
}
/**
@@ -84,4 +86,29 @@ export class PrivateKeyWalletSubprovider extends BaseWalletSubprovider {
const rpcSig = ethUtil.toRpcSig(sig.v, sig.r, sig.s);
return rpcSig;
}
+ /**
+ * Sign an EIP712 Typed Data message. The signing address will be calculated from the private key.
+ * The address must be provided it must match the address calculated from the private key.
+ * If you've added this Subprovider to your app's provider, you can simply send an `eth_signTypedData`
+ * JSON RPC request, and this method will be called auto-magically.
+ * If you are not using this via a ProviderEngine instance, you can call it directly.
+ * @param address Address of the account to sign with
+ * @param data the typed data object
+ * @return Signature hex string (order: rsv)
+ */
+ public async signTypedDataAsync(address: string, typedData: EIP712TypedData): Promise<string> {
+ if (_.isUndefined(typedData)) {
+ throw new Error(WalletSubproviderErrors.DataMissingForSignTypedData);
+ }
+ assert.isETHAddressHex('address', address);
+ if (address !== this._address) {
+ throw new Error(
+ `Requested to sign message with address: ${address}, instantiated with address: ${this._address}`,
+ );
+ }
+ const dataBuff = signTypedDataUtils.generateTypedDataHash(typedData);
+ const sig = ethUtil.ecsign(dataBuff, this._privateKeyBuffer);
+ const rpcSig = ethUtil.toRpcSig(sig.v, sig.r, sig.s);
+ return rpcSig;
+ }
}
diff --git a/packages/subproviders/src/subproviders/signer.ts b/packages/subproviders/src/subproviders/signer.ts
index d5fd86897..eda7db42e 100644
--- a/packages/subproviders/src/subproviders/signer.ts
+++ b/packages/subproviders/src/subproviders/signer.ts
@@ -14,7 +14,7 @@ import { Subprovider } from './subprovider';
export class SignerSubprovider extends Subprovider {
private readonly _web3Wrapper: Web3Wrapper;
/**
- * Instantiates a new SignerSubprovider
+ * Instantiates a new SignerSubprovider.
* @param provider Web3 provider that should handle all user account related requests
*/
constructor(provider: Provider) {
@@ -31,6 +31,8 @@ export class SignerSubprovider extends Subprovider {
*/
// tslint:disable-next-line:prefer-function-over-method async-suffix
public async handleRequest(payload: JSONRPCRequestPayload, next: Callback, end: ErrorCallback): Promise<void> {
+ let message;
+ let address;
switch (payload.method) {
case 'web3_clientVersion':
try {
@@ -59,7 +61,7 @@ export class SignerSubprovider extends Subprovider {
}
return;
case 'eth_sign':
- const [address, message] = payload.params;
+ [address, message] = payload.params;
try {
const signature = await this._web3Wrapper.signMessageAsync(address, message);
end(null, signature);
@@ -67,6 +69,15 @@ export class SignerSubprovider extends Subprovider {
end(err);
}
return;
+ case 'eth_signTypedData':
+ [address, message] = payload.params;
+ try {
+ const signature = await this._web3Wrapper.signTypedDataAsync(address, message);
+ end(null, signature);
+ } catch (err) {
+ end(err);
+ }
+ return;
default:
next();
return;
diff --git a/packages/subproviders/src/types.ts b/packages/subproviders/src/types.ts
index fe58bffa5..e8a47ad34 100644
--- a/packages/subproviders/src/types.ts
+++ b/packages/subproviders/src/types.ts
@@ -107,8 +107,10 @@ export interface ResponseWithTxParams {
export enum WalletSubproviderErrors {
AddressNotFound = 'ADDRESS_NOT_FOUND',
DataMissingForSignPersonalMessage = 'DATA_MISSING_FOR_SIGN_PERSONAL_MESSAGE',
+ DataMissingForSignTypedData = 'DATA_MISSING_FOR_SIGN_TYPED_DATA',
SenderInvalidOrNotSupplied = 'SENDER_INVALID_OR_NOT_SUPPLIED',
FromAddressMissingOrInvalid = 'FROM_ADDRESS_MISSING_OR_INVALID',
+ MethodNotSupported = 'METHOD_NOT_SUPPORTED',
}
export enum LedgerSubproviderErrors {
TooOldLedgerFirmware = 'TOO_OLD_LEDGER_FIRMWARE',
diff --git a/packages/subproviders/test/unit/eth_lightwallet_subprovider_test.ts b/packages/subproviders/test/unit/eth_lightwallet_subprovider_test.ts
index 063817a95..49698ce9e 100644
--- a/packages/subproviders/test/unit/eth_lightwallet_subprovider_test.ts
+++ b/packages/subproviders/test/unit/eth_lightwallet_subprovider_test.ts
@@ -73,6 +73,13 @@ describe('EthLightwalletSubprovider', () => {
const txHex = await ethLightwalletSubprovider.signTransactionAsync(fixtureData.TX_DATA);
expect(txHex).to.be.equal(fixtureData.TX_DATA_SIGNED_RESULT);
});
+ it('signs an EIP712 sign typed data message', async () => {
+ const signature = await ethLightwalletSubprovider.signTypedDataAsync(
+ fixtureData.TEST_RPC_ACCOUNT_0,
+ fixtureData.EIP712_TEST_TYPED_DATA,
+ );
+ expect(signature).to.be.equal(fixtureData.EIP712_TEST_TYPED_DATA_SIGNED_RESULT);
+ });
});
});
describe('calls through a provider', () => {
@@ -129,6 +136,20 @@ describe('EthLightwalletSubprovider', () => {
});
provider.sendAsync(payload, callback);
});
+ it('signs an EIP712 sign typed data message with eth_signTypedData', (done: DoneCallback) => {
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_signTypedData',
+ params: [fixtureData.TEST_RPC_ACCOUNT_0, fixtureData.EIP712_TEST_TYPED_DATA],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result).to.be.equal(fixtureData.EIP712_TEST_TYPED_DATA_SIGNED_RESULT);
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
});
describe('failure cases', () => {
it('should throw if `data` param not hex when calling eth_sign', (done: DoneCallback) => {
diff --git a/packages/subproviders/test/unit/mnemonic_wallet_subprovider_test.ts b/packages/subproviders/test/unit/mnemonic_wallet_subprovider_test.ts
index f2bdda3cd..61dcbf6da 100644
--- a/packages/subproviders/test/unit/mnemonic_wallet_subprovider_test.ts
+++ b/packages/subproviders/test/unit/mnemonic_wallet_subprovider_test.ts
@@ -47,6 +47,13 @@ describe('MnemonicWalletSubprovider', () => {
const txHex = await subprovider.signTransactionAsync(txData);
expect(txHex).to.be.equal(fixtureData.TX_DATA_ACCOUNT_1_SIGNED_RESULT);
});
+ it('signs an EIP712 sign typed data message', async () => {
+ const signature = await subprovider.signTypedDataAsync(
+ fixtureData.TEST_RPC_ACCOUNT_0,
+ fixtureData.EIP712_TEST_TYPED_DATA,
+ );
+ expect(signature).to.be.equal(fixtureData.EIP712_TEST_TYPED_DATA_SIGNED_RESULT);
+ });
});
describe('failure cases', () => {
it('throws an error if address is invalid ', async () => {
@@ -118,6 +125,20 @@ describe('MnemonicWalletSubprovider', () => {
});
provider.sendAsync(payload, callback);
});
+ it('signs an EIP712 sign typed data message with eth_signTypedData', (done: DoneCallback) => {
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_signTypedData',
+ params: [fixtureData.TEST_RPC_ACCOUNT_0, fixtureData.EIP712_TEST_TYPED_DATA],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result).to.be.equal(fixtureData.EIP712_TEST_TYPED_DATA_SIGNED_RESULT);
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
});
describe('failure cases', () => {
it('should throw if `data` param not hex when calling eth_sign', (done: DoneCallback) => {
diff --git a/packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts b/packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts
index 95773145f..4cd70e5ed 100644
--- a/packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts
+++ b/packages/subproviders/test/unit/private_key_wallet_subprovider_test.ts
@@ -32,6 +32,13 @@ describe('PrivateKeyWalletSubprovider', () => {
const txHex = await subprovider.signTransactionAsync(fixtureData.TX_DATA);
expect(txHex).to.be.equal(fixtureData.TX_DATA_SIGNED_RESULT);
});
+ it('signs an EIP712 sign typed data message', async () => {
+ const signature = await subprovider.signTypedDataAsync(
+ fixtureData.TEST_RPC_ACCOUNT_0,
+ fixtureData.EIP712_TEST_TYPED_DATA,
+ );
+ expect(signature).to.be.equal(fixtureData.EIP712_TEST_TYPED_DATA_SIGNED_RESULT);
+ });
});
});
describe('calls through a provider', () => {
@@ -103,6 +110,20 @@ describe('PrivateKeyWalletSubprovider', () => {
});
provider.sendAsync(payload, callback);
});
+ it('signs an EIP712 sign typed data message with eth_signTypedData', (done: DoneCallback) => {
+ const payload = {
+ jsonrpc: '2.0',
+ method: 'eth_signTypedData',
+ params: [fixtureData.TEST_RPC_ACCOUNT_0, fixtureData.EIP712_TEST_TYPED_DATA],
+ id: 1,
+ };
+ const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => {
+ expect(err).to.be.a('null');
+ expect(response.result).to.be.equal(fixtureData.EIP712_TEST_TYPED_DATA_SIGNED_RESULT);
+ done();
+ });
+ provider.sendAsync(payload, callback);
+ });
});
describe('failure cases', () => {
it('should throw if `data` param not hex when calling eth_sign', (done: DoneCallback) => {
diff --git a/packages/subproviders/test/utils/fixture_data.ts b/packages/subproviders/test/utils/fixture_data.ts
index 7cf502c97..3eb4493b5 100644
--- a/packages/subproviders/test/utils/fixture_data.ts
+++ b/packages/subproviders/test/utils/fixture_data.ts
@@ -30,4 +30,35 @@ export const fixtureData = {
'0xf85f8080822710940000000000000000000000000000000000000000808078a0712854c73c69445cc1b22a7c3d7312ff9a97fe4ffba35fd636e8236b211b6e7ca0647cee031615e52d916c7c707025bc64ad525d8f1b9876c3435a863b42743178',
TX_DATA_ACCOUNT_1_SIGNED_RESULT:
'0xf85f8080822710940000000000000000000000000000000000000000808078a04b02af7ff3f18ce114b601542cc8ebdc50921354f75dd510d31793453a0710e6a0540082a01e475465801b8186a2edc79ec1a2dcf169b9781c25a58a417023c9ca',
+ EIP712_TEST_TYPED_DATA: {
+ types: {
+ EIP712Domain: [
+ {
+ name: 'name',
+ type: 'string',
+ },
+ ],
+ Test: [
+ {
+ name: 'testAddress',
+ type: 'address',
+ },
+ {
+ name: 'testNumber',
+ type: 'uint256',
+ },
+ ],
+ },
+ domain: {
+ name: 'Test',
+ },
+ message: {
+ testAddress: '0x0000000000000000000000000000000000000000',
+ testNumber: '12345',
+ },
+ primaryType: 'Test',
+ },
+ EIP712_TEST_TYPED_DATA_HASH: '0xb460d69ca60383293877cd765c0f97bd832d66bca720f7e32222ce1118832493',
+ EIP712_TEST_TYPED_DATA_SIGNED_RESULT:
+ '0x20af5b6bfc3658942198d6eeda159b4ed589f90cee6eac3ba117818ffba5fd7e354a353aad93faabd6eb6c66e17921c92bd1cd09c92a770f554470dc3e254ce701',
};