diff options
-rw-r--r-- | src/utils/order_validation_utils.ts | 9 | ||||
-rw-r--r-- | test/order_validation_test.ts | 142 |
2 files changed, 107 insertions, 44 deletions
diff --git a/src/utils/order_validation_utils.ts b/src/utils/order_validation_utils.ts index de20cf06d..160afc43e 100644 --- a/src/utils/order_validation_utils.ts +++ b/src/utils/order_validation_utils.ts @@ -145,6 +145,9 @@ export class OrderValidationUtils { signedOrder.takerTokenAddress, senderAddress); const isTakerTokenZRX = signedOrder.takerTokenAddress === zrxTokenAddress; + // exchangeRate is the price of one maker token denominated in taker tokens + const exchangeRate = signedOrder.takerTokenAmount.div(signedOrder.makerTokenAmount); + const fillMakerAmount = fillTakerAmount.div(exchangeRate); const requiredTakerAmount = isTakerTokenZRX ? fillTakerAmount.plus(signedOrder.takerFee) : fillTakerAmount; if (requiredTakerAmount.greaterThan(takerBalance)) { @@ -155,7 +158,11 @@ export class OrderValidationUtils { } if (!isTakerTokenZRX) { - const takerZRXBalance = await this.tokenWrapper.getBalanceAsync(zrxTokenAddress, senderAddress); + const isMakerTokenZRX = signedOrder.makerTokenAddress === zrxTokenAddress; + let takerZRXBalance = await this.tokenWrapper.getBalanceAsync(zrxTokenAddress, senderAddress); + if (isMakerTokenZRX) { + takerZRXBalance = takerZRXBalance.plus(fillMakerAmount); + } const takerZRXAllowance = await this.tokenWrapper.getProxyAllowanceAsync(zrxTokenAddress, senderAddress); if (signedOrder.takerFee.greaterThan(takerZRXBalance)) { diff --git a/test/order_validation_test.ts b/test/order_validation_test.ts index 9e6817c8b..8f3e6ce0b 100644 --- a/test/order_validation_test.ts +++ b/test/order_validation_test.ts @@ -207,7 +207,7 @@ describe('OrderValidation', () => { .to.be.rejectedWith(ExchangeContractErrs.OrderAlreadyCancelledOrFilled); }); }); - describe('#validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync', () => { + describe('#validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync', () => { describe('should throw when not enough balance or allowance to fulfill the order', () => { const balanceToSubtractFromMaker = new BigNumber(3); const balanceToSubtractFromTaker = new BigNumber(3); @@ -218,22 +218,6 @@ describe('OrderValidation', () => { makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, ); }); - it('should throw when taker balance is less than fill amount', async () => { - await zeroEx.token.transferAsync( - takerTokenAddress, takerAddress, coinbase, balanceToSubtractFromTaker, - ); - return expect((orderValidationUtils as any).validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync( - signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress, - )).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerBalance); - }); - 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((orderValidationUtils as any).validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync( - signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress, - )).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance); - }); it('should throw when maker balance is less than maker fill amount', async () => { await zeroEx.token.transferAsync( makerTokenAddress, makerAddress, coinbase, balanceToSubtractFromMaker, @@ -278,23 +262,6 @@ describe('OrderValidation', () => { signedOrder, fillTakerAmount, zrxTokenAddress, )).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerFeeAllowance); }); - 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((orderValidationUtils as any).validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync( - signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress, - )).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerFeeBalance); - }); - 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((orderValidationUtils as any).validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync( - signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress, - )).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerFeeAllowance); - }); }); describe('should throw on insufficient balance or allowance when makerToken is ZRX', () => { @@ -326,6 +293,95 @@ describe('OrderValidation', () => { )).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerAllowance); }); }); + describe('should correctly validate fees amounts if taker token is ZRX', + () => { + let signedOrder: SignedOrder; + let txHash: string; + it('should not throw if maker will have enough ZRX to pay fees after the transfer', async () => { + const makerFee = new BigNumber(2); + const takerFee = new BigNumber(2); + signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( + makerTokenAddress, zrxTokenAddress, makerFee, takerFee, + makerAddress, takerAddress, fillableAmount, feeRecipient, + ); + txHash = await zeroEx.token.transferAsync(zrxTokenAddress, makerAddress, coinbase, makerFee); + await zeroEx.awaitTransactionMinedAsync(txHash); + await (orderValidationUtils as any).validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync( + signedOrder, fillTakerAmount, zrxTokenAddress, + ); + }); + it('should throw if maker will not have enough ZRX to pay fees even after the transfer', async () => { + const makerFee = fillableAmount.plus(1); + const takerFee = fillableAmount.plus(1); + signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( + makerTokenAddress, zrxTokenAddress, makerFee, takerFee, + makerAddress, takerAddress, fillableAmount, feeRecipient, + ); + txHash = await zeroEx.token.transferAsync(zrxTokenAddress, makerAddress, coinbase, makerFee); + await zeroEx.awaitTransactionMinedAsync(txHash); + return expect( + (orderValidationUtils as any).validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync( + signedOrder, fillTakerAmount, zrxTokenAddress, + )).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerFeeBalance); + }); + }); + }); + describe('#validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync', () => { + describe('should throw when not enough balance or allowance to fulfill the order', () => { + const balanceToSubtractFromMaker = new BigNumber(3); + const balanceToSubtractFromTaker = 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, balanceToSubtractFromTaker, + ); + return expect((orderValidationUtils as any).validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync( + signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress, + )).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerBalance); + }); + 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((orderValidationUtils as any).validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync( + signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress, + )).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance); + }); + }); + 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 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((orderValidationUtils as any).validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync( + signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress, + )).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerFeeBalance); + }); + 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((orderValidationUtils as any).validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync( + signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress, + )).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerFeeAllowance); + }); + }); describe('should throw on insufficient balance or allowance when takerToken is ZRX', () => { const makerFee = new BigNumber(2); @@ -356,21 +412,21 @@ describe('OrderValidation', () => { )).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance); }); }); - describe('should not throw if user doesn\'t have sufficient ZRX to pay the fees, but will after a transfer', + describe('should correctly validate fees amounts if maker token is ZRX', () => { let signedOrder: SignedOrder; let txHash: string; - it('should not throw if maker will have enough ZRX to pay fees after the transfer', async () => { + it('should not throw if taker will have enough ZRX to pay fees after the transfer', async () => { const makerFee = new BigNumber(2); const takerFee = new BigNumber(2); signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, zrxTokenAddress, makerFee, takerFee, + zrxTokenAddress, takerTokenAddress, makerFee, takerFee, makerAddress, takerAddress, fillableAmount, feeRecipient, ); - txHash = await zeroEx.token.transferAsync(zrxTokenAddress, makerAddress, coinbase, makerFee); + txHash = await zeroEx.token.transferAsync(zrxTokenAddress, takerAddress, coinbase, takerFee); await zeroEx.awaitTransactionMinedAsync(txHash); - await (orderValidationUtils as any).validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync( - signedOrder, fillTakerAmount, zrxTokenAddress, + await (orderValidationUtils as any).validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync( + signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress, ); }); it('should throw if maker will not have enough ZRX to pay fees even after the transfer', async () => { @@ -380,11 +436,11 @@ describe('OrderValidation', () => { makerTokenAddress, zrxTokenAddress, makerFee, takerFee, makerAddress, takerAddress, fillableAmount, feeRecipient, ); - txHash = await zeroEx.token.transferAsync(zrxTokenAddress, makerAddress, coinbase, makerFee); + txHash = await zeroEx.token.transferAsync(zrxTokenAddress, takerAddress, coinbase, takerFee); await zeroEx.awaitTransactionMinedAsync(txHash); return expect( - (orderValidationUtils as any).validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync( - signedOrder, fillTakerAmount, zrxTokenAddress, + (orderValidationUtils as any).validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync( + signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress, )).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerFeeBalance); }); }); |