aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJacob Evans <jacob@dekz.net>2018-10-05 09:45:53 +0800
committerJacob Evans <jacob@dekz.net>2018-10-05 15:12:17 +0800
commit75d274f330dc0c18577e764ca77ffb36d5a3f27e (patch)
tree9a70714a89783dfe58ffa002d39f3967de957bdc
parent6e462b7dba61611a5347c9aa181d4ae69294d7af (diff)
downloaddexon-sol-tools-75d274f330dc0c18577e764ca77ffb36d5a3f27e.tar
dexon-sol-tools-75d274f330dc0c18577e764ca77ffb36d5a3f27e.tar.gz
dexon-sol-tools-75d274f330dc0c18577e764ca77ffb36d5a3f27e.tar.bz2
dexon-sol-tools-75d274f330dc0c18577e764ca77ffb36d5a3f27e.tar.lz
dexon-sol-tools-75d274f330dc0c18577e764ca77ffb36d5a3f27e.tar.xz
dexon-sol-tools-75d274f330dc0c18577e764ca77ffb36d5a3f27e.tar.zst
dexon-sol-tools-75d274f330dc0c18577e764ca77ffb36d5a3f27e.zip
Return SignedOrder from signing utils.
Create a helper back in EIP712Utils for code cleanup. Moved constants in order-utils into the constants object
-rw-r--r--packages/0x.js/CHANGELOG.json2
-rw-r--r--packages/contract-wrappers/src/utils/transaction_encoder.ts27
-rw-r--r--packages/contracts/test/utils/transaction_factory.ts34
-rw-r--r--packages/json-schemas/schemas/eip712_typed_data.ts2
-rw-r--r--packages/order-utils/CHANGELOG.json6
-rw-r--r--packages/order-utils/src/constants.ts47
-rw-r--r--packages/order-utils/src/eip712_utils.ts75
-rw-r--r--packages/order-utils/src/index.ts9
-rw-r--r--packages/order-utils/src/order_hash.ts43
-rw-r--r--packages/order-utils/src/signature_utils.ts64
-rw-r--r--packages/order-utils/test/eip712_utils_test.ts44
-rw-r--r--packages/order-utils/test/order_hash_test.ts14
-rw-r--r--packages/order-utils/test/signature_utils_test.ts51
-rw-r--r--packages/subproviders/CHANGELOG.json4
-rw-r--r--packages/subproviders/src/index.ts9
-rw-r--r--packages/subproviders/src/subproviders/eth_lightwallet_subprovider.ts31
-rw-r--r--packages/subproviders/src/subproviders/metamask_subprovider.ts6
-rw-r--r--packages/subproviders/src/subproviders/mnemonic_wallet.ts21
-rw-r--r--packages/subproviders/src/subproviders/private_key_wallet.ts3
-rw-r--r--packages/subproviders/src/subproviders/signer.ts2
-rw-r--r--packages/subproviders/test/unit/eth_lightwallet_subprovider_test.ts21
-rw-r--r--packages/types/CHANGELOG.json4
-rw-r--r--packages/types/src/index.ts20
-rw-r--r--packages/utils/src/sign_typed_data_utils.ts29
-rw-r--r--packages/web3-wrapper/src/web3_wrapper.ts4
25 files changed, 380 insertions, 192 deletions
diff --git a/packages/0x.js/CHANGELOG.json b/packages/0x.js/CHANGELOG.json
index c25acd3bf..3a184382c 100644
--- a/packages/0x.js/CHANGELOG.json
+++ b/packages/0x.js/CHANGELOG.json
@@ -12,7 +12,7 @@
},
{
"note":
- "Removed `SignerType` (including `SignerType.Metamask`). Please use the `MetamaskSubprovider` to wrap web3.currentProvider.",
+ "Removed `SignerType` (including `SignerType.Metamask`). Please use the `MetamaskSubprovider` to wrap `web3.currentProvider`.",
"pr": 1102
}
]
diff --git a/packages/contract-wrappers/src/utils/transaction_encoder.ts b/packages/contract-wrappers/src/utils/transaction_encoder.ts
index 1800f49ad..d9735778e 100644
--- a/packages/contract-wrappers/src/utils/transaction_encoder.ts
+++ b/packages/contract-wrappers/src/utils/transaction_encoder.ts
@@ -1,5 +1,5 @@
import { schemas } from '@0xproject/json-schemas';
-import { EIP712_DOMAIN_NAME, EIP712_DOMAIN_SCHEMA, EIP712_DOMAIN_VERSION } from '@0xproject/order-utils';
+import { eip712Utils } from '@0xproject/order-utils';
import { Order, SignedOrder } from '@0xproject/types';
import { BigNumber, signTypedDataUtils } from '@0xproject/utils';
import _ = require('lodash');
@@ -8,15 +8,6 @@ import { ExchangeContract } from '../contract_wrappers/generated/exchange';
import { assert } from './assert';
-const EIP712_ZEROEX_TRANSACTION_SCHEMA = {
- name: 'ZeroExTransaction',
- parameters: [
- { name: 'salt', type: 'uint256' },
- { name: 'signerAddress', type: 'address' },
- { name: 'data', type: 'bytes' },
- ],
-};
-
/**
* Transaction Encoder. Transaction messages exist for the purpose of calling methods on the Exchange contract
* in the context of another address. For example, UserA can encode and sign a fillOrder transaction and UserB
@@ -37,23 +28,11 @@ export class TransactionEncoder {
public getTransactionHex(data: string, salt: BigNumber, signerAddress: string): string {
const exchangeAddress = this._getExchangeContract().address;
const executeTransactionData = {
- salt: salt.toString(),
+ salt,
signerAddress,
data,
};
- const typedData = {
- types: {
- EIP712Domain: EIP712_DOMAIN_SCHEMA.parameters,
- ZeroExTransaction: EIP712_ZEROEX_TRANSACTION_SCHEMA.parameters,
- },
- domain: {
- name: EIP712_DOMAIN_NAME,
- version: EIP712_DOMAIN_VERSION,
- verifyingContract: exchangeAddress,
- },
- message: executeTransactionData,
- primaryType: EIP712_ZEROEX_TRANSACTION_SCHEMA.name,
- };
+ const typedData = eip712Utils.createZeroExTransactionTypedData(executeTransactionData, exchangeAddress);
const eip712MessageBuffer = signTypedDataUtils.signTypedDataHash(typedData);
const messageHex = `0x${eip712MessageBuffer.toString('hex')}`;
return messageHex;
diff --git a/packages/contracts/test/utils/transaction_factory.ts b/packages/contracts/test/utils/transaction_factory.ts
index 47880cca5..6e4cc4b2f 100644
--- a/packages/contracts/test/utils/transaction_factory.ts
+++ b/packages/contracts/test/utils/transaction_factory.ts
@@ -1,9 +1,4 @@
-import {
- EIP712_DOMAIN_NAME,
- EIP712_DOMAIN_SCHEMA,
- EIP712_DOMAIN_VERSION,
- generatePseudoRandomSalt,
-} from '@0xproject/order-utils';
+import { eip712Utils, generatePseudoRandomSalt } from '@0xproject/order-utils';
import { SignatureType } from '@0xproject/types';
import { signTypedDataUtils } from '@0xproject/utils';
import * as ethUtil from 'ethereumjs-util';
@@ -11,15 +6,6 @@ import * as ethUtil from 'ethereumjs-util';
import { signingUtils } from './signing_utils';
import { SignedTransaction } from './types';
-const EIP712_ZEROEX_TRANSACTION_SCHEMA = {
- name: 'ZeroExTransaction',
- parameters: [
- { name: 'salt', type: 'uint256' },
- { name: 'signerAddress', type: 'address' },
- { name: 'data', type: 'bytes' },
- ],
-};
-
export class TransactionFactory {
private readonly _signerBuff: Buffer;
private readonly _exchangeAddress: string;
@@ -33,30 +19,18 @@ export class TransactionFactory {
const salt = generatePseudoRandomSalt();
const signerAddress = `0x${this._signerBuff.toString('hex')}`;
const executeTransactionData = {
- salt: salt.toString(),
+ salt,
signerAddress,
data,
};
- const typedData = {
- types: {
- EIP712Domain: EIP712_DOMAIN_SCHEMA.parameters,
- ZeroExTransaction: EIP712_ZEROEX_TRANSACTION_SCHEMA.parameters,
- },
- domain: {
- name: EIP712_DOMAIN_NAME,
- version: EIP712_DOMAIN_VERSION,
- verifyingContract: this._exchangeAddress,
- },
- message: executeTransactionData,
- primaryType: EIP712_ZEROEX_TRANSACTION_SCHEMA.name,
- };
+
+ const typedData = eip712Utils.createZeroExTransactionTypedData(executeTransactionData, this._exchangeAddress);
const eip712MessageBuffer = signTypedDataUtils.signTypedDataHash(typedData);
const signature = signingUtils.signMessage(eip712MessageBuffer, this._privateKey, signatureType);
const signedTx = {
exchangeAddress: this._exchangeAddress,
signature: `0x${signature.toString('hex')}`,
...executeTransactionData,
- salt,
};
return signedTx;
}
diff --git a/packages/json-schemas/schemas/eip712_typed_data.ts b/packages/json-schemas/schemas/eip712_typed_data.ts
index 4c4664878..371ff8a78 100644
--- a/packages/json-schemas/schemas/eip712_typed_data.ts
+++ b/packages/json-schemas/schemas/eip712_typed_data.ts
@@ -1,5 +1,5 @@
export const eip712TypedData = {
- id: 'eip712TypedData',
+ id: '/eip712TypedData',
type: 'object',
properties: {
types: {
diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json
index a9d2fde8b..2555ad350 100644
--- a/packages/order-utils/CHANGELOG.json
+++ b/packages/order-utils/CHANGELOG.json
@@ -3,15 +3,15 @@
"version": "2.0.0",
"changes": [
{
- "note": "Added ecSignOrderAsync to first sign an order as EIP712 and fallback to EthSign",
+ "note": "Added `ecSignOrderAsync` to first sign an order as EIP712 and fallback to EthSign",
"pr": 1102
},
{
- "note": "Added ecSignTypedDataOrderAsync to sign an order exclusively as EIP712",
+ "note": "Added `ecSignTypedDataOrderAsync` to sign an order exclusively as EIP712",
"pr": 1102
},
{
- "note": "Rename ecSignOrderHashAsync to ecSignHashAsync removing SignerType parameter",
+ "note": "Rename `ecSignOrderHashAsync` to `ecSignHashAsync` removing `SignerType` parameter",
"pr": 1102
}
]
diff --git a/packages/order-utils/src/constants.ts b/packages/order-utils/src/constants.ts
index 5403606c3..7de20a696 100644
--- a/packages/order-utils/src/constants.ts
+++ b/packages/order-utils/src/constants.ts
@@ -13,16 +13,39 @@ export const constants = {
BASE_16: 16,
INFINITE_TIMESTAMP_SEC: new BigNumber(2524604400), // Close to infinite
ZERO_AMOUNT: new BigNumber(0),
-};
-
-export const EIP712_DOMAIN_NAME = '0x Protocol';
-export const EIP712_DOMAIN_VERSION = '2';
-
-export const EIP712_DOMAIN_SCHEMA = {
- name: 'EIP712Domain',
- parameters: [
- { name: 'name', type: 'string' },
- { name: 'version', type: 'string' },
- { name: 'verifyingContract', type: 'address' },
- ],
+ EIP712_DOMAIN_NAME: '0x Protocol',
+ EIP712_DOMAIN_VERSION: '2',
+ EIP712_DOMAIN_SCHEMA: {
+ name: 'EIP712Domain',
+ parameters: [
+ { name: 'name', type: 'string' },
+ { name: 'version', type: 'string' },
+ { name: 'verifyingContract', type: 'address' },
+ ],
+ },
+ EIP712_ORDER_SCHEMA: {
+ name: 'Order',
+ parameters: [
+ { name: 'makerAddress', type: 'address' },
+ { name: 'takerAddress', type: 'address' },
+ { name: 'feeRecipientAddress', type: 'address' },
+ { name: 'senderAddress', type: 'address' },
+ { name: 'makerAssetAmount', type: 'uint256' },
+ { name: 'takerAssetAmount', type: 'uint256' },
+ { name: 'makerFee', type: 'uint256' },
+ { name: 'takerFee', type: 'uint256' },
+ { name: 'expirationTimeSeconds', type: 'uint256' },
+ { name: 'salt', type: 'uint256' },
+ { name: 'makerAssetData', type: 'bytes' },
+ { name: 'takerAssetData', type: 'bytes' },
+ ],
+ },
+ EIP712_ZEROEX_TRANSACTION_SCHEMA: {
+ name: 'ZeroExTransaction',
+ parameters: [
+ { name: 'salt', type: 'uint256' },
+ { name: 'signerAddress', type: 'address' },
+ { name: 'data', type: 'bytes' },
+ ],
+ },
};
diff --git a/packages/order-utils/src/eip712_utils.ts b/packages/order-utils/src/eip712_utils.ts
new file mode 100644
index 000000000..43b421de7
--- /dev/null
+++ b/packages/order-utils/src/eip712_utils.ts
@@ -0,0 +1,75 @@
+import { EIP712Object, EIP712TypedData, EIP712Types, Order, ZeroExTransaction } from '@0xproject/types';
+import * as _ from 'lodash';
+
+import { constants } from './constants';
+
+export const eip712Utils = {
+ /**
+ * Creates a EIP712TypedData object specific to the 0x protocol for use with signTypedData.
+ * @param primaryType The primary type found in message
+ * @param types The additional types for the data in message
+ * @param message The contents of the message
+ * @param exchangeAddress The address of the exchange contract
+ * @return A typed data object
+ */
+ createTypedData: (
+ primaryType: string,
+ types: EIP712Types,
+ message: EIP712Object,
+ exchangeAddress: string,
+ ): EIP712TypedData => {
+ const typedData = {
+ types: {
+ EIP712Domain: constants.EIP712_DOMAIN_SCHEMA.parameters,
+ ...types,
+ },
+ domain: {
+ name: constants.EIP712_DOMAIN_NAME,
+ version: constants.EIP712_DOMAIN_VERSION,
+ verifyingContract: exchangeAddress,
+ },
+ message,
+ primaryType,
+ };
+ return typedData;
+ },
+ /**
+ * Creates an Order EIP712TypedData object for use with signTypedData.
+ * @param Order the order
+ * @return A typed data object
+ */
+ createOrderTypedData: (order: Order): EIP712TypedData => {
+ const normalizedOrder = _.mapValues(order, value => {
+ return !_.isString(value) ? value.toString() : value;
+ });
+ const typedData = eip712Utils.createTypedData(
+ constants.EIP712_ORDER_SCHEMA.name,
+ { Order: constants.EIP712_ORDER_SCHEMA.parameters },
+ normalizedOrder,
+ order.exchangeAddress,
+ );
+ return typedData;
+ },
+ /**
+ * Creates an ExecuteTransaction EIP712TypedData object for use with signTypedData and
+ * 0x Exchange executeTransaction.
+ * @param ZeroExTransaction the 0x transaction
+ * @param exchangeAddress The address of the exchange contract
+ * @return A typed data object
+ */
+ createZeroExTransactionTypedData: (
+ zeroExTransaction: ZeroExTransaction,
+ exchangeAddress: string,
+ ): EIP712TypedData => {
+ const normalizedTransaction = _.mapValues(zeroExTransaction, value => {
+ return !_.isString(value) ? value.toString() : value;
+ });
+ const typedData = eip712Utils.createTypedData(
+ constants.EIP712_ZEROEX_TRANSACTION_SCHEMA.name,
+ { ZeroExTransaction: constants.EIP712_ZEROEX_TRANSACTION_SCHEMA.parameters },
+ normalizedTransaction,
+ exchangeAddress,
+ );
+ return typedData;
+ },
+};
diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts
index 89a843d8f..2e05fdf2b 100644
--- a/packages/order-utils/src/index.ts
+++ b/packages/order-utils/src/index.ts
@@ -18,7 +18,8 @@ export { ExchangeTransferSimulator } from './exchange_transfer_simulator';
export { BalanceAndProxyAllowanceLazyStore } from './store/balance_and_proxy_allowance_lazy_store';
export { OrderFilledCancelledLazyStore } from './store/order_filled_cancelled_lazy_store';
-export { EIP712_DOMAIN_NAME, EIP712_DOMAIN_SCHEMA, EIP712_DOMAIN_VERSION } from './constants';
+export { constants } from './constants';
+export { eip712Utils } from './eip712_utils';
export { Provider, JSONRPCRequestPayload, JSONRPCErrorCallback, JSONRPCResponsePayload } from 'ethereum-types';
export {
@@ -34,6 +35,12 @@ export {
OrderStateValid,
OrderStateInvalid,
ExchangeContractErrs,
+ EIP712Parameter,
+ EIP712TypedData,
+ EIP712Types,
+ EIP712Object,
+ EIP712ObjectValue,
+ ZeroExTransaction,
} from '@0xproject/types';
export {
OrderError,
diff --git a/packages/order-utils/src/order_hash.ts b/packages/order-utils/src/order_hash.ts
index 37b9da811..a6dd6688c 100644
--- a/packages/order-utils/src/order_hash.ts
+++ b/packages/order-utils/src/order_hash.ts
@@ -4,28 +4,10 @@ import { signTypedDataUtils } from '@0xproject/utils';
import * as _ from 'lodash';
import { assert } from './assert';
-import { EIP712_DOMAIN_NAME, EIP712_DOMAIN_SCHEMA, EIP712_DOMAIN_VERSION } from './constants';
+import { eip712Utils } from './eip712_utils';
const INVALID_TAKER_FORMAT = 'instance.takerAddress is not of a type(s) string';
-export const EIP712_ORDER_SCHEMA = {
- name: 'Order',
- parameters: [
- { name: 'makerAddress', type: 'address' },
- { name: 'takerAddress', type: 'address' },
- { name: 'feeRecipientAddress', type: 'address' },
- { name: 'senderAddress', type: 'address' },
- { name: 'makerAssetAmount', type: 'uint256' },
- { name: 'takerAssetAmount', type: 'uint256' },
- { name: 'makerFee', type: 'uint256' },
- { name: 'takerFee', type: 'uint256' },
- { name: 'expirationTimeSeconds', type: 'uint256' },
- { name: 'salt', type: 'uint256' },
- { name: 'makerAssetData', type: 'bytes' },
- { name: 'takerAssetData', type: 'bytes' },
- ],
-};
-
export const orderHashUtils = {
/**
* Checks if the supplied hex encoded order hash is valid.
@@ -45,7 +27,7 @@ export const orderHashUtils = {
/**
* Computes the orderHash for a supplied order.
* @param order An object that conforms to the Order or SignedOrder interface definitions.
- * @return The resulting orderHash from hashing the supplied order.
+ * @return Hex encoded string orderHash from hashing the supplied order.
*/
getOrderHashHex(order: SignedOrder | Order): string {
try {
@@ -64,27 +46,12 @@ export const orderHashUtils = {
return orderHashHex;
},
/**
- * Computes the orderHash for a supplied order and returns it as a Buffer
+ * Computes the orderHash for a supplied order
* @param order An object that conforms to the Order or SignedOrder interface definitions.
- * @return The resulting orderHash from hashing the supplied order as a Buffer
+ * @return A Buffer containing the resulting orderHash from hashing the supplied order
*/
getOrderHashBuffer(order: SignedOrder | Order): Buffer {
- const normalizedOrder = _.mapValues(order, value => {
- return _.isObject(value) ? value.toString() : value;
- });
- const typedData = {
- types: {
- EIP712Domain: EIP712_DOMAIN_SCHEMA.parameters,
- Order: EIP712_ORDER_SCHEMA.parameters,
- },
- domain: {
- name: EIP712_DOMAIN_NAME,
- version: EIP712_DOMAIN_VERSION,
- verifyingContract: order.exchangeAddress,
- },
- message: normalizedOrder,
- primaryType: EIP712_ORDER_SCHEMA.name,
- };
+ const typedData = eip712Utils.createOrderTypedData(order);
const orderHashBuff = signTypedDataUtils.signTypedDataHash(typedData);
return orderHashBuff;
},
diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts
index 2d7fcfc9e..8c92b87dd 100644
--- a/packages/order-utils/src/signature_utils.ts
+++ b/packages/order-utils/src/signature_utils.ts
@@ -1,5 +1,5 @@
import { schemas } from '@0xproject/json-schemas';
-import { ECSignature, Order, SignatureType, ValidatorSignature } from '@0xproject/types';
+import { ECSignature, Order, SignatureType, SignedOrder, ValidatorSignature } from '@0xproject/types';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider } from 'ethereum-types';
import * as ethUtil from 'ethereumjs-util';
@@ -7,11 +7,11 @@ import * as _ from 'lodash';
import { artifacts } from './artifacts';
import { assert } from './assert';
-import { EIP712_DOMAIN_NAME, EIP712_DOMAIN_SCHEMA, EIP712_DOMAIN_VERSION } from './constants';
+import { eip712Utils } from './eip712_utils';
import { ExchangeContract } from './generated_contract_wrappers/exchange';
import { IValidatorContract } from './generated_contract_wrappers/i_validator';
import { IWalletContract } from './generated_contract_wrappers/i_wallet';
-import { EIP712_ORDER_SCHEMA, orderHashUtils } from './order_hash';
+import { orderHashUtils } from './order_hash';
import { OrderError } from './types';
import { utils } from './utils';
@@ -195,53 +195,45 @@ export const signatureUtils = {
},
/**
* Signs an order and returns its elliptic curve signature and signature type. First `eth_signTypedData` is requested
- * then a fallback to `eth_sign` if not available on this provider.
+ * then a fallback to `eth_sign` if not available on the supplied provider.
* @param order The Order to sign.
* @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
- * must be available via the Provider supplied to 0x.js.
- * @return A hex encoded string containing the Elliptic curve signature generated by signing the orderHash and the Signature Type.
+ * must be available via the supplied Provider.
+ * @return A SignedOrder containing the order and Elliptic curve signature with Signature Type.
*/
- async ecSignOrderAsync(provider: Provider, order: Order, signerAddress: string): Promise<string> {
+ async ecSignOrderAsync(provider: Provider, order: Order, signerAddress: string): Promise<SignedOrder> {
try {
- const signatureHex = signatureUtils.ecSignTypedDataOrderAsync(provider, order, signerAddress);
- return signatureHex;
+ const signedOrder = signatureUtils.ecSignTypedDataOrderAsync(provider, order, signerAddress);
+ return signedOrder;
} catch (err) {
// Fallback to using EthSign when ethSignTypedData is not supported
const orderHash = orderHashUtils.getOrderHashHex(order);
const signatureHex = await signatureUtils.ecSignHashAsync(provider, orderHash, signerAddress);
- return signatureHex;
+ const signedOrder = {
+ ...order,
+ signature: signatureHex,
+ };
+ return signedOrder;
}
},
/**
* Signs an order using `eth_signTypedData` and returns its elliptic curve signature and signature type.
* @param order The Order to sign.
* @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
- * must be available via the Provider supplied to 0x.js.
- * @return A hex encoded string containing the Elliptic curve signature generated by signing the order with `eth_signTypedData`
- * and the Signature Type.
+ * must be available via the supplied Provider.
+ * @return A SignedOrder containing the order and Elliptic curve signature with Signature Type.
*/
- async ecSignTypedDataOrderAsync(provider: Provider, order: Order, signerAddress: string): Promise<string> {
+ async ecSignTypedDataOrderAsync(provider: Provider, order: Order, signerAddress: string): Promise<SignedOrder> {
assert.isWeb3Provider('provider', provider);
assert.isETHAddressHex('signerAddress', signerAddress);
const web3Wrapper = new Web3Wrapper(provider);
await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper);
+ // Detect if Metamask to transition users to the MetamaskSubprovider
+ if ((provider as any).isMetaMask) {
+ throw new Error('Unsupported Provider, please use MetamaskSubprovider.');
+ }
const normalizedSignerAddress = signerAddress.toLowerCase();
- const normalizedOrder = _.mapValues(order, value => {
- return _.isObject(value) ? value.toString() : value;
- });
- const typedData = {
- types: {
- EIP712Domain: EIP712_DOMAIN_SCHEMA.parameters,
- Order: EIP712_ORDER_SCHEMA.parameters,
- },
- domain: {
- name: EIP712_DOMAIN_NAME,
- version: EIP712_DOMAIN_VERSION,
- verifyingContract: order.exchangeAddress,
- },
- message: normalizedOrder,
- primaryType: 'Order',
- };
+ const typedData = eip712Utils.createOrderTypedData(order);
const signature = await web3Wrapper.signTypedDataAsync(normalizedSignerAddress, typedData);
const ecSignatureRSV = parseSignatureHexAsRSV(signature);
const signatureBuffer = Buffer.concat([
@@ -251,14 +243,16 @@ export const signatureUtils = {
ethUtil.toBuffer(SignatureType.EIP712),
]);
const signatureHex = `0x${signatureBuffer.toString('hex')}`;
- return signatureHex;
+ return {
+ ...order,
+ signature: signatureHex,
+ };
},
/**
* Signs a hash and returns its elliptic curve signature and signature type.
- * This method currently supports TestRPC, Geth and Parity above and below V1.6.6
* @param msgHash Hex encoded message to sign.
* @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
- * must be available via the Provider supplied to 0x.js.
+ * must be available via the supplied Provider.
* @return A hex encoded string containing the Elliptic curve signature generated by signing the msgHash and the Signature Type.
*/
async ecSignHashAsync(provider: Provider, msgHash: string, signerAddress: string): Promise<string> {
@@ -268,6 +262,10 @@ export const signatureUtils = {
const web3Wrapper = new Web3Wrapper(provider);
await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper);
const normalizedSignerAddress = signerAddress.toLowerCase();
+ // Detect if Metamask to transition users to the MetamaskSubprovider
+ if ((provider as any).isMetaMask) {
+ throw new Error('Unsupported Provider, please use MetamaskSubprovider.');
+ }
const signature = await web3Wrapper.signMessageAsync(normalizedSignerAddress, msgHash);
const prefixedMsgHashHex = signatureUtils.addSignedMessagePrefix(msgHash);
diff --git a/packages/order-utils/test/eip712_utils_test.ts b/packages/order-utils/test/eip712_utils_test.ts
new file mode 100644
index 000000000..dc76595db
--- /dev/null
+++ b/packages/order-utils/test/eip712_utils_test.ts
@@ -0,0 +1,44 @@
+import { BigNumber } from '@0xproject/utils';
+import * as chai from 'chai';
+import 'mocha';
+
+import { constants } from '../src/constants';
+import { eip712Utils } from '../src/eip712_utils';
+
+import { chaiSetup } from './utils/chai_setup';
+
+chaiSetup.configure();
+const expect = chai.expect;
+
+describe('EIP712 Utils', () => {
+ describe('createTypedData', () => {
+ it('adds in the EIP712DomainSeparator', () => {
+ const primaryType = 'Test';
+ const typedData = eip712Utils.createTypedData(
+ primaryType,
+ { Test: [{ name: 'testValue', type: 'uint256' }] },
+ { testValue: '1' },
+ constants.NULL_ADDRESS,
+ );
+ expect(typedData.domain).to.not.be.undefined();
+ expect(typedData.types.EIP712Domain).to.not.be.undefined();
+ const domainObject = typedData.domain;
+ expect(domainObject.name).to.eq(constants.EIP712_DOMAIN_NAME);
+ expect(typedData.primaryType).to.eq(primaryType);
+ });
+ });
+ describe('createTypedData', () => {
+ it('adds in the EIP712DomainSeparator', () => {
+ const typedData = eip712Utils.createZeroExTransactionTypedData(
+ {
+ salt: new BigNumber('0'),
+ data: constants.NULL_BYTES,
+ signerAddress: constants.NULL_BYTES,
+ },
+ constants.NULL_ADDRESS,
+ );
+ expect(typedData.primaryType).to.eq(constants.EIP712_ZEROEX_TRANSACTION_SCHEMA.name);
+ expect(typedData.types.EIP712Domain).to.not.be.undefined();
+ });
+ });
+});
diff --git a/packages/order-utils/test/order_hash_test.ts b/packages/order-utils/test/order_hash_test.ts
index 3fdbbad21..fe44218d6 100644
--- a/packages/order-utils/test/order_hash_test.ts
+++ b/packages/order-utils/test/order_hash_test.ts
@@ -35,6 +35,20 @@ describe('Order hashing', () => {
const orderHash = orderHashUtils.getOrderHashHex(order);
expect(orderHash).to.be.equal(expectedOrderHash);
});
+ it('calculates the order hash if amounts are strings', async () => {
+ // It's common for developers using javascript to provide the amounts
+ // as strings. Since we eventually toString() the BigNumber
+ // before encoding we should result in the same orderHash in this scenario
+ // tslint:disable-next-line:no-unnecessary-type-assertion
+ const orderHash = orderHashUtils.getOrderHashHex({
+ ...order,
+ makerAssetAmount: '0',
+ takerAssetAmount: '0',
+ makerFee: '0',
+ takerFee: '0',
+ } as any);
+ expect(orderHash).to.be.equal(expectedOrderHash);
+ });
it('throws a readable error message if taker format is invalid', async () => {
const orderWithInvalidtakerFormat = {
...order,
diff --git a/packages/order-utils/test/signature_utils_test.ts b/packages/order-utils/test/signature_utils_test.ts
index 03354cd65..7f6987f6a 100644
--- a/packages/order-utils/test/signature_utils_test.ts
+++ b/packages/order-utils/test/signature_utils_test.ts
@@ -152,14 +152,14 @@ describe('Signature utils', () => {
ethUtil.toBuffer(SignatureType.EIP712),
]);
const signatureHex = `0x${signatureBuffer.toString('hex')}`;
- const eip712Signature = await signatureUtils.ecSignOrderAsync(provider, order, makerAddress);
+ const signedOrder = await signatureUtils.ecSignOrderAsync(provider, order, makerAddress);
const isValidSignature = await signatureUtils.isValidSignatureAsync(
provider,
orderHashHex,
- eip712Signature,
+ signedOrder.signature,
makerAddress,
);
- expect(signatureHex).to.eq(eip712Signature);
+ expect(signatureHex).to.eq(signedOrder.signature);
expect(isValidSignature).to.eq(true);
});
});
@@ -238,6 +238,51 @@ describe('Signature utils', () => {
expect(isValidSignature).to.be.true();
});
});
+ describe('#ecSignTypedDataOrderAsync', () => {
+ let makerAddress: string;
+ const fakeExchangeContractAddress = '0x1dc4c1cefef38a777b15aa20260a54e584b16c48';
+ let order: Order;
+ before(async () => {
+ const availableAddreses = await web3Wrapper.getAvailableAddressesAsync();
+ makerAddress = availableAddreses[0];
+ order = {
+ makerAddress,
+ takerAddress: constants.NULL_ADDRESS,
+ senderAddress: constants.NULL_ADDRESS,
+ feeRecipientAddress: constants.NULL_ADDRESS,
+ makerAssetData: constants.NULL_ADDRESS,
+ takerAssetData: constants.NULL_ADDRESS,
+ exchangeAddress: fakeExchangeContractAddress,
+ salt: new BigNumber(0),
+ makerFee: new BigNumber(0),
+ takerFee: new BigNumber(0),
+ makerAssetAmount: new BigNumber(0),
+ takerAssetAmount: new BigNumber(0),
+ expirationTimeSeconds: new BigNumber(0),
+ };
+ });
+ it('should return the correct Signature for signatureHex concatenated as R + S + V', async () => {
+ const expectedSignature =
+ '0x1cd472c439833774b55d248c31b6585f21aea1b9363ebb4ec58549e46b62eb5a6f696f5781f62de008ee7f77650ef940d99c97ec1dee67b3f5cea1bbfdfeb2eba602';
+ const fakeProvider = {
+ async sendAsync(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): Promise<void> {
+ if (payload.method === 'eth_signTypedData') {
+ const [address, typedData] = payload.params;
+ const signature = await web3Wrapper.signTypedDataAsync(address, typedData);
+ callback(null, {
+ id: 42,
+ jsonrpc: '2.0',
+ result: signature,
+ });
+ } else {
+ callback(null, { id: 42, jsonrpc: '2.0', result: [makerAddress] });
+ }
+ },
+ };
+ const signedOrder = await signatureUtils.ecSignTypedDataOrderAsync(fakeProvider, order, makerAddress);
+ expect(signedOrder.signature).to.equal(expectedSignature);
+ });
+ });
describe('#convertECSignatureToSignatureHex', () => {
const ecSignature: ECSignature = {
v: 27,
diff --git a/packages/subproviders/CHANGELOG.json b/packages/subproviders/CHANGELOG.json
index 6a6f7848b..387207b01 100644
--- a/packages/subproviders/CHANGELOG.json
+++ b/packages/subproviders/CHANGELOG.json
@@ -3,11 +3,11 @@
"version": "2.1.0",
"changes": [
{
- "note": "Add Metamask Subprovider to handle inconsistent JSON RPC behaviour",
+ "note": "Add `MetamaskSubprovider` to handle inconsistent JSON RPC behaviour",
"pr": 1102
},
{
- "note": "Add support for eth_signTypedData in Mnemonic, Private and EthLightWallet",
+ "note": "Add support for `eth_signTypedData` in wallets Mnemonic, Private and EthLightWallet",
"pr": 1102
}
]
diff --git a/packages/subproviders/src/index.ts b/packages/subproviders/src/index.ts
index 8b5446007..6a8100e68 100644
--- a/packages/subproviders/src/index.ts
+++ b/packages/subproviders/src/index.ts
@@ -48,6 +48,13 @@ 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';
diff --git a/packages/subproviders/src/subproviders/eth_lightwallet_subprovider.ts b/packages/subproviders/src/subproviders/eth_lightwallet_subprovider.ts
index e3afeff1b..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 this Subprovider 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,10 @@ 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;
}
/**
@@ -80,11 +81,11 @@ export class EthLightwalletSubprovider extends BaseWalletSubprovider {
* @param data the typed data object
* @return Signature hex string (order: rsv)
*/
- public async signTypedDataAsync(address: string, typedData: any): Promise<string> {
- let privKey = this._keystore.exportPrivateKey(address, this._pwDerivedKey);
- const privKeyWallet = new PrivateKeyWalletSubprovider(privKey);
- privKey = '';
- const result = privKeyWallet.signTypedDataAsync(address, typedData);
+ 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/metamask_subprovider.ts b/packages/subproviders/src/subproviders/metamask_subprovider.ts
index 724edd574..46fc2a9cd 100644
--- a/packages/subproviders/src/subproviders/metamask_subprovider.ts
+++ b/packages/subproviders/src/subproviders/metamask_subprovider.ts
@@ -18,7 +18,7 @@ export class MetamaskSubprovider extends Subprovider {
private readonly _web3Wrapper: Web3Wrapper;
private readonly _provider: Provider;
/**
- * Instantiates a new SignerSubprovider
+ * Instantiates a new MetamaskSubprovider
* @param provider Web3 provider that should handle all user account related requests
*/
constructor(provider: Provider) {
@@ -83,7 +83,9 @@ export class MetamaskSubprovider extends Subprovider {
case 'eth_signTypedData_v3':
[address, message] = payload.params;
try {
- // Metamask has namespaced signTypedData to v3 for an indeterminate period of time.
+ // 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>({
diff --git a/packages/subproviders/src/subproviders/mnemonic_wallet.ts b/packages/subproviders/src/subproviders/mnemonic_wallet.ts
index de99b632a..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)
@@ -109,16 +110,16 @@ export class MnemonicWalletSubprovider extends BaseWalletSubprovider {
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.
+ * 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: any): Promise<string> {
+ public async signTypedDataAsync(address: string, typedData: EIP712TypedData): Promise<string> {
if (_.isUndefined(typedData)) {
throw new Error(WalletSubproviderErrors.DataMissingForSignPersonalMessage);
}
diff --git a/packages/subproviders/src/subproviders/private_key_wallet.ts b/packages/subproviders/src/subproviders/private_key_wallet.ts
index 51409077d..96e4190ed 100644
--- a/packages/subproviders/src/subproviders/private_key_wallet.ts
+++ b/packages/subproviders/src/subproviders/private_key_wallet.ts
@@ -1,4 +1,5 @@
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';
@@ -95,7 +96,7 @@ export class PrivateKeyWalletSubprovider extends BaseWalletSubprovider {
* @param data the typed data object
* @return Signature hex string (order: rsv)
*/
- public async signTypedDataAsync(address: string, typedData: any): Promise<string> {
+ public async signTypedDataAsync(address: string, typedData: EIP712TypedData): Promise<string> {
if (_.isUndefined(typedData)) {
throw new Error(WalletSubproviderErrors.DataMissingForSignTypedData);
}
diff --git a/packages/subproviders/src/subproviders/signer.ts b/packages/subproviders/src/subproviders/signer.ts
index 6b519865f..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) {
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/types/CHANGELOG.json b/packages/types/CHANGELOG.json
index 65dd75101..53e1f3716 100644
--- a/packages/types/CHANGELOG.json
+++ b/packages/types/CHANGELOG.json
@@ -5,6 +5,10 @@
{
"note": "Added `EIP712Parameter` `EIP712Types` `EIP712TypedData` for EIP712 signing",
"pr": 1102
+ },
+ {
+ "note": "Added `ZeroExTransaction` type for Exchange executeTransaction",
+ "pr": 1102
}
]
},
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
index d57bdfb6f..6bc966ba1 100644
--- a/packages/types/src/index.ts
+++ b/packages/types/src/index.ts
@@ -42,6 +42,15 @@ export interface SignedOrder extends Order {
}
/**
+ * ZeroExTransaction for use with 0x Exchange executeTransaction
+ */
+export interface ZeroExTransaction {
+ salt: BigNumber;
+ signerAddress: string;
+ data: string;
+}
+
+/**
* Elliptic Curve signature
*/
export interface ECSignature {
@@ -598,9 +607,16 @@ export interface EIP712Parameter {
export interface EIP712Types {
[key: string]: EIP712Parameter[];
}
+
+export type EIP712ObjectValue = string | number | EIP712Object;
+
+export interface EIP712Object {
+ [key: string]: EIP712ObjectValue;
+}
+
export interface EIP712TypedData {
types: EIP712Types;
- domain: any;
- message: any;
+ domain: EIP712Object;
+ message: EIP712Object;
primaryType: string;
}
diff --git a/packages/utils/src/sign_typed_data_utils.ts b/packages/utils/src/sign_typed_data_utils.ts
index b72fd099b..657a59ed0 100644
--- a/packages/utils/src/sign_typed_data_utils.ts
+++ b/packages/utils/src/sign_typed_data_utils.ts
@@ -1,7 +1,8 @@
import * as ethUtil from 'ethereumjs-util';
import * as ethers from 'ethers';
+import * as _ from 'lodash';
-import { EIP712TypedData, EIP712Types } from '@0xproject/types';
+import { EIP712Object, EIP712ObjectValue, EIP712TypedData, EIP712Types } from '@0xproject/types';
export const signTypedDataUtils = {
/**
@@ -42,32 +43,40 @@ export const signTypedDataUtils = {
}
return result;
},
- _encodeData(primaryType: string, data: any, types: EIP712Types): string {
+ _encodeData(primaryType: string, data: EIP712Object, types: EIP712Types): string {
const encodedTypes = ['bytes32'];
- const encodedValues = [signTypedDataUtils._typeHash(primaryType, types)];
+ const encodedValues: Array<Buffer | EIP712ObjectValue> = [signTypedDataUtils._typeHash(primaryType, types)];
for (const field of types[primaryType]) {
- let value = data[field.name];
+ const value = data[field.name];
if (field.type === 'string' || field.type === 'bytes') {
- value = ethUtil.sha3(value);
+ const hashValue = ethUtil.sha3(value as string);
encodedTypes.push('bytes32');
- encodedValues.push(value);
+ encodedValues.push(hashValue);
} else if (types[field.type] !== undefined) {
encodedTypes.push('bytes32');
- value = ethUtil.sha3(signTypedDataUtils._encodeData(field.type, value, types));
- encodedValues.push(value);
+ const hashValue = ethUtil.sha3(
+ // tslint:disable-next-line:no-unnecessary-type-assertion
+ signTypedDataUtils._encodeData(field.type, value as EIP712Object, types),
+ );
+ encodedValues.push(hashValue);
} else if (field.type.lastIndexOf(']') === field.type.length - 1) {
throw new Error('Arrays currently unimplemented in encodeData');
} else {
encodedTypes.push(field.type);
- encodedValues.push(value);
+ const normalizedValue = signTypedDataUtils._normalizeValue(field.type, value);
+ encodedValues.push(normalizedValue);
}
}
return ethers.utils.defaultAbiCoder.encode(encodedTypes, encodedValues);
},
+ _normalizeValue(type: string, value: any): EIP712ObjectValue {
+ const normalizedValue = type === 'uint256' && _.isObject(value) && value.isBigNumber ? value.toString() : value;
+ return normalizedValue;
+ },
_typeHash(primaryType: string, types: EIP712Types): Buffer {
return ethUtil.sha3(signTypedDataUtils._encodeType(primaryType, types));
},
- _structHash(primaryType: string, data: any, types: EIP712Types): Buffer {
+ _structHash(primaryType: string, data: EIP712Object, types: EIP712Types): Buffer {
return ethUtil.sha3(signTypedDataUtils._encodeData(primaryType, data, types));
},
};
diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts
index df6879a48..034bafb5f 100644
--- a/packages/web3-wrapper/src/web3_wrapper.ts
+++ b/packages/web3-wrapper/src/web3_wrapper.ts
@@ -318,9 +318,9 @@ export class Web3Wrapper {
* Sign an EIP712 typed data message with a specific address's private key (`eth_signTypedData`)
* @param address Address of signer
* @param typedData Typed data message to sign
- * @returns Signature string (might be VRS or RSV depending on the Signer)
+ * @returns Signature string (as RSV)
*/
- public async signTypedDataAsync(address: string, typedData: object): Promise<string> {
+ public async signTypedDataAsync(address: string, typedData: any): Promise<string> {
assert.isETHAddressHex('address', address);
assert.doesConformToSchema('typedData', typedData, schemas.eip712TypedData);
const signData = await this.sendRawPayloadAsync<string>({