aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol35
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol55
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol61
-rw-r--r--packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol4
-rw-r--r--packages/contracts/src/utils/eip712_utils.ts63
-rw-r--r--packages/contracts/src/utils/order_factory.ts6
-rw-r--r--packages/contracts/src/utils/order_utils.ts56
-rw-r--r--packages/contracts/src/utils/transaction_factory.ts30
-rw-r--r--packages/contracts/src/utils/types.ts10
-rw-r--r--packages/contracts/test/exchange/libs.ts9
10 files changed, 279 insertions, 50 deletions
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', () => {