diff options
author | Brandon Millman <brandon.millman@gmail.com> | 2017-09-29 00:06:23 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-09-29 00:06:23 +0800 |
commit | a0af2719963df3313b0eae40952203319ee1e64f (patch) | |
tree | bee5d0b011d5d8686711fe8da0bf7553305db3ab | |
parent | be12c5b5386410db9a1a28e86e944864c808c4c9 (diff) | |
parent | d21fbbc4c83cd7dd434f7e8389d48ed35fe0a5b0 (diff) | |
download | dexon-sol-tools-a0af2719963df3313b0eae40952203319ee1e64f.tar dexon-sol-tools-a0af2719963df3313b0eae40952203319ee1e64f.tar.gz dexon-sol-tools-a0af2719963df3313b0eae40952203319ee1e64f.tar.bz2 dexon-sol-tools-a0af2719963df3313b0eae40952203319ee1e64f.tar.lz dexon-sol-tools-a0af2719963df3313b0eae40952203319ee1e64f.tar.xz dexon-sol-tools-a0af2719963df3313b0eae40952203319ee1e64f.tar.zst dexon-sol-tools-a0af2719963df3313b0eae40952203319ee1e64f.zip |
Merge pull request #172 from 0xProject/bmillman_optional_validation
Add OrderTransactionOpts to enable optional validation to exchange_wr…
-rw-r--r-- | src/contract_wrappers/exchange_wrapper.ts | 112 | ||||
-rw-r--r-- | src/types.ts | 8 | ||||
-rw-r--r-- | test/exchange_wrapper_test.ts | 209 |
3 files changed, 280 insertions, 49 deletions
diff --git a/src/contract_wrappers/exchange_wrapper.ts b/src/contract_wrappers/exchange_wrapper.ts index 6d4d90441..d02a6e642 100644 --- a/src/contract_wrappers/exchange_wrapper.ts +++ b/src/contract_wrappers/exchange_wrapper.ts @@ -29,6 +29,7 @@ import { LogWithDecodedArgs, MethodOpts, ValidateOrderFillableOpts, + OrderTransactionOpts, } from '../types'; import {assert} from '../utils/assert'; import {utils} from '../utils/utils'; @@ -40,6 +41,8 @@ import {TokenWrapper} from './token_wrapper'; import {decorators} from '../utils/decorators'; import {artifacts} from '../artifacts'; +const SHOULD_VALIDATE_BY_DEFAULT = true; + /** * This class includes all the functionality related to calling methods and subscribing to * events of the 0x Exchange smart contract. @@ -154,19 +157,26 @@ export class ExchangeWrapper extends ContractWrapper { * @param takerAddress The user Ethereum address who would like to fill this order. * Must be available via the supplied Web3.Provider * passed to 0x.js. - * @return Transaction hash. + * @param orderTransactionOpts Optional arguments this method accepts. + * @return Transaction hash. */ @decorators.contractCallErrorHandler public async fillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber.BigNumber, shouldThrowOnInsufficientBalanceOrAllowance: boolean, - takerAddress: string): Promise<string> { + takerAddress: string, + orderTransactionOpts?: OrderTransactionOpts): Promise<string> { assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.isBigNumber('fillTakerTokenAmount', fillTakerTokenAmount); assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); const exchangeInstance = await this._getExchangeContractAsync(); - await this.validateFillOrderThrowIfInvalidAsync(signedOrder, fillTakerTokenAmount, takerAddress); + const shouldValidate = _.isUndefined(orderTransactionOpts) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; + if (shouldValidate) { + await this.validateFillOrderThrowIfInvalidAsync(signedOrder, fillTakerTokenAmount, takerAddress); + } const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder); @@ -211,12 +221,14 @@ export class ExchangeWrapper extends ContractWrapper { * @param takerAddress The user Ethereum address who would like to fill these * orders. Must be available via the supplied Web3.Provider * passed to 0x.js. - * @return Transaction hash. + * @param orderTransactionOpts Optional arguments this method accepts. + * @return Transaction hash. */ @decorators.contractCallErrorHandler public async fillOrdersUpToAsync(signedOrders: SignedOrder[], fillTakerTokenAmount: BigNumber.BigNumber, shouldThrowOnInsufficientBalanceOrAllowance: boolean, - takerAddress: string): Promise<string> { + takerAddress: string, + orderTransactionOpts?: OrderTransactionOpts): Promise<string> { assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); const takerTokenAddresses = _.map(signedOrders, signedOrder => signedOrder.takerTokenAddress); assert.hasAtMostOneUniqueValue(takerTokenAddresses, @@ -227,10 +239,15 @@ export class ExchangeWrapper extends ContractWrapper { assert.isBigNumber('fillTakerTokenAmount', fillTakerTokenAmount); assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - for (const signedOrder of signedOrders) { - await this.validateFillOrderThrowIfInvalidAsync( - signedOrder, fillTakerTokenAmount, takerAddress); + + const shouldValidate = _.isUndefined(orderTransactionOpts) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; + if (shouldValidate) { + await Promise.all(signedOrders.map(signedOrder => this.validateFillOrderThrowIfInvalidAsync( + signedOrder, fillTakerTokenAmount, takerAddress))); } + if (_.isEmpty(signedOrders)) { throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); } @@ -292,12 +309,14 @@ export class ExchangeWrapper extends ContractWrapper { * @param takerAddress The user Ethereum address who would like to fill * these orders. Must be available via the supplied * Web3.Provider passed to 0x.js. - * @return Transaction hash. + * @param orderTransactionOpts Optional arguments this method accepts. + * @return Transaction hash. */ @decorators.contractCallErrorHandler public async batchFillOrdersAsync(orderFillRequests: OrderFillRequest[], shouldThrowOnInsufficientBalanceOrAllowance: boolean, - takerAddress: string): Promise<string> { + takerAddress: string, + orderTransactionOpts?: OrderTransactionOpts): Promise<string> { assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema); const exchangeContractAddresses = _.map( orderFillRequests, @@ -307,9 +326,13 @@ export class ExchangeWrapper extends ContractWrapper { ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress); assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - for (const orderFillRequest of orderFillRequests) { - await this.validateFillOrderThrowIfInvalidAsync( - orderFillRequest.signedOrder, orderFillRequest.takerTokenFillAmount, takerAddress); + const shouldValidate = _.isUndefined(orderTransactionOpts) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; + if (shouldValidate) { + await Promise.all(orderFillRequests.map(orderFillRequest => this.validateFillOrderThrowIfInvalidAsync( + orderFillRequest.signedOrder, orderFillRequest.takerTokenFillAmount, takerAddress)), + ); } if (_.isEmpty(orderFillRequests)) { throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); @@ -365,18 +388,25 @@ export class ExchangeWrapper extends ContractWrapper { * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill. * @param takerAddress The user Ethereum address who would like to fill this order. * Must be available via the supplied Web3.Provider passed to 0x.js. - * @return Transaction hash. + * @param orderTransactionOpts Optional arguments this method accepts. + * @return Transaction hash. */ @decorators.contractCallErrorHandler public async fillOrKillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber.BigNumber, - takerAddress: string): Promise<string> { + takerAddress: string, + orderTransactionOpts?: OrderTransactionOpts): Promise<string> { assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.isBigNumber('fillTakerTokenAmount', fillTakerTokenAmount); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); const exchangeInstance = await this._getExchangeContractAsync(); - await this.validateFillOrKillOrderThrowIfInvalidAsync(signedOrder, fillTakerTokenAmount, takerAddress); + const shouldValidate = _.isUndefined(orderTransactionOpts) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; + if (shouldValidate) { + await this.validateFillOrKillOrderThrowIfInvalidAsync(signedOrder, fillTakerTokenAmount, takerAddress); + } const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(signedOrder); @@ -411,11 +441,13 @@ export class ExchangeWrapper extends ContractWrapper { * @param orderFillOrKillRequests An array of objects that conform to the OrderFillOrKillRequest interface. * @param takerAddress The user Ethereum address who would like to fill there orders. * Must be available via the supplied Web3.Provider passed to 0x.js. - * @return Transaction hash. + * @param orderTransactionOpts Optional arguments this method accepts. + * @return Transaction hash. */ @decorators.contractCallErrorHandler public async batchFillOrKillAsync(orderFillOrKillRequests: OrderFillOrKillRequest[], - takerAddress: string): Promise<string> { + takerAddress: string, + orderTransactionOpts?: OrderTransactionOpts): Promise<string> { assert.doesConformToSchema('orderFillOrKillRequests', orderFillOrKillRequests, schemas.orderFillOrKillRequestsSchema); const exchangeContractAddresses = _.map( @@ -429,9 +461,14 @@ export class ExchangeWrapper extends ContractWrapper { throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); } const exchangeInstance = await this._getExchangeContractAsync(); - for (const request of orderFillOrKillRequests) { - await this.validateFillOrKillOrderThrowIfInvalidAsync( - request.signedOrder, request.fillTakerAmount, takerAddress); + + const shouldValidate = _.isUndefined(orderTransactionOpts) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; + if (shouldValidate) { + await Promise.all(orderFillOrKillRequests.map(request => this.validateFillOrKillOrderThrowIfInvalidAsync( + request.signedOrder, request.fillTakerAmount, takerAddress)), + ); } const orderAddressesValuesAndTakerTokenFillAmounts = _.map(orderFillOrKillRequests, request => { @@ -478,17 +515,25 @@ export class ExchangeWrapper extends ContractWrapper { * @param order An object that conforms to the Order or SignedOrder interface. * The order you would like to cancel. * @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel. - * @return Transaction hash. + * @param transactionOpts Optional arguments this method accepts. + * @return Transaction hash. */ @decorators.contractCallErrorHandler - public async cancelOrderAsync( - order: Order|SignedOrder, cancelTakerTokenAmount: BigNumber.BigNumber): Promise<string> { + public async cancelOrderAsync(order: Order|SignedOrder, + cancelTakerTokenAmount: BigNumber.BigNumber, + orderTransactionOpts?: OrderTransactionOpts): Promise<string> { assert.doesConformToSchema('order', order, schemas.orderSchema); assert.isBigNumber('takerTokenCancelAmount', cancelTakerTokenAmount); await assert.isSenderAddressAsync('order.maker', order.maker, this._web3Wrapper); const exchangeInstance = await this._getExchangeContractAsync(); - await this.validateCancelOrderThrowIfInvalidAsync(order, cancelTakerTokenAmount); + + const shouldValidate = _.isUndefined(orderTransactionOpts) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; + if (shouldValidate) { + await this.validateCancelOrderThrowIfInvalidAsync(order, cancelTakerTokenAmount); + } const [orderAddresses, orderValues] = ExchangeWrapper._getOrderAddressesAndValues(order); const gas = await exchangeInstance.cancelOrder.estimateGasAsync( @@ -515,10 +560,12 @@ export class ExchangeWrapper extends ContractWrapper { * All orders must be from the same maker. * @param orderCancellationRequests An array of objects that conform to the OrderCancellationRequest * interface. - * @return Transaction hash. + * @param transactionOpts Optional arguments this method accepts. + * @return Transaction hash. */ @decorators.contractCallErrorHandler - public async batchCancelOrdersAsync(orderCancellationRequests: OrderCancellationRequest[]): Promise<string> { + public async batchCancelOrdersAsync(orderCancellationRequests: OrderCancellationRequest[], + orderTransactionOpts?: OrderTransactionOpts): Promise<string> { assert.doesConformToSchema('orderCancellationRequests', orderCancellationRequests, schemas.orderCancellationRequestsSchema); const exchangeContractAddresses = _.map( @@ -531,10 +578,13 @@ export class ExchangeWrapper extends ContractWrapper { assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed); const maker = makers[0]; await assert.isSenderAddressAsync('maker', maker, this._web3Wrapper); - for (const cancellationRequest of orderCancellationRequests) { - await this.validateCancelOrderThrowIfInvalidAsync( - cancellationRequest.order, cancellationRequest.takerTokenCancelAmount, - ); + const shouldValidate = _.isUndefined(orderTransactionOpts) ? + SHOULD_VALIDATE_BY_DEFAULT : + orderTransactionOpts.shouldValidate; + if (shouldValidate) { + await Promise.all(orderCancellationRequests.map(cancellationRequest => + this.validateCancelOrderThrowIfInvalidAsync( + cancellationRequest.order, cancellationRequest.takerTokenCancelAmount))); } if (_.isEmpty(orderCancellationRequests)) { throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); diff --git a/src/types.ts b/src/types.ts index 2d069f596..02230b0ab 100644 --- a/src/types.ts +++ b/src/types.ts @@ -452,3 +452,11 @@ export interface ValidateOrderFillableOpts { export interface MethodOpts { defaultBlock?: Web3.BlockParam; } + +/* + * shouldValidate: Flag indicating whether the library should make attempts to validate a transaction before + * broadcasting it. For example, order has a valid signature, maker has sufficient funds, etc. + */ +export interface OrderTransactionOpts { + shouldValidate: boolean; +} diff --git a/test/exchange_wrapper_test.ts b/test/exchange_wrapper_test.ts index 9c0617671..45a2d3907 100644 --- a/test/exchange_wrapper_test.ts +++ b/test/exchange_wrapper_test.ts @@ -18,6 +18,7 @@ import { OrderCancellationRequest, OrderFillRequest, LogFillContractEventArgs, + OrderFillOrKillRequest, } from '../src'; import {DoneCallback} from '../src/types'; import {FillScenarios} from './utils/fill_scenarios'; @@ -93,14 +94,47 @@ describe('ExchangeWrapper', () => { ]; await zeroEx.exchange.batchFillOrKillAsync(orderFillOrKillRequests, takerAddress); }); + describe('order transaction options', () => { + let signedOrder: SignedOrder; + let orderFillOrKillRequests: OrderFillOrKillRequest[]; + const fillableAmount = new BigNumber(5); + beforeEach(async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + ); + orderFillOrKillRequests = [ + { + signedOrder, + fillTakerAmount: new BigNumber(0), + }, + ]; + }); + it('should validate when orderTransactionOptions are not present', async () => { + return expect(zeroEx.exchange.batchFillOrKillAsync(orderFillOrKillRequests, takerAddress)) + .to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + }); + it('should validate when orderTransactionOptions specify to validate', async () => { + return expect(zeroEx.exchange.batchFillOrKillAsync(orderFillOrKillRequests, takerAddress, { + shouldValidate: true, + })).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + }); + it('should not validate when orderTransactionOptions specify not to validate', async () => { + return expect(zeroEx.exchange.batchFillOrKillAsync(orderFillOrKillRequests, takerAddress, { + shouldValidate: false, + })).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + }); + }); }); describe('#fillOrKillOrderAsync', () => { + let signedOrder: SignedOrder; + const fillableAmount = new BigNumber(5); + beforeEach(async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + ); + }); describe('successful fills', () => { it('should fill a valid order', async () => { - const fillableAmount = new BigNumber(5); - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, - ); expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)) .to.be.bignumber.equal(fillableAmount); expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)) @@ -120,10 +154,6 @@ describe('ExchangeWrapper', () => { .to.be.bignumber.equal(fillableAmount.minus(fillTakerAmount)); }); it('should partially fill a valid order', async () => { - const fillableAmount = new BigNumber(5); - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, - ); const partialFillAmount = new BigNumber(3); await zeroEx.exchange.fillOrKillOrderAsync(signedOrder, partialFillAmount, takerAddress); expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)) @@ -136,6 +166,23 @@ describe('ExchangeWrapper', () => { .to.be.bignumber.equal(fillableAmount.minus(partialFillAmount)); }); }); + describe('order transaction options', () => { + const emptyFillableAmount = new BigNumber(0); + it('should validate when orderTransactionOptions are not present', async () => { + return expect(zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress)) + .to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + }); + it('should validate when orderTransactionOptions specify to validate', async () => { + return expect(zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, { + shouldValidate: true, + })).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + }); + it('should not validate when orderTransactionOptions specify not to validate', async () => { + return expect(zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, { + shouldValidate: false, + })).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + }); + }); }); }); describe('fill order(s)', () => { @@ -212,6 +259,32 @@ describe('ExchangeWrapper', () => { .to.be.bignumber.equal(makerFee.plus(takerFee)); }); }); + describe('order transaction options', () => { + let signedOrder: SignedOrder; + const emptyFillTakerAmount = new BigNumber(0); + beforeEach(async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + ); + }); + it('should validate when orderTransactionOptions are not present', async () => { + return expect(zeroEx.exchange.fillOrderAsync( + signedOrder, emptyFillTakerAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, + )).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + }); + it('should validate when orderTransactionOptions specify to validate', async () => { + return expect(zeroEx.exchange.fillOrderAsync( + signedOrder, emptyFillTakerAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { + shouldValidate: true, + })).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + }); + it('should not validate when orderTransactionOptions specify not to validate', async () => { + return expect(zeroEx.exchange.fillOrderAsync( + signedOrder, emptyFillTakerAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { + shouldValidate: false, + })).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + }); + }); }); describe('#batchFillOrdersAsync', () => { let signedOrder: SignedOrder; @@ -228,18 +301,20 @@ describe('ExchangeWrapper', () => { makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, ); anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder); - orderFillBatch = [ - { - signedOrder, - takerTokenFillAmount: fillTakerAmount, - }, - { - signedOrder: anotherSignedOrder, - takerTokenFillAmount: fillTakerAmount, - }, - ]; }); describe('successful batch fills', () => { + beforeEach(() => { + orderFillBatch = [ + { + signedOrder, + takerTokenFillAmount: fillTakerAmount, + }, + { + signedOrder: anotherSignedOrder, + takerTokenFillAmount: fillTakerAmount, + }, + ]; + }); it('should throw if a batch is empty', async () => { return expect(zeroEx.exchange.batchFillOrdersAsync( [], shouldThrowOnInsufficientBalanceOrAllowance, takerAddress), @@ -255,6 +330,38 @@ describe('ExchangeWrapper', () => { expect(anotherFilledAmount).to.be.bignumber.equal(fillTakerAmount); }); }); + describe('order transaction options', () => { + beforeEach(async () => { + const emptyFillTakerAmount = new BigNumber(0); + orderFillBatch = [ + { + signedOrder, + takerTokenFillAmount: emptyFillTakerAmount, + }, + { + signedOrder: anotherSignedOrder, + takerTokenFillAmount: emptyFillTakerAmount, + }, + ]; + }); + it('should validate when orderTransactionOptions are not present', async () => { + return expect(zeroEx.exchange.batchFillOrdersAsync( + orderFillBatch, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress), + ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + }); + it('should validate when orderTransactionOptions specify to validate', async () => { + return expect(zeroEx.exchange.batchFillOrdersAsync( + orderFillBatch, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { + shouldValidate: true, + })).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + }); + it('should not validate when orderTransactionOptions specify not to validate', async () => { + return expect(zeroEx.exchange.batchFillOrdersAsync( + orderFillBatch, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { + shouldValidate: false, + })).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + }); + }); }); describe('#fillOrdersUpTo', () => { let signedOrder: SignedOrder; @@ -292,6 +399,26 @@ describe('ExchangeWrapper', () => { expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount); }); }); + describe('order transaction options', () => { + const emptyFillUpToAmount = new BigNumber(0); + it('should validate when orderTransactionOptions are not present', async () => { + return expect(zeroEx.exchange.fillOrdersUpToAsync( + signedOrders, emptyFillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, + )).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + }); + it('should validate when orderTransactionOptions specify to validate', async () => { + return expect(zeroEx.exchange.fillOrdersUpToAsync( + signedOrders, emptyFillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { + shouldValidate: true, + })).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + }); + it('should not validate when orderTransactionOptions specify not to validate', async () => { + return expect(zeroEx.exchange.fillOrdersUpToAsync( + signedOrders, emptyFillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { + shouldValidate: false, + })).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + }); + }); }); }); describe('cancel order(s)', () => { @@ -323,6 +450,23 @@ describe('ExchangeWrapper', () => { expect(cancelledAmount).to.be.bignumber.equal(cancelAmount); }); }); + describe('order transaction options', () => { + const emptyCancelTakerTokenAmount = new BigNumber(0); + it('should validate when orderTransactionOptions are not present', async () => { + return expect(zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount)) + .to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); + }); + it('should validate when orderTransactionOptions specify to validate', async () => { + return expect(zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, { + shouldValidate: true, + })).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); + }); + it('should not validate when orderTransactionOptions specify not to validate', async () => { + return expect(zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, { + shouldValidate: false, + })).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); + }); + }); }); describe('#batchCancelOrdersAsync', () => { let anotherSignedOrder: SignedOrder; @@ -369,6 +513,35 @@ describe('ExchangeWrapper', () => { expect(anotherCancelledAmount).to.be.bignumber.equal(cancelAmount); }); }); + describe('order transaction options', () => { + beforeEach(async () => { + const emptyTakerTokenCancelAmount = new BigNumber(0); + cancelBatch = [ + { + order: signedOrder, + takerTokenCancelAmount: emptyTakerTokenCancelAmount, + }, + { + order: anotherSignedOrder, + takerTokenCancelAmount: emptyTakerTokenCancelAmount, + }, + ]; + }); + it('should validate when orderTransactionOptions are not present', async () => { + return expect(zeroEx.exchange.batchCancelOrdersAsync(cancelBatch)) + .to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); + }); + it('should validate when orderTransactionOptions specify to validate', async () => { + return expect(zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, { + shouldValidate: true, + })).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); + }); + it('should not validate when orderTransactionOptions specify not to validate', async () => { + return expect(zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, { + shouldValidate: false, + })).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); + }); + }); }); }); describe('tests that require partially filled order', () => { |