added coverage for swap quote calculator

This commit is contained in:
David Sun
2019-07-18 13:35:15 -07:00
parent c375199daa
commit 65b2fa13ac
3 changed files with 181 additions and 20 deletions

View File

@@ -220,7 +220,7 @@ function calculateQuoteInfo(
let takerTokenAmount = marketOperation === MarketOperation.Sell ? tokenAmount : constants.ZERO_AMOUNT; let takerTokenAmount = marketOperation === MarketOperation.Sell ? tokenAmount : constants.ZERO_AMOUNT;
let zrxTakerTokenAmount = constants.ZERO_AMOUNT; let zrxTakerTokenAmount = constants.ZERO_AMOUNT;
if (!shouldDisableFeeOrderCalculations && isMakerAssetZrxToken) { if (isMakerAssetZrxToken) {
if (marketOperation === MarketOperation.Buy) { if (marketOperation === MarketOperation.Buy) {
takerTokenAmount = findTakerTokenAmountNeededToBuyZrx(ordersAndFillableAmounts, makerTokenAmount); takerTokenAmount = findTakerTokenAmountNeededToBuyZrx(ordersAndFillableAmounts, makerTokenAmount);
} else { } else {
@@ -229,7 +229,7 @@ function calculateQuoteInfo(
takerTokenAmount, takerTokenAmount,
); );
} }
} else if (!shouldDisableFeeOrderCalculations) { } else {
const findTokenAndZrxAmount = const findTokenAndZrxAmount =
marketOperation === MarketOperation.Buy marketOperation === MarketOperation.Buy
? findTakerTokenAndZrxAmountNeededToBuyAsset ? findTakerTokenAndZrxAmountNeededToBuyAsset
@@ -246,7 +246,7 @@ function calculateQuoteInfo(
} }
const zrxAmountToBuyAsset = tokenAndZrxAmountToBuyAsset[1]; const zrxAmountToBuyAsset = tokenAndZrxAmountToBuyAsset[1];
// find eth amount needed to buy zrx // find eth amount needed to buy zrx
zrxTakerTokenAmount = findTakerTokenAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset); zrxTakerTokenAmount = shouldDisableFeeOrderCalculations ? constants.ZERO_AMOUNT : findTakerTokenAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset);
} }
const feeTakerTokenAmount = zrxTakerTokenAmount; const feeTakerTokenAmount = zrxTakerTokenAmount;

View File

@@ -1,15 +1,13 @@
import { MarketOperation, SignedOrder } from '@0x/types'; import { MarketOperation } from '@0x/types';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
import * as chai from 'chai'; import * as chai from 'chai';
import 'mocha'; import 'mocha';
import * as TypeMoq from 'typemoq';
import { constants } from '../src/constants'; import { constants } from '../src/constants';
import { } from '../src/types';
import { affiliateFeeUtils } from '../src/utils/affiliate_fee_utils'; import { affiliateFeeUtils } from '../src/utils/affiliate_fee_utils';
import { chaiSetup } from './utils/chai_setup'; import { chaiSetup } from './utils/chai_setup';
import { getFullyFillableSwapQuoteWithNoFees, getSignedOrdersWithNoFees, getSignedOrdersWithFees, getFullyFillableSwapQuoteWithFees } from './utils/swap_quote'; import { getFullyFillableSwapQuoteWithFees, getFullyFillableSwapQuoteWithNoFees, getSignedOrdersWithFees, getSignedOrdersWithNoFees } from './utils/swap_quote';
chaiSetup.configure(); chaiSetup.configure();
const expect = chai.expect; const expect = chai.expect;
@@ -29,7 +27,7 @@ describe('affiliateFeeUtils', () => {
NULL_ADDRESS, NULL_ADDRESS,
NULL_ADDRESS, NULL_ADDRESS,
FILLABLE_FEE_AMOUNTS, FILLABLE_FEE_AMOUNTS,
) );
const fakeOrders = getSignedOrdersWithNoFees( const fakeOrders = getSignedOrdersWithNoFees(
FAKE_MAKER_ASSET_DATA, FAKE_MAKER_ASSET_DATA,
FAKE_TAKER_ASSET_DATA, FAKE_TAKER_ASSET_DATA,
@@ -57,7 +55,7 @@ describe('affiliateFeeUtils', () => {
const fakeSwapQuoteWithFees = getFullyFillableSwapQuoteWithFees( const fakeSwapQuoteWithFees = getFullyFillableSwapQuoteWithFees(
FAKE_MAKER_ASSET_DATA, FAKE_MAKER_ASSET_DATA,
FAKE_TAKER_ASSET_DATA, FAKE_TAKER_ASSET_DATA,
fakeOrders, fakeOrdersWithFees,
fakeFeeOrders, fakeFeeOrders,
MARKET_OPERATION, MARKET_OPERATION,
); );
@@ -83,4 +81,4 @@ describe('affiliateFeeUtils', () => {
expect(updatedSwapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.deep.equal(new BigNumber(14)); expect(updatedSwapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.deep.equal(new BigNumber(14));
}); });
}); });
}) });

