diff options
author | Fabio Berger <me@fabioberger.com> | 2018-06-12 06:14:19 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-12 06:14:19 +0800 |
commit | bc0ae6be318a15bf8670a6da9a59d9bdb12cadae (patch) | |
tree | d4d2ffbac9270c332b1e788c2ac39829a5e89359 /packages/order-utils/test | |
parent | e0c0584c593e3d948652c1cb58f39042c5b8f488 (diff) | |
parent | c03119d10ad0f2633a78980bd939c65fedfd0531 (diff) | |
download | dexon-sol-tools-bc0ae6be318a15bf8670a6da9a59d9bdb12cadae.tar dexon-sol-tools-bc0ae6be318a15bf8670a6da9a59d9bdb12cadae.tar.gz dexon-sol-tools-bc0ae6be318a15bf8670a6da9a59d9bdb12cadae.tar.bz2 dexon-sol-tools-bc0ae6be318a15bf8670a6da9a59d9bdb12cadae.tar.lz dexon-sol-tools-bc0ae6be318a15bf8670a6da9a59d9bdb12cadae.tar.xz dexon-sol-tools-bc0ae6be318a15bf8670a6da9a59d9bdb12cadae.tar.zst dexon-sol-tools-bc0ae6be318a15bf8670a6da9a59d9bdb12cadae.zip |
Merge pull request #684 from 0xProject/fix/contract-wrappers/exchangeTransferSimulator
Move ExchangeTransferSimulator & OrderValidationUtils to Order-Utils
Diffstat (limited to 'packages/order-utils/test')
3 files changed, 273 insertions, 0 deletions
diff --git a/packages/order-utils/test/exchange_transfer_simulator_test.ts b/packages/order-utils/test/exchange_transfer_simulator_test.ts new file mode 100644 index 000000000..eeae42698 --- /dev/null +++ b/packages/order-utils/test/exchange_transfer_simulator_test.ts @@ -0,0 +1,177 @@ +import { BlockchainLifecycle, devConstants } from '@0xproject/dev-utils'; +import { ExchangeContractErrs } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import 'make-promises-safe'; + +import { artifacts } from '../src/artifacts'; +import { constants } from '../src/constants'; +import { ExchangeTransferSimulator } from '../src/exchange_transfer_simulator'; +import { DummyERC20TokenContract } from '../src/generated_contract_wrappers/dummy_e_r_c20_token'; +import { ERC20ProxyContract } from '../src/generated_contract_wrappers/e_r_c20_proxy'; +import { ERC20TokenContract } from '../src/generated_contract_wrappers/e_r_c20_token'; +import { BalanceAndProxyAllowanceLazyStore } from '../src/store/balance_and_proxy_allowance_lazy_store'; +import { TradeSide, TransferType } from '../src/types'; + +import { chaiSetup } from './utils/chai_setup'; +import { SimpleERC20BalanceAndProxyAllowanceFetcher } from './utils/simple_erc20_balance_and_proxy_allowance_fetcher'; +import { provider, web3Wrapper } from './utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('ExchangeTransferSimulator', async () => { + const transferAmount = new BigNumber(5); + let userAddresses: string[]; + let dummyERC20Token: DummyERC20TokenContract; + let coinbase: string; + let sender: string; + let recipient: string; + let exampleTokenAddress: string; + let exchangeTransferSimulator: ExchangeTransferSimulator; + let txHash: string; + let erc20ProxyAddress: string; + before(async function(): Promise<void> { + const mochaTestTimeoutMs = 20000; + this.timeout(mochaTestTimeoutMs); + + userAddresses = await web3Wrapper.getAvailableAddressesAsync(); + [coinbase, sender, recipient] = userAddresses; + + const txDefaults = { + gas: devConstants.GAS_LIMIT, + from: devConstants.TESTRPC_FIRST_ADDRESS, + }; + + const erc20Proxy = await ERC20ProxyContract.deployFrom0xArtifactAsync( + artifacts.ERC20Proxy, + provider, + txDefaults, + ); + erc20ProxyAddress = erc20Proxy.address; + + const totalSupply = new BigNumber(100000000000000000000); + const name = 'Test'; + const symbol = 'TST'; + const decimals = new BigNumber(18); + // tslint:disable-next-line:no-unused-variable + dummyERC20Token = await DummyERC20TokenContract.deployFrom0xArtifactAsync( + artifacts.DummyERC20Token, + provider, + txDefaults, + name, + symbol, + decimals, + totalSupply, + ); + + exampleTokenAddress = dummyERC20Token.address; + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('#transferFromAsync', function(): void { + // HACK: For some reason these tests need a slightly longer timeout + const mochaTestTimeoutMs = 3000; + this.timeout(mochaTestTimeoutMs); + + beforeEach(() => { + const simpleERC20BalanceAndProxyAllowanceFetcher = new SimpleERC20BalanceAndProxyAllowanceFetcher( + (dummyERC20Token as any) as ERC20TokenContract, + erc20ProxyAddress, + ); + const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( + simpleERC20BalanceAndProxyAllowanceFetcher, + ); + exchangeTransferSimulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); + }); + 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 () => { + txHash = await dummyERC20Token.approve.sendTransactionAsync(erc20ProxyAddress, transferAmount, { + from: sender, + }); + await web3Wrapper.awaitTransactionSuccessAsync(txHash); + 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 dummyERC20Token.transfer.sendTransactionAsync(sender, transferAmount, { + from: coinbase, + }); + await web3Wrapper.awaitTransactionSuccessAsync(txHash); + + txHash = await dummyERC20Token.approve.sendTransactionAsync(erc20ProxyAddress, transferAmount, { + from: sender, + }); + await web3Wrapper.awaitTransactionSuccessAsync(txHash); + + await exchangeTransferSimulator.transferFromAsync( + exampleTokenAddress, + sender, + recipient, + transferAmount, + TradeSide.Taker, + TransferType.Trade, + ); + 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); + expect(senderBalance).to.be.bignumber.equal(0); + expect(recipientBalance).to.be.bignumber.equal(transferAmount); + expect(senderProxyAllowance).to.be.bignumber.equal(0); + }); + it("doesn't update proxyAllowance after transfer if unlimited", async () => { + txHash = await dummyERC20Token.transfer.sendTransactionAsync(sender, transferAmount, { + from: coinbase, + }); + await web3Wrapper.awaitTransactionSuccessAsync(txHash); + txHash = await dummyERC20Token.approve.sendTransactionAsync( + erc20ProxyAddress, + constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, + { + from: sender, + }, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash); + await exchangeTransferSimulator.transferFromAsync( + exampleTokenAddress, + sender, + recipient, + transferAmount, + TradeSide.Taker, + TransferType.Trade, + ); + 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); + expect(senderBalance).to.be.bignumber.equal(0); + expect(recipientBalance).to.be.bignumber.equal(transferAmount); + expect(senderProxyAllowance).to.be.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); + }); + }); +}); diff --git a/packages/order-utils/test/order_validation_utils_test.ts b/packages/order-utils/test/order_validation_utils_test.ts new file mode 100644 index 000000000..d3ff867d7 --- /dev/null +++ b/packages/order-utils/test/order_validation_utils_test.ts @@ -0,0 +1,70 @@ +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import 'mocha'; + +import { OrderValidationUtils } from '../src/order_validation_utils'; + +import { chaiSetup } from './utils/chai_setup'; + +chaiSetup.configure(); +const expect = chai.expect; + +describe('OrderValidationUtils', () => { + describe('#isRoundingError', () => { + it('should return false if there is a rounding error of 0.1%', async () => { + const numerator = new BigNumber(20); + const denominator = new BigNumber(999); + const target = new BigNumber(50); + // rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1% + const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target); + expect(isRoundingError).to.be.false(); + }); + + it('should return false if there is a rounding of 0.09%', async () => { + const numerator = new BigNumber(20); + const denominator = new BigNumber(9991); + const target = new BigNumber(500); + // rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09% + const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target); + expect(isRoundingError).to.be.false(); + }); + + it('should return true if there is a rounding error of 0.11%', async () => { + const numerator = new BigNumber(20); + const denominator = new BigNumber(9989); + const target = new BigNumber(500); + // rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011% + const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target); + expect(isRoundingError).to.be.true(); + }); + + it('should return true if there is a rounding error > 0.1%', async () => { + const numerator = new BigNumber(3); + const denominator = new BigNumber(7); + const target = new BigNumber(10); + // rounding error = ((3*10/7) - floor(3*10/7)) / (3*10/7) = 6.67% + const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target); + expect(isRoundingError).to.be.true(); + }); + + it('should return false when there is no rounding error', async () => { + const numerator = new BigNumber(1); + const denominator = new BigNumber(2); + const target = new BigNumber(10); + + const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target); + expect(isRoundingError).to.be.false(); + }); + + it('should return false when there is rounding error <= 0.1%', async () => { + // randomly generated numbers + const numerator = new BigNumber(76564); + const denominator = new BigNumber(676373677); + const target = new BigNumber(105762562); + // rounding error = ((76564*105762562/676373677) - floor(76564*105762562/676373677)) / + // (76564*105762562/676373677) = 0.0007% + const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target); + expect(isRoundingError).to.be.false(); + }); + }); +}); diff --git a/packages/order-utils/test/utils/simple_erc20_balance_and_proxy_allowance_fetcher.ts b/packages/order-utils/test/utils/simple_erc20_balance_and_proxy_allowance_fetcher.ts new file mode 100644 index 000000000..d41eaca40 --- /dev/null +++ b/packages/order-utils/test/utils/simple_erc20_balance_and_proxy_allowance_fetcher.ts @@ -0,0 +1,26 @@ +import { BigNumber } from '@0xproject/utils'; + +import { AbstractBalanceAndProxyAllowanceFetcher } from '../../src/abstract/abstract_balance_and_proxy_allowance_fetcher'; + +import { ERC20TokenContract } from '../../src/generated_contract_wrappers/e_r_c20_token'; + +export class SimpleERC20BalanceAndProxyAllowanceFetcher implements AbstractBalanceAndProxyAllowanceFetcher { + private _erc20TokenContract: ERC20TokenContract; + private _erc20ProxyAddress: string; + constructor(erc20TokenWrapper: ERC20TokenContract, erc20ProxyAddress: string) { + this._erc20TokenContract = erc20TokenWrapper; + this._erc20ProxyAddress = erc20ProxyAddress; + } + public async getBalanceAsync(_assetData: string, userAddress: string): Promise<BigNumber> { + // HACK: We cheat and don't pass in the assetData since it's always the same token used + // in our tests. + const balance = await this._erc20TokenContract.balanceOf.callAsync(userAddress); + return balance; + } + public async getProxyAllowanceAsync(_assetData: string, userAddress: string): Promise<BigNumber> { + // HACK: We cheat and don't pass in the assetData since it's always the same token used + // in our tests. + const proxyAllowance = await this._erc20TokenContract.allowance.callAsync(userAddress, this._erc20ProxyAddress); + return proxyAllowance; + } +} |