From e1ab9aa690ebb07d5391cf9b0e6944f5368de518 Mon Sep 17 00:00:00 2001 From: David Sun Date: Wed, 19 Jun 2019 17:08:12 -0700 Subject: [PATCH] rewritten test cases for SwapQuoter fixed testing for swap quoter + setup for consumer testing added framework for testing consumers added testing and updated some types --- packages/asset-buyer/package.json | 6 +- packages/asset-buyer/src/constants.ts | 1 - packages/asset-buyer/src/index.ts | 3 +- .../forwarder_swap_quote_consumer.ts | 45 +++--- .../src/{asset_buyer.ts => swap_quoter.ts} | 8 +- packages/asset-buyer/src/types.ts | 6 +- .../src/utils/swap_quote_calculator.ts | 26 ++-- packages/asset-buyer/src/utils/utils.ts | 2 +- .../forwarder_swap_quote_consumer_test.ts | 142 ++++++++++++++++++ packages/asset-buyer/test/global_hooks.ts | 6 + ..._test.ts => swap_quote_calculator_test.ts} | 136 ++++++++--------- ...sset_buyer_test.ts => swap_quoter_test.ts} | 97 +++++++----- packages/asset-buyer/test/utils/migrate.ts | 18 +++ packages/asset-buyer/test/utils/mocks.ts | 47 ++++-- packages/asset-buyer/test/utils/swap_quote.ts | 41 +++++ .../asset-buyer/test/utils/web3_wrapper.ts | 8 + 16 files changed, 430 insertions(+), 162 deletions(-) rename packages/asset-buyer/src/{asset_buyer.ts => swap_quoter.ts} (98%) create mode 100644 packages/asset-buyer/test/forwarder_swap_quote_consumer_test.ts create mode 100644 packages/asset-buyer/test/global_hooks.ts rename packages/asset-buyer/test/{buy_quote_calculator_test.ts => swap_quote_calculator_test.ts} (70%) rename packages/asset-buyer/test/{asset_buyer_test.ts => swap_quoter_test.ts} (63%) create mode 100644 packages/asset-buyer/test/utils/migrate.ts create mode 100644 packages/asset-buyer/test/utils/swap_quote.ts create mode 100644 packages/asset-buyer/test/utils/web3_wrapper.ts diff --git a/packages/asset-buyer/package.json b/packages/asset-buyer/package.json index f4aadeefe7..5801f89959 100644 --- a/packages/asset-buyer/package.json +++ b/packages/asset-buyer/package.json @@ -17,7 +17,7 @@ "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", "test:circleci": "yarn test:coverage", - "run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --exit", + "run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js lib/test/global_hooks.js --timeout 10000 --bail --exit", "clean": "shx rm -rf lib test_temp", "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" }, @@ -39,8 +39,12 @@ "dependencies": { "@0x/assert": "^2.0.10", "@0x/connect": "^5.0.10", + "@0x/contract-addresses": "^2.3.3", "@0x/contract-wrappers": "^9.1.4", + "@0x/dev-utils": "^2.2.3", + "@0x/fill-scenarios": "^3.0.10", "@0x/json-schemas": "^3.0.10", + "@0x/migrations": "^4.1.6", "@0x/order-utils": "^8.1.1", "@0x/subproviders": "^4.1.0", "@0x/types": "^2.2.2", diff --git a/packages/asset-buyer/src/constants.ts b/packages/asset-buyer/src/constants.ts index 69839e935e..10dd79b9d4 100644 --- a/packages/asset-buyer/src/constants.ts +++ b/packages/asset-buyer/src/constants.ts @@ -22,7 +22,6 @@ const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = { const DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS: ForwarderSwapQuoteGetOutputOpts = { feePercentage: 0, feeRecipient: NULL_ADDRESS, - ethAmount: new BigNumber(0), }; const DEFAULT_FORWARDER_SWAP_QUOTE_EXECUTE_OPTS: ForwarderSwapQuoteExecutionOpts = DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS; diff --git a/packages/asset-buyer/src/index.ts b/packages/asset-buyer/src/index.ts index ae312a2013..37206e672b 100644 --- a/packages/asset-buyer/src/index.ts +++ b/packages/asset-buyer/src/index.ts @@ -18,7 +18,8 @@ export { export { SignedOrder } from '@0x/types'; export { BigNumber } from '@0x/utils'; -export { SwapQuoter } from './asset_buyer'; +export { ForwarderSwapQuoteConsumer } from './quote_consumers/forwarder_swap_quote_consumer'; +export { SwapQuoter } from './swap_quoter'; export { InsufficientAssetLiquidityError } from './errors'; export { BasicOrderProvider } from './order_providers/basic_order_provider'; diff --git a/packages/asset-buyer/src/quote_consumers/forwarder_swap_quote_consumer.ts b/packages/asset-buyer/src/quote_consumers/forwarder_swap_quote_consumer.ts index 1b78fa200e..12e1130301 100644 --- a/packages/asset-buyer/src/quote_consumers/forwarder_swap_quote_consumer.ts +++ b/packages/asset-buyer/src/quote_consumers/forwarder_swap_quote_consumer.ts @@ -22,21 +22,13 @@ import { assetDataUtils } from '../utils/asset_data_utils'; import { utils } from '../utils/utils'; export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer { - public readonly provider: ZeroExProvider; public readonly networkId: number; private readonly _contractWrappers: ContractWrappers; - constructor( - supportedProvider: SupportedProvider, - options: Partial = {}, - ) { - const { networkId } = _.merge( - {}, - constants.DEFAULT_SWAP_QUOTER_OPTS, - options, - ); + constructor(supportedProvider: SupportedProvider, options: Partial = {}) { + const { networkId } = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options); assert.isNumber('networkId', networkId); const provider = providerUtils.standardizeOrThrow(supportedProvider); @@ -64,12 +56,16 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer): SmartContractParamsInfo { + public getSmartContractParamsOrThrow( + quote: SwapQuote, + opts: Partial, + ): SmartContractParamsInfo { assert.isValidForwarderSwapQuote('quote', quote, this._getEtherTokenAssetDataOrThrow()); const { ethAmount, feeRecipient, feePercentage: unFormattedFeePercentage } = _.merge( @@ -80,14 +76,19 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer o.signature); - const feeSignatures = _.map(orders, o => o.signature); + const feeSignatures = _.map(feeOrders, o => o.signature); const feePercentage = utils.numberPercentageToEtherTokenAmountPercentage(unFormattedFeePercentage); @@ -101,7 +102,11 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer): Promise { + public async executeSwapQuoteOrThrowAsync( + quote: SwapQuote, + opts: Partial, + ): Promise { assert.isValidForwarderSwapQuote('quote', quote, this._getEtherTokenAssetDataOrThrow()); const { ethAmount, takerAddress, gasLimit, gasPrice, feeRecipient, feePercentage } = _.merge( @@ -121,8 +129,9 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer = {}) { + constructor( + supportedProvider: SupportedProvider, + orderProvider: OrderProvider, + options: Partial = {}, + ) { const { networkId, orderRefreshIntervalMs, expiryBufferMs } = _.merge( {}, constants.DEFAULT_SWAP_QUOTER_OPTS, @@ -202,7 +206,7 @@ export class SwapQuoter { ): Promise { const shouldForceOrderRefresh = options.shouldForceOrderRefresh !== undefined ? options.shouldForceOrderRefresh : false; - assert.isString('makerAssetDataa', makerAssetData); + assert.isString('makerAssetData', makerAssetData); assert.isString('takerAssetData', takerAssetData); assetDataUtils.decodeAssetDataOrThrow(makerAssetData); assetDataUtils.decodeAssetDataOrThrow(takerAssetData); diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts index 2987704d94..7fb9ec4b9f 100644 --- a/packages/asset-buyer/src/types.ts +++ b/packages/asset-buyer/src/types.ts @@ -47,6 +47,7 @@ export interface OrderProvider { */ export interface CalldataInfo { calldataHexString: string; + methodAbi: MethodAbi; to: string; ethAmount?: BigNumber; } @@ -128,14 +129,13 @@ export interface SwapQuoteExecutionOpts extends SwapQuoteGetOutputOpts { export interface ForwarderSwapQuoteGetOutputOpts extends SwapQuoteGetOutputOpts { feePercentage: number; feeRecipient: string; - ethAmount: BigNumber; + ethAmount?: BigNumber; } /** * Represents the options for executing a swap quote with ForwarderSwapQuoteConusmer */ -export interface ForwarderSwapQuoteExecutionOpts extends ForwarderSwapQuoteGetOutputOpts, SwapQuoteExecutionOpts { -} +export interface ForwarderSwapQuoteExecutionOpts extends ForwarderSwapQuoteGetOutputOpts, SwapQuoteExecutionOpts {} /** * takerAssetData: String that represents a specific taker asset (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). diff --git a/packages/asset-buyer/src/utils/swap_quote_calculator.ts b/packages/asset-buyer/src/utils/swap_quote_calculator.ts index a22d5bf2ad..c60ad95d8d 100644 --- a/packages/asset-buyer/src/utils/swap_quote_calculator.ts +++ b/packages/asset-buyer/src/utils/swap_quote_calculator.ts @@ -11,7 +11,7 @@ export const swapQuoteCalculator = { calculate( ordersAndFillableAmounts: OrdersAndFillableAmounts, feeOrdersAndFillableAmounts: OrdersAndFillableAmounts, - makerAssetBuyAmount: BigNumber, + makerAssetFillAmount: BigNumber, slippagePercentage: number, isMakerAssetZrxToken: boolean, ): SwapQuote { @@ -19,20 +19,20 @@ export const swapQuoteCalculator = { const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts; const feeOrders = feeOrdersAndFillableAmounts.orders; const remainingFillableFeeAmounts = feeOrdersAndFillableAmounts.remainingFillableMakerAssetAmounts; - const slippageBufferAmount = makerAssetBuyAmount.multipliedBy(slippagePercentage).integerValue(); + const slippageBufferAmount = makerAssetFillAmount.multipliedBy(slippagePercentage).integerValue(); // find the orders that cover the desired assetBuyAmount (with slippage) const { resultOrders, remainingFillAmount, ordersRemainingFillableMakerAssetAmounts, - } = marketUtils.findOrdersThatCoverMakerAssetFillAmount(orders, makerAssetBuyAmount, { + } = marketUtils.findOrdersThatCoverMakerAssetFillAmount(orders, makerAssetFillAmount, { remainingFillableMakerAssetAmounts, slippageBufferAmount, }); // if we do not have enough orders to cover the desired assetBuyAmount, throw if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) { // We needed the amount they requested to buy, plus the amount for slippage - const totalAmountRequested = makerAssetBuyAmount.plus(slippageBufferAmount); + const totalAmountRequested = makerAssetFillAmount.plus(slippageBufferAmount); const amountAbleToFill = totalAmountRequested.minus(remainingFillAmount); // multiplierNeededWithSlippage represents what we need to multiply the assetBuyAmount by // in order to get the total amount needed considering slippage @@ -87,21 +87,21 @@ export const swapQuoteCalculator = { const bestCaseQuoteInfo = calculateQuoteInfo( trimmedOrdersAndFillableAmounts, trimmedFeeOrdersAndFillableAmounts, - makerAssetBuyAmount, + makerAssetFillAmount, isMakerAssetZrxToken, ); // in order to calculate the maxRate, reverse the ordersAndFillableAmounts such that they are sorted from worst rate to best rate const worstCaseQuoteInfo = calculateQuoteInfo( reverseOrdersAndFillableAmounts(trimmedOrdersAndFillableAmounts), reverseOrdersAndFillableAmounts(trimmedFeeOrdersAndFillableAmounts), - makerAssetBuyAmount, + makerAssetFillAmount, isMakerAssetZrxToken, ); return { takerAssetData, makerAssetData, - makerAssetBuyAmount, + makerAssetFillAmount, orders: resultOrders, feeOrders: resultFeeOrders, bestCaseQuoteInfo, @@ -191,30 +191,30 @@ function findTakerTokenAmountNeededToBuyZrx( function findTakerTokenAndZrxAmountNeededToBuyAsset( ordersAndFillableAmounts: OrdersAndFillableAmounts, - makerAssetBuyAmount: BigNumber, + makerAssetFillAmount: BigNumber, ): [BigNumber, BigNumber] { const { orders, remainingFillableMakerAssetAmounts } = ordersAndFillableAmounts; const result = _.reduce( orders, (acc, order, index) => { - const { totalTakerTokenAmount, totalZrxAmount, remainingMakerAssetBuyAmount } = acc; + const { totalTakerTokenAmount, totalZrxAmount, remainingmakerAssetFillAmount } = acc; const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index]; - const makerFillAmount = BigNumber.min(acc.remainingMakerAssetBuyAmount, remainingFillableMakerAssetAmount); + const makerFillAmount = BigNumber.min(acc.remainingmakerAssetFillAmount, remainingFillableMakerAssetAmount); const takerFillAmount = orderCalculationUtils.getTakerFillAmount(order, makerFillAmount); const takerFeeAmount = orderCalculationUtils.getTakerFeeAmount(order, takerFillAmount); return { totalTakerTokenAmount: totalTakerTokenAmount.plus(takerFillAmount), totalZrxAmount: totalZrxAmount.plus(takerFeeAmount), - remainingMakerAssetBuyAmount: BigNumber.max( + remainingmakerAssetFillAmount: BigNumber.max( constants.ZERO_AMOUNT, - remainingMakerAssetBuyAmount.minus(makerFillAmount), + remainingmakerAssetFillAmount.minus(makerFillAmount), ), }; }, { totalTakerTokenAmount: constants.ZERO_AMOUNT, totalZrxAmount: constants.ZERO_AMOUNT, - remainingMakerAssetBuyAmount: makerAssetBuyAmount, + remainingmakerAssetFillAmount: makerAssetFillAmount, }, ); return [result.totalTakerTokenAmount, result.totalZrxAmount]; diff --git a/packages/asset-buyer/src/utils/utils.ts b/packages/asset-buyer/src/utils/utils.ts index 7c75cd4813..f6073eb379 100644 --- a/packages/asset-buyer/src/utils/utils.ts +++ b/packages/asset-buyer/src/utils/utils.ts @@ -15,7 +15,7 @@ export const utils = { return _.find( abi, (def: AbiDefinition): boolean => { - if (def.type === `'function'`) { + if (def.type === 'function') { const methodDef = def as MethodAbi; return methodDef.name === name; } else { diff --git a/packages/asset-buyer/test/forwarder_swap_quote_consumer_test.ts b/packages/asset-buyer/test/forwarder_swap_quote_consumer_test.ts new file mode 100644 index 0000000000..42d06f7e5c --- /dev/null +++ b/packages/asset-buyer/test/forwarder_swap_quote_consumer_test.ts @@ -0,0 +1,142 @@ +import { tokenUtils } from '@0x/contract-wrappers/lib/test/utils/token_utils'; +import { BlockchainLifecycle, callbackErrorReporter } from '@0x/dev-utils'; +import { FillScenarios } from '@0x/fill-scenarios'; +import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; +import { Web3ProviderEngine } from '@0x/subproviders'; +import { SignedOrder } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; +import * as chai from 'chai'; +import 'mocha'; +import * as TypeMoq from 'typemoq'; + +import { ForwarderSwapQuoteConsumer, SwapQuote } from '../src'; + +import { chaiSetup } from './utils/chai_setup'; +import { migrateOnceAsync } from './utils/migrate'; +import { getFullyFillableSwapQuoteWithNoFees, getSignedOrdersWithNoFees } from './utils/swap_quote'; +import { provider, web3Wrapper } from './utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +const FILLABLE_AMOUNTS = [new BigNumber(5), new BigNumber(10)]; +const TESTRPC_NETWORK_ID = 50; + +describe('ForwarderSwapQuoteConsumer', () => { + let userAddresses: string[]; + let makerAddress: string; + let takerAddress: string; + let fillScenarios: FillScenarios; + let feeRecipient: string; + let makerAssetData: string; + let takerAssetData: string; + let wethAssetData: string; + const networkId = TESTRPC_NETWORK_ID; + before(async () => { + const contractAddresses = await migrateOnceAsync(); + await blockchainLifecycle.startAsync(); + userAddresses = await web3Wrapper.getAvailableAddressesAsync(); + fillScenarios = new FillScenarios( + provider, + userAddresses, + contractAddresses.zrxToken, + contractAddresses.exchange, + contractAddresses.erc20Proxy, + contractAddresses.erc721Proxy, + ); + [makerAddress, takerAddress, feeRecipient] = userAddresses; + const [makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses(); + [makerAssetData, takerAssetData, wethAssetData] = [ + assetDataUtils.encodeERC20AssetData(makerTokenAddress), + assetDataUtils.encodeERC20AssetData(takerTokenAddress), + assetDataUtils.encodeERC20AssetData(contractAddresses.etherToken), + ]; + }); + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + // This constructor has incorrect types + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('getSmartContractParamsOrThrow', () => { + + describe('validation', () => { + it('should throw if swap quote provided is not a valid forwarder SwapQuote (taker asset is WETH)', async () => { + const invalidSignedOrders = getSignedOrdersWithNoFees( + makerAssetData, + takerAssetData, + makerAddress, + takerAddress, + FILLABLE_AMOUNTS, + ); + const invalidSwapQuote = getFullyFillableSwapQuoteWithNoFees(makerAssetData, takerAssetData, invalidSignedOrders); + const swapQuoteConsumer = new ForwarderSwapQuoteConsumer(provider, {}); + // TODO(dave4506) finish up testing/coverage + // expect( + // swapQuoteConsumer.getSmartContractParamsOrThrow(invalidSwapQuote, {}), + // ).to.throws(); + }); + }); + + describe('valid swap quote', async () => { + it('provide correct smart contract params with default options', async () => { + const signedOrders = getSignedOrdersWithNoFees( + makerAssetData, + wethAssetData, + makerAddress, + takerAddress, + FILLABLE_AMOUNTS, + ); + const swapQuote = getFullyFillableSwapQuoteWithNoFees(makerAssetData, takerAssetData, signedOrders); + const swapQuoteConsumer = new ForwarderSwapQuoteConsumer(provider, { networkId }); + const smartContractParamsInfo = swapQuoteConsumer.getSmartContractParamsOrThrow(swapQuote, {}); + // console.log(smartContractParamsInfo); + // TODO(dave4506): Add elaborate testing + }); + }); + }); + + describe('getCalldataOrThrow', () => { + + describe('validation', () => { + it('should throw if swap quote provided is not a valid forwarder SwapQuote (taker asset is WETH)', async () => { + const invalidSignedOrders = getSignedOrdersWithNoFees( + makerAssetData, + takerAssetData, + makerAddress, + takerAddress, + FILLABLE_AMOUNTS, + ); + const invalidSwapQuote = getFullyFillableSwapQuoteWithNoFees(makerAssetData, takerAssetData, invalidSignedOrders); + const swapQuoteConsumer = new ForwarderSwapQuoteConsumer(provider, {}); + // TODO(dave4506) finish up testing/coverage + // expect( + // swapQuoteConsumer.getSmartContractParamsOrThrow(invalidSwapQuote, {}), + // ).to.throws(); + }); + }); + + describe('valid swap quote', async () => { + it('provide correct calldata hex with default options', async () => { + const signedOrders = getSignedOrdersWithNoFees( + makerAssetData, + wethAssetData, + makerAddress, + takerAddress, + FILLABLE_AMOUNTS, + ); + const swapQuote = getFullyFillableSwapQuoteWithNoFees(makerAssetData, takerAssetData, signedOrders); + const swapQuoteConsumer = new ForwarderSwapQuoteConsumer(provider, { networkId }); + const callDataInfo = swapQuoteConsumer.getCalldataOrThrow(swapQuote, {}); + // console.log(callDataInfo); + // TODO(dave4506): Add elaborate testing + }); + }); + }); +}); diff --git a/packages/asset-buyer/test/global_hooks.ts b/packages/asset-buyer/test/global_hooks.ts new file mode 100644 index 0000000000..26c37158f7 --- /dev/null +++ b/packages/asset-buyer/test/global_hooks.ts @@ -0,0 +1,6 @@ +before('set up mocha', async function(): Promise { + // HACK: Since the migrations take longer then our global mocha timeout limit + // we manually increase it for this before hook. + const mochaTestTimeoutMs = 25000; + this.timeout(mochaTestTimeoutMs); // tslint:disable-line:no-invalid-this +}); diff --git a/packages/asset-buyer/test/buy_quote_calculator_test.ts b/packages/asset-buyer/test/swap_quote_calculator_test.ts similarity index 70% rename from packages/asset-buyer/test/buy_quote_calculator_test.ts rename to packages/asset-buyer/test/swap_quote_calculator_test.ts index 177fd8fe60..3d68b98592 100644 --- a/packages/asset-buyer/test/buy_quote_calculator_test.ts +++ b/packages/asset-buyer/test/swap_quote_calculator_test.ts @@ -5,8 +5,8 @@ import * as chai from 'chai'; import * as _ from 'lodash'; import 'mocha'; -import { AssetBuyerError, OrdersAndFillableAmounts } from '../src/types'; -import { buyQuoteCalculator } from '../src/utils/buy_quote_calculator'; +import { OrdersAndFillableAmounts, SwapQuoterError } from '../src/types'; +import { swapQuoteCalculator } from '../src/utils/swap_quote_calculator'; import { chaiSetup } from './utils/chai_setup'; import { testHelpers } from './utils/test_helpers'; @@ -15,7 +15,7 @@ chaiSetup.configure(); const expect = chai.expect; // tslint:disable:custom-no-magic-numbers -describe('buyQuoteCalculator', () => { +describe('swapQuoteCalculator', () => { describe('#calculate', () => { let firstOrder: SignedOrder; let firstRemainingFillAmount: BigNumber; @@ -71,12 +71,11 @@ describe('buyQuoteCalculator', () => { it('should throw if not enough maker asset liquidity (multiple orders)', () => { // we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units const errorFunction = () => { - buyQuoteCalculator.calculate( + swapQuoteCalculator.calculate( ordersAndFillableAmounts, smallFeeOrderAndFillableAmount, new BigNumber(500), 0, - 0, false, ); }; @@ -85,11 +84,10 @@ describe('buyQuoteCalculator', () => { it('should throw if not enough maker asset liquidity (multiple orders with 20% slippage)', () => { // we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units const errorFunction = () => { - buyQuoteCalculator.calculate( + swapQuoteCalculator.calculate( ordersAndFillableAmounts, smallFeeOrderAndFillableAmount, new BigNumber(500), - 0, 0.2, false, ); @@ -99,11 +97,10 @@ describe('buyQuoteCalculator', () => { it('should throw if not enough maker asset liquidity (multiple orders with 5% slippage)', () => { // we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units const errorFunction = () => { - buyQuoteCalculator.calculate( + swapQuoteCalculator.calculate( ordersAndFillableAmounts, smallFeeOrderAndFillableAmount, new BigNumber(600), - 0, 0.05, false, ); @@ -117,12 +114,11 @@ describe('buyQuoteCalculator', () => { }; const errorFunction = () => { - buyQuoteCalculator.calculate( + swapQuoteCalculator.calculate( firstOrderAndFillableAmount, smallFeeOrderAndFillableAmount, new BigNumber(201), 0, - 0, false, ); }; @@ -139,12 +135,11 @@ describe('buyQuoteCalculator', () => { remainingFillableMakerAssetAmounts: [completelyFillableOrder.makerAssetAmount], }; const errorFunction = () => { - buyQuoteCalculator.calculate( + swapQuoteCalculator.calculate( completelyFillableOrdersAndFillableAmount, smallFeeOrderAndFillableAmount, new BigNumber(124), 0, - 0, false, ); }; @@ -157,12 +152,11 @@ describe('buyQuoteCalculator', () => { takerFee: new BigNumber(0), }); const errorFunction = () => { - buyQuoteCalculator.calculate( + swapQuoteCalculator.calculate( { orders: [smallOrder], remainingFillableMakerAssetAmounts: [smallOrder.makerAssetAmount] }, smallFeeOrderAndFillableAmount, new BigNumber(600), 0, - 0, false, ); }; @@ -175,11 +169,10 @@ describe('buyQuoteCalculator', () => { takerFee: new BigNumber(0), }); const errorFunction = () => { - buyQuoteCalculator.calculate( + swapQuoteCalculator.calculate( { orders: [smallOrder], remainingFillableMakerAssetAmounts: [smallOrder.makerAssetAmount] }, smallFeeOrderAndFillableAmount, new BigNumber(600), - 0, 0.2, false, ); @@ -189,12 +182,11 @@ describe('buyQuoteCalculator', () => { }); it('should not throw if order is fillable', () => { expect(() => - buyQuoteCalculator.calculate( + swapQuoteCalculator.calculate( ordersAndFillableAmounts, allFeeOrdersAndFillableAmounts, new BigNumber(300), 0, - 0, false, ), ).to.not.throw(); @@ -202,94 +194,102 @@ describe('buyQuoteCalculator', () => { it('should throw if not enough ZRX liquidity', () => { // we request 300 makerAsset units but the ZRX order is only enough to fill the first order, which only has 200 makerAssetUnits available expect(() => - buyQuoteCalculator.calculate( + swapQuoteCalculator.calculate( ordersAndFillableAmounts, smallFeeOrderAndFillableAmount, new BigNumber(300), 0, - 0, false, ), - ).to.throw(AssetBuyerError.InsufficientZrxLiquidity); + ).to.throw(SwapQuoterError.InsufficientZrxLiquidity); }); - it('calculates a correct buyQuote with no slippage', () => { + it('calculates a correct swapQuote with no slippage', () => { // we request 200 makerAsset units which can be filled using the first order // the first order requires a fee of 100 ZRX from the taker which can be filled by the feeOrder const assetBuyAmount = new BigNumber(200); - const feePercentage = 0.02; const slippagePercentage = 0; - const buyQuote = buyQuoteCalculator.calculate( + const swapQuote = swapQuoteCalculator.calculate( ordersAndFillableAmounts, smallFeeOrderAndFillableAmount, assetBuyAmount, - feePercentage, slippagePercentage, false, ); // test if orders are correct - expect(buyQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]); - expect(buyQuote.feeOrders).to.deep.equal([smallFeeOrderAndFillableAmount.orders[0]]); + expect(swapQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]); + expect(swapQuote.feeOrders).to.deep.equal([smallFeeOrderAndFillableAmount.orders[0]]); // test if rates are correct // 50 eth to fill the first order + 100 eth for fees - const expectedEthAmountForAsset = new BigNumber(50); - const expectedEthAmountForZrxFees = new BigNumber(100); - const expectedFillEthAmount = expectedEthAmountForAsset; - const expectedAffiliateFeeEthAmount = expectedEthAmountForAsset.multipliedBy(feePercentage); - const expectedFeeEthAmount = expectedAffiliateFeeEthAmount.plus(expectedEthAmountForZrxFees); - const expectedTotalEthAmount = expectedFillEthAmount.plus(expectedFeeEthAmount); - expect(buyQuote.bestCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedFillEthAmount); - expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount); - expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount); + const expectedTakerAssetAmountForMakerAsset = new BigNumber(50); + const expectedTakerAssetAmountForZrxFees = new BigNumber(100); + const expectedTotalTakerAssetAmount = expectedTakerAssetAmountForMakerAsset.plus( + expectedTakerAssetAmountForZrxFees, + ); + expect(swapQuote.bestCaseQuoteInfo.takerTokenAmount).to.bignumber.equal( + expectedTakerAssetAmountForMakerAsset, + ); + expect(swapQuote.bestCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal( + expectedTakerAssetAmountForZrxFees, + ); + expect(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(expectedTotalTakerAssetAmount); // because we have no slippage protection, minRate is equal to maxRate - expect(buyQuote.worstCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedFillEthAmount); - expect(buyQuote.worstCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount); - expect(buyQuote.worstCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount); - // test if feePercentage gets passed through - expect(buyQuote.feePercentage).to.equal(feePercentage); + expect(swapQuote.worstCaseQuoteInfo.takerTokenAmount).to.bignumber.equal( + expectedTakerAssetAmountForMakerAsset, + ); + expect(swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal( + expectedTakerAssetAmountForZrxFees, + ); + expect(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal( + expectedTotalTakerAssetAmount, + ); }); - it('calculates a correct buyQuote with with slippage', () => { + it('calculates a correct swapQuote with with slippage', () => { // we request 200 makerAsset units which can be filled using the first order // however with 50% slippage we are protecting the buy with 100 extra makerAssetUnits // so we need enough orders to fill 300 makerAssetUnits // 300 makerAssetUnits can only be filled using both orders // the first order requires a fee of 100 ZRX from the taker which can be filled by the feeOrder const assetBuyAmount = new BigNumber(200); - const feePercentage = 0.02; const slippagePercentage = 0.5; - const buyQuote = buyQuoteCalculator.calculate( + const swapQuote = swapQuoteCalculator.calculate( ordersAndFillableAmounts, allFeeOrdersAndFillableAmounts, assetBuyAmount, - feePercentage, slippagePercentage, false, ); // test if orders are correct - expect(buyQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders); - expect(buyQuote.feeOrders).to.deep.equal(allFeeOrdersAndFillableAmounts.orders); + expect(swapQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders); + expect(swapQuote.feeOrders).to.deep.equal(allFeeOrdersAndFillableAmounts.orders); // test if rates are correct // 50 eth to fill the first order + 100 eth for fees - const expectedEthAmountForAsset = new BigNumber(50); - const expectedEthAmountForZrxFees = new BigNumber(100); - const expectedFillEthAmount = expectedEthAmountForAsset; - const expectedAffiliateFeeEthAmount = expectedEthAmountForAsset.multipliedBy(feePercentage); - const expectedFeeEthAmount = expectedAffiliateFeeEthAmount.plus(expectedEthAmountForZrxFees); - const expectedTotalEthAmount = expectedFillEthAmount.plus(expectedFeeEthAmount); - expect(buyQuote.bestCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedFillEthAmount); - expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount); - expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount); + const expectedTakerAssetAmountForMakerAsset = new BigNumber(50); + const expectedTakerAssetAmountForZrxFees = new BigNumber(100); + const expectedTotalTakerAssetAmount = expectedTakerAssetAmountForMakerAsset.plus( + expectedTakerAssetAmountForZrxFees, + ); + expect(swapQuote.bestCaseQuoteInfo.takerTokenAmount).to.bignumber.equal( + expectedTakerAssetAmountForMakerAsset, + ); + expect(swapQuote.bestCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal( + expectedTakerAssetAmountForZrxFees, + ); + expect(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(expectedTotalTakerAssetAmount); // 100 eth to fill the first order + 208 eth for fees - const expectedWorstEthAmountForAsset = new BigNumber(100); - const expectedWorstEthAmountForZrxFees = new BigNumber(208); - const expectedWorstFillEthAmount = expectedWorstEthAmountForAsset; - const expectedWorstAffiliateFeeEthAmount = expectedWorstEthAmountForAsset.multipliedBy(feePercentage); - const expectedWorstFeeEthAmount = expectedWorstAffiliateFeeEthAmount.plus(expectedWorstEthAmountForZrxFees); - const expectedWorstTotalEthAmount = expectedWorstFillEthAmount.plus(expectedWorstFeeEthAmount); - expect(buyQuote.worstCaseQuoteInfo.assetEthAmount).to.bignumber.equal(expectedWorstFillEthAmount); - expect(buyQuote.worstCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedWorstFeeEthAmount); - expect(buyQuote.worstCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedWorstTotalEthAmount); - // test if feePercentage gets passed through - expect(buyQuote.feePercentage).to.equal(feePercentage); + const expectedWorstTakerAssetAmountForMakerAsset = new BigNumber(100); + const expectedWorstTakerAssetAmountForZrxFees = new BigNumber(208); + const expectedWorstTotalTakerAssetAmount = expectedWorstTakerAssetAmountForMakerAsset.plus( + expectedWorstTakerAssetAmountForZrxFees, + ); + expect(swapQuote.worstCaseQuoteInfo.takerTokenAmount).to.bignumber.equal( + expectedWorstTakerAssetAmountForMakerAsset, + ); + expect(swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal( + expectedWorstTakerAssetAmountForZrxFees, + ); + expect(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal( + expectedWorstTotalTakerAssetAmount, + ); }); }); }); diff --git a/packages/asset-buyer/test/asset_buyer_test.ts b/packages/asset-buyer/test/swap_quoter_test.ts similarity index 63% rename from packages/asset-buyer/test/asset_buyer_test.ts rename to packages/asset-buyer/test/swap_quoter_test.ts index f117b4d7ad..b0282e03ae 100644 --- a/packages/asset-buyer/test/asset_buyer_test.ts +++ b/packages/asset-buyer/test/swap_quoter_test.ts @@ -7,14 +7,15 @@ import * as chai from 'chai'; import 'mocha'; import * as TypeMoq from 'typemoq'; -import { AssetBuyer } from '../src'; +import { SwapQuoter } from '../src'; import { constants } from '../src/constants'; import { LiquidityForAssetData, OrderProvider, OrdersAndFillableAmounts } from '../src/types'; import { chaiSetup } from './utils/chai_setup'; import { - mockAvailableAssetDatas, - mockedAssetBuyerWithOrdersAndFillableAmounts, + mockAvailableMakerAssetDatas, + mockAvailableTakerAssetDatas, + mockedSwapQuoterWithOrdersAndFillableAmounts, orderProviderMock, } from './utils/mocks'; @@ -22,7 +23,8 @@ chaiSetup.configure(); const expect = chai.expect; const FAKE_SRA_URL = 'https://fakeurl.com'; -const FAKE_ASSET_DATA = '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48'; +const FAKE_TAKER_ASSET_DATA = '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48'; +const FAKE_MAKER_ASSET_DATA = '0xf47261b00000000000000000000000009f5B0C7e1623793bF0620569b9749e79DF6D0bC5'; const TOKEN_DECIMALS = 18; const DAI_ASSET_DATA = '0xf47261b000000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359"'; const WETH_ASSET_DATA = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; @@ -38,19 +40,23 @@ const expectLiquidityResult = async ( ordersAndFillableAmounts: OrdersAndFillableAmounts, expectedLiquidityResult: LiquidityForAssetData, ) => { - const mockedAssetBuyer = mockedAssetBuyerWithOrdersAndFillableAmounts( + const mockedSwapQuoter = mockedSwapQuoterWithOrdersAndFillableAmounts( web3Provider, orderProvider, - FAKE_ASSET_DATA, + FAKE_MAKER_ASSET_DATA, + WETH_ASSET_DATA, ordersAndFillableAmounts, ); - const liquidityResult = await mockedAssetBuyer.object.getLiquidityForAssetDataAsync(FAKE_ASSET_DATA); + const liquidityResult = await mockedSwapQuoter.object.getLiquidityForMakerTakerAssetDataPairAsync( + FAKE_MAKER_ASSET_DATA, + WETH_ASSET_DATA, + ); expect(liquidityResult).to.deep.equal(expectedLiquidityResult); }; // tslint:disable:custom-no-magic-numbers -describe('AssetBuyer', () => { - describe('getLiquidityForAssetDataAsync', () => { +describe('SwapQuoter', () => { + describe('getLiquidityForMakerTakerAssetDataPairAsync', () => { const mockWeb3Provider = TypeMoq.Mock.ofType(Web3ProviderEngine); const mockOrderProvider = orderProviderMock(); @@ -65,40 +71,51 @@ describe('AssetBuyer', () => { }); describe('validation', () => { - it('should ensure assetData is a string', async () => { - const assetBuyer = AssetBuyer.getAssetBuyerForStandardRelayerAPIUrl( + it('should ensure takerAssetData is a string', async () => { + const swapQuoter = SwapQuoter.getSwapQuoterForStandardRelayerAPIUrl( mockWeb3Provider.object, FAKE_SRA_URL, ); - expect(assetBuyer.getLiquidityForAssetDataAsync(false as any)).to.be.rejectedWith( - 'Expected assetData to be of type string, encountered: false', + expect( + swapQuoter.getLiquidityForMakerTakerAssetDataPairAsync(FAKE_MAKER_ASSET_DATA, false as any), + ).to.be.rejectedWith('Expected takerAssetData to be of type string, encountered: false'); + }); + it('should ensure makerAssetData is a string', async () => { + const swapQuoter = SwapQuoter.getSwapQuoterForStandardRelayerAPIUrl( + mockWeb3Provider.object, + FAKE_SRA_URL, ); + + expect( + swapQuoter.getLiquidityForMakerTakerAssetDataPairAsync(false as any, FAKE_TAKER_ASSET_DATA), + ).to.be.rejectedWith('Expected makerAssetData to be of type string, encountered: false'); }); }); describe('asset pair not supported', () => { - it('should return 0s when no asset pair not supported', async () => { - mockAvailableAssetDatas(mockOrderProvider, FAKE_ASSET_DATA, []); + it('should return 0s when no asset pair are supported', async () => { + mockAvailableMakerAssetDatas(mockOrderProvider, FAKE_TAKER_ASSET_DATA, []); - const assetBuyer = new AssetBuyer(mockWeb3Provider.object, mockOrderProvider.object); - const liquidityResult = await assetBuyer.getLiquidityForAssetDataAsync(FAKE_ASSET_DATA); + const swapQuoter = new SwapQuoter(mockWeb3Provider.object, mockOrderProvider.object); + const liquidityResult = await swapQuoter.getLiquidityForMakerTakerAssetDataPairAsync(FAKE_MAKER_ASSET_DATA, FAKE_TAKER_ASSET_DATA); expect(liquidityResult).to.deep.equal({ - tokensAvailableInBaseUnits: new BigNumber(0), - ethValueAvailableInWei: new BigNumber(0), + makerTokensAvailableInBaseUnits: new BigNumber(0), + takerTokensAvailableInBaseUnits: new BigNumber(0), }); }); + it('should return 0s when only other asset pair supported', async () => { - mockAvailableAssetDatas(mockOrderProvider, FAKE_ASSET_DATA, [DAI_ASSET_DATA]); + mockAvailableMakerAssetDatas(mockOrderProvider, FAKE_TAKER_ASSET_DATA, [DAI_ASSET_DATA]); - const assetBuyer = new AssetBuyer(mockWeb3Provider.object, mockOrderProvider.object); - const liquidityResult = await assetBuyer.getLiquidityForAssetDataAsync(FAKE_ASSET_DATA); + const swapQuoter = new SwapQuoter(mockWeb3Provider.object, mockOrderProvider.object); + const liquidityResult = await swapQuoter.getLiquidityForMakerTakerAssetDataPairAsync(FAKE_MAKER_ASSET_DATA, FAKE_TAKER_ASSET_DATA); expect(liquidityResult).to.deep.equal({ - tokensAvailableInBaseUnits: new BigNumber(0), - ethValueAvailableInWei: new BigNumber(0), + makerTokensAvailableInBaseUnits: new BigNumber(0), + takerTokensAvailableInBaseUnits: new BigNumber(0), }); }); - }); + }); describe('assetData is supported', () => { // orders @@ -112,7 +129,7 @@ describe('AssetBuyer', () => { }); beforeEach(() => { - mockAvailableAssetDatas(mockOrderProvider, FAKE_ASSET_DATA, [WETH_ASSET_DATA]); + mockAvailableMakerAssetDatas(mockOrderProvider, WETH_ASSET_DATA, [FAKE_MAKER_ASSET_DATA]); }); it('should return 0s when no orders available', async () => { @@ -121,8 +138,8 @@ describe('AssetBuyer', () => { remainingFillableMakerAssetAmounts: [], }; const expectedResult = { - tokensAvailableInBaseUnits: new BigNumber(0), - ethValueAvailableInWei: new BigNumber(0), + makerTokensAvailableInBaseUnits: new BigNumber(0), + takerTokensAvailableInBaseUnits: new BigNumber(0), }; await expectLiquidityResult( mockWeb3Provider.object, @@ -139,11 +156,12 @@ describe('AssetBuyer', () => { remainingFillableMakerAssetAmounts: orders.map(o => o.makerAssetAmount), }; - const expectedTokensAvailable = orders[0].makerAssetAmount.plus(orders[1].makerAssetAmount); - const expectedEthValueAvailable = orders[0].takerAssetAmount.plus(orders[1].takerAssetAmount); + const expectedMakerTokensAvailable = orders[0].makerAssetAmount.plus(orders[1].makerAssetAmount); + const expectedTakerTokensAvailable = orders[0].takerAssetAmount.plus(orders[1].takerAssetAmount); + const expectedResult = { - tokensAvailableInBaseUnits: expectedTokensAvailable, - ethValueAvailableInWei: expectedEthValueAvailable, + makerTokensAvailableInBaseUnits: expectedMakerTokensAvailable, + takerTokensAvailableInBaseUnits: expectedTakerTokensAvailable, }; await expectLiquidityResult( @@ -159,9 +177,10 @@ describe('AssetBuyer', () => { orders: [sellTwoTokensFor1Weth], remainingFillableMakerAssetAmounts: [baseUnitAmount(1)], }; + const expectedResult = { - tokensAvailableInBaseUnits: baseUnitAmount(1), - ethValueAvailableInWei: baseUnitAmount(0.5, WETH_DECIMALS), + makerTokensAvailableInBaseUnits: baseUnitAmount(1), + takerTokensAvailableInBaseUnits: baseUnitAmount(0.5, WETH_DECIMALS), }; await expectLiquidityResult( @@ -177,9 +196,10 @@ describe('AssetBuyer', () => { orders: [sellTwoTokensFor1Weth, sellTenTokensFor10Weth], remainingFillableMakerAssetAmounts: [baseUnitAmount(1), baseUnitAmount(3)], }; + const expectedResult = { - tokensAvailableInBaseUnits: baseUnitAmount(4), - ethValueAvailableInWei: baseUnitAmount(3.5, WETH_DECIMALS), + makerTokensAvailableInBaseUnits: baseUnitAmount(4), + takerTokensAvailableInBaseUnits: baseUnitAmount(3.5, WETH_DECIMALS), }; await expectLiquidityResult( @@ -195,9 +215,10 @@ describe('AssetBuyer', () => { orders: [sellTwoTokensFor1Weth, sellTenTokensFor10Weth], remainingFillableMakerAssetAmounts: [baseUnitAmount(0), baseUnitAmount(0)], }; + const expectedResult = { - tokensAvailableInBaseUnits: baseUnitAmount(0), - ethValueAvailableInWei: baseUnitAmount(0, WETH_DECIMALS), + makerTokensAvailableInBaseUnits: baseUnitAmount(0), + takerTokensAvailableInBaseUnits: baseUnitAmount(0, WETH_DECIMALS), }; await expectLiquidityResult( diff --git a/packages/asset-buyer/test/utils/migrate.ts b/packages/asset-buyer/test/utils/migrate.ts new file mode 100644 index 0000000000..665ce0faaa --- /dev/null +++ b/packages/asset-buyer/test/utils/migrate.ts @@ -0,0 +1,18 @@ +import { ContractAddresses } from '@0x/contract-addresses'; +import { devConstants } from '@0x/dev-utils'; +import { runMigrationsOnceAsync } from '@0x/migrations'; + +import { provider } from './web3_wrapper'; + +/** + * Configures and runs the migrations exactly once. Any subsequent times this is + * called, it returns the cached addresses. + * @returns The addresses of contracts that were deployed during the migrations. + */ +export async function migrateOnceAsync(): Promise { + const txDefaults = { + gas: devConstants.GAS_LIMIT, + from: devConstants.TESTRPC_FIRST_ADDRESS, + }; + return runMigrationsOnceAsync(provider, txDefaults); +} diff --git a/packages/asset-buyer/test/utils/mocks.ts b/packages/asset-buyer/test/utils/mocks.ts index e5552fd48f..1fd526ed12 100644 --- a/packages/asset-buyer/test/utils/mocks.ts +++ b/packages/asset-buyer/test/utils/mocks.ts @@ -1,7 +1,7 @@ import { Web3ProviderEngine } from '@0x/subproviders'; import * as TypeMoq from 'typemoq'; -import { SwapQuoter } from '../../src/asset_buyer'; +import { SwapQuoter } from '../../src/swap_quoter'; import { OrderProvider, OrderProviderResponse, OrdersAndFillableAmounts } from '../../src/types'; // tslint:disable:promise-function-async @@ -26,7 +26,7 @@ export const orderProviderMock = () => { return TypeMoq.Mock.ofType(OrderProviderClass, TypeMoq.MockBehavior.Strict); }; -export const mockAvailableAssetDatas = ( +export const mockAvailableMakerAssetDatas = ( mockOrderProvider: TypeMoq.IMock, assetData: string, availableAssetDatas: string[], @@ -39,34 +39,49 @@ export const mockAvailableAssetDatas = ( .verifiable(TypeMoq.Times.once()); }; -const partiallyMockedAssetBuyer = ( +export const mockAvailableTakerAssetDatas = ( + mockOrderProvider: TypeMoq.IMock, + assetData: string, + availableAssetDatas: string[], +) => { + mockOrderProvider + .setup(op => op.getAvailableTakerAssetDatasAsync(TypeMoq.It.isValue(assetData))) + .returns(() => { + return Promise.resolve(availableAssetDatas); + }) + .verifiable(TypeMoq.Times.once()); +}; + +const partiallyMockedSwapQuoter = ( provider: Web3ProviderEngine, orderProvider: OrderProvider, ): TypeMoq.IMock => { - const rawAssetBuyer = new SwapQuoter(provider, orderProvider); - const mockedAssetBuyer = TypeMoq.Mock.ofInstance(rawAssetBuyer, TypeMoq.MockBehavior.Loose, false); - mockedAssetBuyer.callBase = true; - return mockedAssetBuyer; + const rawSwapQuoter = new SwapQuoter(provider, orderProvider); + const mockedSwapQuoter = TypeMoq.Mock.ofInstance(rawSwapQuoter, TypeMoq.MockBehavior.Loose, false); + mockedSwapQuoter.callBase = true; + return mockedSwapQuoter; }; const mockGetOrdersAndAvailableAmounts = ( - mockedAssetBuyer: TypeMoq.IMock, - assetData: string, + mockedSwapQuoter: TypeMoq.IMock, + makerAssetData: string, + takerAssetData: string, ordersAndFillableAmounts: OrdersAndFillableAmounts, ): void => { - mockedAssetBuyer - .setup(a => a.getOrdersAndFillableAmountsAsync(assetData, false)) + mockedSwapQuoter + .setup(a => a.getOrdersAndFillableAmountsAsync(makerAssetData, takerAssetData, false)) .returns(() => Promise.resolve(ordersAndFillableAmounts)) .verifiable(TypeMoq.Times.once()); }; -export const mockedAssetBuyerWithOrdersAndFillableAmounts = ( +export const mockedSwapQuoterWithOrdersAndFillableAmounts = ( provider: Web3ProviderEngine, orderProvider: OrderProvider, - assetData: string, + makerAssetData: string, + takerAssetData: string, ordersAndFillableAmounts: OrdersAndFillableAmounts, ): TypeMoq.IMock => { - const mockedAssetBuyer = partiallyMockedAssetBuyer(provider, orderProvider); - mockGetOrdersAndAvailableAmounts(mockedAssetBuyer, assetData, ordersAndFillableAmounts); - return mockedAssetBuyer; + const mockedAssetQuoter = partiallyMockedSwapQuoter(provider, orderProvider); + mockGetOrdersAndAvailableAmounts(mockedAssetQuoter, makerAssetData, takerAssetData, ordersAndFillableAmounts); + return mockedAssetQuoter; }; diff --git a/packages/asset-buyer/test/utils/swap_quote.ts b/packages/asset-buyer/test/utils/swap_quote.ts new file mode 100644 index 0000000000..f2445f2d5c --- /dev/null +++ b/packages/asset-buyer/test/utils/swap_quote.ts @@ -0,0 +1,41 @@ +import { FillScenarios } from '@0x/fill-scenarios'; +import { orderFactory } from '@0x/order-utils/lib/src/order_factory'; +import { SignedOrder } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import { SupportedProvider } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { SwapQuote } from '../../src'; + +const ZERO_BIG_NUMBER = new BigNumber(0); + +export const getSignedOrdersWithNoFees = (makerAssetData: string, takerAssetData: string, makerAddress: string, takerAddress: string, fillableAmounts: BigNumber[]): SignedOrder[] => { + return _.map(fillableAmounts, (fillableAmount: BigNumber) => orderFactory.createSignedOrderFromPartial( + { + makerAddress, + makerAssetAmount: fillableAmount, + makerAssetData, + takerAssetAmount: fillableAmount, + takerAssetData, + })); +}; + +export const getFullyFillableSwapQuoteWithNoFees = (makerAssetData: string, takerAssetData: string, orders: SignedOrder[]): SwapQuote => { + const makerAssetFillAmount = _.reduce(orders, (a: BigNumber, c: SignedOrder) => a.plus(c.makerAssetAmount), ZERO_BIG_NUMBER); + const totalTakerTokenAmount = _.reduce(orders, (a: BigNumber, c: SignedOrder) => a.plus(c.takerAssetAmount), ZERO_BIG_NUMBER); + const quoteInfo = { + takerTokenAmount: totalTakerTokenAmount, + feeTakerTokenAmount: ZERO_BIG_NUMBER, + totalTakerTokenAmount, + }; + + return { + makerAssetData, + takerAssetData, + orders, + feeOrders: [], + makerAssetFillAmount, + bestCaseQuoteInfo: quoteInfo, + worstCaseQuoteInfo: quoteInfo, + }; +}; diff --git a/packages/asset-buyer/test/utils/web3_wrapper.ts b/packages/asset-buyer/test/utils/web3_wrapper.ts new file mode 100644 index 0000000000..32f8543267 --- /dev/null +++ b/packages/asset-buyer/test/utils/web3_wrapper.ts @@ -0,0 +1,8 @@ +import { web3Factory } from '@0x/dev-utils'; +import { Web3ProviderEngine } from '@0x/subproviders'; +import { Web3Wrapper } from '@0x/web3-wrapper'; + +const provider: Web3ProviderEngine = web3Factory.getRpcProvider({ shouldUseInProcessGanache: true }); +const web3Wrapper = new Web3Wrapper(provider); + +export { provider, web3Wrapper };