View File

@@ -5,6 +5,7 @@ import * as chai from 'chai';
import * as _ from 'lodash'; import * as _ from 'lodash';
import 'mocha'; import 'mocha';
import { constants } from '../src/constants';
import { OrdersAndFillableAmounts, SwapQuoterError } from '../src/types'; import { OrdersAndFillableAmounts, SwapQuoterError } from '../src/types';
import { swapQuoteCalculator } from '../src/utils/swap_quote_calculator'; import { swapQuoteCalculator } from '../src/utils/swap_quote_calculator';
@@ -70,7 +71,7 @@ describe('swapQuoteCalculator', () => {
}); });
describe('InsufficientLiquidityError', () => { describe('InsufficientLiquidityError', () => {
it('should throw if not enough taker asset liquidity (multiple orders)', () => { it('should throw if not enough taker asset liquidity (multiple orders)', () => {
// we have 150 takerAsset units available to fill but attempt to calculate a quote for 200 takerAsset units // we have 150 takerAsset units available to sell but attempt to calculate a quote for 200 takerAsset units
const errorFunction = () => { const errorFunction = () => {
swapQuoteCalculator.calculateMarketSellSwapQuote( swapQuoteCalculator.calculateMarketSellSwapQuote(
ordersAndFillableAmounts, ordersAndFillableAmounts,
@@ -84,7 +85,7 @@ describe('swapQuoteCalculator', () => {
testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(150)); testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(150));
}); });
it('should throw if not enough taker asset liquidity (multiple orders with 20% slippage)', () => { it('should throw if not enough taker asset liquidity (multiple orders with 20% slippage)', () => {
// we have 150 takerAsset units available to fill but attempt to calculate a quote for 200 makerAsset units // we have 150 takerAsset units available to sell but attempt to calculate a quote for 200 takerAsset units
const errorFunction = () => { const errorFunction = () => {
swapQuoteCalculator.calculateMarketSellSwapQuote( swapQuoteCalculator.calculateMarketSellSwapQuote(
ordersAndFillableAmounts, ordersAndFillableAmounts,
@@ -98,7 +99,7 @@ describe('swapQuoteCalculator', () => {
testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(125)); testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(125));
}); });
it('should throw if not enough taker asset liquidity (multiple orders with 5% slippage)', () => { it('should throw if not enough taker asset liquidity (multiple orders with 5% slippage)', () => {
// we have 150 takerAsset units available to fill but attempt to calculate a quote for 200 makerAsset units // we have 150 takerAsset units available to fill but attempt to calculate a quote for 200 takerAsset units
const errorFunction = () => { const errorFunction = () => {
swapQuoteCalculator.calculateMarketSellSwapQuote( swapQuoteCalculator.calculateMarketSellSwapQuote(
ordersAndFillableAmounts, ordersAndFillableAmounts,
@@ -201,12 +202,12 @@ describe('swapQuoteCalculator', () => {
).to.not.throw(); ).to.not.throw();
}); });
it('should throw if not enough ZRX liquidity', () => { 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 // we request 75 takerAsset units but the ZRX order is only enough to fill the first order, which only has 50 takerAsset units available
expect(() => expect(() =>
swapQuoteCalculator.calculateMarketSellSwapQuote( swapQuoteCalculator.calculateMarketSellSwapQuote(
ordersAndFillableAmounts, ordersAndFillableAmounts,
smallFeeOrderAndFillableAmount, smallFeeOrderAndFillableAmount,
new BigNumber(125), new BigNumber(75),
0, 0,
false, false,
false, false,
@@ -214,7 +215,7 @@ describe('swapQuoteCalculator', () => {
).to.throw(SwapQuoterError.InsufficientZrxLiquidity); ).to.throw(SwapQuoterError.InsufficientZrxLiquidity);
}); });
it('calculates a correct swapQuote with no slippage', () => { it('calculates a correct swapQuote with no slippage', () => {
// we request 100 takerAsset units which can be filled using the first order // we request 50 takerAsset 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 // the first order requires a fee of 100 ZRX from the taker which can be filled by the feeOrder
const assetSellAmount = new BigNumber(50); const assetSellAmount = new BigNumber(50);
const slippagePercentage = 0; const slippagePercentage = 0;
@@ -230,7 +231,7 @@ describe('swapQuoteCalculator', () => {
expect(swapQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]); expect(swapQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]);
expect(swapQuote.feeOrders).to.deep.equal([smallFeeOrderAndFillableAmount.orders[0]]); expect(swapQuote.feeOrders).to.deep.equal([smallFeeOrderAndFillableAmount.orders[0]]);
// test if rates are correct // test if rates are correct
// 50 eth to fill the first order + 100 eth for fees // 50 takerAsset units to fill the first order + 100 takerAsset units for fees
const expectedMakerAssetAmountForTakerAsset = new BigNumber(200); const expectedMakerAssetAmountForTakerAsset = new BigNumber(200);
const expectedTakerAssetAmountForZrxFees = new BigNumber(100); const expectedTakerAssetAmountForZrxFees = new BigNumber(100);
const expectedTotalTakerAssetAmount = assetSellAmount.plus(expectedTakerAssetAmountForZrxFees); const expectedTotalTakerAssetAmount = assetSellAmount.plus(expectedTakerAssetAmountForZrxFees);
@@ -258,7 +259,7 @@ describe('swapQuoteCalculator', () => {
// we request 50 takerAsset units which can be filled using the first order // we request 50 takerAsset units which can be filled using the first order
// however with 50% slippage we are protecting the buy with 25 extra takerAssetUnits // however with 50% slippage we are protecting the buy with 25 extra takerAssetUnits
// so we need enough orders to fill 75 takerAssetUnits // so we need enough orders to fill 75 takerAssetUnits
// 150 takerAssetUnits can only be filled using both orders // 75 takerAssetUnits 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 // the first order requires a fee of 100 ZRX from the taker which can be filled by the feeOrder
const assetSellAmount = new BigNumber(50); const assetSellAmount = new BigNumber(50);
const slippagePercentage = 0.5; const slippagePercentage = 0.5;
@@ -274,7 +275,6 @@ describe('swapQuoteCalculator', () => {
expect(swapQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders); expect(swapQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders);
expect(swapQuote.feeOrders).to.deep.equal(allFeeOrdersAndFillableAmounts.orders); expect(swapQuote.feeOrders).to.deep.equal(allFeeOrdersAndFillableAmounts.orders);
// test if rates are correct // test if rates are correct
// 50 eth to fill the first order + 100 eth for fees
const expectedMakerAssetAmountForTakerAsset = new BigNumber(200); const expectedMakerAssetAmountForTakerAsset = new BigNumber(200);
const expectedTakerAssetAmountForZrxFees = new BigNumber(100); const expectedTakerAssetAmountForZrxFees = new BigNumber(100);
const expectedTotalTakerAssetAmount = assetSellAmount.plus(expectedTakerAssetAmountForZrxFees); const expectedTotalTakerAssetAmount = assetSellAmount.plus(expectedTakerAssetAmountForZrxFees);
@@ -286,7 +286,7 @@ describe('swapQuoteCalculator', () => {
expectedTakerAssetAmountForZrxFees, expectedTakerAssetAmountForZrxFees,
); );
expect(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(expectedTotalTakerAssetAmount); expect(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(expectedTotalTakerAssetAmount);
// 100 eth to fill the first order + 208 eth for fees
const expectedWorstMakerAssetAmountForTakerAsset = new BigNumber(100); const expectedWorstMakerAssetAmountForTakerAsset = new BigNumber(100);
const expectedWorstTakerAssetAmountForZrxFees = new BigNumber(99); const expectedWorstTakerAssetAmountForZrxFees = new BigNumber(99);
const expectedWorstTotalTakerAssetAmount = assetSellAmount.plus(expectedWorstTakerAssetAmountForZrxFees); const expectedWorstTotalTakerAssetAmount = assetSellAmount.plus(expectedWorstTakerAssetAmountForZrxFees);
@@ -301,6 +301,88 @@ describe('swapQuoteCalculator', () => {
expectedWorstTotalTakerAssetAmount, expectedWorstTotalTakerAssetAmount,
); );
}); });
it('calculates a correct swapQuote (with fee calculations disabled) with no slippage', () => {
// we request 50 takerAsset units which can be filled using the first order
const assetSellAmount = new BigNumber(50);
const slippagePercentage = 0;
const swapQuote = swapQuoteCalculator.calculateMarketSellSwapQuote(
ordersAndFillableAmounts,
smallFeeOrderAndFillableAmount,
assetSellAmount,
slippagePercentage,
false,
true,
);
// test if orders are correct
expect(swapQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]);
expect(swapQuote.feeOrders).to.deep.equal([]);
// test if rates are correct
const expectedMakerAssetAmountForTakerAsset = new BigNumber(200);
const expectedTotalTakerAssetAmount = assetSellAmount;
expect(swapQuote.bestCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(assetSellAmount);
expect(swapQuote.bestCaseQuoteInfo.makerTokenAmount).to.bignumber.equal(
expectedMakerAssetAmountForTakerAsset,
);
expect(swapQuote.bestCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(
constants.ZERO_AMOUNT,
);
expect(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(expectedTotalTakerAssetAmount);
// because we have no slippage protection, minRate is equal to maxRate
expect(swapQuote.worstCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(assetSellAmount);
expect(swapQuote.worstCaseQuoteInfo.makerTokenAmount).to.bignumber.equal(
expectedMakerAssetAmountForTakerAsset,
);
expect(swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(
constants.ZERO_AMOUNT,
);
expect(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(
expectedTotalTakerAssetAmount,
);
});
it('calculates a correct swapQuote (with fee calculatations disabled) with slippage', () => {
// we request 50 takerAsset units which can be filled using the first order
// however with 50% slippage we are protecting the buy with 25 extra takerAssetUnits
// so we need enough orders to fill 75 takerAssetUnits
// 50 takerAssetUnits 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 assetSellAmount = new BigNumber(50);
const slippagePercentage = 0.5;
const swapQuote = swapQuoteCalculator.calculateMarketSellSwapQuote(
ordersAndFillableAmounts,
allFeeOrdersAndFillableAmounts,
assetSellAmount,
slippagePercentage,
false,
true,
);
// test if orders are correct
expect(swapQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders);
expect(swapQuote.feeOrders).to.deep.equal([]);
// test if rates are correct
const expectedMakerAssetAmountForTakerAsset = new BigNumber(200);
const expectedTotalTakerAssetAmount = assetSellAmount;
expect(swapQuote.bestCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(assetSellAmount);
expect(swapQuote.bestCaseQuoteInfo.makerTokenAmount).to.bignumber.equal(
expectedMakerAssetAmountForTakerAsset,
);
expect(swapQuote.bestCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(
constants.ZERO_AMOUNT,
);
expect(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(expectedTotalTakerAssetAmount);
// 100 eth to fill the first order + 208 eth for fees
const expectedWorstMakerAssetAmountForTakerAsset = new BigNumber(100);
const expectedWorstTotalTakerAssetAmount = assetSellAmount;
expect(swapQuote.worstCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(assetSellAmount);
expect(swapQuote.worstCaseQuoteInfo.makerTokenAmount).to.bignumber.equal(
expectedWorstMakerAssetAmountForTakerAsset,
);
expect(swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(
constants.ZERO_AMOUNT,
);
expect(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(
expectedWorstTotalTakerAssetAmount,
);
});
}); });
describe('#calculateMarketBuySwapQuote', () => { describe('#calculateMarketBuySwapQuote', () => {
let firstOrder: SignedOrder; let firstOrder: SignedOrder;
@@ -588,5 +670,86 @@ describe('swapQuoteCalculator', () => {
expectedWorstTotalTakerAssetAmount, expectedWorstTotalTakerAssetAmount,
); );
}); });
it('calculates a correct swapQuote (with fee calculations disabled) 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 slippagePercentage = 0;
const swapQuote = swapQuoteCalculator.calculateMarketBuySwapQuote(
ordersAndFillableAmounts,
smallFeeOrderAndFillableAmount,
assetBuyAmount,
slippagePercentage,
false,
true,
);
// test if orders are correct
expect(swapQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]);
expect(swapQuote.feeOrders).to.deep.equal([]);
// test if rates are correct
// 50 eth to fill the first order + 100 eth for fees
const expectedTakerAssetAmountForMakerAsset = new BigNumber(50);
const expectedTotalTakerAssetAmount = expectedTakerAssetAmountForMakerAsset;
expect(swapQuote.bestCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(
expectedTakerAssetAmountForMakerAsset,
);
expect(swapQuote.bestCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(
constants.ZERO_AMOUNT,
);
expect(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(expectedTotalTakerAssetAmount);
// because we have no slippage protection, minRate is equal to maxRate
expect(swapQuote.worstCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(
expectedTakerAssetAmountForMakerAsset,
);
expect(swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(
constants.ZERO_AMOUNT,
);
expect(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(
expectedTotalTakerAssetAmount,
);
});
it('calculates a correct swapQuote (with fee calculations disabled) 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 slippagePercentage = 0.5;
const swapQuote = swapQuoteCalculator.calculateMarketBuySwapQuote(
ordersAndFillableAmounts,
allFeeOrdersAndFillableAmounts,
assetBuyAmount,
slippagePercentage,
false,
true,
);
// test if orders are correct
expect(swapQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders);
expect(swapQuote.feeOrders).to.deep.equal([]);
// test if rates are correct
// 50 eth to fill the first order + 100 eth for fees
const expectedTakerAssetAmountForMakerAsset = new BigNumber(50);
const expectedTotalTakerAssetAmount = expectedTakerAssetAmountForMakerAsset;
expect(swapQuote.bestCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(
expectedTakerAssetAmountForMakerAsset,
);
expect(swapQuote.bestCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(
constants.ZERO_AMOUNT,
);
expect(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(expectedTotalTakerAssetAmount);
// 100 eth to fill the first order + 208 eth for fees
const expectedWorstTakerAssetAmountForMakerAsset = new BigNumber(100);
const expectedWorstTotalTakerAssetAmount = expectedWorstTakerAssetAmountForMakerAsset;
expect(swapQuote.worstCaseQuoteInfo.takerTokenAmount).to.bignumber.equal(
expectedWorstTakerAssetAmountForMakerAsset,
);
expect(swapQuote.worstCaseQuoteInfo.feeTakerTokenAmount).to.bignumber.equal(
constants.ZERO_AMOUNT,
);
expect(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount).to.bignumber.equal(
expectedWorstTotalTakerAssetAmount,
);
});
}); });
}); });