From 4670cc1a5f04d61045a82b4a3bbcf56e009afd56 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Tue, 29 May 2018 17:11:33 -0700 Subject: Add missing EIP191 prefix for EIP712 --- .../protocol/Exchange/MixinTransactions.sol | 35 +++++++++--- .../current/protocol/Exchange/libs/LibEIP712.sol | 55 +++++++++++++++++++ .../current/protocol/Exchange/libs/LibOrder.sol | 61 ++++++++++----------- .../contracts/current/test/TestLibs/TestLibs.sol | 4 +- packages/contracts/src/utils/eip712_utils.ts | 63 ++++++++++++++++++++++ packages/contracts/src/utils/order_factory.ts | 6 +-- packages/contracts/src/utils/order_utils.ts | 56 ++++++++++++++++++- .../contracts/src/utils/transaction_factory.ts | 30 ++++++++++- packages/contracts/src/utils/types.ts | 10 ++++ packages/contracts/test/exchange/libs.ts | 9 +++- 10 files changed, 279 insertions(+), 50 deletions(-) create mode 100644 packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol create mode 100644 packages/contracts/src/utils/eip712_utils.ts (limited to 'packages') diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol index 30b0102fd..e3f6b2b2b 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol @@ -20,8 +20,11 @@ pragma solidity ^0.4.24; import "./libs/LibExchangeErrors.sol"; import "./mixins/MSignatureValidator.sol"; import "./mixins/MTransactions.sol"; +import "./libs/LibExchangeErrors.sol"; +import "./libs/LibEIP712.sol"; contract MixinTransactions is + LibEIP712, LibExchangeErrors, MSignatureValidator, MTransactions @@ -34,6 +37,30 @@ contract MixinTransactions is // Address of current transaction signer address public currentContextAddress; + bytes32 constant EXECUTE_TRANSACTION_SCHEMA_HASH = keccak256( + "ExecuteTransaction(", + "uint256 salt,", + "address signer,", + "bytes data", + ")" + ); + + function getExecuteTransactionHash(uint256 salt, address signer, bytes data) + internal + view + returns (bytes32 executeTransactionHash) + { + executeTransactionHash = createEIP712Message( + keccak256( + EXECUTE_TRANSACTION_SCHEMA_HASH, + salt, + bytes32(signer), + keccak256(data) + ) + ); + return executeTransactionHash; + } + /// @dev Executes an exchange method call in the context of signer. /// @param salt Arbitrary number to ensure uniqueness of transaction hash. /// @param signer Address of transaction signer. @@ -53,13 +80,7 @@ contract MixinTransactions is REENTRANCY_ILLEGAL ); - // Calculate transaction hash - bytes32 transactionHash = keccak256(abi.encodePacked( - address(this), - signer, - salt, - data - )); + bytes32 transactionHash = getExecuteTransactionHash(salt, signer, data); // Validate transaction has not been executed require( diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol new file mode 100644 index 000000000..d28ef876f --- /dev/null +++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol @@ -0,0 +1,55 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.4.24; + +contract LibEIP712 { + string public constant EIP191_HEADER = "\x19\x01"; + bytes32 public constant EIP712_DOMAIN_SEPARATOR_NAME_HASH = keccak256("0x Protocol"); + + bytes32 public constant EIP712_DOMAIN_SEPARATOR_VERSION_HASH = keccak256("1"); + + bytes32 public constant EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256( + "DomainSeparator(", + "string name,", + "string version,", + "address contract", + ")" + ); + + function createEIP712Message(bytes32 hashStruct) + internal + view + returns (bytes32 message) + { + // TODO: EIP712 is not finalized yet + // Source: https://github.com/ethereum/EIPs/pull/712 + // TODO: Cache the Domain Separator + message = keccak256( + EIP191_HEADER, + keccak256( + EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH, + EIP712_DOMAIN_SEPARATOR_NAME_HASH, + EIP712_DOMAIN_SEPARATOR_VERSION_HASH, + bytes32(address(this)) + ), + hashStruct + ); + return message; + } +} \ No newline at end of file diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol index 05ea27ffc..6888918e2 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol @@ -18,27 +18,28 @@ pragma solidity ^0.4.24; -contract LibOrder { +import "./LibEIP712.sol"; - bytes32 constant DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked( - "DomainSeparator(address contract)" - )); +contract LibOrder is + LibEIP712 +{ - bytes32 constant ORDER_SCHEMA_HASH = keccak256(abi.encodePacked( - "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,", - ")" + bytes32 constant EIP712_ORDER_SCHEMA_HASH = keccak256( + abi.encodePacked( + "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", + ")" )); // A valid order remains fillable until it is expired, fully filled, or cancelled. @@ -85,17 +86,14 @@ contract LibOrder { view returns (bytes32 orderHash) { - // TODO: EIP712 is not finalized yet - // Source: https://github.com/ethereum/EIPs/pull/712 - orderHash = keccak256(abi.encodePacked( - DOMAIN_SEPARATOR_SCHEMA_HASH, - keccak256(abi.encodePacked(address(this))), - ORDER_SCHEMA_HASH, - keccak256(abi.encodePacked( - order.makerAddress, - order.takerAddress, - order.feeRecipientAddress, - order.senderAddress, + orderHash = createEIP712Message( + keccak256( + abi.encodePacked( + EIP712_ORDER_SCHEMA_HASH, + bytes32(order.makerAddress), + bytes32(order.takerAddress), + bytes32(order.feeRecipientAddress), + bytes32(order.senderAddress), order.makerAssetAmount, order.takerAssetAmount, order.makerFee, @@ -104,8 +102,7 @@ contract LibOrder { order.salt, keccak256(abi.encodePacked(order.makerAssetData)), keccak256(abi.encodePacked(order.takerAssetData)) - )) - )); + ))); return orderHash; } } diff --git a/packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol b/packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol index 47ce0dcf3..010080703 100644 --- a/packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol +++ b/packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol @@ -74,7 +74,7 @@ contract TestLibs is pure returns (bytes32) { - return ORDER_SCHEMA_HASH; + return EIP712_ORDER_SCHEMA_HASH; } function getDomainSeparatorSchemaHash() @@ -82,7 +82,7 @@ contract TestLibs is pure returns (bytes32) { - return DOMAIN_SEPARATOR_SCHEMA_HASH; + return EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH; } function publicAddFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults) diff --git a/packages/contracts/src/utils/eip712_utils.ts b/packages/contracts/src/utils/eip712_utils.ts new file mode 100644 index 000000000..07b45c8af --- /dev/null +++ b/packages/contracts/src/utils/eip712_utils.ts @@ -0,0 +1,63 @@ +import { BigNumber } from '@0xproject/utils'; +import ethUtil = require('ethereumjs-util'); +import * as _ from 'lodash'; + +import { crypto } from './crypto'; +import { EIP712Schema } from './types'; + +const EIP191_PREFIX = '\x19\x01'; +const EIP712_DOMAIN_NAME = '0x Protocol'; +const EIP712_DOMAIN_VERSION = '1'; + +const EIP712_DOMAIN_SCHEMA: EIP712Schema = { + name: 'DomainSeparator', + parameters: [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + { name: 'contract', type: 'address' }, + ], +}; + +export const EIP712Utils = { + compileSchema(schema: EIP712Schema): Buffer { + const namedTypes = _.map(schema.parameters, parameter => `${parameter.type} ${parameter.name}`); + const namedTypesJoined = namedTypes.join(','); + const eip712Schema = `${schema.name}(${namedTypesJoined})`; + const eip712SchemaHashBuffer = crypto.solSHA3([eip712Schema]); + return eip712SchemaHashBuffer; + }, + createEIP712Message(hashStruct: string, contractAddress: string): Buffer { + const domainSeparatorHashHex = EIP712Utils.getDomainSeparatorHashHex(contractAddress); + const messageBuff = crypto.solSHA3([ + EIP191_PREFIX, + new BigNumber(domainSeparatorHashHex), + new BigNumber(hashStruct), + ]); + return messageBuff; + }, + getDomainSeparatorSchemaBuffer(): Buffer { + return EIP712Utils.compileSchema(EIP712_DOMAIN_SCHEMA); + }, + getDomainSeparatorHashHex(exchangeAddress: string): string { + const domainSeparatorSchemaBuffer = EIP712Utils.getDomainSeparatorSchemaBuffer(); + const nameHash = crypto.solSHA3([EIP712_DOMAIN_NAME]); + const versionHash = crypto.solSHA3([EIP712_DOMAIN_VERSION]); + const domainSeparatorHashBuff = crypto.solSHA3([ + domainSeparatorSchemaBuffer, + nameHash, + versionHash, + EIP712Utils.pad32Address(exchangeAddress), + ]); + const domainSeparatorHashHex = `0x${domainSeparatorHashBuff.toString('hex')}`; + return domainSeparatorHashHex; + }, + 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, 32); + return bufferPadded; + }, +}; diff --git a/packages/contracts/src/utils/order_factory.ts b/packages/contracts/src/utils/order_factory.ts index af411c01f..bfe5013e8 100644 --- a/packages/contracts/src/utils/order_factory.ts +++ b/packages/contracts/src/utils/order_factory.ts @@ -1,5 +1,5 @@ import { generatePseudoRandomSalt, orderHashUtils } from '@0xproject/order-utils'; -import { Order, SignatureType, SignedOrder } from '@0xproject/types'; +import { Order, SignatureType, SignedOrder, UnsignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; @@ -26,8 +26,8 @@ export class OrderFactory { takerAddress: constants.NULL_ADDRESS, ...this._defaultOrderParams, ...customOrderParams, - } as any) as Order; - const orderHashBuff = orderHashUtils.getOrderHashBuff(order); + } as any) as UnsignedOrder; + const orderHashBuff = orderHashUtils.getOrderHashBuffer(order); const signature = signingUtils.signMessage(orderHashBuff, this._privateKey, signatureType); const signedOrder = { ...order, diff --git a/packages/contracts/src/utils/order_utils.ts b/packages/contracts/src/utils/order_utils.ts index 0d0329aa1..316350ffb 100644 --- a/packages/contracts/src/utils/order_utils.ts +++ b/packages/contracts/src/utils/order_utils.ts @@ -1,8 +1,29 @@ -import { Order, OrderWithoutExchangeAddress, SignedOrder } from '@0xproject/types'; +import { Order, OrderWithoutExchangeAddress, SignedOrder, UnsignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import ethUtil = require('ethereumjs-util'); +import * as _ from 'lodash'; -import { CancelOrder, MatchOrder } from './types'; +import { crypto } from './crypto'; +import { EIP712Utils } from './eip712_utils'; +import { CancelOrder, EIP712Schema, MatchOrder } from './types'; + +const EIP712_ORDER_SCHEMA: EIP712Schema = { + 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 orderUtils = { createFill: (signedOrder: SignedOrder, takerAssetFillAmount?: BigNumber) => { @@ -37,6 +58,37 @@ export const orderUtils = { }; return orderStruct; }, + getOrderSchemaBuffer(): Buffer { + return EIP712Utils.compileSchema(EIP712_ORDER_SCHEMA); + }, + getOrderHashBuffer(order: SignedOrder | UnsignedOrder): Buffer { + const makerAssetDataHash = crypto.solSHA3([ethUtil.toBuffer(order.makerAssetData)]); + const takerAssetDataHash = crypto.solSHA3([ethUtil.toBuffer(order.takerAssetData)]); + + const orderParamsHashBuff = crypto.solSHA3([ + orderUtils.getOrderSchemaBuffer(), + EIP712Utils.pad32Address(order.makerAddress), + EIP712Utils.pad32Address(order.takerAddress), + EIP712Utils.pad32Address(order.feeRecipientAddress), + EIP712Utils.pad32Address(order.senderAddress), + order.makerAssetAmount, + order.takerAssetAmount, + order.makerFee, + order.takerFee, + order.expirationTimeSeconds, + order.salt, + makerAssetDataHash, + takerAssetDataHash, + ]); + const orderParamsHashHex = `0x${orderParamsHashBuff.toString('hex')}`; + const orderHashBuff = EIP712Utils.createEIP712Message(orderParamsHashHex, order.exchangeAddress); + return orderHashBuff; + }, + getOrderHashHex(order: SignedOrder | UnsignedOrder): string { + const orderHashBuff = orderUtils.getOrderHashBuffer(order); + const orderHashHex = `0x${orderHashBuff.toString('hex')}`; + return orderHashHex; + }, createMatchOrders(signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder): MatchOrder { const fill = { left: orderUtils.getOrderWithoutExchangeAddress(signedOrderLeft), diff --git a/packages/contracts/src/utils/transaction_factory.ts b/packages/contracts/src/utils/transaction_factory.ts index 434611908..1c1029d62 100644 --- a/packages/contracts/src/utils/transaction_factory.ts +++ b/packages/contracts/src/utils/transaction_factory.ts @@ -2,9 +2,22 @@ import { crypto, generatePseudoRandomSalt } from '@0xproject/order-utils'; import { SignatureType } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; +import { crypto } from './crypto'; +import { EIP712Utils } from './eip712_utils'; +import { orderUtils } from './order_utils'; import { signingUtils } from './signing_utils'; -import { SignedTransaction } from './types'; +import { EIP712Schema, SignatureType, SignedTransaction } from './types'; + +const EIP712_EXECUTE_TRANSACTION_SCHEMA: EIP712Schema = { + name: 'ExecuteTransaction', + parameters: [ + { name: 'salt', type: 'uint256' }, + { name: 'signer', type: 'address' }, + { name: 'data', type: 'bytes' }, + ], +}; export class TransactionFactory { private _signerBuff: Buffer; @@ -16,8 +29,21 @@ export class TransactionFactory { this._signerBuff = ethUtil.privateToAddress(this._privateKey); } public newSignedTransaction(data: string, signatureType: SignatureType = SignatureType.EthSign): SignedTransaction { + const executeTransactionSchemaHashBuff = EIP712Utils.compileSchema(EIP712_EXECUTE_TRANSACTION_SCHEMA); + const salt = generatePseudoRandomSalt(); - const txHash = crypto.solSHA3([this._exchangeAddress, this._signerBuff, salt, ethUtil.toBuffer(data)]); + const dataHash = crypto.solSHA3([ethUtil.toBuffer(data)]); + + const executeTransactionDataHash = crypto.solSHA3([ + executeTransactionSchemaHashBuff, + salt, + EIP712Utils.pad32Buffer(this._signerBuff), + dataHash, + ]); + + const executeTransactionMessageHex = `0x${executeTransactionDataHash.toString('hex')}`; + + const txHash = EIP712Utils.createEIP712Message(executeTransactionMessageHex, this._exchangeAddress); const signature = signingUtils.signMessage(txHash, this._privateKey, signatureType); const signedTx = { exchangeAddress: this._exchangeAddress, diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index 360e1fdbc..5b7f1bcee 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -147,3 +147,13 @@ export interface MatchOrder { leftSignature: string; rightSignature: string; } + +export interface EIP712Parameter { + name: string; + type: string; +} + +export interface EIP712Schema { + name: string; + parameters: EIP712Parameter[]; +} diff --git a/packages/contracts/test/exchange/libs.ts b/packages/contracts/test/exchange/libs.ts index 10cb8b34e..b6de4d3b0 100644 --- a/packages/contracts/test/exchange/libs.ts +++ b/packages/contracts/test/exchange/libs.ts @@ -10,6 +10,7 @@ import { addressUtils } from '../../src/utils/address_utils'; import { artifacts } from '../../src/utils/artifacts'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; +import { EIP712Utils } from '../../src/utils/eip712_utils'; import { OrderFactory } from '../../src/utils/order_factory'; import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; @@ -57,13 +58,17 @@ describe('Exchange libs', () => { describe('getOrderSchema', () => { it('should output the correct order schema hash', async () => { const orderSchema = await libs.getOrderSchemaHash.callAsync(); - expect(orderHashUtils._getOrderSchemaHex()).to.be.equal(orderSchema); + const orderSchemaBuffer = orderHashUtils._getOrderSchemaHex(); + const schemaHashHex = `0x${orderSchemaBuffer.toString('hex')}`; + expect(schemaHashHex).to.be.equal(orderSchema); }); }); describe('getDomainSeparatorSchema', () => { it('should output the correct domain separator schema hash', async () => { const domainSeparatorSchema = await libs.getDomainSeparatorSchemaHash.callAsync(); - expect(orderHashUtils._getDomainSeparatorSchemaHex()).to.be.equal(domainSeparatorSchema); + const domainSchemaBuffer = EIP712Utils.getDomainSeparatorSchemaBuffer(); + const schemaHashHex = `0x${domainSchemaBuffer.toString('hex')}`; + expect(schemaHashHex).to.be.equal(domainSeparatorSchema); }); }); describe('getOrderHash', () => { -- cgit v1.2.3 From a59e9f024e76122c76600d938b565ea47938b287 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Mon, 4 Jun 2018 18:40:07 -0700 Subject: Update Order utils to use eip712 --- .../current/protocol/Exchange/libs/LibEIP712.sol | 35 ++++++---- .../current/protocol/Exchange/libs/LibOrder.sol | 30 +++++---- packages/contracts/src/utils/eip712_utils.ts | 63 ------------------ packages/contracts/src/utils/order_factory.ts | 4 +- packages/contracts/src/utils/order_utils.ts | 56 +--------------- .../contracts/src/utils/transaction_factory.ts | 16 +---- packages/contracts/src/utils/types.ts | 10 --- packages/contracts/test/exchange/core.ts | 5 +- packages/contracts/test/exchange/libs.ts | 9 ++- packages/order-utils/src/eip712_utils.ts | 70 ++++++++++++++++++++ packages/order-utils/src/index.ts | 3 +- packages/order-utils/src/order_hash.ts | 75 +++++++++------------- packages/order-utils/src/types.ts | 10 +++ 13 files changed, 162 insertions(+), 224 deletions(-) delete mode 100644 packages/contracts/src/utils/eip712_utils.ts create mode 100644 packages/order-utils/src/eip712_utils.ts (limited to 'packages') diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol index d28ef876f..7704e3db4 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol @@ -20,16 +20,19 @@ pragma solidity ^0.4.24; contract LibEIP712 { string public constant EIP191_HEADER = "\x19\x01"; + bytes32 public constant EIP712_DOMAIN_SEPARATOR_NAME_HASH = keccak256("0x Protocol"); - bytes32 public constant EIP712_DOMAIN_SEPARATOR_VERSION_HASH = keccak256("1"); + bytes32 public constant EIP712_DOMAIN_SEPARATOR_VERSION_HASH = keccak256("2"); bytes32 public constant EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256( - "DomainSeparator(", - "string name,", - "string version,", - "address contract", - ")" + abi.encodePacked( + "DomainSeparator(", + "string name,", + "string version,", + "address contract", + ")" + ) ); function createEIP712Message(bytes32 hashStruct) @@ -41,14 +44,18 @@ contract LibEIP712 { // Source: https://github.com/ethereum/EIPs/pull/712 // TODO: Cache the Domain Separator message = keccak256( - EIP191_HEADER, - keccak256( - EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH, - EIP712_DOMAIN_SEPARATOR_NAME_HASH, - EIP712_DOMAIN_SEPARATOR_VERSION_HASH, - bytes32(address(this)) - ), - hashStruct + abi.encodePacked( + EIP191_HEADER, + keccak256( + abi.encodePacked( + EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH, + EIP712_DOMAIN_SEPARATOR_NAME_HASH, + EIP712_DOMAIN_SEPARATOR_VERSION_HASH, + bytes32(address(this)) + ) + ), + hashStruct + ) ); return message; } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol index 6888918e2..ed532fd59 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol @@ -89,20 +89,22 @@ contract LibOrder is orderHash = createEIP712Message( keccak256( abi.encodePacked( - EIP712_ORDER_SCHEMA_HASH, - bytes32(order.makerAddress), - bytes32(order.takerAddress), - bytes32(order.feeRecipientAddress), - bytes32(order.senderAddress), - order.makerAssetAmount, - order.takerAssetAmount, - order.makerFee, - order.takerFee, - order.expirationTimeSeconds, - order.salt, - keccak256(abi.encodePacked(order.makerAssetData)), - keccak256(abi.encodePacked(order.takerAssetData)) - ))); + EIP712_ORDER_SCHEMA_HASH, + bytes32(order.makerAddress), + bytes32(order.takerAddress), + bytes32(order.feeRecipientAddress), + bytes32(order.senderAddress), + order.makerAssetAmount, + order.takerAssetAmount, + order.makerFee, + order.takerFee, + order.expirationTimeSeconds, + order.salt, + keccak256(abi.encodePacked(order.makerAssetData)), + keccak256(abi.encodePacked(order.takerAssetData)) + ) + ) + ); return orderHash; } } diff --git a/packages/contracts/src/utils/eip712_utils.ts b/packages/contracts/src/utils/eip712_utils.ts deleted file mode 100644 index 07b45c8af..000000000 --- a/packages/contracts/src/utils/eip712_utils.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { BigNumber } from '@0xproject/utils'; -import ethUtil = require('ethereumjs-util'); -import * as _ from 'lodash'; - -import { crypto } from './crypto'; -import { EIP712Schema } from './types'; - -const EIP191_PREFIX = '\x19\x01'; -const EIP712_DOMAIN_NAME = '0x Protocol'; -const EIP712_DOMAIN_VERSION = '1'; - -const EIP712_DOMAIN_SCHEMA: EIP712Schema = { - name: 'DomainSeparator', - parameters: [ - { name: 'name', type: 'string' }, - { name: 'version', type: 'string' }, - { name: 'contract', type: 'address' }, - ], -}; - -export const EIP712Utils = { - compileSchema(schema: EIP712Schema): Buffer { - const namedTypes = _.map(schema.parameters, parameter => `${parameter.type} ${parameter.name}`); - const namedTypesJoined = namedTypes.join(','); - const eip712Schema = `${schema.name}(${namedTypesJoined})`; - const eip712SchemaHashBuffer = crypto.solSHA3([eip712Schema]); - return eip712SchemaHashBuffer; - }, - createEIP712Message(hashStruct: string, contractAddress: string): Buffer { - const domainSeparatorHashHex = EIP712Utils.getDomainSeparatorHashHex(contractAddress); - const messageBuff = crypto.solSHA3([ - EIP191_PREFIX, - new BigNumber(domainSeparatorHashHex), - new BigNumber(hashStruct), - ]); - return messageBuff; - }, - getDomainSeparatorSchemaBuffer(): Buffer { - return EIP712Utils.compileSchema(EIP712_DOMAIN_SCHEMA); - }, - getDomainSeparatorHashHex(exchangeAddress: string): string { - const domainSeparatorSchemaBuffer = EIP712Utils.getDomainSeparatorSchemaBuffer(); - const nameHash = crypto.solSHA3([EIP712_DOMAIN_NAME]); - const versionHash = crypto.solSHA3([EIP712_DOMAIN_VERSION]); - const domainSeparatorHashBuff = crypto.solSHA3([ - domainSeparatorSchemaBuffer, - nameHash, - versionHash, - EIP712Utils.pad32Address(exchangeAddress), - ]); - const domainSeparatorHashHex = `0x${domainSeparatorHashBuff.toString('hex')}`; - return domainSeparatorHashHex; - }, - 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, 32); - return bufferPadded; - }, -}; diff --git a/packages/contracts/src/utils/order_factory.ts b/packages/contracts/src/utils/order_factory.ts index bfe5013e8..dd02e1f5c 100644 --- a/packages/contracts/src/utils/order_factory.ts +++ b/packages/contracts/src/utils/order_factory.ts @@ -1,5 +1,5 @@ import { generatePseudoRandomSalt, orderHashUtils } from '@0xproject/order-utils'; -import { Order, SignatureType, SignedOrder, UnsignedOrder } from '@0xproject/types'; +import { Order, SignatureType, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; @@ -26,7 +26,7 @@ export class OrderFactory { takerAddress: constants.NULL_ADDRESS, ...this._defaultOrderParams, ...customOrderParams, - } as any) as UnsignedOrder; + } as any) as Order; const orderHashBuff = orderHashUtils.getOrderHashBuffer(order); const signature = signingUtils.signMessage(orderHashBuff, this._privateKey, signatureType); const signedOrder = { diff --git a/packages/contracts/src/utils/order_utils.ts b/packages/contracts/src/utils/order_utils.ts index 316350ffb..180761f2c 100644 --- a/packages/contracts/src/utils/order_utils.ts +++ b/packages/contracts/src/utils/order_utils.ts @@ -1,29 +1,8 @@ -import { Order, OrderWithoutExchangeAddress, SignedOrder, UnsignedOrder } from '@0xproject/types'; +import { OrderWithoutExchangeAddress, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import ethUtil = require('ethereumjs-util'); -import * as _ from 'lodash'; -import { crypto } from './crypto'; -import { EIP712Utils } from './eip712_utils'; -import { CancelOrder, EIP712Schema, MatchOrder } from './types'; - -const EIP712_ORDER_SCHEMA: EIP712Schema = { - 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' }, - ], -}; +import { CancelOrder, MatchOrder } from './types'; export const orderUtils = { createFill: (signedOrder: SignedOrder, takerAssetFillAmount?: BigNumber) => { @@ -58,37 +37,6 @@ export const orderUtils = { }; return orderStruct; }, - getOrderSchemaBuffer(): Buffer { - return EIP712Utils.compileSchema(EIP712_ORDER_SCHEMA); - }, - getOrderHashBuffer(order: SignedOrder | UnsignedOrder): Buffer { - const makerAssetDataHash = crypto.solSHA3([ethUtil.toBuffer(order.makerAssetData)]); - const takerAssetDataHash = crypto.solSHA3([ethUtil.toBuffer(order.takerAssetData)]); - - const orderParamsHashBuff = crypto.solSHA3([ - orderUtils.getOrderSchemaBuffer(), - EIP712Utils.pad32Address(order.makerAddress), - EIP712Utils.pad32Address(order.takerAddress), - EIP712Utils.pad32Address(order.feeRecipientAddress), - EIP712Utils.pad32Address(order.senderAddress), - order.makerAssetAmount, - order.takerAssetAmount, - order.makerFee, - order.takerFee, - order.expirationTimeSeconds, - order.salt, - makerAssetDataHash, - takerAssetDataHash, - ]); - const orderParamsHashHex = `0x${orderParamsHashBuff.toString('hex')}`; - const orderHashBuff = EIP712Utils.createEIP712Message(orderParamsHashHex, order.exchangeAddress); - return orderHashBuff; - }, - getOrderHashHex(order: SignedOrder | UnsignedOrder): string { - const orderHashBuff = orderUtils.getOrderHashBuffer(order); - const orderHashHex = `0x${orderHashBuff.toString('hex')}`; - return orderHashHex; - }, createMatchOrders(signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder): MatchOrder { const fill = { left: orderUtils.getOrderWithoutExchangeAddress(signedOrderLeft), diff --git a/packages/contracts/src/utils/transaction_factory.ts b/packages/contracts/src/utils/transaction_factory.ts index 1c1029d62..7aaf3691c 100644 --- a/packages/contracts/src/utils/transaction_factory.ts +++ b/packages/contracts/src/utils/transaction_factory.ts @@ -1,14 +1,9 @@ -import { crypto, generatePseudoRandomSalt } from '@0xproject/order-utils'; +import { crypto, EIP712Schema, EIP712Utils, generatePseudoRandomSalt } from '@0xproject/order-utils'; import { SignatureType } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; import * as ethUtil from 'ethereumjs-util'; -import * as _ from 'lodash'; -import { crypto } from './crypto'; -import { EIP712Utils } from './eip712_utils'; -import { orderUtils } from './order_utils'; import { signingUtils } from './signing_utils'; -import { EIP712Schema, SignatureType, SignedTransaction } from './types'; +import { SignedTransaction } from './types'; const EIP712_EXECUTE_TRANSACTION_SCHEMA: EIP712Schema = { name: 'ExecuteTransaction', @@ -30,20 +25,15 @@ export class TransactionFactory { } public newSignedTransaction(data: string, signatureType: SignatureType = SignatureType.EthSign): SignedTransaction { const executeTransactionSchemaHashBuff = EIP712Utils.compileSchema(EIP712_EXECUTE_TRANSACTION_SCHEMA); - const salt = generatePseudoRandomSalt(); const dataHash = crypto.solSHA3([ethUtil.toBuffer(data)]); - const executeTransactionDataHash = crypto.solSHA3([ executeTransactionSchemaHashBuff, salt, EIP712Utils.pad32Buffer(this._signerBuff), dataHash, ]); - - const executeTransactionMessageHex = `0x${executeTransactionDataHash.toString('hex')}`; - - const txHash = EIP712Utils.createEIP712Message(executeTransactionMessageHex, this._exchangeAddress); + const txHash = EIP712Utils.createEIP712Message(executeTransactionDataHash, this._exchangeAddress); const signature = signingUtils.signMessage(txHash, this._privateKey, signatureType); const signedTx = { exchangeAddress: this._exchangeAddress, diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index 5b7f1bcee..360e1fdbc 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -147,13 +147,3 @@ export interface MatchOrder { leftSignature: string; rightSignature: string; } - -export interface EIP712Parameter { - name: string; - type: string; -} - -export interface EIP712Schema { - name: string; - parameters: EIP712Parameter[]; -} diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index 91ead93f0..6d62c2b86 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -1,5 +1,5 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { assetProxyUtils, crypto, orderHashUtils } from '@0xproject/order-utils'; +import { assetProxyUtils, orderHashUtils } from '@0xproject/order-utils'; import { AssetProxyId, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; @@ -24,8 +24,7 @@ import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; import { ERC721Wrapper } from '../../src/utils/erc721_wrapper'; import { ExchangeWrapper } from '../../src/utils/exchange_wrapper'; import { OrderFactory } from '../../src/utils/order_factory'; -import { orderUtils } from '../../src/utils/order_utils'; -import { ContractName, ERC20BalancesByOwner, OrderStatus } from '../../src/utils/types'; +import { ERC20BalancesByOwner } from '../../src/utils/types'; import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; chaiSetup.configure(); diff --git a/packages/contracts/test/exchange/libs.ts b/packages/contracts/test/exchange/libs.ts index b6de4d3b0..ac9020bce 100644 --- a/packages/contracts/test/exchange/libs.ts +++ b/packages/contracts/test/exchange/libs.ts @@ -1,5 +1,5 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { assetProxyUtils, orderHashUtils } from '@0xproject/order-utils'; +import { assetProxyUtils, EIP712Utils, orderHashUtils } from '@0xproject/order-utils'; import { SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; @@ -10,7 +10,6 @@ import { addressUtils } from '../../src/utils/address_utils'; import { artifacts } from '../../src/utils/artifacts'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; -import { EIP712Utils } from '../../src/utils/eip712_utils'; import { OrderFactory } from '../../src/utils/order_factory'; import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; @@ -58,15 +57,15 @@ describe('Exchange libs', () => { describe('getOrderSchema', () => { it('should output the correct order schema hash', async () => { const orderSchema = await libs.getOrderSchemaHash.callAsync(); - const orderSchemaBuffer = orderHashUtils._getOrderSchemaHex(); - const schemaHashHex = `0x${orderSchemaBuffer.toString('hex')}`; + const schemaHashBuffer = orderHashUtils._getOrderSchemaBuffer(); + const schemaHashHex = `0x${schemaHashBuffer.toString('hex')}`; expect(schemaHashHex).to.be.equal(orderSchema); }); }); describe('getDomainSeparatorSchema', () => { it('should output the correct domain separator schema hash', async () => { const domainSeparatorSchema = await libs.getDomainSeparatorSchemaHash.callAsync(); - const domainSchemaBuffer = EIP712Utils.getDomainSeparatorSchemaBuffer(); + const domainSchemaBuffer = EIP712Utils._getDomainSeparatorSchemaBuffer(); const schemaHashHex = `0x${domainSchemaBuffer.toString('hex')}`; expect(schemaHashHex).to.be.equal(domainSeparatorSchema); }); diff --git a/packages/order-utils/src/eip712_utils.ts b/packages/order-utils/src/eip712_utils.ts new file mode 100644 index 000000000..f7583f8d5 --- /dev/null +++ b/packages/order-utils/src/eip712_utils.ts @@ -0,0 +1,70 @@ +import { BigNumber } from '@0xproject/utils'; +import ethUtil = require('ethereumjs-util'); +import * as _ from 'lodash'; + +import { crypto } from './crypto'; +import { EIP712Schema } 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: 'DomainSeparator', + parameters: [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + { name: 'contract', type: '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 namedTypes = _.map(schema.parameters, parameter => `${parameter.type} ${parameter.name}`); + const namedTypesJoined = namedTypes.join(','); + const eip712Schema = `${schema.name}(${namedTypesJoined})`; + 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 nameHash = crypto.solSHA3([EIP712_DOMAIN_NAME]); + const versionHash = crypto.solSHA3([EIP712_DOMAIN_VERSION]); + const domainSeparatorHashBuff = crypto.solSHA3([ + domainSeparatorSchemaBuffer, + nameHash, + versionHash, + EIP712Utils.pad32Address(exchangeAddress), + ]); + return domainSeparatorHashBuff; + }, +}; diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index b844fbfcb..caa996621 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -4,9 +4,10 @@ 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 } from './types'; export { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher'; export { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher'; export { RemainingFillableCalculator } from './remaining_fillable_calculator'; export { OrderStateUtils } from './order_state_utils'; export { assetProxyUtils } from './asset_proxy_utils'; +export { EIP712Utils } from './eip712_utils'; diff --git a/packages/order-utils/src/order_hash.ts b/packages/order-utils/src/order_hash.ts index 2ef746ef8..b2414febc 100644 --- a/packages/order-utils/src/order_hash.ts +++ b/packages/order-utils/src/order_hash.ts @@ -9,9 +9,29 @@ import * as _ from 'lodash'; import { assert } from './assert'; import { crypto } from './crypto'; +import { EIP712Utils } from './eip712_utils'; +import { EIP712Schema } 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: '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 +65,7 @@ export const orderHashUtils = { throw error; } - const orderHashBuff = this.getOrderHashBuff(order); + const orderHashBuff = orderHashUtils.getOrderHashBuffer(order); const orderHashHex = `0x${orderHashBuff.toString('hex')}`; return orderHashHex; }, @@ -54,15 +74,16 @@ 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 { + getOrderHashBuffer(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, + orderHashUtils._getOrderSchemaBuffer(), + EIP712Utils.pad32Address(order.makerAddress), + EIP712Utils.pad32Address(order.takerAddress), + EIP712Utils.pad32Address(order.feeRecipientAddress), + EIP712Utils.pad32Address(order.senderAddress), order.makerAssetAmount, order.takerAssetAmount, order.makerFee, @@ -72,46 +93,10 @@ export const orderHashUtils = { 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), - ]); + 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 db0bfb249..858c0e18b 100644 --- a/packages/order-utils/src/types.ts +++ b/packages/order-utils/src/types.ts @@ -23,3 +23,13 @@ export interface MessagePrefixOpts { prefixType: MessagePrefixType; shouldAddPrefixBeforeCallingEthSign: boolean; } + +export interface EIP712Parameter { + name: string; + type: string; +} + +export interface EIP712Schema { + name: string; + parameters: EIP712Parameter[]; +} -- cgit v1.2.3 From a8d328bfc926a62d61830334fadc43fc5d013e0e Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Thu, 7 Jun 2018 16:03:14 -0700 Subject: Basic EIP712 encoder --- .../contracts/src/utils/transaction_factory.ts | 21 ++++---- packages/order-utils/src/eip712_utils.ts | 61 +++++++++++++++++----- packages/order-utils/src/order_hash.ts | 19 +------ 3 files changed, 61 insertions(+), 40 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/utils/transaction_factory.ts b/packages/contracts/src/utils/transaction_factory.ts index 7aaf3691c..bb15a0309 100644 --- a/packages/contracts/src/utils/transaction_factory.ts +++ b/packages/contracts/src/utils/transaction_factory.ts @@ -26,21 +26,22 @@ export class TransactionFactory { public newSignedTransaction(data: string, signatureType: SignatureType = SignatureType.EthSign): SignedTransaction { const executeTransactionSchemaHashBuff = EIP712Utils.compileSchema(EIP712_EXECUTE_TRANSACTION_SCHEMA); const salt = generatePseudoRandomSalt(); - const dataHash = crypto.solSHA3([ethUtil.toBuffer(data)]); - const executeTransactionDataHash = crypto.solSHA3([ - executeTransactionSchemaHashBuff, + const signer = `0x${this._signerBuff.toString('hex')}`; + const executeTransactionData = { salt, - EIP712Utils.pad32Buffer(this._signerBuff), - dataHash, - ]); - const txHash = EIP712Utils.createEIP712Message(executeTransactionDataHash, this._exchangeAddress); + signer, + data, + }; + const executeTransactionHashBuff = EIP712Utils.structHash( + EIP712_EXECUTE_TRANSACTION_SCHEMA, + executeTransactionData, + ); + const txHash = EIP712Utils.createEIP712Message(executeTransactionHashBuff, this._exchangeAddress); const signature = signingUtils.signMessage(txHash, this._privateKey, signatureType); const signedTx = { exchangeAddress: this._exchangeAddress, - salt, - signer: `0x${this._signerBuff.toString('hex')}`, - data, signature: `0x${signature.toString('hex')}`, + ...executeTransactionData, }; return signedTx; } diff --git a/packages/order-utils/src/eip712_utils.ts b/packages/order-utils/src/eip712_utils.ts index f7583f8d5..e54ca0e83 100644 --- a/packages/order-utils/src/eip712_utils.ts +++ b/packages/order-utils/src/eip712_utils.ts @@ -2,6 +2,7 @@ import { BigNumber } from '@0xproject/utils'; import ethUtil = require('ethereumjs-util'); import * as _ from 'lodash'; +import { assetProxyUtils } from './asset_proxy_utils'; import { crypto } from './crypto'; import { EIP712Schema } from './types'; @@ -19,6 +20,14 @@ const EIP712_DOMAIN_SCHEMA: EIP712Schema = { ], }; +enum EIP712Types { + String = 'string', + Bytes = 'bytes', + Address = 'address', + Bytes32 = 'bytes32', + Uint256 = 'uint256', +} + export const EIP712Utils = { /** * Compiles the EIP712Schema and returns the hash of the schema. @@ -26,9 +35,7 @@ export const EIP712Utils = { * @return The hash of the compiled schema */ compileSchema(schema: EIP712Schema): Buffer { - const namedTypes = _.map(schema.parameters, parameter => `${parameter.type} ${parameter.name}`); - const namedTypesJoined = namedTypes.join(','); - const eip712Schema = `${schema.name}(${namedTypesJoined})`; + const eip712Schema = EIP712Utils._encodeType(schema); const eip712SchemaHashBuffer = crypto.solSHA3([eip712Schema]); return eip712SchemaHashBuffer; }, @@ -57,14 +64,44 @@ export const EIP712Utils = { }, _getDomainSeparatorHashBuffer(exchangeAddress: string): Buffer { const domainSeparatorSchemaBuffer = EIP712Utils._getDomainSeparatorSchemaBuffer(); - const nameHash = crypto.solSHA3([EIP712_DOMAIN_NAME]); - const versionHash = crypto.solSHA3([EIP712_DOMAIN_VERSION]); - const domainSeparatorHashBuff = crypto.solSHA3([ - domainSeparatorSchemaBuffer, - nameHash, - versionHash, - EIP712Utils.pad32Address(exchangeAddress), - ]); - return domainSeparatorHashBuff; + const encodedData = EIP712Utils._encodeData(EIP712_DOMAIN_SCHEMA, { + name: EIP712_DOMAIN_NAME, + version: EIP712_DOMAIN_VERSION, + contract: 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 encodedTypes = []; + const encodedValues = []; + for (const parameter of schema.parameters) { + const value = data[parameter.name]; + if (parameter.type === EIP712Types.String || parameter.type === EIP712Types.Bytes) { + encodedTypes.push(EIP712Types.Bytes32); + encodedValues.push(crypto.solSHA3([ethUtil.toBuffer(value)])); + } else if (parameter.type === EIP712Types.Uint256) { + encodedTypes.push(EIP712Types.Uint256); + encodedValues.push(value); + } else if (parameter.type === EIP712Types.Address) { + encodedTypes.push(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/order_hash.ts b/packages/order-utils/src/order_hash.ts index b2414febc..46cce4b3b 100644 --- a/packages/order-utils/src/order_hash.ts +++ b/packages/order-utils/src/order_hash.ts @@ -75,24 +75,7 @@ export const orderHashUtils = { * @return The resulting orderHash from hashing the supplied order as a Buffer */ getOrderHashBuffer(order: SignedOrder | Order): Buffer { - const makerAssetDataHash = crypto.solSHA3([ethUtil.toBuffer(order.makerAssetData)]); - const takerAssetDataHash = crypto.solSHA3([ethUtil.toBuffer(order.takerAssetData)]); - - const orderParamsHashBuff = crypto.solSHA3([ - orderHashUtils._getOrderSchemaBuffer(), - EIP712Utils.pad32Address(order.makerAddress), - EIP712Utils.pad32Address(order.takerAddress), - EIP712Utils.pad32Address(order.feeRecipientAddress), - EIP712Utils.pad32Address(order.senderAddress), - order.makerAssetAmount, - order.takerAssetAmount, - order.makerFee, - order.takerFee, - order.expirationTimeSeconds, - order.salt, - makerAssetDataHash, - takerAssetDataHash, - ]); + const orderParamsHashBuff = EIP712Utils.structHash(EIP712_ORDER_SCHEMA, order); const orderHashBuff = EIP712Utils.createEIP712Message(orderParamsHashBuff, order.exchangeAddress); return orderHashBuff; }, -- cgit v1.2.3 From d4ee0e862297c16f8ee62efccd31f1193052c64e Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Mon, 18 Jun 2018 20:59:23 +1000 Subject: Rebase and update feedback Cache the domain separator data with address this Use the EIP712Types enum for types everywhere Rename EIP712 struct ExecuteTransaction to ZeroExTransaction --- .../protocol/Exchange/MixinTransactions.sol | 33 ++++++----- .../current/protocol/Exchange/libs/LibEIP712.sol | 65 ++++++++++------------ .../current/protocol/Exchange/libs/LibOrder.sol | 52 +++++++++-------- .../contracts/src/utils/transaction_factory.ts | 16 +++--- packages/order-utils/src/eip712_utils.ts | 24 ++------ packages/order-utils/src/index.ts | 2 +- packages/order-utils/src/order_hash.ts | 26 ++++----- packages/order-utils/src/types.ts | 10 +++- 8 files changed, 114 insertions(+), 114 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol index e3f6b2b2b..9bdaa0f8f 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol @@ -37,28 +37,31 @@ contract MixinTransactions is // Address of current transaction signer address public currentContextAddress; - bytes32 constant EXECUTE_TRANSACTION_SCHEMA_HASH = keccak256( - "ExecuteTransaction(", + // Hash for the EIP712 ZeroEx Transaction Schema + bytes32 constant EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = keccak256(abi.encodePacked( + "ZeroExTransaction(", "uint256 salt,", "address signer,", "bytes data", ")" - ); + )); - function getExecuteTransactionHash(uint256 salt, address signer, bytes data) + /// @dev Calculates EIP712 hash of the Transaction. + /// @param salt Arbitrary number to ensure uniqueness of transaction hash. + /// @param signer Address of transaction signer. + /// @param data AbiV2 encoded calldata. + /// @return EIP712 hash of the Transaction. + function hashZeroExTransaction(uint256 salt, address signer, bytes data) internal view - returns (bytes32 executeTransactionHash) + returns (bytes32) { - executeTransactionHash = createEIP712Message( - keccak256( - EXECUTE_TRANSACTION_SCHEMA_HASH, - salt, - bytes32(signer), - keccak256(data) - ) - ); - return executeTransactionHash; + return keccak256(abi.encodePacked( + EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH, + salt, + bytes32(signer), + keccak256(abi.encodePacked(data)) + )); } /// @dev Executes an exchange method call in the context of signer. @@ -80,7 +83,7 @@ contract MixinTransactions is REENTRANCY_ILLEGAL ); - bytes32 transactionHash = getExecuteTransactionHash(salt, signer, data); + bytes32 transactionHash = hashEIP712Message(hashZeroExTransaction(salt, signer, data)); // Validate transaction has not been executed require( diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol index 7704e3db4..991137f32 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol @@ -19,44 +19,37 @@ pragma solidity ^0.4.24; contract LibEIP712 { - string public constant EIP191_HEADER = "\x19\x01"; - - bytes32 public constant EIP712_DOMAIN_SEPARATOR_NAME_HASH = keccak256("0x Protocol"); - - bytes32 public constant EIP712_DOMAIN_SEPARATOR_VERSION_HASH = keccak256("2"); - - bytes32 public constant EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256( - abi.encodePacked( - "DomainSeparator(", - "string name,", - "string version,", - "address contract", - ")" - ) - ); + // EIP191 header for EIP712 prefix + string constant EIP191_HEADER = "\x19\x01"; + + // Hash of the EIP712 Domain Separator Schema + bytes32 public constant EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked( + "EIP712Domain(", + "string name,", + "string version,", + "address verifyingContract", + ")" + )); + + // Hash of the EIP712 Domain Separator data + bytes32 public EIP712_DOMAIN_HASH; + + constructor () + public + { + EIP712_DOMAIN_HASH = keccak256(abi.encodePacked( + EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH, + keccak256(abi.encodePacked("0x Protocol")), + keccak256(abi.encodePacked("2")), + bytes32(address(this)) + )); + } - function createEIP712Message(bytes32 hashStruct) + function hashEIP712Message(bytes32 hashStruct) internal view - returns (bytes32 message) + returns (bytes32) { - // TODO: EIP712 is not finalized yet - // Source: https://github.com/ethereum/EIPs/pull/712 - // TODO: Cache the Domain Separator - message = keccak256( - abi.encodePacked( - EIP191_HEADER, - keccak256( - abi.encodePacked( - EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH, - EIP712_DOMAIN_SEPARATOR_NAME_HASH, - EIP712_DOMAIN_SEPARATOR_VERSION_HASH, - bytes32(address(this)) - ) - ), - hashStruct - ) - ); - return message; + return keccak256(abi.encodePacked(EIP191_HEADER, EIP712_DOMAIN_HASH, hashStruct)); } -} \ No newline at end of file +} diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol index ed532fd59..04374f1d0 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol @@ -24,8 +24,8 @@ contract LibOrder is LibEIP712 { - bytes32 constant EIP712_ORDER_SCHEMA_HASH = keccak256( - abi.encodePacked( + // Hash for the EIP712 Order Schema + bytes32 constant EIP712_ORDER_SCHEMA_HASH = keccak256(abi.encodePacked( "Order(", "address makerAddress,", "address takerAddress,", @@ -40,7 +40,8 @@ contract LibOrder is "bytes makerAssetData,", "bytes takerAssetData", ")" - )); + ) + ); // A valid order remains fillable until it is expired, fully filled, or cancelled. // An order's state is unaffected by external factors, like account balances. @@ -86,25 +87,32 @@ contract LibOrder is view returns (bytes32 orderHash) { - orderHash = createEIP712Message( - keccak256( - abi.encodePacked( - EIP712_ORDER_SCHEMA_HASH, - bytes32(order.makerAddress), - bytes32(order.takerAddress), - bytes32(order.feeRecipientAddress), - bytes32(order.senderAddress), - order.makerAssetAmount, - order.takerAssetAmount, - order.makerFee, - order.takerFee, - order.expirationTimeSeconds, - order.salt, - keccak256(abi.encodePacked(order.makerAssetData)), - keccak256(abi.encodePacked(order.takerAssetData)) - ) - ) - ); + orderHash = hashEIP712Message(hashOrder(order)); return orderHash; } + + /// @dev Calculates EIP712 hash of the order. + /// @param order The order structure. + /// @return EIP712 hash of the order. + function hashOrder(Order memory order) + internal + pure + returns (bytes32) + { + return keccak256(abi.encodePacked( + EIP712_ORDER_SCHEMA_HASH, + bytes32(order.makerAddress), + bytes32(order.takerAddress), + bytes32(order.feeRecipientAddress), + bytes32(order.senderAddress), + order.makerAssetAmount, + order.takerAssetAmount, + order.makerFee, + order.takerFee, + order.expirationTimeSeconds, + order.salt, + keccak256(abi.encodePacked(order.makerAssetData)), + keccak256(abi.encodePacked(order.takerAssetData)) + )); + } } diff --git a/packages/contracts/src/utils/transaction_factory.ts b/packages/contracts/src/utils/transaction_factory.ts index bb15a0309..c0a32b83a 100644 --- a/packages/contracts/src/utils/transaction_factory.ts +++ b/packages/contracts/src/utils/transaction_factory.ts @@ -1,16 +1,16 @@ -import { crypto, EIP712Schema, EIP712Utils, generatePseudoRandomSalt } from '@0xproject/order-utils'; +import { crypto, EIP712Schema, EIP712Types, EIP712Utils, generatePseudoRandomSalt } from '@0xproject/order-utils'; import { SignatureType } from '@0xproject/types'; import * as ethUtil from 'ethereumjs-util'; import { signingUtils } from './signing_utils'; import { SignedTransaction } from './types'; -const EIP712_EXECUTE_TRANSACTION_SCHEMA: EIP712Schema = { - name: 'ExecuteTransaction', +const EIP712_ZEROEX_TRANSACTION_SCHEMA: EIP712Schema = { + name: 'ZeroExTransaction', parameters: [ - { name: 'salt', type: 'uint256' }, - { name: 'signer', type: 'address' }, - { name: 'data', type: 'bytes' }, + { name: 'salt', type: EIP712Types.Uint256 }, + { name: 'signer', type: EIP712Types.Address }, + { name: 'data', type: EIP712Types.Bytes }, ], }; @@ -24,7 +24,7 @@ export class TransactionFactory { this._signerBuff = ethUtil.privateToAddress(this._privateKey); } public newSignedTransaction(data: string, signatureType: SignatureType = SignatureType.EthSign): SignedTransaction { - const executeTransactionSchemaHashBuff = EIP712Utils.compileSchema(EIP712_EXECUTE_TRANSACTION_SCHEMA); + const executeTransactionSchemaHashBuff = EIP712Utils.compileSchema(EIP712_ZEROEX_TRANSACTION_SCHEMA); const salt = generatePseudoRandomSalt(); const signer = `0x${this._signerBuff.toString('hex')}`; const executeTransactionData = { @@ -33,7 +33,7 @@ export class TransactionFactory { data, }; const executeTransactionHashBuff = EIP712Utils.structHash( - EIP712_EXECUTE_TRANSACTION_SCHEMA, + EIP712_ZEROEX_TRANSACTION_SCHEMA, executeTransactionData, ); const txHash = EIP712Utils.createEIP712Message(executeTransactionHashBuff, this._exchangeAddress); diff --git a/packages/order-utils/src/eip712_utils.ts b/packages/order-utils/src/eip712_utils.ts index e54ca0e83..00edd2531 100644 --- a/packages/order-utils/src/eip712_utils.ts +++ b/packages/order-utils/src/eip712_utils.ts @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import { assetProxyUtils } from './asset_proxy_utils'; import { crypto } from './crypto'; -import { EIP712Schema } from './types'; +import { EIP712Schema, EIP712Types } from './types'; const EIP191_PREFIX = '\x19\x01'; const EIP712_DOMAIN_NAME = '0x Protocol'; @@ -12,22 +12,14 @@ const EIP712_DOMAIN_VERSION = '2'; const EIP712_VALUE_LENGTH = 32; const EIP712_DOMAIN_SCHEMA: EIP712Schema = { - name: 'DomainSeparator', + name: 'EIP712Domain', parameters: [ - { name: 'name', type: 'string' }, - { name: 'version', type: 'string' }, - { name: 'contract', type: 'address' }, + { name: 'name', type: EIP712Types.String }, + { name: 'version', type: EIP712Types.String }, + { name: 'verifyingContract', type: EIP712Types.Address }, ], }; -enum EIP712Types { - String = 'string', - Bytes = 'bytes', - Address = 'address', - Bytes32 = 'bytes32', - Uint256 = 'uint256', -} - export const EIP712Utils = { /** * Compiles the EIP712Schema and returns the hash of the schema. @@ -67,7 +59,7 @@ export const EIP712Utils = { const encodedData = EIP712Utils._encodeData(EIP712_DOMAIN_SCHEMA, { name: EIP712_DOMAIN_NAME, version: EIP712_DOMAIN_VERSION, - contract: exchangeAddress, + verifyingContract: exchangeAddress, }); const domainSeparatorHashBuff2 = crypto.solSHA3([domainSeparatorSchemaBuffer, ...encodedData]); return domainSeparatorHashBuff2; @@ -79,18 +71,14 @@ export const EIP712Utils = { return encodedType; }, _encodeData(schema: EIP712Schema, data: { [key: string]: any }): any { - const encodedTypes = []; const encodedValues = []; for (const parameter of schema.parameters) { const value = data[parameter.name]; if (parameter.type === EIP712Types.String || parameter.type === EIP712Types.Bytes) { - encodedTypes.push(EIP712Types.Bytes32); encodedValues.push(crypto.solSHA3([ethUtil.toBuffer(value)])); } else if (parameter.type === EIP712Types.Uint256) { - encodedTypes.push(EIP712Types.Uint256); encodedValues.push(value); } else if (parameter.type === EIP712Types.Address) { - encodedTypes.push(EIP712Types.Address); encodedValues.push(EIP712Utils.pad32Address(value)); } else { throw new Error(`Unable to encode ${parameter.type}`); diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index caa996621..5122e60fc 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -4,7 +4,7 @@ export { orderFactory } from './order_factory'; export { constants } from './constants'; export { crypto } from './crypto'; export { generatePseudoRandomSalt } from './salt'; -export { OrderError, MessagePrefixType, MessagePrefixOpts, EIP712Parameter, EIP712Schema } 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 { RemainingFillableCalculator } from './remaining_fillable_calculator'; diff --git a/packages/order-utils/src/order_hash.ts b/packages/order-utils/src/order_hash.ts index 46cce4b3b..61f6c978f 100644 --- a/packages/order-utils/src/order_hash.ts +++ b/packages/order-utils/src/order_hash.ts @@ -10,25 +10,25 @@ import * as _ from 'lodash'; import { assert } from './assert'; import { crypto } from './crypto'; import { EIP712Utils } from './eip712_utils'; -import { EIP712Schema } from './types'; +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: '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' }, + { 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 }, ], }; diff --git a/packages/order-utils/src/types.ts b/packages/order-utils/src/types.ts index 858c0e18b..daa4aee11 100644 --- a/packages/order-utils/src/types.ts +++ b/packages/order-utils/src/types.ts @@ -26,10 +26,18 @@ export interface MessagePrefixOpts { export interface EIP712Parameter { name: string; - type: string; + type: EIP712Types; } export interface EIP712Schema { name: string; parameters: EIP712Parameter[]; } + +export enum EIP712Types { + String = 'string', + Bytes = 'bytes', + Address = 'address', + Bytes32 = 'bytes32', + Uint256 = 'uint256', +} -- cgit v1.2.3 From 560a55bfd1a05bda0d4fb4b97d76e9ea0d9301a7 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Tue, 19 Jun 2018 13:30:13 +1000 Subject: Optimize and remove redundant encodePacked --- .../current/protocol/Exchange/MixinTransactions.sol | 8 ++++---- .../current/protocol/Exchange/libs/LibEIP712.sol | 19 ++++++++++++++----- .../current/protocol/Exchange/libs/LibOrder.sol | 6 ++++-- packages/contracts/src/utils/transaction_factory.ts | 3 +-- packages/order-utils/src/eip712_utils.ts | 2 -- packages/order-utils/src/order_hash.ts | 3 --- 6 files changed, 23 insertions(+), 18 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol index 9bdaa0f8f..f1332363c 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol @@ -53,14 +53,14 @@ contract MixinTransactions is /// @return EIP712 hash of the Transaction. function hashZeroExTransaction(uint256 salt, address signer, bytes data) internal - view + pure returns (bytes32) { - return keccak256(abi.encodePacked( + return keccak256(abi.encode( EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH, salt, - bytes32(signer), - keccak256(abi.encodePacked(data)) + signer, + keccak256(data) )); } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol index 991137f32..b983347a4 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol @@ -22,6 +22,12 @@ contract LibEIP712 { // EIP191 header for EIP712 prefix string constant EIP191_HEADER = "\x19\x01"; + // EIP712 Domain Name value + string constant EIP712_DOMAIN_NAME = "0x Protocol"; + + // EIP712 Domain Version value + string constant EIP712_DOMAIN_VERSION = "2"; + // Hash of the EIP712 Domain Separator Schema bytes32 public constant EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked( "EIP712Domain(", @@ -35,16 +41,19 @@ contract LibEIP712 { bytes32 public EIP712_DOMAIN_HASH; constructor () - public + public { - EIP712_DOMAIN_HASH = keccak256(abi.encodePacked( + EIP712_DOMAIN_HASH = keccak256(abi.encode( EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH, - keccak256(abi.encodePacked("0x Protocol")), - keccak256(abi.encodePacked("2")), - bytes32(address(this)) + keccak256(bytes(EIP712_DOMAIN_NAME)), + keccak256(bytes(EIP712_DOMAIN_VERSION)), + address(this) )); } + /// @dev Calculates EIP712 encoding for a hash struct in this EIP712 Domain. + /// @param hashStruct The EIP712 hash struct. + /// @return EIP712 hash applied to this EIP712 Domain. function hashEIP712Message(bytes32 hashStruct) internal view diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol index 04374f1d0..b21de6927 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol @@ -99,6 +99,8 @@ contract LibOrder is pure returns (bytes32) { + // Note: changing this to abi.encode results in an error on 0.4.24 + // UnimplementedFeatureError: Variable inaccessible, too deep inside stack (17) return keccak256(abi.encodePacked( EIP712_ORDER_SCHEMA_HASH, bytes32(order.makerAddress), @@ -111,8 +113,8 @@ contract LibOrder is order.takerFee, order.expirationTimeSeconds, order.salt, - keccak256(abi.encodePacked(order.makerAssetData)), - keccak256(abi.encodePacked(order.takerAssetData)) + keccak256(order.makerAssetData), + keccak256(order.takerAssetData) )); } } diff --git a/packages/contracts/src/utils/transaction_factory.ts b/packages/contracts/src/utils/transaction_factory.ts index c0a32b83a..19ef4e1bf 100644 --- a/packages/contracts/src/utils/transaction_factory.ts +++ b/packages/contracts/src/utils/transaction_factory.ts @@ -1,4 +1,4 @@ -import { crypto, EIP712Schema, EIP712Types, EIP712Utils, generatePseudoRandomSalt } from '@0xproject/order-utils'; +import { EIP712Schema, EIP712Types, EIP712Utils, generatePseudoRandomSalt } from '@0xproject/order-utils'; import { SignatureType } from '@0xproject/types'; import * as ethUtil from 'ethereumjs-util'; @@ -24,7 +24,6 @@ export class TransactionFactory { this._signerBuff = ethUtil.privateToAddress(this._privateKey); } public newSignedTransaction(data: string, signatureType: SignatureType = SignatureType.EthSign): SignedTransaction { - const executeTransactionSchemaHashBuff = EIP712Utils.compileSchema(EIP712_ZEROEX_TRANSACTION_SCHEMA); const salt = generatePseudoRandomSalt(); const signer = `0x${this._signerBuff.toString('hex')}`; const executeTransactionData = { diff --git a/packages/order-utils/src/eip712_utils.ts b/packages/order-utils/src/eip712_utils.ts index 00edd2531..2594e6d6d 100644 --- a/packages/order-utils/src/eip712_utils.ts +++ b/packages/order-utils/src/eip712_utils.ts @@ -1,8 +1,6 @@ -import { BigNumber } from '@0xproject/utils'; import ethUtil = require('ethereumjs-util'); import * as _ from 'lodash'; -import { assetProxyUtils } from './asset_proxy_utils'; import { crypto } from './crypto'; import { EIP712Schema, EIP712Types } from './types'; diff --git a/packages/order-utils/src/order_hash.ts b/packages/order-utils/src/order_hash.ts index 074624e9e..54c500653 100644 --- a/packages/order-utils/src/order_hash.ts +++ b/packages/order-utils/src/order_hash.ts @@ -1,11 +1,8 @@ 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'; -- cgit v1.2.3 From 9a2e023a514e9204a1c91ee7d9032b61fbd157de Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Tue, 19 Jun 2018 12:55:04 +0200 Subject: Assembler orderHash function --- .../current/protocol/Exchange/libs/LibOrder.sol | 40 ++++++++++++---------- 1 file changed, 22 insertions(+), 18 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol index b21de6927..cf9df2c3d 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol @@ -97,24 +97,28 @@ contract LibOrder is function hashOrder(Order memory order) internal pure - returns (bytes32) + returns (bytes32 result) { - // Note: changing this to abi.encode results in an error on 0.4.24 - // UnimplementedFeatureError: Variable inaccessible, too deep inside stack (17) - return keccak256(abi.encodePacked( - EIP712_ORDER_SCHEMA_HASH, - bytes32(order.makerAddress), - bytes32(order.takerAddress), - bytes32(order.feeRecipientAddress), - bytes32(order.senderAddress), - order.makerAssetAmount, - order.takerAssetAmount, - order.makerFee, - order.takerFee, - order.expirationTimeSeconds, - order.salt, - keccak256(order.makerAssetData), - keccak256(order.takerAssetData) - )); + bytes32 schemaHash = EIP712_ORDER_SCHEMA_HASH; + bytes32 makerAssetDataHash = keccak256(order.makerAssetData); + bytes32 takerAssetDataHash = keccak256(order.takerAssetData); + assembly { + // Backup + let temp1 := mload(sub(order, 32)) + let temp2 := mload(add(order, 320)) + let temp3 := mload(add(order, 352)) + + // Hash in place + mstore(sub(order, 32), schemaHash) + mstore(add(order, 320), makerAssetDataHash) + mstore(add(order, 352), takerAssetDataHash) + result := keccak256(sub(order, 32), 416) + + // Restore + mstore(sub(order, 32), temp1) + mstore(add(order, 320), temp2) + mstore(add(order, 352), temp3) + } + return result; } } -- cgit v1.2.3 From 512bd84cc2478267eb359f340cdde8a110c61572 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Wed, 20 Jun 2018 10:27:45 +1000 Subject: Fix closing parens in liborder --- .../current/protocol/Exchange/libs/LibOrder.sol | 31 +++++++++++----------- 1 file changed, 15 insertions(+), 16 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol index cf9df2c3d..bfc7aaae0 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol @@ -26,22 +26,21 @@ contract LibOrder is // Hash for the EIP712 Order Schema bytes32 constant EIP712_ORDER_SCHEMA_HASH = keccak256(abi.encodePacked( - "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", - ")" - ) - ); + "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", + ")" + )); // A valid order remains fillable until it is expired, fully filled, or cancelled. // An order's state is unaffected by external factors, like account balances. -- cgit v1.2.3