renamed asset-buyer + adding consumers
This commit is contained in:
parent
64e3b6f5ee
commit
549bfe98f1
@ -11,29 +11,28 @@ import { constants } from './constants';
|
|||||||
import { BasicOrderProvider } from './order_providers/basic_order_provider';
|
import { BasicOrderProvider } from './order_providers/basic_order_provider';
|
||||||
import { StandardRelayerAPIOrderProvider } from './order_providers/standard_relayer_api_order_provider';
|
import { StandardRelayerAPIOrderProvider } from './order_providers/standard_relayer_api_order_provider';
|
||||||
import {
|
import {
|
||||||
AssetBuyerError,
|
AssetSwapQuoterError,
|
||||||
AssetBuyerOpts,
|
AssetSwapQuoterOpts,
|
||||||
BuyQuote,
|
|
||||||
BuyQuoteExecutionOpts,
|
|
||||||
BuyQuoteRequestOpts,
|
|
||||||
LiquidityForAssetData,
|
LiquidityForAssetData,
|
||||||
LiquidityRequestOpts,
|
LiquidityRequestOpts,
|
||||||
OrderProvider,
|
OrderProvider,
|
||||||
OrdersAndFillableAmounts,
|
OrdersAndFillableAmounts,
|
||||||
|
SwapQuote,
|
||||||
|
SwapQuoteRequestOpts,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
import { assert } from './utils/assert';
|
import { assert } from './utils/assert';
|
||||||
import { assetDataUtils } from './utils/asset_data_utils';
|
import { assetDataUtils } from './utils/asset_data_utils';
|
||||||
import { buyQuoteCalculator } from './utils/buy_quote_calculator';
|
|
||||||
import { calculateLiquidity } from './utils/calculate_liquidity';
|
import { calculateLiquidity } from './utils/calculate_liquidity';
|
||||||
import { orderProviderResponseProcessor } from './utils/order_provider_response_processor';
|
import { orderProviderResponseProcessor } from './utils/order_provider_response_processor';
|
||||||
|
import { swapQuoteCalculator } from './utils/swap_quote_calculator';
|
||||||
|
|
||||||
interface OrdersEntry {
|
interface OrdersEntry {
|
||||||
ordersAndFillableAmounts: OrdersAndFillableAmounts;
|
ordersAndFillableAmounts: OrdersAndFillableAmounts;
|
||||||
lastRefreshTime: number;
|
lastRefreshTime: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AssetBuyer {
|
export class AssetSwapQuoter {
|
||||||
public readonly provider: ZeroExProvider;
|
public readonly provider: ZeroExProvider;
|
||||||
public readonly orderProvider: OrderProvider;
|
public readonly orderProvider: OrderProvider;
|
||||||
public readonly networkId: number;
|
public readonly networkId: number;
|
||||||
@ -44,7 +43,7 @@ export class AssetBuyer {
|
|||||||
private readonly _ordersEntryMap: ObjectMap<OrdersEntry> = {};
|
private readonly _ordersEntryMap: ObjectMap<OrdersEntry> = {};
|
||||||
/**
|
/**
|
||||||
* Instantiates a new AssetBuyer instance given existing liquidity in the form of orders and feeOrders.
|
* Instantiates a new AssetBuyer instance given existing liquidity in the form of orders and feeOrders.
|
||||||
* @param supportedProvider The Provider instance you would like to use for interacting with the Ethereum network.
|
* @param supportedProvider The Provider instance you would like to use for ikknteracting with the Ethereum network.
|
||||||
* @param orders A non-empty array of objects that conform to SignedOrder. All orders must have the same makerAssetData and takerAssetData (WETH).
|
* @param orders A non-empty array of objects that conform to SignedOrder. All orders must have the same makerAssetData and takerAssetData (WETH).
|
||||||
* @param feeOrders A array of objects that conform to SignedOrder. All orders must have the same makerAssetData (ZRX) and takerAssetData (WETH). Defaults to an empty array.
|
* @param feeOrders A array of objects that conform to SignedOrder. All orders must have the same makerAssetData (ZRX) and takerAssetData (WETH). Defaults to an empty array.
|
||||||
* @param options Initialization options for the AssetBuyer. See type definition for details.
|
* @param options Initialization options for the AssetBuyer. See type definition for details.
|
||||||
@ -54,12 +53,12 @@ export class AssetBuyer {
|
|||||||
public static getAssetBuyerForProvidedOrders(
|
public static getAssetBuyerForProvidedOrders(
|
||||||
supportedProvider: SupportedProvider,
|
supportedProvider: SupportedProvider,
|
||||||
orders: SignedOrder[],
|
orders: SignedOrder[],
|
||||||
options: Partial<AssetBuyerOpts> = {},
|
options: Partial<AssetSwapQuoterOpts> = {},
|
||||||
): AssetBuyer {
|
): AssetSwapQuoter {
|
||||||
assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema);
|
assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema);
|
||||||
assert.assert(orders.length !== 0, `Expected orders to contain at least one order`);
|
assert.assert(orders.length !== 0, `Expected orders to contain at least one order`);
|
||||||
const orderProvider = new BasicOrderProvider(orders);
|
const orderProvider = new BasicOrderProvider(orders);
|
||||||
const assetBuyer = new AssetBuyer(supportedProvider, orderProvider, options);
|
const assetBuyer = new AssetSwapQuoter(supportedProvider, orderProvider, options);
|
||||||
return assetBuyer;
|
return assetBuyer;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -73,13 +72,13 @@ export class AssetBuyer {
|
|||||||
public static getAssetBuyerForStandardRelayerAPIUrl(
|
public static getAssetBuyerForStandardRelayerAPIUrl(
|
||||||
supportedProvider: SupportedProvider,
|
supportedProvider: SupportedProvider,
|
||||||
sraApiUrl: string,
|
sraApiUrl: string,
|
||||||
options: Partial<AssetBuyerOpts> = {},
|
options: Partial<AssetSwapQuoterOpts> = {},
|
||||||
): AssetBuyer {
|
): AssetSwapQuoter {
|
||||||
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||||
assert.isWebUri('sraApiUrl', sraApiUrl);
|
assert.isWebUri('sraApiUrl', sraApiUrl);
|
||||||
const networkId = options.networkId || constants.DEFAULT_ASSET_BUYER_OPTS.networkId;
|
const networkId = options.networkId || constants.DEFAULT_ASSET_SWAP_QUOTER_OPTS.networkId;
|
||||||
const orderProvider = new StandardRelayerAPIOrderProvider(sraApiUrl, networkId);
|
const orderProvider = new StandardRelayerAPIOrderProvider(sraApiUrl, networkId);
|
||||||
const assetBuyer = new AssetBuyer(provider, orderProvider, options);
|
const assetBuyer = new AssetSwapQuoter(provider, orderProvider, options);
|
||||||
return assetBuyer;
|
return assetBuyer;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -93,11 +92,11 @@ export class AssetBuyer {
|
|||||||
constructor(
|
constructor(
|
||||||
supportedProvider: SupportedProvider,
|
supportedProvider: SupportedProvider,
|
||||||
orderProvider: OrderProvider,
|
orderProvider: OrderProvider,
|
||||||
options: Partial<AssetBuyerOpts> = {},
|
options: Partial<AssetSwapQuoter> = {},
|
||||||
) {
|
) {
|
||||||
const { networkId, orderRefreshIntervalMs, expiryBufferSeconds } = _.merge(
|
const { networkId, orderRefreshIntervalMs, expiryBufferSeconds } = _.merge(
|
||||||
{},
|
{},
|
||||||
constants.DEFAULT_ASSET_BUYER_OPTS,
|
constants.DEFAULT_ASSET_SWAP_QUOTER_OPTS,
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||||
@ -115,23 +114,23 @@ export class AssetBuyer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Get a `BuyQuote` containing all information relevant to fulfilling a buy given a desired assetData.
|
* Get a `SwapQuote` containing all information relevant to fulfilling a buy given a desired assetData.
|
||||||
* You can then pass the `BuyQuote` to `executeBuyQuoteAsync` to execute the buy.
|
* You can then pass the `SwapQuote` to `executeSwapQuoteAsync` to execute the buy.
|
||||||
* @param assetData The assetData of the desired asset to buy (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
|
* @param assetData The assetData of the desired asset to buy (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
|
||||||
* @param assetBuyAmount The amount of asset to buy.
|
* @param assetBuyAmount The amount of asset to buy.
|
||||||
* @param options Options for the request. See type definition for more information.
|
* @param options Options for the request. See type definition for more information.
|
||||||
*
|
*
|
||||||
* @return An object that conforms to BuyQuote that satisfies the request. See type definition for more information.
|
* @return An object that conforms to SwapQuote that satisfies the request. See type definition for more information.
|
||||||
*/
|
*/
|
||||||
public async getBuyQuoteAsync(
|
public async getSwapQuoteAsync(
|
||||||
makerAssetData: string,
|
makerAssetData: string,
|
||||||
takerAssetData: string,
|
takerAssetData: string,
|
||||||
makerAssetBuyAmount: BigNumber,
|
makerAssetBuyAmount: BigNumber,
|
||||||
options: Partial<BuyQuoteRequestOpts> = {},
|
options: Partial<SwapQuoteRequestOpts> = {},
|
||||||
): Promise<BuyQuote> {
|
): Promise<SwapQuote> {
|
||||||
const { shouldForceOrderRefresh, slippagePercentage } = _.merge(
|
const { shouldForceOrderRefresh, slippagePercentage } = _.merge(
|
||||||
{},
|
{},
|
||||||
constants.DEFAULT_BUY_QUOTE_REQUEST_OPTS,
|
constants.DEFAULT_SWAP_QUOTE_REQUEST_OPTS,
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
assert.isString('makerAssetData', makerAssetData);
|
assert.isString('makerAssetData', makerAssetData);
|
||||||
@ -151,39 +150,39 @@ export class AssetBuyer {
|
|||||||
shouldForceOrderRefresh,
|
shouldForceOrderRefresh,
|
||||||
]);
|
]);
|
||||||
if (ordersAndFillableAmounts.orders.length === 0) {
|
if (ordersAndFillableAmounts.orders.length === 0) {
|
||||||
throw new Error(`${AssetBuyerError.AssetUnavailable}: For makerAssetdata ${makerAssetData} and takerAssetdata ${takerAssetData}`);
|
throw new Error(`${AssetSwapQuoterError.AssetUnavailable}: For makerAssetdata ${makerAssetData} and takerAssetdata ${takerAssetData}`);
|
||||||
}
|
}
|
||||||
const buyQuote = buyQuoteCalculator.calculate(
|
const swapQuote = swapQuoteCalculator.calculate(
|
||||||
ordersAndFillableAmounts,
|
ordersAndFillableAmounts,
|
||||||
feeOrdersAndFillableAmounts,
|
feeOrdersAndFillableAmounts,
|
||||||
makerAssetBuyAmount,
|
makerAssetBuyAmount,
|
||||||
slippagePercentage,
|
slippagePercentage,
|
||||||
isMakerAssetZrxToken,
|
isMakerAssetZrxToken,
|
||||||
);
|
);
|
||||||
return buyQuote;
|
return swapQuote;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Get a `BuyQuote` containing all information relevant to fulfilling a buy given a desired ERC20 token address.
|
* Get a `SwapQuote` containing all information relevant to fulfilling a buy given a desired ERC20 token address.
|
||||||
* You can then pass the `BuyQuote` to `executeBuyQuoteAsync` to execute the buy.
|
* You can then pass the `SwapQuote` to `executeSwapQuoteAsync` to execute the buy.
|
||||||
* @param tokenAddress The ERC20 token address.
|
* @param tokenAddress The ERC20 token address.
|
||||||
* @param assetBuyAmount The amount of asset to buy.
|
* @param assetBuyAmount The amount of asset to buy.
|
||||||
* @param options Options for the request. See type definition for more information.
|
* @param options Options for the request. See type definition for more information.
|
||||||
*
|
*
|
||||||
* @return An object that conforms to BuyQuote that satisfies the request. See type definition for more information.
|
* @return An object that conforms to SwapQuote that satisfies the request. See type definition for more information.
|
||||||
*/
|
*/
|
||||||
public async getBuyQuoteForERC20TokenAddressAsync(
|
public async getSwapQuoteForERC20TokenAddressAsync(
|
||||||
makerTokenAddress: string,
|
makerTokenAddress: string,
|
||||||
takerTokenAddress: string,
|
takerTokenAddress: string,
|
||||||
makerAssetBuyAmount: BigNumber,
|
makerAssetBuyAmount: BigNumber,
|
||||||
options: Partial<BuyQuoteRequestOpts> = {},
|
options: Partial<SwapQuoteRequestOpts> = {},
|
||||||
): Promise<BuyQuote> {
|
): Promise<SwapQuote> {
|
||||||
assert.isETHAddressHex('makerTokenAddress', makerTokenAddress);
|
assert.isETHAddressHex('makerTokenAddress', makerTokenAddress);
|
||||||
assert.isETHAddressHex('takerTokenAddress', takerTokenAddress);
|
assert.isETHAddressHex('takerTokenAddress', takerTokenAddress);
|
||||||
assert.isBigNumber('makerAssetBuyAmount', makerAssetBuyAmount);
|
assert.isBigNumber('makerAssetBuyAmount', makerAssetBuyAmount);
|
||||||
const makerAssetData = assetDataUtils.encodeERC20AssetData(makerTokenAddress);
|
const makerAssetData = assetDataUtils.encodeERC20AssetData(makerTokenAddress);
|
||||||
const takerAssetData = assetDataUtils.encodeERC20AssetData(takerTokenAddress);
|
const takerAssetData = assetDataUtils.encodeERC20AssetData(takerTokenAddress);
|
||||||
const buyQuote = this.getBuyQuoteAsync(makerAssetData, takerAssetData, makerAssetBuyAmount, options);
|
const swapQuote = this.getSwapQuoteAsync(makerAssetData, takerAssetData, makerAssetBuyAmount, options);
|
||||||
return buyQuote;
|
return swapQuote;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Returns information about available liquidity for an asset
|
* Returns information about available liquidity for an asset
|
||||||
@ -224,13 +223,13 @@ export class AssetBuyer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * Given a BuyQuote and desired rate, attempt to execute the buy.
|
// * Given a SwapQuote and desired rate, attempt to execute the buy.
|
||||||
// * @param buyQuote An object that conforms to BuyQuote. See type definition for more information.
|
// * @param SwapQuote An object that conforms to SwapQuote. See type definition for more information.
|
||||||
// * @param options Options for the execution of the BuyQuote. See type definition for more information.
|
// * @param options Options for the execution of the SwapQuote. See type definition for more information.
|
||||||
// *
|
// *
|
||||||
// * @return A promise of the txHash.
|
// * @return A promise of the txHash.
|
||||||
// */
|
// */
|
||||||
// public async executeBuyQuoteAsync(
|
// public async executeSwapQuoteAsync(
|
||||||
// buyQuote: BuyQuote,
|
// buyQuote: BuyQuote,
|
||||||
// options: Partial<BuyQuoteExecutionOpts> = {},
|
// options: Partial<BuyQuoteExecutionOpts> = {},
|
||||||
// ): Promise<string> {
|
// ): Promise<string> {
|
||||||
@ -401,13 +400,6 @@ export class AssetBuyer {
|
|||||||
return `${makerAssetData}_${takerAssetData}`;
|
return `${makerAssetData}_${takerAssetData}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the assetData that represents the WETH token.
|
|
||||||
* Will throw if WETH does not exist for the current network.
|
|
||||||
*/
|
|
||||||
// private _getEtherTokenAssetDataOrThrow(): string {
|
|
||||||
// return assetDataUtils.getEtherTokenAssetData(this._contractWrappers);
|
|
||||||
// }
|
|
||||||
/**
|
/**
|
||||||
* Get the assetData that represents the ZRX token.
|
* 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.
|
||||||
|
@ -1,26 +1,21 @@
|
|||||||
import { SignedOrder } from '@0x/types';
|
import { SignedOrder } from '@0x/types';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
import { AssetBuyerOpts, BuyQuoteExecutionOpts, BuyQuoteRequestOpts, OrdersAndFillableAmounts } from './types';
|
import { AssetSwapQuoterOpts, ForwarderSwapQuoteExecutionOpts, OrdersAndFillableAmounts, SwapQuoteRequestOpts, SwapQuoteExecutionOpts } from './types';
|
||||||
|
|
||||||
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
|
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||||
const MAINNET_NETWORK_ID = 1;
|
const MAINNET_NETWORK_ID = 1;
|
||||||
|
|
||||||
const DEFAULT_ASSET_BUYER_OPTS: AssetBuyerOpts = {
|
const DEFAULT_ASSET_SWAP_QUOTER_OPTS: AssetSwapQuoterOpts = {
|
||||||
networkId: MAINNET_NETWORK_ID,
|
networkId: MAINNET_NETWORK_ID,
|
||||||
orderRefreshIntervalMs: 10000, // 10 seconds
|
orderRefreshIntervalMs: 10000, // 10 seconds
|
||||||
expiryBufferSeconds: 120, // 2 minutes
|
expiryBufferSeconds: 120, // 2 minutes
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_BUY_QUOTE_REQUEST_OPTS: BuyQuoteRequestOpts = {
|
const DEFAULT_SWAP_QUOTE_REQUEST_OPTS: SwapQuoteRequestOpts = {
|
||||||
feePercentage: 0,
|
|
||||||
shouldForceOrderRefresh: false,
|
shouldForceOrderRefresh: false,
|
||||||
slippagePercentage: 0.2, // 20% slippage protection
|
slippagePercentage: 0.2, // 20% slippage protection,
|
||||||
};
|
allowMarketBuyOrders: true,
|
||||||
|
|
||||||
// Other default values are dynamically determined
|
|
||||||
const DEFAULT_BUY_QUOTE_EXECUTION_OPTS: BuyQuoteExecutionOpts = {
|
|
||||||
feeRecipient: NULL_ADDRESS,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const EMPTY_ORDERS_AND_FILLABLE_AMOUNTS: OrdersAndFillableAmounts = {
|
const EMPTY_ORDERS_AND_FILLABLE_AMOUNTS: OrdersAndFillableAmounts = {
|
||||||
@ -33,8 +28,8 @@ export const constants = {
|
|||||||
NULL_ADDRESS,
|
NULL_ADDRESS,
|
||||||
MAINNET_NETWORK_ID,
|
MAINNET_NETWORK_ID,
|
||||||
ETHER_TOKEN_DECIMALS: 18,
|
ETHER_TOKEN_DECIMALS: 18,
|
||||||
DEFAULT_ASSET_BUYER_OPTS,
|
ONE_AMOUNT: new BigNumber(1),
|
||||||
DEFAULT_BUY_QUOTE_EXECUTION_OPTS,
|
DEFAULT_ASSET_SWAP_QUOTER_OPTS,
|
||||||
DEFAULT_BUY_QUOTE_REQUEST_OPTS,
|
DEFAULT_SWAP_QUOTE_REQUEST_OPTS,
|
||||||
EMPTY_ORDERS_AND_FILLABLE_AMOUNTS,
|
EMPTY_ORDERS_AND_FILLABLE_AMOUNTS,
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
import { AssetBuyerError } from './types';
|
import { AssetSwapQuoterError } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error class representing insufficient asset liquidity
|
* Error class representing insufficient asset liquidity
|
||||||
@ -14,7 +14,7 @@ export class InsufficientAssetLiquidityError extends Error {
|
|||||||
* @param amountAvailableToFill The amount availabe to fill (in base units) factoring in slippage
|
* @param amountAvailableToFill The amount availabe to fill (in base units) factoring in slippage
|
||||||
*/
|
*/
|
||||||
constructor(amountAvailableToFill: BigNumber) {
|
constructor(amountAvailableToFill: BigNumber) {
|
||||||
super(AssetBuyerError.InsufficientAssetLiquidity);
|
super(AssetSwapQuoterError.InsufficientAssetLiquidity);
|
||||||
this.amountAvailableToFill = amountAvailableToFill;
|
this.amountAvailableToFill = amountAvailableToFill;
|
||||||
// Setting prototype so instanceof works. See https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
// Setting prototype so instanceof works. See https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
||||||
Object.setPrototypeOf(this, InsufficientAssetLiquidityError.prototype);
|
Object.setPrototypeOf(this, InsufficientAssetLiquidityError.prototype);
|
||||||
|
@ -18,19 +18,19 @@ export {
|
|||||||
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 { AssetSwapQuoter } 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,
|
AssetSwapQuoterError,
|
||||||
AssetBuyerOpts,
|
AssetSwapQuoterOpts,
|
||||||
BuyQuote,
|
SwapQuote,
|
||||||
BuyQuoteExecutionOpts,
|
SwapQuoteExecutionOpts,
|
||||||
BuyQuoteInfo,
|
SwapQuoteInfo,
|
||||||
BuyQuoteRequestOpts,
|
SwapQuoteRequestOpts,
|
||||||
LiquidityForAssetData,
|
LiquidityForAssetData,
|
||||||
LiquidityRequestOpts,
|
LiquidityRequestOpts,
|
||||||
OrdersAndFillableAmounts,
|
OrdersAndFillableAmounts,
|
||||||
|
@ -5,7 +5,7 @@ import { BigNumber } from '@0x/utils';
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AssetBuyerError,
|
AssetSwapQuoterError,
|
||||||
OrderProvider,
|
OrderProvider,
|
||||||
OrderProviderRequest,
|
OrderProviderRequest,
|
||||||
OrderProviderResponse,
|
OrderProviderResponse,
|
||||||
@ -73,7 +73,7 @@ export class StandardRelayerAPIOrderProvider implements OrderProvider {
|
|||||||
try {
|
try {
|
||||||
orderbook = await this._sraClient.getOrderbookAsync(orderbookRequest, requestOpts);
|
orderbook = await this._sraClient.getOrderbookAsync(orderbookRequest, requestOpts);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(AssetBuyerError.StandardRelayerApiError);
|
throw new Error(AssetSwapQuoterError.StandardRelayerApiError);
|
||||||
}
|
}
|
||||||
const apiOrders = orderbook.asks.records;
|
const apiOrders = orderbook.asks.records;
|
||||||
const orders = StandardRelayerAPIOrderProvider._getSignedOrderWithRemainingFillableMakerAssetAmountFromApi(
|
const orders = StandardRelayerAPIOrderProvider._getSignedOrderWithRemainingFillableMakerAssetAmountFromApi(
|
||||||
@ -101,7 +101,7 @@ export class StandardRelayerAPIOrderProvider implements OrderProvider {
|
|||||||
try {
|
try {
|
||||||
response = await this._sraClient.getAssetPairsAsync(fullRequest);
|
response = await this._sraClient.getAssetPairsAsync(fullRequest);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(AssetBuyerError.StandardRelayerApiError);
|
throw new Error(AssetSwapQuoterError.StandardRelayerApiError);
|
||||||
}
|
}
|
||||||
return _.map(response.records, item => {
|
return _.map(response.records, item => {
|
||||||
if (item.assetDataA.assetData === takerAssetData) {
|
if (item.assetDataA.assetData === takerAssetData) {
|
||||||
@ -129,7 +129,7 @@ export class StandardRelayerAPIOrderProvider implements OrderProvider {
|
|||||||
try {
|
try {
|
||||||
response = await this._sraClient.getAssetPairsAsync(fullRequest);
|
response = await this._sraClient.getAssetPairsAsync(fullRequest);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(AssetBuyerError.StandardRelayerApiError);
|
throw new Error(AssetSwapQuoterError.StandardRelayerApiError);
|
||||||
}
|
}
|
||||||
return _.map(response.records, item => {
|
return _.map(response.records, item => {
|
||||||
if (item.assetDataA.assetData === makerAssetData) {
|
if (item.assetDataA.assetData === makerAssetData) {
|
||||||
|
@ -0,0 +1,172 @@
|
|||||||
|
import { ContractWrappers, ContractWrappersError, ForwarderWrapperError } from '@0x/contract-wrappers';
|
||||||
|
import { BigNumber, providerUtils } from '@0x/utils';
|
||||||
|
import { SupportedProvider, Web3Wrapper, ZeroExProvider } from '@0x/web3-wrapper';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { constants } from '../constants';
|
||||||
|
import {
|
||||||
|
AssetSwapQuoterError,
|
||||||
|
CalldataInformation,
|
||||||
|
SmartContractParams,
|
||||||
|
SwapQuote,
|
||||||
|
SwapQuoteConsumer,
|
||||||
|
SwapQuoteConsumerOpts,
|
||||||
|
SwapQuoteExecutionOpts,
|
||||||
|
SwapQuoteGetOutputOpts,
|
||||||
|
SwapQuoteInfo,
|
||||||
|
Web3TransactionParams} from '../types';
|
||||||
|
import { assert } from '../utils/assert';
|
||||||
|
|
||||||
|
export interface ForwarderSwapQuoteGetOutputOpts extends SwapQuoteGetOutputOpts {
|
||||||
|
feePercentage: number;
|
||||||
|
feeRecipient: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FORWARDER_SWAP_QUOTE_CONSUMER_OPTS = {
|
||||||
|
feePercentage: 0,
|
||||||
|
feeRecipient: constants.NULL_ADDRESS,
|
||||||
|
};
|
||||||
|
|
||||||
|
const addAffiliateFeeToSwapQuoteInfo = (quoteInfo: SwapQuoteInfo, feePercentage: number): SwapQuoteInfo => {
|
||||||
|
const newQuoteInfo = _.clone(quoteInfo);
|
||||||
|
const affiliateFeeAmount = newQuoteInfo.takerTokenAmount.multipliedBy(feePercentage).integerValue(BigNumber.ROUND_CEIL);
|
||||||
|
const newFeeAmount = newQuoteInfo.feeTakerTokenAmount.plus(affiliateFeeAmount);
|
||||||
|
newQuoteInfo.feeTakerTokenAmount = newFeeAmount;
|
||||||
|
newQuoteInfo.totalTakerTokenAmount = newFeeAmount.plus(newQuoteInfo.takerTokenAmount);
|
||||||
|
return newQuoteInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addAffiliateFeeToSwapQuote = (quote: SwapQuote, feePercentage: number): SwapQuote => {
|
||||||
|
const newQuote = _.clone(quote);
|
||||||
|
newQuote.bestCaseQuoteInfo = addAffiliateFeeToSwapQuoteInfo(newQuote.bestCaseQuoteInfo, feePercentage);
|
||||||
|
newQuote.worstCaseQuoteInfo = addAffiliateFeeToSwapQuoteInfo(newQuote.worstCaseQuoteInfo, feePercentage);
|
||||||
|
return newQuote;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumer {
|
||||||
|
|
||||||
|
public readonly provider: ZeroExProvider;
|
||||||
|
public readonly networkId: number;
|
||||||
|
|
||||||
|
private readonly _contractWrappers: ContractWrappers;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
supportedProvider: SupportedProvider,
|
||||||
|
options: Partial<SwapQuoteConsumerOpts> = {},
|
||||||
|
) {
|
||||||
|
const { networkId } = _.merge(
|
||||||
|
{},
|
||||||
|
constants.DEFAULT_ASSET_SWAP_QUOTER_OPTS,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||||
|
assert.isNumber('networkId', networkId);
|
||||||
|
this.provider = provider;
|
||||||
|
this.networkId = networkId;
|
||||||
|
this._contractWrappers = new ContractWrappers(this.provider, {
|
||||||
|
networkId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCalldataOrThrowAsync = async (quote: SwapQuote, opts: Partial<ForwarderSwapQuoteGetOutputOpts>): Promise<CalldataInformation> => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public getWeb3TransactionParamsOrThrowAsync = async (quote: SwapQuote, opts: Partial<ForwarderSwapQuoteGetOutputOpts>): Promise<Web3TransactionParams> => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSmartContractParamsOrThrowAsync = async (quote: SwapQuote, opts: Partial<ForwarderSwapQuoteGetOutputOpts>): Promise<SmartContractParams> => {
|
||||||
|
const { feeRecipient, feePercentage } = _.merge(
|
||||||
|
{},
|
||||||
|
FORWARDER_SWAP_QUOTE_CONSUMER_OPTS,
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.isValidSwapQuote('quote', quote);
|
||||||
|
assert.isNumber('feePercentage', feePercentage);
|
||||||
|
assert.isETHAddressHex('feeRecipient', feeRecipient);
|
||||||
|
|
||||||
|
const swapQuoteWithFeeAdded = addAffiliateFeeToSwapQuote(quote, feePercentage);
|
||||||
|
|
||||||
|
const { orders, feeOrders, makerAssetBuyAmount, worstCaseQuoteInfo } = swapQuoteWithFeeAdded;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
orders: [],
|
||||||
|
makerAssetFillAmount: makerAssetBuyAmount,
|
||||||
|
signatures: [],
|
||||||
|
feeOrders: [],
|
||||||
|
feeSignatures: [],
|
||||||
|
feePercentage: [],
|
||||||
|
feeRecipient: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public executeSwapQuoteOrThrowAsync = async (quote: SwapQuote, opts: Partial<SwapQuoteExecutionOpts>): Promise<string> => {
|
||||||
|
const { ethAmount, takerAddress, feeRecipient, gasLimit, gasPrice, feePercentage } = _.merge(
|
||||||
|
{},
|
||||||
|
FORWARDER_SWAP_QUOTE_CONSUMER_OPTS,
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.isValidSwapQuote('quote', quote);
|
||||||
|
|
||||||
|
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 swapQuoteWithFeeAdded = addAffiliateFeeToSwapQuote(quote, feePercentage);
|
||||||
|
|
||||||
|
const { orders, feeOrders, makerAssetBuyAmount, worstCaseQuoteInfo } = swapQuoteWithFeeAdded;
|
||||||
|
|
||||||
|
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(AssetSwapQuoterError.NoAddressAvailable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const txHash = await this._contractWrappers.forwarder.marketBuyOrdersWithEthAsync(
|
||||||
|
orders,
|
||||||
|
makerAssetBuyAmount,
|
||||||
|
finalTakerAddress,
|
||||||
|
ethAmount || worstCaseQuoteInfo.totalTakerTokenAmount,
|
||||||
|
feeOrders,
|
||||||
|
feePercentage,
|
||||||
|
feeRecipient,
|
||||||
|
{
|
||||||
|
gasLimit,
|
||||||
|
gasPrice,
|
||||||
|
shouldValidate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return txHash;
|
||||||
|
} catch (err) {
|
||||||
|
if (_.includes(err.message, ContractWrappersError.SignatureRequestDenied)) {
|
||||||
|
throw new Error(AssetSwapQuoterError.SignatureRequestDenied);
|
||||||
|
} else if (_.includes(err.message, ForwarderWrapperError.CompleteFillFailed)) {
|
||||||
|
throw new Error(AssetSwapQuoterError.TransactionValueTooLow);
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -36,25 +36,70 @@ export interface OrderProvider {
|
|||||||
getAvailableTakerAssetDatasAsync: (makerAssetData: string) => Promise<string[]>;
|
getAvailableTakerAssetDatasAsync: (makerAssetData: string) => Promise<string[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CalldataInformation {
|
||||||
|
calldataHexString: string;
|
||||||
|
to: string;
|
||||||
|
value: BigNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Web3TransactionParams {
|
||||||
|
from: string;
|
||||||
|
to?: string;
|
||||||
|
value?: string;
|
||||||
|
gas?: string;
|
||||||
|
gasPrice?: string;
|
||||||
|
data?: string;
|
||||||
|
nonce?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SmartContractParams {
|
||||||
|
params: { [name: string]: any };
|
||||||
|
to: string;
|
||||||
|
value: BigNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SwapQuoteConsumer {
|
||||||
|
getCalldataOrThrowAsync(quote: SwapQuote, opts: Partial<SwapQuoteConsumerOpts>): Promise<CalldataInformation>;
|
||||||
|
getWeb3TransactionParamsOrThrowAsync(quote: SwapQuote, opts: Partial<SwapQuoteConsumerOpts>): Promise<Web3TransactionParams>;
|
||||||
|
getSmartContractParamsOrThrowAsync(quote: SwapQuote, opts: Partial<SwapQuoteConsumerOpts>): Promise<SmartContractParams>;
|
||||||
|
executeSwapQuoteOrThrowAsync(quote: SwapQuote, opts: Partial<SwapQuoteExecutionOpts>): Promise<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SwapQuoteConsumerOpts {
|
||||||
|
networkId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SwapQuoteGetOutputOpts {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ethAmount: The desired amount of eth to spend. Defaults to buyQuote.worstCaseQuoteInfo.totalEthAmount.
|
||||||
|
* takerAddress: The address to perform the buy. Defaults to the first available address from the provider.
|
||||||
|
* gasLimit: The amount of gas to send with a transaction (in Gwei). Defaults to an eth_estimateGas rpc call.
|
||||||
|
* gasPrice: Gas price in Wei to use for a transaction
|
||||||
|
*/
|
||||||
|
export interface SwapQuoteExecutionOpts extends SwapQuoteConsumerOpts {
|
||||||
|
ethAmount?: BigNumber;
|
||||||
|
takerAddress?: string;
|
||||||
|
gasLimit?: number;
|
||||||
|
gasPrice?: BigNumber;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* assetData: String that represents a specific asset (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
|
* assetData: String that represents a specific asset (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
|
||||||
* assetBuyAmount: The amount of asset to buy.
|
* assetBuyAmount: The amount of asset to buy.
|
||||||
* orders: An array of objects conforming to SignedOrder. These orders can be used to cover the requested assetBuyAmount plus slippage.
|
* orders: An array of objects conforming to SignedOrder. These orders can be used to cover the requested assetBuyAmount plus slippage.
|
||||||
* feeOrders: An array of objects conforming to SignedOrder. These orders can be used to cover the fees for the orders param above.
|
* feeOrders: An array of objects conforming to SignedOrder. These orders can be used to cover the fees for the orders param above.
|
||||||
* feePercentage: Optional affiliate fee percentage used to calculate the eth amounts above.
|
|
||||||
* bestCaseQuoteInfo: Info about the best case price for the asset.
|
* bestCaseQuoteInfo: Info about the best case price for the asset.
|
||||||
* 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 SwapQuote {
|
||||||
takerAssetData: string;
|
takerAssetData: string;
|
||||||
makerAssetData: string;
|
makerAssetData: string;
|
||||||
makerAssetBuyAmount: BigNumber;
|
makerAssetBuyAmount: BigNumber;
|
||||||
orders: SignedOrder[];
|
orders: SignedOrder[];
|
||||||
feeOrders: SignedOrder[];
|
feeOrders: SignedOrder[];
|
||||||
bestCaseQuoteInfo: BuyQuoteInfo;
|
bestCaseQuoteInfo: SwapQuoteInfo;
|
||||||
worstCaseQuoteInfo: BuyQuoteInfo;
|
worstCaseQuoteInfo: SwapQuoteInfo;
|
||||||
toAddress: string; // exchange address, coordinator address
|
|
||||||
isUsingCoordinator: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,7 +107,7 @@ export interface BuyQuote {
|
|||||||
* feeEthAmount: The amount of eth required to pay the affiliate fee.
|
* feeEthAmount: The amount of eth required to pay the affiliate fee.
|
||||||
* totalEthAmount: The total amount of eth required to complete the buy (filling orders, feeOrders, and paying affiliate fee).
|
* totalEthAmount: The total amount of eth required to complete the buy (filling orders, feeOrders, and paying affiliate fee).
|
||||||
*/
|
*/
|
||||||
export interface BuyQuoteInfo {
|
export interface SwapQuoteInfo {
|
||||||
takerTokenAmount: BigNumber;
|
takerTokenAmount: BigNumber;
|
||||||
feeTakerTokenAmount: BigNumber;
|
feeTakerTokenAmount: BigNumber;
|
||||||
totalTakerTokenAmount: BigNumber;
|
totalTakerTokenAmount: BigNumber;
|
||||||
@ -72,9 +117,10 @@ export interface BuyQuoteInfo {
|
|||||||
* 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 SwapQuoteRequestOpts {
|
||||||
shouldForceOrderRefresh: boolean;
|
shouldForceOrderRefresh: boolean;
|
||||||
slippagePercentage: number;
|
slippagePercentage: number;
|
||||||
|
allowMarketBuyOrders: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -82,21 +128,10 @@ export interface BuyQuoteRequestOpts {
|
|||||||
*
|
*
|
||||||
* 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.
|
||||||
*/
|
*/
|
||||||
export type LiquidityRequestOpts = Pick<BuyQuoteRequestOpts, 'shouldForceOrderRefresh'>;
|
export type LiquidityRequestOpts = Pick<SwapQuoteRequestOpts, 'shouldForceOrderRefresh'>;
|
||||||
|
|
||||||
/**
|
export interface ForwarderSwapQuoteExecutionOpts extends SwapQuoteExecutionOpts {
|
||||||
* ethAmount: The desired amount of eth to spend. Defaults to buyQuote.worstCaseQuoteInfo.totalEthAmount.
|
feeRecipient?: string;
|
||||||
* takerAddress: The address to perform the buy. Defaults to the first available address from the provider.
|
|
||||||
* gasLimit: The amount of gas to send with a transaction (in Gwei). Defaults to an eth_estimateGas rpc call.
|
|
||||||
* gasPrice: Gas price in Wei to use for a transaction
|
|
||||||
* feeRecipient: The address where affiliate fees are sent. Defaults to null address (0x000...000).
|
|
||||||
*/
|
|
||||||
export interface BuyQuoteExecutionOpts {
|
|
||||||
ethAmount?: BigNumber;
|
|
||||||
takerAddress?: string;
|
|
||||||
gasLimit?: number;
|
|
||||||
gasPrice?: BigNumber;
|
|
||||||
feeRecipient: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -104,7 +139,7 @@ export interface BuyQuoteExecutionOpts {
|
|||||||
* orderRefreshIntervalMs: The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. Defaults to 10000ms (10s).
|
* orderRefreshIntervalMs: The interval in ms that getBuyQuoteAsync should trigger an refresh of orders and order states. Defaults to 10000ms (10s).
|
||||||
* expiryBufferSeconds: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m).
|
* expiryBufferSeconds: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m).
|
||||||
*/
|
*/
|
||||||
export interface AssetBuyerOpts {
|
export interface AssetSwapQuoterOpts {
|
||||||
networkId: number;
|
networkId: number;
|
||||||
orderRefreshIntervalMs: number;
|
orderRefreshIntervalMs: number;
|
||||||
expiryBufferSeconds: number;
|
expiryBufferSeconds: number;
|
||||||
@ -113,7 +148,7 @@ export interface AssetBuyerOpts {
|
|||||||
/**
|
/**
|
||||||
* Possible error messages thrown by an AssetBuyer instance or associated static methods.
|
* Possible error messages thrown by an AssetBuyer instance or associated static methods.
|
||||||
*/
|
*/
|
||||||
export enum AssetBuyerError {
|
export enum AssetSwapQuoterError {
|
||||||
NoEtherTokenContractFound = 'NO_ETHER_TOKEN_CONTRACT_FOUND',
|
NoEtherTokenContractFound = 'NO_ETHER_TOKEN_CONTRACT_FOUND',
|
||||||
NoZrxTokenContractFound = 'NO_ZRX_TOKEN_CONTRACT_FOUND',
|
NoZrxTokenContractFound = 'NO_ZRX_TOKEN_CONTRACT_FOUND',
|
||||||
StandardRelayerApiError = 'STANDARD_RELAYER_API_ERROR',
|
StandardRelayerApiError = 'STANDARD_RELAYER_API_ERROR',
|
||||||
|
@ -1,29 +1,27 @@
|
|||||||
import { assert as sharedAssert } from '@0x/assert';
|
import { assert as sharedAssert } from '@0x/assert';
|
||||||
import { schemas } from '@0x/json-schemas';
|
import { schemas } from '@0x/json-schemas';
|
||||||
|
|
||||||
import { BuyQuote, BuyQuoteInfo, OrderProvider, OrderProviderRequest } from '../types';
|
import { OrderProvider, OrderProviderRequest, SwapQuote, SwapQuoteInfo } from '../types';
|
||||||
|
|
||||||
export const assert = {
|
export const assert = {
|
||||||
...sharedAssert,
|
...sharedAssert,
|
||||||
isValidBuyQuote(variableName: string, buyQuote: BuyQuote): void {
|
isValidSwapQuote(variableName: string, swapQuote: SwapQuote): void {
|
||||||
sharedAssert.isHexString(`${variableName}.takerAssetData`, buyQuote.takerAssetData);
|
sharedAssert.isHexString(`${variableName}.takerAssetData`, swapQuote.takerAssetData);
|
||||||
sharedAssert.isHexString(`${variableName}.makerAssetData`, buyQuote.makerAssetData);
|
sharedAssert.isHexString(`${variableName}.makerAssetData`, swapQuote.makerAssetData);
|
||||||
sharedAssert.doesConformToSchema(`${variableName}.orders`, buyQuote.orders, schemas.signedOrdersSchema);
|
sharedAssert.doesConformToSchema(`${variableName}.orders`, swapQuote.orders, schemas.signedOrdersSchema);
|
||||||
sharedAssert.doesConformToSchema(`${variableName}.feeOrders`, buyQuote.feeOrders, schemas.signedOrdersSchema);
|
sharedAssert.doesConformToSchema(`${variableName}.feeOrders`, swapQuote.feeOrders, schemas.signedOrdersSchema);
|
||||||
assert.isValidBuyQuoteInfo(`${variableName}.bestCaseQuoteInfo`, buyQuote.bestCaseQuoteInfo);
|
assert.isValidSwapQuoteInfo(`${variableName}.bestCaseQuoteInfo`, swapQuote.bestCaseQuoteInfo);
|
||||||
assert.isValidBuyQuoteInfo(`${variableName}.worstCaseQuoteInfo`, buyQuote.worstCaseQuoteInfo);
|
assert.isValidSwapQuoteInfo(`${variableName}.worstCaseQuoteInfo`, swapQuote.worstCaseQuoteInfo);
|
||||||
sharedAssert.isBigNumber(`${variableName}.makerAssetBuyAmount`, buyQuote.makerAssetBuyAmount);
|
sharedAssert.isBigNumber(`${variableName}.makerAssetBuyAmount`, swapQuote.makerAssetBuyAmount);
|
||||||
assert.isETHAddressHex(`${variableName}.toAddress`, buyQuote.toAddress);
|
|
||||||
assert.isBoolean(`${variableName}.isUsingCoordinator`, buyQuote.isUsingCoordinator);
|
|
||||||
// TODO(dave4506) Remove once forwarder features are reimplemented
|
// TODO(dave4506) Remove once forwarder features are reimplemented
|
||||||
// if (buyQuote.feePercentage !== undefined) {
|
// if (buyQuote.feePercentage !== undefined) {
|
||||||
// sharedAssert.isNumber(`${variableName}.feePercentage`, buyQuote.feePercentage);
|
// sharedAssert.isNumber(`${variableName}.feePercentage`, buyQuote.feePercentage);
|
||||||
// }
|
// }
|
||||||
},
|
},
|
||||||
isValidBuyQuoteInfo(variableName: string, buyQuoteInfo: BuyQuoteInfo): void {
|
isValidSwapQuoteInfo(variableName: string, swapQuoteInfo: SwapQuoteInfo): void {
|
||||||
sharedAssert.isBigNumber(`${variableName}.takerTokenAmount`, buyQuoteInfo.takerTokenAmount);
|
sharedAssert.isBigNumber(`${variableName}.takerTokenAmount`, swapQuoteInfo.takerTokenAmount);
|
||||||
sharedAssert.isBigNumber(`${variableName}.feeTakerTokenAmount`, buyQuoteInfo.feeTakerTokenAmount);
|
sharedAssert.isBigNumber(`${variableName}.feeTakerTokenAmount`, swapQuoteInfo.feeTakerTokenAmount);
|
||||||
sharedAssert.isBigNumber(`${variableName}.totalTakerTokenAmount`, buyQuoteInfo.totalTakerTokenAmount);
|
sharedAssert.isBigNumber(`${variableName}.totalTakerTokenAmount`, swapQuoteInfo.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);
|
||||||
|
@ -7,7 +7,7 @@ import * as _ from 'lodash';
|
|||||||
|
|
||||||
import { constants } from '../constants';
|
import { constants } from '../constants';
|
||||||
import {
|
import {
|
||||||
AssetBuyerError,
|
AssetSwapQuoterError,
|
||||||
OrderProviderRequest,
|
OrderProviderRequest,
|
||||||
OrderProviderResponse,
|
OrderProviderResponse,
|
||||||
OrdersAndFillableAmounts,
|
OrdersAndFillableAmounts,
|
||||||
@ -19,7 +19,7 @@ export const orderProviderResponseProcessor = {
|
|||||||
const { makerAssetData, takerAssetData } = request;
|
const { makerAssetData, takerAssetData } = request;
|
||||||
_.forEach(response.orders, order => {
|
_.forEach(response.orders, order => {
|
||||||
if (order.makerAssetData !== makerAssetData || order.takerAssetData !== takerAssetData) {
|
if (order.makerAssetData !== makerAssetData || order.takerAssetData !== takerAssetData) {
|
||||||
throw new Error(AssetBuyerError.InvalidOrderProviderResponse);
|
throw new Error(AssetSwapQuoterError.InvalidOrderProviderResponse);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -4,17 +4,17 @@ import * as _ from 'lodash';
|
|||||||
|
|
||||||
import { constants } from '../constants';
|
import { constants } from '../constants';
|
||||||
import { InsufficientAssetLiquidityError } from '../errors';
|
import { InsufficientAssetLiquidityError } from '../errors';
|
||||||
import { AssetBuyerError, BuyQuote, BuyQuoteInfo, OrdersAndFillableAmounts } from '../types';
|
import { AssetSwapQuoterError, OrdersAndFillableAmounts, SwapQuote, SwapQuoteInfo } from '../types';
|
||||||
|
|
||||||
// Calculates a buy quote for orders that have WETH as the takerAsset
|
// Calculates a swap quote for orders
|
||||||
export const buyQuoteCalculator = {
|
export const swapQuoteCalculator = {
|
||||||
calculate(
|
calculate(
|
||||||
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||||
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
|
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||||
makerAssetBuyAmount: BigNumber,
|
makerAssetBuyAmount: BigNumber,
|
||||||
slippagePercentage: number,
|
slippagePercentage: number,
|
||||||
isMakerAssetZrxToken: boolean,
|
isMakerAssetZrxToken: boolean,
|
||||||
): BuyQuote {
|
): SwapQuote {
|
||||||
const orders = ordersAndFillableAmounts.orders;
|
const orders = ordersAndFillableAmounts.orders;
|
||||||
const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts;
|
const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts;
|
||||||
const feeOrders = feeOrdersAndFillableAmounts.orders;
|
const feeOrders = feeOrdersAndFillableAmounts.orders;
|
||||||
@ -64,7 +64,7 @@ export const buyQuoteCalculator = {
|
|||||||
);
|
);
|
||||||
// if we do not have enough feeOrders to cover the fees, throw
|
// if we do not have enough feeOrders to cover the fees, throw
|
||||||
if (feeOrdersAndRemainingFeeAmount.remainingFeeAmount.gt(constants.ZERO_AMOUNT)) {
|
if (feeOrdersAndRemainingFeeAmount.remainingFeeAmount.gt(constants.ZERO_AMOUNT)) {
|
||||||
throw new Error(AssetBuyerError.InsufficientZrxLiquidity);
|
throw new Error(AssetSwapQuoterError.InsufficientZrxLiquidity);
|
||||||
}
|
}
|
||||||
resultFeeOrders = feeOrdersAndRemainingFeeAmount.resultFeeOrders;
|
resultFeeOrders = feeOrdersAndRemainingFeeAmount.resultFeeOrders;
|
||||||
feeOrdersRemainingFillableMakerAssetAmounts =
|
feeOrdersRemainingFillableMakerAssetAmounts =
|
||||||
@ -106,9 +106,6 @@ export const buyQuoteCalculator = {
|
|||||||
feeOrders: resultFeeOrders,
|
feeOrders: resultFeeOrders,
|
||||||
bestCaseQuoteInfo,
|
bestCaseQuoteInfo,
|
||||||
worstCaseQuoteInfo,
|
worstCaseQuoteInfo,
|
||||||
// TODO(dave4506): coordinator metadata for buy quote
|
|
||||||
toAddress: constants.NULL_ADDRESS,
|
|
||||||
isUsingCoordinator: false,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -118,7 +115,7 @@ function calculateQuoteInfo(
|
|||||||
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
|
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||||
makserAssetBuyAmount: BigNumber,
|
makserAssetBuyAmount: BigNumber,
|
||||||
isMakerAssetZrxToken: boolean,
|
isMakerAssetZrxToken: boolean,
|
||||||
): BuyQuoteInfo {
|
): SwapQuoteInfo {
|
||||||
// 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 takerTokenAmount = constants.ZERO_AMOUNT;
|
let takerTokenAmount = constants.ZERO_AMOUNT;
|
||||||
let zrxTakerTokenAmount = constants.ZERO_AMOUNT;
|
let zrxTakerTokenAmount = constants.ZERO_AMOUNT;
|
||||||
@ -132,7 +129,9 @@ function calculateQuoteInfo(
|
|||||||
// find eth amount needed to buy zrx
|
// find eth amount needed to buy zrx
|
||||||
zrxTakerTokenAmount = findTakerTokenAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset);
|
zrxTakerTokenAmount = findTakerTokenAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset);
|
||||||
}
|
}
|
||||||
|
|
||||||
const feeTakerTokenAmount = zrxTakerTokenAmount;
|
const feeTakerTokenAmount = zrxTakerTokenAmount;
|
||||||
|
|
||||||
// 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 totalTakerTokenAmount = takerTokenAmount.plus(feeTakerTokenAmount);
|
const totalTakerTokenAmount = takerTokenAmount.plus(feeTakerTokenAmount);
|
||||||
return {
|
return {
|
12
packages/asset-buyer/src/utils/utils.ts
Normal file
12
packages/asset-buyer/src/utils/utils.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
|
|
||||||
|
import { constants } from '../constants';
|
||||||
|
|
||||||
|
export const utils = {
|
||||||
|
numberPercentageToEtherTokenAmountPercentage(percentage: number): BigNumber {
|
||||||
|
return Web3Wrapper.toBaseUnitAmount(constants.ONE_AMOUNT, constants.ETHER_TOKEN_DECIMALS).multipliedBy(
|
||||||
|
percentage,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user