From 1424a7302a1d1b3a9c36b3a6ce5a344f8045400d Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 9 Oct 2017 13:07:48 +0300 Subject: Implement transfer Emulator and rewrite tests --- test/order_validation_test.ts | 279 +++++++----------------------------------- 1 file changed, 46 insertions(+), 233 deletions(-) (limited to 'test/order_validation_test.ts') diff --git a/test/order_validation_test.ts b/test/order_validation_test.ts index c1a0a6c8c..6f9388a69 100644 --- a/test/order_validation_test.ts +++ b/test/order_validation_test.ts @@ -2,13 +2,16 @@ import * as chai from 'chai'; import * as Web3 from 'web3'; import * as BigNumber from 'bignumber.js'; import promisify = require('es6-promisify'); +import * as Sinon from 'sinon'; import {chaiSetup} from './utils/chai_setup'; import {web3Factory} from './utils/web3_factory'; import {ZeroEx, SignedOrder, Token, ExchangeContractErrs, ZeroExError} from '../src'; +import {TradeSide, TransferType} from '../src/types'; import {TokenUtils} from './utils/token_utils'; import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; import {FillScenarios} from './utils/fill_scenarios'; import {OrderValidationUtils} from '../src/utils/order_validation_utils'; +import {ExchangeTransferSimulator} from '../src/utils/exchange_transfer_simulator'; chaiSetup.configure(); const expect = chai.expect; @@ -207,242 +210,52 @@ describe('OrderValidation', () => { .to.be.rejectedWith(ExchangeContractErrs.OrderAlreadyCancelledOrFilled); }); }); - 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); - 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 maker balance is less than maker fill amount', async () => { - await zeroEx.token.transferAsync( - makerTokenAddress, makerAddress, coinbase, balanceToSubtractFromMaker, - ); - return expect((orderValidationUtils as any).validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync( - signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress, - )).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance); - }); - 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((orderValidationUtils as any).validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync( - signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress, - )).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerAllowance); - }); + describe('#validateFillOrderBalancesAllowancesThrowIfInvalidAsync', () => { + let exchangeTransferSimulator: ExchangeTransferSimulator; + let transferFromAsync: any; + const bigNumberMatch = (expected: BigNumber.BigNumber) => { + return Sinon.match((value: BigNumber.BigNumber) => value.eq(expected)); + }; + beforeEach('create exchangeTransferSimulator', async () => { + exchangeTransferSimulator = new ExchangeTransferSimulator(zeroEx.token); + transferFromAsync = Sinon.spy(); + exchangeTransferSimulator.transferFromAsync = transferFromAsync; }); - describe('should throw when not enough balance or allowance to pay fees', () => { + it('should call exchangeTransferSimulator.transferFrom in a correct order', async () => { 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((orderValidationUtils as any).validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync( - signedOrder, fillTakerAmount, zrxTokenAddress, - )).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerFeeBalance); - }); - 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((orderValidationUtils as any).validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync( - signedOrder, fillTakerAmount, zrxTokenAddress, - )).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerFeeAllowance); - }); - }); - describe('should throw on insufficient balance or allowance when makerToken is ZRX', - () => { - const makerFee = new BigNumber(2); - const takerFee = new BigNumber(2); - let signedOrder: SignedOrder; - beforeEach(async () => { - signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - zrxTokenAddress, takerTokenAddress, makerFee, takerFee, - makerAddress, takerAddress, fillableAmount, feeRecipient, - ); - }); - it('should throw on insufficient balance when makerToken is ZRX', async () => { - const balanceToSubtractFromMaker = new BigNumber(1); - await zeroEx.token.transferAsync( - zrxTokenAddress, makerAddress, coinbase, balanceToSubtractFromMaker, - ); - return expect((orderValidationUtils as any).validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync( - signedOrder, fillTakerAmount, zrxTokenAddress, - )).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance); - }); - it('should throw on insufficient allowance when makerToken is ZRX', async () => { - const oldAllowance = await zeroEx.token.getProxyAllowanceAsync(zrxTokenAddress, makerAddress); - const newAllowanceWhichIsInsufficient = oldAllowance.minus(1); - await zeroEx.token.setProxyAllowanceAsync( - zrxTokenAddress, makerAddress, newAllowanceWhichIsInsufficient); - return expect((orderValidationUtils as any).validateFillOrderMakerBalancesAllowancesThrowIfInvalidAsync( - signedOrder, fillTakerAmount, zrxTokenAddress, - )).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); - const takerFee = new BigNumber(2); - let signedOrder: SignedOrder; - beforeEach(async () => { - signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, zrxTokenAddress, makerFee, takerFee, - makerAddress, takerAddress, fillableAmount, feeRecipient, - ); - }); - it('should throw on insufficient balance when takerToken is ZRX', 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.InsufficientTakerBalance); - }); - it('should throw on insufficient allowance when takerToken is ZRX', async () => { - const oldAllowance = await zeroEx.token.getProxyAllowanceAsync(zrxTokenAddress, takerAddress); - const newAllowanceWhichIsInsufficient = oldAllowance.minus(1); - await zeroEx.token.setProxyAllowanceAsync( - zrxTokenAddress, takerAddress, newAllowanceWhichIsInsufficient); - return expect((orderValidationUtils as any).validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync( - signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress, - )).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance); - }); - }); - describe('should correctly validate fees amounts if maker token is ZRX', - () => { - let signedOrder: SignedOrder; - let txHash: string; - 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( - zrxTokenAddress, takerTokenAddress, makerFee, takerFee, - makerAddress, takerAddress, fillableAmount, feeRecipient, - ); - txHash = await zeroEx.token.transferAsync(zrxTokenAddress, takerAddress, coinbase, takerFee); - await zeroEx.awaitTransactionMinedAsync(txHash); - 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 () => { - const makerFee = fillableAmount.plus(1); - const takerFee = fillableAmount.plus(1); - signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - zrxTokenAddress, takerTokenAddress, makerFee, takerFee, - makerAddress, takerAddress, fillableAmount, feeRecipient, - ); - txHash = await zeroEx.token.transferAsync(zrxTokenAddress, takerAddress, coinbase, takerFee); - await zeroEx.awaitTransactionMinedAsync(txHash); - return expect( - (orderValidationUtils as any).validateFillOrderTakerBalancesAllowancesThrowIfInvalidAsync( - signedOrder, fillTakerAmount, takerAddress, zrxTokenAddress, - )).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerFeeBalance); - }); + const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( + makerTokenAddress, takerTokenAddress, makerFee, takerFee, + makerAddress, takerAddress, fillableAmount, feeRecipient, + ); + await orderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( + exchangeTransferSimulator, signedOrder, fillableAmount, takerAddress, zrxTokenAddress, + ); + expect(transferFromAsync.callCount).to.be.equal(4); + expect( + transferFromAsync.getCall(0).calledWith( + makerTokenAddress, makerAddress, takerAddress, bigNumberMatch(fillableAmount), + TradeSide.Maker, TransferType.Trade, + ), + ).to.be.true(); + expect( + transferFromAsync.getCall(1).calledWith( + takerTokenAddress, takerAddress, makerAddress, bigNumberMatch(fillableAmount), + TradeSide.Taker, TransferType.Trade, + ), + ).to.be.true(); + expect( + transferFromAsync.getCall(2).calledWith( + zrxTokenAddress, makerAddress, feeRecipient, bigNumberMatch(makerFee), + TradeSide.Maker, TransferType.Fee, + ), + ).to.be.true(); + expect( + transferFromAsync.getCall(3).calledWith( + zrxTokenAddress, takerAddress, feeRecipient, bigNumberMatch(takerFee), + TradeSide.Taker, TransferType.Fee, + ), + ).to.be.true(); }); }); }); -- cgit v1.2.3