aboutsummaryrefslogtreecommitdiffstats
path: root/packages/asset-buyer/test/buy_quote_calculator_test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/asset-buyer/test/buy_quote_calculator_test.ts')
-rw-r--r--packages/asset-buyer/test/buy_quote_calculator_test.ts160
1 files changed, 160 insertions, 0 deletions
diff --git a/packages/asset-buyer/test/buy_quote_calculator_test.ts b/packages/asset-buyer/test/buy_quote_calculator_test.ts
new file mode 100644
index 000000000..b987b45a8
--- /dev/null
+++ b/packages/asset-buyer/test/buy_quote_calculator_test.ts
@@ -0,0 +1,160 @@
+import { orderFactory } from '@0xproject/order-utils/lib/src/order_factory';
+import { BigNumber } from '@0xproject/utils';
+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 { chaiSetup } from './utils/chai_setup';
+
+chaiSetup.configure();
+const expect = chai.expect;
+
+// tslint:disable:custom-no-magic-numbers
+describe('buyQuoteCalculator', () => {
+ describe('#calculate', () => {
+ let ordersAndFillableAmounts: OrdersAndFillableAmounts;
+ let smallFeeOrderAndFillableAmount: OrdersAndFillableAmounts;
+ let allFeeOrdersAndFillableAmounts: OrdersAndFillableAmounts;
+ beforeEach(() => {
+ // generate two orders for our desired maker asset
+ // the first order has a rate of 4 makerAsset / WETH with a takerFee of 200 ZRX and has only 200 / 400 makerAsset units left to fill (half fillable)
+ // the second order has a rate of 2 makerAsset / WETH with a takerFee of 100 ZRX and has 200 / 200 makerAsset units left to fill (completely fillable)
+ // generate one order for fees
+ // the fee order has a rate of 1 ZRX / WETH with no taker fee and has 100 ZRX left to fill (completely fillable)
+ const firstOrder = orderFactory.createSignedOrderFromPartial({
+ makerAssetAmount: new BigNumber(400),
+ takerAssetAmount: new BigNumber(100),
+ takerFee: new BigNumber(200),
+ });
+ const firstRemainingFillAmount = new BigNumber(200);
+ const secondOrder = orderFactory.createSignedOrderFromPartial({
+ makerAssetAmount: new BigNumber(200),
+ takerAssetAmount: new BigNumber(100),
+ takerFee: new BigNumber(100),
+ });
+ const secondRemainingFillAmount = secondOrder.makerAssetAmount;
+ ordersAndFillableAmounts = {
+ orders: [firstOrder, secondOrder],
+ remainingFillableMakerAssetAmounts: [firstRemainingFillAmount, secondRemainingFillAmount],
+ };
+ const smallFeeOrder = orderFactory.createSignedOrderFromPartial({
+ makerAssetAmount: new BigNumber(100),
+ takerAssetAmount: new BigNumber(100),
+ });
+ smallFeeOrderAndFillableAmount = {
+ orders: [smallFeeOrder],
+ remainingFillableMakerAssetAmounts: [smallFeeOrder.makerAssetAmount],
+ };
+ const largeFeeOrder = orderFactory.createSignedOrderFromPartial({
+ makerAssetAmount: new BigNumber(110),
+ takerAssetAmount: new BigNumber(200),
+ takerFee: new BigNumber(10),
+ });
+ allFeeOrdersAndFillableAmounts = {
+ orders: [smallFeeOrder, largeFeeOrder],
+ remainingFillableMakerAssetAmounts: [
+ smallFeeOrder.makerAssetAmount,
+ largeFeeOrder.makerAssetAmount.minus(largeFeeOrder.takerFee),
+ ],
+ };
+ });
+ it('should throw if not enough maker asset liquidity', () => {
+ // we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units
+ expect(() =>
+ buyQuoteCalculator.calculate(
+ ordersAndFillableAmounts,
+ smallFeeOrderAndFillableAmount,
+ new BigNumber(500),
+ 0,
+ 0,
+ ),
+ ).to.throw(AssetBuyerError.InsufficientAssetLiquidity);
+ });
+ 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(
+ ordersAndFillableAmounts,
+ smallFeeOrderAndFillableAmount,
+ new BigNumber(300),
+ 0,
+ 0,
+ ),
+ ).to.throw(AssetBuyerError.InsufficientZrxLiquidity);
+ });
+ it('calculates a correct buyQuote 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(
+ ordersAndFillableAmounts,
+ smallFeeOrderAndFillableAmount,
+ assetBuyAmount,
+ feePercentage,
+ slippagePercentage,
+ );
+ // test if orders are correct
+ expect(buyQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]);
+ expect(buyQuote.feeOrders).to.deep.equal([smallFeeOrderAndFillableAmount.orders[0]]);
+ // test if rates are correct
+ // 50 eth to fill the first order + 100 eth for fees
+ const expectedFillEthAmount = new BigNumber(150);
+ const expectedTotalEthAmount = expectedFillEthAmount.mul(feePercentage + 1);
+ const expectedFeeEthAmount = expectedTotalEthAmount.minus(expectedFillEthAmount);
+ const expectedEthPerAssetPrice = expectedFillEthAmount.div(assetBuyAmount);
+ expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount);
+ expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount);
+ expect(buyQuote.bestCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedEthPerAssetPrice);
+ // because we have no slippage protection, minRate is equal to maxRate
+ expect(buyQuote.worstCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount);
+ expect(buyQuote.worstCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount);
+ expect(buyQuote.worstCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedEthPerAssetPrice);
+ // test if feePercentage gets passed through
+ expect(buyQuote.feePercentage).to.equal(feePercentage);
+ });
+ it('calculates a correct buyQuote 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(
+ ordersAndFillableAmounts,
+ allFeeOrdersAndFillableAmounts,
+ assetBuyAmount,
+ feePercentage,
+ slippagePercentage,
+ );
+ // test if orders are correct
+ expect(buyQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders);
+ expect(buyQuote.feeOrders).to.deep.equal(allFeeOrdersAndFillableAmounts.orders);
+ // test if rates are correct
+ // 50 eth to fill the first order + 100 eth for fees
+ const expectedFillEthAmount = new BigNumber(150);
+ const expectedTotalEthAmount = expectedFillEthAmount.mul(feePercentage + 1);
+ const expectedFeeEthAmount = expectedTotalEthAmount.minus(expectedFillEthAmount);
+ const expectedEthPerAssetPrice = expectedFillEthAmount.div(assetBuyAmount);
+ expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount);
+ expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount);
+ expect(buyQuote.bestCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedEthPerAssetPrice);
+ // 100 eth to fill the first order + 200 eth for fees
+ const expectedWorstFillEthAmount = new BigNumber(300);
+ const expectedWorstTotalEthAmount = expectedWorstFillEthAmount.mul(feePercentage + 1);
+ const expectedWorstFeeEthAmount = expectedWorstTotalEthAmount.minus(expectedWorstFillEthAmount);
+ const expectedWorstEthPerAssetPrice = expectedWorstFillEthAmount.div(assetBuyAmount);
+ expect(buyQuote.worstCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedWorstFeeEthAmount);
+ expect(buyQuote.worstCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedWorstTotalEthAmount);
+ expect(buyQuote.worstCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedWorstEthPerAssetPrice);
+ // test if feePercentage gets passed through
+ expect(buyQuote.feePercentage).to.equal(feePercentage);
+ });
+ });
+});