refactored ERC <> ERC for asset-buyer
minor update to interface
This commit is contained in:
@@ -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": {
|
||||||
|
@@ -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.
|
||||||
|
@@ -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,
|
||||||
|
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
|
@@ -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];
|
||||||
}
|
}
|
||||||
|
@@ -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,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -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 = () => {
|
||||||
|
Reference in New Issue
Block a user