feat(asset-buyer): Custom InsufficientAssetLiquidityError error
BREAKING CHANGE: A custom InsufficientAssetLiquidityError error is now raised when there is insufficient liquidity
This commit is contained in:
parent
6d45beccad
commit
c6c45095a8
@ -117,6 +117,16 @@ export enum AssetBuyerError {
|
|||||||
TransactionValueTooLow = 'TRANSACTION_VALUE_TOO_LOW',
|
TransactionValueTooLow = 'TRANSACTION_VALUE_TOO_LOW',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class InsufficientAssetLiquidityError extends Error {
|
||||||
|
public numAssetsAvailable: BigNumber;
|
||||||
|
constructor(numAssetsAvailable: BigNumber) {
|
||||||
|
super(AssetBuyerError.InsufficientAssetLiquidity);
|
||||||
|
this.numAssetsAvailable = numAssetsAvailable;
|
||||||
|
// Setting prototype so instanceof works. See https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
||||||
|
Object.setPrototypeOf(this, InsufficientAssetLiquidityError.prototype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface OrdersAndFillableAmounts {
|
export interface OrdersAndFillableAmounts {
|
||||||
orders: SignedOrder[];
|
orders: SignedOrder[];
|
||||||
remainingFillableMakerAssetAmounts: BigNumber[];
|
remainingFillableMakerAssetAmounts: BigNumber[];
|
||||||
|
@ -3,7 +3,13 @@ import { BigNumber } from '@0x/utils';
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { constants } from '../constants';
|
import { constants } from '../constants';
|
||||||
import { AssetBuyerError, BuyQuote, BuyQuoteInfo, OrdersAndFillableAmounts } from '../types';
|
import {
|
||||||
|
AssetBuyerError,
|
||||||
|
BuyQuote,
|
||||||
|
BuyQuoteInfo,
|
||||||
|
InsufficientAssetLiquidityError,
|
||||||
|
OrdersAndFillableAmounts,
|
||||||
|
} from '../types';
|
||||||
|
|
||||||
import { orderUtils } from './order_utils';
|
import { orderUtils } from './order_utils';
|
||||||
|
|
||||||
@ -33,7 +39,8 @@ export const buyQuoteCalculator = {
|
|||||||
});
|
});
|
||||||
// if we do not have enough orders to cover the desired assetBuyAmount, throw
|
// if we do not have enough orders to cover the desired assetBuyAmount, throw
|
||||||
if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) {
|
if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) {
|
||||||
throw new Error(AssetBuyerError.InsufficientAssetLiquidity);
|
const amountRemaining = assetBuyAmount.minus(remainingFillAmount);
|
||||||
|
throw new InsufficientAssetLiquidityError(amountRemaining);
|
||||||
}
|
}
|
||||||
// if we are not buying ZRX:
|
// if we are not buying ZRX:
|
||||||
// given the orders calculated above, find the fee-orders that cover the desired assetBuyAmount (with slippage)
|
// given the orders calculated above, find the fee-orders that cover the desired assetBuyAmount (with slippage)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { orderFactory } from '@0x/order-utils/lib/src/order_factory';
|
import { orderFactory } from '@0x/order-utils/lib/src/order_factory';
|
||||||
|
import { SignedOrder } from '@0x/types';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import * as chai from 'chai';
|
import * as chai from 'chai';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
@ -8,6 +9,7 @@ import { AssetBuyerError, OrdersAndFillableAmounts } from '../src/types';
|
|||||||
import { buyQuoteCalculator } from '../src/utils/buy_quote_calculator';
|
import { buyQuoteCalculator } from '../src/utils/buy_quote_calculator';
|
||||||
|
|
||||||
import { chaiSetup } from './utils/chai_setup';
|
import { chaiSetup } from './utils/chai_setup';
|
||||||
|
import { testHelpers } from './utils/test_helpers';
|
||||||
|
|
||||||
chaiSetup.configure();
|
chaiSetup.configure();
|
||||||
const expect = chai.expect;
|
const expect = chai.expect;
|
||||||
@ -15,6 +17,10 @@ const expect = chai.expect;
|
|||||||
// tslint:disable:custom-no-magic-numbers
|
// tslint:disable:custom-no-magic-numbers
|
||||||
describe('buyQuoteCalculator', () => {
|
describe('buyQuoteCalculator', () => {
|
||||||
describe('#calculate', () => {
|
describe('#calculate', () => {
|
||||||
|
let firstOrder: SignedOrder;
|
||||||
|
let firstRemainingFillAmount: BigNumber;
|
||||||
|
let secondOrder: SignedOrder;
|
||||||
|
let secondRemainingFillAmount: BigNumber;
|
||||||
let ordersAndFillableAmounts: OrdersAndFillableAmounts;
|
let ordersAndFillableAmounts: OrdersAndFillableAmounts;
|
||||||
let smallFeeOrderAndFillableAmount: OrdersAndFillableAmounts;
|
let smallFeeOrderAndFillableAmount: OrdersAndFillableAmounts;
|
||||||
let allFeeOrdersAndFillableAmounts: OrdersAndFillableAmounts;
|
let allFeeOrdersAndFillableAmounts: OrdersAndFillableAmounts;
|
||||||
@ -24,18 +30,18 @@ describe('buyQuoteCalculator', () => {
|
|||||||
// 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)
|
// 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
|
// 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)
|
// 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({
|
firstOrder = orderFactory.createSignedOrderFromPartial({
|
||||||
makerAssetAmount: new BigNumber(400),
|
makerAssetAmount: new BigNumber(400),
|
||||||
takerAssetAmount: new BigNumber(100),
|
takerAssetAmount: new BigNumber(100),
|
||||||
takerFee: new BigNumber(200),
|
takerFee: new BigNumber(200),
|
||||||
});
|
});
|
||||||
const firstRemainingFillAmount = new BigNumber(200);
|
firstRemainingFillAmount = new BigNumber(200);
|
||||||
const secondOrder = orderFactory.createSignedOrderFromPartial({
|
secondOrder = orderFactory.createSignedOrderFromPartial({
|
||||||
makerAssetAmount: new BigNumber(200),
|
makerAssetAmount: new BigNumber(200),
|
||||||
takerAssetAmount: new BigNumber(100),
|
takerAssetAmount: new BigNumber(100),
|
||||||
takerFee: new BigNumber(100),
|
takerFee: new BigNumber(100),
|
||||||
});
|
});
|
||||||
const secondRemainingFillAmount = secondOrder.makerAssetAmount;
|
secondRemainingFillAmount = secondOrder.makerAssetAmount;
|
||||||
ordersAndFillableAmounts = {
|
ordersAndFillableAmounts = {
|
||||||
orders: [firstOrder, secondOrder],
|
orders: [firstOrder, secondOrder],
|
||||||
remainingFillableMakerAssetAmounts: [firstRemainingFillAmount, secondRemainingFillAmount],
|
remainingFillableMakerAssetAmounts: [firstRemainingFillAmount, secondRemainingFillAmount],
|
||||||
@ -61,9 +67,9 @@ describe('buyQuoteCalculator', () => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
it('should throw if not enough maker asset liquidity', () => {
|
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
|
// we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units
|
||||||
expect(() =>
|
const errorFunction = () => {
|
||||||
buyQuoteCalculator.calculate(
|
buyQuoteCalculator.calculate(
|
||||||
ordersAndFillableAmounts,
|
ordersAndFillableAmounts,
|
||||||
smallFeeOrderAndFillableAmount,
|
smallFeeOrderAndFillableAmount,
|
||||||
@ -71,8 +77,61 @@ describe('buyQuoteCalculator', () => {
|
|||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
false,
|
false,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(400));
|
||||||
|
});
|
||||||
|
it('should throw if not enough maker asset liquidity (partially filled order)', () => {
|
||||||
|
const firstOrderAndFillableAmount: OrdersAndFillableAmounts = {
|
||||||
|
orders: [firstOrder],
|
||||||
|
remainingFillableMakerAssetAmounts: [firstRemainingFillAmount],
|
||||||
|
};
|
||||||
|
|
||||||
|
const errorFunction = () => {
|
||||||
|
buyQuoteCalculator.calculate(
|
||||||
|
firstOrderAndFillableAmount,
|
||||||
|
smallFeeOrderAndFillableAmount,
|
||||||
|
new BigNumber(201),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(200));
|
||||||
|
});
|
||||||
|
it('should throw if not enough maker asset liquidity (completely fillable order)', () => {
|
||||||
|
const completelyFillableOrder = orderFactory.createSignedOrderFromPartial({
|
||||||
|
makerAssetAmount: new BigNumber(123),
|
||||||
|
takerAssetAmount: new BigNumber(100),
|
||||||
|
takerFee: new BigNumber(200),
|
||||||
|
});
|
||||||
|
const completelyFillableOrdersAndFillableAmount: OrdersAndFillableAmounts = {
|
||||||
|
orders: [completelyFillableOrder],
|
||||||
|
remainingFillableMakerAssetAmounts: [completelyFillableOrder.makerAssetAmount],
|
||||||
|
};
|
||||||
|
const errorFunction = () => {
|
||||||
|
buyQuoteCalculator.calculate(
|
||||||
|
completelyFillableOrdersAndFillableAmount,
|
||||||
|
smallFeeOrderAndFillableAmount,
|
||||||
|
new BigNumber(124),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(123));
|
||||||
|
});
|
||||||
|
it('should not throw if order is fillable', () => {
|
||||||
|
expect(() =>
|
||||||
|
buyQuoteCalculator.calculate(
|
||||||
|
ordersAndFillableAmounts,
|
||||||
|
allFeeOrdersAndFillableAmounts,
|
||||||
|
new BigNumber(300),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
),
|
),
|
||||||
).to.throw(AssetBuyerError.InsufficientAssetLiquidity);
|
).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 300 makerAsset units but the ZRX order is only enough to fill the first order, which only has 200 makerAssetUnits available
|
||||||
|
22
packages/asset-buyer/test/utils/test_helpers.ts
Normal file
22
packages/asset-buyer/test/utils/test_helpers.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
|
import { InsufficientAssetLiquidityError } from '../../src/types';
|
||||||
|
|
||||||
|
export const testHelpers = {
|
||||||
|
expectInsufficientLiquidityError: (
|
||||||
|
expect: Chai.ExpectStatic,
|
||||||
|
functionWhichTriggersError: () => void,
|
||||||
|
expectedNumAvailable: BigNumber,
|
||||||
|
): void => {
|
||||||
|
let errorThrown = false;
|
||||||
|
try {
|
||||||
|
functionWhichTriggersError();
|
||||||
|
} catch (e) {
|
||||||
|
errorThrown = true;
|
||||||
|
expect(e).to.be.instanceOf(InsufficientAssetLiquidityError);
|
||||||
|
expect(e.numAssetsAvailable).to.be.bignumber.equal(expectedNumAvailable);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(errorThrown).to.be.true();
|
||||||
|
},
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user