aboutsummaryrefslogtreecommitdiffstats
path: root/packages/asset-buyer/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/asset-buyer/src')
-rw-r--r--packages/asset-buyer/src/asset_buyer.ts365
-rw-r--r--packages/asset-buyer/src/constants.ts40
-rw-r--r--packages/asset-buyer/src/errors.ts22
-rw-r--r--packages/asset-buyer/src/globals.d.ts6
-rw-r--r--packages/asset-buyer/src/index.ts29
-rw-r--r--packages/asset-buyer/src/order_providers/basic_order_provider.ts41
-rw-r--r--packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts111
-rw-r--r--packages/asset-buyer/src/types.ts142
-rw-r--r--packages/asset-buyer/src/utils/assert.ts39
-rw-r--r--packages/asset-buyer/src/utils/asset_data_utils.ts12
-rw-r--r--packages/asset-buyer/src/utils/buy_quote_calculator.ts221
-rw-r--r--packages/asset-buyer/src/utils/calculate_liquidity.ts34
-rw-r--r--packages/asset-buyer/src/utils/order_provider_response_processor.ts171
-rw-r--r--packages/asset-buyer/src/utils/order_utils.ts74
14 files changed, 0 insertions, 1307 deletions
diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts
deleted file mode 100644
index ad4b3bb60..000000000
--- a/packages/asset-buyer/src/asset_buyer.ts
+++ /dev/null
@@ -1,365 +0,0 @@
-import { ContractWrappers, ContractWrappersError, ForwarderWrapperError } from '@0x/contract-wrappers';
-import { schemas } from '@0x/json-schemas';
-import { SignedOrder } from '@0x/order-utils';
-import { ObjectMap } from '@0x/types';
-import { BigNumber } from '@0x/utils';
-import { Web3Wrapper } from '@0x/web3-wrapper';
-import { Provider } from 'ethereum-types';
-import * as _ from 'lodash';
-
-import { constants } from './constants';
-import { BasicOrderProvider } from './order_providers/basic_order_provider';
-import { StandardRelayerAPIOrderProvider } from './order_providers/standard_relayer_api_order_provider';
-import {
- AssetBuyerError,
- AssetBuyerOpts,
- BuyQuote,
- BuyQuoteExecutionOpts,
- BuyQuoteRequestOpts,
- LiquidityForAssetData,
- LiquidityRequestOpts,
- OrderProvider,
- OrdersAndFillableAmounts,
-} from './types';
-
-import { assert } from './utils/assert';
-import { assetDataUtils } from './utils/asset_data_utils';
-import { buyQuoteCalculator } from './utils/buy_quote_calculator';
-import { calculateLiquidity } from './utils/calculate_liquidity';
-import { orderProviderResponseProcessor } from './utils/order_provider_response_processor';
-
-interface OrdersEntry {
- ordersAndFillableAmounts: OrdersAndFillableAmounts;
- lastRefreshTime: number;
-}
-
-export class AssetBuyer {
- public readonly provider: Provider;
- public readonly orderProvider: OrderProvider;
- public readonly networkId: number;
- public readonly orderRefreshIntervalMs: number;
- public readonly expiryBufferSeconds: number;
- private readonly _contractWrappers: ContractWrappers;
- // cache of orders along with the time last updated keyed by assetData
- private readonly _ordersEntryMap: ObjectMap<OrdersEntry> = {};
- /**
- * Instantiates a new AssetBuyer instance given existing liquidity in the form of orders and feeOrders.
- * @param provider The Provider instance you would like to use for interacting with the Ethereum network.
- * @param orders A non-empty array of objects that conform to SignedOrder. All orders must have the same makerAssetData and takerAssetData (WETH).
- * @param feeOrders A array of objects that conform to SignedOrder. All orders must have the same makerAssetData (ZRX) and takerAssetData (WETH). Defaults to an empty array.
- * @param options Initialization options for the AssetBuyer. See type definition for details.
- *
- * @return An instance of AssetBuyer
- */
- public static getAssetBuyerForProvidedOrders(
- provider: Provider,
- orders: SignedOrder[],
- options: Partial<AssetBuyerOpts> = {},
- ): AssetBuyer {
- assert.isWeb3Provider('provider', provider);
- assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema);
- assert.assert(orders.length !== 0, `Expected orders to contain at least one order`);
- const orderProvider = new BasicOrderProvider(orders);
- const assetBuyer = new AssetBuyer(provider, orderProvider, options);
- return assetBuyer;
- }
- /**
- * Instantiates a new AssetBuyer instance given a [Standard Relayer API](https://github.com/0xProject/standard-relayer-api) endpoint
- * @param provider The Provider instance you would like to use for interacting with the Ethereum network.
- * @param sraApiUrl The standard relayer API base HTTP url you would like to source orders from.
- * @param options Initialization options for the AssetBuyer. See type definition for details.
- *
- * @return An instance of AssetBuyer
- */
- public static getAssetBuyerForStandardRelayerAPIUrl(
- provider: Provider,
- sraApiUrl: string,
- options: Partial<AssetBuyerOpts> = {},
- ): AssetBuyer {
- assert.isWeb3Provider('provider', provider);
- assert.isWebUri('sraApiUrl', sraApiUrl);
- const networkId = options.networkId || constants.DEFAULT_ASSET_BUYER_OPTS.networkId;
- const orderProvider = new StandardRelayerAPIOrderProvider(sraApiUrl, networkId);
- const assetBuyer = new AssetBuyer(provider, orderProvider, options);
- return assetBuyer;
- }
- /**
- * Instantiates a new AssetBuyer instance
- * @param provider The Provider instance you would like to use for interacting with the Ethereum network.
- * @param orderProvider An object that conforms to OrderProvider, see type for definition.
- * @param options Initialization options for the AssetBuyer. See type definition for details.
- *
- * @return An instance of AssetBuyer
- */
- constructor(provider: Provider, orderProvider: OrderProvider, options: Partial<AssetBuyerOpts> = {}) {
- const { networkId, orderRefreshIntervalMs, expiryBufferSeconds } = _.merge(
- {},
- constants.DEFAULT_ASSET_BUYER_OPTS,
- options,
- );
- assert.isWeb3Provider('provider', provider);
- assert.isValidOrderProvider('orderProvider', orderProvider);
- assert.isNumber('networkId', networkId);
- assert.isNumber('orderRefreshIntervalMs', orderRefreshIntervalMs);
- assert.isNumber('expiryBufferSeconds', expiryBufferSeconds);
- this.provider = provider;
- this.orderProvider = orderProvider;
- this.networkId = networkId;
- this.orderRefreshIntervalMs = orderRefreshIntervalMs;
- this.expiryBufferSeconds = expiryBufferSeconds;
- this._contractWrappers = new ContractWrappers(this.provider, {
- networkId,
- });
- }
- /**
- * Get a `BuyQuote` containing all information relevant to fulfilling a buy given a desired assetData.
- * You can then pass the `BuyQuote` to `executeBuyQuoteAsync` to execute the buy.
- * @param assetData The assetData of the desired asset to buy (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
- * @param assetBuyAmount The amount of asset to buy.
- * @param options Options for the request. See type definition for more information.
- *
- * @return An object that conforms to BuyQuote that satisfies the request. See type definition for more information.
- */
- public async getBuyQuoteAsync(
- assetData: string,
- assetBuyAmount: BigNumber,
- options: Partial<BuyQuoteRequestOpts> = {},
- ): Promise<BuyQuote> {
- const { feePercentage, shouldForceOrderRefresh, slippagePercentage } = _.merge(
- {},
- constants.DEFAULT_BUY_QUOTE_REQUEST_OPTS,
- options,
- );
- assert.isString('assetData', assetData);
- assert.isBigNumber('assetBuyAmount', assetBuyAmount);
- assert.isValidPercentage('feePercentage', feePercentage);
- assert.isBoolean('shouldForceOrderRefresh', shouldForceOrderRefresh);
- assert.isNumber('slippagePercentage', slippagePercentage);
- const zrxTokenAssetData = this._getZrxTokenAssetDataOrThrow();
- const isMakerAssetZrxToken = assetData === zrxTokenAssetData;
- // get the relevant orders for the makerAsset and fees
- // if the requested assetData is ZRX, don't get the fee info
- const [ordersAndFillableAmounts, feeOrdersAndFillableAmounts] = await Promise.all([
- this.getOrdersAndFillableAmountsAsync(assetData, shouldForceOrderRefresh),
- isMakerAssetZrxToken
- ? Promise.resolve(constants.EMPTY_ORDERS_AND_FILLABLE_AMOUNTS)
- : this.getOrdersAndFillableAmountsAsync(zrxTokenAssetData, shouldForceOrderRefresh),
- shouldForceOrderRefresh,
- ]);
- if (ordersAndFillableAmounts.orders.length === 0) {
- throw new Error(`${AssetBuyerError.AssetUnavailable}: For assetData ${assetData}`);
- }
- const buyQuote = buyQuoteCalculator.calculate(
- ordersAndFillableAmounts,
- feeOrdersAndFillableAmounts,
- assetBuyAmount,
- feePercentage,
- slippagePercentage,
- isMakerAssetZrxToken,
- );
- return buyQuote;
- }
- /**
- * Get a `BuyQuote` containing all information relevant to fulfilling a buy given a desired ERC20 token address.
- * You can then pass the `BuyQuote` to `executeBuyQuoteAsync` to execute the buy.
- * @param tokenAddress The ERC20 token address.
- * @param assetBuyAmount The amount of asset to buy.
- * @param options Options for the request. See type definition for more information.
- *
- * @return An object that conforms to BuyQuote that satisfies the request. See type definition for more information.
- */
- public async getBuyQuoteForERC20TokenAddressAsync(
- tokenAddress: string,
- assetBuyAmount: BigNumber,
- options: Partial<BuyQuoteRequestOpts> = {},
- ): Promise<BuyQuote> {
- assert.isETHAddressHex('tokenAddress', tokenAddress);
- assert.isBigNumber('assetBuyAmount', assetBuyAmount);
- const assetData = assetDataUtils.encodeERC20AssetData(tokenAddress);
- const buyQuote = this.getBuyQuoteAsync(assetData, assetBuyAmount, options);
- return buyQuote;
- }
- /**
- * Returns information about available liquidity for an asset
- * Does not factor in slippage or fees
- * @param assetData The assetData of the desired asset to buy (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
- * @param options Options for the request. See type definition for more information.
- *
- * @return An object that conforms to LiquidityForAssetData that satisfies the request. See type definition for more information.
- */
- public async getLiquidityForAssetDataAsync(
- assetData: string,
- options: Partial<LiquidityRequestOpts> = {},
- ): Promise<LiquidityForAssetData> {
- const shouldForceOrderRefresh =
- options.shouldForceOrderRefresh !== undefined ? options.shouldForceOrderRefresh : false;
- assetDataUtils.decodeAssetDataOrThrow(assetData);
- assert.isBoolean('options.shouldForceOrderRefresh', shouldForceOrderRefresh);
-
- const assetPairs = await this.orderProvider.getAvailableMakerAssetDatasAsync(assetData);
- const etherTokenAssetData = this._getEtherTokenAssetDataOrThrow();
- if (!assetPairs.includes(etherTokenAssetData)) {
- return {
- tokensAvailableInBaseUnits: new BigNumber(0),
- ethValueAvailableInWei: new BigNumber(0),
- };
- }
-
- const ordersAndFillableAmounts = await this.getOrdersAndFillableAmountsAsync(
- assetData,
- shouldForceOrderRefresh,
- );
-
- return calculateLiquidity(ordersAndFillableAmounts);
- }
-
- /**
- * Given a BuyQuote and desired rate, attempt to execute the buy.
- * @param buyQuote An object that conforms to BuyQuote. See type definition for more information.
- * @param options Options for the execution of the BuyQuote. See type definition for more information.
- *
- * @return A promise of the txHash.
- */
- public async executeBuyQuoteAsync(
- buyQuote: BuyQuote,
- options: Partial<BuyQuoteExecutionOpts> = {},
- ): Promise<string> {
- const { ethAmount, takerAddress, feeRecipient, gasLimit, gasPrice } = _.merge(
- {},
- constants.DEFAULT_BUY_QUOTE_EXECUTION_OPTS,
- options,
- );
- assert.isValidBuyQuote('buyQuote', buyQuote);
- if (!_.isUndefined(ethAmount)) {
- assert.isBigNumber('ethAmount', ethAmount);
- }
- if (!_.isUndefined(takerAddress)) {
- assert.isETHAddressHex('takerAddress', takerAddress);
- }
- assert.isETHAddressHex('feeRecipient', feeRecipient);
- if (!_.isUndefined(gasLimit)) {
- assert.isNumber('gasLimit', gasLimit);
- }
- if (!_.isUndefined(gasPrice)) {
- assert.isBigNumber('gasPrice', gasPrice);
- }
- const { orders, feeOrders, feePercentage, assetBuyAmount, worstCaseQuoteInfo } = buyQuote;
- // if no takerAddress is provided, try to get one from the provider
- let finalTakerAddress;
- if (!_.isUndefined(takerAddress)) {
- finalTakerAddress = takerAddress;
- } else {
- const web3Wrapper = new Web3Wrapper(this.provider);
- const availableAddresses = await web3Wrapper.getAvailableAddressesAsync();
- const firstAvailableAddress = _.head(availableAddresses);
- if (!_.isUndefined(firstAvailableAddress)) {
- finalTakerAddress = firstAvailableAddress;
- } else {
- throw new Error(AssetBuyerError.NoAddressAvailable);
- }
- }
- try {
- // if no ethAmount is provided, default to the worst ethAmount from buyQuote
- const txHash = await this._contractWrappers.forwarder.marketBuyOrdersWithEthAsync(
- orders,
- assetBuyAmount,
- finalTakerAddress,
- ethAmount || worstCaseQuoteInfo.totalEthAmount,
- feeOrders,
- feePercentage,
- feeRecipient,
- {
- gasLimit,
- gasPrice,
- shouldValidate: true,
- },
- );
- return txHash;
- } catch (err) {
- if (_.includes(err.message, ContractWrappersError.SignatureRequestDenied)) {
- throw new Error(AssetBuyerError.SignatureRequestDenied);
- } else if (_.includes(err.message, ForwarderWrapperError.CompleteFillFailed)) {
- throw new Error(AssetBuyerError.TransactionValueTooLow);
- } else {
- throw err;
- }
- }
- }
- /**
- * Get the asset data of all assets that are purchaseable with ether token (wETH) in the order provider passed in at init.
- *
- * @return An array of asset data strings that can be purchased using wETH.
- */
- public async getAvailableAssetDatasAsync(): Promise<string[]> {
- const etherTokenAssetData = this._getEtherTokenAssetDataOrThrow();
- return this.orderProvider.getAvailableMakerAssetDatasAsync(etherTokenAssetData);
- }
- /**
- * Grab orders from the map, if there is a miss or it is time to refresh, fetch and process the orders
- * @param assetData The assetData of the desired asset to buy (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
- * @param shouldForceOrderRefresh If set to true, new orders and state will be fetched instead of waiting for the next orderRefreshIntervalMs.
- */
- public async getOrdersAndFillableAmountsAsync(
- assetData: string,
- shouldForceOrderRefresh: boolean,
- ): Promise<OrdersAndFillableAmounts> {
- // try to get ordersEntry from the map
- const ordersEntryIfExists = this._ordersEntryMap[assetData];
- // we should refresh if:
- // we do not have any orders OR
- // we are forced to OR
- // we have some last refresh time AND that time was sufficiently long ago
- const shouldRefresh =
- _.isUndefined(ordersEntryIfExists) ||
- shouldForceOrderRefresh ||
- // tslint:disable:restrict-plus-operands
- ordersEntryIfExists.lastRefreshTime + this.orderRefreshIntervalMs < Date.now();
- if (!shouldRefresh) {
- const result = ordersEntryIfExists.ordersAndFillableAmounts;
- return result;
- }
- const etherTokenAssetData = this._getEtherTokenAssetDataOrThrow();
- const zrxTokenAssetData = this._getZrxTokenAssetDataOrThrow();
- // construct orderProvider request
- const orderProviderRequest = {
- makerAssetData: assetData,
- takerAssetData: etherTokenAssetData,
- networkId: this.networkId,
- };
- const request = orderProviderRequest;
- // get provider response
- const response = await this.orderProvider.getOrdersAsync(request);
- // since the order provider is an injected dependency, validate that it respects the API
- // ie. it should only return maker/taker assetDatas that are specified
- orderProviderResponseProcessor.throwIfInvalidResponse(response, request);
- // process the responses into one object
- const isMakerAssetZrxToken = assetData === zrxTokenAssetData;
- const ordersAndFillableAmounts = await orderProviderResponseProcessor.processAsync(
- response,
- isMakerAssetZrxToken,
- this.expiryBufferSeconds,
- this._contractWrappers.orderValidator,
- );
- const lastRefreshTime = Date.now();
- const updatedOrdersEntry = {
- ordersAndFillableAmounts,
- lastRefreshTime,
- };
- this._ordersEntryMap[assetData] = updatedOrdersEntry;
- return ordersAndFillableAmounts;
- }
- /**
- * Get the assetData that represents the WETH token.
- * Will throw if WETH does not exist for the current network.
- */
- private _getEtherTokenAssetDataOrThrow(): string {
- return assetDataUtils.getEtherTokenAssetData(this._contractWrappers);
- }
- /**
- * Get the assetData that represents the ZRX token.
- * Will throw if ZRX does not exist for the current network.
- */
- private _getZrxTokenAssetDataOrThrow(): string {
- return this._contractWrappers.exchange.getZRXAssetData();
- }
-}
diff --git a/packages/asset-buyer/src/constants.ts b/packages/asset-buyer/src/constants.ts
deleted file mode 100644
index c0e1bf27d..000000000
--- a/packages/asset-buyer/src/constants.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { SignedOrder } from '@0x/types';
-import { BigNumber } from '@0x/utils';
-
-import { AssetBuyerOpts, BuyQuoteExecutionOpts, BuyQuoteRequestOpts, OrdersAndFillableAmounts } from './types';
-
-const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
-const MAINNET_NETWORK_ID = 1;
-
-const DEFAULT_ASSET_BUYER_OPTS: AssetBuyerOpts = {
- networkId: MAINNET_NETWORK_ID,
- orderRefreshIntervalMs: 10000, // 10 seconds
- expiryBufferSeconds: 120, // 2 minutes
-};
-
-const DEFAULT_BUY_QUOTE_REQUEST_OPTS: BuyQuoteRequestOpts = {
- feePercentage: 0,
- shouldForceOrderRefresh: false,
- slippagePercentage: 0.2, // 20% slippage protection
-};
-
-// Other default values are dynamically determined
-const DEFAULT_BUY_QUOTE_EXECUTION_OPTS: BuyQuoteExecutionOpts = {
- feeRecipient: NULL_ADDRESS,
-};
-
-const EMPTY_ORDERS_AND_FILLABLE_AMOUNTS: OrdersAndFillableAmounts = {
- orders: [] as SignedOrder[],
- remainingFillableMakerAssetAmounts: [] as BigNumber[],
-};
-
-export const constants = {
- ZERO_AMOUNT: new BigNumber(0),
- NULL_ADDRESS,
- MAINNET_NETWORK_ID,
- ETHER_TOKEN_DECIMALS: 18,
- DEFAULT_ASSET_BUYER_OPTS,
- DEFAULT_BUY_QUOTE_EXECUTION_OPTS,
- DEFAULT_BUY_QUOTE_REQUEST_OPTS,
- EMPTY_ORDERS_AND_FILLABLE_AMOUNTS,
-};
diff --git a/packages/asset-buyer/src/errors.ts b/packages/asset-buyer/src/errors.ts
deleted file mode 100644
index ec5fe548c..000000000
--- a/packages/asset-buyer/src/errors.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { BigNumber } from '@0x/utils';
-
-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;
- // 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/globals.d.ts b/packages/asset-buyer/src/globals.d.ts
deleted file mode 100644
index 94e63a32d..000000000
--- a/packages/asset-buyer/src/globals.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module '*.json' {
- const json: any;
- /* tslint:disable */
- export default json;
- /* tslint:enable */
-}
diff --git a/packages/asset-buyer/src/index.ts b/packages/asset-buyer/src/index.ts
deleted file mode 100644
index f69cfad69..000000000
--- a/packages/asset-buyer/src/index.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-export {
- JSONRPCRequestPayload,
- JSONRPCResponsePayload,
- JSONRPCResponseError,
- JSONRPCErrorCallback,
- Provider,
-} from 'ethereum-types';
-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 {
- AssetBuyerError,
- AssetBuyerOpts,
- BuyQuote,
- BuyQuoteExecutionOpts,
- BuyQuoteInfo,
- BuyQuoteRequestOpts,
- LiquidityForAssetData,
- LiquidityRequestOpts,
- OrdersAndFillableAmounts,
- OrderProvider,
- OrderProviderRequest,
- OrderProviderResponse,
- SignedOrderWithRemainingFillableMakerAssetAmount,
-} from './types';
diff --git a/packages/asset-buyer/src/order_providers/basic_order_provider.ts b/packages/asset-buyer/src/order_providers/basic_order_provider.ts
deleted file mode 100644
index 76685f27a..000000000
--- a/packages/asset-buyer/src/order_providers/basic_order_provider.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { schemas } from '@0x/json-schemas';
-import { SignedOrder } from '@0x/types';
-import * as _ from 'lodash';
-
-import { OrderProvider, OrderProviderRequest, OrderProviderResponse } from '../types';
-import { assert } from '../utils/assert';
-
-export class BasicOrderProvider implements OrderProvider {
- public readonly orders: SignedOrder[];
- /**
- * Instantiates a new BasicOrderProvider instance
- * @param orders An array of objects that conform to SignedOrder to fetch from.
- * @return An instance of BasicOrderProvider
- */
- constructor(orders: SignedOrder[]) {
- assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema);
- this.orders = orders;
- }
- /**
- * Given an object that conforms to OrderFetcherRequest, return the corresponding OrderProviderResponse that satisfies the request.
- * @param orderProviderRequest An instance of OrderFetcherRequest. See type for more information.
- * @return An instance of OrderProviderResponse. See type for more information.
- */
- public async getOrdersAsync(orderProviderRequest: OrderProviderRequest): Promise<OrderProviderResponse> {
- assert.isValidOrderProviderRequest('orderProviderRequest', orderProviderRequest);
- const { makerAssetData, takerAssetData } = orderProviderRequest;
- const orders = _.filter(this.orders, order => {
- return order.makerAssetData === makerAssetData && order.takerAssetData === takerAssetData;
- });
- return { orders };
- }
- /**
- * Given a taker asset data string, return all availabled paired maker asset data strings.
- * @param takerAssetData A string representing the taker asset data.
- * @return An array of asset data strings that can be purchased using takerAssetData.
- */
- public async getAvailableMakerAssetDatasAsync(takerAssetData: string): Promise<string[]> {
- const ordersWithTakerAssetData = _.filter(this.orders, { takerAssetData });
- return _.map(ordersWithTakerAssetData, order => order.makerAssetData);
- }
-}
diff --git a/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts b/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts
deleted file mode 100644
index 813c9923b..000000000
--- a/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts
+++ /dev/null
@@ -1,111 +0,0 @@
-import { HttpClient } from '@0x/connect';
-import { APIOrder, AssetPairsResponse, OrderbookResponse } from '@0x/types';
-import * as _ from 'lodash';
-
-import {
- AssetBuyerError,
- OrderProvider,
- OrderProviderRequest,
- OrderProviderResponse,
- SignedOrderWithRemainingFillableMakerAssetAmount,
-} from '../types';
-import { assert } from '../utils/assert';
-import { orderUtils } from '../utils/order_utils';
-
-export class StandardRelayerAPIOrderProvider implements OrderProvider {
- public readonly apiUrl: string;
- public readonly networkId: number;
- private readonly _sraClient: HttpClient;
- /**
- * Given an array of APIOrder objects from a standard relayer api, return an array
- * of SignedOrderWithRemainingFillableMakerAssetAmounts
- */
- private static _getSignedOrderWithRemainingFillableMakerAssetAmountFromApi(
- apiOrders: APIOrder[],
- ): SignedOrderWithRemainingFillableMakerAssetAmount[] {
- const result = _.map(apiOrders, apiOrder => {
- const { order, metaData } = apiOrder;
- // calculate remainingFillableMakerAssetAmount from api metadata, else assume order is completely fillable
- const remainingFillableTakerAssetAmount = _.get(
- metaData,
- 'remainingTakerAssetAmount',
- order.takerAssetAmount,
- );
- const remainingFillableMakerAssetAmount = orderUtils.getRemainingMakerAmount(
- order,
- remainingFillableTakerAssetAmount,
- );
- const newOrder = {
- ...order,
- remainingFillableMakerAssetAmount,
- };
- return newOrder;
- });
- return result;
- }
- /**
- * Instantiates a new StandardRelayerAPIOrderProvider instance
- * @param apiUrl The standard relayer API base HTTP url you would like to source orders from.
- * @param networkId The ethereum network id.
- * @return An instance of StandardRelayerAPIOrderProvider
- */
- constructor(apiUrl: string, networkId: number) {
- assert.isWebUri('apiUrl', apiUrl);
- assert.isNumber('networkId', networkId);
- this.apiUrl = apiUrl;
- this.networkId = networkId;
- this._sraClient = new HttpClient(apiUrl);
- }
- /**
- * Given an object that conforms to OrderProviderRequest, return the corresponding OrderProviderResponse that satisfies the request.
- * @param orderProviderRequest An instance of OrderProviderRequest. See type for more information.
- * @return An instance of OrderProviderResponse. See type for more information.
- */
- public async getOrdersAsync(orderProviderRequest: OrderProviderRequest): Promise<OrderProviderResponse> {
- assert.isValidOrderProviderRequest('orderProviderRequest', orderProviderRequest);
- const { makerAssetData, takerAssetData } = orderProviderRequest;
- const orderbookRequest = { baseAssetData: makerAssetData, quoteAssetData: takerAssetData };
- const requestOpts = { networkId: this.networkId };
- let orderbook: OrderbookResponse;
- try {
- orderbook = await this._sraClient.getOrderbookAsync(orderbookRequest, requestOpts);
- } catch (err) {
- throw new Error(AssetBuyerError.StandardRelayerApiError);
- }
- const apiOrders = orderbook.asks.records;
- const orders = StandardRelayerAPIOrderProvider._getSignedOrderWithRemainingFillableMakerAssetAmountFromApi(
- apiOrders,
- );
- return {
- orders,
- };
- }
- /**
- * Given a taker asset data string, return all availabled paired maker asset data strings.
- * @param takerAssetData A string representing the taker asset data.
- * @return An array of asset data strings that can be purchased using takerAssetData.
- */
- public async getAvailableMakerAssetDatasAsync(takerAssetData: string): Promise<string[]> {
- // Return a maximum of 1000 asset datas
- const maxPerPage = 1000;
- const requestOpts = { networkId: this.networkId, perPage: maxPerPage };
- const assetPairsRequest = { assetDataA: takerAssetData };
- const fullRequest = {
- ...requestOpts,
- ...assetPairsRequest,
- };
- let response: AssetPairsResponse;
- try {
- response = await this._sraClient.getAssetPairsAsync(fullRequest);
- } catch (err) {
- throw new Error(AssetBuyerError.StandardRelayerApiError);
- }
- return _.map(response.records, item => {
- if (item.assetDataA.assetData === takerAssetData) {
- return item.assetDataB.assetData;
- } else {
- return item.assetDataA.assetData;
- }
- });
- }
-}
diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts
deleted file mode 100644
index 46a2338ce..000000000
--- a/packages/asset-buyer/src/types.ts
+++ /dev/null
@@ -1,142 +0,0 @@
-import { SignedOrder } from '@0x/types';
-import { BigNumber } from '@0x/utils';
-
-/**
- * makerAssetData: The assetData representing the desired makerAsset.
- * takerAssetData: The assetData representing the desired takerAsset.
- * networkId: The networkId that the desired orders should be for.
- */
-export interface OrderProviderRequest {
- makerAssetData: string;
- takerAssetData: string;
-}
-
-/**
- * orders: An array of orders with optional remaining fillable makerAsset amounts. See type for more info.
- */
-export interface OrderProviderResponse {
- orders: SignedOrderWithRemainingFillableMakerAssetAmount[];
-}
-
-/**
- * A normal SignedOrder with one extra optional property `remainingFillableMakerAssetAmount`
- * remainingFillableMakerAssetAmount: The amount of the makerAsset that is available to be filled
- */
-export interface SignedOrderWithRemainingFillableMakerAssetAmount extends SignedOrder {
- remainingFillableMakerAssetAmount?: BigNumber;
-}
-/**
- * gerOrdersAsync: Given an OrderProviderRequest, get an OrderProviderResponse.
- * getAvailableMakerAssetDatasAsync: Given a taker asset data string, return all availabled paired maker asset data strings.
- */
-export interface OrderProvider {
- getOrdersAsync: (orderProviderRequest: OrderProviderRequest) => Promise<OrderProviderResponse>;
- getAvailableMakerAssetDatasAsync: (takerAssetData: string) => Promise<string[]>;
-}
-
-/**
- * assetData: String that represents a specific asset (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
- * assetBuyAmount: The amount of asset to buy.
- * orders: An array of objects conforming to SignedOrder. These orders can be used to cover the requested assetBuyAmount plus slippage.
- * feeOrders: An array of objects conforming to SignedOrder. These orders can be used to cover the fees for the orders param above.
- * feePercentage: Optional affiliate fee percentage used to calculate the eth amounts above.
- * bestCaseQuoteInfo: Info about the best case price for the asset.
- * worstCaseQuoteInfo: Info about the worst case price for the asset.
- */
-export interface BuyQuote {
- assetData: string;
- assetBuyAmount: BigNumber;
- orders: SignedOrder[];
- feeOrders: SignedOrder[];
- feePercentage?: number;
- bestCaseQuoteInfo: BuyQuoteInfo;
- worstCaseQuoteInfo: BuyQuoteInfo;
-}
-
-/**
- * assetEthAmount: The amount of eth required to pay for the requested asset.
- * feeEthAmount: The amount of eth required to pay the affiliate fee.
- * totalEthAmount: The total amount of eth required to complete the buy (filling orders, feeOrders, and paying affiliate fee).
- */
-export interface BuyQuoteInfo {
- assetEthAmount: BigNumber;
- feeEthAmount: BigNumber;
- totalEthAmount: BigNumber;
-}
-
-/**
- * feePercentage: The affiliate fee percentage. Defaults to 0.
- * shouldForceOrderRefresh: If set to true, new orders and state will be fetched instead of waiting for the next orderRefreshIntervalMs. Defaults to false.
- * slippagePercentage: The percentage buffer to add to account for slippage. Affects max ETH price estimates. Defaults to 0.2 (20%).
- */
-export interface BuyQuoteRequestOpts {
- feePercentage: number;
- shouldForceOrderRefresh: boolean;
- slippagePercentage: number;
-}
-
-/*
- * Options for checking liquidity
- *
- * shouldForceOrderRefresh: If set to true, new orders and state will be fetched instead of waiting for the next orderRefreshIntervalMs. Defaults to false.
- */
-export type LiquidityRequestOpts = Pick<BuyQuoteRequestOpts, 'shouldForceOrderRefresh'>;
-
-/**
- * ethAmount: The desired amount of eth to spend. Defaults to buyQuote.worstCaseQuoteInfo.totalEthAmount.
- * takerAddress: The address to perform the buy. Defaults to the first available address from the provider.
- * gasLimit: The amount of gas to send with a transaction (in Gwei). Defaults to an eth_estimateGas rpc call.
- * gasPrice: Gas price in Wei to use for a transaction
- * feeRecipient: The address where affiliate fees are sent. Defaults to null address (0x000...000).
- */
-export interface BuyQuoteExecutionOpts {
- ethAmount?: BigNumber;
- takerAddress?: string;
- gasLimit?: number;
- gasPrice?: BigNumber;
- feeRecipient: string;
-}
-
-/**
- * networkId: The ethereum network id. Defaults to 1 (mainnet).
- * orderRefreshIntervalMs: The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. Defaults to 10000ms (10s).
- * expiryBufferSeconds: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m).
- */
-export interface AssetBuyerOpts {
- networkId: number;
- orderRefreshIntervalMs: number;
- expiryBufferSeconds: number;
-}
-
-/**
- * Possible error messages thrown by an AssetBuyer instance or associated static methods.
- */
-export enum AssetBuyerError {
- NoEtherTokenContractFound = 'NO_ETHER_TOKEN_CONTRACT_FOUND',
- NoZrxTokenContractFound = 'NO_ZRX_TOKEN_CONTRACT_FOUND',
- StandardRelayerApiError = 'STANDARD_RELAYER_API_ERROR',
- InsufficientAssetLiquidity = 'INSUFFICIENT_ASSET_LIQUIDITY',
- InsufficientZrxLiquidity = 'INSUFFICIENT_ZRX_LIQUIDITY',
- NoAddressAvailable = 'NO_ADDRESS_AVAILABLE',
- InvalidOrderProviderResponse = 'INVALID_ORDER_PROVIDER_RESPONSE',
- AssetUnavailable = 'ASSET_UNAVAILABLE',
- SignatureRequestDenied = 'SIGNATURE_REQUEST_DENIED',
- TransactionValueTooLow = 'TRANSACTION_VALUE_TOO_LOW',
-}
-
-/**
- * orders: An array of signed orders
- * remainingFillableMakerAssetAmounts: A list of fillable amounts for the signed orders. The index of an item in the array associates the amount with the corresponding order.
- */
-export interface OrdersAndFillableAmounts {
- orders: SignedOrder[];
- remainingFillableMakerAssetAmounts: BigNumber[];
-}
-
-/**
- * Represents available liquidity for a given assetData
- */
-export interface LiquidityForAssetData {
- tokensAvailableInBaseUnits: BigNumber;
- ethValueAvailableInWei: BigNumber;
-}
diff --git a/packages/asset-buyer/src/utils/assert.ts b/packages/asset-buyer/src/utils/assert.ts
deleted file mode 100644
index fcf9b0d0e..000000000
--- a/packages/asset-buyer/src/utils/assert.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { assert as sharedAssert } from '@0x/assert';
-import { schemas } from '@0x/json-schemas';
-import * as _ from 'lodash';
-
-import { BuyQuote, BuyQuoteInfo, OrderProvider, OrderProviderRequest } from '../types';
-
-export const assert = {
- ...sharedAssert,
- isValidBuyQuote(variableName: string, buyQuote: BuyQuote): void {
- sharedAssert.isHexString(`${variableName}.assetData`, buyQuote.assetData);
- sharedAssert.doesConformToSchema(`${variableName}.orders`, buyQuote.orders, schemas.signedOrdersSchema);
- sharedAssert.doesConformToSchema(`${variableName}.feeOrders`, buyQuote.feeOrders, schemas.signedOrdersSchema);
- assert.isValidBuyQuoteInfo(`${variableName}.bestCaseQuoteInfo`, buyQuote.bestCaseQuoteInfo);
- assert.isValidBuyQuoteInfo(`${variableName}.worstCaseQuoteInfo`, buyQuote.worstCaseQuoteInfo);
- sharedAssert.isBigNumber(`${variableName}.assetBuyAmount`, buyQuote.assetBuyAmount);
- if (!_.isUndefined(buyQuote.feePercentage)) {
- sharedAssert.isNumber(`${variableName}.feePercentage`, buyQuote.feePercentage);
- }
- },
- isValidBuyQuoteInfo(variableName: string, buyQuoteInfo: BuyQuoteInfo): void {
- sharedAssert.isBigNumber(`${variableName}.assetEthAmount`, buyQuoteInfo.assetEthAmount);
- sharedAssert.isBigNumber(`${variableName}.feeEthAmount`, buyQuoteInfo.feeEthAmount);
- sharedAssert.isBigNumber(`${variableName}.totalEthAmount`, buyQuoteInfo.totalEthAmount);
- },
- isValidOrderProvider(variableName: string, orderFetcher: OrderProvider): void {
- sharedAssert.isFunction(`${variableName}.getOrdersAsync`, orderFetcher.getOrdersAsync);
- },
- isValidOrderProviderRequest(variableName: string, orderFetcherRequest: OrderProviderRequest): void {
- sharedAssert.isHexString(`${variableName}.makerAssetData`, orderFetcherRequest.makerAssetData);
- sharedAssert.isHexString(`${variableName}.takerAssetData`, orderFetcherRequest.takerAssetData);
- },
- isValidPercentage(variableName: string, percentage: number): void {
- assert.isNumber(variableName, percentage);
- assert.assert(
- percentage >= 0 && percentage <= 1,
- `Expected ${variableName} to be between 0 and 1, but is ${percentage}`,
- );
- },
-};
diff --git a/packages/asset-buyer/src/utils/asset_data_utils.ts b/packages/asset-buyer/src/utils/asset_data_utils.ts
deleted file mode 100644
index 70f646902..000000000
--- a/packages/asset-buyer/src/utils/asset_data_utils.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { ContractWrappers } from '@0x/contract-wrappers';
-import { assetDataUtils as sharedAssetDataUtils } from '@0x/order-utils';
-import * as _ from 'lodash';
-
-export const assetDataUtils = {
- ...sharedAssetDataUtils,
- getEtherTokenAssetData(contractWrappers: ContractWrappers): string {
- const etherTokenAddress = contractWrappers.forwarder.etherTokenAddress;
- const etherTokenAssetData = sharedAssetDataUtils.encodeERC20AssetData(etherTokenAddress);
- return etherTokenAssetData;
- },
-};
diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts
deleted file mode 100644
index 125841094..000000000
--- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts
+++ /dev/null
@@ -1,221 +0,0 @@
-import { marketUtils, SignedOrder } from '@0x/order-utils';
-import { BigNumber } from '@0x/utils';
-import * as _ from 'lodash';
-
-import { constants } from '../constants';
-import { InsufficientAssetLiquidityError } from '../errors';
-import { AssetBuyerError, BuyQuote, BuyQuoteInfo, OrdersAndFillableAmounts } from '../types';
-
-import { orderUtils } from './order_utils';
-
-// Calculates a buy quote for orders that have WETH as the takerAsset
-export const buyQuoteCalculator = {
- calculate(
- ordersAndFillableAmounts: OrdersAndFillableAmounts,
- feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
- assetBuyAmount: BigNumber,
- feePercentage: number,
- slippagePercentage: number,
- isMakerAssetZrxToken: boolean,
- ): BuyQuote {
- const orders = ordersAndFillableAmounts.orders;
- const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts;
- const feeOrders = feeOrdersAndFillableAmounts.orders;
- const remainingFillableFeeAmounts = feeOrdersAndFillableAmounts.remainingFillableMakerAssetAmounts;
- const slippageBufferAmount = assetBuyAmount.multipliedBy(slippagePercentage).integerValue();
- // find the orders that cover the desired assetBuyAmount (with slippage)
- const {
- resultOrders,
- remainingFillAmount,
- ordersRemainingFillableMakerAssetAmounts,
- } = marketUtils.findOrdersThatCoverMakerAssetFillAmount(orders, assetBuyAmount, {
- remainingFillableMakerAssetAmounts,
- slippageBufferAmount,
- });
- // if we do not have enough orders to cover the desired assetBuyAmount, throw
- 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 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%), 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)
- .integerValue(BigNumber.ROUND_FLOOR);
-
- 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)
- // TODO(bmillman): optimization
- // update this logic to find the minimum amount of feeOrders to cover the worst case as opposed to
- // finding order that cover all fees, this will help with estimating ETH and minimizing gas usage
- let resultFeeOrders = [] as SignedOrder[];
- let feeOrdersRemainingFillableMakerAssetAmounts = [] as BigNumber[];
- if (!isMakerAssetZrxToken) {
- const feeOrdersAndRemainingFeeAmount = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
- resultOrders,
- feeOrders,
- {
- remainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts,
- remainingFillableFeeAmounts,
- },
- );
- // if we do not have enough feeOrders to cover the fees, throw
- if (feeOrdersAndRemainingFeeAmount.remainingFeeAmount.gt(constants.ZERO_AMOUNT)) {
- throw new Error(AssetBuyerError.InsufficientZrxLiquidity);
- }
- resultFeeOrders = feeOrdersAndRemainingFeeAmount.resultFeeOrders;
- feeOrdersRemainingFillableMakerAssetAmounts =
- feeOrdersAndRemainingFeeAmount.feeOrdersRemainingFillableMakerAssetAmounts;
- }
-
- // assetData information for the result
- const assetData = orders[0].makerAssetData;
- // compile the resulting trimmed set of orders for makerAsset and feeOrders that are needed for assetBuyAmount
- const trimmedOrdersAndFillableAmounts: OrdersAndFillableAmounts = {
- orders: resultOrders,
- remainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts,
- };
- const trimmedFeeOrdersAndFillableAmounts: OrdersAndFillableAmounts = {
- orders: resultFeeOrders,
- remainingFillableMakerAssetAmounts: feeOrdersRemainingFillableMakerAssetAmounts,
- };
- const bestCaseQuoteInfo = calculateQuoteInfo(
- trimmedOrdersAndFillableAmounts,
- trimmedFeeOrdersAndFillableAmounts,
- assetBuyAmount,
- feePercentage,
- isMakerAssetZrxToken,
- );
- // in order to calculate the maxRate, reverse the ordersAndFillableAmounts such that they are sorted from worst rate to best rate
- const worstCaseQuoteInfo = calculateQuoteInfo(
- reverseOrdersAndFillableAmounts(trimmedOrdersAndFillableAmounts),
- reverseOrdersAndFillableAmounts(trimmedFeeOrdersAndFillableAmounts),
- assetBuyAmount,
- feePercentage,
- isMakerAssetZrxToken,
- );
- return {
- assetData,
- orders: resultOrders,
- feeOrders: resultFeeOrders,
- bestCaseQuoteInfo,
- worstCaseQuoteInfo,
- assetBuyAmount,
- feePercentage,
- };
- },
-};
-
-function calculateQuoteInfo(
- ordersAndFillableAmounts: OrdersAndFillableAmounts,
- feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
- assetBuyAmount: BigNumber,
- feePercentage: number,
- isMakerAssetZrxToken: boolean,
-): BuyQuoteInfo {
- // find the total eth and zrx needed to buy assetAmount from the resultOrders from left to right
- let assetEthAmount = constants.ZERO_AMOUNT;
- let zrxEthAmount = constants.ZERO_AMOUNT;
- if (isMakerAssetZrxToken) {
- assetEthAmount = findEthAmountNeededToBuyZrx(ordersAndFillableAmounts, assetBuyAmount);
- } else {
- // find eth and zrx amounts needed to buy
- const ethAndZrxAmountToBuyAsset = findEthAndZrxAmountNeededToBuyAsset(ordersAndFillableAmounts, assetBuyAmount);
- assetEthAmount = ethAndZrxAmountToBuyAsset[0];
- const zrxAmountToBuyAsset = ethAndZrxAmountToBuyAsset[1];
- // find eth amount needed to buy zrx
- zrxEthAmount = findEthAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset);
- }
- // eth amount needed to buy the affiliate fee
- const affiliateFeeEthAmount = assetEthAmount.multipliedBy(feePercentage).integerValue(BigNumber.ROUND_CEIL);
- // eth amount needed for fees is the sum of affiliate fee and zrx fee
- const feeEthAmount = affiliateFeeEthAmount.plus(zrxEthAmount);
- // eth amount needed in total is the sum of the amount needed for the asset and the amount needed for fees
- const totalEthAmount = assetEthAmount.plus(feeEthAmount);
- return {
- assetEthAmount,
- feeEthAmount,
- totalEthAmount,
- };
-}
-
-// given an OrdersAndFillableAmounts, reverse the orders and remainingFillableMakerAssetAmounts properties
-function reverseOrdersAndFillableAmounts(ordersAndFillableAmounts: OrdersAndFillableAmounts): OrdersAndFillableAmounts {
- const ordersCopy = _.clone(ordersAndFillableAmounts.orders);
- const remainingFillableMakerAssetAmountsCopy = _.clone(ordersAndFillableAmounts.remainingFillableMakerAssetAmounts);
- return {
- orders: ordersCopy.reverse(),
- remainingFillableMakerAssetAmounts: remainingFillableMakerAssetAmountsCopy.reverse(),
- };
-}
-
-function findEthAmountNeededToBuyZrx(
- feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
- zrxBuyAmount: BigNumber,
-): BigNumber {
- const { orders, remainingFillableMakerAssetAmounts } = feeOrdersAndFillableAmounts;
- const result = _.reduce(
- orders,
- (acc, order, index) => {
- const { totalEthAmount, remainingZrxBuyAmount } = acc;
- const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index];
- const makerFillAmount = BigNumber.min(remainingZrxBuyAmount, remainingFillableMakerAssetAmount);
- const [takerFillAmount, adjustedMakerFillAmount] = orderUtils.getTakerFillAmountForFeeOrder(
- order,
- makerFillAmount,
- );
- const extraFeeAmount = remainingFillableMakerAssetAmount.isGreaterThanOrEqualTo(adjustedMakerFillAmount)
- ? constants.ZERO_AMOUNT
- : adjustedMakerFillAmount.minus(makerFillAmount);
- return {
- totalEthAmount: totalEthAmount.plus(takerFillAmount),
- remainingZrxBuyAmount: BigNumber.max(
- constants.ZERO_AMOUNT,
- remainingZrxBuyAmount.minus(makerFillAmount).plus(extraFeeAmount),
- ),
- };
- },
- {
- totalEthAmount: constants.ZERO_AMOUNT,
- remainingZrxBuyAmount: zrxBuyAmount,
- },
- );
- return result.totalEthAmount;
-}
-
-function findEthAndZrxAmountNeededToBuyAsset(
- ordersAndFillableAmounts: OrdersAndFillableAmounts,
- assetBuyAmount: BigNumber,
-): [BigNumber, BigNumber] {
- const { orders, remainingFillableMakerAssetAmounts } = ordersAndFillableAmounts;
- const result = _.reduce(
- orders,
- (acc, order, index) => {
- const { totalEthAmount, totalZrxAmount, remainingAssetBuyAmount } = acc;
- const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index];
- const makerFillAmount = BigNumber.min(acc.remainingAssetBuyAmount, remainingFillableMakerAssetAmount);
- const takerFillAmount = orderUtils.getTakerFillAmount(order, makerFillAmount);
- const takerFeeAmount = orderUtils.getTakerFeeAmount(order, takerFillAmount);
- return {
- totalEthAmount: totalEthAmount.plus(takerFillAmount),
- totalZrxAmount: totalZrxAmount.plus(takerFeeAmount),
- remainingAssetBuyAmount: BigNumber.max(
- constants.ZERO_AMOUNT,
- remainingAssetBuyAmount.minus(makerFillAmount),
- ),
- };
- },
- {
- totalEthAmount: constants.ZERO_AMOUNT,
- totalZrxAmount: constants.ZERO_AMOUNT,
- remainingAssetBuyAmount: assetBuyAmount,
- },
- );
- return [result.totalEthAmount, result.totalZrxAmount];
-}
diff --git a/packages/asset-buyer/src/utils/calculate_liquidity.ts b/packages/asset-buyer/src/utils/calculate_liquidity.ts
deleted file mode 100644
index a8d165b4b..000000000
--- a/packages/asset-buyer/src/utils/calculate_liquidity.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { BigNumber } from '@0x/utils';
-
-import { LiquidityForAssetData, OrdersAndFillableAmounts } from '../types';
-
-import { orderUtils } from './order_utils';
-
-export const calculateLiquidity = (ordersAndFillableAmounts: OrdersAndFillableAmounts): LiquidityForAssetData => {
- const { orders, remainingFillableMakerAssetAmounts } = ordersAndFillableAmounts;
- const liquidityInBigNumbers = orders.reduce(
- (acc, order, curIndex) => {
- const availableMakerAssetAmount = remainingFillableMakerAssetAmounts[curIndex];
- if (availableMakerAssetAmount === undefined) {
- throw new Error(`No corresponding fillableMakerAssetAmounts at index ${curIndex}`);
- }
-
- const tokensAvailableForCurrentOrder = availableMakerAssetAmount;
- const ethValueAvailableForCurrentOrder = orderUtils.getTakerFillAmount(order, availableMakerAssetAmount);
- return {
- tokensAvailableInBaseUnits: acc.tokensAvailableInBaseUnits.plus(tokensAvailableForCurrentOrder),
- ethValueAvailableInWei: acc.ethValueAvailableInWei.plus(ethValueAvailableForCurrentOrder),
- };
- },
- {
- tokensAvailableInBaseUnits: new BigNumber(0),
- ethValueAvailableInWei: new BigNumber(0),
- },
- );
-
- // Turn into regular numbers
- return {
- tokensAvailableInBaseUnits: liquidityInBigNumbers.tokensAvailableInBaseUnits,
- ethValueAvailableInWei: liquidityInBigNumbers.ethValueAvailableInWei,
- };
-};
diff --git a/packages/asset-buyer/src/utils/order_provider_response_processor.ts b/packages/asset-buyer/src/utils/order_provider_response_processor.ts
deleted file mode 100644
index f08cd6150..000000000
--- a/packages/asset-buyer/src/utils/order_provider_response_processor.ts
+++ /dev/null
@@ -1,171 +0,0 @@
-import { OrderAndTraderInfo, OrderStatus, OrderValidatorWrapper } from '@0x/contract-wrappers';
-import { sortingUtils } from '@0x/order-utils';
-import { RemainingFillableCalculator } from '@0x/order-utils/lib/src/remaining_fillable_calculator';
-import { SignedOrder } from '@0x/types';
-import { BigNumber } from '@0x/utils';
-import * as _ from 'lodash';
-
-import { constants } from '../constants';
-import {
- AssetBuyerError,
- OrderProviderRequest,
- OrderProviderResponse,
- OrdersAndFillableAmounts,
- SignedOrderWithRemainingFillableMakerAssetAmount,
-} from '../types';
-
-import { orderUtils } from './order_utils';
-
-export const orderProviderResponseProcessor = {
- throwIfInvalidResponse(response: OrderProviderResponse, request: OrderProviderRequest): void {
- const { makerAssetData, takerAssetData } = request;
- _.forEach(response.orders, order => {
- if (order.makerAssetData !== makerAssetData || order.takerAssetData !== takerAssetData) {
- throw new Error(AssetBuyerError.InvalidOrderProviderResponse);
- }
- });
- },
- /**
- * Take the responses for the target orders to buy and fee orders and process them.
- * Processing includes:
- * - Drop orders that are expired or not open orders (null taker address)
- * - If shouldValidateOnChain, attempt to grab fillable amounts from on-chain otherwise assume completely fillable
- * - Sort by rate
- */
- async processAsync(
- orderProviderResponse: OrderProviderResponse,
- isMakerAssetZrxToken: boolean,
- expiryBufferSeconds: number,
- orderValidator?: OrderValidatorWrapper,
- ): Promise<OrdersAndFillableAmounts> {
- // drop orders that are expired or not open
- const filteredOrders = filterOutExpiredAndNonOpenOrders(orderProviderResponse.orders, expiryBufferSeconds);
- // set the orders to be sorted equal to the filtered orders
- let unsortedOrders = filteredOrders;
- // if an orderValidator is provided, use on chain information to calculate remaining fillable makerAsset amounts
- if (!_.isUndefined(orderValidator)) {
- const takerAddresses = _.map(filteredOrders, () => constants.NULL_ADDRESS);
- try {
- const ordersAndTradersInfo = await orderValidator.getOrdersAndTradersInfoAsync(
- filteredOrders,
- takerAddresses,
- );
- // take orders + on chain information and find the valid orders and remaining fillable maker asset amounts
- unsortedOrders = getValidOrdersWithRemainingFillableMakerAssetAmountsFromOnChain(
- filteredOrders,
- ordersAndTradersInfo,
- isMakerAssetZrxToken,
- );
- } catch (err) {
- // Sometimes we observe this call to orderValidator fail with response `0x`
- // Because of differences in Parity / Geth implementations, its very hard to tell if this response is a "system error"
- // or a revert. In this case we just swallow these errors and fallback to partial fill information from the SRA.
- // TODO(bmillman): report these errors so we have an idea of how often we're getting these failures.
- }
- }
- // sort orders by rate
- // TODO(bmillman): optimization
- // provide a feeRate to the sorting function to more accurately sort based on the current market for ZRX tokens
- const sortedOrders = isMakerAssetZrxToken
- ? sortingUtils.sortFeeOrdersByFeeAdjustedRate(unsortedOrders)
- : sortingUtils.sortOrdersByFeeAdjustedRate(unsortedOrders);
- // unbundle orders and fillable amounts and compile final result
- const result = unbundleOrdersWithAmounts(sortedOrders);
- return result;
- },
-};
-
-/**
- * Given an array of orders, return a new array with expired and non open orders filtered out.
- */
-function filterOutExpiredAndNonOpenOrders(
- orders: SignedOrderWithRemainingFillableMakerAssetAmount[],
- expiryBufferSeconds: number,
-): SignedOrderWithRemainingFillableMakerAssetAmount[] {
- const result = _.filter(orders, order => {
- return orderUtils.isOpenOrder(order) && !orderUtils.willOrderExpire(order, expiryBufferSeconds);
- });
- return result;
-}
-
-/**
- * Given an array of orders and corresponding on-chain infos, return a subset of the orders
- * that are still fillable orders with their corresponding remainingFillableMakerAssetAmounts.
- */
-function getValidOrdersWithRemainingFillableMakerAssetAmountsFromOnChain(
- inputOrders: SignedOrder[],
- ordersAndTradersInfo: OrderAndTraderInfo[],
- isMakerAssetZrxToken: boolean,
-): SignedOrderWithRemainingFillableMakerAssetAmount[] {
- // iterate through the input orders and find the ones that are still fillable
- // for the orders that are still fillable, calculate the remaining fillable maker asset amount
- const result = _.reduce(
- inputOrders,
- (accOrders, order, index) => {
- // get corresponding on-chain state for the order
- const { orderInfo, traderInfo } = ordersAndTradersInfo[index];
- // if the order IS NOT fillable, do not add anything to the accumulations and continue iterating
- if (orderInfo.orderStatus !== OrderStatus.Fillable) {
- return accOrders;
- }
- // if the order IS fillable, add the order and calculate the remaining fillable amount
- const transferrableAssetAmount = BigNumber.min(traderInfo.makerAllowance, traderInfo.makerBalance);
- const transferrableFeeAssetAmount = BigNumber.min(traderInfo.makerZrxAllowance, traderInfo.makerZrxBalance);
- const remainingTakerAssetAmount = order.takerAssetAmount.minus(orderInfo.orderTakerAssetFilledAmount);
- const remainingMakerAssetAmount = orderUtils.getRemainingMakerAmount(order, remainingTakerAssetAmount);
- const remainingFillableCalculator = new RemainingFillableCalculator(
- order.makerFee,
- order.makerAssetAmount,
- isMakerAssetZrxToken,
- transferrableAssetAmount,
- transferrableFeeAssetAmount,
- remainingMakerAssetAmount,
- );
- const remainingFillableAmount = remainingFillableCalculator.computeRemainingFillable();
- // if the order does not have any remaining fillable makerAsset, do not add anything to the accumulations and continue iterating
- if (remainingFillableAmount.lte(constants.ZERO_AMOUNT)) {
- return accOrders;
- }
- const orderWithRemainingFillableMakerAssetAmount = {
- ...order,
- remainingFillableMakerAssetAmount: remainingFillableAmount,
- };
- const newAccOrders = _.concat(accOrders, orderWithRemainingFillableMakerAssetAmount);
- return newAccOrders;
- },
- [] as SignedOrderWithRemainingFillableMakerAssetAmount[],
- );
- return result;
-}
-
-/**
- * Given an array of orders with remaining fillable maker asset amounts. Unbundle into an instance of OrdersAndRemainingFillableMakerAssetAmounts.
- * If an order is missing a corresponding remainingFillableMakerAssetAmount, assume it is completely fillable.
- */
-function unbundleOrdersWithAmounts(
- ordersWithAmounts: SignedOrderWithRemainingFillableMakerAssetAmount[],
-): OrdersAndFillableAmounts {
- const result = _.reduce(
- ordersWithAmounts,
- (acc, orderWithAmount) => {
- const { orders, remainingFillableMakerAssetAmounts } = acc;
- const { remainingFillableMakerAssetAmount, ...order } = orderWithAmount;
- // if we are still missing a remainingFillableMakerAssetAmount, assume the order is completely fillable
- const newRemainingAmount = remainingFillableMakerAssetAmount || order.makerAssetAmount;
- // if remaining amount is less than or equal to zero, do not add it
- if (newRemainingAmount.lte(constants.ZERO_AMOUNT)) {
- return acc;
- }
- const newAcc = {
- orders: _.concat(orders, order),
- remainingFillableMakerAssetAmounts: _.concat(remainingFillableMakerAssetAmounts, newRemainingAmount),
- };
- return newAcc;
- },
- {
- orders: [] as SignedOrder[],
- remainingFillableMakerAssetAmounts: [] as BigNumber[],
- },
- );
- return result;
-}
diff --git a/packages/asset-buyer/src/utils/order_utils.ts b/packages/asset-buyer/src/utils/order_utils.ts
deleted file mode 100644
index 3ea3cafd3..000000000
--- a/packages/asset-buyer/src/utils/order_utils.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import { SignedOrder } from '@0x/types';
-import { BigNumber } from '@0x/utils';
-
-import { constants } from '../constants';
-
-export const orderUtils = {
- isOrderExpired(order: SignedOrder): boolean {
- return orderUtils.willOrderExpire(order, 0);
- },
- willOrderExpire(order: SignedOrder, secondsFromNow: number): boolean {
- const millisecondsInSecond = 1000;
- const currentUnixTimestampSec = new BigNumber(Date.now() / millisecondsInSecond).integerValue();
- return order.expirationTimeSeconds.isLessThan(currentUnixTimestampSec.plus(secondsFromNow));
- },
- isOpenOrder(order: SignedOrder): boolean {
- return order.takerAddress === constants.NULL_ADDRESS;
- },
- // given a remaining amount of takerAsset, calculate how much makerAsset is available
- getRemainingMakerAmount(order: SignedOrder, remainingTakerAmount: BigNumber): BigNumber {
- const remainingMakerAmount = remainingTakerAmount
- .times(order.makerAssetAmount)
- .div(order.takerAssetAmount)
- .integerValue(BigNumber.ROUND_FLOOR);
- return remainingMakerAmount;
- },
- // given a desired amount of makerAsset, calculate how much takerAsset is required to fill that amount
- getTakerFillAmount(order: SignedOrder, makerFillAmount: BigNumber): BigNumber {
- // Round up because exchange rate favors Maker
- const takerFillAmount = makerFillAmount
- .multipliedBy(order.takerAssetAmount)
- .div(order.makerAssetAmount)
- .integerValue(BigNumber.ROUND_CEIL);
- return takerFillAmount;
- },
- // given a desired amount of takerAsset to fill, calculate how much fee is required by the taker to fill that amount
- getTakerFeeAmount(order: SignedOrder, takerFillAmount: BigNumber): BigNumber {
- // Round down because Taker fee rate favors Taker
- const takerFeeAmount = takerFillAmount
- .multipliedBy(order.takerFee)
- .div(order.takerAssetAmount)
- .integerValue(BigNumber.ROUND_FLOOR);
- return takerFeeAmount;
- },
- // given a desired amount of takerAsset to fill, calculate how much makerAsset will be filled
- getMakerFillAmount(order: SignedOrder, takerFillAmount: BigNumber): BigNumber {
- // Round down because exchange rate favors Maker
- const makerFillAmount = takerFillAmount
- .multipliedBy(order.makerAssetAmount)
- .div(order.takerAssetAmount)
- .integerValue(BigNumber.ROUND_FLOOR);
- return makerFillAmount;
- },
- // given a desired amount of makerAsset, calculate how much fee is required by the maker to fill that amount
- getMakerFeeAmount(order: SignedOrder, makerFillAmount: BigNumber): BigNumber {
- // Round down because Maker fee rate favors Maker
- const makerFeeAmount = makerFillAmount
- .multipliedBy(order.makerFee)
- .div(order.makerAssetAmount)
- .integerValue(BigNumber.ROUND_FLOOR);
- return makerFeeAmount;
- },
- // given a desired amount of ZRX from a fee order, calculate how much takerAsset is required to fill that amount
- // also calculate how much ZRX needs to be bought in order fill the desired amount + takerFee
- getTakerFillAmountForFeeOrder(order: SignedOrder, makerFillAmount: BigNumber): [BigNumber, BigNumber] {
- // For each unit of TakerAsset we buy (MakerAsset - TakerFee)
- const adjustedTakerFillAmount = makerFillAmount
- .multipliedBy(order.takerAssetAmount)
- .div(order.makerAssetAmount.minus(order.takerFee))
- .integerValue(BigNumber.ROUND_CEIL);
- // The amount that we buy will be greater than makerFillAmount, since we buy some amount for fees.
- const adjustedMakerFillAmount = orderUtils.getMakerFillAmount(order, adjustedTakerFillAmount);
- return [adjustedTakerFillAmount, adjustedMakerFillAmount];
- },
-};