diff options
author | Fabio Berger <me@fabioberger.com> | 2018-06-12 06:23:48 +0800 |
---|---|---|
committer | Fabio Berger <me@fabioberger.com> | 2018-06-12 06:23:48 +0800 |
commit | 7e78f5941ad9cb2fd5225899f79823c7376894c0 (patch) | |
tree | 13fadf90f821e9fead1373a2e567e6d86fe27e0f /packages/order-utils/test | |
parent | 20f93185975d16c77492f05eb86dd89695539cd9 (diff) | |
parent | bc0ae6be318a15bf8670a6da9a59d9bdb12cadae (diff) | |
download | dexon-sol-tools-7e78f5941ad9cb2fd5225899f79823c7376894c0.tar dexon-sol-tools-7e78f5941ad9cb2fd5225899f79823c7376894c0.tar.gz dexon-sol-tools-7e78f5941ad9cb2fd5225899f79823c7376894c0.tar.bz2 dexon-sol-tools-7e78f5941ad9cb2fd5225899f79823c7376894c0.tar.lz dexon-sol-tools-7e78f5941ad9cb2fd5225899f79823c7376894c0.tar.xz dexon-sol-tools-7e78f5941ad9cb2fd5225899f79823c7376894c0.tar.zst dexon-sol-tools-7e78f5941ad9cb2fd5225899f79823c7376894c0.zip |
Merge branch 'v2-prototype' into feature/combinatorial-testing
* v2-prototype: (68 commits)
Stop exporting ArtifactWriter
Fix no-unused-variable tslint rule to include parameters and fix issues
Fix linter exclude rule
Validate all signature types rather then only ECSignatures
Store the instantiated OrderValidationUtils
Remove global hooks from tests and deploy contracts from within the specific tests
Add EmitStatement to ASTVisitor
Fix tslint issues
Add back artifacts file
Fix a bug in SolCompilerArtifacts adapter config overriding
Move OrderValidationUtils (+ tests) and ExchangeTransferSimulator to order-utils
export parseECSignature method
Export ArtifactWriter from migrations package
Remove unused artifact file
Pass in generated contract wrapper to orderValidationUtils at instantiation
Refactor orderValidationUtils to use the generated contract wrapper instead of the higher-level one
Refactor ExchangeTransferSimulator public interface to accet an AbstractBalanceAndProxyAllowanceLazyStore so that this module could be re-used in different contexts.
Increase timeout for contract migrations
Remove some copy-paste code
Await transactions in migrations
...
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; + } +} |