diff options
author | Leonid Logvinov <logvinov.leon@gmail.com> | 2017-10-09 18:07:48 +0800 |
---|---|---|
committer | Leonid Logvinov <logvinov.leon@gmail.com> | 2017-10-09 18:07:48 +0800 |
commit | 1424a7302a1d1b3a9c36b3a6ce5a344f8045400d (patch) | |
tree | 5266fdd4b0f167fc1fa5131fc422f2196a4bf2ff /test | |
parent | 54ac354809c493cedfec5cd19b4707341b2c9f93 (diff) | |
download | dexon-0x-contracts-1424a7302a1d1b3a9c36b3a6ce5a344f8045400d.tar.gz dexon-0x-contracts-1424a7302a1d1b3a9c36b3a6ce5a344f8045400d.tar.zst dexon-0x-contracts-1424a7302a1d1b3a9c36b3a6ce5a344f8045400d.zip |
Implement transfer Emulator and rewrite tests
Diffstat (limited to 'test')
-rw-r--r-- | test/exchange_transfer_simulator_test.ts | 91 | ||||
-rw-r--r-- | test/order_validation_test.ts | 279 |
2 files changed, 137 insertions, 233 deletions
diff --git a/test/exchange_transfer_simulator_test.ts b/test/exchange_transfer_simulator_test.ts new file mode 100644 index 000000000..0331699c8 --- /dev/null +++ b/test/exchange_transfer_simulator_test.ts @@ -0,0 +1,91 @@ +import * as chai from 'chai'; +import * as BigNumber from 'bignumber.js'; +import * as Web3 from 'web3'; +import {chaiSetup} from './utils/chai_setup'; +import {web3Factory} from './utils/web3_factory'; +import {ZeroEx, SignedOrder, ExchangeContractErrs, Token} from '../src'; +import {TradeSide, TransferType} from '../src/types'; +import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; +import {FillScenarios} from './utils/fill_scenarios'; +import {ExchangeTransferSimulator} from '../src/utils/exchange_transfer_simulator'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(); + +describe('ExchangeTransferSimulator', () => { + const web3 = web3Factory.create(); + const zeroEx = new ZeroEx(web3.currentProvider); + const transferAmount = new BigNumber(5); + let userAddresses: string[]; + let tokens: Token[]; + let coinbase: string; + let sender: string; + let recipient: string; + let exampleTokenAddress: string; + let exchangeTransferSimulator: ExchangeTransferSimulator; + let txHash: string; + before(async () => { + userAddresses = await zeroEx.getAvailableAddressesAsync(); + [coinbase, sender, recipient] = userAddresses; + tokens = await zeroEx.tokenRegistry.getTokensAsync(); + exampleTokenAddress = tokens[0].address; + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('#transferFromAsync', () => { + beforeEach(() => { + exchangeTransferSimulator = new ExchangeTransferSimulator(zeroEx.token); + }); + it('throws if the user doesn\'t have enough balance', async () => { + return expect(exchangeTransferSimulator.transferFromAsync( + exampleTokenAddress, sender, recipient, transferAmount, TradeSide.Maker, TransferType.Trade, + )).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance); + }); + it('throws if the user doesn\'t have enough allowance', async () => { + txHash = await zeroEx.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount); + await zeroEx.awaitTransactionMinedAsync(txHash); + return expect(exchangeTransferSimulator.transferFromAsync( + exampleTokenAddress, sender, recipient, transferAmount, TradeSide.Taker, TransferType.Trade, + )).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance); + }); + it('updates balances and proxyAllowance after transfer', async () => { + txHash = await zeroEx.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount); + await zeroEx.awaitTransactionMinedAsync(txHash); + txHash = await zeroEx.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount); + await zeroEx.awaitTransactionMinedAsync(txHash); + await exchangeTransferSimulator.transferFromAsync( + exampleTokenAddress, sender, recipient, transferAmount, TradeSide.Taker, TransferType.Trade, + ); + const senderBalance = await (exchangeTransferSimulator as any).getBalanceAsync(exampleTokenAddress, sender); + const recipientBalance = await (exchangeTransferSimulator as any).getBalanceAsync( + exampleTokenAddress, recipient); + const senderProxyAllowance = await (exchangeTransferSimulator as any).getProxyAllowanceAsync( + exampleTokenAddress, sender); + expect(senderBalance).to.be.bignumber.equal(0); + expect(recipientBalance).to.be.bignumber.equal(transferAmount); + expect(senderProxyAllowance).to.be.bignumber.equal(0); + }); + it('doesn\'t update proxyAllowance after transfer if unlimited', async () => { + txHash = await zeroEx.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount); + await zeroEx.awaitTransactionMinedAsync(txHash); + txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(exampleTokenAddress, sender); + await zeroEx.awaitTransactionMinedAsync(txHash); + await exchangeTransferSimulator.transferFromAsync( + exampleTokenAddress, sender, recipient, transferAmount, TradeSide.Taker, TransferType.Trade, + ); + const senderBalance = await (exchangeTransferSimulator as any).getBalanceAsync(exampleTokenAddress, sender); + const recipientBalance = await (exchangeTransferSimulator as any).getBalanceAsync( + exampleTokenAddress, recipient); + const senderProxyAllowance = await (exchangeTransferSimulator as any).getProxyAllowanceAsync( + exampleTokenAddress, sender); + expect(senderBalance).to.be.bignumber.equal(0); + expect(recipientBalance).to.be.bignumber.equal(transferAmount); + expect(senderProxyAllowance).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); + }); + }); +}); 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(); }); }); }); |