diff options
author | Brandon Millman <brandon.millman@gmail.com> | 2018-10-10 08:04:54 +0800 |
---|---|---|
committer | Brandon Millman <brandon.millman@gmail.com> | 2018-10-10 08:04:54 +0800 |
commit | 8155d311af04339c105f1a29b74f1ddbced85197 (patch) | |
tree | 9b345f05561e806b6d89cec26b371aadafb109ed /packages/order-utils/src | |
parent | cd8e6d9cdf7d6aa1e811bb7d9d7017da342907ed (diff) | |
parent | bd06ebde8d1a5caf138ad3b36f9a5c5f255f2312 (diff) | |
download | dexon-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/order-utils/src')
-rw-r--r-- | packages/order-utils/src/constants.ts | 35 | ||||
-rw-r--r-- | packages/order-utils/src/eip712_utils.ts | 162 | ||||
-rw-r--r-- | packages/order-utils/src/index.ts | 23 | ||||
-rw-r--r-- | packages/order-utils/src/order_factory.ts | 9 | ||||
-rw-r--r-- | packages/order-utils/src/order_hash.ts | 33 | ||||
-rw-r--r-- | packages/order-utils/src/signature_utils.ts | 163 | ||||
-rw-r--r-- | packages/order-utils/src/types.ts | 19 |
7 files changed, 226 insertions, 218 deletions
diff --git a/packages/order-utils/src/constants.ts b/packages/order-utils/src/constants.ts index c23578c20..7de20a696 100644 --- a/packages/order-utils/src/constants.ts +++ b/packages/order-utils/src/constants.ts @@ -13,4 +13,39 @@ export const constants = { BASE_16: 16, INFINITE_TIMESTAMP_SEC: new BigNumber(2524604400), // Close to infinite ZERO_AMOUNT: new BigNumber(0), + 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 index b303c93dc..56f736500 100644 --- a/packages/order-utils/src/eip712_utils.ts +++ b/packages/order-utils/src/eip712_utils.ts @@ -1,109 +1,83 @@ -import ethUtil = require('ethereumjs-util'); +import { assert } from '@0xproject/assert'; +import { schemas } from '@0xproject/json-schemas'; +import { EIP712Object, EIP712TypedData, EIP712Types, Order, ZeroExTransaction } from '@0xproject/types'; import * as _ from 'lodash'; -import { crypto } from './crypto'; -import { EIP712Schema, EIP712Types } from './types'; - -const EIP191_PREFIX = '\x19\x01'; -const EIP712_DOMAIN_NAME = '0x Protocol'; -const EIP712_DOMAIN_VERSION = '2'; -const EIP712_VALUE_LENGTH = 32; - -const EIP712_DOMAIN_SCHEMA: EIP712Schema = { - name: 'EIP712Domain', - parameters: [ - { name: 'name', type: EIP712Types.String }, - { name: 'version', type: EIP712Types.String }, - { name: 'verifyingContract', type: EIP712Types.Address }, - ], -}; +import { constants } from './constants'; export const eip712Utils = { /** - * Compiles the EIP712Schema and returns the hash of the schema. - * @param schema The EIP712 schema. - * @return The hash of the compiled schema - */ - compileSchema(schema: EIP712Schema): Buffer { - const eip712Schema = eip712Utils._encodeType(schema); - const eip712SchemaHashBuffer = crypto.solSHA3([eip712Schema]); - return eip712SchemaHashBuffer; - }, - /** - * Merges the EIP712 hash of a struct with the DomainSeparator for 0x v2. - * @param hashStruct the EIP712 hash of a struct - * @param contractAddress the exchange contract address - * @return The hash of an EIP712 message with domain separator prefixed - */ - createEIP712Message(hashStruct: Buffer, contractAddress: string): Buffer { - const domainSeparatorHashBuffer = eip712Utils._getDomainSeparatorHashBuffer(contractAddress); - const messageBuff = crypto.solSHA3([EIP191_PREFIX, domainSeparatorHashBuffer, hashStruct]); - return messageBuff; - }, - /** - * Pad an address to 32 bytes - * @param address Address to pad - * @return padded address + * 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 */ - pad32Address(address: string): Buffer { - const addressBuffer = ethUtil.toBuffer(address); - const addressPadded = eip712Utils.pad32Buffer(addressBuffer); - return addressPadded; + createTypedData: ( + primaryType: string, + types: EIP712Types, + message: EIP712Object, + exchangeAddress: string, + ): EIP712TypedData => { + assert.isETHAddressHex('exchangeAddress', exchangeAddress); + assert.isString('primaryType', primaryType); + 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, + }; + assert.doesConformToSchema('typedData', typedData, schemas.eip712TypedDataSchema); + return typedData; }, /** - * Pad an buffer to 32 bytes - * @param buffer Address to pad - * @return padded buffer + * Creates an Order EIP712TypedData object for use with signTypedData. + * @param Order the order + * @return A typed data object */ - pad32Buffer(buffer: Buffer): Buffer { - const bufferPadded = ethUtil.setLengthLeft(buffer, EIP712_VALUE_LENGTH); - return bufferPadded; + createOrderTypedData: (order: Order): EIP712TypedData => { + assert.doesConformToSchema('order', order, schemas.orderSchema, [schemas.hexSchema]); + 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; }, /** - * Hash together a EIP712 schema with the corresponding data - * @param schema EIP712-compliant schema - * @param data Data the complies to the schema - * @return A buffer containing the SHA256 hash of the schema and encoded data + * 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 */ - structHash(schema: EIP712Schema, data: { [key: string]: any }): Buffer { - const encodedData = eip712Utils._encodeData(schema, data); - const schemaHash = eip712Utils.compileSchema(schema); - const hashBuffer = crypto.solSHA3([schemaHash, ...encodedData]); - return hashBuffer; - }, - _getDomainSeparatorSchemaBuffer(): Buffer { - return eip712Utils.compileSchema(EIP712_DOMAIN_SCHEMA); - }, - _getDomainSeparatorHashBuffer(exchangeAddress: string): Buffer { - const domainSeparatorSchemaBuffer = eip712Utils._getDomainSeparatorSchemaBuffer(); - const encodedData = eip712Utils._encodeData(EIP712_DOMAIN_SCHEMA, { - name: EIP712_DOMAIN_NAME, - version: EIP712_DOMAIN_VERSION, - verifyingContract: exchangeAddress, + createZeroExTransactionTypedData: ( + zeroExTransaction: ZeroExTransaction, + exchangeAddress: string, + ): EIP712TypedData => { + assert.isETHAddressHex('exchangeAddress', exchangeAddress); + assert.doesConformToSchema('zeroExTransaction', zeroExTransaction, schemas.zeroExTransactionSchema); + const normalizedTransaction = _.mapValues(zeroExTransaction, value => { + return !_.isString(value) ? value.toString() : value; }); - const domainSeparatorHashBuff2 = crypto.solSHA3([domainSeparatorSchemaBuffer, ...encodedData]); - return domainSeparatorHashBuff2; - }, - _encodeType(schema: EIP712Schema): string { - const namedTypes = _.map(schema.parameters, ({ name, type }) => `${type} ${name}`); - const namedTypesJoined = namedTypes.join(','); - const encodedType = `${schema.name}(${namedTypesJoined})`; - return encodedType; - }, - _encodeData(schema: EIP712Schema, data: { [key: string]: any }): any { - const encodedValues = []; - for (const parameter of schema.parameters) { - const value = data[parameter.name]; - if (parameter.type === EIP712Types.String || parameter.type === EIP712Types.Bytes) { - encodedValues.push(crypto.solSHA3([ethUtil.toBuffer(value)])); - } else if (parameter.type === EIP712Types.Uint256) { - encodedValues.push(value); - } else if (parameter.type === EIP712Types.Address) { - encodedValues.push(eip712Utils.pad32Address(value)); - } else { - throw new Error(`Unable to encode ${parameter.type}`); - } - } - return encodedValues; + 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 1553647c6..dbb782b85 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -2,7 +2,6 @@ export { orderHashUtils } from './order_hash'; export { signatureUtils } from './signature_utils'; export { generatePseudoRandomSalt } from './salt'; export { assetDataUtils } from './asset_data_utils'; -export { eip712Utils } from './eip712_utils'; export { marketUtils } from './market_utils'; export { rateUtils } from './rate_utils'; export { sortingUtils } from './sorting_utils'; @@ -19,7 +18,17 @@ 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 { Provider, JSONRPCRequestPayload, JSONRPCErrorCallback, JSONRPCResponsePayload } from 'ethereum-types'; +export { constants } from './constants'; +export { eip712Utils } from './eip712_utils'; + +export { + Provider, + JSONRPCRequestPayload, + JSONRPCErrorCallback, + JSONRPCResponsePayload, + JSONRPCResponseError, +} from 'ethereum-types'; + export { SignedOrder, Order, @@ -29,17 +38,19 @@ export { ERC20AssetData, ERC721AssetData, AssetProxyId, - SignerType, SignatureType, OrderStateValid, OrderStateInvalid, ExchangeContractErrs, + EIP712Parameter, + EIP712TypedData, + EIP712Types, + EIP712Object, + EIP712ObjectValue, + ZeroExTransaction, } from '@0xproject/types'; export { OrderError, - EIP712Parameter, - EIP712Schema, - EIP712Types, TradeSide, TransferType, FindFeeOrdersThatCoverFeesForTargetOrdersOpts, diff --git a/packages/order-utils/src/order_factory.ts b/packages/order-utils/src/order_factory.ts index b1292903a..0f0cd6046 100644 --- a/packages/order-utils/src/order_factory.ts +++ b/packages/order-utils/src/order_factory.ts @@ -1,4 +1,4 @@ -import { Order, SignedOrder, SignerType } from '@0xproject/types'; +import { Order, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Provider } from 'ethereum-types'; import * as _ from 'lodash'; @@ -71,12 +71,7 @@ export const orderFactory = { createOrderOpts, ); const orderHash = orderHashUtils.getOrderHashHex(order); - const signature = await signatureUtils.ecSignOrderHashAsync( - provider, - orderHash, - makerAddress, - SignerType.Default, - ); + const signature = await signatureUtils.ecSignHashAsync(provider, orderHash, makerAddress); const signedOrder: SignedOrder = _.assign(order, { signature }); return signedOrder; }, diff --git a/packages/order-utils/src/order_hash.ts b/packages/order-utils/src/order_hash.ts index 8e98f8767..b523a3523 100644 --- a/packages/order-utils/src/order_hash.ts +++ b/packages/order-utils/src/order_hash.ts @@ -1,31 +1,13 @@ import { schemas, SchemaValidator } from '@0xproject/json-schemas'; import { Order, SignedOrder } from '@0xproject/types'; +import { signTypedDataUtils } from '@0xproject/utils'; import * as _ from 'lodash'; import { assert } from './assert'; import { eip712Utils } from './eip712_utils'; -import { EIP712Schema, EIP712Types } from './types'; const INVALID_TAKER_FORMAT = 'instance.takerAddress is not of a type(s) string'; -const EIP712_ORDER_SCHEMA: EIP712Schema = { - name: 'Order', - parameters: [ - { name: 'makerAddress', type: EIP712Types.Address }, - { name: 'takerAddress', type: EIP712Types.Address }, - { name: 'feeRecipientAddress', type: EIP712Types.Address }, - { name: 'senderAddress', type: EIP712Types.Address }, - { name: 'makerAssetAmount', type: EIP712Types.Uint256 }, - { name: 'takerAssetAmount', type: EIP712Types.Uint256 }, - { name: 'makerFee', type: EIP712Types.Uint256 }, - { name: 'takerFee', type: EIP712Types.Uint256 }, - { name: 'expirationTimeSeconds', type: EIP712Types.Uint256 }, - { name: 'salt', type: EIP712Types.Uint256 }, - { name: 'makerAssetData', type: EIP712Types.Bytes }, - { name: 'takerAssetData', type: EIP712Types.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,16 +46,13 @@ 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 orderParamsHashBuff = eip712Utils.structHash(EIP712_ORDER_SCHEMA, order); - const orderHashBuff = eip712Utils.createEIP712Message(orderParamsHashBuff, order.exchangeAddress); + const typedData = eip712Utils.createOrderTypedData(order); + const orderHashBuff = signTypedDataUtils.generateTypedDataHash(typedData); return orderHashBuff; }, - _getOrderSchemaBuffer(): Buffer { - return eip712Utils.compileSchema(EIP712_ORDER_SCHEMA); - }, }; diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts index 3b656d3fc..372d210d0 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, SignatureType, SignerType, 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,9 +7,11 @@ import * as _ from 'lodash'; import { artifacts } from './artifacts'; import { assert } from './assert'; +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 { orderHashUtils } from './order_hash'; import { OrderError } from './types'; import { utils } from './utils'; @@ -49,7 +51,7 @@ export const signatureUtils = { case SignatureType.EthSign: { const ecSignature = signatureUtils.parseECSignature(signature); - const prefixedMessageHex = signatureUtils.addSignedMessagePrefix(data, SignerType.Default); + const prefixedMessageHex = signatureUtils.addSignedMessagePrefix(data); return signatureUtils.isValidECSignature(prefixedMessageHex, ecSignature, signerAddress); } @@ -192,36 +194,90 @@ export const signatureUtils = { } }, /** - * Signs an orderHash and returns it's elliptic curve signature and signature type. - * This method currently supports TestRPC, Geth and Parity above and below V1.6.6 - * @param orderHash Hex encoded orderHash to sign. + * Signs an order and returns a SignedOrder. First `eth_signTypedData` is requested + * 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. - * @param signerType Different signers add/require different prefixes to be prepended to the message being signed. - * Since we cannot know ahead of time which signer you are using, you must supply a SignerType. - * @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 ecSignOrderHashAsync( - provider: Provider, - orderHash: string, - signerAddress: string, - signerType: SignerType, - ): Promise<string> { + async ecSignOrderAsync(provider: Provider, order: Order, signerAddress: string): Promise<SignedOrder> { + assert.doesConformToSchema('order', order, schemas.orderSchema, [schemas.hexSchema]); + try { + const signedOrder = await signatureUtils.ecSignTypedDataOrderAsync(provider, order, signerAddress); + return signedOrder; + } catch (err) { + // HACK: We are unable to handle specific errors thrown since provider is not an object + // under our control. It could be Metamask Web3, Ethers, or any general RPC provider. + // We check for a user denying the signature request in a way that supports Metamask and + // Coinbase Wallet. Unfortunately for signers with a different error message, + // they will receive two signature requests. + if (err.message.includes('User denied message signature')) { + throw err; + } + const orderHash = orderHashUtils.getOrderHashHex(order); + const signatureHex = await signatureUtils.ecSignHashAsync(provider, orderHash, signerAddress); + const signedOrder = { + ...order, + signature: signatureHex, + }; + return signedOrder; + } + }, + /** + * Signs an order using `eth_signTypedData` and returns a SignedOrder. + * @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 supplied Provider. + * @return A SignedOrder containing the order and Elliptic curve signature with Signature Type. + */ + async ecSignTypedDataOrderAsync(provider: Provider, order: Order, signerAddress: string): Promise<SignedOrder> { assert.isWeb3Provider('provider', provider); - assert.isHexString('orderHash', orderHash); assert.isETHAddressHex('signerAddress', signerAddress); + assert.doesConformToSchema('order', order, schemas.orderSchema, [schemas.hexSchema]); const web3Wrapper = new Web3Wrapper(provider); await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper); const normalizedSignerAddress = signerAddress.toLowerCase(); - - let msgHashHex = orderHash; - const prefixedMsgHashHex = signatureUtils.addSignedMessagePrefix(orderHash, signerType); - // Metamask incorrectly implements eth_sign and does not prefix the message as per the spec - // Source: https://github.com/MetaMask/metamask-extension/commit/a9d36860bec424dcee8db043d3e7da6a5ff5672e - if (signerType === SignerType.Metamask) { - msgHashHex = prefixedMsgHashHex; + const typedData = eip712Utils.createOrderTypedData(order); + try { + const signature = await web3Wrapper.signTypedDataAsync(normalizedSignerAddress, typedData); + const ecSignatureRSV = parseSignatureHexAsRSV(signature); + const signatureBuffer = Buffer.concat([ + ethUtil.toBuffer(ecSignatureRSV.v), + ethUtil.toBuffer(ecSignatureRSV.r), + ethUtil.toBuffer(ecSignatureRSV.s), + ethUtil.toBuffer(SignatureType.EIP712), + ]); + const signatureHex = `0x${signatureBuffer.toString('hex')}`; + return { + ...order, + signature: signatureHex, + }; + } catch (err) { + // Detect if Metamask to transition users to the MetamaskSubprovider + if ((provider as any).isMetaMask) { + throw new Error(OrderError.InvalidMetamaskSigner); + } else { + throw err; + } } - const signature = await web3Wrapper.signMessageAsync(normalizedSignerAddress, msgHashHex); + }, + /** + * Signs a hash using `eth_sign` and returns its elliptic curve signature and signature type. + * @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 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> { + assert.isWeb3Provider('provider', provider); + assert.isHexString('msgHash', msgHash); + assert.isETHAddressHex('signerAddress', signerAddress); + const web3Wrapper = new Web3Wrapper(provider); + await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper); + const normalizedSignerAddress = signerAddress.toLowerCase(); + const signature = await web3Wrapper.signMessageAsync(normalizedSignerAddress, msgHash); + const prefixedMsgHashHex = signatureUtils.addSignedMessagePrefix(msgHash); // HACK: There is no consensus on whether the signatureHex string should be formatted as // v + r + s OR r + s + v, and different clients (even different versions of the same client) @@ -238,10 +294,7 @@ export const signatureUtils = { normalizedSignerAddress, ); if (isValidRSVSignature) { - const convertedSignatureHex = signatureUtils.convertECSignatureToSignatureHex( - ecSignatureRSV, - signerType, - ); + const convertedSignatureHex = signatureUtils.convertECSignatureToSignatureHex(ecSignatureRSV); return convertedSignatureHex; } } @@ -253,41 +306,30 @@ export const signatureUtils = { normalizedSignerAddress, ); if (isValidVRSSignature) { - const convertedSignatureHex = signatureUtils.convertECSignatureToSignatureHex( - ecSignatureVRS, - signerType, - ); + const convertedSignatureHex = signatureUtils.convertECSignatureToSignatureHex(ecSignatureVRS); return convertedSignatureHex; } } - - throw new Error(OrderError.InvalidSignature); + // Detect if Metamask to transition users to the MetamaskSubprovider + if ((provider as any).isMetaMask) { + throw new Error(OrderError.InvalidMetamaskSigner); + } else { + throw new Error(OrderError.InvalidSignature); + } }, /** - * Combines ECSignature with V,R,S and the relevant signature type for use in 0x protocol + * Combines ECSignature with V,R,S and the EthSign signature type for use in 0x protocol * @param ecSignature The ECSignature of the signed data - * @param signerType The SignerType of the signed data * @return Hex encoded string of signature (v,r,s) with Signature Type */ - convertECSignatureToSignatureHex(ecSignature: ECSignature, signerType: SignerType): string { + convertECSignatureToSignatureHex(ecSignature: ECSignature): string { const signatureBuffer = Buffer.concat([ ethUtil.toBuffer(ecSignature.v), ethUtil.toBuffer(ecSignature.r), ethUtil.toBuffer(ecSignature.s), ]); const signatureHex = `0x${signatureBuffer.toString('hex')}`; - let signatureType; - switch (signerType) { - case SignerType.Metamask: - case SignerType.Ledger: - case SignerType.Default: { - signatureType = SignatureType.EthSign; - break; - } - default: - throw new Error(`Unrecognized SignerType: ${signerType}`); - } - const signatureWithType = signatureUtils.convertToSignatureWithType(signatureHex, signatureType); + const signatureWithType = signatureUtils.convertToSignatureWithType(signatureHex, SignatureType.EthSign); return signatureWithType; }, /** @@ -304,28 +346,17 @@ export const signatureUtils = { /** * Adds the relevant prefix to the message being signed. * @param message Message to sign - * @param signerType The type of message prefix to add for a given SignerType. Different signers expect - * specific message prefixes. * @return Prefixed message */ - addSignedMessagePrefix(message: string, signerType: SignerType = SignerType.Default): string { + addSignedMessagePrefix(message: string): string { assert.isString('message', message); - assert.doesBelongToStringEnum('signerType', signerType, SignerType); - switch (signerType) { - case SignerType.Metamask: - case SignerType.Ledger: - case SignerType.Default: { - const msgBuff = ethUtil.toBuffer(message); - const prefixedMsgBuff = ethUtil.hashPersonalMessage(msgBuff); - const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); - return prefixedMsgHex; - } - default: - throw new Error(`Unrecognized SignerType: ${signerType}`); - } + const msgBuff = ethUtil.toBuffer(message); + const prefixedMsgBuff = ethUtil.hashPersonalMessage(msgBuff); + const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); + return prefixedMsgHex; }, /** - * Parse a 0x protocol hex-encoded signature string into it's ECSignature components + * Parse a 0x protocol hex-encoded signature string into its ECSignature components * @param signature A hex encoded ecSignature 0x Protocol signature * @return An ECSignature object with r,s,v parameters */ diff --git a/packages/order-utils/src/types.ts b/packages/order-utils/src/types.ts index a843efaa4..5b13dd754 100644 --- a/packages/order-utils/src/types.ts +++ b/packages/order-utils/src/types.ts @@ -2,6 +2,7 @@ import { BigNumber } from '@0xproject/utils'; export enum OrderError { InvalidSignature = 'INVALID_SIGNATURE', + InvalidMetamaskSigner = "MetaMask provider must be wrapped in a MetamaskSubprovider (from the '@0xproject/subproviders' package) in order to work with this method.", } export enum TradeSide { @@ -14,24 +15,6 @@ export enum TransferType { Fee = 'fee', } -export interface EIP712Parameter { - name: string; - type: EIP712Types; -} - -export interface EIP712Schema { - name: string; - parameters: EIP712Parameter[]; -} - -export enum EIP712Types { - Address = 'address', - Bytes = 'bytes', - Bytes32 = 'bytes32', - String = 'string', - Uint256 = 'uint256', -} - export interface CreateOrderOpts { takerAddress?: string; senderAddress?: string; |