From c6c45095a8511814db6aa33e39794ae60debad8b Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Thu, 13 Dec 2018 15:16:51 -0800 Subject: feat(asset-buyer): Custom InsufficientAssetLiquidityError error BREAKING CHANGE: A custom InsufficientAssetLiquidityError error is now raised when there is insufficient liquidity --- packages/asset-buyer/src/types.ts | 10 +++ .../asset-buyer/src/utils/buy_quote_calculator.ts | 11 +++- .../asset-buyer/test/buy_quote_calculator_test.ts | 73 +++++++++++++++++++--- packages/asset-buyer/test/utils/test_helpers.ts | 22 +++++++ 4 files changed, 107 insertions(+), 9 deletions(-) create mode 100644 packages/asset-buyer/test/utils/test_helpers.ts diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts index 3b573edca..dfa17a22d 100644 --- a/packages/asset-buyer/src/types.ts +++ b/packages/asset-buyer/src/types.ts @@ -117,6 +117,16 @@ export enum AssetBuyerError { 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 { orders: SignedOrder[]; remainingFillableMakerAssetAmounts: BigNumber[]; diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts index b15b880c2..e52c9c5bf 100644 --- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts +++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts @@ -3,7 +3,13 @@ import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; import { constants } from '../constants'; -import { AssetBuyerError, BuyQuote, BuyQuoteInfo, OrdersAndFillableAmounts } from '../types'; +import { + AssetBuyerError, + BuyQuote, + BuyQuoteInfo, + InsufficientAssetLiquidityError, + OrdersAndFillableAmounts, +} from '../types'; 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 (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: // 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 a30017b72..1dd829a0f 100644 --- a/packages/asset-buyer/test/buy_quote_calculator_test.ts +++ b/packages/asset-buyer/test/buy_quote_calculator_test.ts @@ -1,4 +1,5 @@ import { orderFactory } from '@0x/order-utils/lib/src/order_factory'; +import { SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; import * as _ from 'lodash'; @@ -8,6 +9,7 @@ import { AssetBuyerError, OrdersAndFillableAmounts } from '../src/types'; import { buyQuoteCalculator } from '../src/utils/buy_quote_calculator'; import { chaiSetup } from './utils/chai_setup'; +import { testHelpers } from './utils/test_helpers'; chaiSetup.configure(); const expect = chai.expect; @@ -15,6 +17,10 @@ const expect = chai.expect; // tslint:disable:custom-no-magic-numbers describe('buyQuoteCalculator', () => { describe('#calculate', () => { + let firstOrder: SignedOrder; + let firstRemainingFillAmount: BigNumber; + let secondOrder: SignedOrder; + let secondRemainingFillAmount: BigNumber; let ordersAndFillableAmounts: OrdersAndFillableAmounts; let smallFeeOrderAndFillableAmount: 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) // 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({ + firstOrder = orderFactory.createSignedOrderFromPartial({ makerAssetAmount: new BigNumber(400), takerAssetAmount: new BigNumber(100), takerFee: new BigNumber(200), }); - const firstRemainingFillAmount = new BigNumber(200); - const secondOrder = orderFactory.createSignedOrderFromPartial({ + firstRemainingFillAmount = new BigNumber(200); + secondOrder = orderFactory.createSignedOrderFromPartial({ makerAssetAmount: new BigNumber(200), takerAssetAmount: new BigNumber(100), takerFee: new BigNumber(100), }); - const secondRemainingFillAmount = secondOrder.makerAssetAmount; + secondRemainingFillAmount = secondOrder.makerAssetAmount; ordersAndFillableAmounts = { orders: [firstOrder, secondOrder], 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 - expect(() => + const errorFunction = () => { buyQuoteCalculator.calculate( ordersAndFillableAmounts, smallFeeOrderAndFillableAmount, @@ -71,8 +77,61 @@ describe('buyQuoteCalculator', () => { 0, 0, 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', () => { // we request 300 makerAsset units but the ZRX order is only enough to fill the first order, which only has 200 makerAssetUnits available diff --git a/packages/asset-buyer/test/utils/test_helpers.ts b/packages/asset-buyer/test/utils/test_helpers.ts new file mode 100644 index 000000000..fd1313ac8 --- /dev/null +++ b/packages/asset-buyer/test/utils/test_helpers.ts @@ -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(); + }, +}; -- cgit v1.2.3 From a3d93d17cdefc2258a9f08e6fc680df1fb2b8456 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 14 Dec 2018 10:23:01 -0800 Subject: Factor in slippage amount in InsufficientAssetLiquidityError error, and show in instant --- packages/asset-buyer/src/index.ts | 1 + packages/asset-buyer/src/types.ts | 6 ++--- .../asset-buyer/src/utils/buy_quote_calculator.ts | 17 +++++++++++-- .../asset-buyer/test/buy_quote_calculator_test.ts | 28 ++++++++++++++++++++++ packages/asset-buyer/test/utils/test_helpers.ts | 4 ++-- packages/instant/src/constants.ts | 1 + packages/instant/src/util/asset.ts | 17 ++++++++++++- packages/instant/src/util/buy_quote_updater.ts | 7 +++++- 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); -- cgit v1.2.3 From c3884dfa32d0d8292163892934d7f243e813590f Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 14 Dec 2018 11:06:15 -0800 Subject: More tests for assetBuyerErrorMessage --- packages/instant/test/util/asset.test.ts | 41 +++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/packages/instant/test/util/asset.test.ts b/packages/instant/test/util/asset.test.ts index fc4e4e2e4..c13424685 100644 --- a/packages/instant/test/util/asset.test.ts +++ b/packages/instant/test/util/asset.test.ts @@ -1,5 +1,6 @@ -import { AssetBuyerError } from '@0x/asset-buyer'; +import { AssetBuyerError, InsufficientAssetLiquidityError, BigNumber } from '@0x/asset-buyer'; import { AssetProxyId, ObjectMap } from '@0x/types'; +import { Web3Wrapper } from '@0x/web3-wrapper'; import { Asset, AssetMetaData, ERC20Asset, ERC20AssetMetaData, Network, ZeroExInstantError } from '../../src/types'; import { assetUtils } from '../../src/util/asset'; @@ -19,6 +20,16 @@ const ZRX_ASSET: ERC20Asset = { const META_DATA_MAP: ObjectMap = { [ZRX_ASSET_DATA]: ZRX_META_DATA, }; +const WAX_ASSET: ERC20Asset = { + assetData: '0xf47261b000000000000000000000000039bb259f66e1c59d5abef88375979b4d20d98022', + metaData: { + assetProxyId: AssetProxyId.ERC20, + decimals: 8, + primaryColor: '#EDB740', + symbol: 'wax', + name: 'WAX', + }, +}; describe('assetDataUtil', () => { describe('bestNameForAsset', () => { @@ -47,13 +58,37 @@ describe('assetDataUtil', () => { }); }); describe('assetBuyerErrorMessage', () => { - it('should return message for InsufficientAssetLiquidity', () => { + it('should return message for generic InsufficientAssetLiquidity error', () => { const insufficientAssetError = new Error(AssetBuyerError.InsufficientAssetLiquidity); expect(assetUtils.assetBuyerErrorMessage(ZRX_ASSET, insufficientAssetError)).toEqual( 'Not enough ZRX available', ); }); - it('should return message for InsufficientAssetLiquidity', () => { + it('should return custom message for InsufficientAssetLiquidityError error for token w/ 18 decimals', () => { + const amountAvailable = Web3Wrapper.toBaseUnitAmount(new BigNumber(20.059), 18); + expect( + assetUtils.assetBuyerErrorMessage(ZRX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), + ).toEqual('There are only 20.05 ZRX available to buy'); + }); + it('should return custom message for InsufficientAssetLiquidityError error for token w/ 18 decimals and small amount available', () => { + const amountAvailable = Web3Wrapper.toBaseUnitAmount(new BigNumber(0.01), 18); + expect( + assetUtils.assetBuyerErrorMessage(ZRX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), + ).toEqual('There are only 0.01 ZRX available to buy'); + }); + it('should return custom message for InsufficientAssetLiquidityError error for token w/ 8 decimals', () => { + const amountAvailable = Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 8); + expect( + assetUtils.assetBuyerErrorMessage(WAX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), + ).toEqual('There are only 3 WAX available to buy'); + }); + it('should generic message for InsufficientAssetLiquidityError error when amount available rounds to zero', () => { + const amountAvailable = Web3Wrapper.toBaseUnitAmount(new BigNumber(0.002), 18); + expect( + assetUtils.assetBuyerErrorMessage(ZRX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), + ).toEqual('Not enough ZRX available'); + }); + it('should return message for InsufficientZrxLiquidity', () => { const insufficientZrxError = new Error(AssetBuyerError.InsufficientZrxLiquidity); expect(assetUtils.assetBuyerErrorMessage(ZRX_ASSET, insufficientZrxError)).toEqual( 'Not enough ZRX available', -- cgit v1.2.3 From 3e596f6a8c211eb39917cfd2a9a68a6facf2c904 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 14 Dec 2018 11:08:21 -0800 Subject: Nesting errors --- packages/instant/test/util/asset.test.ts | 48 +++++++++++++++++--------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/packages/instant/test/util/asset.test.ts b/packages/instant/test/util/asset.test.ts index c13424685..dbf2e15a5 100644 --- a/packages/instant/test/util/asset.test.ts +++ b/packages/instant/test/util/asset.test.ts @@ -64,29 +64,31 @@ describe('assetDataUtil', () => { 'Not enough ZRX available', ); }); - it('should return custom message for InsufficientAssetLiquidityError error for token w/ 18 decimals', () => { - const amountAvailable = Web3Wrapper.toBaseUnitAmount(new BigNumber(20.059), 18); - expect( - assetUtils.assetBuyerErrorMessage(ZRX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), - ).toEqual('There are only 20.05 ZRX available to buy'); - }); - it('should return custom message for InsufficientAssetLiquidityError error for token w/ 18 decimals and small amount available', () => { - const amountAvailable = Web3Wrapper.toBaseUnitAmount(new BigNumber(0.01), 18); - expect( - assetUtils.assetBuyerErrorMessage(ZRX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), - ).toEqual('There are only 0.01 ZRX available to buy'); - }); - it('should return custom message for InsufficientAssetLiquidityError error for token w/ 8 decimals', () => { - const amountAvailable = Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 8); - expect( - assetUtils.assetBuyerErrorMessage(WAX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), - ).toEqual('There are only 3 WAX available to buy'); - }); - it('should generic message for InsufficientAssetLiquidityError error when amount available rounds to zero', () => { - const amountAvailable = Web3Wrapper.toBaseUnitAmount(new BigNumber(0.002), 18); - expect( - assetUtils.assetBuyerErrorMessage(ZRX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), - ).toEqual('Not enough ZRX available'); + describe('InsufficientAssetLiquidityError', () => { + it('should return custom message for token w/ 18 decimals', () => { + const amountAvailable = Web3Wrapper.toBaseUnitAmount(new BigNumber(20.059), 18); + expect( + assetUtils.assetBuyerErrorMessage(ZRX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), + ).toEqual('There are only 20.05 ZRX available to buy'); + }); + it('should return custom message for token w/ 18 decimals and small amount available', () => { + const amountAvailable = Web3Wrapper.toBaseUnitAmount(new BigNumber(0.01), 18); + expect( + assetUtils.assetBuyerErrorMessage(ZRX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), + ).toEqual('There are only 0.01 ZRX available to buy'); + }); + it('should return custom message for token w/ 8 decimals', () => { + const amountAvailable = Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 8); + expect( + assetUtils.assetBuyerErrorMessage(WAX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), + ).toEqual('There are only 3 WAX available to buy'); + }); + it('should generic message when amount available rounds to zero', () => { + const amountAvailable = Web3Wrapper.toBaseUnitAmount(new BigNumber(0.002), 18); + expect( + assetUtils.assetBuyerErrorMessage(ZRX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), + ).toEqual('Not enough ZRX available'); + }); }); it('should return message for InsufficientZrxLiquidity', () => { const insufficientZrxError = new Error(AssetBuyerError.InsufficientZrxLiquidity); -- cgit v1.2.3 From 69054d85e80f9e41200015a9b0eef2a9fe00f439 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 14 Dec 2018 15:18:20 -0800 Subject: Only send in amountAvailableToFill if it's a non-zero amount, add additional tests and nest, and put error into its own file --- packages/asset-buyer/src/errors.ts | 16 ++ packages/asset-buyer/src/index.ts | 2 +- packages/asset-buyer/src/types.ts | 12 +- .../asset-buyer/src/utils/buy_quote_calculator.ts | 16 +- .../asset-buyer/test/buy_quote_calculator_test.ts | 196 ++++++++++++--------- packages/asset-buyer/test/utils/test_helpers.ts | 10 +- packages/instant/src/util/asset.ts | 6 +- 7 files changed, 154 insertions(+), 104 deletions(-) create mode 100644 packages/asset-buyer/src/errors.ts diff --git a/packages/asset-buyer/src/errors.ts b/packages/asset-buyer/src/errors.ts new file mode 100644 index 000000000..ef05a5d0a --- /dev/null +++ b/packages/asset-buyer/src/errors.ts @@ -0,0 +1,16 @@ +import { BigNumber } from '@0x/utils'; + +import { AssetBuyerError } from './types'; + +/** + * Error class representing insufficient asset liquidity + */ +export class InsufficientAssetLiquidityError extends Error { + public amountAvailableToFill?: BigNumber; + constructor(amountAvailableToFill?: BigNumber) { + super(AssetBuyerError.InsufficientAssetLiquidity); + 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/index.ts b/packages/asset-buyer/src/index.ts index 59515e24f..a42d7e272 100644 --- a/packages/asset-buyer/src/index.ts +++ b/packages/asset-buyer/src/index.ts @@ -9,6 +9,7 @@ export { SignedOrder } from '@0x/types'; export { BigNumber } from '@0x/utils'; export { AssetBuyer } from './asset_buyer'; +export { InsufficientAssetLiquidityError } from './errors'; export { BasicOrderProvider } from './order_providers/basic_order_provider'; export { StandardRelayerAPIOrderProvider } from './order_providers/standard_relayer_api_order_provider'; export { @@ -18,7 +19,6 @@ 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 d435f337e..d5d6be695 100644 --- a/packages/asset-buyer/src/types.ts +++ b/packages/asset-buyer/src/types.ts @@ -102,7 +102,7 @@ export interface AssetBuyerOpts { } /** - * Possible errors thrown by an AssetBuyer instance or associated static methods. + * Possible error messages thrown by an AssetBuyer instance or associated static methods. */ export enum AssetBuyerError { NoEtherTokenContractFound = 'NO_ETHER_TOKEN_CONTRACT_FOUND', @@ -117,16 +117,6 @@ export enum AssetBuyerError { TransactionValueTooLow = 'TRANSACTION_VALUE_TOO_LOW', } -export class InsufficientAssetLiquidityError extends Error { - public amountAvailableToFill: BigNumber; - constructor(amountAvailableToFill: BigNumber) { - super(AssetBuyerError.InsufficientAssetLiquidity); - 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); - } -} - export interface OrdersAndFillableAmounts { orders: SignedOrder[]; remainingFillableMakerAssetAmounts: BigNumber[]; diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts index 23d3e9b24..59293d1b7 100644 --- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts +++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts @@ -1,16 +1,10 @@ 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'; -import { - AssetBuyerError, - BuyQuote, - BuyQuoteInfo, - InsufficientAssetLiquidityError, - OrdersAndFillableAmounts, -} from '../types'; +import { InsufficientAssetLiquidityError } from '../errors'; +import { AssetBuyerError, BuyQuote, BuyQuoteInfo, OrdersAndFillableAmounts } from '../types'; import { orderUtils } from './order_utils'; @@ -53,7 +47,11 @@ export const buyQuoteCalculator = { .div(multiplerNeededWithSlippage) .round(0, BigNumber.ROUND_DOWN); - throw new InsufficientAssetLiquidityError(amountAvailableToFillConsideringSlippage); + throw new InsufficientAssetLiquidityError( + amountAvailableToFillConsideringSlippage.gt(constants.ZERO_AMOUNT) + ? amountAvailableToFillConsideringSlippage + : undefined, + ); } // 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 98458cd5b..fdc17ef25 100644 --- a/packages/asset-buyer/test/buy_quote_calculator_test.ts +++ b/packages/asset-buyer/test/buy_quote_calculator_test.ts @@ -67,87 +67,125 @@ 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( - ordersAndFillableAmounts, - smallFeeOrderAndFillableAmount, - new BigNumber(500), - 0, - 0, - false, - ); - }; - 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], - remainingFillableMakerAssetAmounts: [firstRemainingFillAmount], - }; + describe('InsufficientLiquidityError', () => { + 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( + ordersAndFillableAmounts, + smallFeeOrderAndFillableAmount, + new BigNumber(500), + 0, + 0, + false, + ); + }; + 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], + 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 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 throw with 1 amount available if no slippage', () => { + const smallOrder = orderFactory.createSignedOrderFromPartial({ + makerAssetAmount: new BigNumber(1), + takerAssetAmount: new BigNumber(1), + takerFee: new BigNumber(0), + }); + const errorFunction = () => { + buyQuoteCalculator.calculate( + { orders: [smallOrder], remainingFillableMakerAssetAmounts: [smallOrder.makerAssetAmount] }, + smallFeeOrderAndFillableAmount, + new BigNumber(600), + 0, + 0, + false, + ); + }; + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(1)); + }); + it('should throw without amount available to fill if amount rounds to 0', () => { + const smallOrder = orderFactory.createSignedOrderFromPartial({ + makerAssetAmount: new BigNumber(1), + takerAssetAmount: new BigNumber(1), + takerFee: new BigNumber(0), + }); + const errorFunction = () => { + buyQuoteCalculator.calculate( + { orders: [smallOrder], remainingFillableMakerAssetAmounts: [smallOrder.makerAssetAmount] }, + smallFeeOrderAndFillableAmount, + new BigNumber(600), + 0, + 0.2, + false, + ); + }; + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, undefined); }); - 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(() => diff --git a/packages/asset-buyer/test/utils/test_helpers.ts b/packages/asset-buyer/test/utils/test_helpers.ts index b99906792..d746316f8 100644 --- a/packages/asset-buyer/test/utils/test_helpers.ts +++ b/packages/asset-buyer/test/utils/test_helpers.ts @@ -1,12 +1,12 @@ import { BigNumber } from '@0x/utils'; -import { InsufficientAssetLiquidityError } from '../../src/types'; +import { InsufficientAssetLiquidityError } from '../../src/errors'; export const testHelpers = { expectInsufficientLiquidityError: ( expect: Chai.ExpectStatic, functionWhichTriggersError: () => void, - expectedAmountAvailableToFill: BigNumber, + expectedAmountAvailableToFill?: BigNumber, ): void => { let errorThrown = false; try { @@ -14,7 +14,11 @@ export const testHelpers = { } catch (e) { errorThrown = true; expect(e).to.be.instanceOf(InsufficientAssetLiquidityError); - expect(e.amountAvailableToFill).to.be.bignumber.equal(expectedAmountAvailableToFill); + if (expectedAmountAvailableToFill) { + expect(e.amountAvailableToFill).to.be.bignumber.equal(expectedAmountAvailableToFill); + } else { + expect(e.amountAvailableToFill).to.be.undefined(); + } } expect(errorThrown).to.be.true(); diff --git a/packages/instant/src/util/asset.ts b/packages/instant/src/util/asset.ts index 5e83e7e6d..756194f1f 100644 --- a/packages/instant/src/util/asset.ts +++ b/packages/instant/src/util/asset.ts @@ -113,7 +113,11 @@ 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) { + if ( + error instanceof InsufficientAssetLiquidityError && + error.amountAvailableToFill && + error.amountAvailableToFill.greaterThan(BIG_NUMBER_ZERO) + ) { const unitAmountAvailableToFill = Web3Wrapper.toUnitAmount( error.amountAvailableToFill, asset.metaData.decimals, -- cgit v1.2.3 From 5981823ac14f8f129e1866c3e39caed2211d5cea Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 14 Dec 2018 15:18:35 -0800 Subject: Update asset-buyer changelog describing error change --- packages/asset-buyer/CHANGELOG.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/asset-buyer/CHANGELOG.json b/packages/asset-buyer/CHANGELOG.json index 470d9b03b..44837d040 100644 --- a/packages/asset-buyer/CHANGELOG.json +++ b/packages/asset-buyer/CHANGELOG.json @@ -1,4 +1,12 @@ [ + { + "version": "4.0.0", + "changes": [ + { + "note": "Raise custom InsufficientAssetLiquidityError error with amountAvailableToFill attribute" + } + ] + }, { "version": "3.0.4", "changes": [ -- cgit v1.2.3 From ab10119c5af208be60782395b1faff906f0948f2 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 14 Dec 2018 15:24:01 -0800 Subject: one more test for undefined --- packages/instant/test/util/asset.test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/instant/test/util/asset.test.ts b/packages/instant/test/util/asset.test.ts index dbf2e15a5..213e69dd8 100644 --- a/packages/instant/test/util/asset.test.ts +++ b/packages/instant/test/util/asset.test.ts @@ -83,7 +83,12 @@ describe('assetDataUtil', () => { assetUtils.assetBuyerErrorMessage(WAX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), ).toEqual('There are only 3 WAX available to buy'); }); - it('should generic message when amount available rounds to zero', () => { + it('should return generic message when InsufficientAssetLiquidityError contains undefined value', () => { + expect(assetUtils.assetBuyerErrorMessage(ZRX_ASSET, new InsufficientAssetLiquidityError())).toEqual( + 'Not enough ZRX available', + ); + }); + it('should return generic message when amount available rounds to zero', () => { const amountAvailable = Web3Wrapper.toBaseUnitAmount(new BigNumber(0.002), 18); expect( assetUtils.assetBuyerErrorMessage(ZRX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), -- cgit v1.2.3 From 219902a169f662737de68e8c79e1cb6a164ad548 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 14 Dec 2018 15:55:52 -0800 Subject: rename boolean var --- packages/asset-buyer/test/utils/test_helpers.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/asset-buyer/test/utils/test_helpers.ts b/packages/asset-buyer/test/utils/test_helpers.ts index d746316f8..9c7c244af 100644 --- a/packages/asset-buyer/test/utils/test_helpers.ts +++ b/packages/asset-buyer/test/utils/test_helpers.ts @@ -8,11 +8,11 @@ export const testHelpers = { functionWhichTriggersError: () => void, expectedAmountAvailableToFill?: BigNumber, ): void => { - let errorThrown = false; + let wasErrorThrown = false; try { functionWhichTriggersError(); } catch (e) { - errorThrown = true; + wasErrorThrown = true; expect(e).to.be.instanceOf(InsufficientAssetLiquidityError); if (expectedAmountAvailableToFill) { expect(e.amountAvailableToFill).to.be.bignumber.equal(expectedAmountAvailableToFill); @@ -21,6 +21,6 @@ export const testHelpers = { } } - expect(errorThrown).to.be.true(); + expect(wasErrorThrown).to.be.true(); }, }; -- cgit v1.2.3 From 2e2e157fc8bb9da26e01ebf0b62e1cb74515d187 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 14 Dec 2018 16:20:45 -0800 Subject: Fix for asset-buyer documentation --- packages/monorepo-scripts/src/doc_gen_configs.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/monorepo-scripts/src/doc_gen_configs.ts b/packages/monorepo-scripts/src/doc_gen_configs.ts index 7a14f8664..875590b34 100644 --- a/packages/monorepo-scripts/src/doc_gen_configs.ts +++ b/packages/monorepo-scripts/src/doc_gen_configs.ts @@ -9,6 +9,7 @@ export const docGenConfigs: DocGenConfigs = { Array: 'https://developer.mozilla.org/pt-PT/docs/Web/JavaScript/Reference/Global_Objects/Array', BigNumber: 'http://mikemcl.github.io/bignumber.js', Error: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error', + ErrorConstructor: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error', Buffer: 'https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/node/v9/index.d.ts#L262', 'solc.StandardContractOutput': 'https://solidity.readthedocs.io/en/v0.4.24/using-the-compiler.html#output-description', -- cgit v1.2.3 From aa9aa1f58a4e63b5e5c7863f3b7afb020d7712c5 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 14 Dec 2018 17:05:52 -0800 Subject: more documentation --- packages/asset-buyer/src/errors.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/asset-buyer/src/errors.ts b/packages/asset-buyer/src/errors.ts index ef05a5d0a..68e9a684b 100644 --- a/packages/asset-buyer/src/errors.ts +++ b/packages/asset-buyer/src/errors.ts @@ -6,7 +6,13 @@ import { AssetBuyerError } from './types'; * Error class representing insufficient asset liquidity */ export class InsufficientAssetLiquidityError extends Error { + /** + * The amount availabe to fill (in base units) factoring in slippage. + */ public amountAvailableToFill?: BigNumber; + /** + * @param amountAvailableToFill The amount availabe to fill (in base units) factoring in slippage + */ constructor(amountAvailableToFill?: BigNumber) { super(AssetBuyerError.InsufficientAssetLiquidity); this.amountAvailableToFill = amountAvailableToFill; -- cgit v1.2.3 From c8c8219c055cc5798cf5cdc71199ee7ae505cd5a Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 21 Dec 2018 16:26:07 -0800 Subject: Make amountAvailableToFill required --- packages/asset-buyer/src/errors.ts | 4 ++-- packages/asset-buyer/src/utils/buy_quote_calculator.ts | 6 +----- packages/instant/src/util/asset.ts | 1 - 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/asset-buyer/src/errors.ts b/packages/asset-buyer/src/errors.ts index 68e9a684b..ec5fe548c 100644 --- a/packages/asset-buyer/src/errors.ts +++ b/packages/asset-buyer/src/errors.ts @@ -9,11 +9,11 @@ export class InsufficientAssetLiquidityError extends Error { /** * The amount availabe to fill (in base units) factoring in slippage. */ - public amountAvailableToFill?: BigNumber; + public amountAvailableToFill: BigNumber; /** * @param amountAvailableToFill The amount availabe to fill (in base units) factoring in slippage */ - constructor(amountAvailableToFill?: BigNumber) { + constructor(amountAvailableToFill: BigNumber) { super(AssetBuyerError.InsufficientAssetLiquidity); 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 diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts index 59293d1b7..ceeee93d3 100644 --- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts +++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts @@ -47,11 +47,7 @@ export const buyQuoteCalculator = { .div(multiplerNeededWithSlippage) .round(0, BigNumber.ROUND_DOWN); - throw new InsufficientAssetLiquidityError( - amountAvailableToFillConsideringSlippage.gt(constants.ZERO_AMOUNT) - ? amountAvailableToFillConsideringSlippage - : undefined, - ); + 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/instant/src/util/asset.ts b/packages/instant/src/util/asset.ts index e7aa55c88..b009a327f 100644 --- a/packages/instant/src/util/asset.ts +++ b/packages/instant/src/util/asset.ts @@ -115,7 +115,6 @@ export const assetUtils = { const assetName = assetUtils.bestNameForAsset(asset, 'of this asset'); if ( error instanceof InsufficientAssetLiquidityError && - error.amountAvailableToFill && error.amountAvailableToFill.greaterThan(BIG_NUMBER_ZERO) ) { const unitAmountAvailableToFill = Web3Wrapper.toUnitAmount( -- cgit v1.2.3 From 26977f6408442883c20001a57a3b9001f4ab9b25 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 21 Dec 2018 16:29:53 -0800 Subject: Fix var name and use floor instead of .round(0, ROUND_DOWN) --- packages/asset-buyer/src/utils/buy_quote_calculator.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts index ceeee93d3..fcded6ab1 100644 --- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts +++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts @@ -36,16 +36,14 @@ export const buyQuoteCalculator = { if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) { // 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 + 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 - // 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); + // i.e. if slippagePercent was 0.2 (20%), multiplierNeededWithSlippage would be 1.2 + const multiplierNeededWithSlippage = new BigNumber(1).plus(slippagePercentage); + // Given amountAvailableToFillConsideringSlippage * multiplierNeededWithSlippage = amountAbleToFill + // We divide amountUnableToFill by multiplierNeededWithSlippage to determine amountAvailableToFillConsideringSlippage + const amountAvailableToFillConsideringSlippage = amountAbleToFill.div(multiplierNeededWithSlippage).floor(); throw new InsufficientAssetLiquidityError(amountAvailableToFillConsideringSlippage); } -- cgit v1.2.3 From 89f67b9becf3b4c0d13cceeaca4d5656ad500a9a Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Wed, 2 Jan 2019 16:56:28 -0800 Subject: take out no-longer necessary test --- packages/instant/test/util/asset.test.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/instant/test/util/asset.test.ts b/packages/instant/test/util/asset.test.ts index 213e69dd8..402a556d5 100644 --- a/packages/instant/test/util/asset.test.ts +++ b/packages/instant/test/util/asset.test.ts @@ -1,4 +1,4 @@ -import { AssetBuyerError, InsufficientAssetLiquidityError, BigNumber } from '@0x/asset-buyer'; +import { AssetBuyerError, BigNumber, InsufficientAssetLiquidityError } from '@0x/asset-buyer'; import { AssetProxyId, ObjectMap } from '@0x/types'; import { Web3Wrapper } from '@0x/web3-wrapper'; @@ -83,11 +83,6 @@ describe('assetDataUtil', () => { assetUtils.assetBuyerErrorMessage(WAX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), ).toEqual('There are only 3 WAX available to buy'); }); - it('should return generic message when InsufficientAssetLiquidityError contains undefined value', () => { - expect(assetUtils.assetBuyerErrorMessage(ZRX_ASSET, new InsufficientAssetLiquidityError())).toEqual( - 'Not enough ZRX available', - ); - }); it('should return generic message when amount available rounds to zero', () => { const amountAvailable = Web3Wrapper.toBaseUnitAmount(new BigNumber(0.002), 18); expect( -- cgit v1.2.3 From 76dde294f10bb3d5c1074cd6599668bcb1cdd8ec Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Fri, 4 Jan 2019 07:54:01 -0800 Subject: Add PR number --- packages/asset-buyer/CHANGELOG.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/asset-buyer/CHANGELOG.json b/packages/asset-buyer/CHANGELOG.json index 44837d040..6069d6a12 100644 --- a/packages/asset-buyer/CHANGELOG.json +++ b/packages/asset-buyer/CHANGELOG.json @@ -3,7 +3,8 @@ "version": "4.0.0", "changes": [ { - "note": "Raise custom InsufficientAssetLiquidityError error with amountAvailableToFill attribute" + "note": "Raise custom InsufficientAssetLiquidityError error with amountAvailableToFill attribute", + "pr": 1437 } ] }, -- cgit v1.2.3