diff options
-rw-r--r-- | src/contract_wrappers/exchange_wrapper.ts | 65 | ||||
-rw-r--r-- | src/types.ts | 11 | ||||
-rw-r--r-- | src/utils/assert.ts | 6 | ||||
-rw-r--r-- | test/exchange_wrapper_test.ts | 335 |
4 files changed, 236 insertions, 181 deletions
diff --git a/src/contract_wrappers/exchange_wrapper.ts b/src/contract_wrappers/exchange_wrapper.ts index e4d360690..e824108de 100644 --- a/src/contract_wrappers/exchange_wrapper.ts +++ b/src/contract_wrappers/exchange_wrapper.ts @@ -134,37 +134,60 @@ export class ExchangeWrapper extends ContractWrapper { */ public async fillOrderAsync(signedOrder: SignedOrder, fillTakerAmount: BigNumber.BigNumber, shouldCheckTransfer: boolean, takerAddress: string): Promise<void> { - assert.doesConformToSchema('signedOrder', - SchemaValidator.convertToJSONSchemaCompatibleObject(signedOrder as object), - signedOrderSchema); - assert.isBigNumber('fillTakerAmount', fillTakerAmount); + await this.batchFillOrderAsync([signedOrder], [fillTakerAmount], shouldCheckTransfer, takerAddress); + } + /** + * Batched version of fillOrderAsync. Executes fills atomically in a single transaction. + */ + public async batchFillOrderAsync(signedOrders: SignedOrder[], fillTakerAmounts: BigNumber.BigNumber[], + shouldCheckTransfer: boolean, takerAddress: string): Promise<void> { + assert.isSameLength('signedOrders', signedOrders, 'fillTakerAmounts', fillTakerAmounts); + assert.assert(!_.isEmpty(signedOrders), 'Can not cancel an empty batch'); assert.isBoolean('shouldCheckTransfer', shouldCheckTransfer); await assert.isSenderAddressAsync('takerAddress', takerAddress, this.web3Wrapper); - + // _.zip doesn't type check if values have different types :'( + const ordersAndAmounts = _.zip<any>(signedOrders, fillTakerAmounts); + _.forEach(ordersAndAmounts, + async ([signedOrder, fillTakerAmount]: [SignedOrder, BigNumber.BigNumber]) => { + assert.doesConformToSchema('signedOrder', + SchemaValidator.convertToJSONSchemaCompatibleObject(signedOrder as object), signedOrderSchema); + assert.isBigNumber('fillTakerAmount', fillTakerAmount); + await this.validateFillOrderAndThrowIfInvalidAsync(signedOrder, fillTakerAmount, takerAddress); + }); const exchangeInstance = await this.getExchangeContractAsync(); - await this.validateFillOrderAndThrowIfInvalidAsync(signedOrder, fillTakerAmount, takerAddress); - const [orderAddresses, orderValues] = ExchangeWrapper.getOrderAddressesAndValues(signedOrder); - const gas = await exchangeInstance.fill.estimateGas( - orderAddresses, - orderValues, - fillTakerAmount, + const orderAddressesValuesAndSignatureArray = _.map(signedOrders, signedOrder => { + return [ + ...ExchangeWrapper.getOrderAddressesAndValues(signedOrder), + signedOrder.ecSignature.v, + signedOrder.ecSignature.r, + signedOrder.ecSignature.s, + ]; + }); + // _.unzip doesn't type check if values have different types :'( + const [orderAddressesArray, orderValuesArray, vArray, rArray, sArray] = _.unzip<any>( + orderAddressesValuesAndSignatureArray, + ); + const gas = await exchangeInstance.batchFill.estimateGas( + orderAddressesArray, + orderValuesArray, + fillTakerAmounts, shouldCheckTransfer, - signedOrder.ecSignature.v, - signedOrder.ecSignature.r, - signedOrder.ecSignature.s, + vArray, + rArray, + sArray, { from: takerAddress, }, ); - const response: ContractResponse = await exchangeInstance.fill( - orderAddresses, - orderValues, - fillTakerAmount, + const response: ContractResponse = await exchangeInstance.batchFill( + orderAddressesArray, + orderValuesArray, + fillTakerAmounts, shouldCheckTransfer, - signedOrder.ecSignature.v, - signedOrder.ecSignature.r, - signedOrder.ecSignature.s, + vArray, + rArray, + sArray, { from: takerAddress, gas, diff --git a/src/types.ts b/src/types.ts index 1034893a4..f3cfa440c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -68,6 +68,12 @@ export interface ExchangeContract extends ContractInstance { estimateGas: (orderAddresses: OrderAddresses, orderValues: OrderValues, fillAmount: BigNumber.BigNumber, shouldCheckTransfer: boolean, v: number, r: string, s: string, txOpts?: TxOpts) => number; }; + batchFill: { + (orderAddresses: OrderAddresses[], orderValues: OrderValues[], fillAmount: BigNumber.BigNumber[], + shouldCheckTransfer: boolean, v: number[], r: string[], s: string[], txOpts?: TxOpts): ContractResponse; + estimateGas: (orderAddresses: OrderAddresses[], orderValues: OrderValues[], fillAmount: BigNumber.BigNumber[], + shouldCheckTransfer: boolean, v: number[], r: string[], s: string[], txOpts?: TxOpts) => number; + }; cancel: { (orderAddresses: OrderAddresses, orderValues: OrderValues, cancelAmount: BigNumber.BigNumber, txOpts?: TxOpts): ContractResponse; @@ -227,3 +233,8 @@ export interface OrderCancellationRequest { order: Order|SignedOrder; takerTokenCancelAmount: BigNumber.BigNumber; } + +export interface OrderFillRequest { + signedOrder: SignedOrder; + takerTokenFillAmount: BigNumber.BigNumber; +} diff --git a/src/utils/assert.ts b/src/utils/assert.ts index 4dc6945a2..61b7527e6 100644 --- a/src/utils/assert.ts +++ b/src/utils/assert.ts @@ -42,6 +42,12 @@ export const assert = { const availableAddresses = await web3Wrapper.getAvailableAddressesAsync(); this.assert(!_.isEmpty(availableAddresses), 'No addresses were available on the provided web3 instance'); }, + isSameLength(variableName1: string, value1: any[], variableName2: string, value2: any[]) { + const length1 = value1.length; + const length2 = value2.length; + this.assert(length1 === length2, `${variableName1} and ${variableName2} length mismatch. \ +${length1} != ${length2}`); + }, isNumber(variableName: string, value: number): void { this.assert(_.isFinite(value), this.typeAssertionMessage(variableName, 'number', value)); }, diff --git a/test/exchange_wrapper_test.ts b/test/exchange_wrapper_test.ts index 68224a68c..b0aa351a4 100644 --- a/test/exchange_wrapper_test.ts +++ b/test/exchange_wrapper_test.ts @@ -120,13 +120,14 @@ describe('ExchangeWrapper', () => { expect(isValid).to.be.true(); }); }); - describe('#fillOrderAsync', () => { + describe('fill order(s)', () => { let makerTokenAddress: string; let takerTokenAddress: string; let coinbase: string; let makerAddress: string; let takerAddress: string; let feeRecipient: string; + const fillableAmount = new BigNumber(5); const fillTakerAmount = new BigNumber(5); const shouldCheckTransfer = false; before(async () => { @@ -136,190 +137,204 @@ describe('ExchangeWrapper', () => { makerTokenAddress = makerToken.address; takerTokenAddress = takerToken.address; }); - describe('failed fills', () => { - it('should throw when the fill amount is zero', async () => { - const fillableAmount = new BigNumber(5); - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, - ); - const zeroFillAmount = new BigNumber(0); - return expect(zeroEx.exchange.fillOrderAsync( - signedOrder, zeroFillAmount, shouldCheckTransfer, takerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.ORDER_REMAINING_FILL_AMOUNT_ZERO); - }); - it('should throw when sender is not a taker', async () => { - const fillableAmount = new BigNumber(5); - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, - ); - const nonExistentSenderAddress = userAddresses[6]; - return expect(zeroEx.exchange.fillOrderAsync( - signedOrder, fillTakerAmount, shouldCheckTransfer, nonExistentSenderAddress, - )).to.be.rejectedWith(ExchangeContractErrs.TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER); - }); - it('should throw when order is expired', async () => { - const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017 - const fillableAmount = new BigNumber(5); - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, expirationInPast, - ); - return expect(zeroEx.exchange.fillOrderAsync( - signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.ORDER_FILL_EXPIRED); - }); - describe('should throw when not enough balance or allowance to fulfill the order', () => { - const fillableAmount = new BigNumber(5); - const balanceToSubtractFromMaker = new BigNumber(3); - const lackingAllowance = new BigNumber(3); - let signedOrder: SignedOrder; - beforeEach('create fillable signed order', async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( + describe('#fillOrderAsync', () => { + describe('failed fills', () => { + it('should throw when the fill amount is zero', async () => { + const signedOrder = await fillScenarios.createFillableSignedOrderAsync( makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, ); + const zeroFillAmount = new BigNumber(0); + return expect(zeroEx.exchange.fillOrderAsync( + signedOrder, zeroFillAmount, shouldCheckTransfer, takerAddress, + )).to.be.rejectedWith(ExchangeContractErrs.ORDER_REMAINING_FILL_AMOUNT_ZERO); }); - it('should throw when taker balance is less than fill amount', async () => { - await zeroEx.token.transferAsync( - takerTokenAddress, takerAddress, coinbase, balanceToSubtractFromMaker, + it('should throw when sender is not a taker', async () => { + const signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, ); + const nonExistentSenderAddress = userAddresses[6]; return expect(zeroEx.exchange.fillOrderAsync( - signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_TAKER_BALANCE); + signedOrder, fillTakerAmount, shouldCheckTransfer, nonExistentSenderAddress, + )).to.be.rejectedWith(ExchangeContractErrs.TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER); }); - it('should throw when taker allowance is less than fill amount', async () => { - const newAllowanceWhichIsLessThanFillAmount = fillTakerAmount.minus(lackingAllowance); - await zeroEx.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress, - newAllowanceWhichIsLessThanFillAmount); + it('should throw when order is expired', async () => { + const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017 + const signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, + fillableAmount, expirationInPast, + ); return expect(zeroEx.exchange.fillOrderAsync( signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_TAKER_ALLOWANCE); + )).to.be.rejectedWith(ExchangeContractErrs.ORDER_FILL_EXPIRED); + }); + describe('should throw when not enough balance or allowance to fulfill the order', () => { + const balanceToSubtractFromMaker = new BigNumber(3); + const lackingAllowance = new BigNumber(3); + let signedOrder: SignedOrder; + beforeEach('create fillable signed order', async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + ); + }); + it('should throw when taker balance is less than fill amount', async () => { + await zeroEx.token.transferAsync( + takerTokenAddress, takerAddress, coinbase, balanceToSubtractFromMaker, + ); + return expect(zeroEx.exchange.fillOrderAsync( + signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress, + )).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_TAKER_BALANCE); + }); + it('should throw when taker allowance is less than fill amount', async () => { + const newAllowanceWhichIsLessThanFillAmount = fillTakerAmount.minus(lackingAllowance); + await zeroEx.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress, + newAllowanceWhichIsLessThanFillAmount); + return expect(zeroEx.exchange.fillOrderAsync( + signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress, + )).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_TAKER_ALLOWANCE); + }); + it('should throw when maker balance is less than maker fill amount', async () => { + await zeroEx.token.transferAsync( + makerTokenAddress, makerAddress, coinbase, balanceToSubtractFromMaker, + ); + return expect(zeroEx.exchange.fillOrderAsync( + signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress, + )).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_MAKER_BALANCE); + }); + it('should throw when maker allowance is less than maker fill amount', async () => { + const newAllowanceWhichIsLessThanFillAmount = fillTakerAmount.minus(lackingAllowance); + await zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, + newAllowanceWhichIsLessThanFillAmount); + return expect(zeroEx.exchange.fillOrderAsync( + signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress, + )).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_MAKER_ALLOWANCE); + }); }); - it('should throw when maker balance is less than maker fill amount', async () => { - await zeroEx.token.transferAsync( - makerTokenAddress, makerAddress, coinbase, balanceToSubtractFromMaker, + it('should throw when there a rounding error would have occurred', async () => { + const makerAmount = new BigNumber(3); + const takerAmount = new BigNumber(5); + const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync( + makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, + makerAmount, takerAmount, ); + const fillTakerAmountThatCausesRoundingError = new BigNumber(3); return expect(zeroEx.exchange.fillOrderAsync( - signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_MAKER_BALANCE); + signedOrder, fillTakerAmountThatCausesRoundingError, shouldCheckTransfer, takerAddress, + )).to.be.rejectedWith(ExchangeContractErrs.ORDER_FILL_ROUNDING_ERROR); }); - it('should throw when maker allowance is less than maker fill amount', async () => { - const newAllowanceWhichIsLessThanFillAmount = fillTakerAmount.minus(lackingAllowance); - await zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, - newAllowanceWhichIsLessThanFillAmount); - return expect(zeroEx.exchange.fillOrderAsync( - signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_MAKER_ALLOWANCE); + describe('should throw when not enough balance or allowance to pay fees', () => { + const makerFee = new BigNumber(2); + const takerFee = new BigNumber(2); + let signedOrder: SignedOrder; + beforeEach('setup', async () => { + signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( + makerTokenAddress, takerTokenAddress, makerFee, takerFee, + makerAddress, takerAddress, fillableAmount, feeRecipient, + ); + }); + it('should throw when maker doesn\'t have enough balance to pay fees', async () => { + const balanceToSubtractFromMaker = new BigNumber(1); + await zeroEx.token.transferAsync( + zrxTokenAddress, makerAddress, coinbase, balanceToSubtractFromMaker, + ); + return expect(zeroEx.exchange.fillOrderAsync( + signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress, + )).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_MAKER_FEE_BALANCE); + }); + it('should throw when maker doesn\'t have enough allowance to pay fees', async () => { + const newAllowanceWhichIsLessThanFees = makerFee.minus(1); + await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, makerAddress, + newAllowanceWhichIsLessThanFees); + return expect(zeroEx.exchange.fillOrderAsync( + signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress, + )).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_MAKER_FEE_ALLOWANCE); + }); + it('should throw when taker doesn\'t have enough balance to pay fees', async () => { + const balanceToSubtractFromTaker = new BigNumber(1); + await zeroEx.token.transferAsync( + zrxTokenAddress, takerAddress, coinbase, balanceToSubtractFromTaker, + ); + return expect(zeroEx.exchange.fillOrderAsync( + signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress, + )).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_TAKER_FEE_BALANCE); + }); + it('should throw when taker doesn\'t have enough allowance to pay fees', async () => { + const newAllowanceWhichIsLessThanFees = makerFee.minus(1); + await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, takerAddress, + newAllowanceWhichIsLessThanFees); + return expect(zeroEx.exchange.fillOrderAsync( + signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress, + )).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_TAKER_FEE_ALLOWANCE); + }); }); }); - it('should throw when there a rounding error would have occurred', async () => { - const makerAmount = new BigNumber(3); - const takerAmount = new BigNumber(5); - const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, - makerAmount, takerAmount, - ); - const fillTakerAmountThatCausesRoundingError = new BigNumber(3); - return expect(zeroEx.exchange.fillOrderAsync( - signedOrder, fillTakerAmountThatCausesRoundingError, shouldCheckTransfer, takerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.ORDER_FILL_ROUNDING_ERROR); - }); - describe('should throw when not enough balance or allowance to pay fees', () => { - const fillableAmount = new BigNumber(5); - const makerFee = new BigNumber(2); - const takerFee = new BigNumber(2); - let signedOrder: SignedOrder; - beforeEach('setup', async () => { - signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, takerTokenAddress, makerFee, takerFee, - makerAddress, takerAddress, fillableAmount, feeRecipient, + describe('successful fills', () => { + it('should fill a valid order', async () => { + 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)) + .to.be.bignumber.equal(0); + expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)) + .to.be.bignumber.equal(0); + expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)) + .to.be.bignumber.equal(fillableAmount); + await zeroEx.exchange.fillOrderAsync( + signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress); + expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)) + .to.be.bignumber.equal(fillableAmount.minus(fillTakerAmount)); + expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)) + .to.be.bignumber.equal(fillTakerAmount); + expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)) + .to.be.bignumber.equal(fillTakerAmount); + expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)) + .to.be.bignumber.equal(fillableAmount.minus(fillTakerAmount)); }); - it('should throw when maker doesn\'t have enough balance to pay fees', async () => { - const balanceToSubtractFromMaker = new BigNumber(1); - await zeroEx.token.transferAsync( - zrxTokenAddress, makerAddress, coinbase, balanceToSubtractFromMaker, + it('should partially fill the valid order', async () => { + const signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, ); - return expect(zeroEx.exchange.fillOrderAsync( - signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_MAKER_FEE_BALANCE); + const partialFillAmount = new BigNumber(3); + await zeroEx.exchange.fillOrderAsync( + signedOrder, partialFillAmount, shouldCheckTransfer, takerAddress); + expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)) + .to.be.bignumber.equal(fillableAmount.minus(partialFillAmount)); + expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)) + .to.be.bignumber.equal(partialFillAmount); + expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)) + .to.be.bignumber.equal(partialFillAmount); + expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)) + .to.be.bignumber.equal(fillableAmount.minus(partialFillAmount)); }); - it('should throw when maker doesn\'t have enough allowance to pay fees', async () => { - const newAllowanceWhichIsLessThanFees = makerFee.minus(1); - await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, makerAddress, - newAllowanceWhichIsLessThanFees); - return expect(zeroEx.exchange.fillOrderAsync( - signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_MAKER_FEE_ALLOWANCE); - }); - it('should throw when taker doesn\'t have enough balance to pay fees', async () => { - const balanceToSubtractFromTaker = new BigNumber(1); - await zeroEx.token.transferAsync( - zrxTokenAddress, takerAddress, coinbase, balanceToSubtractFromTaker, + it('should fill the valid orders with fees', async () => { + const makerFee = new BigNumber(1); + const takerFee = new BigNumber(2); + const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( + makerTokenAddress, takerTokenAddress, makerFee, takerFee, + makerAddress, takerAddress, fillableAmount, feeRecipient, ); - return expect(zeroEx.exchange.fillOrderAsync( - signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_TAKER_FEE_BALANCE); - }); - it('should throw when taker doesn\'t have enough allowance to pay fees', async () => { - const newAllowanceWhichIsLessThanFees = makerFee.minus(1); - await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, takerAddress, - newAllowanceWhichIsLessThanFees); - return expect(zeroEx.exchange.fillOrderAsync( - signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_TAKER_FEE_ALLOWANCE); + await zeroEx.exchange.fillOrderAsync( + signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress); + expect(await zeroEx.token.getBalanceAsync(zrxTokenAddress, feeRecipient)) + .to.be.bignumber.equal(makerFee.plus(takerFee)); }); }); }); - describe('successful fills', () => { - it('should fill a valid order', async () => { - const fillableAmount = new BigNumber(5); - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( + describe('#batchFillOrderAsync', () => { + let anotherSignedOrder: SignedOrder; + let anotherOrderHashHex: string; + beforeEach(async () => { + anotherSignedOrder = 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)) - .to.be.bignumber.equal(0); - expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)) - .to.be.bignumber.equal(0); - expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)) - .to.be.bignumber.equal(fillableAmount); - await zeroEx.exchange.fillOrderAsync(signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress); - expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)) - .to.be.bignumber.equal(fillableAmount.minus(fillTakerAmount)); - expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)) - .to.be.bignumber.equal(fillTakerAmount); - expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)) - .to.be.bignumber.equal(fillTakerAmount); - expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)) - .to.be.bignumber.equal(fillableAmount.minus(fillTakerAmount)); + anotherOrderHashHex = await zeroEx.getOrderHashHexAsync(anotherSignedOrder); }); - it('should partially fill the 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.fillOrderAsync(signedOrder, partialFillAmount, shouldCheckTransfer, takerAddress); - expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)) - .to.be.bignumber.equal(fillableAmount.minus(partialFillAmount)); - expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)) - .to.be.bignumber.equal(partialFillAmount); - expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)) - .to.be.bignumber.equal(partialFillAmount); - expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)) - .to.be.bignumber.equal(fillableAmount.minus(partialFillAmount)); + describe('failed batch fills', () => { + }); - it('should fill the valid orders with fees', async () => { - const fillableAmount = new BigNumber(5); - const makerFee = new BigNumber(1); - const takerFee = new BigNumber(2); - const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, takerTokenAddress, makerFee, takerFee, - makerAddress, takerAddress, fillableAmount, feeRecipient, - ); - await zeroEx.exchange.fillOrderAsync(signedOrder, fillTakerAmount, shouldCheckTransfer, takerAddress); - expect(await zeroEx.token.getBalanceAsync(zrxTokenAddress, feeRecipient)) - .to.be.bignumber.equal(makerFee.plus(takerFee)); + describe('successful batch fills', () => { + }); }); }); @@ -399,7 +414,7 @@ describe('ExchangeWrapper', () => { return expect(zeroEx.exchange.batchCancelOrderAsync([])) .to.be.rejectedWith('Can not cancel an empty batch'); }); - it.only('should throw when orders have different makers', async () => { + it('should throw when orders have different makers', async () => { const signedOrderWithADifferentMaker = await fillScenarios.createFillableSignedOrderAsync( makerTokenAddress, takerTokenAddress, takerAddress, takerAddress, fillableAmount, ); |