diff options
author | Fabio Berger <me@fabioberger.com> | 2017-11-13 10:12:37 +0800 |
---|---|---|
committer | Fabio Berger <me@fabioberger.com> | 2017-11-13 10:12:37 +0800 |
commit | e33027c6244b99a1fb181404668bd2a259d923e2 (patch) | |
tree | 24438eda01318a80b5cbc7f1939d1640764a9859 /test/order_state_watcher_test.ts | |
parent | 5d2b6585c66fc17a36bb9841a3b3fb009e23024c (diff) | |
parent | b0be323e899ea7be42b6c695b4fd6d526070b213 (diff) | |
download | dexon-sol-tools-e33027c6244b99a1fb181404668bd2a259d923e2.tar.gz dexon-sol-tools-e33027c6244b99a1fb181404668bd2a259d923e2.tar.zst dexon-sol-tools-e33027c6244b99a1fb181404668bd2a259d923e2.zip |
Merge branch 'development' into feature/receipt-status
* development: (164 commits)
Remove old tests
Remove unused code
Fix tests
Remove redundant spaces
Don't store empty objects
Fix a typo
Remove duplicate operations
Remove redundant instance variables
Fix tests
Remove blockStore and default to numConfirmations === 0
Add a comment
Store number of confirmations in a blockStore
Remove tautology check
Pass blockStore to eventWatcher
Fix last merge conflicts
Clear cache on unsubscribe
Clear store cache on events
Add more configs for order watcher
Make subscribe function async and make blockStore operational
Adjust tests to new interface
...
# Conflicts:
# package.json
# src/types.ts
# yarn.lock
Diffstat (limited to 'test/order_state_watcher_test.ts')
-rw-r--r-- | test/order_state_watcher_test.ts | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/test/order_state_watcher_test.ts b/test/order_state_watcher_test.ts new file mode 100644 index 000000000..c8a4a8064 --- /dev/null +++ b/test/order_state_watcher_test.ts @@ -0,0 +1,356 @@ +import 'mocha'; +import * as chai from 'chai'; +import * as _ from 'lodash'; +import * as Web3 from 'web3'; +import BigNumber from 'bignumber.js'; +import { chaiSetup } from './utils/chai_setup'; +import { web3Factory } from './utils/web3_factory'; +import { Web3Wrapper } from '../src/web3_wrapper'; +import { OrderStateWatcher } from '../src/order_watcher/order_state_watcher'; +import { + Token, + ZeroEx, + LogEvent, + DecodedLogEvent, + ZeroExConfig, + OrderState, + SignedOrder, + ZeroExError, + OrderStateValid, + OrderStateInvalid, + ExchangeContractErrs, +} from '../src'; +import { TokenUtils } from './utils/token_utils'; +import { FillScenarios } from './utils/fill_scenarios'; +import { DoneCallback } from '../src/types'; +import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; +import {reportCallbackErrors} from './utils/report_callback_errors'; + +const TIMEOUT_MS = 150; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(); + +describe('OrderStateWatcher', () => { + let web3: Web3; + let zeroEx: ZeroEx; + let tokens: Token[]; + let tokenUtils: TokenUtils; + let fillScenarios: FillScenarios; + let userAddresses: string[]; + let zrxTokenAddress: string; + let exchangeContractAddress: string; + let makerToken: Token; + let takerToken: Token; + let maker: string; + let taker: string; + let web3Wrapper: Web3Wrapper; + let signedOrder: SignedOrder; + const fillableAmount = new BigNumber(5); + before(async () => { + web3 = web3Factory.create(); + zeroEx = new ZeroEx(web3.currentProvider); + exchangeContractAddress = await zeroEx.exchange.getContractAddressAsync(); + userAddresses = await zeroEx.getAvailableAddressesAsync(); + [, maker, taker] = userAddresses; + tokens = await zeroEx.tokenRegistry.getTokensAsync(); + tokenUtils = new TokenUtils(tokens); + zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address; + fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress); + [makerToken, takerToken] = tokenUtils.getNonProtocolTokens(); + web3Wrapper = (zeroEx as any)._web3Wrapper; + }); + describe('#removeOrder', async () => { + it('should successfully remove existing order', async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerToken.address, takerToken.address, maker, taker, fillableAmount, + ); + const orderHash = ZeroEx.getOrderHashHex(signedOrder); + zeroEx.orderStateWatcher.addOrder(signedOrder); + expect((zeroEx.orderStateWatcher as any)._orderByOrderHash).to.include({ + [orderHash]: signedOrder, + }); + let dependentOrderHashes = (zeroEx.orderStateWatcher as any)._dependentOrderHashes; + expect(dependentOrderHashes[signedOrder.maker][signedOrder.makerTokenAddress]).to.have.keys(orderHash); + zeroEx.orderStateWatcher.removeOrder(orderHash); + expect((zeroEx.orderStateWatcher as any)._orderByOrderHash).to.not.include({ + [orderHash]: signedOrder, + }); + dependentOrderHashes = (zeroEx.orderStateWatcher as any)._dependentOrderHashes; + expect(dependentOrderHashes[signedOrder.maker]).to.be.undefined(); + }); + it('should no-op when removing a non-existing order', async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerToken.address, takerToken.address, maker, taker, fillableAmount, + ); + const orderHash = ZeroEx.getOrderHashHex(signedOrder); + const nonExistentOrderHash = `0x${orderHash.substr(2).split('').reverse().join('')}`; + zeroEx.orderStateWatcher.removeOrder(nonExistentOrderHash); + }); + }); + describe('#subscribe', async () => { + afterEach(async () => { + zeroEx.orderStateWatcher.unsubscribe(); + }); + it('should fail when trying to subscribe twice', async () => { + zeroEx.orderStateWatcher.subscribe(_.noop); + expect(() => zeroEx.orderStateWatcher.subscribe(_.noop)) + .to.throw(ZeroExError.SubscriptionAlreadyPresent); + }); + }); + describe('tests with cleanup', async () => { + afterEach(async () => { + zeroEx.orderStateWatcher.unsubscribe(); + const orderHash = ZeroEx.getOrderHashHex(signedOrder); + zeroEx.orderStateWatcher.removeOrder(orderHash); + }); + it('should emit orderStateInvalid when maker allowance set to 0 for watched order', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerToken.address, takerToken.address, maker, taker, fillableAmount, + ); + const orderHash = ZeroEx.getOrderHashHex(signedOrder); + zeroEx.orderStateWatcher.addOrder(signedOrder); + const callback = reportCallbackErrors(done)((orderState: OrderState) => { + expect(orderState.isValid).to.be.false(); + const invalidOrderState = orderState as OrderStateInvalid; + expect(invalidOrderState.orderHash).to.be.equal(orderHash); + expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance); + done(); + }); + zeroEx.orderStateWatcher.subscribe(callback); + await zeroEx.token.setProxyAllowanceAsync(makerToken.address, maker, new BigNumber(0)); + })().catch(done); + }); + it('should not emit an orderState event when irrelevant Transfer event received', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerToken.address, takerToken.address, maker, taker, fillableAmount, + ); + const orderHash = ZeroEx.getOrderHashHex(signedOrder); + zeroEx.orderStateWatcher.addOrder(signedOrder); + const callback = reportCallbackErrors(done)((orderState: OrderState) => { + throw new Error('OrderState callback fired for irrelevant order'); + }); + zeroEx.orderStateWatcher.subscribe(callback); + const notTheMaker = userAddresses[0]; + const anyRecipient = taker; + const transferAmount = new BigNumber(2); + const notTheMakerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, notTheMaker); + await zeroEx.token.transferAsync(makerToken.address, notTheMaker, anyRecipient, transferAmount); + setTimeout(() => { + done(); + }, TIMEOUT_MS); + })().catch(done); + }); + it('should emit orderStateInvalid when maker moves balance backing watched order', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerToken.address, takerToken.address, maker, taker, fillableAmount, + ); + const orderHash = ZeroEx.getOrderHashHex(signedOrder); + zeroEx.orderStateWatcher.addOrder(signedOrder); + const callback = reportCallbackErrors(done)((orderState: OrderState) => { + expect(orderState.isValid).to.be.false(); + const invalidOrderState = orderState as OrderStateInvalid; + expect(invalidOrderState.orderHash).to.be.equal(orderHash); + expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerBalance); + done(); + }); + zeroEx.orderStateWatcher.subscribe(callback); + const anyRecipient = taker; + const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); + await zeroEx.token.transferAsync(makerToken.address, maker, anyRecipient, makerBalance); + })().catch(done); + }); + it('should emit orderStateInvalid when watched order fully filled', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerToken.address, takerToken.address, maker, taker, fillableAmount, + ); + const orderHash = ZeroEx.getOrderHashHex(signedOrder); + zeroEx.orderStateWatcher.addOrder(signedOrder); + + let eventCount = 0; + const callback = reportCallbackErrors(done)((orderState: OrderState) => { + eventCount++; + expect(orderState.isValid).to.be.false(); + const invalidOrderState = orderState as OrderStateInvalid; + expect(invalidOrderState.orderHash).to.be.equal(orderHash); + expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero); + if (eventCount === 2) { + done(); + } + }); + zeroEx.orderStateWatcher.subscribe(callback); + + const shouldThrowOnInsufficientBalanceOrAllowance = true; + await zeroEx.exchange.fillOrderAsync( + signedOrder, fillableAmount, shouldThrowOnInsufficientBalanceOrAllowance, taker, + ); + })().catch(done); + }); + it('should emit orderStateValid when watched order partially filled', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerToken.address, takerToken.address, maker, taker, fillableAmount, + ); + + const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); + const takerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, taker); + + const fillAmountInBaseUnits = new BigNumber(2); + const orderHash = ZeroEx.getOrderHashHex(signedOrder); + zeroEx.orderStateWatcher.addOrder(signedOrder); + + let eventCount = 0; + const callback = reportCallbackErrors(done)((orderState: OrderState) => { + eventCount++; + expect(orderState.isValid).to.be.true(); + const validOrderState = orderState as OrderStateValid; + expect(validOrderState.orderHash).to.be.equal(orderHash); + const orderRelevantState = validOrderState.orderRelevantState; + const remainingMakerBalance = makerBalance.sub(fillAmountInBaseUnits); + const remainingFillable = fillableAmount.minus(fillAmountInBaseUnits); + expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal( + remainingFillable); + expect(orderRelevantState.makerBalance).to.be.bignumber.equal(remainingMakerBalance); + if (eventCount === 2) { + done(); + } + }); + zeroEx.orderStateWatcher.subscribe(callback); + const shouldThrowOnInsufficientBalanceOrAllowance = true; + await zeroEx.exchange.fillOrderAsync( + signedOrder, fillAmountInBaseUnits, shouldThrowOnInsufficientBalanceOrAllowance, taker, + ); + })().catch(done); + }); + describe('remainingFillableMakerTokenAmount', () => { + it('should calculate correct remaining fillable', (done: DoneCallback) => { + (async () => { + const takerFillableAmount = new BigNumber(10); + const makerFillableAmount = new BigNumber(20); + signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync( + makerToken.address, takerToken.address, maker, taker, makerFillableAmount, takerFillableAmount); + const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); + const takerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, taker); + const fillAmountInBaseUnits = new BigNumber(2); + const orderHash = ZeroEx.getOrderHashHex(signedOrder); + zeroEx.orderStateWatcher.addOrder(signedOrder); + let eventCount = 0; + const callback = reportCallbackErrors(done)((orderState: OrderState) => { + eventCount++; + expect(orderState.isValid).to.be.true(); + const validOrderState = orderState as OrderStateValid; + expect(validOrderState.orderHash).to.be.equal(orderHash); + const orderRelevantState = validOrderState.orderRelevantState; + expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal( + new BigNumber(16)); + if (eventCount === 2) { + done(); + } + }); + zeroEx.orderStateWatcher.subscribe(callback); + const shouldThrowOnInsufficientBalanceOrAllowance = true; + await zeroEx.exchange.fillOrderAsync( + signedOrder, fillAmountInBaseUnits, shouldThrowOnInsufficientBalanceOrAllowance, taker, + ); + })().catch(done); + }); + it('should equal approved amount when approved amount is lowest', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerToken.address, takerToken.address, maker, taker, fillableAmount, + ); + + const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); + + const changedMakerApprovalAmount = new BigNumber(3); + zeroEx.orderStateWatcher.addOrder(signedOrder); + + const callback = reportCallbackErrors(done)((orderState: OrderState) => { + const validOrderState = orderState as OrderStateValid; + const orderRelevantState = validOrderState.orderRelevantState; + expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal( + changedMakerApprovalAmount); + done(); + }); + zeroEx.orderStateWatcher.subscribe(callback); + await zeroEx.token.setProxyAllowanceAsync(makerToken.address, maker, changedMakerApprovalAmount); + })().catch(done); + }); + it('should equal balance amount when balance amount is lowest', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerToken.address, takerToken.address, maker, taker, fillableAmount, + ); + + const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); + + const remainingAmount = new BigNumber(1); + const transferAmount = makerBalance.sub(remainingAmount); + zeroEx.orderStateWatcher.addOrder(signedOrder); + + const callback = reportCallbackErrors(done)((orderState: OrderState) => { + const validOrderState = orderState as OrderStateValid; + const orderRelevantState = validOrderState.orderRelevantState; + expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal( + remainingAmount); + done(); + }); + zeroEx.orderStateWatcher.subscribe(callback); + await zeroEx.token.transferAsync( + makerToken.address, maker, ZeroEx.NULL_ADDRESS, transferAmount); + })().catch(done); + }); + }); + it('should emit orderStateInvalid when watched order cancelled', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerToken.address, takerToken.address, maker, taker, fillableAmount, + ); + const orderHash = ZeroEx.getOrderHashHex(signedOrder); + zeroEx.orderStateWatcher.addOrder(signedOrder); + + const callback = reportCallbackErrors(done)((orderState: OrderState) => { + expect(orderState.isValid).to.be.false(); + const invalidOrderState = orderState as OrderStateInvalid; + expect(invalidOrderState.orderHash).to.be.equal(orderHash); + expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero); + done(); + }); + zeroEx.orderStateWatcher.subscribe(callback); + + const shouldThrowOnInsufficientBalanceOrAllowance = true; + await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount); + })().catch(done); + }); + it('should emit orderStateValid when watched order partially cancelled', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerToken.address, takerToken.address, maker, taker, fillableAmount, + ); + + const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); + const takerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, taker); + + const cancelAmountInBaseUnits = new BigNumber(2); + const orderHash = ZeroEx.getOrderHashHex(signedOrder); + zeroEx.orderStateWatcher.addOrder(signedOrder); + + const callback = reportCallbackErrors(done)((orderState: OrderState) => { + expect(orderState.isValid).to.be.true(); + const validOrderState = orderState as OrderStateValid; + expect(validOrderState.orderHash).to.be.equal(orderHash); + const orderRelevantState = validOrderState.orderRelevantState; + expect(orderRelevantState.canceledTakerTokenAmount).to.be.bignumber.equal(cancelAmountInBaseUnits); + done(); + }); + zeroEx.orderStateWatcher.subscribe(callback); + await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmountInBaseUnits); + })().catch(done); + }); + }); +}); |