diff options
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | src/ts/0x.js.ts | 57 | ||||
-rw-r--r-- | src/ts/globals.d.ts | 5 | ||||
-rw-r--r-- | src/ts/types.ts | 7 | ||||
-rw-r--r-- | src/ts/utils/constants.ts | 3 | ||||
-rw-r--r-- | test/0x.js.ts | 38 |
6 files changed, 110 insertions, 1 deletions
diff --git a/package.json b/package.json index a55f7bfa2..3f27a8d2e 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "dependencies": { "bignumber.js": "^4.0.2", "es6-promisify": "^5.0.0", + "ethereumjs-abi": "^0.6.4", "ethereumjs-util": "^5.1.1", "jsonschema": "^1.1.1", "lodash": "^4.17.4", diff --git a/src/ts/0x.js.ts b/src/ts/0x.js.ts index cc3c43e79..7c6557b5b 100644 --- a/src/ts/0x.js.ts +++ b/src/ts/0x.js.ts @@ -1,12 +1,16 @@ import * as BigNumber from 'bignumber.js'; +import * as BN from 'bn.js'; import * as ethUtil from 'ethereumjs-util'; import * as Web3 from 'web3'; +import * as ethABI from 'ethereumjs-abi'; +import * as _ from 'lodash'; +import {constants} from './utils/constants'; import {assert} from './utils/assert'; import {Web3Wrapper} from './web3_wrapper'; import {ExchangeWrapper} from './contract_wrappers/exchange_wrapper'; import contract = require('truffle-contract'); import {ECSignatureSchema} from './schemas/ec_signature_schema'; -import {ECSignature} from './types'; +import {SolidityTypes, ECSignature} from './types'; const MAX_DIGITS_IN_UNSIGNED_256_INT = 78; @@ -14,6 +18,48 @@ export class ZeroEx { public web3Wrapper: Web3Wrapper; public exchange: ExchangeWrapper; /** + * Computes the orderHash given the order parameters and returns it as a hex encoded string. + */ + public static getOrderHashHex(exchangeContractAddr: string, makerAddr: string, takerAddr: string, + tokenMAddress: string, tokenTAddress: string, feeRecipient: string, + valueM: BigNumber.BigNumber, valueT: BigNumber.BigNumber, + makerFee: BigNumber.BigNumber, takerFee: BigNumber.BigNumber, + expiration: BigNumber.BigNumber, salt: BigNumber.BigNumber): string { + takerAddr = _.isEmpty(takerAddr) ? constants.NULL_ADDRESS : takerAddr ; + assert.isETHAddressHex('exchangeContractAddr', exchangeContractAddr); + assert.isETHAddressHex('makerAddr', makerAddr); + assert.isETHAddressHex('takerAddr', takerAddr); + assert.isETHAddressHex('tokenMAddress', tokenMAddress); + assert.isETHAddressHex('tokenTAddress', tokenTAddress); + assert.isETHAddressHex('feeRecipient', feeRecipient); + assert.isBigNumber('valueM', valueM); + assert.isBigNumber('valueT', valueT); + assert.isBigNumber('makerFee', makerFee); + assert.isBigNumber('takerFee', takerFee); + assert.isBigNumber('expiration', expiration); + assert.isBigNumber('salt', salt); + + const orderParts = [ + {value: exchangeContractAddr, type: SolidityTypes.address}, + {value: makerAddr, type: SolidityTypes.address}, + {value: takerAddr, type: SolidityTypes.address}, + {value: tokenMAddress, type: SolidityTypes.address}, + {value: tokenTAddress, type: SolidityTypes.address}, + {value: feeRecipient, type: SolidityTypes.address}, + {value: this.bigNumberToBN(valueM), type: SolidityTypes.uint256}, + {value: this.bigNumberToBN(valueT), type: SolidityTypes.uint256}, + {value: this.bigNumberToBN(makerFee), type: SolidityTypes.uint256}, + {value: this.bigNumberToBN(takerFee), type: SolidityTypes.uint256}, + {value: this.bigNumberToBN(expiration), type: SolidityTypes.uint256}, + {value: this.bigNumberToBN(salt), type: SolidityTypes.uint256}, + ]; + const types = _.map(orderParts, o => o.type); + const values = _.map(orderParts, o => o.value); + const hashBuff = ethABI.soliditySHA3(types, values); + const hashHex = ethUtil.bufferToHex(hashBuff); + return hashHex; + } + /** * Verifies that the elliptic curve signature `signature` was generated * by signing `data` with the private key corresponding to the `signerAddressHex` address. */ @@ -80,6 +126,15 @@ export class ZeroEx { const baseUnitAmount = amount.times(unit); return baseUnitAmount; } + /** + * Converts BigNumber instance to BN + * The only we convert to BN is to remain compatible with `ethABI. soliditySHA3 ` that + * expects values of Solidity type `uint` to be of type `BN`. + * We do not use BN anywhere else in the codebase. + */ + private static bigNumberToBN(value: BigNumber.BigNumber) { + return new BN(value.toString(), 10); + } constructor(web3: Web3) { this.web3Wrapper = new Web3Wrapper(web3); this.exchange = new ExchangeWrapper(this.web3Wrapper); diff --git a/src/ts/globals.d.ts b/src/ts/globals.d.ts index 04328509a..30225d0d8 100644 --- a/src/ts/globals.d.ts +++ b/src/ts/globals.d.ts @@ -1,4 +1,5 @@ declare module 'chai-bignumber'; +declare module 'bn.js'; declare interface Schema { id: string; @@ -51,3 +52,7 @@ declare function promisify(original: any, settings?: any): ((...arg: any[]) => P declare module 'es6-promisify' { export = promisify; } + +declare module 'ethereumjs-abi' { + const soliditySHA3: (argTypes: string[], args: any[]) => Buffer; +} diff --git a/src/ts/types.ts b/src/ts/types.ts index 01380af02..04123782e 100644 --- a/src/ts/types.ts +++ b/src/ts/types.ts @@ -1,4 +1,5 @@ import * as _ from 'lodash'; +import * as BigNumber from 'bignumber.js'; // Utility function to create a K:V from a list of strings // Adapted from: https://basarat.gitbooks.io/typescript/content/docs/types/literal-types.html @@ -28,3 +29,9 @@ export interface ECSignature { export interface ExchangeContract { isValidSignature: any; } + +export const SolidityTypes = strEnum([ + 'address', + 'uint256', +]); +export type SolidityTypes = keyof typeof SolidityTypes; diff --git a/src/ts/utils/constants.ts b/src/ts/utils/constants.ts new file mode 100644 index 000000000..60af7b674 --- /dev/null +++ b/src/ts/utils/constants.ts @@ -0,0 +1,3 @@ +export const constants = { + NULL_ADDRESS: '0x0000000000000000000000000000000000000000', +} diff --git a/test/0x.js.ts b/test/0x.js.ts index a913fd6b5..d5b2015fb 100644 --- a/test/0x.js.ts +++ b/test/0x.js.ts @@ -3,12 +3,50 @@ import * as chai from 'chai'; import 'mocha'; import * as BigNumber from 'bignumber.js'; import ChaiBigNumber = require('chai-bignumber'); +import {constants} from '../src/ts/utils/constants'; // Use BigNumber chai add-on chai.use(ChaiBigNumber()); const expect = chai.expect; describe('ZeroEx library', () => { + describe('#getOrderHash', () => { + const ORDER_HASH = '0x103a5e97dab5dbeb8f385636f86a7d1e458a7ccbe1bd194727f0b2f85ab116c7'; + it('defaults takerAddress to NULL address', () => { + const orderHash = ZeroEx.getOrderHashHex( + constants.NULL_ADDRESS, + constants.NULL_ADDRESS, + '', + constants.NULL_ADDRESS, + constants.NULL_ADDRESS, + constants.NULL_ADDRESS, + new BigNumber(0), + new BigNumber(0), + new BigNumber(0), + new BigNumber(0), + new BigNumber(0), + new BigNumber(0), + ); + expect(orderHash).to.be.equal(ORDER_HASH); + }); + it('calculates the order hash', () => { + const orderHash = ZeroEx.getOrderHashHex( + constants.NULL_ADDRESS, + constants.NULL_ADDRESS, + constants.NULL_ADDRESS, + constants.NULL_ADDRESS, + constants.NULL_ADDRESS, + constants.NULL_ADDRESS, + new BigNumber(0), + new BigNumber(0), + new BigNumber(0), + new BigNumber(0), + new BigNumber(0), + new BigNumber(0), + ); + expect(orderHash).to.be.equal(ORDER_HASH); + }); + }); describe('#isValidSignature', () => { // This test data was borrowed from the JSON RPC documentation // Source: https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign |