aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAmir Bandeali <abandeali1@gmail.com>2019-02-06 02:03:30 +0800
committerGitHub <noreply@github.com>2019-02-06 02:03:30 +0800
commitb5eb47f60930d040b94177023cd147834f98db1d (patch)
tree62ea668a6bbb6394b472e5433756e052e447b682
parent69c7c03fb34b3f21f65c40b73baa21184a296fb2 (diff)
parentaf4ed0f39c9eb3e314a6b05de9cedab98e7cb233 (diff)
downloaddexon-0x-contracts-b5eb47f60930d040b94177023cd147834f98db1d.tar
dexon-0x-contracts-b5eb47f60930d040b94177023cd147834f98db1d.tar.gz
dexon-0x-contracts-b5eb47f60930d040b94177023cd147834f98db1d.tar.bz2
dexon-0x-contracts-b5eb47f60930d040b94177023cd147834f98db1d.tar.lz
dexon-0x-contracts-b5eb47f60930d040b94177023cd147834f98db1d.tar.xz
dexon-0x-contracts-b5eb47f60930d040b94177023cd147834f98db1d.tar.zst
dexon-0x-contracts-b5eb47f60930d040b94177023cd147834f98db1d.zip
Merge pull request #1576 from 0xProject/feat/transaction-hash
Add transactionHashUtils to order-utils package
-rw-r--r--contracts/exchange/test/transactions.ts5
-rw-r--r--contracts/exchange/test/utils/exchange_wrapper.ts13
-rw-r--r--contracts/test-utils/CHANGELOG.json4
-rw-r--r--contracts/test-utils/src/index.ts1
-rw-r--r--contracts/test-utils/src/transaction_factory.ts26
-rw-r--r--contracts/test-utils/src/types.ts8
-rw-r--r--packages/contract-wrappers/CHANGELOG.json10
-rw-r--r--packages/contract-wrappers/src/utils/transaction_encoder.ts19
-rw-r--r--packages/contract-wrappers/test/transaction_encoder_test.ts4
-rw-r--r--packages/json-schemas/CHANGELOG.json4
-rw-r--r--packages/json-schemas/schemas/zero_ex_transaction_schema.json3
-rw-r--r--packages/order-utils/CHANGELOG.json17
-rw-r--r--packages/order-utils/src/constants.ts10
-rw-r--r--packages/order-utils/src/eip712_utils.ts55
-rw-r--r--packages/order-utils/src/index.ts12
-rw-r--r--packages/order-utils/src/order_hash.ts17
-rw-r--r--packages/order-utils/src/transaction_hash.ts46
-rw-r--r--packages/order-utils/test/eip712_utils_test.ts55
-rw-r--r--packages/order-utils/test/order_hash_test.ts5
-rw-r--r--packages/order-utils/test/transaction_hash_test.ts56
-rw-r--r--packages/types/CHANGELOG.json8
-rw-r--r--packages/types/src/index.ts11
-rw-r--r--python-packages/json_schemas/src/zero_ex/json_schemas/schemas/zero_ex_transaction_schema.json3
23 files changed, 298 insertions, 94 deletions
diff --git a/contracts/exchange/test/transactions.ts b/contracts/exchange/test/transactions.ts
index 613953493..4f8c07579 100644
--- a/contracts/exchange/test/transactions.ts
+++ b/contracts/exchange/test/transactions.ts
@@ -8,14 +8,13 @@ import {
OrderFactory,
orderUtils,
provider,
- SignedTransaction,
TransactionFactory,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
-import { OrderWithoutExchangeAddress, RevertReason, SignedOrder } from '@0x/types';
+import { OrderWithoutExchangeAddress, RevertReason, SignedOrder, SignedZeroExTransaction } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import * as _ from 'lodash';
@@ -41,7 +40,7 @@ describe('Exchange transactions', () => {
let erc20Balances: ERC20BalancesByOwner;
let signedOrder: SignedOrder;
- let signedTx: SignedTransaction;
+ let signedTx: SignedZeroExTransaction;
let orderWithoutExchangeAddress: OrderWithoutExchangeAddress;
let orderFactory: OrderFactory;
let makerTransactionFactory: TransactionFactory;
diff --git a/contracts/exchange/test/utils/exchange_wrapper.ts b/contracts/exchange/test/utils/exchange_wrapper.ts
index eca535c65..03c37c4a9 100644
--- a/contracts/exchange/test/utils/exchange_wrapper.ts
+++ b/contracts/exchange/test/utils/exchange_wrapper.ts
@@ -1,14 +1,7 @@
import { artifacts as erc20Artifacts } from '@0x/contracts-erc20';
import { artifacts as erc721Artifacts } from '@0x/contracts-erc721';
-import {
- FillResults,
- formatters,
- LogDecoder,
- OrderInfo,
- orderUtils,
- SignedTransaction,
-} from '@0x/contracts-test-utils';
-import { SignedOrder } from '@0x/types';
+import { FillResults, formatters, LogDecoder, OrderInfo, orderUtils } from '@0x/contracts-test-utils';
+import { SignedOrder, SignedZeroExTransaction } from '@0x/types';
import { AbiEncoder, BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { MethodAbi, Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
@@ -206,7 +199,7 @@ export class ExchangeWrapper {
return tx;
}
public async executeTransactionAsync(
- signedTx: SignedTransaction,
+ signedTx: SignedZeroExTransaction,
from: string,
): Promise<TransactionReceiptWithDecodedLogs> {
const txHash = await this._exchange.executeTransaction.sendTransactionAsync(
diff --git a/contracts/test-utils/CHANGELOG.json b/contracts/test-utils/CHANGELOG.json
index 51b726e82..0c6174313 100644
--- a/contracts/test-utils/CHANGELOG.json
+++ b/contracts/test-utils/CHANGELOG.json
@@ -5,6 +5,10 @@
{
"note": "Upgrade the bignumber.js to v8.0.2",
"pr": 1517
+ },
+ {
+ "note": "Import `ZeroExTransaction` type instead of using type defined in this package",
+ "pr": 1576
}
],
"timestamp": 1549373905
diff --git a/contracts/test-utils/src/index.ts b/contracts/test-utils/src/index.ts
index 7880de0bf..5ac927e47 100644
--- a/contracts/test-utils/src/index.ts
+++ b/contracts/test-utils/src/index.ts
@@ -31,7 +31,6 @@ export {
MarketBuyOrders,
MarketSellOrders,
ERC721TokenIdsByOwner,
- SignedTransaction,
OrderStatus,
AllowanceAmountScenario,
AssetDataScenario,
diff --git a/contracts/test-utils/src/transaction_factory.ts b/contracts/test-utils/src/transaction_factory.ts
index dbab3ade4..e6cd4c23f 100644
--- a/contracts/test-utils/src/transaction_factory.ts
+++ b/contracts/test-utils/src/transaction_factory.ts
@@ -1,10 +1,8 @@
-import { eip712Utils, generatePseudoRandomSalt } from '@0x/order-utils';
-import { SignatureType } from '@0x/types';
-import { signTypedDataUtils } from '@0x/utils';
+import { generatePseudoRandomSalt, transactionHashUtils } from '@0x/order-utils';
+import { SignatureType, SignedZeroExTransaction } from '@0x/types';
import * as ethUtil from 'ethereumjs-util';
import { signingUtils } from './signing_utils';
-import { SignedTransaction } from './types';
export class TransactionFactory {
private readonly _signerBuff: Buffer;
@@ -15,23 +13,25 @@ export class TransactionFactory {
this._exchangeAddress = exchangeAddress;
this._signerBuff = ethUtil.privateToAddress(this._privateKey);
}
- public newSignedTransaction(data: string, signatureType: SignatureType = SignatureType.EthSign): SignedTransaction {
+ public newSignedTransaction(
+ data: string,
+ signatureType: SignatureType = SignatureType.EthSign,
+ ): SignedZeroExTransaction {
const salt = generatePseudoRandomSalt();
const signerAddress = `0x${this._signerBuff.toString('hex')}`;
- const executeTransactionData = {
+ const transaction = {
salt,
signerAddress,
data,
+ verifyingContractAddress: this._exchangeAddress,
};
- const typedData = eip712Utils.createZeroExTransactionTypedData(executeTransactionData, this._exchangeAddress);
- const eip712MessageBuffer = signTypedDataUtils.generateTypedDataHash(typedData);
- const signature = signingUtils.signMessage(eip712MessageBuffer, this._privateKey, signatureType);
- const signedTx = {
- exchangeAddress: this._exchangeAddress,
+ const transactionHashBuffer = transactionHashUtils.getTransactionHashBuffer(transaction);
+ const signature = signingUtils.signMessage(transactionHashBuffer, this._privateKey, signatureType);
+ const signedTransaction = {
+ ...transaction,
signature: `0x${signature.toString('hex')}`,
- ...executeTransactionData,
};
- return signedTx;
+ return signedTransaction;
}
}
diff --git a/contracts/test-utils/src/types.ts b/contracts/test-utils/src/types.ts
index 60cb2b102..16c3a9f3d 100644
--- a/contracts/test-utils/src/types.ts
+++ b/contracts/test-utils/src/types.ts
@@ -107,14 +107,6 @@ export enum ContractName {
BalanceThresholdFilter = 'BalanceThresholdFilter',
}
-export interface SignedTransaction {
- exchangeAddress: string;
- salt: BigNumber;
- signerAddress: string;
- data: string;
- signature: string;
-}
-
export interface TransferAmountsByMatchOrders {
// Left Maker
amountBoughtByLeftMaker: BigNumber;
diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json
index 413563f80..ae3176537 100644
--- a/packages/contract-wrappers/CHANGELOG.json
+++ b/packages/contract-wrappers/CHANGELOG.json
@@ -1,10 +1,18 @@
[
{
- "version": "6.0.1",
+ "version": "7.0.0",
"changes": [
{
"note": "Fix OrderValidatorWrapper constructor to use the correct address",
"pr": 1568
+ },
+ {
+ "note": "Use new `ZeroExTransaction` interface",
+ "pr": 1576
+ },
+ {
+ "note": "Rename `getTransactionHex` to `getTransactionHashHex`",
+ "pr": 1576
}
],
"timestamp": 1549373905
diff --git a/packages/contract-wrappers/src/utils/transaction_encoder.ts b/packages/contract-wrappers/src/utils/transaction_encoder.ts
index 0cf08a8fe..307487a9b 100644
--- a/packages/contract-wrappers/src/utils/transaction_encoder.ts
+++ b/packages/contract-wrappers/src/utils/transaction_encoder.ts
@@ -1,9 +1,9 @@
import { ExchangeContract } from '@0x/abi-gen-wrappers';
import { schemas } from '@0x/json-schemas';
-import { eip712Utils } from '@0x/order-utils';
+import { transactionHashUtils } from '@0x/order-utils';
import { Order, SignedOrder } from '@0x/types';
-import { BigNumber, signTypedDataUtils } from '@0x/utils';
+import { BigNumber } from '@0x/utils';
import _ = require('lodash');
import { assert } from './assert';
@@ -19,23 +19,22 @@ export class TransactionEncoder {
this._exchangeInstance = exchangeInstance;
}
/**
- * Encodes the transaction data for use with the Exchange contract.
+ * Hashes the transaction data for use with the Exchange contract.
* @param data The ABI Encoded 0x Exchange method. I.e fillOrder
* @param salt A random value to provide uniqueness and prevent replay attacks.
* @param signerAddress The address which will sign this transaction.
- * @return An unsigned hex encoded transaction for use in 0x Exchange executeTransaction.
+ * @return The hash of the 0x transaction.
*/
- public getTransactionHex(data: string, salt: BigNumber, signerAddress: string): string {
+ public getTransactionHashHex(data: string, salt: BigNumber, signerAddress: string): string {
const exchangeAddress = this._getExchangeContract().address;
- const executeTransactionData = {
+ const transaction = {
+ verifyingContractAddress: exchangeAddress,
salt,
signerAddress,
data,
};
- const typedData = eip712Utils.createZeroExTransactionTypedData(executeTransactionData, exchangeAddress);
- const eip712MessageBuffer = signTypedDataUtils.generateTypedDataHash(typedData);
- const messageHex = `0x${eip712MessageBuffer.toString('hex')}`;
- return messageHex;
+ const hashHex = transactionHashUtils.getTransactionHashHex(transaction);
+ return hashHex;
}
/**
* Encodes a fillOrder transaction.
diff --git a/packages/contract-wrappers/test/transaction_encoder_test.ts b/packages/contract-wrappers/test/transaction_encoder_test.ts
index ef9eb2cf3..a996b9f08 100644
--- a/packages/contract-wrappers/test/transaction_encoder_test.ts
+++ b/packages/contract-wrappers/test/transaction_encoder_test.ts
@@ -83,8 +83,8 @@ describe('TransactionEncoder', () => {
signerAddress: string = takerAddress,
): Promise<void> => {
const salt = generatePseudoRandomSalt();
- const encodedTransaction = encoder.getTransactionHex(data, salt, signerAddress);
- const signature = await signatureUtils.ecSignHashAsync(provider, encodedTransaction, signerAddress);
+ const transactionHash = encoder.getTransactionHashHex(data, salt, signerAddress);
+ const signature = await signatureUtils.ecSignHashAsync(provider, transactionHash, signerAddress);
txHash = await contractWrappers.exchange.executeTransactionAsync(
salt,
signerAddress,
diff --git a/packages/json-schemas/CHANGELOG.json b/packages/json-schemas/CHANGELOG.json
index 869f8bdf1..e174975d1 100644
--- a/packages/json-schemas/CHANGELOG.json
+++ b/packages/json-schemas/CHANGELOG.json
@@ -5,6 +5,10 @@
{
"note": "Upgrade the bignumber.js to v8.0.2",
"pr": 1517
+ },
+ {
+ "note": "Add `verifyingContractAddress` to `zeroExTransactionSchema`",
+ "pr": 1576
}
],
"timestamp": 1549373905
diff --git a/packages/json-schemas/schemas/zero_ex_transaction_schema.json b/packages/json-schemas/schemas/zero_ex_transaction_schema.json
index 0c714f14d..ffdf1f229 100644
--- a/packages/json-schemas/schemas/zero_ex_transaction_schema.json
+++ b/packages/json-schemas/schemas/zero_ex_transaction_schema.json
@@ -1,10 +1,11 @@
{
"id": "/zeroExTransactionSchema",
"properties": {
+ "verifyingContractAddress": { "$ref": "/addressSchema" },
"data": { "$ref": "/hexSchema" },
"signerAddress": { "$ref": "/addressSchema" },
"salt": { "$ref": "/wholeNumberSchema" }
},
- "required": ["data", "salt", "signerAddress"],
+ "required": ["verifyingContractAddress", "data", "salt", "signerAddress"],
"type": "object"
}
diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json
index 5169208c9..b13ce197a 100644
--- a/packages/order-utils/CHANGELOG.json
+++ b/packages/order-utils/CHANGELOG.json
@@ -1,5 +1,22 @@
[
{
+ "version": "5.0.0",
+ "changes": [
+ {
+ "note": "Add `transactionHashUtils`",
+ "pr": 1576
+ },
+ {
+ "note": "Refactor `eip712Utils` to allow custom domain params",
+ "pr": 1576
+ },
+ {
+ "note": "Export constant EIP712 params",
+ "pr": 1576
+ }
+ ]
+ },
+ {
"version": "4.0.0",
"changes": [
{
diff --git a/packages/order-utils/src/constants.ts b/packages/order-utils/src/constants.ts
index a9a687719..3c93575b3 100644
--- a/packages/order-utils/src/constants.ts
+++ b/packages/order-utils/src/constants.ts
@@ -68,9 +68,9 @@ export const constants = {
SELECTOR_CHAR_LENGTH_WITH_PREFIX: 10,
INFINITE_TIMESTAMP_SEC: new BigNumber(2524604400), // Close to infinite
ZERO_AMOUNT: new BigNumber(0),
- EIP712_DOMAIN_NAME: '0x Protocol',
- EIP712_DOMAIN_VERSION: '2',
- EIP712_DOMAIN_SCHEMA: {
+ EXCHANGE_DOMAIN_NAME: '0x Protocol',
+ EXCHANGE_DOMAIN_VERSION: '2',
+ DEFAULT_DOMAIN_SCHEMA: {
name: 'EIP712Domain',
parameters: [
{ name: 'name', type: 'string' },
@@ -78,7 +78,7 @@ export const constants = {
{ name: 'verifyingContract', type: 'address' },
],
},
- EIP712_ORDER_SCHEMA: {
+ EXCHANGE_ORDER_SCHEMA: {
name: 'Order',
parameters: [
{ name: 'makerAddress', type: 'address' },
@@ -95,7 +95,7 @@ export const constants = {
{ name: 'takerAssetData', type: 'bytes' },
],
},
- EIP712_ZEROEX_TRANSACTION_SCHEMA: {
+ EXCHANGE_ZEROEX_TRANSACTION_SCHEMA: {
name: 'ZeroExTransaction',
parameters: [
{ name: 'salt', type: 'uint256' },
diff --git a/packages/order-utils/src/eip712_utils.ts b/packages/order-utils/src/eip712_utils.ts
index 385fda989..313653c63 100644
--- a/packages/order-utils/src/eip712_utils.ts
+++ b/packages/order-utils/src/eip712_utils.ts
@@ -1,36 +1,49 @@
import { assert } from '@0x/assert';
import { schemas } from '@0x/json-schemas';
-import { EIP712Object, EIP712TypedData, EIP712Types, Order, ZeroExTransaction } from '@0x/types';
+import {
+ EIP712DomainWithDefaultSchema,
+ EIP712Object,
+ EIP712TypedData,
+ EIP712Types,
+ Order,
+ ZeroExTransaction,
+} from '@0x/types';
import * as _ from 'lodash';
import { constants } from './constants';
+export const DEFAULT_DOMAIN_SCHEMA = constants.DEFAULT_DOMAIN_SCHEMA;
+export const EXCHANGE_DOMAIN_NAME = constants.EXCHANGE_DOMAIN_NAME;
+export const EXCHANGE_DOMAIN_VERSION = constants.EXCHANGE_DOMAIN_VERSION;
+export const EXCHANGE_ORDER_SCHEMA = constants.EXCHANGE_ORDER_SCHEMA;
+export const EXCHANGE_ZEROEX_TRANSACTION_SCHEMA = constants.EXCHANGE_ZEROEX_TRANSACTION_SCHEMA;
+
export const eip712Utils = {
/**
* Creates a EIP712TypedData object specific to the 0x protocol for use with signTypedData.
* @param primaryType The primary type found in message
* @param types The additional types for the data in message
* @param message The contents of the message
- * @param exchangeAddress The address of the exchange contract
+ * @param domain Domain containing a name (optional), version (optional), and verifying contract address
* @return A typed data object
*/
createTypedData: (
primaryType: string,
types: EIP712Types,
message: EIP712Object,
- exchangeAddress: string,
+ domain: EIP712DomainWithDefaultSchema,
): EIP712TypedData => {
- assert.isETHAddressHex('exchangeAddress', exchangeAddress);
+ assert.isETHAddressHex('verifyingContractAddress', domain.verifyingContractAddress);
assert.isString('primaryType', primaryType);
const typedData = {
types: {
- EIP712Domain: constants.EIP712_DOMAIN_SCHEMA.parameters,
+ EIP712Domain: DEFAULT_DOMAIN_SCHEMA.parameters,
...types,
},
domain: {
- name: constants.EIP712_DOMAIN_NAME,
- version: constants.EIP712_DOMAIN_VERSION,
- verifyingContract: exchangeAddress,
+ name: _.isUndefined(domain.name) ? EXCHANGE_DOMAIN_NAME : domain.name,
+ version: _.isUndefined(domain.version) ? EXCHANGE_DOMAIN_VERSION : domain.version,
+ verifyingContract: domain.verifyingContractAddress,
},
message,
primaryType,
@@ -48,11 +61,14 @@ export const eip712Utils = {
const normalizedOrder = _.mapValues(order, value => {
return !_.isString(value) ? value.toString() : value;
});
+ const domain = {
+ verifyingContractAddress: order.exchangeAddress,
+ };
const typedData = eip712Utils.createTypedData(
- constants.EIP712_ORDER_SCHEMA.name,
- { Order: constants.EIP712_ORDER_SCHEMA.parameters },
+ EXCHANGE_ORDER_SCHEMA.name,
+ { Order: EXCHANGE_ORDER_SCHEMA.parameters },
normalizedOrder,
- order.exchangeAddress,
+ domain,
);
return typedData;
},
@@ -60,23 +76,22 @@ export const eip712Utils = {
* Creates an ExecuteTransaction EIP712TypedData object for use with signTypedData and
* 0x Exchange executeTransaction.
* @param ZeroExTransaction the 0x transaction
- * @param exchangeAddress The address of the exchange contract
* @return A typed data object
*/
- createZeroExTransactionTypedData: (
- zeroExTransaction: ZeroExTransaction,
- exchangeAddress: string,
- ): EIP712TypedData => {
- assert.isETHAddressHex('exchangeAddress', exchangeAddress);
+ createZeroExTransactionTypedData: (zeroExTransaction: ZeroExTransaction): EIP712TypedData => {
+ assert.isETHAddressHex('verifyingContractAddress', zeroExTransaction.verifyingContractAddress);
assert.doesConformToSchema('zeroExTransaction', zeroExTransaction, schemas.zeroExTransactionSchema);
const normalizedTransaction = _.mapValues(zeroExTransaction, value => {
return !_.isString(value) ? value.toString() : value;
});
+ const domain = {
+ verifyingContractAddress: zeroExTransaction.verifyingContractAddress,
+ };
const typedData = eip712Utils.createTypedData(
- constants.EIP712_ZEROEX_TRANSACTION_SCHEMA.name,
- { ZeroExTransaction: constants.EIP712_ZEROEX_TRANSACTION_SCHEMA.parameters },
+ EXCHANGE_ZEROEX_TRANSACTION_SCHEMA.name,
+ { ZeroExTransaction: EXCHANGE_ZEROEX_TRANSACTION_SCHEMA.parameters },
normalizedTransaction,
- exchangeAddress,
+ domain,
);
return typedData;
},
diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts
index 2150a02e4..436677efc 100644
--- a/packages/order-utils/src/index.ts
+++ b/packages/order-utils/src/index.ts
@@ -3,6 +3,7 @@ export { signatureUtils } from './signature_utils';
export { generatePseudoRandomSalt } from './salt';
export { assetDataUtils } from './asset_data_utils';
export { marketUtils } from './market_utils';
+export { transactionHashUtils } from './transaction_hash';
export { rateUtils } from './rate_utils';
export { sortingUtils } from './sorting_utils';
export { orderParsingUtils } from './parsing_utils';
@@ -18,7 +19,14 @@ export { ExchangeTransferSimulator } from './exchange_transfer_simulator';
export { BalanceAndProxyAllowanceLazyStore } from './store/balance_and_proxy_allowance_lazy_store';
export { OrderFilledCancelledLazyStore } from './store/order_filled_cancelled_lazy_store';
-export { eip712Utils } from './eip712_utils';
+export {
+ eip712Utils,
+ DEFAULT_DOMAIN_SCHEMA,
+ EXCHANGE_DOMAIN_NAME,
+ EXCHANGE_DOMAIN_VERSION,
+ EXCHANGE_ORDER_SCHEMA,
+ EXCHANGE_ZEROEX_TRANSACTION_SCHEMA,
+} from './eip712_utils';
export {
Provider,
@@ -50,7 +58,9 @@ export {
EIP712Types,
EIP712Object,
EIP712ObjectValue,
+ EIP712DomainWithDefaultSchema,
ZeroExTransaction,
+ SignedZeroExTransaction,
} from '@0x/types';
export {
OrderError,
diff --git a/packages/order-utils/src/order_hash.ts b/packages/order-utils/src/order_hash.ts
index c8e9be71e..ce7e6d85f 100644
--- a/packages/order-utils/src/order_hash.ts
+++ b/packages/order-utils/src/order_hash.ts
@@ -4,6 +4,7 @@ import { signTypedDataUtils } from '@0x/utils';
import * as _ from 'lodash';
import { assert } from './assert';
+import { constants } from './constants';
import { eip712Utils } from './eip712_utils';
const INVALID_TAKER_FORMAT = 'instance.takerAddress is not of a type(s) string';
@@ -34,8 +35,9 @@ export const orderHashUtils = {
assert.doesConformToSchema('order', order, schemas.orderSchema, [schemas.hexSchema]);
} catch (error) {
if (_.includes(error.message, INVALID_TAKER_FORMAT)) {
- const errMsg =
- 'Order taker must be of type string. If you want anyone to be able to fill an order - pass ZeroEx.NULL_ADDRESS';
+ const errMsg = `Order taker must be of type string. If you want anyone to be able to fill an order - pass ${
+ constants.NULL_ADDRESS
+ }`;
throw new Error(errMsg);
}
throw error;
@@ -51,6 +53,17 @@ export const orderHashUtils = {
* @return A Buffer containing the resulting orderHash from hashing the supplied order
*/
getOrderHashBuffer(order: SignedOrder | Order): Buffer {
+ try {
+ assert.doesConformToSchema('order', order, schemas.orderSchema, [schemas.hexSchema]);
+ } catch (error) {
+ if (_.includes(error.message, INVALID_TAKER_FORMAT)) {
+ const errMsg = `Order taker must be of type string. If you want anyone to be able to fill an order - pass ${
+ constants.NULL_ADDRESS
+ }`;
+ throw new Error(errMsg);
+ }
+ throw error;
+ }
const typedData = eip712Utils.createOrderTypedData(order);
const orderHashBuff = signTypedDataUtils.generateTypedDataHash(typedData);
return orderHashBuff;
diff --git a/packages/order-utils/src/transaction_hash.ts b/packages/order-utils/src/transaction_hash.ts
new file mode 100644
index 000000000..2b2345af7
--- /dev/null
+++ b/packages/order-utils/src/transaction_hash.ts
@@ -0,0 +1,46 @@
+import { schemas, SchemaValidator } from '@0x/json-schemas';
+import { SignedZeroExTransaction, ZeroExTransaction } from '@0x/types';
+import { signTypedDataUtils } from '@0x/utils';
+import * as _ from 'lodash';
+
+import { assert } from './assert';
+import { eip712Utils } from './eip712_utils';
+
+export const transactionHashUtils = {
+ /**
+ * Checks if the supplied hex encoded 0x transaction hash is valid.
+ * Note: Valid means it has the expected format, not that a transaction with the transactionHash exists.
+ * Use this method when processing transactionHashes submitted as user input.
+ * @param transactionHash Hex encoded transactionHash.
+ * @return Whether the supplied transactionHash has the expected format.
+ */
+ isValidTransactionHash(transactionHash: string): boolean {
+ // Since this method can be called to check if any arbitrary string conforms to an transactionHash's
+ // format, we only assert that we were indeed passed a string.
+ assert.isString('transactionHash', transactionHash);
+ const schemaValidator = new SchemaValidator();
+ const isValid = schemaValidator.validate(transactionHash, schemas.orderHashSchema).valid;
+ return isValid;
+ },
+ /**
+ * Computes the transactionHash for a supplied 0x transaction.
+ * @param transaction An object that conforms to the ZeroExTransaction or SignedZeroExTransaction interface definitions.
+ * @return Hex encoded string transactionHash from hashing the supplied order.
+ */
+ getTransactionHashHex(transaction: ZeroExTransaction | SignedZeroExTransaction): string {
+ assert.doesConformToSchema('transaction', transaction, schemas.zeroExTransactionSchema, [schemas.hexSchema]);
+ const transactionHashBuff = transactionHashUtils.getTransactionHashBuffer(transaction);
+ const transactionHashHex = `0x${transactionHashBuff.toString('hex')}`;
+ return transactionHashHex;
+ },
+ /**
+ * Computes the transactionHash for a supplied 0x transaction.
+ * @param transaction An object that conforms to the ZeroExTransaction or SignedZeroExTransaction interface definitions.
+ * @return A Buffer containing the resulting transactionHash from hashing the supplied 0x transaction.
+ */
+ getTransactionHashBuffer(transaction: ZeroExTransaction | SignedZeroExTransaction): Buffer {
+ const typedData = eip712Utils.createZeroExTransactionTypedData(transaction);
+ const transactionHashBuff = signTypedDataUtils.generateTypedDataHash(typedData);
+ return transactionHashBuff;
+ },
+};
diff --git a/packages/order-utils/test/eip712_utils_test.ts b/packages/order-utils/test/eip712_utils_test.ts
index a54e49958..4208e9beb 100644
--- a/packages/order-utils/test/eip712_utils_test.ts
+++ b/packages/order-utils/test/eip712_utils_test.ts
@@ -3,7 +3,12 @@ import * as chai from 'chai';
import 'mocha';
import { constants } from '../src/constants';
-import { eip712Utils } from '../src/eip712_utils';
+import {
+ eip712Utils,
+ EXCHANGE_DOMAIN_NAME,
+ EXCHANGE_DOMAIN_VERSION,
+ EXCHANGE_ZEROEX_TRANSACTION_SCHEMA,
+} from '../src/eip712_utils';
import { chaiSetup } from './utils/chai_setup';
@@ -12,33 +17,55 @@ const expect = chai.expect;
describe('EIP712 Utils', () => {
describe('createTypedData', () => {
- it('adds in the EIP712DomainSeparator', () => {
+ it('adds in the EIP712DomainSeparator with default values', () => {
const primaryType = 'Test';
const typedData = eip712Utils.createTypedData(
primaryType,
{ Test: [{ name: 'testValue', type: 'uint256' }] },
{ testValue: '1' },
- constants.NULL_ADDRESS,
+ { verifyingContractAddress: constants.NULL_ADDRESS },
);
expect(typedData.domain).to.not.be.undefined();
expect(typedData.types.EIP712Domain).to.not.be.undefined();
const domainObject = typedData.domain;
- expect(domainObject.name).to.eq(constants.EIP712_DOMAIN_NAME);
+ expect(domainObject.name).to.eq(EXCHANGE_DOMAIN_NAME);
+ expect(domainObject.version).to.eq(EXCHANGE_DOMAIN_VERSION);
+ expect(domainObject.verifyingContract).to.eq(constants.NULL_ADDRESS);
+ expect(typedData.primaryType).to.eq(primaryType);
+ });
+ it('adds in the EIP712DomainSeparator without default values', () => {
+ const primaryType = 'Test';
+ const domainName = 'testDomain';
+ const domainVersion = 'testVersion';
+ const typedData = eip712Utils.createTypedData(
+ primaryType,
+ { Test: [{ name: 'testValue', type: 'uint256' }] },
+ { testValue: '1' },
+ { name: domainName, version: domainVersion, verifyingContractAddress: constants.NULL_ADDRESS },
+ );
+ expect(typedData.domain).to.not.be.undefined();
+ expect(typedData.types.EIP712Domain).to.not.be.undefined();
+ const domainObject = typedData.domain;
+ expect(domainObject.name).to.eq(domainName);
+ expect(domainObject.version).to.eq(domainVersion);
+ expect(domainObject.verifyingContract).to.eq(constants.NULL_ADDRESS);
expect(typedData.primaryType).to.eq(primaryType);
});
});
- describe('createTypedData', () => {
+ describe('createZeroExTransactionTypedData', () => {
it('adds in the EIP712DomainSeparator', () => {
- const typedData = eip712Utils.createZeroExTransactionTypedData(
- {
- salt: new BigNumber('0'),
- data: constants.NULL_BYTES,
- signerAddress: constants.NULL_ADDRESS,
- },
- constants.NULL_ADDRESS,
- );
- expect(typedData.primaryType).to.eq(constants.EIP712_ZEROEX_TRANSACTION_SCHEMA.name);
+ const typedData = eip712Utils.createZeroExTransactionTypedData({
+ salt: new BigNumber('0'),
+ data: constants.NULL_BYTES,
+ signerAddress: constants.NULL_ADDRESS,
+ verifyingContractAddress: constants.NULL_ADDRESS,
+ });
+ expect(typedData.primaryType).to.eq(EXCHANGE_ZEROEX_TRANSACTION_SCHEMA.name);
expect(typedData.types.EIP712Domain).to.not.be.undefined();
+ const domainObject = typedData.domain;
+ expect(domainObject.name).to.eq(EXCHANGE_DOMAIN_NAME);
+ expect(domainObject.version).to.eq(EXCHANGE_DOMAIN_VERSION);
+ expect(domainObject.verifyingContract).to.eq(constants.NULL_ADDRESS);
});
});
});
diff --git a/packages/order-utils/test/order_hash_test.ts b/packages/order-utils/test/order_hash_test.ts
index 30fb15a37..514fc2709 100644
--- a/packages/order-utils/test/order_hash_test.ts
+++ b/packages/order-utils/test/order_hash_test.ts
@@ -54,8 +54,9 @@ describe('Order hashing', () => {
...order,
takerAddress: (null as any) as string,
};
- const expectedErrorMessage =
- 'Order taker must be of type string. If you want anyone to be able to fill an order - pass ZeroEx.NULL_ADDRESS';
+ const expectedErrorMessage = `Order taker must be of type string. If you want anyone to be able to fill an order - pass ${
+ constants.NULL_ADDRESS
+ }`;
expect(() => orderHashUtils.getOrderHashHex(orderWithInvalidtakerFormat)).to.throw(expectedErrorMessage);
});
});
diff --git a/packages/order-utils/test/transaction_hash_test.ts b/packages/order-utils/test/transaction_hash_test.ts
new file mode 100644
index 000000000..c7ca33a64
--- /dev/null
+++ b/packages/order-utils/test/transaction_hash_test.ts
@@ -0,0 +1,56 @@
+import { ZeroExTransaction } from '@0x/types';
+import { BigNumber } from '@0x/utils';
+import * as chai from 'chai';
+import 'mocha';
+
+import { transactionHashUtils } from '../src';
+
+import { constants } from '../src/constants';
+
+import { chaiSetup } from './utils/chai_setup';
+
+chaiSetup.configure();
+const expect = chai.expect;
+
+describe('0x transaction hashing', () => {
+ describe('#getTransactionHashHex', () => {
+ const expectedTransactionHash = '0x82c9bb2dcac4f868ec7a15c20ff6175cfc384c20ae6a872aa0342a840f108c2b';
+ const fakeVerifyingContractAddress = '0x5e72914535f202659083db3a02c984188fa26e9f';
+ const transaction: ZeroExTransaction = {
+ verifyingContractAddress: fakeVerifyingContractAddress,
+ signerAddress: constants.NULL_ADDRESS,
+ salt: new BigNumber(0),
+ data: constants.NULL_BYTES,
+ };
+ it('calculates the transaction hash', async () => {
+ const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
+ expect(transactionHash).to.be.equal(expectedTransactionHash);
+ });
+ it('calculates the transaction hash if amounts are strings', async () => {
+ // It's common for developers using javascript to provide the amounts
+ // as strings. Since we eventually toString() the BigNumber
+ // before encoding we should result in the same orderHash in this scenario
+ // tslint:disable-next-line:no-unnecessary-type-assertion
+ const transactionHash = transactionHashUtils.getTransactionHashHex({
+ ...transaction,
+ salt: '0',
+ } as any);
+ expect(transactionHash).to.be.equal(expectedTransactionHash);
+ });
+ });
+ describe('#isValidTransactionHash', () => {
+ it('returns false if the value is not a hex string', () => {
+ const isValid = transactionHashUtils.isValidTransactionHash('not a hex');
+ expect(isValid).to.be.false();
+ });
+ it('returns false if the length is wrong', () => {
+ const isValid = transactionHashUtils.isValidTransactionHash('0xdeadbeef');
+ expect(isValid).to.be.false();
+ });
+ it('returns true if order hash is correct', () => {
+ const orderHashLength = 65;
+ const isValid = transactionHashUtils.isValidTransactionHash(`0x${Array(orderHashLength).join('0')}`);
+ expect(isValid).to.be.true();
+ });
+ });
+});
diff --git a/packages/types/CHANGELOG.json b/packages/types/CHANGELOG.json
index b9ddb5acf..c2a54f824 100644
--- a/packages/types/CHANGELOG.json
+++ b/packages/types/CHANGELOG.json
@@ -5,6 +5,14 @@
{
"note": "Upgrade the bignumber.js to v8.0.2",
"pr": 1517
+ },
+ {
+ "note": "Update `ZeroExTransaction` type and add `SignedZeroExTransaction` type",
+ "pr": 1576
+ },
+ {
+ "note": "Add `EIP712DomainWithDefaultSchema` type",
+ "pr": 1576
}
],
"timestamp": 1549373905
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
index 49f788fb0..a30a0494f 100644
--- a/packages/types/src/index.ts
+++ b/packages/types/src/index.ts
@@ -45,11 +45,16 @@ export interface SignedOrder extends Order {
* ZeroExTransaction for use with 0x Exchange executeTransaction
*/
export interface ZeroExTransaction {
+ verifyingContractAddress: string;
salt: BigNumber;
signerAddress: string;
data: string;
}
+export interface SignedZeroExTransaction extends ZeroExTransaction {
+ signature: string;
+}
+
/**
* Elliptic Curve signature
*/
@@ -685,3 +690,9 @@ export interface DutchAuctionDetails {
currentAmount: BigNumber;
currentTimeSeconds: BigNumber;
}
+
+export interface EIP712DomainWithDefaultSchema {
+ name?: string;
+ version?: string;
+ verifyingContractAddress: string;
+}
diff --git a/python-packages/json_schemas/src/zero_ex/json_schemas/schemas/zero_ex_transaction_schema.json b/python-packages/json_schemas/src/zero_ex/json_schemas/schemas/zero_ex_transaction_schema.json
index 0c714f14d..ffdf1f229 100644
--- a/python-packages/json_schemas/src/zero_ex/json_schemas/schemas/zero_ex_transaction_schema.json
+++ b/python-packages/json_schemas/src/zero_ex/json_schemas/schemas/zero_ex_transaction_schema.json
@@ -1,10 +1,11 @@
{
"id": "/zeroExTransactionSchema",
"properties": {
+ "verifyingContractAddress": { "$ref": "/addressSchema" },
"data": { "$ref": "/hexSchema" },
"signerAddress": { "$ref": "/addressSchema" },
"salt": { "$ref": "/wholeNumberSchema" }
},
- "required": ["data", "salt", "signerAddress"],
+ "required": ["verifyingContractAddress", "data", "salt", "signerAddress"],
"type": "object"
}