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
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -19,44 +19,37 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
contract LibEIP712 {
|
||||
string public constant EIP191_HEADER = "\x19\x01";
|
||||
// EIP191 header for EIP712 prefix
|
||||
string constant EIP191_HEADER = "\x19\x01";
|
||||
|
||||
bytes32 public constant EIP712_DOMAIN_SEPARATOR_NAME_HASH = keccak256("0x Protocol");
|
||||
// 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",
|
||||
")"
|
||||
));
|
||||
|
||||
bytes32 public constant EIP712_DOMAIN_SEPARATOR_VERSION_HASH = keccak256("2");
|
||||
// Hash of the EIP712 Domain Separator data
|
||||
bytes32 public EIP712_DOMAIN_HASH;
|
||||
|
||||
bytes32 public constant EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(
|
||||
abi.encodePacked(
|
||||
"DomainSeparator(",
|
||||
"string name,",
|
||||
"string version,",
|
||||
"address contract",
|
||||
")"
|
||||
)
|
||||
);
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 },
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -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',
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user