refactored ERC <> ERC for asset-buyer

minor update to interface
This commit is contained in:
David Sun
2019-05-31 13:47:38 -07:00
parent 2fdc0426ff
commit 64e3b6f5ee
10 changed files with 287 additions and 187 deletions

View File

@@ -4,7 +4,7 @@
"engines": { "engines": {
"node": ">=6.12" "node": ">=6.12"
}, },
"description": "Convenience package for discovering and buying assets with Ether.", "description": "Convenience package for discovering and buying assets for both on chain and off chain needs.",
"main": "lib/src/index.js", "main": "lib/src/index.js",
"types": "lib/src/index.d.ts", "types": "lib/src/index.d.ts",
"scripts": { "scripts": {

View File

@@ -124,39 +124,39 @@ export class AssetBuyer {
* @return An object that conforms to BuyQuote that satisfies 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( public async getBuyQuoteAsync(
assetData: string, makerAssetData: string,
assetBuyAmount: BigNumber, takerAssetData: string,
makerAssetBuyAmount: BigNumber,
options: Partial<BuyQuoteRequestOpts> = {}, options: Partial<BuyQuoteRequestOpts> = {},
): Promise<BuyQuote> { ): Promise<BuyQuote> {
const { feePercentage, shouldForceOrderRefresh, slippagePercentage } = _.merge( const { shouldForceOrderRefresh, slippagePercentage } = _.merge(
{}, {},
constants.DEFAULT_BUY_QUOTE_REQUEST_OPTS, constants.DEFAULT_BUY_QUOTE_REQUEST_OPTS,
options, options,
); );
assert.isString('assetData', assetData); assert.isString('makerAssetData', makerAssetData);
assert.isBigNumber('assetBuyAmount', assetBuyAmount); assert.isString('takerAssetData', takerAssetData);
assert.isValidPercentage('feePercentage', feePercentage); assert.isBigNumber('makerAssetBuyAmount', makerAssetBuyAmount);
assert.isBoolean('shouldForceOrderRefresh', shouldForceOrderRefresh); assert.isBoolean('shouldForceOrderRefresh', shouldForceOrderRefresh);
assert.isNumber('slippagePercentage', slippagePercentage); assert.isNumber('slippagePercentage', slippagePercentage);
const zrxTokenAssetData = this._getZrxTokenAssetDataOrThrow(); const zrxTokenAssetData = this._getZrxTokenAssetDataOrThrow();
const isMakerAssetZrxToken = assetData === zrxTokenAssetData; const isMakerAssetZrxToken = makerAssetData === zrxTokenAssetData;
// get the relevant orders for the makerAsset and fees // get the relevant orders for the makerAsset and fees
// if the requested assetData is ZRX, don't get the fee info // if the requested assetData is ZRX, don't get the fee info
const [ordersAndFillableAmounts, feeOrdersAndFillableAmounts] = await Promise.all([ const [ordersAndFillableAmounts, feeOrdersAndFillableAmounts] = await Promise.all([
this.getOrdersAndFillableAmountsAsync(assetData, shouldForceOrderRefresh), this.getOrdersAndFillableAmountsAsync(makerAssetData, takerAssetData, shouldForceOrderRefresh),
isMakerAssetZrxToken isMakerAssetZrxToken
? Promise.resolve(constants.EMPTY_ORDERS_AND_FILLABLE_AMOUNTS) ? Promise.resolve(constants.EMPTY_ORDERS_AND_FILLABLE_AMOUNTS)
: this.getOrdersAndFillableAmountsAsync(zrxTokenAssetData, shouldForceOrderRefresh), : this.getOrdersAndFillableAmountsAsync(zrxTokenAssetData, takerAssetData, shouldForceOrderRefresh),
shouldForceOrderRefresh, shouldForceOrderRefresh,
]); ]);
if (ordersAndFillableAmounts.orders.length === 0) { if (ordersAndFillableAmounts.orders.length === 0) {
throw new Error(`${AssetBuyerError.AssetUnavailable}: For assetData ${assetData}`); throw new Error(`${AssetBuyerError.AssetUnavailable}: For makerAssetdata ${makerAssetData} and takerAssetdata ${takerAssetData}`);
} }
const buyQuote = buyQuoteCalculator.calculate( const buyQuote = buyQuoteCalculator.calculate(
ordersAndFillableAmounts, ordersAndFillableAmounts,
feeOrdersAndFillableAmounts, feeOrdersAndFillableAmounts,
assetBuyAmount, makerAssetBuyAmount,
feePercentage,
slippagePercentage, slippagePercentage,
isMakerAssetZrxToken, isMakerAssetZrxToken,
); );
@@ -172,14 +172,17 @@ export class AssetBuyer {
* @return An object that conforms to BuyQuote that satisfies 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( public async getBuyQuoteForERC20TokenAddressAsync(
tokenAddress: string, makerTokenAddress: string,
assetBuyAmount: BigNumber, takerTokenAddress: string,
makerAssetBuyAmount: BigNumber,
options: Partial<BuyQuoteRequestOpts> = {}, options: Partial<BuyQuoteRequestOpts> = {},
): Promise<BuyQuote> { ): Promise<BuyQuote> {
assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('makerTokenAddress', makerTokenAddress);
assert.isBigNumber('assetBuyAmount', assetBuyAmount); assert.isETHAddressHex('takerTokenAddress', takerTokenAddress);
const assetData = assetDataUtils.encodeERC20AssetData(tokenAddress); assert.isBigNumber('makerAssetBuyAmount', makerAssetBuyAmount);
const buyQuote = this.getBuyQuoteAsync(assetData, assetBuyAmount, options); const makerAssetData = assetDataUtils.encodeERC20AssetData(makerTokenAddress);
const takerAssetData = assetDataUtils.encodeERC20AssetData(takerTokenAddress);
const buyQuote = this.getBuyQuoteAsync(makerAssetData, takerAssetData, makerAssetBuyAmount, options);
return buyQuote; return buyQuote;
} }
/** /**
@@ -190,125 +193,162 @@ export class AssetBuyer {
* *
* @return An object that conforms to LiquidityForAssetData that satisfies 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( public async getLiquidityForMakerTakerAssetdataPairAsync(
assetData: string, makerAssetData: string,
takerAssetData: string,
options: Partial<LiquidityRequestOpts> = {}, options: Partial<LiquidityRequestOpts> = {},
): Promise<LiquidityForAssetData> { ): Promise<LiquidityForAssetData> {
const shouldForceOrderRefresh = const shouldForceOrderRefresh =
options.shouldForceOrderRefresh !== undefined ? options.shouldForceOrderRefresh : false; options.shouldForceOrderRefresh !== undefined ? options.shouldForceOrderRefresh : false;
assert.isString('assetData', assetData); assert.isString('makerAssetDataa', makerAssetData);
assetDataUtils.decodeAssetDataOrThrow(assetData); assert.isString('takerAssetData', takerAssetData);
assetDataUtils.decodeAssetDataOrThrow(makerAssetData);
assetDataUtils.decodeAssetDataOrThrow(takerAssetData);
assert.isBoolean('options.shouldForceOrderRefresh', shouldForceOrderRefresh); assert.isBoolean('options.shouldForceOrderRefresh', shouldForceOrderRefresh);
const assetPairs = await this.orderProvider.getAvailableMakerAssetDatasAsync(assetData); const assetPairs = await this.orderProvider.getAvailableMakerAssetDatasAsync(takerAssetData);
const etherTokenAssetData = this._getEtherTokenAssetDataOrThrow(); if (!assetPairs.includes(makerAssetData)) {
if (!assetPairs.includes(etherTokenAssetData)) {
return { return {
tokensAvailableInBaseUnits: new BigNumber(0), makerTokensAvailableInBaseUnits: new BigNumber(0),
ethValueAvailableInWei: new BigNumber(0), takerTokensAvailableInBaseUnits: new BigNumber(0),
}; };
} }
const ordersAndFillableAmounts = await this.getOrdersAndFillableAmountsAsync( const ordersAndFillableAmounts = await this.getOrdersAndFillableAmountsAsync(
assetData, makerAssetData,
takerAssetData,
shouldForceOrderRefresh, shouldForceOrderRefresh,
); );
return calculateLiquidity(ordersAndFillableAmounts); 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 (ethAmount !== undefined) {
// assert.isBigNumber('ethAmount', ethAmount);
// }
// if (takerAddress !== undefined) {
// assert.isETHAddressHex('takerAddress', takerAddress);
// }
// assert.isETHAddressHex('feeRecipient', feeRecipient);
// if (gasLimit !== undefined) {
// assert.isNumber('gasLimit', gasLimit);
// }
// if (gasPrice !== undefined) {
// assert.isBigNumber('gasPrice', gasPrice);
// }
// const { orders, feeOrders, makerAssetBuyAmount, worstCaseQuoteInfo } = buyQuote;
// // TODO(dave4506) upgrade logic for asset-buyer2.0
// // if no takerAddress is provided, try to get one from the provider
// // let finalTakerAddress;
// // if (takerAddress !== undefined) {
// // finalTakerAddress = takerAddress;
// // } else {
// // const web3Wrapper = new Web3Wrapper(this.provider);
// // const availableAddresses = await web3Wrapper.getAvailableAddressesAsync();
// // const firstAvailableAddress = _.head(availableAddresses);
// // if (firstAvailableAddress !== undefined) {
// // 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;
// // }
// // }
// return Promise.resolve('test');
// }
/** /**
* Given a BuyQuote and desired rate, attempt to execute the buy. * Get the asset data of all assets that can be used to purchase makerAssetData in the order provider passed in at init.
* @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 (ethAmount !== undefined) {
assert.isBigNumber('ethAmount', ethAmount);
}
if (takerAddress !== undefined) {
assert.isETHAddressHex('takerAddress', takerAddress);
}
assert.isETHAddressHex('feeRecipient', feeRecipient);
if (gasLimit !== undefined) {
assert.isNumber('gasLimit', gasLimit);
}
if (gasPrice !== undefined) {
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 (takerAddress !== undefined) {
finalTakerAddress = takerAddress;
} else {
const web3Wrapper = new Web3Wrapper(this.provider);
const availableAddresses = await web3Wrapper.getAvailableAddressesAsync();
const firstAvailableAddress = _.head(availableAddresses);
if (firstAvailableAddress !== undefined) {
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. * @return An array of asset data strings that can be purchased using wETH.
*/ */
public async getAvailableAssetDatasAsync(): Promise<string[]> { public async getAvailableTakerAssetDatasAsync(makerAssetData: string): Promise<string[]> {
const etherTokenAssetData = this._getEtherTokenAssetDataOrThrow(); assert.isString('makerAssetDataa', makerAssetData);
return this.orderProvider.getAvailableMakerAssetDatasAsync(etherTokenAssetData); assetDataUtils.decodeAssetDataOrThrow(makerAssetData);
return this.orderProvider.getAvailableTakerAssetDatasAsync(makerAssetData);
} }
/**
* Get the asset data of all assets that are purchaseable with makerAssetData in the order provider passed in at init.
*
* @return An array of asset data strings that can be purchased using wETH.
*/
public async getAvailableMakerAssetDatasAsync(takerAssetData: string): Promise<string[]> {
assert.isString('takerAssetData', takerAssetData);
assetDataUtils.decodeAssetDataOrThrow(takerAssetData);
return this.orderProvider.getAvailableMakerAssetDatasAsync(takerAssetData);
}
/**
* validates that the taker + maker asset pair is availalbe from the order provider passed
*
* @return A boolean on if the taker + maker pair exists
*/
public async isTakerMakerAssetDataPairAvailableAsync(makerAssetData: string, takerAssetData: string): Promise<boolean> {
assert.isString('makerAssetDataa', makerAssetData);
assert.isString('takerAssetData', takerAssetData);
assetDataUtils.decodeAssetDataOrThrow(makerAssetData);
assetDataUtils.decodeAssetDataOrThrow(takerAssetData);
return _.findIndex(await this.getAvailableMakerAssetDatasAsync(takerAssetData), makerAssetData) !== -1;
}
/** /**
* Grab orders from the map, if there is a miss or it is time to refresh, fetch and process the orders * 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 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. * @param shouldForceOrderRefresh If set to true, new orders and state will be fetched instead of waiting for the next orderRefreshIntervalMs.
*/ */
public async getOrdersAndFillableAmountsAsync( public async getOrdersAndFillableAmountsAsync(
assetData: string, makerAssetData: string,
takerAssetData: string,
shouldForceOrderRefresh: boolean, shouldForceOrderRefresh: boolean,
): Promise<OrdersAndFillableAmounts> { ): Promise<OrdersAndFillableAmounts> {
assert.isString('makerAssetDataa', makerAssetData);
assert.isString('takerAssetData', takerAssetData);
assetDataUtils.decodeAssetDataOrThrow(makerAssetData);
assetDataUtils.decodeAssetDataOrThrow(takerAssetData);
// try to get ordersEntry from the map // try to get ordersEntry from the map
const ordersEntryIfExists = this._ordersEntryMap[assetData]; const ordersEntryIfExists = this._ordersEntryMap[this._getOrdersEntryMapKey(makerAssetData, takerAssetData)];
// we should refresh if: // we should refresh if:
// we do not have any orders OR // we do not have any orders OR
// we are forced to OR // we are forced to OR
@@ -322,12 +362,11 @@ export class AssetBuyer {
const result = ordersEntryIfExists.ordersAndFillableAmounts; const result = ordersEntryIfExists.ordersAndFillableAmounts;
return result; return result;
} }
const etherTokenAssetData = this._getEtherTokenAssetDataOrThrow();
const zrxTokenAssetData = this._getZrxTokenAssetDataOrThrow(); const zrxTokenAssetData = this._getZrxTokenAssetDataOrThrow();
// construct orderProvider request // construct orderProvider request
const orderProviderRequest = { const orderProviderRequest = {
makerAssetData: assetData, makerAssetData,
takerAssetData: etherTokenAssetData, takerAssetData,
networkId: this.networkId, networkId: this.networkId,
}; };
const request = orderProviderRequest; const request = orderProviderRequest;
@@ -337,7 +376,7 @@ export class AssetBuyer {
// ie. it should only return maker/taker assetDatas that are specified // ie. it should only return maker/taker assetDatas that are specified
orderProviderResponseProcessor.throwIfInvalidResponse(response, request); orderProviderResponseProcessor.throwIfInvalidResponse(response, request);
// process the responses into one object // process the responses into one object
const isMakerAssetZrxToken = assetData === zrxTokenAssetData; const isMakerAssetZrxToken = makerAssetData === zrxTokenAssetData;
const ordersAndFillableAmounts = await orderProviderResponseProcessor.processAsync( const ordersAndFillableAmounts = await orderProviderResponseProcessor.processAsync(
response, response,
isMakerAssetZrxToken, isMakerAssetZrxToken,
@@ -349,16 +388,26 @@ export class AssetBuyer {
ordersAndFillableAmounts, ordersAndFillableAmounts,
lastRefreshTime, lastRefreshTime,
}; };
this._ordersEntryMap[assetData] = updatedOrdersEntry; this._ordersEntryMap[this._getOrdersEntryMapKey(makerAssetData, takerAssetData)] = updatedOrdersEntry;
return ordersAndFillableAmounts; return ordersAndFillableAmounts;
} }
/**
*
* get the key for _orderEntryMap for maker + taker asset pair
*/
// tslint:disable-next-line: prefer-function-over-method
private _getOrdersEntryMapKey(makerAssetData: string, takerAssetData: string): string {
return `${makerAssetData}_${takerAssetData}`;
}
/** /**
* Get the assetData that represents the WETH token. * Get the assetData that represents the WETH token.
* Will throw if WETH does not exist for the current network. * Will throw if WETH does not exist for the current network.
*/ */
private _getEtherTokenAssetDataOrThrow(): string { // private _getEtherTokenAssetDataOrThrow(): string {
return assetDataUtils.getEtherTokenAssetData(this._contractWrappers); // return assetDataUtils.getEtherTokenAssetData(this._contractWrappers);
} // }
/** /**
* Get the assetData that represents the ZRX token. * Get the assetData that represents the ZRX token.
* Will throw if ZRX does not exist for the current network. * Will throw if ZRX does not exist for the current network.

View File

@@ -13,13 +13,17 @@ export {
Web3JsV2Provider, Web3JsV2Provider,
Web3JsV3Provider, Web3JsV3Provider,
} from 'ethereum-types'; } from 'ethereum-types';
// TODO(dave4506): if this lives under the 0x.js library, then these type exports should be removed in favor of minimizing redundancy
export { SignedOrder } from '@0x/types'; export { SignedOrder } from '@0x/types';
export { BigNumber } from '@0x/utils'; export { BigNumber } from '@0x/utils';
export { AssetBuyer } from './asset_buyer'; export { AssetBuyer } from './asset_buyer';
export { InsufficientAssetLiquidityError } from './errors'; export { InsufficientAssetLiquidityError } from './errors';
export { BasicOrderProvider } from './order_providers/basic_order_provider'; export { BasicOrderProvider } from './order_providers/basic_order_provider';
export { StandardRelayerAPIOrderProvider } from './order_providers/standard_relayer_api_order_provider'; export { StandardRelayerAPIOrderProvider } from './order_providers/standard_relayer_api_order_provider';
export { export {
AssetBuyerError, AssetBuyerError,
AssetBuyerOpts, AssetBuyerOpts,

View File

@@ -38,4 +38,13 @@ export class BasicOrderProvider implements OrderProvider {
const ordersWithTakerAssetData = _.filter(this.orders, { takerAssetData }); const ordersWithTakerAssetData = _.filter(this.orders, { takerAssetData });
return _.map(ordersWithTakerAssetData, order => order.makerAssetData); return _.map(ordersWithTakerAssetData, order => order.makerAssetData);
} }
/**
* Given a maker asset data string, return all availabled paired taker asset data strings.
* @param makerAssetData A string representing the maker asset data.
* @return An array of asset data strings that can be used to purchased makerAssetData.
*/
public async getAvailableTakerAssetDatasAsync(makerAssetData: string): Promise<string[]> {
const ordersWithMakerAssetData = _.filter(this.orders, { makerAssetData });
return _.map(ordersWithMakerAssetData, order => order.takerAssetData);
}
} }

View File

@@ -111,4 +111,32 @@ export class StandardRelayerAPIOrderProvider implements OrderProvider {
} }
}); });
} }
/**
* Given a maker asset data string, return all availabled paired taker asset data strings.
* @param makerAssetData A string representing the maker asset data.
* @return An array of asset data strings that can be used to purchased makerAssetData.
*/
public async getAvailableTakerAssetDatasAsync(makerAssetData: string): Promise<string[]> {
// Return a maximum of 1000 asset datas
const maxPerPage = 1000;
const requestOpts = { networkId: this.networkId, perPage: maxPerPage };
const assetPairsRequest = { assetDataA: makerAssetData };
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 === makerAssetData) {
return item.assetDataB.assetData;
} else {
return item.assetDataA.assetData;
}
});
}
} }

