diff options
author | Fabio Berger <me@fabioberger.com> | 2018-06-20 19:25:29 +0800 |
---|---|---|
committer | Fabio Berger <me@fabioberger.com> | 2018-06-20 19:25:29 +0800 |
commit | 2ffab2218584fac5d3ea2b264a7c746d963083d9 (patch) | |
tree | f7558147b811c06300e2c7697591466ae7cf48ba /packages/order-utils/src | |
parent | 5541327968ad6f974a37c49057253746f738a709 (diff) | |
parent | 096eaa20d785baee07b06b3f0848797e470fbda0 (diff) | |
download | dexon-0x-contracts-2ffab2218584fac5d3ea2b264a7c746d963083d9.tar.gz dexon-0x-contracts-2ffab2218584fac5d3ea2b264a7c746d963083d9.tar.zst dexon-0x-contracts-2ffab2218584fac5d3ea2b264a7c746d963083d9.zip |
Merge branch 'v2-prototype' into feature/combinatorial-testing
* v2-prototype: (22 commits)
Fix closing parens in liborder
Update after rebase
ERC721Proxy Always call safeTransferFrom
Rename makerEpoch => orderEpoch
Make cancelOrdersUpTo compatible with sender abstraction
Update PR template
Use Image component instead of img tag
Assembler orderHash function
Optimize and remove redundant encodePacked
Fix linting issue
Fix bug where we do fetch balances on wallet login
Check network state immediately instead of waiting for delay
Fix onboarding persisting when changing routes
Consolidate account state messaging logic
Only elevate wallet zIndex when onboarding is in progress
Rebase and update feedback
Run linter
Add Portal v2 logging
Simplified handling of source < 32 edge case
Basic EIP712 encoder
...
Diffstat (limited to 'packages/order-utils/src')
-rw-r--r-- | packages/order-utils/src/eip712_utils.ts | 93 | ||||
-rw-r--r-- | packages/order-utils/src/index.ts | 3 | ||||
-rw-r--r-- | packages/order-utils/src/order_hash.ts | 87 | ||||
-rw-r--r-- | packages/order-utils/src/types.ts | 18 |
4 files changed, 139 insertions, 62 deletions
diff --git a/packages/order-utils/src/eip712_utils.ts b/packages/order-utils/src/eip712_utils.ts new file mode 100644 index 000000000..2594e6d6d --- /dev/null +++ b/packages/order-utils/src/eip712_utils.ts @@ -0,0 +1,93 @@ +import ethUtil = require('ethereumjs-util'); +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 }, + ], +}; + +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; + }, + pad32Address(address: string): Buffer { + const addressBuffer = ethUtil.toBuffer(address); + const addressPadded = EIP712Utils.pad32Buffer(addressBuffer); + return addressPadded; + }, + pad32Buffer(buffer: Buffer): Buffer { + const bufferPadded = ethUtil.setLengthLeft(buffer, EIP712_VALUE_LENGTH); + return bufferPadded; + }, + _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, + }); + 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; + }, + 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; + }, +}; diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index 592b9b7f6..b4a7a6b67 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -13,12 +13,13 @@ export { orderFactory } from './order_factory'; export { constants } from './constants'; export { crypto } from './crypto'; export { generatePseudoRandomSalt } from './salt'; -export { OrderError, MessagePrefixType, MessagePrefixOpts } from './types'; +export { OrderError, MessagePrefixType, MessagePrefixOpts, EIP712Parameter, EIP712Schema, EIP712Types } from './types'; export { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher'; export { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher'; export { BalanceAndProxyAllowanceLazyStore } from './store/balance_and_proxy_allowance_lazy_store'; export { RemainingFillableCalculator } from './remaining_fillable_calculator'; export { OrderStateUtils } from './order_state_utils'; export { assetProxyUtils } from './asset_proxy_utils'; +export { EIP712Utils } from './eip712_utils'; export { OrderValidationUtils } from './order_validation_utils'; export { ExchangeTransferSimulator } from './exchange_transfer_simulator'; diff --git a/packages/order-utils/src/order_hash.ts b/packages/order-utils/src/order_hash.ts index b9d0b43aa..54c500653 100644 --- a/packages/order-utils/src/order_hash.ts +++ b/packages/order-utils/src/order_hash.ts @@ -1,14 +1,31 @@ import { schemas, SchemaValidator } from '@0xproject/json-schemas'; import { Order, SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import { assert } from './assert'; -import { crypto } from './crypto'; +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. @@ -42,7 +59,7 @@ export const orderHashUtils = { throw error; } - const orderHashBuff = this.getOrderHashBuff(order); + const orderHashBuff = orderHashUtils.getOrderHashBuffer(order); const orderHashHex = `0x${orderHashBuff.toString('hex')}`; return orderHashHex; }, @@ -51,64 +68,12 @@ export const orderHashUtils = { * @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 */ - getOrderHashBuff(order: SignedOrder | Order): Buffer { - const makerAssetDataHash = crypto.solSHA3([ethUtil.toBuffer(order.makerAssetData)]); - const takerAssetDataHash = crypto.solSHA3([ethUtil.toBuffer(order.takerAssetData)]); - - const orderParamsHashBuff = crypto.solSHA3([ - order.makerAddress, - order.takerAddress, - order.feeRecipientAddress, - order.senderAddress, - order.makerAssetAmount, - order.takerAssetAmount, - order.makerFee, - order.takerFee, - order.expirationTimeSeconds, - order.salt, - makerAssetDataHash, - takerAssetDataHash, - ]); - const orderParamsHashHex = `0x${orderParamsHashBuff.toString('hex')}`; - const orderSchemaHashHex = this._getOrderSchemaHex(); - const domainSeparatorHashHex = this._getDomainSeparatorHashHex(order.exchangeAddress); - const domainSeparatorSchemaHex = this._getDomainSeparatorSchemaHex(); - const orderHashBuff = crypto.solSHA3([ - new BigNumber(domainSeparatorSchemaHex), - new BigNumber(domainSeparatorHashHex), - new BigNumber(orderSchemaHashHex), - new BigNumber(orderParamsHashHex), - ]); + getOrderHashBuffer(order: SignedOrder | Order): Buffer { + const orderParamsHashBuff = EIP712Utils.structHash(EIP712_ORDER_SCHEMA, order); + const orderHashBuff = EIP712Utils.createEIP712Message(orderParamsHashBuff, order.exchangeAddress); return orderHashBuff; }, - _getOrderSchemaHex(): string { - const orderSchemaHashBuff = crypto.solSHA3([ - 'Order(', - 'address makerAddress,', - 'address takerAddress,', - 'address feeRecipientAddress,', - 'address senderAddress,', - 'uint256 makerAssetAmount,', - 'uint256 takerAssetAmount,', - 'uint256 makerFee,', - 'uint256 takerFee,', - 'uint256 expirationTimeSeconds,', - 'uint256 salt,', - 'bytes makerAssetData,', - 'bytes takerAssetData,', - ')', - ]); - const schemaHashHex = `0x${orderSchemaHashBuff.toString('hex')}`; - return schemaHashHex; - }, - _getDomainSeparatorSchemaHex(): string { - const domainSeparatorSchemaHashBuff = crypto.solSHA3(['DomainSeparator(address contract)']); - const schemaHashHex = `0x${domainSeparatorSchemaHashBuff.toString('hex')}`; - return schemaHashHex; - }, - _getDomainSeparatorHashHex(exchangeAddress: string): string { - const domainSeparatorHashBuff = crypto.solSHA3([exchangeAddress]); - const domainSeparatorHashHex = `0x${domainSeparatorHashBuff.toString('hex')}`; - return domainSeparatorHashHex; + _getOrderSchemaBuffer(): Buffer { + return EIP712Utils.compileSchema(EIP712_ORDER_SCHEMA); }, }; diff --git a/packages/order-utils/src/types.ts b/packages/order-utils/src/types.ts index 3f1fce66d..b08e74e71 100644 --- a/packages/order-utils/src/types.ts +++ b/packages/order-utils/src/types.ts @@ -33,3 +33,21 @@ export enum TransferType { Trade = 'trade', 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', +} |