diff options
author | Fabio Berger <me@fabioberger.com> | 2018-01-25 23:42:58 +0800 |
---|---|---|
committer | Fabio Berger <me@fabioberger.com> | 2018-01-25 23:42:58 +0800 |
commit | 71d68f975cd7bc089f0cbef4e5888a73eab4ee42 (patch) | |
tree | 9482602fc23d2baec3fff1fb97750ad45adc6eca /packages/0x.js/test | |
parent | ec3d8a034fe763d8255935985b1fb97aff6c177b (diff) | |
parent | f58f0ddb67555c3f0c7252ea3e003824984c48ad (diff) | |
download | dexon-sol-tools-71d68f975cd7bc089f0cbef4e5888a73eab4ee42.tar dexon-sol-tools-71d68f975cd7bc089f0cbef4e5888a73eab4ee42.tar.gz dexon-sol-tools-71d68f975cd7bc089f0cbef4e5888a73eab4ee42.tar.bz2 dexon-sol-tools-71d68f975cd7bc089f0cbef4e5888a73eab4ee42.tar.lz dexon-sol-tools-71d68f975cd7bc089f0cbef4e5888a73eab4ee42.tar.xz dexon-sol-tools-71d68f975cd7bc089f0cbef4e5888a73eab4ee42.tar.zst dexon-sol-tools-71d68f975cd7bc089f0cbef4e5888a73eab4ee42.zip |
Merge branch 'development' into feature/portal-ledger-support
* development: (437 commits)
Publish
Update yarn.lock
Update the CHANGELOG
Fix the bug making it impossible to specify the custom ZRX address
Fix fill/cancel order by looking for NoError instead of empty blockchainErr given the BlockchainErrs type refactor
Add a comment about a yarn bug
Add our mainnet and kovan nodes as backups for Portal requests
Fix bug hiding the user info from topBar
Add dev-utils package to top level README
Prettier newline
Prettier
Allow Token symbols to be alphanumeric
Update CHANGELOG, rebase on development
Should not -> cannot
Reject negative amounts in isValidBaseUnitAmount
Re-add changelog for 0x.js
Fix prettier
Update yarn.lock
Move tests to a separate folder
Change file layout
...
# Conflicts:
# packages/website/README.md
Diffstat (limited to 'packages/0x.js/test')
25 files changed, 1957 insertions, 1003 deletions
diff --git a/packages/0x.js/test/0x.js_test.ts b/packages/0x.js/test/0x.js_test.ts index 6b7a70699..5bc2c149c 100644 --- a/packages/0x.js/test/0x.js_test.ts +++ b/packages/0x.js/test/0x.js_test.ts @@ -1,18 +1,18 @@ -import BigNumber from 'bignumber.js'; +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import * as _ from 'lodash'; import 'mocha'; import * as Sinon from 'sinon'; -import {ApprovalContractEventArgs, LogWithDecodedArgs, Order, TokenEvents, ZeroEx, ZeroExError} from '../src'; +import { ApprovalContractEventArgs, LogWithDecodedArgs, Order, TokenEvents, ZeroEx } from '../src'; -import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; -import {chaiSetup} from './utils/chai_setup'; -import {constants} from './utils/constants'; -import {TokenUtils} from './utils/token_utils'; -import {web3Factory} from './utils/web3_factory'; +import { chaiSetup } from './utils/chai_setup'; +import { constants } from './utils/constants'; +import { TokenUtils } from './utils/token_utils'; +import { web3Factory } from './utils/web3_factory'; -const blockchainLifecycle = new BlockchainLifecycle(); +const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL); chaiSetup.configure(); const expect = chai.expect; @@ -41,11 +41,11 @@ describe('ZeroEx library', () => { // Check that all nested web3 wrapper instances return the updated provider const nestedWeb3WrapperProvider = (zeroEx as any)._web3Wrapper.getCurrentProvider(); - expect((nestedWeb3WrapperProvider).zeroExTestId).to.be.a('number'); + expect(nestedWeb3WrapperProvider.zeroExTestId).to.be.a('number'); const exchangeWeb3WrapperProvider = (zeroEx.exchange as any)._web3Wrapper.getCurrentProvider(); - expect((exchangeWeb3WrapperProvider).zeroExTestId).to.be.a('number'); + expect(exchangeWeb3WrapperProvider.zeroExTestId).to.be.a('number'); const tokenRegistryWeb3WrapperProvider = (zeroEx.tokenRegistry as any)._web3Wrapper.getCurrentProvider(); - expect((tokenRegistryWeb3WrapperProvider).zeroExTestId).to.be.a('number'); + expect(tokenRegistryWeb3WrapperProvider.zeroExTestId).to.be.a('number'); }); }); describe('#isValidSignature', () => { @@ -58,22 +58,25 @@ describe('ZeroEx library', () => { s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254', }; const address = '0x5409ed021d9299bf6814279a6a1411a7e866a631'; - it('should return false if the data doesn\'t pertain to the signature & address', async () => { + it("should return false if the data doesn't pertain to the signature & address", async () => { expect(ZeroEx.isValidSignature('0x0', signature, address)).to.be.false(); return expect( (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync('0x0', signature, address), ).to.become(false); }); - it('should return false if the address doesn\'t pertain to the signature & data', async () => { + it("should return false if the address doesn't pertain to the signature & data", async () => { const validUnrelatedAddress = '0x8b0292b11a196601ed2ce54b665cafeca0347d42'; expect(ZeroEx.isValidSignature(dataHex, signature, validUnrelatedAddress)).to.be.false(); return expect( - (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(dataHex, signature, - validUnrelatedAddress), + (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync( + dataHex, + signature, + validUnrelatedAddress, + ), ).to.become(false); }); - it('should return false if the signature doesn\'t pertain to the dataHex & address', async () => { - const wrongSignature = _.assign({}, signature, {v: 28}); + it("should return false if the signature doesn't pertain to the dataHex & address", async () => { + const wrongSignature = _.assign({}, signature, { v: 28 }); expect(ZeroEx.isValidSignature(dataHex, wrongSignature, address)).to.be.false(); return expect( (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(dataHex, wrongSignature, address), @@ -82,9 +85,9 @@ describe('ZeroEx library', () => { it('should return true if the signature does pertain to the dataHex & address', async () => { const isValidSignatureLocal = ZeroEx.isValidSignature(dataHex, signature, address); expect(isValidSignatureLocal).to.be.true(); - const isValidSignatureOnContract = await (zeroEx.exchange as any) - ._isValidSignatureUsingContractCallAsync(dataHex, signature, address); - return expect(isValidSignatureOnContract).to.be.true(); + return expect( + (zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(dataHex, signature, address), + ).to.become(true); }); }); describe('#generateSalt', () => { @@ -114,6 +117,13 @@ describe('ZeroEx library', () => { }); }); describe('#toUnitAmount', () => { + it('should throw if invalid baseUnit amount supplied as argument', () => { + const invalidBaseUnitAmount = new BigNumber(1000000000.4); + const decimals = 6; + expect(() => ZeroEx.toUnitAmount(invalidBaseUnitAmount, decimals)).to.throw( + 'amount should be in baseUnits (no decimals), found value: 1000000000.4', + ); + }); it('Should return the expected unit amount for the decimals passed in', () => { const baseUnitAmount = new BigNumber(1000000000); const decimals = 6; @@ -130,6 +140,13 @@ describe('ZeroEx library', () => { const expectedUnitAmount = new BigNumber(1000000000); expect(baseUnitAmount).to.be.bignumber.equal(expectedUnitAmount); }); + it('should throw if unitAmount has more decimals then specified as the max decimal precision', () => { + const unitAmount = new BigNumber(0.823091); + const decimals = 5; + expect(() => ZeroEx.toBaseUnitAmount(unitAmount, decimals)).to.throw( + 'Invalid unit amount: 0.823091 - Too many decimal places', + ); + }); }); describe('#getOrderHashHex', () => { const expectedOrderHash = '0x39da987067a3c9e5f1617694f1301326ba8c8b0498ebef5df4863bed394e3c83'; @@ -152,6 +169,15 @@ describe('ZeroEx library', () => { const orderHash = ZeroEx.getOrderHashHex(order); expect(orderHash).to.be.equal(expectedOrderHash); }); + it('throws a readable error message if taker format is invalid', async () => { + const orderWithInvalidtakerFormat = { + ...order, + taker: (null as any) as string, + }; + const expectedErrorMessage = + 'Order taker must be of type string. If you want anyone to be able to fill an order - pass ZeroEx.NULL_ADDRESS'; + expect(() => ZeroEx.getOrderHashHex(orderWithInvalidtakerFormat)).to.throw(expectedErrorMessage); + }); }); describe('#signOrderHashAsync', () => { let stubs: Sinon.SinonStub[] = []; @@ -177,16 +203,15 @@ describe('ZeroEx library', () => { }); it('should return the correct ECSignature for signatureHex concatenated as R + S + V', async () => { const orderHash = '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004'; - // tslint:disable-next-line: max-line-length - const signature = '0x22109d11d79cb8bf96ed88625e1cd9558800c4073332a9a02857499883ee5ce3050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb021b'; + const signature = + '0x22109d11d79cb8bf96ed88625e1cd9558800c4073332a9a02857499883ee5ce3050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb021b'; const expectedECSignature = { v: 27, r: '0x22109d11d79cb8bf96ed88625e1cd9558800c4073332a9a02857499883ee5ce3', s: '0x050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb02', }; stubs = [ - Sinon.stub((zeroEx as any)._web3Wrapper, 'signTransactionAsync') - .returns(Promise.resolve(signature)), + Sinon.stub((zeroEx as any)._web3Wrapper, 'signTransactionAsync').returns(Promise.resolve(signature)), Sinon.stub(ZeroEx, 'isValidSignature').returns(true), ]; @@ -195,16 +220,15 @@ describe('ZeroEx library', () => { }); it('should return the correct ECSignature for signatureHex concatenated as V + R + S', async () => { const orderHash = '0xc793e33ffded933b76f2f48d9aa3339fc090399d5e7f5dec8d3660f5480793f7'; - // tslint:disable-next-line: max-line-length - const signature = '0x1bc80bedc6756722672753413efdd749b5adbd4fd552595f59c13427407ee9aee02dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960'; + const signature = + '0x1bc80bedc6756722672753413efdd749b5adbd4fd552595f59c13427407ee9aee02dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960'; const expectedECSignature = { v: 27, r: '0xc80bedc6756722672753413efdd749b5adbd4fd552595f59c13427407ee9aee0', s: '0x2dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960', }; stubs = [ - Sinon.stub((zeroEx as any)._web3Wrapper, 'signTransactionAsync') - .returns(Promise.resolve(signature)), + Sinon.stub((zeroEx as any)._web3Wrapper, 'signTransactionAsync').returns(Promise.resolve(signature)), Sinon.stub(ZeroEx, 'isValidSignature').returns(true), ]; @@ -244,22 +268,15 @@ describe('ZeroEx library', () => { const zeroExWithWrongExchangeAddress = new ZeroEx(web3.currentProvider, zeroExConfig); expect(zeroExWithWrongExchangeAddress.exchange.getContractAddress()).to.be.equal(ZeroEx.NULL_ADDRESS); }); - it('allows to specify ether token contract address', async () => { - const zeroExConfig = { - etherTokenContractAddress: ZeroEx.NULL_ADDRESS, - networkId: constants.TESTRPC_NETWORK_ID, - }; - const zeroExWithWrongEtherTokenAddress = new ZeroEx(web3.currentProvider, zeroExConfig); - expect(zeroExWithWrongEtherTokenAddress.etherToken.getContractAddress()).to.be.equal(ZeroEx.NULL_ADDRESS); - }); it('allows to specify token registry token contract address', async () => { const zeroExConfig = { tokenRegistryContractAddress: ZeroEx.NULL_ADDRESS, networkId: constants.TESTRPC_NETWORK_ID, }; const zeroExWithWrongTokenRegistryAddress = new ZeroEx(web3.currentProvider, zeroExConfig); - expect(zeroExWithWrongTokenRegistryAddress.tokenRegistry.getContractAddress()) - .to.be.equal(ZeroEx.NULL_ADDRESS); + expect(zeroExWithWrongTokenRegistryAddress.tokenRegistry.getContractAddress()).to.be.equal( + ZeroEx.NULL_ADDRESS, + ); }); }); }); diff --git a/packages/0x.js/test/artifacts_test.ts b/packages/0x.js/test/artifacts_test.ts index f4599a24d..e8ab9aa97 100644 --- a/packages/0x.js/test/artifacts_test.ts +++ b/packages/0x.js/test/artifacts_test.ts @@ -1,14 +1,12 @@ -import * as chai from 'chai'; import * as fs from 'fs'; import HDWalletProvider = require('truffle-hdwallet-provider'); -import {ZeroEx} from '../src'; +import { ZeroEx } from '../src'; -import {chaiSetup} from './utils/chai_setup'; -import {constants} from './utils/constants'; +import { chaiSetup } from './utils/chai_setup'; +import { constants } from './utils/constants'; chaiSetup.configure(); -const expect = chai.expect; // Those tests are slower cause they're talking to a remote node const TIMEOUT = 10000; diff --git a/packages/0x.js/test/assert_test.ts b/packages/0x.js/test/assert_test.ts index 9fa7f780f..1f2820070 100644 --- a/packages/0x.js/test/assert_test.ts +++ b/packages/0x.js/test/assert_test.ts @@ -1,11 +1,11 @@ import * as chai from 'chai'; import 'mocha'; -import {ZeroEx} from '../src'; -import {assert} from '../src/utils/assert'; +import { ZeroEx } from '../src'; +import { assert } from '../src/utils/assert'; -import {constants} from './utils/constants'; -import {web3Factory} from './utils/web3_factory'; +import { constants } from './utils/constants'; +import { web3Factory } from './utils/web3_factory'; const expect = chai.expect; @@ -19,22 +19,25 @@ describe('Assertion library', () => { it('throws when address is invalid', async () => { const address = '0xdeadbeef'; const varName = 'address'; - return expect(assert.isSenderAddressAsync(varName, address, (zeroEx as any)._web3Wrapper)) - .to.be.rejectedWith(`Expected ${varName} to be of type ETHAddressHex, encountered: ${address}`); + return expect( + assert.isSenderAddressAsync(varName, address, (zeroEx as any)._web3Wrapper), + ).to.be.rejectedWith(`Expected ${varName} to be of type ETHAddressHex, encountered: ${address}`); }); it('throws when address is unavailable', async () => { const validUnrelatedAddress = '0x8b0292b11a196601eddce54b665cafeca0347d42'; const varName = 'address'; - return expect(assert.isSenderAddressAsync(varName, validUnrelatedAddress, (zeroEx as any)._web3Wrapper)) - .to.be.rejectedWith( - `Specified ${varName} ${validUnrelatedAddress} isn't available through the supplied web3 provider`, - ); + return expect( + assert.isSenderAddressAsync(varName, validUnrelatedAddress, (zeroEx as any)._web3Wrapper), + ).to.be.rejectedWith( + `Specified ${varName} ${validUnrelatedAddress} isn't available through the supplied web3 provider`, + ); }); - it('doesn\'t throw if address is available', async () => { + it("doesn't throw if address is available", async () => { const availableAddress = (await zeroEx.getAvailableAddressesAsync())[0]; const varName = 'address'; - return expect(assert.isSenderAddressAsync(varName, availableAddress, (zeroEx as any)._web3Wrapper)) - .to.become(undefined); + return expect( + assert.isSenderAddressAsync(varName, availableAddress, (zeroEx as any)._web3Wrapper), + ).to.become(undefined); }); }); }); diff --git a/packages/0x.js/test/ether_token_wrapper_test.ts b/packages/0x.js/test/ether_token_wrapper_test.ts index d3e4439ee..b810fc9f1 100644 --- a/packages/0x.js/test/ether_token_wrapper_test.ts +++ b/packages/0x.js/test/ether_token_wrapper_test.ts @@ -1,18 +1,34 @@ -import BigNumber from 'bignumber.js'; +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import 'mocha'; import * as Web3 from 'web3'; -import {ZeroEx, ZeroExError} from '../src'; +import { + ApprovalContractEventArgs, + BlockParamLiteral, + BlockRange, + DecodedLogEvent, + DepositContractEventArgs, + EtherTokenEvents, + Token, + TransferContractEventArgs, + WithdrawalContractEventArgs, + ZeroEx, + ZeroExError, +} from '../src'; +import { artifacts } from '../src/artifacts'; +import { DoneCallback } from '../src/types'; -import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; -import {chaiSetup} from './utils/chai_setup'; -import {constants} from './utils/constants'; -import {web3Factory} from './utils/web3_factory'; +import { chaiSetup } from './utils/chai_setup'; +import { constants } from './utils/constants'; +import { reportNodeCallbackErrors } from './utils/report_callback_errors'; +import { TokenUtils } from './utils/token_utils'; +import { web3Factory } from './utils/web3_factory'; chaiSetup.configure(); const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(); +const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL); // Since the address depositing/withdrawing ETH/WETH also needs to pay gas costs for the transaction, // a small amount of ETH will be used to pay this gas cost. We therefore check that the difference between @@ -23,24 +39,32 @@ const MAX_REASONABLE_GAS_COST_IN_WEI = 62517; describe('EtherTokenWrapper', () => { let web3: Web3; let zeroEx: ZeroEx; + let tokens: Token[]; let userAddresses: string[]; let addressWithETH: string; let wethContractAddress: string; let depositWeiAmount: BigNumber; let decimalPlaces: number; + let addressWithoutFunds: string; const gasPrice = new BigNumber(1); const zeroExConfig = { gasPrice, networkId: constants.TESTRPC_NETWORK_ID, }; + const transferAmount = new BigNumber(42); + const allowanceAmount = new BigNumber(42); + const depositAmount = new BigNumber(42); + const withdrawalAmount = new BigNumber(42); before(async () => { web3 = web3Factory.create(); zeroEx = new ZeroEx(web3.currentProvider, zeroExConfig); + tokens = await zeroEx.tokenRegistry.getTokensAsync(); userAddresses = await zeroEx.getAvailableAddressesAsync(); addressWithETH = userAddresses[0]; - wethContractAddress = zeroEx.etherToken.getContractAddress(); + wethContractAddress = (zeroEx.etherToken as any)._getContractAddress(artifacts.EtherTokenArtifact); depositWeiAmount = (zeroEx as any)._web3Wrapper.toWei(new BigNumber(5)); decimalPlaces = 7; + addressWithoutFunds = userAddresses[1]; }); beforeEach(async () => { await blockchainLifecycle.startAsync(); @@ -55,7 +79,7 @@ describe('EtherTokenWrapper', () => { expect(preETHBalance).to.be.bignumber.gt(0); expect(preWETHBalance).to.be.bignumber.equal(0); - const txHash = await zeroEx.etherToken.depositAsync(depositWeiAmount, addressWithETH); + const txHash = await zeroEx.etherToken.depositAsync(wethContractAddress, depositWeiAmount, addressWithETH); await zeroEx.awaitTransactionMinedAsync(txHash); const postETHBalanceInWei = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH); @@ -73,7 +97,7 @@ describe('EtherTokenWrapper', () => { const overETHBalanceinWei = preETHBalance.add(extraETHBalance); return expect( - zeroEx.etherToken.depositAsync(overETHBalanceinWei, addressWithETH), + zeroEx.etherToken.depositAsync(wethContractAddress, overETHBalanceinWei, addressWithETH), ).to.be.rejectedWith(ZeroExError.InsufficientEthBalanceForDeposit); }); }); @@ -81,7 +105,7 @@ describe('EtherTokenWrapper', () => { it('should successfully withdraw ETH in return for Wrapped ETH tokens', async () => { const ETHBalanceInWei = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH); - await zeroEx.etherToken.depositAsync(depositWeiAmount, addressWithETH); + await zeroEx.etherToken.depositAsync(wethContractAddress, depositWeiAmount, addressWithETH); const expectedPreETHBalance = ETHBalanceInWei.minus(depositWeiAmount); const preETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH); @@ -90,7 +114,7 @@ describe('EtherTokenWrapper', () => { expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI); expect(preWETHBalance).to.be.bignumber.equal(depositWeiAmount); - const txHash = await zeroEx.etherToken.withdrawAsync(depositWeiAmount, addressWithETH); + const txHash = await zeroEx.etherToken.withdrawAsync(wethContractAddress, depositWeiAmount, addressWithETH); await zeroEx.awaitTransactionMinedAsync(txHash); const postETHBalance = await (zeroEx as any)._web3Wrapper.getBalanceInWeiAsync(addressWithETH); @@ -108,8 +132,244 @@ describe('EtherTokenWrapper', () => { const overWETHBalance = preWETHBalance.add(999999999); return expect( - zeroEx.etherToken.withdrawAsync(overWETHBalance, addressWithETH), + zeroEx.etherToken.withdrawAsync(wethContractAddress, overWETHBalance, addressWithETH), ).to.be.rejectedWith(ZeroExError.InsufficientWEthBalanceForWithdrawal); }); }); + describe('#subscribe', () => { + const indexFilterValues = {}; + let etherTokenAddress: string; + before(() => { + const tokenUtils = new TokenUtils(tokens); + const etherToken = tokenUtils.getWethTokenOrThrow(); + etherTokenAddress = etherToken.address; + }); + afterEach(() => { + zeroEx.etherToken.unsubscribeAll(); + }); + // Hack: Mocha does not allow a test to be both async and have a `done` callback + // Since we need to await the receipt of the event in the `subscribe` callback, + // we do need both. A hack is to make the top-level async fn w/ a done callback and then + // wrap the rest of the test in an async block + // Source: https://github.com/mochajs/mocha/issues/2407 + it('Should receive the Transfer event when tokens are transfered', (done: DoneCallback) => { + (async () => { + const callback = reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<TransferContractEventArgs>) => { + expect(logEvent).to.not.be.undefined(); + expect(logEvent.isRemoved).to.be.false(); + expect(logEvent.log.logIndex).to.be.equal(0); + expect(logEvent.log.transactionIndex).to.be.equal(0); + expect(logEvent.log.blockNumber).to.be.a('number'); + const args = logEvent.log.args; + expect(args._from).to.be.equal(addressWithETH); + expect(args._to).to.be.equal(addressWithoutFunds); + expect(args._value).to.be.bignumber.equal(transferAmount); + }, + ); + await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH); + zeroEx.etherToken.subscribe(etherTokenAddress, EtherTokenEvents.Transfer, indexFilterValues, callback); + await zeroEx.token.transferAsync( + etherTokenAddress, + addressWithETH, + addressWithoutFunds, + transferAmount, + ); + })().catch(done); + }); + it('Should receive the Approval event when allowance is being set', (done: DoneCallback) => { + (async () => { + const callback = reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { + expect(logEvent).to.not.be.undefined(); + expect(logEvent.isRemoved).to.be.false(); + const args = logEvent.log.args; + expect(args._owner).to.be.equal(addressWithETH); + expect(args._spender).to.be.equal(addressWithoutFunds); + expect(args._value).to.be.bignumber.equal(allowanceAmount); + }, + ); + zeroEx.etherToken.subscribe(etherTokenAddress, EtherTokenEvents.Approval, indexFilterValues, callback); + await zeroEx.token.setAllowanceAsync( + etherTokenAddress, + addressWithETH, + addressWithoutFunds, + allowanceAmount, + ); + })().catch(done); + }); + it('Should receive the Deposit event when ether is being deposited', (done: DoneCallback) => { + (async () => { + const callback = reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<DepositContractEventArgs>) => { + expect(logEvent).to.not.be.undefined(); + expect(logEvent.isRemoved).to.be.false(); + const args = logEvent.log.args; + expect(args._owner).to.be.equal(addressWithETH); + expect(args._value).to.be.bignumber.equal(depositAmount); + }, + ); + zeroEx.etherToken.subscribe(etherTokenAddress, EtherTokenEvents.Deposit, indexFilterValues, callback); + await zeroEx.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH); + })().catch(done); + }); + it('Should receive the Withdrawal event when ether is being withdrawn', (done: DoneCallback) => { + (async () => { + const callback = reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<WithdrawalContractEventArgs>) => { + expect(logEvent).to.not.be.undefined(); + expect(logEvent.isRemoved).to.be.false(); + const args = logEvent.log.args; + expect(args._owner).to.be.equal(addressWithETH); + expect(args._value).to.be.bignumber.equal(depositAmount); + }, + ); + await zeroEx.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH); + zeroEx.etherToken.subscribe( + etherTokenAddress, + EtherTokenEvents.Withdrawal, + indexFilterValues, + callback, + ); + await zeroEx.etherToken.withdrawAsync(etherTokenAddress, withdrawalAmount, addressWithETH); + })().catch(done); + }); + it('should cancel outstanding subscriptions when ZeroEx.setProvider is called', (done: DoneCallback) => { + (async () => { + const callbackNeverToBeCalled = reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { + done(new Error('Expected this subscription to have been cancelled')); + }, + ); + zeroEx.etherToken.subscribe( + etherTokenAddress, + EtherTokenEvents.Transfer, + indexFilterValues, + callbackNeverToBeCalled, + ); + const callbackToBeCalled = reportNodeCallbackErrors(done)(); + const newProvider = web3Factory.getRpcProvider(); + zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID); + await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH); + zeroEx.etherToken.subscribe( + etherTokenAddress, + EtherTokenEvents.Transfer, + indexFilterValues, + callbackToBeCalled, + ); + await zeroEx.token.transferAsync( + etherTokenAddress, + addressWithETH, + addressWithoutFunds, + transferAmount, + ); + })().catch(done); + }); + it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => { + (async () => { + const callbackNeverToBeCalled = reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { + done(new Error('Expected this subscription to have been cancelled')); + }, + ); + await zeroEx.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH); + const subscriptionToken = zeroEx.etherToken.subscribe( + etherTokenAddress, + EtherTokenEvents.Transfer, + indexFilterValues, + callbackNeverToBeCalled, + ); + zeroEx.etherToken.unsubscribe(subscriptionToken); + await zeroEx.token.transferAsync( + etherTokenAddress, + addressWithETH, + addressWithoutFunds, + transferAmount, + ); + done(); + })().catch(done); + }); + }); + describe('#getLogsAsync', () => { + let etherTokenAddress: string; + let tokenTransferProxyAddress: string; + const blockRange: BlockRange = { + fromBlock: 0, + toBlock: BlockParamLiteral.Latest, + }; + let txHash: string; + before(() => { + addressWithETH = userAddresses[0]; + const tokenUtils = new TokenUtils(tokens); + const etherToken = tokenUtils.getWethTokenOrThrow(); + etherTokenAddress = etherToken.address; + tokenTransferProxyAddress = zeroEx.proxy.getContractAddress(); + }); + it('should get logs with decoded args emitted by Approval', async () => { + txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH); + await zeroEx.awaitTransactionMinedAsync(txHash); + const eventName = EtherTokenEvents.Approval; + const indexFilterValues = {}; + const logs = await zeroEx.etherToken.getLogsAsync<ApprovalContractEventArgs>( + etherTokenAddress, + eventName, + blockRange, + indexFilterValues, + ); + expect(logs).to.have.length(1); + const args = logs[0].args; + expect(logs[0].event).to.be.equal(eventName); + expect(args._owner).to.be.equal(addressWithETH); + expect(args._spender).to.be.equal(tokenTransferProxyAddress); + expect(args._value).to.be.bignumber.equal(zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); + }); + it('should get logs with decoded args emitted by Deposit', async () => { + await zeroEx.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH); + const eventName = EtherTokenEvents.Deposit; + const indexFilterValues = {}; + const logs = await zeroEx.etherToken.getLogsAsync<DepositContractEventArgs>( + etherTokenAddress, + eventName, + blockRange, + indexFilterValues, + ); + expect(logs).to.have.length(1); + const args = logs[0].args; + expect(logs[0].event).to.be.equal(eventName); + expect(args._owner).to.be.equal(addressWithETH); + expect(args._value).to.be.bignumber.equal(depositAmount); + }); + it('should only get the logs with the correct event name', async () => { + txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH); + await zeroEx.awaitTransactionMinedAsync(txHash); + const differentEventName = EtherTokenEvents.Transfer; + const indexFilterValues = {}; + const logs = await zeroEx.etherToken.getLogsAsync( + etherTokenAddress, + differentEventName, + blockRange, + indexFilterValues, + ); + expect(logs).to.have.length(0); + }); + it('should only get the logs with the correct indexed fields', async () => { + txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH); + await zeroEx.awaitTransactionMinedAsync(txHash); + txHash = await zeroEx.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithoutFunds); + await zeroEx.awaitTransactionMinedAsync(txHash); + const eventName = EtherTokenEvents.Approval; + const indexFilterValues = { + _owner: addressWithETH, + }; + const logs = await zeroEx.etherToken.getLogsAsync<ApprovalContractEventArgs>( + etherTokenAddress, + eventName, + blockRange, + indexFilterValues, + ); + expect(logs).to.have.length(1); + const args = logs[0].args; + expect(args._owner).to.be.equal(addressWithETH); + }); + }); }); diff --git a/packages/0x.js/test/event_watcher_test.ts b/packages/0x.js/test/event_watcher_test.ts index 3d92c62a3..f92fb2b02 100644 --- a/packages/0x.js/test/event_watcher_test.ts +++ b/packages/0x.js/test/event_watcher_test.ts @@ -1,22 +1,17 @@ -import {Web3Wrapper} from '@0xproject/web3-wrapper'; -import BigNumber from 'bignumber.js'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; import * as _ from 'lodash'; import 'mocha'; import * as Sinon from 'sinon'; import * as Web3 from 'web3'; -import { - DecodedLogEvent, - LogEvent, - ZeroEx, -} from '../src'; -import {EventWatcher} from '../src/order_watcher/event_watcher'; -import {DoneCallback} from '../src/types'; +import { LogEvent } from '../src'; +import { EventWatcher } from '../src/order_watcher/event_watcher'; +import { DoneCallback } from '../src/types'; -import {chaiSetup} from './utils/chai_setup'; -import {constants} from './utils/constants'; -import {web3Factory} from './utils/web3_factory'; +import { chaiSetup } from './utils/chai_setup'; +import { reportNodeCallbackErrors } from './utils/report_callback_errors'; +import { web3Factory } from './utils/web3_factory'; chaiSetup.configure(); const expect = chai.expect; @@ -26,7 +21,6 @@ describe('EventWatcher', () => { let stubs: Sinon.SinonStub[] = []; let eventWatcher: EventWatcher; let web3Wrapper: Web3Wrapper; - const numConfirmations = 0; const logA: Web3.LogEntry = { address: '0x71d271f8b14adef568f8f28f1587ce7271ac4ca5', blockHash: null, @@ -43,7 +37,7 @@ describe('EventWatcher', () => { blockNumber: null, data: '', logIndex: null, - topics: [ '0xf341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567' ], + topics: ['0xf341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567'], transactionHash: '0x01ef3c048b18d9b09ea195b4ed94cf8dd5f3d857a1905ff886b152cfb1166f25', transactionIndex: 0, }; @@ -53,7 +47,7 @@ describe('EventWatcher', () => { blockNumber: null, data: '', logIndex: null, - topics: [ '0xf341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567' ], + topics: ['0xf341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567'], transactionHash: '0x01ef3c048b18d9b09ea195b4ed94cf8dd5f3d857a1905ff886b152cfb1166f25', transactionIndex: 0, }; @@ -84,13 +78,14 @@ describe('EventWatcher', () => { const getLogsStub = Sinon.stub(web3Wrapper, 'getLogsAsync'); getLogsStub.onCall(0).returns(logs); stubs.push(getLogsStub); - const callback = (event: LogEvent) => { + const expectedToBeCalledOnce = false; + const callback = reportNodeCallbackErrors(done, expectedToBeCalledOnce)((event: LogEvent) => { const expectedLogEvent = expectedLogEvents.shift(); expect(event).to.be.deep.equal(expectedLogEvent); if (_.isEmpty(expectedLogEvents)) { done(); } - }; + }); eventWatcher.subscribe(callback); }); it('correctly computes the difference and emits only changes', (done: DoneCallback) => { @@ -118,13 +113,14 @@ describe('EventWatcher', () => { getLogsStub.onCall(0).returns(initialLogs); getLogsStub.onCall(1).returns(changedLogs); stubs.push(getLogsStub); - const callback = (event: LogEvent) => { + const expectedToBeCalledOnce = false; + const callback = reportNodeCallbackErrors(done, expectedToBeCalledOnce)((event: LogEvent) => { const expectedLogEvent = expectedLogEvents.shift(); expect(event).to.be.deep.equal(expectedLogEvent); if (_.isEmpty(expectedLogEvents)) { done(); } - }; + }); eventWatcher.subscribe(callback); }); }); diff --git a/packages/0x.js/test/exchange_transfer_simulator_test.ts b/packages/0x.js/test/exchange_transfer_simulator_test.ts index a1d9bdade..20b4a05ca 100644 --- a/packages/0x.js/test/exchange_transfer_simulator_test.ts +++ b/packages/0x.js/test/exchange_transfer_simulator_test.ts @@ -1,18 +1,18 @@ -import BigNumber from 'bignumber.js'; +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; -import {ExchangeContractErrs, Token, ZeroEx} from '../src'; -import {BlockParamLiteral, TradeSide, TransferType} from '../src/types'; -import {ExchangeTransferSimulator} from '../src/utils/exchange_transfer_simulator'; +import { ExchangeContractErrs, Token, ZeroEx } from '../src'; +import { BlockParamLiteral, TradeSide, TransferType } from '../src/types'; +import { ExchangeTransferSimulator } from '../src/utils/exchange_transfer_simulator'; -import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; -import {chaiSetup} from './utils/chai_setup'; -import {constants} from './utils/constants'; -import {web3Factory} from './utils/web3_factory'; +import { chaiSetup } from './utils/chai_setup'; +import { constants } from './utils/constants'; +import { web3Factory } from './utils/web3_factory'; chaiSetup.configure(); const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(); +const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL); describe('ExchangeTransferSimulator', () => { const web3 = web3Factory.create(); @@ -45,17 +45,31 @@ describe('ExchangeTransferSimulator', () => { beforeEach(() => { exchangeTransferSimulator = new ExchangeTransferSimulator(zeroEx.token, BlockParamLiteral.Latest); }); - it('throws if the user doesn\'t have enough allowance', async () => { - return expect(exchangeTransferSimulator.transferFromAsync( - exampleTokenAddress, sender, recipient, transferAmount, TradeSide.Taker, TransferType.Trade, - )).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance); + it("throws if the user doesn't have enough allowance", async () => { + return expect( + exchangeTransferSimulator.transferFromAsync( + exampleTokenAddress, + sender, + recipient, + transferAmount, + TradeSide.Taker, + TransferType.Trade, + ), + ).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance); }); - it('throws if the user doesn\'t have enough balance', async () => { + it("throws if the user doesn't have enough balance", async () => { txHash = await zeroEx.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount); await zeroEx.awaitTransactionMinedAsync(txHash); - return expect(exchangeTransferSimulator.transferFromAsync( - exampleTokenAddress, sender, recipient, transferAmount, TradeSide.Maker, TransferType.Trade, - )).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance); + return expect( + exchangeTransferSimulator.transferFromAsync( + exampleTokenAddress, + sender, + recipient, + transferAmount, + TradeSide.Maker, + TransferType.Trade, + ), + ).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance); }); it('updates balances and proxyAllowance after transfer', async () => { txHash = await zeroEx.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount); @@ -63,9 +77,14 @@ describe('ExchangeTransferSimulator', () => { txHash = await zeroEx.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount); await zeroEx.awaitTransactionMinedAsync(txHash); await exchangeTransferSimulator.transferFromAsync( - exampleTokenAddress, sender, recipient, transferAmount, TradeSide.Taker, TransferType.Trade, + exampleTokenAddress, + sender, + recipient, + transferAmount, + TradeSide.Taker, + TransferType.Trade, ); - const store = (exchangeTransferSimulator as any).store; + const store = (exchangeTransferSimulator as any)._store; const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender); const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient); const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender); @@ -73,15 +92,20 @@ describe('ExchangeTransferSimulator', () => { expect(recipientBalance).to.be.bignumber.equal(transferAmount); expect(senderProxyAllowance).to.be.bignumber.equal(0); }); - it('doesn\'t update proxyAllowance after transfer if unlimited', async () => { + 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, + exampleTokenAddress, + sender, + recipient, + transferAmount, + TradeSide.Taker, + TransferType.Trade, ); - const store = (exchangeTransferSimulator as any).store; + const store = (exchangeTransferSimulator as any)._store; const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender); const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient); const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender); diff --git a/packages/0x.js/test/exchange_wrapper_test.ts b/packages/0x.js/test/exchange_wrapper_test.ts index 14559c706..7e0ffd818 100644 --- a/packages/0x.js/test/exchange_wrapper_test.ts +++ b/packages/0x.js/test/exchange_wrapper_test.ts @@ -1,34 +1,35 @@ -import BigNumber from 'bignumber.js'; +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; +import * as _ from 'lodash'; import 'mocha'; import * as Web3 from 'web3'; import { + BlockRange, DecodedLogEvent, ExchangeContractErrs, ExchangeEvents, LogCancelContractEventArgs, - LogEvent, LogFillContractEventArgs, OrderCancellationRequest, OrderFillRequest, SignedOrder, - SubscriptionOpts, Token, ZeroEx, } from '../src'; -import {BlockParamLiteral, DoneCallback} from '../src/types'; +import { BlockParamLiteral, DoneCallback } from '../src/types'; -import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; -import {chaiSetup} from './utils/chai_setup'; -import {constants} from './utils/constants'; -import {FillScenarios} from './utils/fill_scenarios'; -import {TokenUtils} from './utils/token_utils'; -import {web3Factory} from './utils/web3_factory'; +import { chaiSetup } from './utils/chai_setup'; +import { constants } from './utils/constants'; +import { FillScenarios } from './utils/fill_scenarios'; +import { reportNodeCallbackErrors } from './utils/report_callback_errors'; +import { TokenUtils } from './utils/token_utils'; +import { web3Factory } from './utils/web3_factory'; chaiSetup.configure(); const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(); +const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL); const NON_EXISTENT_ORDER_HASH = '0x79370342234e7acd6bbeac335bd3bb1d368383294b64b8160a00f4060e4d3777'; @@ -53,6 +54,7 @@ describe('ExchangeWrapper', () => { tokenUtils = new TokenUtils(tokens); zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address; fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress); + await fillScenarios.initTokenBalancesAsync(); }); beforeEach(async () => { await blockchainLifecycle.startAsync(); @@ -71,7 +73,7 @@ describe('ExchangeWrapper', () => { before(async () => { [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses; tokens = await zeroEx.tokenRegistry.getTokensAsync(); - const [makerToken, takerToken] = tokenUtils.getNonProtocolTokens(); + const [makerToken, takerToken] = tokenUtils.getDummyTokens(); makerTokenAddress = makerToken.address; takerTokenAddress = takerToken.address; }); @@ -80,10 +82,18 @@ describe('ExchangeWrapper', () => { const fillableAmount = new BigNumber(5); const partialFillTakerAmount = new BigNumber(2); const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); const orderFillRequests = [ { @@ -103,7 +113,11 @@ describe('ExchangeWrapper', () => { const fillableAmount = new BigNumber(5); beforeEach(async () => { signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); orderFillRequests = [ { @@ -113,18 +127,23 @@ describe('ExchangeWrapper', () => { ]; }); it('should validate when orderTransactionOptions are not present', async () => { - return expect(zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress)) - .to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + return expect( + zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress), + ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); it('should validate when orderTransactionOptions specify to validate', async () => { - return expect(zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, { - shouldValidate: true, - })).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + return expect( + zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, { + shouldValidate: true, + }), + ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect(zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, { - shouldValidate: false, - })).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + return expect( + zeroEx.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, { + shouldValidate: false, + }), + ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); }); }); @@ -133,57 +152,78 @@ describe('ExchangeWrapper', () => { const fillableAmount = new BigNumber(5); beforeEach(async () => { signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); }); describe('successful fills', () => { it('should fill a valid order', async () => { - 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); + 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.fillOrKillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress); - expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)) - .to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount)); - expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)) - .to.be.bignumber.equal(takerTokenFillAmount); - expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)) - .to.be.bignumber.equal(takerTokenFillAmount); - expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)) - .to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount)); + expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal( + fillableAmount.minus(takerTokenFillAmount), + ); + expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal( + takerTokenFillAmount, + ); + expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal( + takerTokenFillAmount, + ); + expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal( + fillableAmount.minus(takerTokenFillAmount), + ); }); it('should partially fill a valid order', async () => { const partialFillAmount = new BigNumber(3); await zeroEx.exchange.fillOrKillOrderAsync(signedOrder, partialFillAmount, 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)); + 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('order transaction options', () => { const emptyFillableAmount = new BigNumber(0); it('should validate when orderTransactionOptions are not present', async () => { - return expect(zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress)) - .to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + return expect( + zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress), + ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); it('should validate when orderTransactionOptions specify to validate', async () => { - return expect(zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, { - shouldValidate: true, - })).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + return expect( + zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, { + shouldValidate: true, + }), + ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect(zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, { - shouldValidate: false, - })).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + return expect( + zeroEx.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, { + shouldValidate: false, + }), + ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); }); }); @@ -201,7 +241,7 @@ describe('ExchangeWrapper', () => { before(async () => { [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses; tokens = await zeroEx.tokenRegistry.getTokensAsync(); - const [makerToken, takerToken] = tokenUtils.getNonProtocolTokens(); + const [makerToken, takerToken] = tokenUtils.getDummyTokens(); makerTokenAddress = makerToken.address; takerTokenAddress = takerToken.address; }); @@ -209,57 +249,96 @@ describe('ExchangeWrapper', () => { 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); + 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, + ); const txHash = await zeroEx.exchange.fillOrderAsync( - signedOrder, takerTokenFillAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress); + signedOrder, + takerTokenFillAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + ); await zeroEx.awaitTransactionMinedAsync(txHash); - expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)) - .to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount)); - expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)) - .to.be.bignumber.equal(takerTokenFillAmount); - expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)) - .to.be.bignumber.equal(takerTokenFillAmount); - expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)) - .to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount)); + expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress)).to.be.bignumber.equal( + fillableAmount.minus(takerTokenFillAmount), + ); + expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress)).to.be.bignumber.equal( + takerTokenFillAmount, + ); + expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress)).to.be.bignumber.equal( + takerTokenFillAmount, + ); + expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress)).to.be.bignumber.equal( + fillableAmount.minus(takerTokenFillAmount), + ); }); it('should partially fill the valid order', async () => { const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); const partialFillAmount = new BigNumber(3); const txHash = await zeroEx.exchange.fillOrderAsync( - signedOrder, partialFillAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress); + signedOrder, + partialFillAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + ); await zeroEx.awaitTransactionMinedAsync(txHash); - 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)); + 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 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, + makerTokenAddress, + takerTokenAddress, + makerFee, + takerFee, + makerAddress, + takerAddress, + fillableAmount, + feeRecipient, ); const txHash = await zeroEx.exchange.fillOrderAsync( - signedOrder, takerTokenFillAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress); + signedOrder, + takerTokenFillAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + ); await zeroEx.awaitTransactionMinedAsync(txHash); - expect(await zeroEx.token.getBalanceAsync(zrxTokenAddress, feeRecipient)) - .to.be.bignumber.equal(makerFee.plus(takerFee)); + expect(await zeroEx.token.getBalanceAsync(zrxTokenAddress, feeRecipient)).to.be.bignumber.equal( + makerFee.plus(takerFee), + ); }); }); describe('order transaction options', () => { @@ -267,25 +346,71 @@ describe('ExchangeWrapper', () => { const emptyFillTakerAmount = new BigNumber(0); beforeEach(async () => { signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); }); it('should validate when orderTransactionOptions are not present', async () => { - return expect(zeroEx.exchange.fillOrderAsync( - signedOrder, emptyFillTakerAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + return expect( + zeroEx.exchange.fillOrderAsync( + signedOrder, + emptyFillTakerAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + ), + ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); it('should validate when orderTransactionOptions specify to validate', async () => { - return expect(zeroEx.exchange.fillOrderAsync( - signedOrder, emptyFillTakerAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { - shouldValidate: true, - })).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + return expect( + zeroEx.exchange.fillOrderAsync( + signedOrder, + emptyFillTakerAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + { + shouldValidate: true, + }, + ), + ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect(zeroEx.exchange.fillOrderAsync( - signedOrder, emptyFillTakerAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { - shouldValidate: false, - })).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + return expect( + zeroEx.exchange.fillOrderAsync( + signedOrder, + emptyFillTakerAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + { + shouldValidate: false, + }, + ), + ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + }); + }); + describe('negative fill amount', async () => { + let signedOrder: SignedOrder; + const negativeFillTakerAmount = new BigNumber(-100); + beforeEach(async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, + ); + }); + it('should not allow the exchange wrapper to fill if amount is negative', async () => { + return expect( + zeroEx.exchange.fillOrderAsync( + signedOrder, + negativeFillTakerAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + ), + ).to.be.rejected(); }); }); }); @@ -297,11 +422,19 @@ describe('ExchangeWrapper', () => { let orderFillBatch: OrderFillRequest[]; beforeEach(async () => { signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); signedOrderHashHex = ZeroEx.getOrderHashHex(signedOrder); anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder); }); @@ -319,13 +452,20 @@ describe('ExchangeWrapper', () => { ]; }); it('should throw if a batch is empty', async () => { - return expect(zeroEx.exchange.batchFillOrdersAsync( - [], shouldThrowOnInsufficientBalanceOrAllowance, takerAddress), + return expect( + zeroEx.exchange.batchFillOrdersAsync( + [], + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + ), ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); }); it('should successfully fill multiple orders', async () => { const txHash = await zeroEx.exchange.batchFillOrdersAsync( - orderFillBatch, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress); + orderFillBatch, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + ); await zeroEx.awaitTransactionMinedAsync(txHash); const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex); const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex); @@ -348,21 +488,61 @@ describe('ExchangeWrapper', () => { ]; }); it('should validate when orderTransactionOptions are not present', async () => { - return expect(zeroEx.exchange.batchFillOrdersAsync( - orderFillBatch, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress), + return expect( + zeroEx.exchange.batchFillOrdersAsync( + orderFillBatch, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + ), ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); it('should validate when orderTransactionOptions specify to validate', async () => { - return expect(zeroEx.exchange.batchFillOrdersAsync( - orderFillBatch, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { - shouldValidate: true, - })).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + return expect( + zeroEx.exchange.batchFillOrdersAsync( + orderFillBatch, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + { + shouldValidate: true, + }, + ), + ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect(zeroEx.exchange.batchFillOrdersAsync( - orderFillBatch, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { - shouldValidate: false, - })).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + return expect( + zeroEx.exchange.batchFillOrdersAsync( + orderFillBatch, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + { + shouldValidate: false, + }, + ), + ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + }); + }); + describe('negative batch fill amount', async () => { + beforeEach(async () => { + const negativeFillTakerAmount = new BigNumber(-100); + orderFillBatch = [ + { + signedOrder, + takerTokenFillAmount, + }, + { + signedOrder: anotherSignedOrder, + takerTokenFillAmount: negativeFillTakerAmount, + }, + ]; + }); + it('should not allow the exchange wrapper to batch fill if any amount is negative', async () => { + return expect( + zeroEx.exchange.batchFillOrdersAsync( + orderFillBatch, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + ), + ).to.be.rejected(); }); }); }); @@ -375,24 +555,57 @@ describe('ExchangeWrapper', () => { const fillUpToAmount = fillableAmount.plus(fillableAmount).minus(1); beforeEach(async () => { signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); signedOrderHashHex = ZeroEx.getOrderHashHex(signedOrder); anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder); signedOrders = [signedOrder, anotherSignedOrder]; }); describe('successful batch fills', () => { it('should throw if a batch is empty', async () => { - return expect(zeroEx.exchange.fillOrdersUpToAsync( - [], fillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress), + return expect( + zeroEx.exchange.fillOrdersUpToAsync( + [], + fillUpToAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + ), ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); }); - it('should successfully fill up to specified amount', async () => { + it('should successfully fill up to specified amount when all orders are fully funded', async () => { + const txHash = await zeroEx.exchange.fillOrdersUpToAsync( + signedOrders, + fillUpToAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + ); + await zeroEx.awaitTransactionMinedAsync(txHash); + const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex); + const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex); + expect(filledAmount).to.be.bignumber.equal(fillableAmount); + const remainingFillAmount = fillableAmount.minus(1); + expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount); + }); + it('should successfully fill up to specified amount even if filling all orders would fail', async () => { + const missingBalance = new BigNumber(1); // User will still have enough balance to fill up to 9, + // but won't have 10 to fully fill all orders in a batch. + await zeroEx.token.transferAsync(makerTokenAddress, makerAddress, coinbase, missingBalance); const txHash = await zeroEx.exchange.fillOrdersUpToAsync( - signedOrders, fillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, + signedOrders, + fillUpToAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, ); await zeroEx.awaitTransactionMinedAsync(txHash); const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex); @@ -402,24 +615,57 @@ describe('ExchangeWrapper', () => { expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount); }); }); + describe('failed batch fills', () => { + it("should fail validation if user doesn't have enough balance without fill up to", async () => { + const missingBalance = new BigNumber(2); // User will only have enough balance to fill up to 8 + await zeroEx.token.transferAsync(makerTokenAddress, makerAddress, coinbase, missingBalance); + return expect( + zeroEx.exchange.fillOrdersUpToAsync( + signedOrders, + fillUpToAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + ), + ).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance); + }); + }); describe('order transaction options', () => { const emptyFillUpToAmount = new BigNumber(0); it('should validate when orderTransactionOptions are not present', async () => { - return expect(zeroEx.exchange.fillOrdersUpToAsync( - signedOrders, emptyFillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + return expect( + zeroEx.exchange.fillOrdersUpToAsync( + signedOrders, + emptyFillUpToAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + ), + ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); it('should validate when orderTransactionOptions specify to validate', async () => { - return expect(zeroEx.exchange.fillOrdersUpToAsync( - signedOrders, emptyFillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { - shouldValidate: true, - })).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + return expect( + zeroEx.exchange.fillOrdersUpToAsync( + signedOrders, + emptyFillUpToAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + { + shouldValidate: true, + }, + ), + ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect(zeroEx.exchange.fillOrdersUpToAsync( - signedOrders, emptyFillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, { - shouldValidate: false, - })).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + return expect( + zeroEx.exchange.fillOrdersUpToAsync( + signedOrders, + emptyFillUpToAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, + { + shouldValidate: false, + }, + ), + ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); }); }); @@ -436,11 +682,15 @@ describe('ExchangeWrapper', () => { const cancelAmount = new BigNumber(3); beforeEach(async () => { [coinbase, makerAddress, takerAddress] = userAddresses; - const [makerToken, takerToken] = tokenUtils.getNonProtocolTokens(); + const [makerToken, takerToken] = tokenUtils.getDummyTokens(); makerTokenAddress = makerToken.address; takerTokenAddress = takerToken.address; signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); orderHashHex = ZeroEx.getOrderHashHex(signedOrder); }); @@ -456,18 +706,23 @@ describe('ExchangeWrapper', () => { describe('order transaction options', () => { const emptyCancelTakerTokenAmount = new BigNumber(0); it('should validate when orderTransactionOptions are not present', async () => { - return expect(zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount)) - .to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); + return expect( + zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount), + ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); }); it('should validate when orderTransactionOptions specify to validate', async () => { - return expect(zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, { - shouldValidate: true, - })).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); + return expect( + zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, { + shouldValidate: true, + }), + ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); }); it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect(zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, { - shouldValidate: false, - })).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); + return expect( + zeroEx.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, { + shouldValidate: false, + }), + ).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); }); }); }); @@ -477,7 +732,11 @@ describe('ExchangeWrapper', () => { let cancelBatch: OrderCancellationRequest[]; beforeEach(async () => { anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); anotherOrderHashHex = ZeroEx.getOrderHashHex(anotherSignedOrder); cancelBatch = [ @@ -494,15 +753,21 @@ describe('ExchangeWrapper', () => { describe('failed batch cancels', () => { it('should throw when orders have different makers', async () => { const signedOrderWithDifferentMaker = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, takerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + takerAddress, + takerAddress, + fillableAmount, ); - return expect(zeroEx.exchange.batchCancelOrdersAsync([ - cancelBatch[0], - { - order: signedOrderWithDifferentMaker, - takerTokenCancelAmount: cancelAmount, - }, - ])).to.be.rejectedWith(ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed); + return expect( + zeroEx.exchange.batchCancelOrdersAsync([ + cancelBatch[0], + { + order: signedOrderWithDifferentMaker, + takerTokenCancelAmount: cancelAmount, + }, + ]), + ).to.be.rejectedWith(ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed); }); }); describe('successful batch cancels', () => { @@ -531,18 +796,23 @@ describe('ExchangeWrapper', () => { ]; }); it('should validate when orderTransactionOptions are not present', async () => { - return expect(zeroEx.exchange.batchCancelOrdersAsync(cancelBatch)) - .to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); + return expect(zeroEx.exchange.batchCancelOrdersAsync(cancelBatch)).to.be.rejectedWith( + ExchangeContractErrs.OrderCancelAmountZero, + ); }); it('should validate when orderTransactionOptions specify to validate', async () => { - return expect(zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, { - shouldValidate: true, - })).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); + return expect( + zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, { + shouldValidate: true, + }), + ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); }); it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect(zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, { - shouldValidate: false, - })).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); + return expect( + zeroEx.exchange.batchCancelOrdersAsync(cancelBatch, { + shouldValidate: false, + }), + ).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); }); }); }); @@ -557,7 +827,8 @@ describe('ExchangeWrapper', () => { let orderHash: string; before(() => { takerAddress = userAddresses[1]; - const [makerToken, takerToken] = tokens; + tokenUtils = new TokenUtils(tokens); + const [makerToken, takerToken] = tokenUtils.getDummyTokens(); makerTokenAddress = makerToken.address; takerTokenAddress = takerToken.address; }); @@ -565,7 +836,11 @@ describe('ExchangeWrapper', () => { fillableAmount = new BigNumber(5); partialFillAmount = new BigNumber(2); signedOrder = await fillScenarios.createPartiallyFilledSignedOrderAsync( - makerTokenAddress, takerTokenAddress, takerAddress, fillableAmount, partialFillAmount, + makerTokenAddress, + takerTokenAddress, + takerAddress, + fillableAmount, + partialFillAmount, ); orderHash = ZeroEx.getOrderHashHex(signedOrder); }); @@ -589,8 +864,7 @@ describe('ExchangeWrapper', () => { return expect(zeroEx.exchange.getFilledTakerAmountAsync(invalidOrderHashHex)).to.be.rejected(); }); it('should return zero if passed a valid but non-existent orderHash', async () => { - const filledValueT = await zeroEx.exchange.getFilledTakerAmountAsync(NON_EXISTENT_ORDER_HASH, - ); + const filledValueT = await zeroEx.exchange.getFilledTakerAmountAsync(NON_EXISTENT_ORDER_HASH); expect(filledValueT).to.be.bignumber.equal(0); }); it('should return the filledValueT for a valid and partially filled orderHash', async () => { @@ -633,14 +907,18 @@ describe('ExchangeWrapper', () => { const cancelTakerAmountInBaseUnits = new BigNumber(1); before(() => { [coinbase, makerAddress, takerAddress] = userAddresses; - const [makerToken, takerToken] = tokens; + const [makerToken, takerToken] = tokenUtils.getDummyTokens(); makerTokenAddress = makerToken.address; takerTokenAddress = takerToken.address; }); beforeEach(async () => { fillableAmount = new BigNumber(5); signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); }); afterEach(async () => { @@ -653,70 +931,74 @@ describe('ExchangeWrapper', () => { // Source: https://github.com/mochajs/mocha/issues/2407 it('Should receive the LogFill event when an order is filled', (done: DoneCallback) => { (async () => { - - const callback = (err: Error, logEvent: DecodedLogEvent<LogFillContractEventArgs>) => { - expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill); - done(); - }; - zeroEx.exchange.subscribe( - ExchangeEvents.LogFill, indexFilterValues, callback, + const callback = reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => { + expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill); + }, ); + zeroEx.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callback); await zeroEx.exchange.fillOrderAsync( - signedOrder, takerTokenFillAmountInBaseUnits, shouldThrowOnInsufficientBalanceOrAllowance, + signedOrder, + takerTokenFillAmountInBaseUnits, + shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, ); })().catch(done); }); it('Should receive the LogCancel event when an order is cancelled', (done: DoneCallback) => { (async () => { - - const callback = (err: Error, logEvent: DecodedLogEvent<LogCancelContractEventArgs>) => { - expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogCancel); - done(); - }; - zeroEx.exchange.subscribe( - ExchangeEvents.LogCancel, indexFilterValues, callback, + const callback = reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<LogCancelContractEventArgs>) => { + expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogCancel); + }, ); + zeroEx.exchange.subscribe(ExchangeEvents.LogCancel, indexFilterValues, callback); await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelTakerAmountInBaseUnits); })().catch(done); }); it('Outstanding subscriptions are cancelled when zeroEx.setProvider called', (done: DoneCallback) => { (async () => { - - const callbackNeverToBeCalled = (err: Error, logEvent: DecodedLogEvent<LogFillContractEventArgs>) => { - done(new Error('Expected this subscription to have been cancelled')); - }; - zeroEx.exchange.subscribe( - ExchangeEvents.LogFill, indexFilterValues, callbackNeverToBeCalled, + const callbackNeverToBeCalled = reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => { + done(new Error('Expected this subscription to have been cancelled')); + }, ); + zeroEx.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callbackNeverToBeCalled); const newProvider = web3Factory.getRpcProvider(); zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID); - const callback = (err: Error, logEvent: DecodedLogEvent<LogFillContractEventArgs>) => { - expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill); - done(); - }; - zeroEx.exchange.subscribe( - ExchangeEvents.LogFill, indexFilterValues, callback, + const callback = reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => { + expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill); + }, ); + zeroEx.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callback); await zeroEx.exchange.fillOrderAsync( - signedOrder, takerTokenFillAmountInBaseUnits, shouldThrowOnInsufficientBalanceOrAllowance, + signedOrder, + takerTokenFillAmountInBaseUnits, + shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, ); })().catch(done); }); it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => { (async () => { - const callbackNeverToBeCalled = (err: Error, logEvent: DecodedLogEvent<LogFillContractEventArgs>) => { - done(new Error('Expected this subscription to have been cancelled')); - }; + const callbackNeverToBeCalled = reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => { + done(new Error('Expected this subscription to have been cancelled')); + }, + ); const subscriptionToken = zeroEx.exchange.subscribe( - ExchangeEvents.LogFill, indexFilterValues, callbackNeverToBeCalled, + ExchangeEvents.LogFill, + indexFilterValues, + callbackNeverToBeCalled, ); zeroEx.exchange.unsubscribe(subscriptionToken); await zeroEx.exchange.fillOrderAsync( - signedOrder, takerTokenFillAmountInBaseUnits, shouldThrowOnInsufficientBalanceOrAllowance, + signedOrder, + takerTokenFillAmountInBaseUnits, + shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, ); done(); @@ -731,17 +1013,22 @@ describe('ExchangeWrapper', () => { const fillableAmount = new BigNumber(5); before(async () => { [, makerAddress, takerAddress] = userAddresses; - const [makerToken, takerToken] = tokenUtils.getNonProtocolTokens(); + const [makerToken, takerToken] = tokenUtils.getDummyTokens(); makerTokenAddress = makerToken.address; takerTokenAddress = takerToken.address; }); - it('get\'s the same hash as the local function', async () => { + it("get's the same hash as the local function", async () => { const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); const orderHash = ZeroEx.getOrderHashHex(signedOrder); - const orderHashFromContract = await (zeroEx.exchange as any) - ._getOrderHashHexUsingContractCallAsync(signedOrder); + const orderHashFromContract = await (zeroEx.exchange as any)._getOrderHashHexUsingContractCallAsync( + signedOrder, + ); expect(orderHash).to.equal(orderHashFromContract); }); }); @@ -759,59 +1046,87 @@ describe('ExchangeWrapper', () => { let takerAddress: string; const fillableAmount = new BigNumber(5); const shouldThrowOnInsufficientBalanceOrAllowance = true; - const subscriptionOpts: SubscriptionOpts = { + const blockRange: BlockRange = { fromBlock: 0, toBlock: BlockParamLiteral.Latest, }; let txHash: string; before(async () => { [, makerAddress, takerAddress] = userAddresses; - const [makerToken, takerToken] = tokenUtils.getNonProtocolTokens(); + const [makerToken, takerToken] = tokenUtils.getDummyTokens(); makerTokenAddress = makerToken.address; takerTokenAddress = takerToken.address; }); it('should get logs with decoded args emitted by LogFill', async () => { const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); txHash = await zeroEx.exchange.fillOrderAsync( - signedOrder, fillableAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, + signedOrder, + fillableAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, ); await zeroEx.awaitTransactionMinedAsync(txHash); const eventName = ExchangeEvents.LogFill; const indexFilterValues = {}; - const logs = await zeroEx.exchange.getLogsAsync(eventName, subscriptionOpts, indexFilterValues); + const logs = await zeroEx.exchange.getLogsAsync(eventName, blockRange, indexFilterValues); expect(logs).to.have.length(1); expect(logs[0].event).to.be.equal(eventName); }); it('should only get the logs with the correct event name', async () => { const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); txHash = await zeroEx.exchange.fillOrderAsync( - signedOrder, fillableAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, + signedOrder, + fillableAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, ); await zeroEx.awaitTransactionMinedAsync(txHash); const differentEventName = ExchangeEvents.LogCancel; const indexFilterValues = {}; - const logs = await zeroEx.exchange.getLogsAsync(differentEventName, subscriptionOpts, indexFilterValues); + const logs = await zeroEx.exchange.getLogsAsync(differentEventName, blockRange, indexFilterValues); expect(logs).to.have.length(0); }); it('should only get the logs with the correct indexed fields', async () => { const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); txHash = await zeroEx.exchange.fillOrderAsync( - signedOrder, fillableAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, + signedOrder, + fillableAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, ); await zeroEx.awaitTransactionMinedAsync(txHash); const differentMakerAddress = userAddresses[2]; const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, differentMakerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + differentMakerAddress, + takerAddress, + fillableAmount, ); txHash = await zeroEx.exchange.fillOrderAsync( - anotherSignedOrder, fillableAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, + anotherSignedOrder, + fillableAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, ); await zeroEx.awaitTransactionMinedAsync(txHash); @@ -820,7 +1135,9 @@ describe('ExchangeWrapper', () => { maker: differentMakerAddress, }; const logs = await zeroEx.exchange.getLogsAsync<LogFillContractEventArgs>( - eventName, subscriptionOpts, indexFilterValues, + eventName, + blockRange, + indexFilterValues, ); expect(logs).to.have.length(1); const args = logs[0].args; diff --git a/packages/0x.js/test/expiration_watcher_test.ts b/packages/0x.js/test/expiration_watcher_test.ts index d4581259d..770615f88 100644 --- a/packages/0x.js/test/expiration_watcher_test.ts +++ b/packages/0x.js/test/expiration_watcher_test.ts @@ -1,27 +1,27 @@ -import {Web3Wrapper} from '@0xproject/web3-wrapper'; -import BigNumber from 'bignumber.js'; +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import * as _ from 'lodash'; import 'mocha'; import * as Sinon from 'sinon'; import * as Web3 from 'web3'; -import {ZeroEx} from '../src/0x'; -import {ExpirationWatcher} from '../src/order_watcher/expiration_watcher'; -import {DoneCallback, Token} from '../src/types'; -import {constants} from '../src/utils/constants'; -import {utils} from '../src/utils/utils'; +import { ZeroEx } from '../src/0x'; +import { ExpirationWatcher } from '../src/order_watcher/expiration_watcher'; +import { DoneCallback, Token } from '../src/types'; +import { constants } from '../src/utils/constants'; +import { utils } from '../src/utils/utils'; -import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; -import {chaiSetup} from './utils/chai_setup'; -import {FillScenarios} from './utils/fill_scenarios'; -import {reportCallbackErrors} from './utils/report_callback_errors'; -import {TokenUtils} from './utils/token_utils'; -import {web3Factory} from './utils/web3_factory'; +import { chaiSetup } from './utils/chai_setup'; +import { constants as testConstants } from './utils/constants'; +import { FillScenarios } from './utils/fill_scenarios'; +import { reportNoErrorCallbackErrors } from './utils/report_callback_errors'; +import { TokenUtils } from './utils/token_utils'; +import { web3Factory } from './utils/web3_factory'; chaiSetup.configure(); const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(); +const blockchainLifecycle = new BlockchainLifecycle(testConstants.RPC_URL); describe('ExpirationWatcher', () => { let web3: Web3; @@ -56,13 +56,13 @@ describe('ExpirationWatcher', () => { fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress); [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses; tokens = await zeroEx.tokenRegistry.getTokensAsync(); - const [makerToken, takerToken] = tokenUtils.getNonProtocolTokens(); + const [makerToken, takerToken] = tokenUtils.getDummyTokens(); makerTokenAddress = makerToken.address; takerTokenAddress = takerToken.address; }); beforeEach(async () => { await blockchainLifecycle.startAsync(); - const sinonTimerConfig = {shouldAdvanceTime: true} as any; + const sinonTimerConfig = { shouldAdvanceTime: true } as any; // This constructor has incorrect types timer = Sinon.useFakeTimers(sinonTimerConfig); currentUnixTimestampSec = utils.getCurrentUnixTimestampSec(); @@ -78,31 +78,38 @@ describe('ExpirationWatcher', () => { const orderLifetimeSec = 60; const expirationUnixTimestampSec = currentUnixTimestampSec.plus(orderLifetimeSec); const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, expirationUnixTimestampSec, ); const orderHash = ZeroEx.getOrderHashHex(signedOrder); expirationWatcher.addOrder(orderHash, signedOrder.expirationUnixTimestampSec.times(1000)); - const callbackAsync = reportCallbackErrors(done)(async (hash: string) => { + const callbackAsync = reportNoErrorCallbackErrors(done)((hash: string) => { expect(hash).to.be.equal(orderHash); expect(utils.getCurrentUnixTimestampSec()).to.be.bignumber.gte(expirationUnixTimestampSec); - done(); }); expirationWatcher.subscribe(callbackAsync); timer.tick(orderLifetimeSec * 1000); })().catch(done); }); - it('doesn\'t emit events before order expires', (done: DoneCallback) => { + it("doesn't emit events before order expires", (done: DoneCallback) => { (async () => { const orderLifetimeSec = 60; const expirationUnixTimestampSec = currentUnixTimestampSec.plus(orderLifetimeSec); const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, expirationUnixTimestampSec, ); const orderHash = ZeroEx.getOrderHashHex(signedOrder); expirationWatcher.addOrder(orderHash, signedOrder.expirationUnixTimestampSec.times(1000)); - const callbackAsync = reportCallbackErrors(done)(async (hash: string) => { + const callbackAsync = reportNoErrorCallbackErrors(done)(async (hash: string) => { done(new Error('Emitted expiration went before the order actually expired')); }); expirationWatcher.subscribe(callbackAsync); @@ -118,11 +125,19 @@ describe('ExpirationWatcher', () => { const order1ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order1Lifetime); const order2ExpirationUnixTimestampSec = currentUnixTimestampSec.plus(order2Lifetime); const signedOrder1 = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, order1ExpirationUnixTimestampSec, ); const signedOrder2 = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, order2ExpirationUnixTimestampSec, ); const orderHash1 = ZeroEx.getOrderHashHex(signedOrder1); @@ -130,7 +145,8 @@ describe('ExpirationWatcher', () => { expirationWatcher.addOrder(orderHash2, signedOrder2.expirationUnixTimestampSec.times(1000)); expirationWatcher.addOrder(orderHash1, signedOrder1.expirationUnixTimestampSec.times(1000)); const expirationOrder = [orderHash1, orderHash2]; - const callbackAsync = reportCallbackErrors(done)(async (hash: string) => { + const expectToBeCalledOnce = false; + const callbackAsync = reportNoErrorCallbackErrors(done, expectToBeCalledOnce)((hash: string) => { const orderHash = expirationOrder.shift(); expect(hash).to.be.equal(orderHash); if (_.isEmpty(expirationOrder)) { diff --git a/packages/0x.js/test/order_state_watcher_test.ts b/packages/0x.js/test/order_state_watcher_test.ts index b5968dc24..2e9202fe2 100644 --- a/packages/0x.js/test/order_state_watcher_test.ts +++ b/packages/0x.js/test/order_state_watcher_test.ts @@ -1,39 +1,34 @@ -import {Web3Wrapper} from '@0xproject/web3-wrapper'; -import BigNumber from 'bignumber.js'; +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import * as _ from 'lodash'; import 'mocha'; import * as Web3 from 'web3'; import { - DecodedLogEvent, ExchangeContractErrs, - LogEvent, OrderState, OrderStateInvalid, OrderStateValid, SignedOrder, Token, ZeroEx, - ZeroExConfig, ZeroExError, } from '../src'; -import {OrderStateWatcher} from '../src/order_watcher/order_state_watcher'; -import {DoneCallback} from '../src/types'; +import { DoneCallback } from '../src/types'; -import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; -import {chaiSetup} from './utils/chai_setup'; -import {constants} from './utils/constants'; -import {FillScenarios} from './utils/fill_scenarios'; -import {reportCallbackErrors} from './utils/report_callback_errors'; -import {TokenUtils} from './utils/token_utils'; -import {web3Factory} from './utils/web3_factory'; +import { chaiSetup } from './utils/chai_setup'; +import { constants } from './utils/constants'; +import { FillScenarios } from './utils/fill_scenarios'; +import { reportNodeCallbackErrors } from './utils/report_callback_errors'; +import { TokenUtils } from './utils/token_utils'; +import { web3Factory } from './utils/web3_factory'; const TIMEOUT_MS = 150; chaiSetup.configure(); const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(); +const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL); describe('OrderStateWatcher', () => { let web3: Web3; @@ -48,7 +43,6 @@ describe('OrderStateWatcher', () => { let takerToken: Token; let maker: string; let taker: string; - let web3Wrapper: Web3Wrapper; let signedOrder: SignedOrder; const config = { networkId: constants.TESTRPC_NETWORK_ID, @@ -65,8 +59,8 @@ describe('OrderStateWatcher', () => { 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; + await fillScenarios.initTokenBalancesAsync(); + [makerToken, takerToken] = tokenUtils.getDummyTokens(); }); beforeEach(async () => { await blockchainLifecycle.startAsync(); @@ -77,7 +71,11 @@ describe('OrderStateWatcher', () => { describe('#removeOrder', async () => { it('should successfully remove existing order', async () => { signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerToken.address, takerToken.address, maker, taker, fillableAmount, + makerToken.address, + takerToken.address, + maker, + taker, + fillableAmount, ); const orderHash = ZeroEx.getOrderHashHex(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder); @@ -95,10 +93,18 @@ describe('OrderStateWatcher', () => { }); it('should no-op when removing a non-existing order', async () => { signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerToken.address, takerToken.address, maker, taker, fillableAmount, + makerToken.address, + takerToken.address, + maker, + taker, + fillableAmount, ); const orderHash = ZeroEx.getOrderHashHex(signedOrder); - const nonExistentOrderHash = `0x${orderHash.substr(2).split('').reverse().join('')}`; + const nonExistentOrderHash = `0x${orderHash + .substr(2) + .split('') + .reverse() + .join('')}`; zeroEx.orderStateWatcher.removeOrder(nonExistentOrderHash); }); }); @@ -108,8 +114,7 @@ describe('OrderStateWatcher', () => { }); it('should fail when trying to subscribe twice', async () => { zeroEx.orderStateWatcher.subscribe(_.noop); - expect(() => zeroEx.orderStateWatcher.subscribe(_.noop)) - .to.throw(ZeroExError.SubscriptionAlreadyPresent); + expect(() => zeroEx.orderStateWatcher.subscribe(_.noop)).to.throw(ZeroExError.SubscriptionAlreadyPresent); }); }); describe('tests with cleanup', async () => { @@ -121,16 +126,19 @@ describe('OrderStateWatcher', () => { 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, + makerToken.address, + takerToken.address, + maker, + taker, + fillableAmount, ); const orderHash = ZeroEx.getOrderHashHex(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder); - const callback = reportCallbackErrors(done)((orderState: OrderState) => { + const callback = reportNodeCallbackErrors(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)); @@ -139,18 +147,20 @@ describe('OrderStateWatcher', () => { 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, + makerToken.address, + takerToken.address, + maker, + taker, + fillableAmount, ); - const orderHash = ZeroEx.getOrderHashHex(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder); - const callback = reportCallbackErrors(done)((orderState: OrderState) => { + const callback = reportNodeCallbackErrors(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(); @@ -160,16 +170,19 @@ describe('OrderStateWatcher', () => { 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, + makerToken.address, + takerToken.address, + maker, + taker, + fillableAmount, ); const orderHash = ZeroEx.getOrderHashHex(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder); - const callback = reportCallbackErrors(done)((orderState: OrderState) => { + const callback = reportNodeCallbackErrors(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; @@ -180,40 +193,48 @@ describe('OrderStateWatcher', () => { it('should emit orderStateInvalid when watched order fully filled', (done: DoneCallback) => { (async () => { signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerToken.address, takerToken.address, maker, taker, fillableAmount, + makerToken.address, + takerToken.address, + maker, + taker, + fillableAmount, ); const orderHash = ZeroEx.getOrderHashHex(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder); - const callback = reportCallbackErrors(done)((orderState: OrderState) => { + const callback = reportNodeCallbackErrors(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.fillOrderAsync( - signedOrder, fillableAmount, shouldThrowOnInsufficientBalanceOrAllowance, taker, + 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, + 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); - const callback = reportCallbackErrors(done)((orderState: OrderState) => { + const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { expect(orderState.isValid).to.be.true(); const validOrderState = orderState as OrderStateValid; expect(validOrderState.orderHash).to.be.equal(orderHash); @@ -221,16 +242,20 @@ describe('OrderStateWatcher', () => { const remainingMakerBalance = makerBalance.sub(fillAmountInBaseUnits); const remainingFillable = fillableAmount.minus(fillAmountInBaseUnits); expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal( - remainingFillable); + remainingFillable, + ); expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal( - remainingFillable); + remainingFillable, + ); expect(orderRelevantState.makerBalance).to.be.bignumber.equal(remainingMakerBalance); - done(); }); zeroEx.orderStateWatcher.subscribe(callback); const shouldThrowOnInsufficientBalanceOrAllowance = true; await zeroEx.exchange.fillOrderAsync( - signedOrder, fillAmountInBaseUnits, shouldThrowOnInsufficientBalanceOrAllowance, taker, + signedOrder, + fillAmountInBaseUnits, + shouldThrowOnInsufficientBalanceOrAllowance, + taker, ); })().catch(done); }); @@ -239,13 +264,17 @@ describe('OrderStateWatcher', () => { const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(2), 18); const takerFee = ZeroEx.toBaseUnitAmount(new BigNumber(0), 18); signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerToken.address, takerToken.address, makerFee, takerFee, maker, taker, fillableAmount, - taker); - const orderHash = ZeroEx.getOrderHashHex(signedOrder); + makerToken.address, + takerToken.address, + makerFee, + takerFee, + maker, + taker, + fillableAmount, + taker, + ); + const callback = reportNodeCallbackErrors(done)(); zeroEx.orderStateWatcher.addOrder(signedOrder); - const callback = reportCallbackErrors(done)((orderState: OrderState) => { - done(); - }); zeroEx.orderStateWatcher.subscribe(callback); await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, maker, new BigNumber(0)); })().catch(done); @@ -256,51 +285,60 @@ describe('OrderStateWatcher', () => { const takerFillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(10), decimals); const makerFillableAmount = ZeroEx.toBaseUnitAmount(new BigNumber(20), decimals); signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync( - makerToken.address, takerToken.address, maker, taker, makerFillableAmount, + 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 = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals); const orderHash = ZeroEx.getOrderHashHex(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder); - const callback = reportCallbackErrors(done)((orderState: OrderState) => { + const callback = reportNodeCallbackErrors(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.remainingFillableMakerTokenAmount).to.be.bignumber.equal( - ZeroEx.toBaseUnitAmount(new BigNumber(16), decimals)); + ZeroEx.toBaseUnitAmount(new BigNumber(16), decimals), + ); expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal( - ZeroEx.toBaseUnitAmount(new BigNumber(8), decimals)); - done(); + ZeroEx.toBaseUnitAmount(new BigNumber(8), decimals), + ); }); zeroEx.orderStateWatcher.subscribe(callback); const shouldThrowOnInsufficientBalanceOrAllowance = true; await zeroEx.exchange.fillOrderAsync( - signedOrder, fillAmountInBaseUnits, shouldThrowOnInsufficientBalanceOrAllowance, taker, + 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, + makerToken.address, + takerToken.address, + maker, + taker, + fillableAmount, ); - const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); - const changedMakerApprovalAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals); zeroEx.orderStateWatcher.addOrder(signedOrder); - const callback = reportCallbackErrors(done)((orderState: OrderState) => { + const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { const validOrderState = orderState as OrderStateValid; const orderRelevantState = validOrderState.orderRelevantState; expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal( - changedMakerApprovalAmount); + changedMakerApprovalAmount, + ); expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal( - changedMakerApprovalAmount); - done(); + changedMakerApprovalAmount, + ); }); zeroEx.orderStateWatcher.subscribe(callback); await zeroEx.token.setProxyAllowanceAsync(makerToken.address, maker, changedMakerApprovalAmount); @@ -309,7 +347,11 @@ describe('OrderStateWatcher', () => { it('should equal balance amount when balance amount is lowest', (done: DoneCallback) => { (async () => { signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerToken.address, takerToken.address, maker, taker, fillableAmount, + makerToken.address, + takerToken.address, + maker, + taker, + fillableAmount, ); const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); @@ -318,19 +360,19 @@ describe('OrderStateWatcher', () => { const transferAmount = makerBalance.sub(remainingAmount); zeroEx.orderStateWatcher.addOrder(signedOrder); - const callback = reportCallbackErrors(done)((orderState: OrderState) => { + const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { expect(orderState.isValid).to.be.true(); const validOrderState = orderState as OrderStateValid; const orderRelevantState = validOrderState.orderRelevantState; expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal( - remainingAmount); + remainingAmount, + ); expect(orderRelevantState.remainingFillableTakerTokenAmount).to.be.bignumber.equal( - remainingAmount); - done(); + remainingAmount, + ); }); zeroEx.orderStateWatcher.subscribe(callback); - await zeroEx.token.transferAsync( - makerToken.address, maker, ZeroEx.NULL_ADDRESS, transferAmount); + await zeroEx.token.transferAsync(makerToken.address, maker, ZeroEx.NULL_ADDRESS, transferAmount); })().catch(done); }); it('should equal remaining amount when partially cancelled and order has fees', (done: DoneCallback) => { @@ -339,22 +381,27 @@ describe('OrderStateWatcher', () => { const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals); const feeRecipient = taker; signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerToken.address, takerToken.address, makerFee, takerFee, maker, - taker, fillableAmount, feeRecipient); - - const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); + makerToken.address, + takerToken.address, + makerFee, + takerFee, + maker, + taker, + fillableAmount, + feeRecipient, + ); const remainingTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(4), decimals); const transferTokenAmount = makerFee.sub(remainingTokenAmount); zeroEx.orderStateWatcher.addOrder(signedOrder); - const callback = reportCallbackErrors(done)((orderState: OrderState) => { + const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { expect(orderState.isValid).to.be.true(); const validOrderState = orderState as OrderStateValid; const orderRelevantState = validOrderState.orderRelevantState; expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal( - remainingTokenAmount); - done(); + remainingTokenAmount, + ); }); zeroEx.orderStateWatcher.subscribe(callback); await zeroEx.exchange.cancelOrderAsync(signedOrder, transferTokenAmount); @@ -366,29 +413,37 @@ describe('OrderStateWatcher', () => { const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals); const feeRecipient = taker; signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerToken.address, takerToken.address, makerFee, takerFee, maker, - taker, fillableAmount, feeRecipient); - - const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); + makerToken.address, + takerToken.address, + makerFee, + takerFee, + maker, + taker, + fillableAmount, + feeRecipient, + ); const remainingFeeAmount = ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals); - const transferFeeAmount = makerFee.sub(remainingFeeAmount); const remainingTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(4), decimals); const transferTokenAmount = makerFee.sub(remainingTokenAmount); zeroEx.orderStateWatcher.addOrder(signedOrder); - const callback = reportCallbackErrors(done)((orderState: OrderState) => { + const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { const validOrderState = orderState as OrderStateValid; const orderRelevantState = validOrderState.orderRelevantState; expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal( - remainingFeeAmount); - done(); + remainingFeeAmount, + ); }); zeroEx.orderStateWatcher.subscribe(callback); await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, maker, remainingFeeAmount); await zeroEx.token.transferAsync( - makerToken.address, maker, ZeroEx.NULL_ADDRESS, transferTokenAmount); + makerToken.address, + maker, + ZeroEx.NULL_ADDRESS, + transferTokenAmount, + ); })().catch(done); }); it('should calculate full amount when all available and non-divisible', (done: DoneCallback) => { @@ -397,43 +452,54 @@ describe('OrderStateWatcher', () => { const makerFee = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals); const feeRecipient = taker; signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerToken.address, takerToken.address, makerFee, takerFee, maker, - taker, fillableAmount, feeRecipient); + makerToken.address, + takerToken.address, + makerFee, + takerFee, + maker, + taker, + fillableAmount, + feeRecipient, + ); - const makerBalance = await zeroEx.token.getBalanceAsync(makerToken.address, maker); zeroEx.orderStateWatcher.addOrder(signedOrder); - const callback = reportCallbackErrors(done)((orderState: OrderState) => { + const callback = reportNodeCallbackErrors(done)((orderState: OrderState) => { const validOrderState = orderState as OrderStateValid; const orderRelevantState = validOrderState.orderRelevantState; expect(orderRelevantState.remainingFillableMakerTokenAmount).to.be.bignumber.equal( - fillableAmount); - done(); + fillableAmount, + ); }); zeroEx.orderStateWatcher.subscribe(callback); await zeroEx.token.setProxyAllowanceAsync( - makerToken.address, maker, ZeroEx.toBaseUnitAmount(new BigNumber(100), decimals)); + makerToken.address, + maker, + ZeroEx.toBaseUnitAmount(new BigNumber(100), decimals), + ); })().catch(done); }); }); it('should emit orderStateInvalid when watched order cancelled', (done: DoneCallback) => { (async () => { signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerToken.address, takerToken.address, maker, taker, fillableAmount, + makerToken.address, + takerToken.address, + maker, + taker, + fillableAmount, ); const orderHash = ZeroEx.getOrderHashHex(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder); - const callback = reportCallbackErrors(done)((orderState: OrderState) => { + const callback = reportNodeCallbackErrors(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); }); @@ -441,44 +507,48 @@ describe('OrderStateWatcher', () => { (async () => { const remainingFillableAmountInBaseUnits = new BigNumber(100); signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerToken.address, takerToken.address, maker, taker, fillableAmount, + makerToken.address, + takerToken.address, + maker, + taker, + fillableAmount, ); const orderHash = ZeroEx.getOrderHashHex(signedOrder); zeroEx.orderStateWatcher.addOrder(signedOrder); - const callback = reportCallbackErrors(done)((orderState: OrderState) => { + const callback = reportNodeCallbackErrors(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.OrderFillRoundingError); - done(); }); zeroEx.orderStateWatcher.subscribe(callback); await zeroEx.exchange.cancelOrderAsync( - signedOrder, fillableAmount.minus(remainingFillableAmountInBaseUnits), + signedOrder, + fillableAmount.minus(remainingFillableAmountInBaseUnits), ); })().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, + 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) => { + const callback = reportNodeCallbackErrors(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.cancelledTakerTokenAmount).to.be.bignumber.equal(cancelAmountInBaseUnits); - done(); }); zeroEx.orderStateWatcher.subscribe(callback); await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmountInBaseUnits); diff --git a/packages/0x.js/test/order_validation_test.ts b/packages/0x.js/test/order_validation_test.ts index d585c1f3c..be3e0590c 100644 --- a/packages/0x.js/test/order_validation_test.ts +++ b/packages/0x.js/test/order_validation_test.ts @@ -1,23 +1,23 @@ -import BigNumber from 'bignumber.js'; +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import * as Sinon from 'sinon'; import * as Web3 from 'web3'; -import {ExchangeContractErrs, SignedOrder, Token, ZeroEx, ZeroExError} from '../src'; -import {BlockParamLiteral, TradeSide, TransferType} from '../src/types'; -import {ExchangeTransferSimulator} from '../src/utils/exchange_transfer_simulator'; -import {OrderValidationUtils} from '../src/utils/order_validation_utils'; +import { ExchangeContractErrs, SignedOrder, Token, ZeroEx, ZeroExError } from '../src'; +import { BlockParamLiteral, TradeSide, TransferType } from '../src/types'; +import { ExchangeTransferSimulator } from '../src/utils/exchange_transfer_simulator'; +import { OrderValidationUtils } from '../src/utils/order_validation_utils'; -import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; -import {chaiSetup} from './utils/chai_setup'; -import {constants} from './utils/constants'; -import {FillScenarios} from './utils/fill_scenarios'; -import {TokenUtils} from './utils/token_utils'; -import {web3Factory} from './utils/web3_factory'; +import { chaiSetup } from './utils/chai_setup'; +import { constants } from './utils/constants'; +import { FillScenarios } from './utils/fill_scenarios'; +import { TokenUtils } from './utils/token_utils'; +import { web3Factory } from './utils/web3_factory'; chaiSetup.configure(); const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(); +const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL); describe('OrderValidation', () => { let web3: Web3; @@ -34,7 +34,6 @@ describe('OrderValidation', () => { let makerAddress: string; let takerAddress: string; let feeRecipient: string; - let orderValidationUtils: OrderValidationUtils; const fillableAmount = new BigNumber(5); const fillTakerAmount = new BigNumber(5); const config = { @@ -50,10 +49,9 @@ describe('OrderValidation', () => { tokenUtils = new TokenUtils(tokens); zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address; fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress); - const [makerToken, takerToken] = tokenUtils.getNonProtocolTokens(); + const [makerToken, takerToken] = tokenUtils.getDummyTokens(); makerTokenAddress = makerToken.address; takerTokenAddress = takerToken.address; - orderValidationUtils = new OrderValidationUtils(zeroEx.token, zeroEx.exchange); }); beforeEach(async () => { await blockchainLifecycle.startAsync(); @@ -64,108 +62,152 @@ describe('OrderValidation', () => { describe('validateOrderFillableOrThrowAsync', () => { it('should succeed if the order is fillable', async () => { const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, - ); - await zeroEx.exchange.validateOrderFillableOrThrowAsync( - signedOrder, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); + await zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder); }); it('should succeed if the order is asymmetric and fillable', async () => { const makerFillableAmount = fillableAmount; const takerFillableAmount = fillableAmount.minus(4); const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, - makerFillableAmount, takerFillableAmount, - ); - await zeroEx.exchange.validateOrderFillableOrThrowAsync( - signedOrder, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + makerFillableAmount, + takerFillableAmount, ); + await zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder); }); it('should throw when the order is fully filled or cancelled', async () => { const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount); - return expect(zeroEx.exchange.validateOrderFillableOrThrowAsync( - signedOrder, - )).to.be.rejectedWith(ExchangeContractErrs.OrderRemainingFillAmountZero); + return expect(zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith( + ExchangeContractErrs.OrderRemainingFillAmountZero, + ); }); 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, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, + expirationInPast, + ); + return expect(zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith( + ExchangeContractErrs.OrderFillExpired, ); - return expect(zeroEx.exchange.validateOrderFillableOrThrowAsync( - signedOrder, - )).to.be.rejectedWith(ExchangeContractErrs.OrderFillExpired); }); }); describe('validateFillOrderAndThrowIfInvalidAsync', () => { it('should throw when the fill amount is zero', async () => { const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); const zeroFillAmount = new BigNumber(0); - return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( - signedOrder, zeroFillAmount, takerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); + return expect( + zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, zeroFillAmount, takerAddress), + ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); it('should throw when the signature is invalid', async () => { const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); // 27 <--> 28 - signedOrder.ecSignature.v = (28 - signedOrder.ecSignature.v) + 27; - return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( - signedOrder, fillableAmount, takerAddress, - )).to.be.rejectedWith(ZeroExError.InvalidSignature); + signedOrder.ecSignature.v = 28 - signedOrder.ecSignature.v + 27; + return expect( + zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillableAmount, takerAddress), + ).to.be.rejectedWith(ZeroExError.InvalidSignature); }); it('should throw when the order is fully filled or cancelled', async () => { const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount); - return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( - signedOrder, fillableAmount, takerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.OrderRemainingFillAmountZero); + return expect( + zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillableAmount, takerAddress), + ).to.be.rejectedWith(ExchangeContractErrs.OrderRemainingFillAmountZero); }); it('should throw when sender is not a taker', async () => { const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); const nonTakerAddress = userAddresses[6]; - return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( - signedOrder, fillTakerAmount, nonTakerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker); + return expect( + zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillTakerAmount, nonTakerAddress), + ).to.be.rejectedWith(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker); }); 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, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, + expirationInPast, ); - return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( - signedOrder, fillTakerAmount, takerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.OrderFillExpired); + return expect( + zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillTakerAmount, takerAddress), + ).to.be.rejectedWith(ExchangeContractErrs.OrderFillExpired); }); 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, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + makerAmount, + takerAmount, ); const fillTakerAmountThatCausesRoundingError = new BigNumber(3); - return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( - signedOrder, fillTakerAmountThatCausesRoundingError, takerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.OrderFillRoundingError); + return expect( + zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( + signedOrder, + fillTakerAmountThatCausesRoundingError, + takerAddress, + ), + ).to.be.rejectedWith(ExchangeContractErrs.OrderFillRoundingError); }); }); describe('#validateFillOrKillOrderAndThrowIfInvalidAsync', () => { it('should throw if remaining fillAmount is less then the desired fillAmount', async () => { const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); const tooLargeFillAmount = new BigNumber(7); const fillAmountDifference = tooLargeFillAmount.minus(fillableAmount); @@ -174,44 +216,56 @@ describe('OrderValidation', () => { await zeroEx.token.transferAsync(makerTokenAddress, coinbase, makerAddress, fillAmountDifference); await zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, tooLargeFillAmount); - return expect(zeroEx.exchange.validateFillOrKillOrderThrowIfInvalidAsync( - signedOrder, tooLargeFillAmount, takerAddress, - )).to.be.rejectedWith(ExchangeContractErrs.InsufficientRemainingFillAmount); + return expect( + zeroEx.exchange.validateFillOrKillOrderThrowIfInvalidAsync( + signedOrder, + tooLargeFillAmount, + takerAddress, + ), + ).to.be.rejectedWith(ExchangeContractErrs.InsufficientRemainingFillAmount); }); }); describe('validateCancelOrderAndThrowIfInvalidAsync', () => { let signedOrder: SignedOrder; - let orderHashHex: string; const cancelAmount = new BigNumber(3); beforeEach(async () => { [coinbase, makerAddress, takerAddress] = userAddresses; - const [makerToken, takerToken] = tokenUtils.getNonProtocolTokens(); + const [makerToken, takerToken] = tokenUtils.getDummyTokens(); makerTokenAddress = makerToken.address; takerTokenAddress = takerToken.address; signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, ); - orderHashHex = ZeroEx.getOrderHashHex(signedOrder); }); it('should throw when cancel amount is zero', async () => { const zeroCancelAmount = new BigNumber(0); - return expect(zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, zeroCancelAmount)) - .to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); + return expect( + zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, zeroCancelAmount), + ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); }); it('should throw when order is expired', async () => { const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017 const expiredSignedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, - fillableAmount, expirationInPast, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, + expirationInPast, ); - orderHashHex = ZeroEx.getOrderHashHex(expiredSignedOrder); - return expect(zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(expiredSignedOrder, cancelAmount)) - .to.be.rejectedWith(ExchangeContractErrs.OrderCancelExpired); + return expect( + zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(expiredSignedOrder, cancelAmount), + ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelExpired); }); it('should throw when order is already cancelled or filled', async () => { await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount); - return expect(zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, fillableAmount)) - .to.be.rejectedWith(ExchangeContractErrs.OrderAlreadyCancelledOrFilled); + return expect( + zeroEx.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, fillableAmount), + ).to.be.rejectedWith(ExchangeContractErrs.OrderAlreadyCancelledOrFilled); }); }); describe('#validateFillOrderBalancesAllowancesThrowIfInvalidAsync', () => { @@ -229,82 +283,159 @@ describe('OrderValidation', () => { const makerFee = new BigNumber(2); const takerFee = new BigNumber(2); const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, takerTokenAddress, makerFee, takerFee, - makerAddress, takerAddress, fillableAmount, feeRecipient, + makerTokenAddress, + takerTokenAddress, + makerFee, + takerFee, + makerAddress, + takerAddress, + fillableAmount, + feeRecipient, ); await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTransferSimulator, signedOrder, fillableAmount, takerAddress, zrxTokenAddress, + 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, - ), + 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, - ), + 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, - ), + 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, - ), + transferFromAsync + .getCall(3) + .calledWith( + zrxTokenAddress, + takerAddress, + feeRecipient, + bigNumberMatch(takerFee), + TradeSide.Taker, + TransferType.Fee, + ), ).to.be.true(); }); it('should call exchangeTransferSimulator.transferFrom with correct values for an open order', async () => { const makerFee = new BigNumber(2); const takerFee = new BigNumber(2); const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, takerTokenAddress, makerFee, takerFee, - makerAddress, ZeroEx.NULL_ADDRESS, fillableAmount, feeRecipient, + makerTokenAddress, + takerTokenAddress, + makerFee, + takerFee, + makerAddress, + ZeroEx.NULL_ADDRESS, + fillableAmount, + feeRecipient, ); await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTransferSimulator, signedOrder, fillableAmount, takerAddress, zrxTokenAddress, + 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, - ), + 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, - ), + 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, - ), + 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, - ), + transferFromAsync + .getCall(3) + .calledWith( + zrxTokenAddress, + takerAddress, + feeRecipient, + bigNumberMatch(takerFee), + TradeSide.Taker, + TransferType.Fee, + ), ).to.be.true(); }); it('should correctly round the fillMakerTokenAmount', async () => { const makerTokenAmount = new BigNumber(3); const takerTokenAmount = new BigNumber(1); const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, makerTokenAmount, takerTokenAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + makerTokenAmount, + takerTokenAmount, ); await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTransferSimulator, signedOrder, takerTokenAmount, takerAddress, zrxTokenAddress, + exchangeTransferSimulator, + signedOrder, + takerTokenAmount, + takerAddress, + zrxTokenAddress, ); expect(transferFromAsync.callCount).to.be.equal(4); const makerFillAmount = transferFromAsync.getCall(0).args[3]; @@ -314,12 +445,22 @@ describe('OrderValidation', () => { const makerFee = new BigNumber(2); const takerFee = new BigNumber(4); const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, takerTokenAddress, makerFee, takerFee, makerAddress, takerAddress, - fillableAmount, ZeroEx.NULL_ADDRESS, + makerTokenAddress, + takerTokenAddress, + makerFee, + takerFee, + makerAddress, + takerAddress, + fillableAmount, + ZeroEx.NULL_ADDRESS, ); const fillTakerTokenAmount = fillableAmount.div(2).round(0); await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTransferSimulator, signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress, + exchangeTransferSimulator, + signedOrder, + fillTakerTokenAmount, + takerAddress, + zrxTokenAddress, ); const makerPartialFee = makerFee.div(2); const takerPartialFee = takerFee.div(2); diff --git a/packages/0x.js/test/remaining_fillable_calculator_test.ts b/packages/0x.js/test/remaining_fillable_calculator_test.ts index 610bf9b1a..4c6b8f3ac 100644 --- a/packages/0x.js/test/remaining_fillable_calculator_test.ts +++ b/packages/0x.js/test/remaining_fillable_calculator_test.ts @@ -1,4 +1,4 @@ -import BigNumber from 'bignumber.js'; +import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import 'mocha'; @@ -7,7 +7,6 @@ import { RemainingFillableCalculator } from '../src/order_watcher/remaining_fill import { ECSignature, SignedOrder } from '../src/types'; import { chaiSetup } from './utils/chai_setup'; -import { TokenUtils } from './utils/token_utils'; chaiSetup.configure(); const expect = chai.expect; @@ -27,29 +26,34 @@ describe('RemainingFillableCalculator', () => { const decimals: number = 4; const zero: BigNumber = new BigNumber(0); const zeroAddress = '0x0'; - const signature: ECSignature = { v: 27, r: '', s: ''}; + const signature: ECSignature = { v: 27, r: '', s: '' }; beforeEach(async () => { - [makerAmount, takerAmount, makerFeeAmount] = [ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals), - ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals), - ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals)]; + [makerAmount, takerAmount, makerFeeAmount] = [ + ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals), + ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals), + ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals), + ]; [transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount] = [ - ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals), - ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals)]; + ZeroEx.toBaseUnitAmount(new BigNumber(50), decimals), + ZeroEx.toBaseUnitAmount(new BigNumber(5), decimals), + ]; }); function buildSignedOrder(): SignedOrder { - return { ecSignature: signature, - exchangeContractAddress: zeroAddress, - feeRecipient: zeroAddress, - maker: zeroAddress, - taker: zeroAddress, - makerFee: makerFeeAmount, - takerFee: zero, - makerTokenAmount: makerAmount, - takerTokenAmount: takerAmount, - makerTokenAddress: makerToken, - takerTokenAddress: takerToken, - salt: zero, - expirationUnixTimestampSec: zero }; + return { + ecSignature: signature, + exchangeContractAddress: zeroAddress, + feeRecipient: zeroAddress, + maker: zeroAddress, + taker: zeroAddress, + makerFee: makerFeeAmount, + takerFee: zero, + makerTokenAmount: makerAmount, + takerTokenAmount: takerAmount, + makerTokenAddress: makerToken, + takerTokenAddress: takerToken, + salt: zero, + expirationUnixTimestampSec: zero, + }; } describe('Maker token is NOT ZRX', () => { before(async () => { @@ -58,23 +62,38 @@ describe('RemainingFillableCalculator', () => { it('calculates the correct amount when unfilled and funds available', () => { signedOrder = buildSignedOrder(); remainingMakerTokenAmount = signedOrder.makerTokenAmount; - calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, - transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount); + calculator = new RemainingFillableCalculator( + signedOrder, + isMakerTokenZRX, + transferrableMakerTokenAmount, + transferrableMakerFeeTokenAmount, + remainingMakerTokenAmount, + ); expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount); }); it('calculates the correct amount when partially filled and funds available', () => { signedOrder = buildSignedOrder(); remainingMakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals); - calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, - transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount); + calculator = new RemainingFillableCalculator( + signedOrder, + isMakerTokenZRX, + transferrableMakerTokenAmount, + transferrableMakerFeeTokenAmount, + remainingMakerTokenAmount, + ); expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount); }); it('calculates the amount to be 0 when all fee funds are transferred', () => { signedOrder = buildSignedOrder(); transferrableMakerFeeTokenAmount = zero; remainingMakerTokenAmount = signedOrder.makerTokenAmount; - calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, - transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount); + calculator = new RemainingFillableCalculator( + signedOrder, + isMakerTokenZRX, + transferrableMakerTokenAmount, + transferrableMakerFeeTokenAmount, + remainingMakerTokenAmount, + ); expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(zero); }); it('calculates the correct amount when balance is less than remaining fillable', () => { @@ -82,41 +101,58 @@ describe('RemainingFillableCalculator', () => { const partiallyFilledAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals); remainingMakerTokenAmount = signedOrder.makerTokenAmount.minus(partiallyFilledAmount); transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(partiallyFilledAmount); - calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, - transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount); + calculator = new RemainingFillableCalculator( + signedOrder, + isMakerTokenZRX, + transferrableMakerTokenAmount, + transferrableMakerFeeTokenAmount, + remainingMakerTokenAmount, + ); expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(transferrableMakerTokenAmount); }); describe('Order to Fee Ratio is < 1', () => { beforeEach(async () => { - [makerAmount, takerAmount, makerFeeAmount] = [ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals), - ZeroEx.toBaseUnitAmount(new BigNumber(6), decimals), - ZeroEx.toBaseUnitAmount(new BigNumber(6), decimals)]; + [makerAmount, takerAmount, makerFeeAmount] = [ + ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals), + ZeroEx.toBaseUnitAmount(new BigNumber(6), decimals), + ZeroEx.toBaseUnitAmount(new BigNumber(6), decimals), + ]; }); it('calculates the correct amount when funds unavailable', () => { signedOrder = buildSignedOrder(); remainingMakerTokenAmount = signedOrder.makerTokenAmount; const transferredAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals); transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(transferredAmount); - calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, - transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, - remainingMakerTokenAmount); + calculator = new RemainingFillableCalculator( + signedOrder, + isMakerTokenZRX, + transferrableMakerTokenAmount, + transferrableMakerFeeTokenAmount, + remainingMakerTokenAmount, + ); expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(transferrableMakerTokenAmount); }); }); describe('Ratio is not evenly divisble', () => { beforeEach(async () => { - [makerAmount, takerAmount, makerFeeAmount] = [ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals), - ZeroEx.toBaseUnitAmount(new BigNumber(7), decimals), - ZeroEx.toBaseUnitAmount(new BigNumber(7), decimals)]; + [makerAmount, takerAmount, makerFeeAmount] = [ + ZeroEx.toBaseUnitAmount(new BigNumber(3), decimals), + ZeroEx.toBaseUnitAmount(new BigNumber(7), decimals), + ZeroEx.toBaseUnitAmount(new BigNumber(7), decimals), + ]; }); it('calculates the correct amount when funds unavailable', () => { signedOrder = buildSignedOrder(); remainingMakerTokenAmount = signedOrder.makerTokenAmount; const transferredAmount = ZeroEx.toBaseUnitAmount(new BigNumber(2), decimals); transferrableMakerTokenAmount = remainingMakerTokenAmount.minus(transferredAmount); - calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, - transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, - remainingMakerTokenAmount); + calculator = new RemainingFillableCalculator( + signedOrder, + isMakerTokenZRX, + transferrableMakerTokenAmount, + transferrableMakerFeeTokenAmount, + remainingMakerTokenAmount, + ); const calculatedFillableAmount = calculator.computeRemainingMakerFillable(); expect(calculatedFillableAmount.lessThanOrEqualTo(transferrableMakerTokenAmount)).to.be.true(); expect(calculatedFillableAmount).to.be.bignumber.greaterThan(new BigNumber(0)); @@ -135,15 +171,25 @@ describe('RemainingFillableCalculator', () => { transferrableMakerTokenAmount = makerAmount.plus(makerFeeAmount); transferrableMakerFeeTokenAmount = transferrableMakerTokenAmount; remainingMakerTokenAmount = signedOrder.makerTokenAmount; - calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, - transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount); + calculator = new RemainingFillableCalculator( + signedOrder, + isMakerTokenZRX, + transferrableMakerTokenAmount, + transferrableMakerFeeTokenAmount, + remainingMakerTokenAmount, + ); expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount); }); it('calculates the correct amount when partially filled and funds available', () => { signedOrder = buildSignedOrder(); remainingMakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), decimals); - calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, - transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount); + calculator = new RemainingFillableCalculator( + signedOrder, + isMakerTokenZRX, + transferrableMakerTokenAmount, + transferrableMakerFeeTokenAmount, + remainingMakerTokenAmount, + ); expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(remainingMakerTokenAmount); }); it('calculates the amount to be 0 when all fee funds are transferred', () => { @@ -151,8 +197,13 @@ describe('RemainingFillableCalculator', () => { transferrableMakerTokenAmount = zero; transferrableMakerFeeTokenAmount = zero; remainingMakerTokenAmount = signedOrder.makerTokenAmount; - calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, - transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount); + calculator = new RemainingFillableCalculator( + signedOrder, + isMakerTokenZRX, + transferrableMakerTokenAmount, + transferrableMakerFeeTokenAmount, + remainingMakerTokenAmount, + ); expect(calculator.computeRemainingMakerFillable()).to.be.bignumber.equal(zero); }); it('calculates the correct amount when balance is less than remaining fillable', () => { @@ -164,8 +215,13 @@ describe('RemainingFillableCalculator', () => { const orderToFeeRatio = signedOrder.makerTokenAmount.dividedToIntegerBy(signedOrder.makerFee); const expectedFillableAmount = new BigNumber(450980); - calculator = new RemainingFillableCalculator(signedOrder, isMakerTokenZRX, - transferrableMakerTokenAmount, transferrableMakerFeeTokenAmount, remainingMakerTokenAmount); + calculator = new RemainingFillableCalculator( + signedOrder, + isMakerTokenZRX, + transferrableMakerTokenAmount, + transferrableMakerFeeTokenAmount, + remainingMakerTokenAmount, + ); const calculatedFillableAmount = calculator.computeRemainingMakerFillable(); const numberOfFillsInRatio = calculatedFillableAmount.dividedToIntegerBy(orderToFeeRatio); const calculatedFillableAmountPlusFees = calculatedFillableAmount.plus(numberOfFillsInRatio); diff --git a/packages/0x.js/test/subscription_test.ts b/packages/0x.js/test/subscription_test.ts index 3aeeaa109..f4c6f748f 100644 --- a/packages/0x.js/test/subscription_test.ts +++ b/packages/0x.js/test/subscription_test.ts @@ -1,37 +1,26 @@ -import BigNumber from 'bignumber.js'; -import * as chai from 'chai'; +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import 'mocha'; import * as Sinon from 'sinon'; import * as Web3 from 'web3'; -import { - ApprovalContractEventArgs, - DecodedLogEvent, - Token, - TokenEvents, - ZeroEx, - ZeroExError, -} from '../src'; -import {BlockParamLiteral, DoneCallback} from '../src/types'; +import { ApprovalContractEventArgs, DecodedLogEvent, Token, TokenEvents, ZeroEx } from '../src'; +import { DoneCallback } from '../src/types'; -import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; -import {chaiSetup} from './utils/chai_setup'; -import {constants} from './utils/constants'; -import {reportCallbackErrors} from './utils/report_callback_errors'; -import {TokenUtils} from './utils/token_utils'; -import {web3Factory} from './utils/web3_factory'; +import { chaiSetup } from './utils/chai_setup'; +import { constants } from './utils/constants'; +import { assertNodeCallbackError } from './utils/report_callback_errors'; +import { web3Factory } from './utils/web3_factory'; chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(); +const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL); describe('SubscriptionTest', () => { let web3: Web3; let zeroEx: ZeroEx; let userAddresses: string[]; let tokens: Token[]; - let tokenUtils: TokenUtils; let coinbase: string; let addressWithoutFunds: string; const config = { @@ -42,7 +31,6 @@ describe('SubscriptionTest', () => { zeroEx = new ZeroEx(web3.currentProvider, config); userAddresses = await zeroEx.getAvailableAddressesAsync(); tokens = await zeroEx.tokenRegistry.getTokensAsync(); - tokenUtils = new TokenUtils(tokens); coinbase = userAddresses[0]; addressWithoutFunds = userAddresses[1]; }); @@ -54,9 +42,7 @@ describe('SubscriptionTest', () => { }); describe('#subscribe', () => { const indexFilterValues = {}; - const shouldThrowOnInsufficientBalanceOrAllowance = true; let tokenAddress: string; - const transferAmount = new BigNumber(42); const allowanceAmount = new BigNumber(42); let stubs: Sinon.SinonStub[] = []; before(() => { @@ -71,50 +57,26 @@ describe('SubscriptionTest', () => { it('Should receive the Error when an error occurs while fetching the block', (done: DoneCallback) => { (async () => { const errMsg = 'Error fetching block'; - const callback = reportCallbackErrors(done)( - (err: Error, logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { - expect(err.message).to.be.equal(errMsg); - expect(logEvent).to.be.undefined(); - done(); - }, - ); - stubs = [ - Sinon.stub((zeroEx as any)._web3Wrapper, 'getBlockAsync') - .throws(new Error(errMsg)), - ]; - zeroEx.token.subscribe( - tokenAddress, TokenEvents.Approval, indexFilterValues, callback); + const callback = assertNodeCallbackError(done, errMsg); + stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getBlockAsync').throws(new Error(errMsg))]; + zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback); await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount); })().catch(done); }); it('Should receive the Error when an error occurs while reconciling the new block', (done: DoneCallback) => { (async () => { const errMsg = 'Error fetching logs'; - const callback = reportCallbackErrors(done)( - (err: Error, logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { - expect(err.message).to.be.equal(errMsg); - expect(logEvent).to.be.undefined(); - done(); - }, - ); - stubs = [ - Sinon.stub((zeroEx as any)._web3Wrapper, 'getLogsAsync') - .throws(new Error(errMsg)), - ]; - zeroEx.token.subscribe( - tokenAddress, TokenEvents.Approval, indexFilterValues, callback); + const callback = assertNodeCallbackError(done, errMsg); + stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getLogsAsync').throws(new Error(errMsg))]; + zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback); await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount); })().catch(done); }); it('Should allow unsubscribeAll to be called successfully after an error', (done: DoneCallback) => { (async () => { - const callback = (err: Error, logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => _.noop; - zeroEx.token.subscribe( - tokenAddress, TokenEvents.Approval, indexFilterValues, callback); - stubs = [ - Sinon.stub((zeroEx as any)._web3Wrapper, 'getBlockAsync') - .throws(new Error('JSON RPC error')), - ]; + const callback = (err: Error | null, logEvent?: DecodedLogEvent<ApprovalContractEventArgs>) => _.noop; + zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback); + stubs = [Sinon.stub((zeroEx as any)._web3Wrapper, 'getBlockAsync').throws(new Error('JSON RPC error'))]; zeroEx.token.unsubscribeAll(); done(); })().catch(done); diff --git a/packages/0x.js/test/token_registry_wrapper_test.ts b/packages/0x.js/test/token_registry_wrapper_test.ts index f1f307f3a..0a170db8f 100644 --- a/packages/0x.js/test/token_registry_wrapper_test.ts +++ b/packages/0x.js/test/token_registry_wrapper_test.ts @@ -1,28 +1,28 @@ -import {schemas, SchemaValidator} from '@0xproject/json-schemas'; +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { schemas, SchemaValidator } from '@0xproject/json-schemas'; import * as chai from 'chai'; import * as _ from 'lodash'; import 'mocha'; -import {Token, ZeroEx} from '../src'; +import { Token, ZeroEx } from '../src'; -import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; -import {chaiSetup} from './utils/chai_setup'; -import {constants} from './utils/constants'; -import {web3Factory} from './utils/web3_factory'; +import { chaiSetup } from './utils/chai_setup'; +import { constants } from './utils/constants'; +import { web3Factory } from './utils/web3_factory'; chaiSetup.configure(); const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(); +const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL); const TOKEN_REGISTRY_SIZE_AFTER_MIGRATION = 7; describe('TokenRegistryWrapper', () => { let zeroEx: ZeroEx; let tokens: Token[]; - const tokenAddressBySymbol: {[symbol: string]: string} = {}; - const tokenAddressByName: {[symbol: string]: string} = {}; - const tokenBySymbol: {[symbol: string]: Token} = {}; - const tokenByName: {[symbol: string]: Token} = {}; + const tokenAddressBySymbol: { [symbol: string]: string } = {}; + const tokenAddressByName: { [symbol: string]: string } = {}; + const tokenBySymbol: { [symbol: string]: Token } = {}; + const tokenByName: { [symbol: string]: Token } = {}; const registeredSymbol = 'ZRX'; const registeredName = '0x Protocol Token'; const unregisteredSymbol = 'MAL'; diff --git a/packages/0x.js/test/token_transfer_proxy_wrapper_test.ts b/packages/0x.js/test/token_transfer_proxy_wrapper_test.ts index 05861d112..15bd7a8ba 100644 --- a/packages/0x.js/test/token_transfer_proxy_wrapper_test.ts +++ b/packages/0x.js/test/token_transfer_proxy_wrapper_test.ts @@ -1,11 +1,10 @@ import * as chai from 'chai'; -import {ZeroEx} from '../src'; -import {TokenTransferProxyWrapper} from '../src/contract_wrappers/token_transfer_proxy_wrapper'; +import { ZeroEx } from '../src'; -import {chaiSetup} from './utils/chai_setup'; -import {constants} from './utils/constants'; -import {web3Factory} from './utils/web3_factory'; +import { chaiSetup } from './utils/chai_setup'; +import { constants } from './utils/constants'; +import { web3Factory } from './utils/web3_factory'; chaiSetup.configure(); const expect = chai.expect; diff --git a/packages/0x.js/test/token_wrapper_test.ts b/packages/0x.js/test/token_wrapper_test.ts index ae6016869..4ba1f07c5 100644 --- a/packages/0x.js/test/token_wrapper_test.ts +++ b/packages/0x.js/test/token_wrapper_test.ts @@ -1,35 +1,32 @@ -import {promisify} from '@0xproject/utils'; -import {Web3Wrapper} from '@0xproject/web3-wrapper'; -import BigNumber from 'bignumber.js'; +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { BigNumber } from '@0xproject/utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; import 'mocha'; import * as Web3 from 'web3'; import { ApprovalContractEventArgs, - ContractEvent, + BlockParamLiteral, + BlockRange, DecodedLogEvent, - LogEvent, - LogWithDecodedArgs, - SubscriptionOpts, Token, - TokenContractEventArgs, TokenEvents, TransferContractEventArgs, ZeroEx, ZeroExError, } from '../src'; -import {BlockParamLiteral, DoneCallback} from '../src/types'; +import { DoneCallback } from '../src/types'; -import {BlockchainLifecycle} from './utils/blockchain_lifecycle'; -import {chaiSetup} from './utils/chai_setup'; -import {constants} from './utils/constants'; -import {TokenUtils} from './utils/token_utils'; -import {web3Factory} from './utils/web3_factory'; +import { chaiSetup } from './utils/chai_setup'; +import { constants } from './utils/constants'; +import { reportNodeCallbackErrors } from './utils/report_callback_errors'; +import { TokenUtils } from './utils/token_utils'; +import { web3Factory } from './utils/web3_factory'; chaiSetup.configure(); const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(); +const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL); describe('TokenWrapper', () => { let web3: Web3; @@ -71,25 +68,24 @@ describe('TokenWrapper', () => { const toAddress = addressWithoutFunds; const preBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress); expect(preBalance).to.be.bignumber.equal(0); - const txHash = await zeroEx.token.transferAsync(token.address, fromAddress, toAddress, transferAmount); - const receipt = await zeroEx.awaitTransactionMinedAsync(txHash); + await zeroEx.token.transferAsync(token.address, fromAddress, toAddress, transferAmount); const postBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress); return expect(postBalance).to.be.bignumber.equal(transferAmount); }); it('should fail to transfer tokens if fromAddress has an insufficient balance', async () => { const fromAddress = addressWithoutFunds; const toAddress = coinbase; - return expect(zeroEx.token.transferAsync( - token.address, fromAddress, toAddress, transferAmount, - )).to.be.rejectedWith(ZeroExError.InsufficientBalanceForTransfer); + return expect( + zeroEx.token.transferAsync(token.address, fromAddress, toAddress, transferAmount), + ).to.be.rejectedWith(ZeroExError.InsufficientBalanceForTransfer); }); it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => { const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065'; const fromAddress = coinbase; const toAddress = coinbase; - return expect(zeroEx.token.transferAsync( - nonExistentTokenAddress, fromAddress, toAddress, transferAmount, - )).to.be.rejectedWith(ZeroExError.TokenContractDoesNotExist); + return expect( + zeroEx.token.transferAsync(nonExistentTokenAddress, fromAddress, toAddress, transferAmount), + ).to.be.rejectedWith(ZeroExError.TokenContractDoesNotExist); }); }); describe('#transferFromAsync', () => { @@ -108,24 +104,22 @@ describe('TokenWrapper', () => { const fromAddressBalance = await zeroEx.token.getBalanceAsync(token.address, fromAddress); expect(fromAddressBalance).to.be.bignumber.greaterThan(transferAmount); - const fromAddressAllowance = await zeroEx.token.getAllowanceAsync(token.address, fromAddress, - toAddress); + const fromAddressAllowance = await zeroEx.token.getAllowanceAsync(token.address, fromAddress, toAddress); expect(fromAddressAllowance).to.be.bignumber.equal(0); - return expect(zeroEx.token.transferFromAsync( - token.address, fromAddress, toAddress, senderAddress, transferAmount, - )).to.be.rejectedWith(ZeroExError.InsufficientAllowanceForTransfer); + return expect( + zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount), + ).to.be.rejectedWith(ZeroExError.InsufficientAllowanceForTransfer); }); - it('[regression] should fail to transfer tokens if set allowance for toAddress instead of senderAddress', - async () => { + it('[regression] should fail to transfer tokens if set allowance for toAddress instead of senderAddress', async () => { const fromAddress = coinbase; const transferAmount = new BigNumber(42); await zeroEx.token.setAllowanceAsync(token.address, fromAddress, toAddress, transferAmount); - return expect(zeroEx.token.transferFromAsync( - token.address, fromAddress, toAddress, senderAddress, transferAmount, - )).to.be.rejectedWith(ZeroExError.InsufficientAllowanceForTransfer); + return expect( + zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount), + ).to.be.rejectedWith(ZeroExError.InsufficientAllowanceForTransfer); }); it('should fail to transfer tokens if fromAddress has insufficient balance', async () => { const fromAddress = addressWithoutFunds; @@ -135,13 +129,16 @@ describe('TokenWrapper', () => { expect(fromAddressBalance).to.be.bignumber.equal(0); await zeroEx.token.setAllowanceAsync(token.address, fromAddress, senderAddress, transferAmount); - const fromAddressAllowance = await zeroEx.token.getAllowanceAsync(token.address, fromAddress, - senderAddress); + const fromAddressAllowance = await zeroEx.token.getAllowanceAsync( + token.address, + fromAddress, + senderAddress, + ); expect(fromAddressAllowance).to.be.bignumber.equal(transferAmount); - return expect(zeroEx.token.transferFromAsync( - token.address, fromAddress, toAddress, senderAddress, transferAmount, - )).to.be.rejectedWith(ZeroExError.InsufficientBalanceForTransfer); + return expect( + zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount), + ).to.be.rejectedWith(ZeroExError.InsufficientBalanceForTransfer); }); it('should successfully transfer tokens', async () => { const fromAddress = coinbase; @@ -152,17 +149,22 @@ describe('TokenWrapper', () => { const transferAmount = new BigNumber(42); await zeroEx.token.setAllowanceAsync(token.address, fromAddress, senderAddress, transferAmount); - await zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, - transferAmount); + await zeroEx.token.transferFromAsync(token.address, fromAddress, toAddress, senderAddress, transferAmount); const postBalance = await zeroEx.token.getBalanceAsync(token.address, toAddress); return expect(postBalance).to.be.bignumber.equal(transferAmount); }); it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => { const fromAddress = coinbase; const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065'; - return expect(zeroEx.token.transferFromAsync( - nonExistentTokenAddress, fromAddress, toAddress, senderAddress, new BigNumber(42), - )).to.be.rejectedWith(ZeroExError.TokenContractDoesNotExist); + return expect( + zeroEx.token.transferFromAsync( + nonExistentTokenAddress, + fromAddress, + toAddress, + senderAddress, + new BigNumber(42), + ), + ).to.be.rejectedWith(ZeroExError.TokenContractDoesNotExist); }); }); describe('#getBalanceAsync', () => { @@ -177,8 +179,9 @@ describe('TokenWrapper', () => { it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => { const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065'; const ownerAddress = coinbase; - return expect(zeroEx.token.getBalanceAsync(nonExistentTokenAddress, ownerAddress)) - .to.be.rejectedWith(ZeroExError.TokenContractDoesNotExist); + return expect(zeroEx.token.getBalanceAsync(nonExistentTokenAddress, ownerAddress)).to.be.rejectedWith( + ZeroExError.TokenContractDoesNotExist, + ); }); it('should return a balance of 0 for a non-existent owner address', async () => { const token = tokens[0]; @@ -196,22 +199,25 @@ describe('TokenWrapper', () => { zeroExWithoutAccounts = new ZeroEx(web3WithoutAccounts.currentProvider, config); }); it('should return balance even when called with Web3 provider instance without addresses', async () => { - const token = tokens[0]; - const ownerAddress = coinbase; - const balance = await zeroExWithoutAccounts.token.getBalanceAsync(token.address, ownerAddress); - const expectedBalance = new BigNumber('1000000000000000000000000000'); - return expect(balance).to.be.bignumber.equal(expectedBalance); + const token = tokens[0]; + const ownerAddress = coinbase; + const balance = await zeroExWithoutAccounts.token.getBalanceAsync(token.address, ownerAddress); + const expectedBalance = new BigNumber('1000000000000000000000000000'); + return expect(balance).to.be.bignumber.equal(expectedBalance); }); }); }); describe('#setAllowanceAsync', () => { - it('should set the spender\'s allowance', async () => { + it("should set the spender's allowance", async () => { const token = tokens[0]; const ownerAddress = coinbase; const spenderAddress = addressWithoutFunds; - const allowanceBeforeSet = await zeroEx.token.getAllowanceAsync(token.address, ownerAddress, - spenderAddress); + const allowanceBeforeSet = await zeroEx.token.getAllowanceAsync( + token.address, + ownerAddress, + spenderAddress, + ); const expectedAllowanceBeforeAllowanceSet = new BigNumber(0); expect(allowanceBeforeSet).to.be.bignumber.equal(expectedAllowanceBeforeAllowanceSet); @@ -224,7 +230,7 @@ describe('TokenWrapper', () => { }); }); describe('#setUnlimitedAllowanceAsync', () => { - it('should set the unlimited spender\'s allowance', async () => { + it("should set the unlimited spender's allowance", async () => { const token = tokens[0]; const ownerAddress = coinbase; const spenderAddress = addressWithoutFunds; @@ -246,10 +252,18 @@ describe('TokenWrapper', () => { ); await zeroEx.token.transferFromAsync( - zrx.address, coinbase, userWithNormalAllowance, userWithNormalAllowance, transferAmount, + zrx.address, + coinbase, + userWithNormalAllowance, + userWithNormalAllowance, + transferAmount, ); await zeroEx.token.transferFromAsync( - zrx.address, coinbase, userWithUnlimitedAllowance, userWithUnlimitedAllowance, transferAmount, + zrx.address, + coinbase, + userWithUnlimitedAllowance, + userWithUnlimitedAllowance, + transferAmount, ); const finalBalanceWithNormalAllowance = await web3Wrapper.getBalanceInWeiAsync(userWithNormalAllowance); @@ -305,7 +319,9 @@ describe('TokenWrapper', () => { await zeroEx.token.setAllowanceAsync(token.address, ownerAddress, spenderAddress, amountInBaseUnits); const allowance = await zeroExWithoutAccounts.token.getAllowanceAsync( - token.address, ownerAddress, spenderAddress, + token.address, + ownerAddress, + spenderAddress, ); const expectedAllowance = amountInBaseUnits; return expect(allowance).to.be.bignumber.equal(expectedAllowance); @@ -354,7 +370,6 @@ describe('TokenWrapper', () => { }); describe('#subscribe', () => { const indexFilterValues = {}; - const shouldThrowOnInsufficientBalanceOrAllowance = true; let tokenAddress: string; const transferAmount = new BigNumber(42); const allowanceAmount = new BigNumber(42); @@ -372,65 +387,66 @@ describe('TokenWrapper', () => { // Source: https://github.com/mochajs/mocha/issues/2407 it('Should receive the Transfer event when tokens are transfered', (done: DoneCallback) => { (async () => { - const callback = (err: Error, logEvent: DecodedLogEvent<TransferContractEventArgs>) => { - expect(logEvent).to.not.be.undefined(); - expect(logEvent.isRemoved).to.be.false(); - expect(logEvent.log.logIndex).to.be.equal(0); - expect(logEvent.log.transactionIndex).to.be.equal(0); - expect(logEvent.log.blockNumber).to.be.a('number'); - const args = logEvent.log.args; - expect(args._from).to.be.equal(coinbase); - expect(args._to).to.be.equal(addressWithoutFunds); - expect(args._value).to.be.bignumber.equal(transferAmount); - done(); - }; - zeroEx.token.subscribe( - tokenAddress, TokenEvents.Transfer, indexFilterValues, callback); + const callback = reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<TransferContractEventArgs>) => { + expect(logEvent.isRemoved).to.be.false(); + expect(logEvent.log.logIndex).to.be.equal(0); + expect(logEvent.log.transactionIndex).to.be.equal(0); + expect(logEvent.log.blockNumber).to.be.a('number'); + const args = logEvent.log.args; + expect(args._from).to.be.equal(coinbase); + expect(args._to).to.be.equal(addressWithoutFunds); + expect(args._value).to.be.bignumber.equal(transferAmount); + }, + ); + zeroEx.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callback); await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount); })().catch(done); }); it('Should receive the Approval event when allowance is being set', (done: DoneCallback) => { (async () => { - const callback = (err: Error, logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { - expect(logEvent).to.not.be.undefined(); - expect(logEvent.isRemoved).to.be.false(); - const args = logEvent.log.args; - expect(args._owner).to.be.equal(coinbase); - expect(args._spender).to.be.equal(addressWithoutFunds); - expect(args._value).to.be.bignumber.equal(allowanceAmount); - done(); - }; - zeroEx.token.subscribe( - tokenAddress, TokenEvents.Approval, indexFilterValues, callback); + const callback = reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { + expect(logEvent).to.not.be.undefined(); + expect(logEvent.isRemoved).to.be.false(); + const args = logEvent.log.args; + expect(args._owner).to.be.equal(coinbase); + expect(args._spender).to.be.equal(addressWithoutFunds); + expect(args._value).to.be.bignumber.equal(allowanceAmount); + }, + ); + zeroEx.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback); await zeroEx.token.setAllowanceAsync(tokenAddress, coinbase, addressWithoutFunds, allowanceAmount); })().catch(done); }); it('Outstanding subscriptions are cancelled when zeroEx.setProvider called', (done: DoneCallback) => { (async () => { - const callbackNeverToBeCalled = (err: Error, logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { - done(new Error('Expected this subscription to have been cancelled')); - }; - zeroEx.token.subscribe( - tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackNeverToBeCalled, + const callbackNeverToBeCalled = reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { + done(new Error('Expected this subscription to have been cancelled')); + }, ); - const callbackToBeCalled = (err: Error, logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { - done(); - }; + zeroEx.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackNeverToBeCalled); + const callbackToBeCalled = reportNodeCallbackErrors(done)(); const newProvider = web3Factory.getRpcProvider(); zeroEx.setProvider(newProvider, constants.TESTRPC_NETWORK_ID); - zeroEx.token.subscribe( - tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackToBeCalled, - ); + zeroEx.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackToBeCalled); await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount); })().catch(done); }); it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => { (async () => { - const callbackNeverToBeCalled = (err: Error, logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { - done(new Error('Expected this subscription to have been cancelled')); - }; + const callbackNeverToBeCalled = reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { + done(new Error('Expected this subscription to have been cancelled')); + }, + ); const subscriptionToken = zeroEx.token.subscribe( - tokenAddress, TokenEvents.Transfer, indexFilterValues, callbackNeverToBeCalled); + tokenAddress, + TokenEvents.Transfer, + indexFilterValues, + callbackNeverToBeCalled, + ); zeroEx.token.unsubscribe(subscriptionToken); await zeroEx.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount); done(); @@ -440,7 +456,7 @@ describe('TokenWrapper', () => { describe('#getLogsAsync', () => { let tokenAddress: string; let tokenTransferProxyAddress: string; - const subscriptionOpts: SubscriptionOpts = { + const blockRange: BlockRange = { fromBlock: 0, toBlock: BlockParamLiteral.Latest, }; @@ -456,7 +472,10 @@ describe('TokenWrapper', () => { const eventName = TokenEvents.Approval; const indexFilterValues = {}; const logs = await zeroEx.token.getLogsAsync<ApprovalContractEventArgs>( - tokenAddress, eventName, subscriptionOpts, indexFilterValues, + tokenAddress, + eventName, + blockRange, + indexFilterValues, ); expect(logs).to.have.length(1); const args = logs[0].args; @@ -471,7 +490,10 @@ describe('TokenWrapper', () => { const differentEventName = TokenEvents.Transfer; const indexFilterValues = {}; const logs = await zeroEx.token.getLogsAsync( - tokenAddress, differentEventName, subscriptionOpts, indexFilterValues, + tokenAddress, + differentEventName, + blockRange, + indexFilterValues, ); expect(logs).to.have.length(0); }); @@ -485,7 +507,10 @@ describe('TokenWrapper', () => { _owner: coinbase, }; const logs = await zeroEx.token.getLogsAsync<ApprovalContractEventArgs>( - tokenAddress, eventName, subscriptionOpts, indexFilterValues, + tokenAddress, + eventName, + blockRange, + indexFilterValues, ); expect(logs).to.have.length(1); const args = logs[0].args; @@ -493,3 +518,4 @@ describe('TokenWrapper', () => { }); }); }); +// tslint:disable:max-file-line-count diff --git a/packages/0x.js/test/utils/blockchain_lifecycle.ts b/packages/0x.js/test/utils/blockchain_lifecycle.ts deleted file mode 100644 index 9a44ccd6f..000000000 --- a/packages/0x.js/test/utils/blockchain_lifecycle.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {RPC} from './rpc'; - -export class BlockchainLifecycle { - private rpc: RPC; - private snapshotIdsStack: number[]; - constructor() { - this.rpc = new RPC(); - this.snapshotIdsStack = []; - } - // TODO: In order to run these tests on an actual node, we should check if we are running against - // TestRPC, if so, use snapshots, otherwise re-deploy contracts before every test - public async startAsync(): Promise<void> { - const snapshotId = await this.rpc.takeSnapshotAsync(); - this.snapshotIdsStack.push(snapshotId); - } - public async revertAsync(): Promise<void> { - const snapshotId = this.snapshotIdsStack.pop() as number; - const didRevert = await this.rpc.revertSnapshotAsync(snapshotId); - if (!didRevert) { - throw new Error(`Snapshot with id #${snapshotId} failed to revert`); - } - } - public async mineABlock(): Promise<void> { - await this.rpc.mineBlockAsync(); - } -} diff --git a/packages/0x.js/test/utils/constants.ts b/packages/0x.js/test/utils/constants.ts index 75fdf49c9..a9e665c25 100644 --- a/packages/0x.js/test/utils/constants.ts +++ b/packages/0x.js/test/utils/constants.ts @@ -1,12 +1,11 @@ export const constants = { NULL_ADDRESS: '0x0000000000000000000000000000000000000000', - RPC_HOST: 'localhost', - RPC_PORT: 8545, + RPC_URL: 'http://localhost:8545', ROPSTEN_NETWORK_ID: 3, KOVAN_NETWORK_ID: 42, TESTRPC_NETWORK_ID: 50, - KOVAN_RPC_URL: 'https://kovan.infura.io', - ROPSTEN_RPC_URL: 'https://ropsten.infura.io', + KOVAN_RPC_URL: 'https://kovan.infura.io/', + ROPSTEN_RPC_URL: 'https://ropsten.infura.io/', ZRX_DECIMALS: 18, GAS_ESTIMATE: 500000, }; diff --git a/packages/0x.js/test/utils/fill_scenarios.ts b/packages/0x.js/test/utils/fill_scenarios.ts index 090493935..1a61487f4 100644 --- a/packages/0x.js/test/utils/fill_scenarios.ts +++ b/packages/0x.js/test/utils/fill_scenarios.ts @@ -1,116 +1,202 @@ -import BigNumber from 'bignumber.js'; +import { BigNumber } from '@0xproject/utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; -import {SignedOrder, Token, ZeroEx} from '../../src'; -import {orderFactory} from '../utils/order_factory'; +import { SignedOrder, Token, ZeroEx } from '../../src'; +import { artifacts } from '../../src/artifacts'; +import { DummyTokenContract } from '../../src/contract_wrappers/generated/dummy_token'; +import { orderFactory } from '../utils/order_factory'; -import {constants} from './constants'; +import { constants } from './constants'; + +const INITIAL_COINBASE_TOKEN_SUPPLY_IN_UNITS = new BigNumber(100); export class FillScenarios { - private zeroEx: ZeroEx; - private userAddresses: string[]; - private tokens: Token[]; - private coinbase: string; - private zrxTokenAddress: string; - private exchangeContractAddress: string; - constructor(zeroEx: ZeroEx, userAddresses: string[], - tokens: Token[], zrxTokenAddress: string, exchangeContractAddress: string) { - this.zeroEx = zeroEx; - this.userAddresses = userAddresses; - this.tokens = tokens; - this.coinbase = userAddresses[0]; - this.zrxTokenAddress = zrxTokenAddress; - this.exchangeContractAddress = exchangeContractAddress; + private _zeroEx: ZeroEx; + private _userAddresses: string[]; + private _tokens: Token[]; + private _coinbase: string; + private _zrxTokenAddress: string; + private _exchangeContractAddress: string; + constructor( + zeroEx: ZeroEx, + userAddresses: string[], + tokens: Token[], + zrxTokenAddress: string, + exchangeContractAddress: string, + ) { + this._zeroEx = zeroEx; + this._userAddresses = userAddresses; + this._tokens = tokens; + this._coinbase = userAddresses[0]; + this._zrxTokenAddress = zrxTokenAddress; + this._exchangeContractAddress = exchangeContractAddress; + } + public async initTokenBalancesAsync() { + const web3Wrapper = (this._zeroEx as any)._web3Wrapper as Web3Wrapper; + for (const token of this._tokens) { + if (token.symbol !== 'ZRX' && token.symbol !== 'WETH') { + const contractInstance = web3Wrapper.getContractInstance( + artifacts.DummyTokenArtifact.abi, + token.address, + ); + const defaults = {}; + const dummyToken = new DummyTokenContract(contractInstance, defaults); + const tokenSupply = ZeroEx.toBaseUnitAmount(INITIAL_COINBASE_TOKEN_SUPPLY_IN_UNITS, token.decimals); + const txHash = await dummyToken.setBalance.sendTransactionAsync(this._coinbase, tokenSupply, { + from: this._coinbase, + }); + await this._zeroEx.awaitTransactionMinedAsync(txHash); + } + } } - public async createFillableSignedOrderAsync(makerTokenAddress: string, takerTokenAddress: string, - makerAddress: string, takerAddress: string, - fillableAmount: BigNumber, - expirationUnixTimestampSec?: BigNumber): - Promise<SignedOrder> { + public async createFillableSignedOrderAsync( + makerTokenAddress: string, + takerTokenAddress: string, + makerAddress: string, + takerAddress: string, + fillableAmount: BigNumber, + expirationUnixTimestampSec?: BigNumber, + ): Promise<SignedOrder> { return this.createAsymmetricFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, - fillableAmount, fillableAmount, expirationUnixTimestampSec, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, + fillableAmount, + expirationUnixTimestampSec, ); } public async createFillableSignedOrderWithFeesAsync( - makerTokenAddress: string, takerTokenAddress: string, - makerFee: BigNumber, takerFee: BigNumber, - makerAddress: string, takerAddress: string, + makerTokenAddress: string, + takerTokenAddress: string, + makerFee: BigNumber, + takerFee: BigNumber, + makerAddress: string, + takerAddress: string, fillableAmount: BigNumber, - feeRecepient: string, expirationUnixTimestampSec?: BigNumber, + feeRecepient: string, + expirationUnixTimestampSec?: BigNumber, ): Promise<SignedOrder> { - return this.createAsymmetricFillableSignedOrderWithFeesAsync( - makerTokenAddress, takerTokenAddress, makerFee, takerFee, makerAddress, takerAddress, - fillableAmount, fillableAmount, feeRecepient, expirationUnixTimestampSec, + return this._createAsymmetricFillableSignedOrderWithFeesAsync( + makerTokenAddress, + takerTokenAddress, + makerFee, + takerFee, + makerAddress, + takerAddress, + fillableAmount, + fillableAmount, + feeRecepient, + expirationUnixTimestampSec, ); } public async createAsymmetricFillableSignedOrderAsync( - makerTokenAddress: string, takerTokenAddress: string, makerAddress: string, takerAddress: string, - makerFillableAmount: BigNumber, takerFillableAmount: BigNumber, - expirationUnixTimestampSec?: BigNumber): Promise<SignedOrder> { + makerTokenAddress: string, + takerTokenAddress: string, + makerAddress: string, + takerAddress: string, + makerFillableAmount: BigNumber, + takerFillableAmount: BigNumber, + expirationUnixTimestampSec?: BigNumber, + ): Promise<SignedOrder> { const makerFee = new BigNumber(0); const takerFee = new BigNumber(0); const feeRecepient = constants.NULL_ADDRESS; - return this.createAsymmetricFillableSignedOrderWithFeesAsync( - makerTokenAddress, takerTokenAddress, makerFee, takerFee, makerAddress, takerAddress, - makerFillableAmount, takerFillableAmount, feeRecepient, expirationUnixTimestampSec, + return this._createAsymmetricFillableSignedOrderWithFeesAsync( + makerTokenAddress, + takerTokenAddress, + makerFee, + takerFee, + makerAddress, + takerAddress, + makerFillableAmount, + takerFillableAmount, + feeRecepient, + expirationUnixTimestampSec, ); } - public async createPartiallyFilledSignedOrderAsync(makerTokenAddress: string, takerTokenAddress: string, - takerAddress: string, fillableAmount: BigNumber, - partialFillAmount: BigNumber) { - const [makerAddress] = this.userAddresses; + public async createPartiallyFilledSignedOrderAsync( + makerTokenAddress: string, + takerTokenAddress: string, + takerAddress: string, + fillableAmount: BigNumber, + partialFillAmount: BigNumber, + ) { + const [makerAddress] = this._userAddresses; const signedOrder = await this.createAsymmetricFillableSignedOrderAsync( - makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, - fillableAmount, fillableAmount, + makerTokenAddress, + takerTokenAddress, + makerAddress, + takerAddress, + fillableAmount, + fillableAmount, ); const shouldThrowOnInsufficientBalanceOrAllowance = false; - await this.zeroEx.exchange.fillOrderAsync( - signedOrder, partialFillAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, + await this._zeroEx.exchange.fillOrderAsync( + signedOrder, + partialFillAmount, + shouldThrowOnInsufficientBalanceOrAllowance, + takerAddress, ); return signedOrder; } - private async createAsymmetricFillableSignedOrderWithFeesAsync( - makerTokenAddress: string, takerTokenAddress: string, - makerFee: BigNumber, takerFee: BigNumber, - makerAddress: string, takerAddress: string, - makerFillableAmount: BigNumber, takerFillableAmount: BigNumber, - feeRecepient: string, expirationUnixTimestampSec?: BigNumber): Promise<SignedOrder> { - + private async _createAsymmetricFillableSignedOrderWithFeesAsync( + makerTokenAddress: string, + takerTokenAddress: string, + makerFee: BigNumber, + takerFee: BigNumber, + makerAddress: string, + takerAddress: string, + makerFillableAmount: BigNumber, + takerFillableAmount: BigNumber, + feeRecepient: string, + expirationUnixTimestampSec?: BigNumber, + ): Promise<SignedOrder> { await Promise.all([ - this.increaseBalanceAndAllowanceAsync(makerTokenAddress, makerAddress, makerFillableAmount), - this.increaseBalanceAndAllowanceAsync(takerTokenAddress, takerAddress, takerFillableAmount), + this._increaseBalanceAndAllowanceAsync(makerTokenAddress, makerAddress, makerFillableAmount), + this._increaseBalanceAndAllowanceAsync(takerTokenAddress, takerAddress, takerFillableAmount), ]); await Promise.all([ - this.increaseBalanceAndAllowanceAsync(this.zrxTokenAddress, makerAddress, makerFee), - this.increaseBalanceAndAllowanceAsync(this.zrxTokenAddress, takerAddress, takerFee), + this._increaseBalanceAndAllowanceAsync(this._zrxTokenAddress, makerAddress, makerFee), + this._increaseBalanceAndAllowanceAsync(this._zrxTokenAddress, takerAddress, takerFee), ]); - const signedOrder = await orderFactory.createSignedOrderAsync(this.zeroEx, - makerAddress, takerAddress, makerFee, takerFee, - makerFillableAmount, makerTokenAddress, takerFillableAmount, takerTokenAddress, - this.exchangeContractAddress, feeRecepient, expirationUnixTimestampSec); + const signedOrder = await orderFactory.createSignedOrderAsync( + this._zeroEx, + makerAddress, + takerAddress, + makerFee, + takerFee, + makerFillableAmount, + makerTokenAddress, + takerFillableAmount, + takerTokenAddress, + this._exchangeContractAddress, + feeRecepient, + expirationUnixTimestampSec, + ); return signedOrder; } - private async increaseBalanceAndAllowanceAsync( - tokenAddress: string, address: string, amount: BigNumber): Promise<void> { + private async _increaseBalanceAndAllowanceAsync( + tokenAddress: string, + address: string, + amount: BigNumber, + ): Promise<void> { if (amount.isZero() || address === ZeroEx.NULL_ADDRESS) { return; // noop } await Promise.all([ - this.increaseBalanceAsync(tokenAddress, address, amount), - this.increaseAllowanceAsync(tokenAddress, address, amount), + this._increaseBalanceAsync(tokenAddress, address, amount), + this._increaseAllowanceAsync(tokenAddress, address, amount), ]); } - private async increaseBalanceAsync( - tokenAddress: string, address: string, amount: BigNumber): Promise<void> { - await this.zeroEx.token.transferAsync(tokenAddress, this.coinbase, address, amount); + private async _increaseBalanceAsync(tokenAddress: string, address: string, amount: BigNumber): Promise<void> { + await this._zeroEx.token.transferAsync(tokenAddress, this._coinbase, address, amount); } - private async increaseAllowanceAsync( - tokenAddress: string, address: string, amount: BigNumber): Promise<void> { - const oldMakerAllowance = await this.zeroEx.token.getProxyAllowanceAsync(tokenAddress, address); + private async _increaseAllowanceAsync(tokenAddress: string, address: string, amount: BigNumber): Promise<void> { + const oldMakerAllowance = await this._zeroEx.token.getProxyAllowanceAsync(tokenAddress, address); const newMakerAllowance = oldMakerAllowance.plus(amount); - await this.zeroEx.token.setProxyAllowanceAsync( - tokenAddress, address, newMakerAllowance, - ); + await this._zeroEx.token.setProxyAllowanceAsync(tokenAddress, address, newMakerAllowance); } } diff --git a/packages/0x.js/test/utils/order_factory.ts b/packages/0x.js/test/utils/order_factory.ts index 41d93e340..60a7c5fb2 100644 --- a/packages/0x.js/test/utils/order_factory.ts +++ b/packages/0x.js/test/utils/order_factory.ts @@ -1,7 +1,7 @@ -import BigNumber from 'bignumber.js'; +import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; -import {SignedOrder, ZeroEx} from '../../src'; +import { SignedOrder, ZeroEx } from '../../src'; export const orderFactory = { async createSignedOrderAsync( @@ -16,11 +16,12 @@ export const orderFactory = { takerTokenAddress: string, exchangeContractAddress: string, feeRecipient: string, - expirationUnixTimestampSecIfExists?: BigNumber): Promise<SignedOrder> { + expirationUnixTimestampSecIfExists?: BigNumber, + ): Promise<SignedOrder> { const defaultExpirationUnixTimestampSec = new BigNumber(2524604400); // Close to infinite - const expirationUnixTimestampSec = _.isUndefined(expirationUnixTimestampSecIfExists) ? - defaultExpirationUnixTimestampSec : - expirationUnixTimestampSecIfExists; + const expirationUnixTimestampSec = _.isUndefined(expirationUnixTimestampSecIfExists) + ? defaultExpirationUnixTimestampSec + : expirationUnixTimestampSecIfExists; const order = { maker, taker, @@ -37,7 +38,7 @@ export const orderFactory = { }; const orderHash = ZeroEx.getOrderHashHex(order); const ecSignature = await zeroEx.signOrderHashAsync(orderHash, maker); - const signedOrder: SignedOrder = _.assign(order, {ecSignature}); + const signedOrder: SignedOrder = _.assign(order, { ecSignature }); return signedOrder; }, }; diff --git a/packages/0x.js/test/utils/report_callback_errors.ts b/packages/0x.js/test/utils/report_callback_errors.ts index 8a8f4d966..27c9745c9 100644 --- a/packages/0x.js/test/utils/report_callback_errors.ts +++ b/packages/0x.js/test/utils/report_callback_errors.ts @@ -1,10 +1,22 @@ +import * as chai from 'chai'; +import * as _ from 'lodash'; + import { DoneCallback } from '../../src/types'; -export const reportCallbackErrors = (done: DoneCallback) => { - return (f: (...args: any[]) => void) => { - const wrapped = async (...args: any[]) => { +const expect = chai.expect; + +export const reportNoErrorCallbackErrors = (done: DoneCallback, expectToBeCalledOnce = true) => { + return <T>(f?: (value: T) => void) => { + const wrapped = (value: T) => { + if (_.isUndefined(f)) { + done(); + return; + } try { - f(...args); + f(value); + if (expectToBeCalledOnce) { + done(); + } } catch (err) { done(err); } @@ -12,3 +24,43 @@ export const reportCallbackErrors = (done: DoneCallback) => { return wrapped; }; }; + +export const reportNodeCallbackErrors = (done: DoneCallback, expectToBeCalledOnce = true) => { + return <T>(f?: (value: T) => void) => { + const wrapped = (error: Error | null, value: T | undefined) => { + if (!_.isNull(error)) { + done(error); + } else { + if (_.isUndefined(f)) { + done(); + return; + } + try { + f(value as T); + if (expectToBeCalledOnce) { + done(); + } + } catch (err) { + done(err); + } + } + }; + return wrapped; + }; +}; + +export const assertNodeCallbackError = (done: DoneCallback, errMsg: string) => { + const wrapped = <T>(error: Error | null, value: T | undefined) => { + if (_.isNull(error)) { + done(new Error('Expected callback to receive an error')); + } else { + try { + expect(error.message).to.be.equal(errMsg); + done(); + } catch (err) { + done(err); + } + } + }; + return wrapped; +}; diff --git a/packages/0x.js/test/utils/rpc.ts b/packages/0x.js/test/utils/rpc.ts deleted file mode 100644 index 309a96d5e..000000000 --- a/packages/0x.js/test/utils/rpc.ts +++ /dev/null @@ -1,58 +0,0 @@ -import * as ethUtil from 'ethereumjs-util'; -import * as request from 'request-promise-native'; - -import {constants} from './constants'; - -export class RPC { - private host: string; - private port: number; - private id: number; - constructor() { - this.host = constants.RPC_HOST; - this.port = constants.RPC_PORT; - this.id = 0; - } - public async takeSnapshotAsync(): Promise<number> { - const method = 'evm_snapshot'; - const params: any[] = []; - const payload = this.toPayload(method, params); - const snapshotIdHex = await this.sendAsync(payload); - const snapshotId = ethUtil.bufferToInt(ethUtil.toBuffer(snapshotIdHex)); - return snapshotId; - } - public async revertSnapshotAsync(snapshotId: number): Promise<boolean> { - const method = 'evm_revert'; - const params = [snapshotId]; - const payload = this.toPayload(method, params); - const didRevert = await this.sendAsync(payload); - return didRevert; - } - public async mineBlockAsync(): Promise<void> { - const method = 'evm_mine'; - const params: any[] = []; - const payload = this.toPayload(method, params); - await this.sendAsync(payload); - } - private toPayload(method: string, params: any[] = []): string { - const payload = JSON.stringify({ - id: this.id, - method, - params, - }); - this.id += 1; - return payload; - } - private async sendAsync(payload: string): Promise<any> { - const opts = { - method: 'POST', - uri: `http://${this.host}:${this.port}`, - body: payload, - headers: { - 'content-type': 'application/json', - }, - }; - const bodyString = await request(opts); - const body = JSON.parse(bodyString); - return body.result; - } -} diff --git a/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts b/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts index e5e279873..53f2be83d 100644 --- a/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts +++ b/packages/0x.js/test/utils/subproviders/empty_wallet_subprovider.ts @@ -1,4 +1,4 @@ -import {JSONRPCPayload} from '../../../src/types'; +import { JSONRPCPayload } from '../../../src/types'; /* * This class implements the web3-provider-engine subprovider interface and returns @@ -8,7 +8,7 @@ import {JSONRPCPayload} from '../../../src/types'; export class EmptyWalletSubprovider { // This method needs to be here to satisfy the interface but linter wants it to be static. // tslint:disable-next-line:prefer-function-over-method - public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error|null, result: any) => void) { + public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error | null, result: any) => void) { switch (payload.method) { case 'eth_accounts': end(null, []); diff --git a/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts b/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts index 059163f2e..e1113a851 100644 --- a/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts +++ b/packages/0x.js/test/utils/subproviders/fake_gas_estimate_subprovider.ts @@ -1,4 +1,4 @@ -import {JSONRPCPayload} from '../../../src/types'; +import { JSONRPCPayload } from '../../../src/types'; /* * This class implements the web3-provider-engine subprovider interface and returns @@ -9,16 +9,16 @@ import {JSONRPCPayload} from '../../../src/types'; * Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js */ export class FakeGasEstimateSubprovider { - private constantGasAmount: number; + private _constantGasAmount: number; constructor(constantGasAmount: number) { - this.constantGasAmount = constantGasAmount; + this._constantGasAmount = constantGasAmount; } // This method needs to be here to satisfy the interface but linter wants it to be static. // tslint:disable-next-line:prefer-function-over-method - public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error|null, result: any) => void) { + public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error | null, result: any) => void) { switch (payload.method) { case 'eth_estimateGas': - end(null, this.constantGasAmount); + end(null, this._constantGasAmount); return; default: diff --git a/packages/0x.js/test/utils/token_utils.ts b/packages/0x.js/test/utils/token_utils.ts index 9c20f52a1..d3fc22ff4 100644 --- a/packages/0x.js/test/utils/token_utils.ts +++ b/packages/0x.js/test/utils/token_utils.ts @@ -1,25 +1,33 @@ import * as _ from 'lodash'; -import {InternalZeroExError, Token} from '../../src/types'; +import { InternalZeroExError, Token } from '../../src/types'; const PROTOCOL_TOKEN_SYMBOL = 'ZRX'; +const WETH_TOKEN_SYMBOL = 'WETH'; export class TokenUtils { - private tokens: Token[]; + private _tokens: Token[]; constructor(tokens: Token[]) { - this.tokens = tokens; + this._tokens = tokens; } public getProtocolTokenOrThrow(): Token { - const zrxToken = _.find(this.tokens, {symbol: PROTOCOL_TOKEN_SYMBOL}); + const zrxToken = _.find(this._tokens, { symbol: PROTOCOL_TOKEN_SYMBOL }); if (_.isUndefined(zrxToken)) { throw new Error(InternalZeroExError.ZrxNotInTokenRegistry); } return zrxToken; } - public getNonProtocolTokens(): Token[] { - const nonProtocolTokens = _.filter(this.tokens, token => { - return token.symbol !== PROTOCOL_TOKEN_SYMBOL; + public getWethTokenOrThrow(): Token { + const wethToken = _.find(this._tokens, { symbol: WETH_TOKEN_SYMBOL }); + if (_.isUndefined(wethToken)) { + throw new Error(InternalZeroExError.WethNotInTokenRegistry); + } + return wethToken; + } + public getDummyTokens(): Token[] { + const dummyTokens = _.filter(this._tokens, token => { + return !_.includes([PROTOCOL_TOKEN_SYMBOL, WETH_TOKEN_SYMBOL], token.symbol); }); - return nonProtocolTokens; + return dummyTokens; } } diff --git a/packages/0x.js/test/utils/web3_factory.ts b/packages/0x.js/test/utils/web3_factory.ts index da4828943..26c26e03d 100644 --- a/packages/0x.js/test/utils/web3_factory.ts +++ b/packages/0x.js/test/utils/web3_factory.ts @@ -3,14 +3,20 @@ // we are not running in a browser env. // Filed issue: https://github.com/ethereum/web3.js/issues/844 (global as any).XMLHttpRequest = undefined; -import * as Web3 from 'web3'; import ProviderEngine = require('web3-provider-engine'); import RpcSubprovider = require('web3-provider-engine/subproviders/rpc'); -import {EmptyWalletSubprovider} from './subproviders/empty_wallet_subprovider'; -import {FakeGasEstimateSubprovider} from './subproviders/fake_gas_estimate_subprovider'; +import { EmptyWalletSubprovider } from './subproviders/empty_wallet_subprovider'; +import { FakeGasEstimateSubprovider } from './subproviders/fake_gas_estimate_subprovider'; + +import { constants } from './constants'; -import {constants} from './constants'; +// HACK: web3 leaks XMLHttpRequest into the global scope and causes requests to hang +// because they are using the wrong XHR package. +// importing web3 after subproviders fixes this issue +// Filed issue: https://github.com/ethereum/web3.js/issues/844 +// tslint:disable-next-line:ordered-imports +import * as Web3 from 'web3'; export const web3Factory = { create(hasAddresses: boolean = true): Web3 { @@ -21,14 +27,15 @@ export const web3Factory = { }, getRpcProvider(hasAddresses: boolean = true): Web3.Provider { const provider = new ProviderEngine(); - const rpcUrl = `http://${constants.RPC_HOST}:${constants.RPC_PORT}`; if (!hasAddresses) { provider.addProvider(new EmptyWalletSubprovider()); } provider.addProvider(new FakeGasEstimateSubprovider(constants.GAS_ESTIMATE)); - provider.addProvider(new RpcSubprovider({ - rpcUrl, - })); + provider.addProvider( + new RpcSubprovider({ + rpcUrl: constants.RPC_URL, + }), + ); provider.start(); return provider; }, |