View File

@@ -25,6 +25,7 @@ export interface OrderProviderResponse {
export interface SignedOrderWithRemainingFillableMakerAssetAmount extends SignedOrder { export interface SignedOrderWithRemainingFillableMakerAssetAmount extends SignedOrder {
remainingFillableMakerAssetAmount?: BigNumber; remainingFillableMakerAssetAmount?: BigNumber;
} }
/** /**
* gerOrdersAsync: Given an OrderProviderRequest, get an OrderProviderResponse. * gerOrdersAsync: Given an OrderProviderRequest, get an OrderProviderResponse.
* getAvailableMakerAssetDatasAsync: Given a taker asset data string, return all availabled paired maker asset data strings. * getAvailableMakerAssetDatasAsync: Given a taker asset data string, return all availabled paired maker asset data strings.
@@ -32,6 +33,7 @@ export interface SignedOrderWithRemainingFillableMakerAssetAmount extends Signed
export interface OrderProvider { export interface OrderProvider {
getOrdersAsync: (orderProviderRequest: OrderProviderRequest) => Promise<OrderProviderResponse>; getOrdersAsync: (orderProviderRequest: OrderProviderRequest) => Promise<OrderProviderResponse>;
getAvailableMakerAssetDatasAsync: (takerAssetData: string) => Promise<string[]>; getAvailableMakerAssetDatasAsync: (takerAssetData: string) => Promise<string[]>;
getAvailableTakerAssetDatasAsync: (makerAssetData: string) => Promise<string[]>;
} }
/** /**
@@ -44,13 +46,15 @@ export interface OrderProvider {
* worstCaseQuoteInfo: Info about the worst case price for the asset. * worstCaseQuoteInfo: Info about the worst case price for the asset.
*/ */
export interface BuyQuote { export interface BuyQuote {
assetData: string; takerAssetData: string;
assetBuyAmount: BigNumber; makerAssetData: string;
makerAssetBuyAmount: BigNumber;
orders: SignedOrder[]; orders: SignedOrder[];
feeOrders: SignedOrder[]; feeOrders: SignedOrder[];
feePercentage?: number;
bestCaseQuoteInfo: BuyQuoteInfo; bestCaseQuoteInfo: BuyQuoteInfo;
worstCaseQuoteInfo: BuyQuoteInfo; worstCaseQuoteInfo: BuyQuoteInfo;
toAddress: string; // exchange address, coordinator address
isUsingCoordinator: boolean;
} }
/** /**
@@ -59,18 +63,16 @@ export interface BuyQuote {
* totalEthAmount: The total amount of eth required to complete the buy (filling orders, feeOrders, and paying affiliate fee). * totalEthAmount: The total amount of eth required to complete the buy (filling orders, feeOrders, and paying affiliate fee).
*/ */
export interface BuyQuoteInfo { export interface BuyQuoteInfo {
assetEthAmount: BigNumber; takerTokenAmount: BigNumber;
feeEthAmount: BigNumber; feeTakerTokenAmount: BigNumber;
totalEthAmount: BigNumber; totalTakerTokenAmount: 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. * 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%). * slippagePercentage: The percentage buffer to add to account for slippage. Affects max ETH price estimates. Defaults to 0.2 (20%).
*/ */
export interface BuyQuoteRequestOpts { export interface BuyQuoteRequestOpts {
feePercentage: number;
shouldForceOrderRefresh: boolean; shouldForceOrderRefresh: boolean;
slippagePercentage: number; slippagePercentage: number;
} }
@@ -137,6 +139,6 @@ export interface OrdersAndFillableAmounts {
* Represents available liquidity for a given assetData * Represents available liquidity for a given assetData
*/ */
export interface LiquidityForAssetData { export interface LiquidityForAssetData {
tokensAvailableInBaseUnits: BigNumber; makerTokensAvailableInBaseUnits: BigNumber;
ethValueAvailableInWei: BigNumber; takerTokensAvailableInBaseUnits: BigNumber;
} }

View File

@@ -6,20 +6,24 @@ import { BuyQuote, BuyQuoteInfo, OrderProvider, OrderProviderRequest } from '../
export const assert = { export const assert = {
...sharedAssert, ...sharedAssert,
isValidBuyQuote(variableName: string, buyQuote: BuyQuote): void { isValidBuyQuote(variableName: string, buyQuote: BuyQuote): void {
sharedAssert.isHexString(`${variableName}.assetData`, buyQuote.assetData); sharedAssert.isHexString(`${variableName}.takerAssetData`, buyQuote.takerAssetData);
sharedAssert.isHexString(`${variableName}.makerAssetData`, buyQuote.makerAssetData);
sharedAssert.doesConformToSchema(`${variableName}.orders`, buyQuote.orders, schemas.signedOrdersSchema); sharedAssert.doesConformToSchema(`${variableName}.orders`, buyQuote.orders, schemas.signedOrdersSchema);
sharedAssert.doesConformToSchema(`${variableName}.feeOrders`, buyQuote.feeOrders, schemas.signedOrdersSchema); sharedAssert.doesConformToSchema(`${variableName}.feeOrders`, buyQuote.feeOrders, schemas.signedOrdersSchema);
assert.isValidBuyQuoteInfo(`${variableName}.bestCaseQuoteInfo`, buyQuote.bestCaseQuoteInfo); assert.isValidBuyQuoteInfo(`${variableName}.bestCaseQuoteInfo`, buyQuote.bestCaseQuoteInfo);
assert.isValidBuyQuoteInfo(`${variableName}.worstCaseQuoteInfo`, buyQuote.worstCaseQuoteInfo); assert.isValidBuyQuoteInfo(`${variableName}.worstCaseQuoteInfo`, buyQuote.worstCaseQuoteInfo);
sharedAssert.isBigNumber(`${variableName}.assetBuyAmount`, buyQuote.assetBuyAmount); sharedAssert.isBigNumber(`${variableName}.makerAssetBuyAmount`, buyQuote.makerAssetBuyAmount);
if (buyQuote.feePercentage !== undefined) { assert.isETHAddressHex(`${variableName}.toAddress`, buyQuote.toAddress);
sharedAssert.isNumber(`${variableName}.feePercentage`, buyQuote.feePercentage); assert.isBoolean(`${variableName}.isUsingCoordinator`, buyQuote.isUsingCoordinator);
} // TODO(dave4506) Remove once forwarder features are reimplemented
// if (buyQuote.feePercentage !== undefined) {
// sharedAssert.isNumber(`${variableName}.feePercentage`, buyQuote.feePercentage);
// }
}, },
isValidBuyQuoteInfo(variableName: string, buyQuoteInfo: BuyQuoteInfo): void { isValidBuyQuoteInfo(variableName: string, buyQuoteInfo: BuyQuoteInfo): void {
sharedAssert.isBigNumber(`${variableName}.assetEthAmount`, buyQuoteInfo.assetEthAmount); sharedAssert.isBigNumber(`${variableName}.takerTokenAmount`, buyQuoteInfo.takerTokenAmount);
sharedAssert.isBigNumber(`${variableName}.feeEthAmount`, buyQuoteInfo.feeEthAmount); sharedAssert.isBigNumber(`${variableName}.feeTakerTokenAmount`, buyQuoteInfo.feeTakerTokenAmount);
sharedAssert.isBigNumber(`${variableName}.totalEthAmount`, buyQuoteInfo.totalEthAmount); sharedAssert.isBigNumber(`${variableName}.totalTakerTokenAmount`, buyQuoteInfo.totalTakerTokenAmount);
}, },
isValidOrderProvider(variableName: string, orderFetcher: OrderProvider): void { isValidOrderProvider(variableName: string, orderFetcher: OrderProvider): void {
sharedAssert.isFunction(`${variableName}.getOrdersAsync`, orderFetcher.getOrdersAsync); sharedAssert.isFunction(`${variableName}.getOrdersAsync`, orderFetcher.getOrdersAsync);

View File

@@ -11,8 +11,7 @@ export const buyQuoteCalculator = {
calculate( calculate(
ordersAndFillableAmounts: OrdersAndFillableAmounts, ordersAndFillableAmounts: OrdersAndFillableAmounts,
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts, feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
assetBuyAmount: BigNumber, makerAssetBuyAmount: BigNumber,
feePercentage: number,
slippagePercentage: number, slippagePercentage: number,
isMakerAssetZrxToken: boolean, isMakerAssetZrxToken: boolean,
): BuyQuote { ): BuyQuote {
@@ -20,20 +19,20 @@ export const buyQuoteCalculator = {
const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts; const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts;
const feeOrders = feeOrdersAndFillableAmounts.orders; const feeOrders = feeOrdersAndFillableAmounts.orders;
const remainingFillableFeeAmounts = feeOrdersAndFillableAmounts.remainingFillableMakerAssetAmounts; const remainingFillableFeeAmounts = feeOrdersAndFillableAmounts.remainingFillableMakerAssetAmounts;
const slippageBufferAmount = assetBuyAmount.multipliedBy(slippagePercentage).integerValue(); const slippageBufferAmount = makerAssetBuyAmount.multipliedBy(slippagePercentage).integerValue();
// find the orders that cover the desired assetBuyAmount (with slippage) // find the orders that cover the desired assetBuyAmount (with slippage)
const { const {
resultOrders, resultOrders,
remainingFillAmount, remainingFillAmount,
ordersRemainingFillableMakerAssetAmounts, ordersRemainingFillableMakerAssetAmounts,
} = marketUtils.findOrdersThatCoverMakerAssetFillAmount(orders, assetBuyAmount, { } = marketUtils.findOrdersThatCoverMakerAssetFillAmount(orders, makerAssetBuyAmount, {
remainingFillableMakerAssetAmounts, remainingFillableMakerAssetAmounts,
slippageBufferAmount, slippageBufferAmount,
}); });
// if we do not have enough orders to cover the desired assetBuyAmount, throw // if we do not have enough orders to cover the desired assetBuyAmount, throw
if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) { if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) {
// We needed the amount they requested to buy, plus the amount for slippage // We needed the amount they requested to buy, plus the amount for slippage
const totalAmountRequested = assetBuyAmount.plus(slippageBufferAmount); const totalAmountRequested = makerAssetBuyAmount.plus(slippageBufferAmount);
const amountAbleToFill = totalAmountRequested.minus(remainingFillAmount); const amountAbleToFill = totalAmountRequested.minus(remainingFillAmount);
// multiplierNeededWithSlippage represents what we need to multiply the assetBuyAmount by // multiplierNeededWithSlippage represents what we need to multiply the assetBuyAmount by
// in order to get the total amount needed considering slippage // in order to get the total amount needed considering slippage
@@ -73,7 +72,9 @@ export const buyQuoteCalculator = {
} }
// assetData information for the result // assetData information for the result
const assetData = orders[0].makerAssetData; const takerAssetData = orders[0].takerAssetData;
const makerAssetData = orders[0].makerAssetData;
// compile the resulting trimmed set of orders for makerAsset and feeOrders that are needed for assetBuyAmount // compile the resulting trimmed set of orders for makerAsset and feeOrders that are needed for assetBuyAmount
const trimmedOrdersAndFillableAmounts: OrdersAndFillableAmounts = { const trimmedOrdersAndFillableAmounts: OrdersAndFillableAmounts = {
orders: resultOrders, orders: resultOrders,
@@ -86,26 +87,28 @@ export const buyQuoteCalculator = {
const bestCaseQuoteInfo = calculateQuoteInfo( const bestCaseQuoteInfo = calculateQuoteInfo(
trimmedOrdersAndFillableAmounts, trimmedOrdersAndFillableAmounts,
trimmedFeeOrdersAndFillableAmounts, trimmedFeeOrdersAndFillableAmounts,
assetBuyAmount, makerAssetBuyAmount,
feePercentage,
isMakerAssetZrxToken, isMakerAssetZrxToken,
); );
// in order to calculate the maxRate, reverse the ordersAndFillableAmounts such that they are sorted from worst rate to best rate // in order to calculate the maxRate, reverse the ordersAndFillableAmounts such that they are sorted from worst rate to best rate
const worstCaseQuoteInfo = calculateQuoteInfo( const worstCaseQuoteInfo = calculateQuoteInfo(
reverseOrdersAndFillableAmounts(trimmedOrdersAndFillableAmounts), reverseOrdersAndFillableAmounts(trimmedOrdersAndFillableAmounts),
reverseOrdersAndFillableAmounts(trimmedFeeOrdersAndFillableAmounts), reverseOrdersAndFillableAmounts(trimmedFeeOrdersAndFillableAmounts),
assetBuyAmount, makerAssetBuyAmount,
feePercentage,
isMakerAssetZrxToken, isMakerAssetZrxToken,
); );
return { return {
assetData, takerAssetData,
makerAssetData,
makerAssetBuyAmount,
orders: resultOrders, orders: resultOrders,
feeOrders: resultFeeOrders, feeOrders: resultFeeOrders,
bestCaseQuoteInfo, bestCaseQuoteInfo,
worstCaseQuoteInfo, worstCaseQuoteInfo,
assetBuyAmount, // TODO(dave4506): coordinator metadata for buy quote
feePercentage, toAddress: constants.NULL_ADDRESS,
isUsingCoordinator: false,
}; };
}, },
}; };
@@ -113,33 +116,29 @@ export const buyQuoteCalculator = {
function calculateQuoteInfo( function calculateQuoteInfo(
ordersAndFillableAmounts: OrdersAndFillableAmounts, ordersAndFillableAmounts: OrdersAndFillableAmounts,
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts, feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
assetBuyAmount: BigNumber, makserAssetBuyAmount: BigNumber,
feePercentage: number,
isMakerAssetZrxToken: boolean, isMakerAssetZrxToken: boolean,
): BuyQuoteInfo { ): BuyQuoteInfo {
// find the total eth and zrx needed to buy assetAmount from the resultOrders from left to right // find the total eth and zrx needed to buy assetAmount from the resultOrders from left to right
let assetEthAmount = constants.ZERO_AMOUNT; let takerTokenAmount = constants.ZERO_AMOUNT;
let zrxEthAmount = constants.ZERO_AMOUNT; let zrxTakerTokenAmount = constants.ZERO_AMOUNT;
if (isMakerAssetZrxToken) { if (isMakerAssetZrxToken) {
assetEthAmount = findEthAmountNeededToBuyZrx(ordersAndFillableAmounts, assetBuyAmount); takerTokenAmount = findTakerTokenAmountNeededToBuyZrx(ordersAndFillableAmounts, makserAssetBuyAmount);
} else { } else {
// find eth and zrx amounts needed to buy // find eth and zrx amounts needed to buy
const ethAndZrxAmountToBuyAsset = findEthAndZrxAmountNeededToBuyAsset(ordersAndFillableAmounts, assetBuyAmount); const takerTokenAndZrxAmountToBuyAsset = findTakerTokenAndZrxAmountNeededToBuyAsset(ordersAndFillableAmounts, makserAssetBuyAmount);
assetEthAmount = ethAndZrxAmountToBuyAsset[0]; takerTokenAmount = takerTokenAndZrxAmountToBuyAsset[0];
const zrxAmountToBuyAsset = ethAndZrxAmountToBuyAsset[1]; const zrxAmountToBuyAsset = takerTokenAndZrxAmountToBuyAsset[1];
// find eth amount needed to buy zrx // find eth amount needed to buy zrx
zrxEthAmount = findEthAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset); zrxTakerTokenAmount = findTakerTokenAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset);
} }
// eth amount needed to buy the affiliate fee const feeTakerTokenAmount = zrxTakerTokenAmount;
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 // 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); const totalTakerTokenAmount = takerTokenAmount.plus(feeTakerTokenAmount);
return { return {
assetEthAmount, takerTokenAmount,
feeEthAmount, feeTakerTokenAmount,
totalEthAmount, totalTakerTokenAmount,
}; };
} }
@@ -153,7 +152,7 @@ function reverseOrdersAndFillableAmounts(ordersAndFillableAmounts: OrdersAndFill
}; };
} }
function findEthAmountNeededToBuyZrx( function findTakerTokenAmountNeededToBuyZrx(
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts, feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
zrxBuyAmount: BigNumber, zrxBuyAmount: BigNumber,
): BigNumber { ): BigNumber {
@@ -161,18 +160,19 @@ function findEthAmountNeededToBuyZrx(
const result = _.reduce( const result = _.reduce(
orders, orders,
(acc, order, index) => { (acc, order, index) => {
const { totalEthAmount, remainingZrxBuyAmount } = acc; const { totalTakerTokenAmount, remainingZrxBuyAmount } = acc;
const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index]; const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index];
const makerFillAmount = BigNumber.min(remainingZrxBuyAmount, remainingFillableMakerAssetAmount); const makerFillAmount = BigNumber.min(remainingZrxBuyAmount, remainingFillableMakerAssetAmount);
const [takerFillAmount, adjustedMakerFillAmount] = orderCalculationUtils.getTakerFillAmountForFeeOrder( const [takerFillAmount, adjustedMakerFillAmount] = orderCalculationUtils.getTakerFillAmountForFeeOrder(
order, order,
makerFillAmount, makerFillAmount,
); );
// TODO(dave4506) may remove if this is for affiliate fees (asset-buyer2.0)
const extraFeeAmount = remainingFillableMakerAssetAmount.isGreaterThanOrEqualTo(adjustedMakerFillAmount) const extraFeeAmount = remainingFillableMakerAssetAmount.isGreaterThanOrEqualTo(adjustedMakerFillAmount)
? constants.ZERO_AMOUNT ? constants.ZERO_AMOUNT
: adjustedMakerFillAmount.minus(makerFillAmount); : adjustedMakerFillAmount.minus(makerFillAmount);
return { return {
totalEthAmount: totalEthAmount.plus(takerFillAmount), totalTakerTokenAmount: totalTakerTokenAmount.plus(takerFillAmount),
remainingZrxBuyAmount: BigNumber.max( remainingZrxBuyAmount: BigNumber.max(
constants.ZERO_AMOUNT, constants.ZERO_AMOUNT,
remainingZrxBuyAmount.minus(makerFillAmount).plus(extraFeeAmount), remainingZrxBuyAmount.minus(makerFillAmount).plus(extraFeeAmount),
@@ -180,40 +180,40 @@ function findEthAmountNeededToBuyZrx(
}; };
}, },
{ {
totalEthAmount: constants.ZERO_AMOUNT, totalTakerTokenAmount: constants.ZERO_AMOUNT,
remainingZrxBuyAmount: zrxBuyAmount, remainingZrxBuyAmount: zrxBuyAmount,
}, },
); );
return result.totalEthAmount; return result.totalTakerTokenAmount;
} }
function findEthAndZrxAmountNeededToBuyAsset( function findTakerTokenAndZrxAmountNeededToBuyAsset(
ordersAndFillableAmounts: OrdersAndFillableAmounts, ordersAndFillableAmounts: OrdersAndFillableAmounts,
assetBuyAmount: BigNumber, makerAssetBuyAmount: BigNumber,
): [BigNumber, BigNumber] { ): [BigNumber, BigNumber] {
const { orders, remainingFillableMakerAssetAmounts } = ordersAndFillableAmounts; const { orders, remainingFillableMakerAssetAmounts } = ordersAndFillableAmounts;
const result = _.reduce( const result = _.reduce(
orders, orders,
(acc, order, index) => { (acc, order, index) => {
const { totalEthAmount, totalZrxAmount, remainingAssetBuyAmount } = acc; const { totalTakerTokenAmount, totalZrxAmount, remainingMakerAssetBuyAmount } = acc;
const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index]; const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index];
const makerFillAmount = BigNumber.min(acc.remainingAssetBuyAmount, remainingFillableMakerAssetAmount); const makerFillAmount = BigNumber.min(acc.remainingMakerAssetBuyAmount, remainingFillableMakerAssetAmount);
const takerFillAmount = orderCalculationUtils.getTakerFillAmount(order, makerFillAmount); const takerFillAmount = orderCalculationUtils.getTakerFillAmount(order, makerFillAmount);
const takerFeeAmount = orderCalculationUtils.getTakerFeeAmount(order, takerFillAmount); const takerFeeAmount = orderCalculationUtils.getTakerFeeAmount(order, takerFillAmount);
return { return {
totalEthAmount: totalEthAmount.plus(takerFillAmount), totalTakerTokenAmount: totalTakerTokenAmount.plus(takerFillAmount),
totalZrxAmount: totalZrxAmount.plus(takerFeeAmount), totalZrxAmount: totalZrxAmount.plus(takerFeeAmount),
remainingAssetBuyAmount: BigNumber.max( remainingMakerAssetBuyAmount: BigNumber.max(
constants.ZERO_AMOUNT, constants.ZERO_AMOUNT,
remainingAssetBuyAmount.minus(makerFillAmount), remainingMakerAssetBuyAmount.minus(makerFillAmount),
), ),
}; };
}, },
{ {
totalEthAmount: constants.ZERO_AMOUNT, totalTakerTokenAmount: constants.ZERO_AMOUNT,
totalZrxAmount: constants.ZERO_AMOUNT, totalZrxAmount: constants.ZERO_AMOUNT,
remainingAssetBuyAmount: assetBuyAmount, remainingMakerAssetBuyAmount: makerAssetBuyAmount,
}, },
); );
return [result.totalEthAmount, result.totalZrxAmount]; return [result.totalTakerTokenAmount, result.totalZrxAmount];
} }

View File

@@ -12,25 +12,25 @@ export const calculateLiquidity = (ordersAndFillableAmounts: OrdersAndFillableAm
throw new Error(`No corresponding fillableMakerAssetAmounts at index ${curIndex}`); throw new Error(`No corresponding fillableMakerAssetAmounts at index ${curIndex}`);
} }
const tokensAvailableForCurrentOrder = availableMakerAssetAmount; const makerTokensAvailableForCurrentOrder = availableMakerAssetAmount;
const ethValueAvailableForCurrentOrder = orderCalculationUtils.getTakerFillAmount( const takerTokensAvailableForCurrentOrder = orderCalculationUtils.getTakerFillAmount(
order, order,
availableMakerAssetAmount, makerTokensAvailableForCurrentOrder,
); );
return { return {
tokensAvailableInBaseUnits: acc.tokensAvailableInBaseUnits.plus(tokensAvailableForCurrentOrder), makerTokensAvailableInBaseUnits: acc.makerTokensAvailableInBaseUnits.plus(makerTokensAvailableForCurrentOrder),
ethValueAvailableInWei: acc.ethValueAvailableInWei.plus(ethValueAvailableForCurrentOrder), takerTokensAvailableInBaseUnits: acc.takerTokensAvailableInBaseUnits.plus(takerTokensAvailableForCurrentOrder),
}; };
}, },
{ {
tokensAvailableInBaseUnits: new BigNumber(0), makerTokensAvailableInBaseUnits: new BigNumber(0),
ethValueAvailableInWei: new BigNumber(0), takerTokensAvailableInBaseUnits: new BigNumber(0),
}, },
); );
// Turn into regular numbers // Turn into regular numbers
return { return {
tokensAvailableInBaseUnits: liquidityInBigNumbers.tokensAvailableInBaseUnits, makerTokensAvailableInBaseUnits: liquidityInBigNumbers.makerTokensAvailableInBaseUnits,
ethValueAvailableInWei: liquidityInBigNumbers.ethValueAvailableInWei, takerTokensAvailableInBaseUnits: liquidityInBigNumbers.takerTokensAvailableInBaseUnits,
}; };
}; };

View File

@@ -16,6 +16,10 @@ class OrderProviderClass implements OrderProvider {
public async getAvailableMakerAssetDatasAsync(takerAssetData: string): Promise<string[]> { public async getAvailableMakerAssetDatasAsync(takerAssetData: string): Promise<string[]> {
return Promise.resolve([]); return Promise.resolve([]);
} }
// tslint:disable-next-line:prefer-function-over-method
public async getAvailableTakerAssetDatasAsync(takerAssetData: string): Promise<string[]> {
return Promise.resolve([]);
}
} }
export const orderProviderMock = () => { export const orderProviderMock = () => {