aboutsummaryrefslogtreecommitdiffstats
path: root/packages/asset-buyer
diff options
context:
space:
mode:
Diffstat (limited to 'packages/asset-buyer')
-rw-r--r--packages/asset-buyer/src/asset_buyer.ts18
-rw-r--r--packages/asset-buyer/src/constants.ts1
-rw-r--r--packages/asset-buyer/src/index.ts2
-rw-r--r--packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts125
-rw-r--r--packages/asset-buyer/src/types.ts7
-rw-r--r--packages/asset-buyer/src/utils/asset_data_utils.ts26
-rw-r--r--packages/asset-buyer/src/utils/buy_quote_calculator.ts1
7 files changed, 166 insertions, 14 deletions
diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts
index 81b46d89a..5f57e3d7f 100644
--- a/packages/asset-buyer/src/asset_buyer.ts
+++ b/packages/asset-buyer/src/asset_buyer.ts
@@ -1,6 +1,6 @@
import { ContractWrappers } from '@0xproject/contract-wrappers';
import { schemas } from '@0xproject/json-schemas';
-import { assetDataUtils, SignedOrder } from '@0xproject/order-utils';
+import { SignedOrder } from '@0xproject/order-utils';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider } from 'ethereum-types';
@@ -19,6 +19,7 @@ import {
} from './types';
import { assert } from './utils/assert';
+import { assetDataUtils } from './utils/asset_data_utils';
import { buyQuoteCalculator } from './utils/buy_quote_calculator';
import { orderFetcherResponseProcessor } from './utils/order_fetcher_response_processor';
@@ -284,24 +285,13 @@ export class AssetBuyer {
* Will throw if WETH does not exist for the current network.
*/
private _getEtherTokenAssetData(): string {
- const etherTokenAddressIfExists = this._contractWrappers.etherToken.getContractAddressIfExists();
- if (_.isUndefined(etherTokenAddressIfExists)) {
- throw new Error(AssetBuyerError.NoEtherTokenContractFound);
- }
- const etherTokenAssetData = assetDataUtils.encodeERC20AssetData(etherTokenAddressIfExists);
- return etherTokenAssetData;
+ 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 _getZrxTokenAssetData(): string {
- let zrxTokenAssetData: string;
- try {
- zrxTokenAssetData = this._contractWrappers.exchange.getZRXAssetData();
- } catch (err) {
- throw new Error(AssetBuyerError.NoZrxTokenContractFound);
- }
- return zrxTokenAssetData;
+ return assetDataUtils.getZrxTokenAssetData(this._contractWrappers);
}
}
diff --git a/packages/asset-buyer/src/constants.ts b/packages/asset-buyer/src/constants.ts
index 701832fd4..0ebe0f8e2 100644
--- a/packages/asset-buyer/src/constants.ts
+++ b/packages/asset-buyer/src/constants.ts
@@ -15,4 +15,5 @@ export const constants = {
DEFAULT_ORDER_REFRESH_INTERVAL_MS: 10000, // 10 seconds
ETHER_TOKEN_DECIMALS: 18,
DEFAULT_BUY_QUOTE_REQUEST_OPTS,
+ MAX_PER_PAGE: 10000,
};
diff --git a/packages/asset-buyer/src/index.ts b/packages/asset-buyer/src/index.ts
index efd3523fd..2156b7e96 100644
--- a/packages/asset-buyer/src/index.ts
+++ b/packages/asset-buyer/src/index.ts
@@ -5,6 +5,7 @@ export { BigNumber } from '@0xproject/utils';
export { AssetBuyer } from './asset_buyer';
export { ProvidedOrderFetcher } from './order_fetchers/provided_order_fetcher';
export { StandardRelayerAPIOrderFetcher } from './order_fetchers/standard_relayer_api_order_fetcher';
+export { StandardRelayerAPIAssetBuyerManager } from './standard_relayer_api_asset_buyer_manager';
export {
AssetBuyerError,
BuyQuote,
@@ -12,4 +13,5 @@ export {
OrderFetcherRequest,
OrderFetcherResponse,
SignedOrderWithRemainingFillableMakerAssetAmount,
+ StandardRelayerApiAssetBuyerManagerError,
} from './types';
diff --git a/packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts b/packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts
new file mode 100644
index 000000000..6cd96fab2
--- /dev/null
+++ b/packages/asset-buyer/src/standard_relayer_api_asset_buyer_manager.ts
@@ -0,0 +1,125 @@
+import { HttpClient } from '@0xproject/connect';
+import { ContractWrappers } from '@0xproject/contract-wrappers';
+import { ObjectMap } from '@0xproject/types';
+import { Provider } from 'ethereum-types';
+import * as _ from 'lodash';
+
+import { AssetBuyer } from './asset_buyer';
+import { constants } from './constants';
+import { assert } from './utils/assert';
+import { assetDataUtils } from './utils/asset_data_utils';
+
+import { OrderFetcher, StandardRelayerApiAssetBuyerManagerError } from './types';
+
+export class StandardRelayerAPIAssetBuyerManager {
+ // Map of assetData to AssetBuyer for that assetData
+ public readonly assetBuyerMap: ObjectMap<AssetBuyer>;
+ /**
+ * Returns an array of all assetDatas available at the provided sraApiUrl
+ * @param sraApiUrl The standard relayer API base HTTP url you would like to source orders from.
+ * @param pairedWithAssetData Optional filter argument to return assetDatas that only pair with this assetData value.
+ *
+ * @return An array of all assetDatas available at the provider sraApiUrl
+ */
+ public static async getAllAvailableAssetDatasAsync(
+ sraApiUrl: string,
+ pairedWithAssetData?: string,
+ ): Promise<string[]> {
+ const client = new HttpClient(sraApiUrl);
+ const params = {
+ assetDataA: pairedWithAssetData,
+ perPage: constants.MAX_PER_PAGE,
+ };
+ const assetPairsResponse = await client.getAssetPairsAsync(params);
+ return _.uniq(_.map(assetPairsResponse.records, pairsItem => pairsItem.assetDataB.assetData));
+ }
+ /**
+ * Instantiates a new StandardRelayerAPIAssetBuyerManager instance with all available assetDatas at the provided sraApiUrl
+ * @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 orderFetcher An object that conforms to OrderFetcher, see type for definition.
+ * @param networkId The ethereum network id. Defaults to 1 (mainnet).
+ * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states.
+ * Defaults to 10000ms (10s).
+ * @return An promise of an instance of StandardRelayerAPIAssetBuyerManager
+ */
+ public static async getAssetBuyerManagerWithAllAvailableAssetDatasAsync(
+ provider: Provider,
+ sraApiUrl: string,
+ orderFetcher: OrderFetcher,
+ networkId: number = constants.MAINNET_NETWORK_ID,
+ orderRefreshIntervalMs?: number,
+ ): Promise<StandardRelayerAPIAssetBuyerManager> {
+ const contractWrappers = new ContractWrappers(provider, { networkId });
+ const etherTokenAssetData = assetDataUtils.getEtherTokenAssetData(contractWrappers);
+ const assetDatas = await StandardRelayerAPIAssetBuyerManager.getAllAvailableAssetDatasAsync(
+ sraApiUrl,
+ etherTokenAssetData,
+ );
+ return new StandardRelayerAPIAssetBuyerManager(
+ provider,
+ assetDatas,
+ orderFetcher,
+ networkId,
+ orderRefreshIntervalMs,
+ );
+ }
+ /**
+ * Instantiates a new StandardRelayerAPIAssetBuyerManager instance
+ * @param provider The Provider instance you would like to use for interacting with the Ethereum network.
+ * @param assetDatas The assetDatas of the desired assets to buy (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
+ * @param orderFetcher An object that conforms to OrderFetcher, see type for definition.
+ * @param networkId The ethereum network id. Defaults to 1 (mainnet).
+ * @param orderRefreshIntervalMs The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states.
+ * Defaults to 10000ms (10s).
+ * @return An instance of StandardRelayerAPIAssetBuyerManager
+ */
+ constructor(
+ provider: Provider,
+ assetDatas: string[],
+ orderFetcher: OrderFetcher,
+ networkId?: number,
+ orderRefreshIntervalMs?: number,
+ ) {
+ assert.assert(assetDatas.length > 0, `Expected 'assetDatas' to be a non-empty array.`);
+ this.assetBuyerMap = _.reduce(
+ assetDatas,
+ (accAssetBuyerMap: ObjectMap<AssetBuyer>, assetData: string) => {
+ accAssetBuyerMap[assetData] = new AssetBuyer(
+ provider,
+ assetData,
+ orderFetcher,
+ networkId,
+ orderRefreshIntervalMs,
+ );
+ return accAssetBuyerMap;
+ },
+ {},
+ );
+ }
+ /**
+ * Get a AssetBuyer for the provided assetData
+ * @param assetData The desired assetData.
+ *
+ * @return An instance of AssetBuyer
+ */
+ public getAssetBuyerFromAssetData(assetData: string): AssetBuyer {
+ const assetBuyer = this.assetBuyerMap[assetData];
+ if (_.isUndefined(assetBuyer)) {
+ throw new Error(
+ `${StandardRelayerApiAssetBuyerManagerError.AssetBuyerNotFound}: For assetData ${assetData}`,
+ );
+ }
+ return assetBuyer;
+ }
+ /**
+ * Get a AssetBuyer for the provided ERC20 tokenAddress
+ * @param tokenAddress The desired tokenAddress.
+ *
+ * @return An instance of AssetBuyer
+ */
+ public getAssetBuyerFromERC20TokenAddress(tokenAddress: string): AssetBuyer {
+ const assetData = assetDataUtils.encodeERC20AssetData(tokenAddress);
+ return this.getAssetBuyerFromAssetData(assetData);
+ }
+}
diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts
index 339bce52c..c30bdf4bb 100644
--- a/packages/asset-buyer/src/types.ts
+++ b/packages/asset-buyer/src/types.ts
@@ -70,6 +70,13 @@ export enum AssetBuyerError {
NoAddressAvailable = 'NO_ADDRESS_AVAILABLE',
}
+/**
+ * Possible errors thrown by an StandardRelayerApiAssetBuyerManager instance or associated static methods.
+ */
+export enum StandardRelayerApiAssetBuyerManagerError {
+ AssetBuyerNotFound = 'ASSET_BUYER_NOT_FOUND',
+}
+
export interface AssetBuyerOrdersAndFillableAmounts {
orders: SignedOrder[];
feeOrders: SignedOrder[];
diff --git a/packages/asset-buyer/src/utils/asset_data_utils.ts b/packages/asset-buyer/src/utils/asset_data_utils.ts
new file mode 100644
index 000000000..10ff31057
--- /dev/null
+++ b/packages/asset-buyer/src/utils/asset_data_utils.ts
@@ -0,0 +1,26 @@
+import { ContractWrappers } from '@0xproject/contract-wrappers';
+import { assetDataUtils as sharedAssetDataUtils } from '@0xproject/order-utils';
+import * as _ from 'lodash';
+
+import { AssetBuyerError } from '../types';
+
+export const assetDataUtils = {
+ ...sharedAssetDataUtils,
+ getEtherTokenAssetData(contractWrappers: ContractWrappers): string {
+ const etherTokenAddressIfExists = contractWrappers.etherToken.getContractAddressIfExists();
+ if (_.isUndefined(etherTokenAddressIfExists)) {
+ throw new Error(AssetBuyerError.NoEtherTokenContractFound);
+ }
+ const etherTokenAssetData = sharedAssetDataUtils.encodeERC20AssetData(etherTokenAddressIfExists);
+ return etherTokenAssetData;
+ },
+ getZrxTokenAssetData(contractWrappers: ContractWrappers): string {
+ let zrxTokenAssetData: string;
+ try {
+ zrxTokenAssetData = contractWrappers.exchange.getZRXAssetData();
+ } catch (err) {
+ throw new Error(AssetBuyerError.NoZrxTokenContractFound);
+ }
+ return zrxTokenAssetData;
+ },
+};
diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts
index 52cecf8ad..31823cc02 100644
--- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts
+++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts
@@ -59,6 +59,7 @@ export const buyQuoteCalculator = {
let maxEthAmount = constants.ZERO_AMOUNT;
let cumulativeMakerAmount = constants.ZERO_AMOUNT;
_.forEach(allOrders, (order, index) => {
+ // TODO: Move this logic to order_utils
const remainingFillableMakerAssetAmount = allRemainingAmounts[index];
const orderRate = order.takerAssetAmount.div(order.makerAssetAmount);
const claimableTakerAssetAmount = orderRate.mul(remainingFillableMakerAssetAmount);