diff options
-rw-r--r-- | src/0x.js.ts | 28 | ||||
-rw-r--r-- | src/contract_wrappers/exchange_wrapper.ts | 31 | ||||
-rw-r--r-- | src/types.ts | 9 | ||||
-rw-r--r-- | src/utils/utils.ts | 35 | ||||
-rw-r--r-- | test/exchange_wrapper_test.ts | 4 |
5 files changed, 60 insertions, 47 deletions
diff --git a/src/0x.js.ts b/src/0x.js.ts index 0f437e039..eb7698bd4 100644 --- a/src/0x.js.ts +++ b/src/0x.js.ts @@ -4,21 +4,18 @@ import {bigNumberConfigs} from './bignumber_config'; import * as ethUtil from 'ethereumjs-util'; import contract = require('truffle-contract'); import * as Web3 from 'web3'; -import * as ethABI from 'ethereumjs-abi'; import findVersions = require('find-versions'); import compareVersions = require('compare-versions'); import {Web3Wrapper} from './web3_wrapper'; import {constants} from './utils/constants'; import {utils} from './utils/utils'; import {assert} from './utils/assert'; -import {SchemaValidator} from './utils/schema_validator'; import {ExchangeWrapper} from './contract_wrappers/exchange_wrapper'; import {TokenRegistryWrapper} from './contract_wrappers/token_registry_wrapper'; import {ecSignatureSchema} from './schemas/ec_signature_schema'; import {TokenWrapper} from './contract_wrappers/token_wrapper'; -import {SolidityTypes, ECSignature, ZeroExError} from './types'; +import {ECSignature, ZeroExError} from './types'; import {Order, SignedOrder} from './types'; -import {orderSchema} from './schemas/order_schemas'; import * as ExchangeArtifacts from './artifacts/Exchange.json'; // Customize our BigNumber instances @@ -133,28 +130,7 @@ export class ZeroEx { */ public async getOrderHashHexAsync(order: Order|SignedOrder): Promise<string> { const exchangeContractAddr = await this.getExchangeAddressAsync(); - assert.doesConformToSchema('order', - SchemaValidator.convertToJSONSchemaCompatibleObject(order as object), - orderSchema); - - const orderParts = [ - {value: exchangeContractAddr, type: SolidityTypes.address}, - {value: order.maker, type: SolidityTypes.address}, - {value: order.taker, type: SolidityTypes.address}, - {value: order.makerTokenAddress, type: SolidityTypes.address}, - {value: order.takerTokenAddress, type: SolidityTypes.address}, - {value: order.feeRecipient, type: SolidityTypes.address}, - {value: utils.bigNumberToBN(order.makerTokenAmount), type: SolidityTypes.uint256}, - {value: utils.bigNumberToBN(order.takerTokenAmount), type: SolidityTypes.uint256}, - {value: utils.bigNumberToBN(order.makerFee), type: SolidityTypes.uint256}, - {value: utils.bigNumberToBN(order.takerFee), type: SolidityTypes.uint256}, - {value: utils.bigNumberToBN(order.expirationUnixTimestampSec), type: SolidityTypes.uint256}, - {value: utils.bigNumberToBN(order.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); + const hashHex = utils.getOrderHashHex(order, exchangeContractAddr); return hashHex; } /** diff --git a/src/contract_wrappers/exchange_wrapper.ts b/src/contract_wrappers/exchange_wrapper.ts index 4b7bd172f..537eb08a4 100644 --- a/src/contract_wrappers/exchange_wrapper.ts +++ b/src/contract_wrappers/exchange_wrapper.ts @@ -25,7 +25,7 @@ import {utils} from '../utils/utils'; import {ContractWrapper} from './contract_wrapper'; import * as ExchangeArtifacts from '../artifacts/Exchange.json'; import {ecSignatureSchema} from '../schemas/ec_signature_schema'; -import {signedOrderSchema} from '../schemas/order_schemas'; +import {signedOrderSchema, orderSchema} from '../schemas/order_schemas'; import {SchemaValidator} from '../utils/schema_validator'; import {constants} from '../utils/constants'; import {TokenWrapper} from './token_wrapper'; @@ -173,23 +173,23 @@ export class ExchangeWrapper extends ContractWrapper { this.throwErrorLogsAsErrors(response.logs); } /** - * Cancels the order. + * Cancel a given fill amount of an order. Cancellations are cumulative. */ - public async cancelOrderAsync(order: Order, cancelAmount: BigNumber.BigNumber): Promise<void> { + public async cancelOrderAsync(order: Order, takerTokenCancelAmount: BigNumber.BigNumber): Promise<void> { assert.doesConformToSchema('order', SchemaValidator.convertToJSONSchemaCompatibleObject(order as object), - signedOrderSchema); - assert.isBigNumber('cancelAmount', cancelAmount); + orderSchema); + assert.isBigNumber('takerTokenCancelAmount', takerTokenCancelAmount); await assert.isSenderAddressAvailableAsync(this.web3Wrapper, 'order.maker', order.maker); const exchangeInstance = await this.getExchangeContractAsync(); - await this.validateCancelOrderAndThrowIfInvalidAsync(order, cancelAmount); + await this.validateCancelOrderAndThrowIfInvalidAsync(order, takerTokenCancelAmount); const [orderAddresses, orderValues] = ExchangeWrapper.getOrderAddressesAndValues(order); const gas = await exchangeInstance.cancel.estimateGas( orderAddresses, orderValues, - cancelAmount, + takerTokenCancelAmount, { from: order.maker, }, @@ -197,7 +197,7 @@ export class ExchangeWrapper extends ContractWrapper { const response: ContractResponse = await exchangeInstance.cancel( orderAddresses, orderValues, - cancelAmount, + takerTokenCancelAmount, { from: order.maker, gas, @@ -231,14 +231,13 @@ export class ExchangeWrapper extends ContractWrapper { logEventObj.watch(callback); this.exchangeLogEventObjs.push(logEventObj); } - /** - * Get order hash + * Computes the orderHash for a given order and returns it as a hex encoded string. */ public async getOrderHashAsync(order: Order): Promise<string> { const [orderAddresses, orderValues] = ExchangeWrapper.getOrderAddressesAndValues(order); const exchangeInstance = await this.getExchangeContractAsync(); - const orderHash = await exchangeInstance.getOrderHash.call(orderAddresses, orderValues); + const orderHash = utils.getOrderHashHex(order, exchangeInstance.address); return orderHash; } private async stopWatchingExchangeLogEventsAsync() { @@ -257,7 +256,7 @@ export class ExchangeWrapper extends ContractWrapper { if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== senderAddress) { throw new Error(ExchangeContractErrs.TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER); } - const currentUnixTimestampSec = Date.now() / 1000; + const currentUnixTimestampSec = utils.getCurrentUnixTimestamp(); if (signedOrder.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) { throw new Error(ExchangeContractErrs.ORDER_FILL_EXPIRED); } @@ -272,9 +271,9 @@ export class ExchangeWrapper extends ContractWrapper { throw new Error(ExchangeContractErrs.ORDER_FILL_ROUNDING_ERROR); } } - private async validateCancelOrderAndThrowIfInvalidAsync(order: Order, - cancelAmount: BigNumber.BigNumber): Promise<void> { - if (cancelAmount.eq(0)) { + private async validateCancelOrderAndThrowIfInvalidAsync( + order: Order, takerTokenCancelAmount: BigNumber.BigNumber): Promise<void> { + if (takerTokenCancelAmount.eq(0)) { throw new Error(ExchangeContractErrs.ORDER_CANCEL_AMOUNT_ZERO); } const orderHash = await this.getOrderHashAsync(order); @@ -282,7 +281,7 @@ export class ExchangeWrapper extends ContractWrapper { if (order.takerTokenAmount.minus(unavailableAmount).eq(0)) { throw new Error(ExchangeContractErrs.ORDER_ALREADY_CANCELLED_OR_FILLED); } - const currentUnixTimestampSec = Date.now() / 1000; + const currentUnixTimestampSec = utils.getCurrentUnixTimestamp(); if (order.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) { throw new Error(ExchangeContractErrs.ORDER_CANCEL_EXPIRED); } diff --git a/src/types.ts b/src/types.ts index 92cafd409..fa3560ef8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -44,7 +44,10 @@ export interface ContractEventObj { } export type CreateContractEvent = (indexFilterValues: IndexFilterValues, subscriptionOpts: SubscriptionOpts) => ContractEventObj; -export interface ExchangeContract { +export interface ContractInstance { + address: string; +} +export interface ExchangeContract extends ContractInstance { isValidSignature: { call: (signerAddressHex: string, dataHex: string, v: number, r: string, s: string, txOpts?: TxOpts) => Promise<boolean>; @@ -85,7 +88,7 @@ export interface ExchangeContract { }; } -export interface TokenContract { +export interface TokenContract extends ContractInstance { balanceOf: { call: (address: string) => Promise<BigNumber.BigNumber>; }; @@ -98,7 +101,7 @@ export interface TokenContract { approve: (proxyAddress: string, amountInBaseUnits: BigNumber.BigNumber, txOpts: TxOpts) => void; } -export interface TokenRegistryContract { +export interface TokenRegistryContract extends ContractInstance { getTokenMetaData: { call: (address: string) => Promise<TokenMetadata>; }; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 114b46f6c..4cea36127 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,5 +1,12 @@ import * as _ from 'lodash'; import * as BN from 'bn.js'; +import * as ethABI from 'ethereumjs-abi'; +import * as ethUtil from 'ethereumjs-util'; +import {orderSchema} from '../schemas/order_schemas'; +import {SchemaValidator} from './schema_validator'; +import {Order, SolidityTypes} from '../types'; +import {assert} from './assert'; +import * as BigNumber from 'bignumber.js'; export const utils = { /** @@ -25,4 +32,32 @@ export const utils = { spawnSwitchErr(name: string, value: any) { return new Error(`Unexpected switch value: ${value} encountered for ${name}`); }, + getOrderHashHex(order: Order, exchangeContractAddr: string): string { + assert.doesConformToSchema('order', + SchemaValidator.convertToJSONSchemaCompatibleObject(order as object), + orderSchema); + + const orderParts = [ + {value: exchangeContractAddr, type: SolidityTypes.address}, + {value: order.maker, type: SolidityTypes.address}, + {value: order.taker, type: SolidityTypes.address}, + {value: order.makerTokenAddress, type: SolidityTypes.address}, + {value: order.takerTokenAddress, type: SolidityTypes.address}, + {value: order.feeRecipient, type: SolidityTypes.address}, + {value: utils.bigNumberToBN(order.makerTokenAmount), type: SolidityTypes.uint256}, + {value: utils.bigNumberToBN(order.takerTokenAmount), type: SolidityTypes.uint256}, + {value: utils.bigNumberToBN(order.makerFee), type: SolidityTypes.uint256}, + {value: utils.bigNumberToBN(order.takerFee), type: SolidityTypes.uint256}, + {value: utils.bigNumberToBN(order.expirationUnixTimestampSec), type: SolidityTypes.uint256}, + {value: utils.bigNumberToBN(order.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; + }, + getCurrentUnixTimestamp(): BigNumber.BigNumber { + return new BigNumber(Date.now() / 1000); + }, }; diff --git a/test/exchange_wrapper_test.ts b/test/exchange_wrapper_test.ts index a60494a09..3a3ec1ca7 100644 --- a/test/exchange_wrapper_test.ts +++ b/test/exchange_wrapper_test.ts @@ -158,7 +158,7 @@ describe('ExchangeWrapper', () => { )).to.be.rejectedWith(ExchangeContractErrs.TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER); }); it('should throw when order is expired', async () => { - const expirationInPast = new BigNumber(42); + const expirationInPast = new BigNumber(1496826058); const fillableAmount = new BigNumber(5); const signedOrder = await fillScenarios.createFillableSignedOrderAsync( makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, expirationInPast, @@ -350,7 +350,7 @@ describe('ExchangeWrapper', () => { .to.be.rejectedWith(ExchangeContractErrs.ORDER_CANCEL_AMOUNT_ZERO); }); it('should throw when order is expired', async () => { - const expirationInPast = new BigNumber(42); + const expirationInPast = new BigNumber(1496826058); const expiredSignedOrder = await fillScenarios.createFillableSignedOrderAsync( makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, expirationInPast, ); |