aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/asset-buyer/src/index.ts1
-rw-r--r--packages/asset-buyer/src/types.ts6
-rw-r--r--packages/asset-buyer/src/utils/buy_quote_calculator.ts17
-rw-r--r--packages/asset-buyer/test/buy_quote_calculator_test.ts28
-rw-r--r--packages/asset-buyer/test/utils/test_helpers.ts4
-rw-r--r--packages/instant/src/constants.ts1
-rw-r--r--packages/instant/src/util/asset.ts17
-rw-r--r--packages/instant/src/util/buy_quote_updater.ts7
8 files changed, 72 insertions, 9 deletions
diff --git a/packages/asset-buyer/src/index.ts b/packages/asset-buyer/src/index.ts
index 8418edb42..59515e24f 100644
--- a/packages/asset-buyer/src/index.ts
+++ b/packages/asset-buyer/src/index.ts
@@ -18,6 +18,7 @@ export {
BuyQuoteExecutionOpts,
BuyQuoteInfo,
BuyQuoteRequestOpts,
+ InsufficientAssetLiquidityError,
OrderProvider,
OrderProviderRequest,
OrderProviderResponse,
diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts
index dfa17a22d..d435f337e 100644
--- a/packages/asset-buyer/src/types.ts
+++ b/packages/asset-buyer/src/types.ts
@@ -118,10 +118,10 @@ export enum AssetBuyerError {
}
export class InsufficientAssetLiquidityError extends Error {
- public numAssetsAvailable: BigNumber;
- constructor(numAssetsAvailable: BigNumber) {
+ public amountAvailableToFill: BigNumber;
+ constructor(amountAvailableToFill: BigNumber) {
super(AssetBuyerError.InsufficientAssetLiquidity);
- this.numAssetsAvailable = numAssetsAvailable;
+ this.amountAvailableToFill = amountAvailableToFill;
// 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);
}
diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts
index e52c9c5bf..23d3e9b24 100644
--- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts
+++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts
@@ -1,5 +1,6 @@
import { marketUtils, SignedOrder } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
import { constants } from '../constants';
@@ -39,8 +40,20 @@ export const buyQuoteCalculator = {
});
// if we do not have enough orders to cover the desired assetBuyAmount, throw
if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) {
- const amountRemaining = assetBuyAmount.minus(remainingFillAmount);
- throw new InsufficientAssetLiquidityError(amountRemaining);
+ // We needed the amount they requested to buy, plus the amount for slippage
+ const totalAmountRequested = assetBuyAmount.plus(slippageBufferAmount);
+ const amountUnableToFill = totalAmountRequested.minus(remainingFillAmount);
+ // multiplerNeededWithSlippage represents what we need to multiply the assetBuyAmount by
+ // in order to get the total amount needed considering slippage
+ // i.e. if slippagePercent was 0.2 (20%), multiplerNeededWithSlippage would be 1.2
+ const multiplerNeededWithSlippage = new BigNumber(1).plus(slippagePercentage);
+ // Given amountAvailableToFillConsideringSlippage * multiplerNeededWithSlippage = amountUnableToFill
+ // We divide amountUnableToFill by multiplerNeededWithSlippage to determine amountAvailableToFillConsideringSlippage
+ const amountAvailableToFillConsideringSlippage = amountUnableToFill
+ .div(multiplerNeededWithSlippage)
+ .round(0, BigNumber.ROUND_DOWN);
+
+ throw new InsufficientAssetLiquidityError(amountAvailableToFillConsideringSlippage);
}
// if we are not buying ZRX:
// given the orders calculated above, find the fee-orders that cover the desired assetBuyAmount (with slippage)
diff --git a/packages/asset-buyer/test/buy_quote_calculator_test.ts b/packages/asset-buyer/test/buy_quote_calculator_test.ts
index 1dd829a0f..98458cd5b 100644
--- a/packages/asset-buyer/test/buy_quote_calculator_test.ts
+++ b/packages/asset-buyer/test/buy_quote_calculator_test.ts
@@ -81,6 +81,34 @@ describe('buyQuoteCalculator', () => {
};
testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(400));
});
+ 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(
+ ordersAndFillableAmounts,
+ smallFeeOrderAndFillableAmount,
+ new BigNumber(500),
+ 0,
+ 0.2,
+ false,
+ );
+ };
+ testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(333));
+ });
+ 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(
+ ordersAndFillableAmounts,
+ smallFeeOrderAndFillableAmount,
+ new BigNumber(600),
+ 0,
+ 0.05,
+ false,
+ );
+ };
+ testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(380));
+ });
it('should throw if not enough maker asset liquidity (partially filled order)', () => {
const firstOrderAndFillableAmount: OrdersAndFillableAmounts = {
orders: [firstOrder],
diff --git a/packages/asset-buyer/test/utils/test_helpers.ts b/packages/asset-buyer/test/utils/test_helpers.ts
index fd1313ac8..b99906792 100644
--- a/packages/asset-buyer/test/utils/test_helpers.ts
+++ b/packages/asset-buyer/test/utils/test_helpers.ts
@@ -6,7 +6,7 @@ export const testHelpers = {
expectInsufficientLiquidityError: (
expect: Chai.ExpectStatic,
functionWhichTriggersError: () => void,
- expectedNumAvailable: BigNumber,
+ expectedAmountAvailableToFill: BigNumber,
): void => {
let errorThrown = false;
try {
@@ -14,7 +14,7 @@ export const testHelpers = {
} catch (e) {
errorThrown = true;
expect(e).to.be.instanceOf(InsufficientAssetLiquidityError);
- expect(e.numAssetsAvailable).to.be.bignumber.equal(expectedNumAvailable);
+ expect(e.amountAvailableToFill).to.be.bignumber.equal(expectedAmountAvailableToFill);
}
expect(errorThrown).to.be.true();
diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts
index f83eb4ac7..2417c8615 100644
--- a/packages/instant/src/constants.ts
+++ b/packages/instant/src/constants.ts
@@ -16,6 +16,7 @@ export const ONE_SECOND_MS = 1000;
export const ONE_MINUTE_MS = ONE_SECOND_MS * 60;
export const GIT_SHA = process.env.GIT_SHA;
export const NODE_ENV = process.env.NODE_ENV;
+export const SLIPPAGE_PERCENTAGE = 0.2;
export const NPM_PACKAGE_VERSION = process.env.NPM_PACKAGE_VERSION;
export const ACCOUNT_UPDATE_INTERVAL_TIME_MS = ONE_SECOND_MS * 5;
export const BUY_QUOTE_UPDATE_INTERVAL_TIME_MS = ONE_SECOND_MS * 15;
diff --git a/packages/instant/src/util/asset.ts b/packages/instant/src/util/asset.ts
index 13f84ef74..5e83e7e6d 100644
--- a/packages/instant/src/util/asset.ts
+++ b/packages/instant/src/util/asset.ts
@@ -1,7 +1,10 @@
-import { AssetBuyerError } from '@0x/asset-buyer';
+import { AssetBuyerError, InsufficientAssetLiquidityError } from '@0x/asset-buyer';
import { AssetProxyId, ObjectMap } from '@0x/types';
+import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
+import { BIG_NUMBER_ZERO } from '../constants';
import { assetDataNetworkMapping } from '../data/asset_data_network_mapping';
import { Asset, AssetMetaData, ERC20Asset, Network, ZeroExInstantError } from '../types';
@@ -110,6 +113,18 @@ export const assetUtils = {
assetBuyerErrorMessage: (asset: ERC20Asset, error: Error): string | undefined => {
if (error.message === AssetBuyerError.InsufficientAssetLiquidity) {
const assetName = assetUtils.bestNameForAsset(asset, 'of this asset');
+ if (error instanceof InsufficientAssetLiquidityError) {
+ const unitAmountAvailableToFill = Web3Wrapper.toUnitAmount(
+ error.amountAvailableToFill,
+ asset.metaData.decimals,
+ );
+ const roundedUnitAmountAvailableToFill = unitAmountAvailableToFill.round(2, BigNumber.ROUND_DOWN);
+
+ if (roundedUnitAmountAvailableToFill.greaterThan(BIG_NUMBER_ZERO)) {
+ return `There are only ${roundedUnitAmountAvailableToFill} ${assetName} available to buy`;
+ }
+ }
+
return `Not enough ${assetName} available`;
} else if (error.message === AssetBuyerError.InsufficientZrxLiquidity) {
return 'Not enough ZRX available';
diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts
index 6191c92e3..37974e71c 100644
--- a/packages/instant/src/util/buy_quote_updater.ts
+++ b/packages/instant/src/util/buy_quote_updater.ts
@@ -5,6 +5,7 @@ import * as _ from 'lodash';
import { Dispatch } from 'redux';
import { oc } from 'ts-optchain';
+import { SLIPPAGE_PERCENTAGE } from '../constants';
import { Action, actions } from '../redux/actions';
import { AffiliateInfo, ERC20Asset, QuoteFetchOrigin } from '../types';
import { analytics } from '../util/analytics';
@@ -33,8 +34,12 @@ export const buyQuoteUpdater = {
}
const feePercentage = oc(options.affiliateInfo).feePercentage();
let newBuyQuote: BuyQuote | undefined;
+ const slippagePercentage = SLIPPAGE_PERCENTAGE;
try {
- newBuyQuote = await assetBuyer.getBuyQuoteAsync(asset.assetData, baseUnitValue, { feePercentage });
+ newBuyQuote = await assetBuyer.getBuyQuoteAsync(asset.assetData, baseUnitValue, {
+ feePercentage,
+ slippagePercentage,
+ });
} catch (error) {
const errorMessage = assetUtils.assetBuyerErrorMessage(asset, error);