refactored for market sell + buy

This commit is contained in:
David Sun
2019-06-27 16:54:13 -07:00
parent 1207b68f57
commit 288a7d4cea
4 changed files with 117 additions and 44 deletions

View File

@@ -5,7 +5,6 @@ import {
ForwarderSwapQuoteExecutionOpts, ForwarderSwapQuoteExecutionOpts,
ForwarderSwapQuoteGetOutputOpts, ForwarderSwapQuoteGetOutputOpts,
OrdersAndFillableAmounts, OrdersAndFillableAmounts,
SwapQuoteOperation,
SwapQuoteRequestOpts, SwapQuoteRequestOpts,
SwapQuoterOpts, SwapQuoterOpts,
} from './types'; } from './types';
@@ -30,7 +29,6 @@ const DEFAULT_FORWARDER_SWAP_QUOTE_EXECUTE_OPTS: ForwarderSwapQuoteExecutionOpts
const DEFAULT_SWAP_QUOTE_REQUEST_OPTS: SwapQuoteRequestOpts = { const DEFAULT_SWAP_QUOTE_REQUEST_OPTS: SwapQuoteRequestOpts = {
shouldForceOrderRefresh: false, shouldForceOrderRefresh: false,
slippagePercentage: 0.2, // 20% slippage protection, slippagePercentage: 0.2, // 20% slippage protection,
operation: SwapQuoteOperation.MarketBuy,
}; };
const EMPTY_ORDERS_AND_FILLABLE_AMOUNTS: OrdersAndFillableAmounts = { const EMPTY_ORDERS_AND_FILLABLE_AMOUNTS: OrdersAndFillableAmounts = {

View File

@@ -12,13 +12,14 @@ import { StandardRelayerAPIOrderProvider } from './order_providers/standard_rela
import { import {
LiquidityForAssetData, LiquidityForAssetData,
LiquidityRequestOpts, LiquidityRequestOpts,
MarketBuySwapQuote,
MarketSellSwapQuote,
OrderProvider, OrderProvider,
OrdersAndFillableAmounts, OrdersAndFillableAmounts,
SwapQuote, SwapQuote,
SwapQuoteRequestOpts, SwapQuoteRequestOpts,
SwapQuoterError, SwapQuoterError,
SwapQuoterOpts, SwapQuoterOpts,
SwapQuoteOperation,
} from './types'; } from './types';
import { assert } from './utils/assert'; import { assert } from './utils/assert';
@@ -126,25 +127,25 @@ export class SwapQuoter {
* You can then pass the `SwapQuote` to a `SwapQuoteConsumer` to execute a buy, or process SwapQuote for on-chain consumption. * You can then pass the `SwapQuote` to a `SwapQuoteConsumer` to execute a buy, or process SwapQuote for on-chain consumption.
* @param makerAssetData The makerAssetData of the desired asset to swap for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). * @param makerAssetData The makerAssetData of the desired asset to swap for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
* @param takerAssetData The takerAssetData of the asset to swap makerAssetData for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). * @param takerAssetData The takerAssetData of the asset to swap makerAssetData for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
* @param makerAssetSwapAmount The amount of maker asset to swap for. * @param takerAssetSellAmount The amount of maker asset to swap for.
* @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 SwapQuote 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 getSwapQuoteAsync( public async getMarketSellSwapQuoteAsync(
makerAssetData: string, makerAssetData: string,
takerAssetData: string, takerAssetData: string,
assetSwapAmount: BigNumber, takerAssetSellAmount: BigNumber,
options: Partial<SwapQuoteRequestOpts> = {}, options: Partial<SwapQuoteRequestOpts> = {},
): Promise<SwapQuote> { ): Promise<MarketSellSwapQuote> {
const { shouldForceOrderRefresh, slippagePercentage, operation } = _.merge( const { shouldForceOrderRefresh, slippagePercentage } = _.merge(
{}, {},
constants.DEFAULT_SWAP_QUOTE_REQUEST_OPTS, constants.DEFAULT_SWAP_QUOTE_REQUEST_OPTS,
options, options,
); );
assert.isString('makerAssetData', makerAssetData); assert.isString('makerAssetData', makerAssetData);
assert.isString('takerAssetData', takerAssetData); assert.isString('takerAssetData', takerAssetData);
assert.isBigNumber('assetSwapAmount', assetSwapAmount); assert.isBigNumber('takerAssetSellAmount', takerAssetSellAmount);
assert.isBoolean('shouldForceOrderRefresh', shouldForceOrderRefresh); assert.isBoolean('shouldForceOrderRefresh', shouldForceOrderRefresh);
assert.isNumber('slippagePercentage', slippagePercentage); assert.isNumber('slippagePercentage', slippagePercentage);
const zrxTokenAssetData = this._getZrxTokenAssetDataOrThrow(); const zrxTokenAssetData = this._getZrxTokenAssetDataOrThrow();
@@ -165,24 +166,68 @@ export class SwapQuoter {
}: For makerAssetdata ${makerAssetData} and takerAssetdata ${takerAssetData}`, }: For makerAssetdata ${makerAssetData} and takerAssetdata ${takerAssetData}`,
); );
} }
let swapQuote: SwapQuote; const swapQuote = swapQuoteCalculator.calculateMarketSellSwapQuote(
if (operation === SwapQuoteOperation.MarketBuy) {
swapQuote = swapQuoteCalculator.calculateMarketBuySwapQuote(
ordersAndFillableAmounts, ordersAndFillableAmounts,
feeOrdersAndFillableAmounts, feeOrdersAndFillableAmounts,
assetSwapAmount, takerAssetSellAmount,
slippagePercentage, slippagePercentage,
isMakerAssetZrxToken, isMakerAssetZrxToken,
); );
} else { return swapQuote;
swapQuote = swapQuoteCalculator.calculateMarketSellSwapQuote( }
ordersAndFillableAmounts,
feeOrdersAndFillableAmounts, /**
assetSwapAmount, * Get a `SwapQuote` containing all information relevant to fulfilling a swap between a desired ERC20 token address and ERC20 owned by a provided address.
slippagePercentage, * You can then pass the `SwapQuote` to a `SwapQuoteConsumer` to execute a buy, or process SwapQuote for on-chain consumption.
isMakerAssetZrxToken, * @param makerAssetData The makerAssetData of the desired asset to swap for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
* @param takerAssetData The takerAssetData of the asset to swap makerAssetData for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
* @param makerAssetSwapAmount The amount of maker asset to swap for.
* @param options Options for 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 getMarketBuySwapQuoteAsync(
makerAssetData: string,
takerAssetData: string,
makerAssetBuyAmount: BigNumber,
options: Partial<SwapQuoteRequestOpts> = {},
): Promise<MarketBuySwapQuote> {
const { shouldForceOrderRefresh, slippagePercentage } = _.merge(
{},
constants.DEFAULT_SWAP_QUOTE_REQUEST_OPTS,
options,
);
assert.isString('makerAssetData', makerAssetData);
assert.isString('takerAssetData', takerAssetData);
assert.isBigNumber('makerAssetBuyAmount', makerAssetBuyAmount);
assert.isBoolean('shouldForceOrderRefresh', shouldForceOrderRefresh);
assert.isNumber('slippagePercentage', slippagePercentage);
const zrxTokenAssetData = this._getZrxTokenAssetDataOrThrow();
const isMakerAssetZrxToken = makerAssetData === zrxTokenAssetData;
// get the relevant orders for the makerAsset and fees
// if the requested assetData is ZRX, don't get the fee info
const [ordersAndFillableAmounts, feeOrdersAndFillableAmounts] = await Promise.all([
this.getOrdersAndFillableAmountsAsync(makerAssetData, takerAssetData, shouldForceOrderRefresh),
isMakerAssetZrxToken
? Promise.resolve(constants.EMPTY_ORDERS_AND_FILLABLE_AMOUNTS)
: this.getOrdersAndFillableAmountsAsync(zrxTokenAssetData, takerAssetData, shouldForceOrderRefresh),
shouldForceOrderRefresh,
]);
if (ordersAndFillableAmounts.orders.length === 0) {
throw new Error(
`${
SwapQuoterError.AssetUnavailable
}: For makerAssetdata ${makerAssetData} and takerAssetdata ${takerAssetData}`,
); );
} }
const swapQuote = swapQuoteCalculator.calculateMarketBuySwapQuote(
ordersAndFillableAmounts,
feeOrdersAndFillableAmounts,
makerAssetBuyAmount,
slippagePercentage,
isMakerAssetZrxToken,
);
return swapQuote; return swapQuote;
} }
/** /**
@@ -195,18 +240,43 @@ export class SwapQuoter {
* *
* @return An object that conforms to SwapQuote 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 getSwapQuoteForERC20TokenAddressAsync( public async getMarketBuySwapQuoteForERC20TokenAddressAsync(
makerTokenAddress: string, makerTokenAddress: string,
takerTokenAddress: string, takerTokenAddress: string,
assetSwapAmount: BigNumber, makerAssetBuyAmount: BigNumber,
options: Partial<SwapQuoteRequestOpts> = {}, options: Partial<SwapQuoteRequestOpts> = {},
): Promise<SwapQuote> { ): Promise<SwapQuote> {
assert.isETHAddressHex('makerTokenAddress', makerTokenAddress); assert.isETHAddressHex('makerTokenAddress', makerTokenAddress);
assert.isETHAddressHex('takerTokenAddress', takerTokenAddress); assert.isETHAddressHex('takerTokenAddress', takerTokenAddress);
assert.isBigNumber('assetSwapAmount', assetSwapAmount); 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 swapQuote = this.getSwapQuoteAsync(makerAssetData, takerAssetData, assetSwapAmount, options); const swapQuote = this.getMarketBuySwapQuoteAsync(makerAssetData, takerAssetData, makerAssetBuyAmount, options);
return swapQuote;
}
/**
* Get a `SwapQuote` containing all information relevant to fulfilling a swap between a desired ERC20 token address and ERC20 owned by a provided address.
* You can then pass the `SwapQuote` to a `SwapQuoteConsumer` to execute a buy, or process SwapQuote for on-chain consumption.
* @param makerAssetData The makerAssetData of the desired asset to swap for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
* @param takerAssetData The takerAssetData of the asset to swap makerAssetData for (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
* @param makerAssetSwapAmount The amount of maker asset to swap for.
* @param options Options for 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 getMarketSellSwapQuoteForERC20TokenAddressAsync(
makerTokenAddress: string,
takerTokenAddress: string,
takerAssetSellAmount: BigNumber,
options: Partial<SwapQuoteRequestOpts> = {},
): Promise<SwapQuote> {
assert.isETHAddressHex('makerTokenAddress', makerTokenAddress);
assert.isETHAddressHex('takerTokenAddress', takerTokenAddress);
assert.isBigNumber('takerAssetSellAmount', takerAssetSellAmount);
const makerAssetData = assetDataUtils.encodeERC20AssetData(makerTokenAddress);
const takerAssetData = assetDataUtils.encodeERC20AssetData(takerTokenAddress);
const swapQuote = this.getMarketSellSwapQuoteAsync(makerAssetData, takerAssetData, takerAssetSellAmount, options);
return swapQuote; return swapQuote;
} }
/** /**

View File

@@ -154,11 +154,6 @@ export interface ForwarderSwapQuoteGetOutputOpts extends SwapQuoteGetOutputOpts
*/ */
export interface ForwarderSwapQuoteExecutionOpts extends ForwarderSwapQuoteGetOutputOpts, SwapQuoteExecutionOpts {} export interface ForwarderSwapQuoteExecutionOpts extends ForwarderSwapQuoteGetOutputOpts, SwapQuoteExecutionOpts {}
export enum SwapQuoteOperation {
MarketSell = 'MARKET_SELL',
MarketBuy = 'MARKET_BUY',
}
/** /**
* takerAssetData: String that represents a specific taker asset (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). * takerAssetData: String that represents a specific taker asset (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
* makerAssetData: String that represents a specific maker asset (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md). * makerAssetData: String that represents a specific maker asset (for more info: https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
@@ -171,13 +166,22 @@ export enum SwapQuoteOperation {
export interface SwapQuote { export interface SwapQuote {
takerAssetData: string; takerAssetData: string;
makerAssetData: string; makerAssetData: string;
takerAssetFillAmount?: BigNumber;
makerAssetFillAmount?: BigNumber;
orders: SignedOrder[]; orders: SignedOrder[];
feeOrders: SignedOrder[]; feeOrders: SignedOrder[];
bestCaseQuoteInfo: SwapQuoteInfo; bestCaseQuoteInfo: SwapQuoteInfo;
worstCaseQuoteInfo: SwapQuoteInfo; worstCaseQuoteInfo: SwapQuoteInfo;
operation: SwapQuoteOperation; }
export interface MarketSellSwapQuote extends SwapQuote {
takerAssetFillAmount: BigNumber;
bestCaseQuoteInfo: MarketSellSwapQuoteInfo;
worstCaseQuoteInfo: MarketSellSwapQuoteInfo;
}
export interface MarketBuySwapQuote extends SwapQuote {
makerAssetFillAmount: BigNumber;
bestCaseQuoteInfo: MarketBuySwapQuoteInfo;
worstCaseQuoteInfo: MarketBuySwapQuoteInfo;
} }
export interface SwapQuoteWithAffiliateFee extends SwapQuote { export interface SwapQuoteWithAffiliateFee extends SwapQuote {
@@ -190,12 +194,18 @@ export interface SwapQuoteWithAffiliateFee extends SwapQuote {
* 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 SwapQuoteInfo { export interface SwapQuoteInfo {
takerTokenAmount: BigNumber;
makerTokenAmount: BigNumber;
feeTakerTokenAmount: BigNumber; feeTakerTokenAmount: BigNumber;
totalTakerTokenAmount: BigNumber; totalTakerTokenAmount: BigNumber;
} }
export interface MarketSellSwapQuoteInfo extends SwapQuoteInfo {
makerTokenAmount: BigNumber;
}
export interface MarketBuySwapQuoteInfo extends SwapQuoteInfo {
takerTokenAmount: BigNumber;
}
/** /**
* 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%).
@@ -203,7 +213,6 @@ export interface SwapQuoteInfo {
export interface SwapQuoteRequestOpts { export interface SwapQuoteRequestOpts {
shouldForceOrderRefresh: boolean; shouldForceOrderRefresh: boolean;
slippagePercentage: number; slippagePercentage: number;
operation: SwapQuoteOperation;
} }
/* /*

View File

@@ -4,7 +4,7 @@ import * as _ from 'lodash';
import { constants } from '../constants'; import { constants } from '../constants';
import { InsufficientAssetLiquidityError } from '../errors'; import { InsufficientAssetLiquidityError } from '../errors';
import { OrdersAndFillableAmounts, SwapQuote, SwapQuoteInfo, SwapQuoteOperation, SwapQuoterError } from '../types'; import { MarketBuySwapQuote, MarketBuySwapQuoteInfo, MarketSellSwapQuote, MarketSellSwapQuoteInfo, OrdersAndFillableAmounts, SwapQuoterError } from '../types';
// Calculates a swap quote for orders // Calculates a swap quote for orders
export const swapQuoteCalculator = { export const swapQuoteCalculator = {
@@ -14,7 +14,7 @@ export const swapQuoteCalculator = {
takerAssetFillAmount: BigNumber, takerAssetFillAmount: BigNumber,
slippagePercentage: number, slippagePercentage: number,
isMakerAssetZrxToken: boolean, isMakerAssetZrxToken: boolean,
): SwapQuote { ): MarketSellSwapQuote {
const orders = ordersAndFillableAmounts.orders; const orders = ordersAndFillableAmounts.orders;
const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts; const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts;
const remainingFillableTakerAssetAmounts = remainingFillableMakerAssetAmounts.map((makerAssetAmount: BigNumber, index: number) => { const remainingFillableTakerAssetAmounts = remainingFillableMakerAssetAmounts.map((makerAssetAmount: BigNumber, index: number) => {
@@ -112,7 +112,6 @@ export const swapQuoteCalculator = {
feeOrders: resultFeeOrders, feeOrders: resultFeeOrders,
bestCaseQuoteInfo, bestCaseQuoteInfo,
worstCaseQuoteInfo, worstCaseQuoteInfo,
operation: SwapQuoteOperation.MarketBuy,
}; };
}, },
calculateMarketBuySwapQuote( calculateMarketBuySwapQuote(
@@ -121,7 +120,7 @@ export const swapQuoteCalculator = {
makerAssetFillAmount: BigNumber, makerAssetFillAmount: BigNumber,
slippagePercentage: number, slippagePercentage: number,
isMakerAssetZrxToken: boolean, isMakerAssetZrxToken: boolean,
): SwapQuote { ): MarketBuySwapQuote {
const orders = ordersAndFillableAmounts.orders; const orders = ordersAndFillableAmounts.orders;
const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts; const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts;
const feeOrders = feeOrdersAndFillableAmounts.orders; const feeOrders = feeOrdersAndFillableAmounts.orders;
@@ -213,7 +212,6 @@ export const swapQuoteCalculator = {
feeOrders: resultFeeOrders, feeOrders: resultFeeOrders,
bestCaseQuoteInfo, bestCaseQuoteInfo,
worstCaseQuoteInfo, worstCaseQuoteInfo,
operation: SwapQuoteOperation.MarketBuy,
}; };
}, },
}; };
@@ -223,7 +221,7 @@ function calculateMarketBuyQuoteInfo(
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts, feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
makerAssetBuyAmount: BigNumber, makerAssetBuyAmount: BigNumber,
isMakerAssetZrxToken: boolean, isMakerAssetZrxToken: boolean,
): SwapQuoteInfo { ): MarketBuySwapQuoteInfo {
// 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;
@@ -247,7 +245,6 @@ function calculateMarketBuyQuoteInfo(
const totalTakerTokenAmount = takerTokenAmount.plus(feeTakerTokenAmount); const totalTakerTokenAmount = takerTokenAmount.plus(feeTakerTokenAmount);
return { return {
takerTokenAmount, takerTokenAmount,
makerTokenAmount: makerAssetBuyAmount,
feeTakerTokenAmount, feeTakerTokenAmount,
totalTakerTokenAmount, totalTakerTokenAmount,
}; };
@@ -258,7 +255,7 @@ function calculateMarketSellQuoteInfo(
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts, feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
takerAssetSellAmount: BigNumber, takerAssetSellAmount: BigNumber,
isMakerAssetZrxToken: boolean, isMakerAssetZrxToken: boolean,
): SwapQuoteInfo { ): MarketSellSwapQuoteInfo {
// 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 makerTokenAmount = constants.ZERO_AMOUNT; let makerTokenAmount = constants.ZERO_AMOUNT;
let zrxTakerTokenAmount = constants.ZERO_AMOUNT; let zrxTakerTokenAmount = constants.ZERO_AMOUNT;
@@ -281,7 +278,6 @@ function calculateMarketSellQuoteInfo(
// 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 = takerAssetSellAmount.plus(feeTakerTokenAmount); const totalTakerTokenAmount = takerAssetSellAmount.plus(feeTakerTokenAmount);
return { return {
takerTokenAmount: takerAssetSellAmount,
makerTokenAmount, makerTokenAmount,
feeTakerTokenAmount, feeTakerTokenAmount,
totalTakerTokenAmount, totalTakerTokenAmount,