refactored types and market sell operation
This commit is contained in:
parent
d0ea74e180
commit
b4ac6d3439
@ -7,13 +7,10 @@ import * as _ from 'lodash';
|
||||
import { constants } from '../constants';
|
||||
import {
|
||||
CalldataInfo,
|
||||
ExchangeMarketBuySmartContractParams,
|
||||
ExchangeMarketSellSmartContractParams,
|
||||
MarketBuySwapQuote,
|
||||
MarketSellSwapQuote,
|
||||
ExchangeSmartContractParams,
|
||||
SmartContractParamsInfo,
|
||||
SwapQuote,
|
||||
SwapQuoteConsumer,
|
||||
SwapQuoteConsumerBase,
|
||||
SwapQuoteConsumerError,
|
||||
SwapQuoteConsumerOpts,
|
||||
SwapQuoteExecutionOpts,
|
||||
@ -23,8 +20,7 @@ import { assert } from '../utils/assert';
|
||||
import { swapQuoteConsumerUtils } from '../utils/swap_quote_consumer_utils';
|
||||
import { utils } from '../utils/utils';
|
||||
|
||||
export class ExchangeSwapQuoteConsumer
|
||||
implements SwapQuoteConsumer<ExchangeMarketBuySmartContractParams | ExchangeMarketSellSmartContractParams> {
|
||||
export class ExchangeSwapQuoteConsumer implements SwapQuoteConsumerBase<ExchangeSmartContractParams> {
|
||||
public readonly provider: ZeroExProvider;
|
||||
public readonly networkId: number;
|
||||
|
||||
@ -48,19 +44,18 @@ export class ExchangeSwapQuoteConsumer
|
||||
): Promise<CalldataInfo> {
|
||||
assert.isValidSwapQuote('quote', quote);
|
||||
|
||||
const consumableQuote = (quote as any) as (MarketBuySwapQuote | MarketSellSwapQuote);
|
||||
const smartContractParamsInfo = await this.getSmartContractParamsOrThrowAsync(consumableQuote, opts);
|
||||
const { to, methodAbi, ethAmount } = smartContractParamsInfo;
|
||||
const { to, methodAbi, ethAmount, params } = await this.getSmartContractParamsOrThrowAsync(quote, opts);
|
||||
|
||||
const abiEncoder = new AbiEncoder.Method(methodAbi);
|
||||
|
||||
const { orders, signatures } = params;
|
||||
let args: any[];
|
||||
if (utils.isSwapQuoteMarketBuy(consumableQuote)) {
|
||||
const marketBuyParams = (smartContractParamsInfo.params as any) as ExchangeMarketBuySmartContractParams;
|
||||
args = [marketBuyParams.orders, marketBuyParams.makerAssetFillAmount, marketBuyParams.signatures];
|
||||
if (params.type === 'marketBuy') {
|
||||
const { makerAssetFillAmount } = params;
|
||||
args = [orders, makerAssetFillAmount, signatures];
|
||||
} else {
|
||||
const marketSellParams = (smartContractParamsInfo.params as any) as ExchangeMarketSellSmartContractParams;
|
||||
args = [marketSellParams.orders, marketSellParams.takerAssetFillAmount, marketSellParams.signatures];
|
||||
const { takerAssetFillAmount } = params;
|
||||
args = [orders, takerAssetFillAmount, signatures];
|
||||
}
|
||||
const calldataHexString = abiEncoder.encode(args);
|
||||
return {
|
||||
@ -73,36 +68,36 @@ export class ExchangeSwapQuoteConsumer
|
||||
|
||||
public async getSmartContractParamsOrThrowAsync(
|
||||
quote: SwapQuote,
|
||||
opts: Partial<SwapQuoteGetOutputOpts>,
|
||||
): Promise<SmartContractParamsInfo<ExchangeMarketBuySmartContractParams | ExchangeMarketSellSmartContractParams>> {
|
||||
_opts: Partial<SwapQuoteGetOutputOpts>,
|
||||
): Promise<SmartContractParamsInfo<ExchangeSmartContractParams>> {
|
||||
assert.isValidSwapQuote('quote', quote);
|
||||
|
||||
const consumableQuote = (quote as any) as (MarketBuySwapQuote | MarketSellSwapQuote);
|
||||
|
||||
const { orders } = consumableQuote;
|
||||
const { orders } = quote;
|
||||
|
||||
const signatures = _.map(orders, o => o.signature);
|
||||
|
||||
let params: ExchangeMarketBuySmartContractParams | ExchangeMarketSellSmartContractParams;
|
||||
let params: ExchangeSmartContractParams;
|
||||
let methodName: string;
|
||||
|
||||
if (utils.isSwapQuoteMarketBuy(consumableQuote)) {
|
||||
const { makerAssetFillAmount } = consumableQuote;
|
||||
if (quote.type === 'marketBuy') {
|
||||
const { makerAssetFillAmount } = quote;
|
||||
|
||||
params = {
|
||||
orders,
|
||||
signatures,
|
||||
makerAssetFillAmount,
|
||||
type: 'marketBuy',
|
||||
};
|
||||
|
||||
methodName = 'marketBuyOrders';
|
||||
} else {
|
||||
const { takerAssetFillAmount } = consumableQuote;
|
||||
const { takerAssetFillAmount } = quote;
|
||||
|
||||
params = {
|
||||
orders,
|
||||
signatures,
|
||||
takerAssetFillAmount,
|
||||
type: 'marketSell',
|
||||
};
|
||||
|
||||
methodName = 'marketSellOrders';
|
||||
@ -138,16 +133,14 @@ export class ExchangeSwapQuoteConsumer
|
||||
assert.isBigNumber('gasPrice', gasPrice);
|
||||
}
|
||||
|
||||
const consumableQuote = (quote as any) as (MarketBuySwapQuote | MarketSellSwapQuote);
|
||||
|
||||
const { orders } = consumableQuote;
|
||||
const { orders } = quote;
|
||||
|
||||
const finalTakerAddress = await swapQuoteConsumerUtils.getTakerAddressOrThrowAsync(this.provider, opts);
|
||||
|
||||
try {
|
||||
let txHash: string;
|
||||
if (utils.isSwapQuoteMarketBuy(consumableQuote)) {
|
||||
const { makerAssetFillAmount } = consumableQuote;
|
||||
if (quote.type === 'marketBuy') {
|
||||
const { makerAssetFillAmount } = quote;
|
||||
txHash = await this._contractWrappers.exchange.marketBuyOrdersNoThrowAsync(
|
||||
orders,
|
||||
makerAssetFillAmount,
|
||||
@ -159,7 +152,7 @@ export class ExchangeSwapQuoteConsumer
|
||||
},
|
||||
);
|
||||
} else {
|
||||
const { takerAssetFillAmount } = consumableQuote;
|
||||
const { takerAssetFillAmount } = quote;
|
||||
txHash = await this._contractWrappers.exchange.marketSellOrdersNoThrowAsync(
|
||||
orders,
|
||||
takerAssetFillAmount,
|
||||
|
@ -7,15 +7,12 @@ import * as _ from 'lodash';
|
||||
import { constants } from '../constants';
|
||||
import {
|
||||
CalldataInfo,
|
||||
ForwarderMarketBuySmartContractParams,
|
||||
ForwarderMarketSellSmartContractParams,
|
||||
ForwarderSmartContractParams,
|
||||
ForwarderSwapQuoteExecutionOpts,
|
||||
ForwarderSwapQuoteGetOutputOpts,
|
||||
MarketBuySwapQuote,
|
||||
MarketSellSwapQuote,
|
||||
SmartContractParamsInfo,
|
||||
SwapQuote,
|
||||
SwapQuoteConsumer,
|
||||
SwapQuoteConsumerBase,
|
||||
SwapQuoteConsumerError,
|
||||
SwapQuoteConsumerOpts,
|
||||
} from '../types';
|
||||
@ -25,8 +22,7 @@ import { assetDataUtils } from '../utils/asset_data_utils';
|
||||
import { swapQuoteConsumerUtils } from '../utils/swap_quote_consumer_utils';
|
||||
import { utils } from '../utils/utils';
|
||||
|
||||
export class ForwarderSwapQuoteConsumer
|
||||
implements SwapQuoteConsumer<ForwarderMarketBuySmartContractParams | ForwarderMarketSellSmartContractParams> {
|
||||
export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumerBase<ForwarderSmartContractParams> {
|
||||
public readonly provider: ZeroExProvider;
|
||||
public readonly networkId: number;
|
||||
|
||||
@ -55,34 +51,18 @@ export class ForwarderSwapQuoteConsumer
|
||||
): Promise<CalldataInfo> {
|
||||
assert.isValidForwarderSwapQuote('quote', quote, this._getEtherTokenAssetDataOrThrow());
|
||||
|
||||
const consumableQuote = (quote as any) as (MarketBuySwapQuote | MarketSellSwapQuote);
|
||||
const smartContractParamsInfo = await this.getSmartContractParamsOrThrowAsync(consumableQuote, opts);
|
||||
const { to, methodAbi, ethAmount } = smartContractParamsInfo;
|
||||
const { to, methodAbi, ethAmount, params } = await this.getSmartContractParamsOrThrowAsync(quote, opts);
|
||||
|
||||
const abiEncoder = new AbiEncoder.Method(methodAbi);
|
||||
|
||||
const { orders, signatures, feeOrders, feeSignatures, feePercentage, feeRecipient } = params;
|
||||
|
||||
let args: any[];
|
||||
if (utils.isSwapQuoteMarketBuy(consumableQuote)) {
|
||||
const marketBuyParams = (smartContractParamsInfo.params as any) as ForwarderMarketBuySmartContractParams;
|
||||
args = [
|
||||
marketBuyParams.orders,
|
||||
marketBuyParams.makerAssetFillAmount,
|
||||
marketBuyParams.signatures,
|
||||
marketBuyParams.feeOrders,
|
||||
marketBuyParams.feeSignatures,
|
||||
marketBuyParams.feePercentage,
|
||||
marketBuyParams.feeRecipient,
|
||||
];
|
||||
if (params.type === 'marketBuy') {
|
||||
const { makerAssetFillAmount } = params;
|
||||
args = [orders, makerAssetFillAmount, signatures, feeOrders, feeSignatures, feePercentage, feeRecipient];
|
||||
} else {
|
||||
const marketSellParams = (smartContractParamsInfo.params as any) as ForwarderMarketSellSmartContractParams;
|
||||
args = [
|
||||
marketSellParams.orders,
|
||||
marketSellParams.signatures,
|
||||
marketSellParams.feeOrders,
|
||||
marketSellParams.feeSignatures,
|
||||
marketSellParams.feePercentage,
|
||||
marketSellParams.feeRecipient,
|
||||
];
|
||||
args = [orders, signatures, feeOrders, feeSignatures, feePercentage, feeRecipient];
|
||||
}
|
||||
const calldataHexString = abiEncoder.encode(args);
|
||||
return {
|
||||
@ -101,9 +81,7 @@ export class ForwarderSwapQuoteConsumer
|
||||
public async getSmartContractParamsOrThrowAsync(
|
||||
quote: SwapQuote,
|
||||
opts: Partial<ForwarderSwapQuoteGetOutputOpts>,
|
||||
): Promise<
|
||||
SmartContractParamsInfo<ForwarderMarketBuySmartContractParams | ForwarderMarketSellSmartContractParams>
|
||||
> {
|
||||
): Promise<SmartContractParamsInfo<ForwarderSmartContractParams>> {
|
||||
assert.isValidForwarderSwapQuote('quote', quote, this._getEtherTokenAssetDataOrThrow());
|
||||
|
||||
const { ethAmount, feeRecipient, feePercentage: unFormattedFeePercentage } = _.merge(
|
||||
@ -118,27 +96,20 @@ export class ForwarderSwapQuoteConsumer
|
||||
assert.isBigNumber('ethAmount', ethAmount);
|
||||
}
|
||||
|
||||
const swapQuoteWithAffiliateFee = affiliateFeeUtils.getSwapQuoteWithAffiliateFee(
|
||||
quote,
|
||||
unFormattedFeePercentage,
|
||||
);
|
||||
const quoteWithAffiliateFee = affiliateFeeUtils.getSwapQuoteWithAffiliateFee(quote, unFormattedFeePercentage);
|
||||
|
||||
const consumableQuoteWithAffiliateFee = (swapQuoteWithAffiliateFee as any) as (
|
||||
| MarketBuySwapQuote
|
||||
| MarketSellSwapQuote);
|
||||
|
||||
const { orders, feeOrders, worstCaseQuoteInfo } = swapQuoteWithAffiliateFee;
|
||||
const { orders, feeOrders, worstCaseQuoteInfo } = quoteWithAffiliateFee;
|
||||
|
||||
const signatures = _.map(orders, o => o.signature);
|
||||
const feeSignatures = _.map(feeOrders, o => o.signature);
|
||||
|
||||
const feePercentage = utils.numberPercentageToEtherTokenAmountPercentage(unFormattedFeePercentage);
|
||||
|
||||
let params: ForwarderMarketBuySmartContractParams | ForwarderMarketSellSmartContractParams;
|
||||
let params: ForwarderSmartContractParams;
|
||||
let methodName: string;
|
||||
|
||||
if (utils.isSwapQuoteMarketBuy(consumableQuoteWithAffiliateFee)) {
|
||||
const { makerAssetFillAmount } = consumableQuoteWithAffiliateFee;
|
||||
if (quoteWithAffiliateFee.type === 'marketBuy') {
|
||||
const { makerAssetFillAmount } = quoteWithAffiliateFee;
|
||||
|
||||
params = {
|
||||
orders,
|
||||
@ -148,20 +119,19 @@ export class ForwarderSwapQuoteConsumer
|
||||
feeSignatures,
|
||||
feePercentage,
|
||||
feeRecipient,
|
||||
type: 'marketBuy',
|
||||
};
|
||||
|
||||
methodName = 'marketBuyOrdersWithEth';
|
||||
} else {
|
||||
const { takerAssetFillAmount } = consumableQuoteWithAffiliateFee;
|
||||
|
||||
params = {
|
||||
orders,
|
||||
takerAssetFillAmount,
|
||||
signatures,
|
||||
feeOrders,
|
||||
feeSignatures,
|
||||
feePercentage,
|
||||
feeRecipient,
|
||||
type: 'marketSell',
|
||||
};
|
||||
methodName = 'marketSellOrdersWithEth';
|
||||
}
|
||||
@ -210,20 +180,16 @@ export class ForwarderSwapQuoteConsumer
|
||||
assert.isBigNumber('gasPrice', gasPrice);
|
||||
}
|
||||
|
||||
const swapQuoteWithAffiliateFee = affiliateFeeUtils.getSwapQuoteWithAffiliateFee(quote, feePercentage);
|
||||
const quoteWithAffiliateFee = affiliateFeeUtils.getSwapQuoteWithAffiliateFee(quote, feePercentage);
|
||||
|
||||
const consumableQuoteWithAffiliateFee = (swapQuoteWithAffiliateFee as any) as (
|
||||
| MarketBuySwapQuote
|
||||
| MarketSellSwapQuote);
|
||||
|
||||
const { orders, feeOrders, worstCaseQuoteInfo } = consumableQuoteWithAffiliateFee;
|
||||
const { orders, feeOrders, worstCaseQuoteInfo } = quoteWithAffiliateFee;
|
||||
|
||||
const finalTakerAddress = await swapQuoteConsumerUtils.getTakerAddressOrThrowAsync(this.provider, opts);
|
||||
|
||||
try {
|
||||
let txHash: string;
|
||||
if (utils.isSwapQuoteMarketBuy(consumableQuoteWithAffiliateFee)) {
|
||||
const { makerAssetFillAmount } = consumableQuoteWithAffiliateFee;
|
||||
if (quoteWithAffiliateFee.type === 'marketBuy') {
|
||||
const { makerAssetFillAmount } = quoteWithAffiliateFee;
|
||||
txHash = await this._contractWrappers.forwarder.marketBuyOrdersWithEthAsync(
|
||||
orders,
|
||||
makerAssetFillAmount,
|
||||
|
@ -66,46 +66,48 @@ export interface SmartContractParamsInfo<T> {
|
||||
methodAbi: MethodAbi;
|
||||
}
|
||||
|
||||
export interface SmartContractParamsBase {
|
||||
orders: SignedOrder[];
|
||||
signatures: string[];
|
||||
}
|
||||
|
||||
export type MarketOperation = 'marketBuy' | 'marketSell';
|
||||
|
||||
/**
|
||||
* orders: An array of objects conforming to SignedOrder. These orders can be used to cover the requested assetBuyAmount plus slippage.
|
||||
* makerAssetFillAmount: The amount of makerAsset to swap for.
|
||||
* signatures: An array of signatures that attest that the maker of the orders in fact made the orders.
|
||||
*/
|
||||
export interface ExchangeMarketBuySmartContractParams {
|
||||
orders: SignedOrder[];
|
||||
export interface ExchangeMarketBuySmartContractParams extends SmartContractParamsBase {
|
||||
makerAssetFillAmount: BigNumber;
|
||||
signatures: string[];
|
||||
type: 'marketBuy';
|
||||
}
|
||||
|
||||
export interface ExchangeMarketSellSmartContractParams {
|
||||
orders: SignedOrder[];
|
||||
export interface ExchangeMarketSellSmartContractParams extends SmartContractParamsBase {
|
||||
takerAssetFillAmount: BigNumber;
|
||||
signatures: string[];
|
||||
type: 'marketSell';
|
||||
}
|
||||
|
||||
/**
|
||||
* orders: An array of objects conforming to SignedOrder. These orders can be used to cover the requested assetBuyAmount plus slippage.
|
||||
* makerAssetFillAmount: The amount of makerAsset to swap for.
|
||||
* feeOrders: An array of objects conforming to SignedOrder. These orders can be used to cover the fees for the orders param above.
|
||||
* signatures: An array of signatures that attest that the maker of the orders in fact made the orders.
|
||||
* feeOrders: An array of objects conforming to SignedOrder. These orders can be used to cover the fees for the orders param above.
|
||||
* feeSignatures: An array of signatures that attest that the maker of the fee orders in fact made the orders.
|
||||
* feePercentage: percentage (up to 5%) of the taker asset paid to feeRecipient
|
||||
* feeRecipient: address of the receiver of the feePercentage of taker asset
|
||||
*/
|
||||
export interface ForwarderMarketBuySmartContractParams extends ExchangeMarketBuySmartContractParams {
|
||||
export type ExchangeSmartContractParams = ExchangeMarketBuySmartContractParams | ExchangeMarketSellSmartContractParams;
|
||||
|
||||
export interface ForwarderSmartContractParamsBase {
|
||||
feeOrders: SignedOrder[];
|
||||
feeSignatures: string[];
|
||||
feePercentage: BigNumber;
|
||||
feeRecipient: string;
|
||||
}
|
||||
|
||||
export interface ForwarderMarketSellSmartContractParams extends ExchangeMarketSellSmartContractParams {
|
||||
feeOrders: SignedOrder[];
|
||||
feeSignatures: string[];
|
||||
feePercentage: BigNumber;
|
||||
feeRecipient: string;
|
||||
}
|
||||
export interface ForwarderMarketBuySmartContractParams
|
||||
extends ExchangeMarketBuySmartContractParams,
|
||||
ForwarderSmartContractParamsBase {}
|
||||
|
||||
export interface ForwarderMarketSellSmartContractParams
|
||||
extends Omit<ExchangeMarketSellSmartContractParams, 'takerAssetFillAmount'>,
|
||||
ForwarderSmartContractParamsBase {}
|
||||
|
||||
export type ForwarderSmartContractParams =
|
||||
| ForwarderMarketBuySmartContractParams
|
||||
| ForwarderMarketSellSmartContractParams;
|
||||
|
||||
/**
|
||||
* Interface that varying SwapQuoteConsumers adhere to (exchange consumer, router consumer, forwarder consumer, coordinator consumer)
|
||||
@ -113,7 +115,7 @@ export interface ForwarderMarketSellSmartContractParams extends ExchangeMarketSe
|
||||
* getSmartContractParamsOrThrow: Get SmartContractParamsInfo to swap for tokens with provided SwapQuote. Throws if invalid SwapQuote is provided.
|
||||
* executeSwapQuoteOrThrowAsync: Executes a web3 transaction to swap for tokens with provided SwapQuote. Throws if invalid SwapQuote is provided.
|
||||
*/
|
||||
export interface SwapQuoteConsumer<T> {
|
||||
export interface SwapQuoteConsumerBase<T> {
|
||||
getCalldataOrThrowAsync(quote: SwapQuote, opts: Partial<SwapQuoteGetOutputOpts>): Promise<CalldataInfo>;
|
||||
getSmartContractParamsOrThrowAsync(
|
||||
quote: SwapQuote,
|
||||
@ -161,6 +163,8 @@ export interface ForwarderSwapQuoteGetOutputOpts extends SwapQuoteGetOutputOpts
|
||||
*/
|
||||
export interface ForwarderSwapQuoteExecutionOpts extends ForwarderSwapQuoteGetOutputOpts, SwapQuoteExecutionOpts {}
|
||||
|
||||
export type SwapQuote = MarketBuySwapQuote | MarketSellSwapQuote;
|
||||
|
||||
/**
|
||||
* 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).
|
||||
@ -170,7 +174,7 @@ export interface ForwarderSwapQuoteExecutionOpts extends ForwarderSwapQuoteGetOu
|
||||
* bestCaseQuoteInfo: Info about the best case price for the asset.
|
||||
* worstCaseQuoteInfo: Info about the worst case price for the asset.
|
||||
*/
|
||||
export interface SwapQuote {
|
||||
export interface SwapQuoteBase {
|
||||
takerAssetData: string;
|
||||
makerAssetData: string;
|
||||
orders: SignedOrder[];
|
||||
@ -179,22 +183,25 @@ export interface SwapQuote {
|
||||
worstCaseQuoteInfo: SwapQuoteInfo;
|
||||
}
|
||||
|
||||
export interface MarketSellSwapQuote extends SwapQuote {
|
||||
export interface MarketSellSwapQuote extends SwapQuoteBase {
|
||||
takerAssetFillAmount: BigNumber;
|
||||
bestCaseQuoteInfo: SwapQuoteInfo;
|
||||
worstCaseQuoteInfo: SwapQuoteInfo;
|
||||
type: 'marketSell';
|
||||
}
|
||||
|
||||
export interface MarketBuySwapQuote extends SwapQuote {
|
||||
export interface MarketBuySwapQuote extends SwapQuoteBase {
|
||||
makerAssetFillAmount: BigNumber;
|
||||
bestCaseQuoteInfo: SwapQuoteInfo;
|
||||
worstCaseQuoteInfo: SwapQuoteInfo;
|
||||
type: 'marketBuy';
|
||||
}
|
||||
|
||||
export interface SwapQuoteWithAffiliateFee extends SwapQuote {
|
||||
export interface SwapQuoteWithAffiliateFeeBase {
|
||||
feePercentage: number;
|
||||
}
|
||||
|
||||
export interface MarketSellSwapQuoteWithAffiliateFee extends SwapQuoteWithAffiliateFeeBase, MarketSellSwapQuote {}
|
||||
|
||||
export interface MarketBuySwapQuoteWithAffiliateFee extends SwapQuoteWithAffiliateFeeBase, MarketBuySwapQuote {}
|
||||
|
||||
export type SwapQuoteWithAffiliateFee = MarketBuySwapQuoteWithAffiliateFee | MarketSellSwapQuoteWithAffiliateFee;
|
||||
/**
|
||||
* assetEthAmount: The amount of eth required to pay for the requested asset.
|
||||
* feeEthAmount: The amount of eth required to pay any fee concerned with completing the swap.
|
||||
|
@ -3,8 +3,7 @@ import { schemas } from '@0x/json-schemas';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { OrderProvider, OrderProviderRequest, SwapQuote, SwapQuoteConsumerError, SwapQuoteInfo } from '../types';
|
||||
import { utils } from '../utils/utils';
|
||||
import { OrderProvider, OrderProviderRequest, SwapQuote, SwapQuoteInfo } from '../types';
|
||||
|
||||
export const assert = {
|
||||
...sharedAssert,
|
||||
@ -15,12 +14,10 @@ export const assert = {
|
||||
sharedAssert.doesConformToSchema(`${variableName}.feeOrders`, swapQuote.feeOrders, schemas.signedOrdersSchema);
|
||||
assert.isValidSwapQuoteInfo(`${variableName}.bestCaseQuoteInfo`, swapQuote.bestCaseQuoteInfo);
|
||||
assert.isValidSwapQuoteInfo(`${variableName}.worstCaseQuoteInfo`, swapQuote.worstCaseQuoteInfo);
|
||||
if (utils.isSwapQuoteMarketBuy(swapQuote)) {
|
||||
if (swapQuote.type === 'marketBuy') {
|
||||
sharedAssert.isBigNumber(`${variableName}.makerAssetFillAmount`, swapQuote.makerAssetFillAmount);
|
||||
} else if (utils.isSwapQuoteMarketSell(swapQuote)) {
|
||||
sharedAssert.isBigNumber(`${variableName}.takerAssetFillAmount`, swapQuote.takerAssetFillAmount);
|
||||
} else {
|
||||
throw new Error(SwapQuoteConsumerError.InvalidMarketSellOrMarketBuySwapQuote);
|
||||
sharedAssert.isBigNumber(`${variableName}.takerAssetFillAmount`, swapQuote.takerAssetFillAmount);
|
||||
}
|
||||
},
|
||||
isValidForwarderSwapQuote(variableName: string, swapQuote: SwapQuote, wethAssetData: string): void {
|
||||
|
@ -6,8 +6,10 @@ import { constants } from '../constants';
|
||||
import { InsufficientAssetLiquidityError } from '../errors';
|
||||
import {
|
||||
MarketBuySwapQuote,
|
||||
MarketOperation,
|
||||
MarketSellSwapQuote,
|
||||
OrdersAndFillableAmounts,
|
||||
SwapQuote,
|
||||
SwapQuoteInfo,
|
||||
SwapQuoterError,
|
||||
} from '../types';
|
||||
@ -21,109 +23,14 @@ export const swapQuoteCalculator = {
|
||||
slippagePercentage: number,
|
||||
isMakerAssetZrxToken: boolean,
|
||||
): MarketSellSwapQuote {
|
||||
const orders = ordersAndFillableAmounts.orders;
|
||||
const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts;
|
||||
const remainingFillableTakerAssetAmounts = remainingFillableMakerAssetAmounts.map(
|
||||
(makerAssetAmount: BigNumber, index: number) => {
|
||||
return orderCalculationUtils.getTakerFillAmount(orders[index], makerAssetAmount);
|
||||
},
|
||||
);
|
||||
const feeOrders = feeOrdersAndFillableAmounts.orders;
|
||||
const remainingFillableFeeAmounts = feeOrdersAndFillableAmounts.remainingFillableMakerAssetAmounts;
|
||||
const slippageBufferAmount = takerAssetFillAmount.multipliedBy(slippagePercentage).integerValue();
|
||||
// find the orders that cover the desired assetBuyAmount (with slippage)
|
||||
const {
|
||||
resultOrders,
|
||||
remainingFillAmount,
|
||||
ordersRemainingFillableTakerAssetAmounts,
|
||||
} = marketUtils.findOrdersThatCoverTakerAssetFillAmount(orders, takerAssetFillAmount, {
|
||||
remainingFillableTakerAssetAmounts,
|
||||
slippageBufferAmount,
|
||||
});
|
||||
const ordersRemainingFillableMakerAssetAmounts = _.map(
|
||||
ordersRemainingFillableTakerAssetAmounts,
|
||||
(takerAssetAmount: BigNumber, index: number) => {
|
||||
return orderCalculationUtils.getMakerFillAmount(resultOrders[index], takerAssetAmount);
|
||||
},
|
||||
);
|
||||
// if we do not have enough orders to cover the desired assetBuyAmount, throw
|
||||
if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) {
|
||||
// We needed the amount they requested to buy, plus the amount for slippage
|
||||
const totalAmountRequested = takerAssetFillAmount.plus(slippageBufferAmount);
|
||||
const amountAbleToFill = totalAmountRequested.minus(remainingFillAmount);
|
||||
// multiplierNeededWithSlippage represents what we need to multiply the assetBuyAmount by
|
||||
// in order to get the total amount needed considering slippage
|
||||
// i.e. if slippagePercent was 0.2 (20%), multiplierNeededWithSlippage would be 1.2
|
||||
const multiplierNeededWithSlippage = new BigNumber(1).plus(slippagePercentage);
|
||||
// Given amountAvailableToFillConsideringSlippage * multiplierNeededWithSlippage = amountAbleToFill
|
||||
// We divide amountUnableToFill by multiplierNeededWithSlippage to determine amountAvailableToFillConsideringSlippage
|
||||
const amountAvailableToFillConsideringSlippage = amountAbleToFill
|
||||
.div(multiplierNeededWithSlippage)
|
||||
.integerValue(BigNumber.ROUND_FLOOR);
|
||||
|
||||
throw new InsufficientAssetLiquidityError(amountAvailableToFillConsideringSlippage);
|
||||
}
|
||||
// if we are not buying ZRX:
|
||||
// given the orders calculated above, find the fee-orders that cover the desired assetBuyAmount (with slippage)
|
||||
// TODO(bmillman): optimization
|
||||
// update this logic to find the minimum amount of feeOrders to cover the worst case as opposed to
|
||||
// finding order that cover all fees, this will help with estimating ETH and minimizing gas usage
|
||||
let resultFeeOrders = [] as SignedOrder[];
|
||||
let feeOrdersRemainingFillableMakerAssetAmounts = [] as BigNumber[];
|
||||
if (!isMakerAssetZrxToken) {
|
||||
const feeOrdersAndRemainingFeeAmount = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
|
||||
resultOrders,
|
||||
feeOrders,
|
||||
{
|
||||
remainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts,
|
||||
remainingFillableFeeAmounts,
|
||||
},
|
||||
);
|
||||
// if we do not have enough feeOrders to cover the fees, throw
|
||||
if (feeOrdersAndRemainingFeeAmount.remainingFeeAmount.gt(constants.ZERO_AMOUNT)) {
|
||||
throw new Error(SwapQuoterError.InsufficientZrxLiquidity);
|
||||
}
|
||||
resultFeeOrders = feeOrdersAndRemainingFeeAmount.resultFeeOrders;
|
||||
feeOrdersRemainingFillableMakerAssetAmounts =
|
||||
feeOrdersAndRemainingFeeAmount.feeOrdersRemainingFillableMakerAssetAmounts;
|
||||
}
|
||||
|
||||
// assetData information for the result
|
||||
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
|
||||
const trimmedOrdersAndFillableAmounts: OrdersAndFillableAmounts = {
|
||||
orders: resultOrders,
|
||||
remainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts,
|
||||
};
|
||||
const trimmedFeeOrdersAndFillableAmounts: OrdersAndFillableAmounts = {
|
||||
orders: resultFeeOrders,
|
||||
remainingFillableMakerAssetAmounts: feeOrdersRemainingFillableMakerAssetAmounts,
|
||||
};
|
||||
const bestCaseQuoteInfo = calculateMarketSellQuoteInfo(
|
||||
trimmedOrdersAndFillableAmounts,
|
||||
trimmedFeeOrdersAndFillableAmounts,
|
||||
return calculateSwapQuote(
|
||||
ordersAndFillableAmounts,
|
||||
feeOrdersAndFillableAmounts,
|
||||
takerAssetFillAmount,
|
||||
slippagePercentage,
|
||||
isMakerAssetZrxToken,
|
||||
);
|
||||
// in order to calculate the maxRate, reverse the ordersAndFillableAmounts such that they are sorted from worst rate to best rate
|
||||
const worstCaseQuoteInfo = calculateMarketSellQuoteInfo(
|
||||
reverseOrdersAndFillableAmounts(trimmedOrdersAndFillableAmounts),
|
||||
reverseOrdersAndFillableAmounts(trimmedFeeOrdersAndFillableAmounts),
|
||||
takerAssetFillAmount,
|
||||
isMakerAssetZrxToken,
|
||||
);
|
||||
|
||||
return {
|
||||
takerAssetData,
|
||||
makerAssetData,
|
||||
takerAssetFillAmount,
|
||||
orders: resultOrders,
|
||||
feeOrders: resultFeeOrders,
|
||||
bestCaseQuoteInfo,
|
||||
worstCaseQuoteInfo,
|
||||
};
|
||||
'marketSell',
|
||||
) as MarketSellSwapQuote;
|
||||
},
|
||||
calculateMarketBuySwapQuote(
|
||||
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||
@ -132,120 +39,201 @@ export const swapQuoteCalculator = {
|
||||
slippagePercentage: number,
|
||||
isMakerAssetZrxToken: boolean,
|
||||
): MarketBuySwapQuote {
|
||||
const orders = ordersAndFillableAmounts.orders;
|
||||
const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts;
|
||||
const feeOrders = feeOrdersAndFillableAmounts.orders;
|
||||
const remainingFillableFeeAmounts = feeOrdersAndFillableAmounts.remainingFillableMakerAssetAmounts;
|
||||
const slippageBufferAmount = makerAssetFillAmount.multipliedBy(slippagePercentage).integerValue();
|
||||
// find the orders that cover the desired assetBuyAmount (with slippage)
|
||||
const {
|
||||
resultOrders,
|
||||
remainingFillAmount,
|
||||
ordersRemainingFillableMakerAssetAmounts,
|
||||
} = marketUtils.findOrdersThatCoverMakerAssetFillAmount(orders, makerAssetFillAmount, {
|
||||
remainingFillableMakerAssetAmounts,
|
||||
slippageBufferAmount,
|
||||
});
|
||||
// if we do not have enough orders to cover the desired assetBuyAmount, throw
|
||||
if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) {
|
||||
// We needed the amount they requested to buy, plus the amount for slippage
|
||||
const totalAmountRequested = makerAssetFillAmount.plus(slippageBufferAmount);
|
||||
const amountAbleToFill = totalAmountRequested.minus(remainingFillAmount);
|
||||
// multiplierNeededWithSlippage represents what we need to multiply the assetBuyAmount by
|
||||
// in order to get the total amount needed considering slippage
|
||||
// i.e. if slippagePercent was 0.2 (20%), multiplierNeededWithSlippage would be 1.2
|
||||
const multiplierNeededWithSlippage = new BigNumber(1).plus(slippagePercentage);
|
||||
// Given amountAvailableToFillConsideringSlippage * multiplierNeededWithSlippage = amountAbleToFill
|
||||
// We divide amountUnableToFill by multiplierNeededWithSlippage to determine amountAvailableToFillConsideringSlippage
|
||||
const amountAvailableToFillConsideringSlippage = amountAbleToFill
|
||||
.div(multiplierNeededWithSlippage)
|
||||
.integerValue(BigNumber.ROUND_FLOOR);
|
||||
|
||||
throw new InsufficientAssetLiquidityError(amountAvailableToFillConsideringSlippage);
|
||||
}
|
||||
// if we are not buying ZRX:
|
||||
// given the orders calculated above, find the fee-orders that cover the desired assetBuyAmount (with slippage)
|
||||
// TODO(bmillman): optimization
|
||||
// update this logic to find the minimum amount of feeOrders to cover the worst case as opposed to
|
||||
// finding order that cover all fees, this will help with estimating ETH and minimizing gas usage
|
||||
let resultFeeOrders = [] as SignedOrder[];
|
||||
let feeOrdersRemainingFillableMakerAssetAmounts = [] as BigNumber[];
|
||||
if (!isMakerAssetZrxToken) {
|
||||
const feeOrdersAndRemainingFeeAmount = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
|
||||
resultOrders,
|
||||
feeOrders,
|
||||
{
|
||||
remainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts,
|
||||
remainingFillableFeeAmounts,
|
||||
},
|
||||
);
|
||||
// if we do not have enough feeOrders to cover the fees, throw
|
||||
if (feeOrdersAndRemainingFeeAmount.remainingFeeAmount.gt(constants.ZERO_AMOUNT)) {
|
||||
throw new Error(SwapQuoterError.InsufficientZrxLiquidity);
|
||||
}
|
||||
resultFeeOrders = feeOrdersAndRemainingFeeAmount.resultFeeOrders;
|
||||
feeOrdersRemainingFillableMakerAssetAmounts =
|
||||
feeOrdersAndRemainingFeeAmount.feeOrdersRemainingFillableMakerAssetAmounts;
|
||||
}
|
||||
|
||||
// assetData information for the result
|
||||
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
|
||||
const trimmedOrdersAndFillableAmounts: OrdersAndFillableAmounts = {
|
||||
orders: resultOrders,
|
||||
remainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts,
|
||||
};
|
||||
const trimmedFeeOrdersAndFillableAmounts: OrdersAndFillableAmounts = {
|
||||
orders: resultFeeOrders,
|
||||
remainingFillableMakerAssetAmounts: feeOrdersRemainingFillableMakerAssetAmounts,
|
||||
};
|
||||
const bestCaseQuoteInfo = calculateMarketBuyQuoteInfo(
|
||||
trimmedOrdersAndFillableAmounts,
|
||||
trimmedFeeOrdersAndFillableAmounts,
|
||||
return calculateSwapQuote(
|
||||
ordersAndFillableAmounts,
|
||||
feeOrdersAndFillableAmounts,
|
||||
makerAssetFillAmount,
|
||||
slippagePercentage,
|
||||
isMakerAssetZrxToken,
|
||||
);
|
||||
// in order to calculate the maxRate, reverse the ordersAndFillableAmounts such that they are sorted from worst rate to best rate
|
||||
const worstCaseQuoteInfo = calculateMarketBuyQuoteInfo(
|
||||
reverseOrdersAndFillableAmounts(trimmedOrdersAndFillableAmounts),
|
||||
reverseOrdersAndFillableAmounts(trimmedFeeOrdersAndFillableAmounts),
|
||||
makerAssetFillAmount,
|
||||
isMakerAssetZrxToken,
|
||||
);
|
||||
|
||||
return {
|
||||
takerAssetData,
|
||||
makerAssetData,
|
||||
makerAssetFillAmount,
|
||||
orders: resultOrders,
|
||||
feeOrders: resultFeeOrders,
|
||||
bestCaseQuoteInfo,
|
||||
worstCaseQuoteInfo,
|
||||
};
|
||||
'marketBuy',
|
||||
) as MarketBuySwapQuote;
|
||||
},
|
||||
};
|
||||
|
||||
function calculateMarketBuyQuoteInfo(
|
||||
function calculateSwapQuote(
|
||||
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||
makerTokenAmount: BigNumber,
|
||||
assetFillAmount: BigNumber,
|
||||
slippagePercentage: number,
|
||||
isMakerAssetZrxToken: boolean,
|
||||
marketOperation: MarketOperation,
|
||||
): SwapQuote {
|
||||
const orders = ordersAndFillableAmounts.orders;
|
||||
const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts;
|
||||
const remainingFillableTakerAssetAmounts = remainingFillableMakerAssetAmounts.map(
|
||||
(makerAssetAmount: BigNumber, index: number) => {
|
||||
return orderCalculationUtils.getTakerFillAmount(orders[index], makerAssetAmount);
|
||||
},
|
||||
);
|
||||
const feeOrders = feeOrdersAndFillableAmounts.orders;
|
||||
const remainingFillableFeeAmounts = feeOrdersAndFillableAmounts.remainingFillableMakerAssetAmounts;
|
||||
|
||||
const slippageBufferAmount = assetFillAmount.multipliedBy(slippagePercentage).integerValue();
|
||||
|
||||
let resultOrders: SignedOrder[];
|
||||
let remainingFillAmount: BigNumber;
|
||||
let ordersRemainingFillableMakerAssetAmounts: BigNumber[];
|
||||
|
||||
if (marketOperation === 'marketBuy') {
|
||||
// find the orders that cover the desired assetBuyAmount (with slippage)
|
||||
({
|
||||
resultOrders,
|
||||
remainingFillAmount,
|
||||
ordersRemainingFillableMakerAssetAmounts,
|
||||
} = marketUtils.findOrdersThatCoverMakerAssetFillAmount(orders, assetFillAmount, {
|
||||
remainingFillableMakerAssetAmounts,
|
||||
slippageBufferAmount,
|
||||
}));
|
||||
} else {
|
||||
let ordersRemainingFillableTakerAssetAmounts: BigNumber[];
|
||||
// find the orders that cover the desired assetBuyAmount (with slippage)
|
||||
({
|
||||
resultOrders,
|
||||
remainingFillAmount,
|
||||
ordersRemainingFillableTakerAssetAmounts,
|
||||
} = marketUtils.findOrdersThatCoverTakerAssetFillAmount(orders, assetFillAmount, {
|
||||
remainingFillableTakerAssetAmounts,
|
||||
slippageBufferAmount,
|
||||
}));
|
||||
|
||||
ordersRemainingFillableMakerAssetAmounts = _.map(
|
||||
ordersRemainingFillableTakerAssetAmounts,
|
||||
(takerAssetAmount: BigNumber, index: number) => {
|
||||
return orderCalculationUtils.getMakerFillAmount(resultOrders[index], takerAssetAmount);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// if we do not have enough orders to cover the desired assetBuyAmount, throw
|
||||
if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) {
|
||||
// We needed the amount they requested to buy, plus the amount for slippage
|
||||
const totalAmountRequested = assetFillAmount.plus(slippageBufferAmount);
|
||||
const amountAbleToFill = totalAmountRequested.minus(remainingFillAmount);
|
||||
// multiplierNeededWithSlippage represents what we need to multiply the assetBuyAmount by
|
||||
// in order to get the total amount needed considering slippage
|
||||
// i.e. if slippagePercent was 0.2 (20%), multiplierNeededWithSlippage would be 1.2
|
||||
const multiplierNeededWithSlippage = new BigNumber(1).plus(slippagePercentage);
|
||||
// Given amountAvailableToFillConsideringSlippage * multiplierNeededWithSlippage = amountAbleToFill
|
||||
// We divide amountUnableToFill by multiplierNeededWithSlippage to determine amountAvailableToFillConsideringSlippage
|
||||
const amountAvailableToFillConsideringSlippage = amountAbleToFill
|
||||
.div(multiplierNeededWithSlippage)
|
||||
.integerValue(BigNumber.ROUND_FLOOR);
|
||||
|
||||
throw new InsufficientAssetLiquidityError(amountAvailableToFillConsideringSlippage);
|
||||
}
|
||||
// if we are not buying ZRX:
|
||||
// given the orders calculated above, find the fee-orders that cover the desired assetBuyAmount (with slippage)
|
||||
// TODO(bmillman): optimization
|
||||
// update this logic to find the minimum amount of feeOrders to cover the worst case as opposed to
|
||||
// finding order that cover all fees, this will help with estimating ETH and minimizing gas usage
|
||||
let resultFeeOrders = [] as SignedOrder[];
|
||||
let feeOrdersRemainingFillableMakerAssetAmounts = [] as BigNumber[];
|
||||
if (!isMakerAssetZrxToken) {
|
||||
const feeOrdersAndRemainingFeeAmount = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
|
||||
resultOrders,
|
||||
feeOrders,
|
||||
{
|
||||
remainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts,
|
||||
remainingFillableFeeAmounts,
|
||||
},
|
||||
);
|
||||
// if we do not have enough feeOrders to cover the fees, throw
|
||||
if (feeOrdersAndRemainingFeeAmount.remainingFeeAmount.gt(constants.ZERO_AMOUNT)) {
|
||||
throw new Error(SwapQuoterError.InsufficientZrxLiquidity);
|
||||
}
|
||||
resultFeeOrders = feeOrdersAndRemainingFeeAmount.resultFeeOrders;
|
||||
feeOrdersRemainingFillableMakerAssetAmounts =
|
||||
feeOrdersAndRemainingFeeAmount.feeOrdersRemainingFillableMakerAssetAmounts;
|
||||
}
|
||||
|
||||
// assetData information for the result
|
||||
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
|
||||
const trimmedOrdersAndFillableAmounts: OrdersAndFillableAmounts = {
|
||||
orders: resultOrders,
|
||||
remainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts,
|
||||
};
|
||||
const trimmedFeeOrdersAndFillableAmounts: OrdersAndFillableAmounts = {
|
||||
orders: resultFeeOrders,
|
||||
remainingFillableMakerAssetAmounts: feeOrdersRemainingFillableMakerAssetAmounts,
|
||||
};
|
||||
|
||||
const bestCaseQuoteInfo = calculateQuoteInfo(
|
||||
trimmedOrdersAndFillableAmounts,
|
||||
trimmedFeeOrdersAndFillableAmounts,
|
||||
assetFillAmount,
|
||||
isMakerAssetZrxToken,
|
||||
marketOperation,
|
||||
);
|
||||
// in order to calculate the maxRate, reverse the ordersAndFillableAmounts such that they are sorted from worst rate to best rate
|
||||
const worstCaseQuoteInfo = calculateQuoteInfo(
|
||||
reverseOrdersAndFillableAmounts(trimmedOrdersAndFillableAmounts),
|
||||
reverseOrdersAndFillableAmounts(trimmedFeeOrdersAndFillableAmounts),
|
||||
assetFillAmount,
|
||||
isMakerAssetZrxToken,
|
||||
marketOperation,
|
||||
);
|
||||
|
||||
const quoteBase = {
|
||||
takerAssetData,
|
||||
makerAssetData,
|
||||
orders: resultOrders,
|
||||
feeOrders: resultFeeOrders,
|
||||
bestCaseQuoteInfo,
|
||||
worstCaseQuoteInfo,
|
||||
};
|
||||
|
||||
if (marketOperation === 'marketBuy') {
|
||||
return {
|
||||
...quoteBase,
|
||||
type: 'marketBuy',
|
||||
makerAssetFillAmount: assetFillAmount,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...quoteBase,
|
||||
type: 'marketSell',
|
||||
takerAssetFillAmount: assetFillAmount,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function calculateQuoteInfo(
|
||||
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||
tokenAmount: BigNumber,
|
||||
isMakerAssetZrxToken: boolean,
|
||||
marketOperation: MarketOperation,
|
||||
): SwapQuoteInfo {
|
||||
// find the total eth and zrx needed to buy assetAmount from the resultOrders from left to right
|
||||
let takerTokenAmount = constants.ZERO_AMOUNT;
|
||||
let makerTokenAmount = marketOperation === 'marketBuy' ? tokenAmount : constants.ZERO_AMOUNT;
|
||||
let takerTokenAmount = marketOperation === 'marketSell' ? tokenAmount : constants.ZERO_AMOUNT;
|
||||
let zrxTakerTokenAmount = constants.ZERO_AMOUNT;
|
||||
|
||||
if (isMakerAssetZrxToken) {
|
||||
takerTokenAmount = findTakerTokenAmountNeededToBuyZrx(ordersAndFillableAmounts, makerTokenAmount);
|
||||
if (marketOperation === 'marketBuy') {
|
||||
takerTokenAmount = findTakerTokenAmountNeededToBuyZrx(ordersAndFillableAmounts, makerTokenAmount);
|
||||
} else {
|
||||
makerTokenAmount = findZrxTokenAmountFromSellingTakerTokenAmount(
|
||||
ordersAndFillableAmounts,
|
||||
takerTokenAmount,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const findTokenAndZrxAmount =
|
||||
marketOperation === 'marketBuy'
|
||||
? findTakerTokenAndZrxAmountNeededToBuyAsset
|
||||
: findMakerTokenAmountReceivedAndZrxAmountNeededToSellAsset;
|
||||
// find eth and zrx amounts needed to buy
|
||||
const takerTokenAndZrxAmountToBuyAsset = findTakerTokenAndZrxAmountNeededToBuyAsset(
|
||||
ordersAndFillableAmounts,
|
||||
makerTokenAmount,
|
||||
);
|
||||
takerTokenAmount = takerTokenAndZrxAmountToBuyAsset[0];
|
||||
const zrxAmountToBuyAsset = takerTokenAndZrxAmountToBuyAsset[1];
|
||||
const tokenAndZrxAmountToBuyAsset = findTokenAndZrxAmount(ordersAndFillableAmounts, makerTokenAmount);
|
||||
if (marketOperation === 'marketBuy') {
|
||||
takerTokenAmount = tokenAndZrxAmountToBuyAsset[0];
|
||||
} else {
|
||||
makerTokenAmount = tokenAndZrxAmountToBuyAsset[0];
|
||||
}
|
||||
const zrxAmountToBuyAsset = tokenAndZrxAmountToBuyAsset[1];
|
||||
// find eth amount needed to buy zrx
|
||||
zrxTakerTokenAmount = findTakerTokenAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset);
|
||||
}
|
||||
@ -261,42 +249,6 @@ function calculateMarketBuyQuoteInfo(
|
||||
totalTakerTokenAmount,
|
||||
};
|
||||
}
|
||||
|
||||
function calculateMarketSellQuoteInfo(
|
||||
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||
takerTokenAmount: BigNumber,
|
||||
isMakerAssetZrxToken: boolean,
|
||||
): SwapQuoteInfo {
|
||||
// find the total eth and zrx needed to buy assetAmount from the resultOrders from left to right
|
||||
let makerTokenAmount = constants.ZERO_AMOUNT;
|
||||
let zrxTakerTokenAmount = constants.ZERO_AMOUNT;
|
||||
if (isMakerAssetZrxToken) {
|
||||
makerTokenAmount = findZrxTokenAmountFromSellingTakerTokenAmount(ordersAndFillableAmounts, takerTokenAmount);
|
||||
} else {
|
||||
// find eth and zrx amounts needed to buy
|
||||
const takerTokenAndZrxAmountToBuyAsset = findMakerTokenAmountReceivedAndZrxAmountNeededToSellAsset(
|
||||
ordersAndFillableAmounts,
|
||||
takerTokenAmount,
|
||||
);
|
||||
makerTokenAmount = takerTokenAndZrxAmountToBuyAsset[0];
|
||||
const zrxAmountToSellAsset = takerTokenAndZrxAmountToBuyAsset[1];
|
||||
// find eth amount needed to buy zrx
|
||||
zrxTakerTokenAmount = findTakerTokenAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToSellAsset);
|
||||
}
|
||||
|
||||
const feeTakerTokenAmount = zrxTakerTokenAmount;
|
||||
|
||||
// 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);
|
||||
return {
|
||||
makerTokenAmount,
|
||||
takerTokenAmount,
|
||||
feeTakerTokenAmount,
|
||||
totalTakerTokenAmount,
|
||||
};
|
||||
}
|
||||
|
||||
// given an OrdersAndFillableAmounts, reverse the orders and remainingFillableMakerAssetAmounts properties
|
||||
function reverseOrdersAndFillableAmounts(ordersAndFillableAmounts: OrdersAndFillableAmounts): OrdersAndFillableAmounts {
|
||||
const ordersCopy = _.clone(ordersAndFillableAmounts.orders);
|
||||
|
@ -4,7 +4,6 @@ import { AbiDefinition, ContractAbi, MethodAbi } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { constants } from '../constants';
|
||||
import { MarketBuySwapQuote, MarketSellSwapQuote, SwapQuote } from '../types';
|
||||
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
export const utils = {
|
||||
@ -26,10 +25,4 @@ export const utils = {
|
||||
},
|
||||
) as MethodAbi | undefined;
|
||||
},
|
||||
isSwapQuoteMarketBuy(quote: SwapQuote): quote is MarketBuySwapQuote {
|
||||
return (quote as MarketSellSwapQuote).takerAssetFillAmount !== undefined;
|
||||
},
|
||||
isSwapQuoteMarketSell(quote: SwapQuote): quote is MarketSellSwapQuote {
|
||||
return (quote as MarketBuySwapQuote).makerAssetFillAmount !== undefined;
|
||||
},
|
||||
};
|
||||
|
@ -3,7 +3,7 @@ import { SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { MarketBuySwapQuote, MarketSellSwapQuote } from '../../src';
|
||||
import { MarketOperation, SwapQuote } from '../../src/types';
|
||||
|
||||
const ZERO_BIG_NUMBER = new BigNumber(0);
|
||||
|
||||
@ -25,11 +25,12 @@ export const getSignedOrdersWithNoFees = (
|
||||
);
|
||||
};
|
||||
|
||||
export const getFullyFillableMarketBuySwapQuoteWithNoFees = (
|
||||
export const getFullyFillableSwapQuoteWithNoFees = (
|
||||
makerAssetData: string,
|
||||
takerAssetData: string,
|
||||
orders: SignedOrder[],
|
||||
): MarketBuySwapQuote => {
|
||||
operation: MarketOperation,
|
||||
): SwapQuote => {
|
||||
const makerAssetFillAmount = _.reduce(
|
||||
orders,
|
||||
(a: BigNumber, c: SignedOrder) => a.plus(c.makerAssetAmount),
|
||||
@ -47,46 +48,26 @@ export const getFullyFillableMarketBuySwapQuoteWithNoFees = (
|
||||
totalTakerTokenAmount,
|
||||
};
|
||||
|
||||
return {
|
||||
const quoteBase = {
|
||||
makerAssetData,
|
||||
takerAssetData,
|
||||
orders,
|
||||
feeOrders: [],
|
||||
makerAssetFillAmount,
|
||||
bestCaseQuoteInfo: quoteInfo,
|
||||
worstCaseQuoteInfo: quoteInfo,
|
||||
};
|
||||
};
|
||||
|
||||
export const getFullyFillableMarketSellSwapQuoteWithNoFees = (
|
||||
makerAssetData: string,
|
||||
takerAssetData: string,
|
||||
orders: SignedOrder[],
|
||||
): MarketSellSwapQuote => {
|
||||
const makerAssetFillAmount = _.reduce(
|
||||
orders,
|
||||
(a: BigNumber, c: SignedOrder) => a.plus(c.makerAssetAmount),
|
||||
ZERO_BIG_NUMBER,
|
||||
);
|
||||
const totalTakerTokenAmount = _.reduce(
|
||||
orders,
|
||||
(a: BigNumber, c: SignedOrder) => a.plus(c.takerAssetAmount),
|
||||
ZERO_BIG_NUMBER,
|
||||
);
|
||||
const quoteInfo = {
|
||||
makerTokenAmount: makerAssetFillAmount,
|
||||
takerTokenAmount: totalTakerTokenAmount,
|
||||
feeTakerTokenAmount: ZERO_BIG_NUMBER,
|
||||
totalTakerTokenAmount,
|
||||
};
|
||||
|
||||
return {
|
||||
makerAssetData,
|
||||
takerAssetData,
|
||||
orders,
|
||||
feeOrders: [],
|
||||
takerAssetFillAmount: totalTakerTokenAmount,
|
||||
bestCaseQuoteInfo: quoteInfo,
|
||||
worstCaseQuoteInfo: quoteInfo,
|
||||
};
|
||||
if (operation === 'marketBuy') {
|
||||
return {
|
||||
...quoteBase,
|
||||
type: 'marketBuy',
|
||||
makerAssetFillAmount,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...quoteBase,
|
||||
type: 'marketSell',
|
||||
takerAssetFillAmount: totalTakerTokenAmount,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
FindFeeOrdersThatCoverFeesForTargetOrdersOpts,
|
||||
FindOrdersThatCoverMakerAssetFillAmountOpts,
|
||||
FindOrdersThatCoverTakerAssetFillAmountOpts,
|
||||
MarketOperation,
|
||||
OrdersAndRemainingMakerFillAmount,
|
||||
OrdersAndRemainingTakerFillAmount,
|
||||
} from './types';
|
||||
@ -20,60 +21,12 @@ export const marketUtils = {
|
||||
takerAssetFillAmount: BigNumber,
|
||||
opts?: FindOrdersThatCoverTakerAssetFillAmountOpts,
|
||||
): OrdersAndRemainingTakerFillAmount<T> {
|
||||
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
|
||||
assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount);
|
||||
// try to get remainingFillableTakerAssetAmounts from opts, if it's not there, use takerAssetAmount values from orders
|
||||
const remainingFillableTakerAssetAmounts = _.get(
|
||||
opts,
|
||||
'remainingFillableTakerAssetAmounts',
|
||||
_.map(orders, order => order.takerAssetAmount),
|
||||
) as BigNumber[];
|
||||
_.forEach(remainingFillableTakerAssetAmounts, (amount, index) =>
|
||||
assert.isValidBaseUnitAmount(`remainingFillableTakerAssetAmount[${index}]`, amount),
|
||||
);
|
||||
assert.assert(
|
||||
orders.length === remainingFillableTakerAssetAmounts.length,
|
||||
'Expected orders.length to equal opts.remainingFillableMakerAssetAmounts.length',
|
||||
);
|
||||
// try to get slippageBufferAmount from opts, if it's not there, default to 0
|
||||
const slippageBufferAmount = _.get(opts, 'slippageBufferAmount', constants.ZERO_AMOUNT) as BigNumber;
|
||||
assert.isValidBaseUnitAmount('opts.slippageBufferAmount', slippageBufferAmount);
|
||||
// calculate total amount of makerAsset needed to be filled
|
||||
const totalFillAmount = takerAssetFillAmount.plus(slippageBufferAmount);
|
||||
// iterate through the orders input from left to right until we have enough makerAsset to fill totalFillAmount
|
||||
const result = _.reduce(
|
||||
return findOrdersThatCoverAssetFillAmount(
|
||||
orders,
|
||||
({ resultOrders, remainingFillAmount, ordersRemainingFillableTakerAssetAmounts }, order, index) => {
|
||||
if (remainingFillAmount.isLessThanOrEqualTo(constants.ZERO_AMOUNT)) {
|
||||
return {
|
||||
resultOrders,
|
||||
remainingFillAmount: constants.ZERO_AMOUNT,
|
||||
ordersRemainingFillableTakerAssetAmounts,
|
||||
};
|
||||
} else {
|
||||
const takerAssetAmountAvailable = remainingFillableTakerAssetAmounts[index];
|
||||
const shouldIncludeOrder = takerAssetAmountAvailable.gt(constants.ZERO_AMOUNT);
|
||||
// if there is no makerAssetAmountAvailable do not append order to resultOrders
|
||||
// if we have exceeded the total amount we want to fill set remainingFillAmount to 0
|
||||
return {
|
||||
resultOrders: shouldIncludeOrder ? _.concat(resultOrders, order) : resultOrders,
|
||||
ordersRemainingFillableTakerAssetAmounts: shouldIncludeOrder
|
||||
? _.concat(ordersRemainingFillableTakerAssetAmounts, takerAssetAmountAvailable)
|
||||
: ordersRemainingFillableTakerAssetAmounts,
|
||||
remainingFillAmount: BigNumber.max(
|
||||
constants.ZERO_AMOUNT,
|
||||
remainingFillAmount.minus(takerAssetAmountAvailable),
|
||||
),
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
resultOrders: [] as T[],
|
||||
remainingFillAmount: totalFillAmount,
|
||||
ordersRemainingFillableTakerAssetAmounts: [] as BigNumber[],
|
||||
},
|
||||
);
|
||||
return result;
|
||||
takerAssetFillAmount,
|
||||
'marketSell',
|
||||
opts,
|
||||
) as OrdersAndRemainingTakerFillAmount<T>;
|
||||
},
|
||||
/**
|
||||
* Takes an array of orders and returns a subset of those orders that has enough makerAssetAmount
|
||||
@ -90,60 +43,12 @@ export const marketUtils = {
|
||||
makerAssetFillAmount: BigNumber,
|
||||
opts?: FindOrdersThatCoverMakerAssetFillAmountOpts,
|
||||
): OrdersAndRemainingMakerFillAmount<T> {
|
||||
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
|
||||
assert.isValidBaseUnitAmount('makerAssetFillAmount', makerAssetFillAmount);
|
||||
// try to get remainingFillableMakerAssetAmounts from opts, if it's not there, use makerAssetAmount values from orders
|
||||
const remainingFillableMakerAssetAmounts = _.get(
|
||||
opts,
|
||||
'remainingFillableMakerAssetAmounts',
|
||||
_.map(orders, order => order.makerAssetAmount),
|
||||
) as BigNumber[];
|
||||
_.forEach(remainingFillableMakerAssetAmounts, (amount, index) =>
|
||||
assert.isValidBaseUnitAmount(`remainingFillableMakerAssetAmount[${index}]`, amount),
|
||||
);
|
||||
assert.assert(
|
||||
orders.length === remainingFillableMakerAssetAmounts.length,
|
||||
'Expected orders.length to equal opts.remainingFillableMakerAssetAmounts.length',
|
||||
);
|
||||
// try to get slippageBufferAmount from opts, if it's not there, default to 0
|
||||
const slippageBufferAmount = _.get(opts, 'slippageBufferAmount', constants.ZERO_AMOUNT) as BigNumber;
|
||||
assert.isValidBaseUnitAmount('opts.slippageBufferAmount', slippageBufferAmount);
|
||||
// calculate total amount of makerAsset needed to be filled
|
||||
const totalFillAmount = makerAssetFillAmount.plus(slippageBufferAmount);
|
||||
// iterate through the orders input from left to right until we have enough makerAsset to fill totalFillAmount
|
||||
const result = _.reduce(
|
||||
return findOrdersThatCoverAssetFillAmount(
|
||||
orders,
|
||||
({ resultOrders, remainingFillAmount, ordersRemainingFillableMakerAssetAmounts }, order, index) => {
|
||||
if (remainingFillAmount.isLessThanOrEqualTo(constants.ZERO_AMOUNT)) {
|
||||
return {
|
||||
resultOrders,
|
||||
remainingFillAmount: constants.ZERO_AMOUNT,
|
||||
ordersRemainingFillableMakerAssetAmounts,
|
||||
};
|
||||
} else {
|
||||
const makerAssetAmountAvailable = remainingFillableMakerAssetAmounts[index];
|
||||
const shouldIncludeOrder = makerAssetAmountAvailable.gt(constants.ZERO_AMOUNT);
|
||||
// if there is no makerAssetAmountAvailable do not append order to resultOrders
|
||||
// if we have exceeded the total amount we want to fill set remainingFillAmount to 0
|
||||
return {
|
||||
resultOrders: shouldIncludeOrder ? _.concat(resultOrders, order) : resultOrders,
|
||||
ordersRemainingFillableMakerAssetAmounts: shouldIncludeOrder
|
||||
? _.concat(ordersRemainingFillableMakerAssetAmounts, makerAssetAmountAvailable)
|
||||
: ordersRemainingFillableMakerAssetAmounts,
|
||||
remainingFillAmount: BigNumber.max(
|
||||
constants.ZERO_AMOUNT,
|
||||
remainingFillAmount.minus(makerAssetAmountAvailable),
|
||||
),
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
resultOrders: [] as T[],
|
||||
remainingFillAmount: totalFillAmount,
|
||||
ordersRemainingFillableMakerAssetAmounts: [] as BigNumber[],
|
||||
},
|
||||
);
|
||||
return result;
|
||||
makerAssetFillAmount,
|
||||
'marketBuy',
|
||||
opts,
|
||||
) as OrdersAndRemainingMakerFillAmount<T>;
|
||||
},
|
||||
/**
|
||||
* Takes an array of orders and an array of feeOrders. Returns a subset of the feeOrders that has enough ZRX
|
||||
@ -222,3 +127,82 @@ export const marketUtils = {
|
||||
// https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarding-contract-specification.md#over-buying-zrx
|
||||
},
|
||||
};
|
||||
|
||||
function findOrdersThatCoverAssetFillAmount<T extends Order>(
|
||||
orders: T[],
|
||||
assetFillAmount: BigNumber,
|
||||
operation: MarketOperation,
|
||||
opts?: FindOrdersThatCoverTakerAssetFillAmountOpts,
|
||||
): OrdersAndRemainingTakerFillAmount<T> | OrdersAndRemainingMakerFillAmount<T> {
|
||||
const variablePrefix = operation === 'marketBuy' ? 'maker' : 'taker';
|
||||
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
|
||||
assert.isValidBaseUnitAmount(`${variablePrefix}AssetFillAmount}`, assetFillAmount);
|
||||
// try to get remainingFillableTakerAssetAmounts from opts, if it's not there, use takerAssetAmount values from orders
|
||||
const remainingFillableAssetAmounts = _.get(
|
||||
opts,
|
||||
`remainingFillable${variablePrefix}AssetAmounts`,
|
||||
_.map(orders, order => (operation === 'marketBuy' ? order.makerAssetAmount : order.takerAssetAmount)),
|
||||
) as BigNumber[];
|
||||
_.forEach(remainingFillableAssetAmounts, (amount, index) =>
|
||||
assert.isValidBaseUnitAmount(`remainingFillable${variablePrefix}AssetAmount[${index}]`, amount),
|
||||
);
|
||||
assert.assert(
|
||||
orders.length === remainingFillableAssetAmounts.length,
|
||||
`Expected orders.length to equal opts.remainingFillable${variablePrefix}AssetAmounts.length`,
|
||||
);
|
||||
// try to get slippageBufferAmount from opts, if it's not there, default to 0
|
||||
const slippageBufferAmount = _.get(opts, 'slippageBufferAmount', constants.ZERO_AMOUNT) as BigNumber;
|
||||
assert.isValidBaseUnitAmount('opts.slippageBufferAmount', slippageBufferAmount);
|
||||
// calculate total amount of asset needed to be filled
|
||||
const totalFillAmount = assetFillAmount.plus(slippageBufferAmount);
|
||||
// iterate through the orders input from left to right until we have enough makerAsset to fill totalFillAmount
|
||||
const result = _.reduce(
|
||||
orders,
|
||||
({ resultOrders, remainingFillAmount, ordersRemainingFillableAssetAmounts }, order, index) => {
|
||||
if (remainingFillAmount.isLessThanOrEqualTo(constants.ZERO_AMOUNT)) {
|
||||
return {
|
||||
resultOrders,
|
||||
remainingFillAmount: constants.ZERO_AMOUNT,
|
||||
ordersRemainingFillableAssetAmounts,
|
||||
};
|
||||
} else {
|
||||
const assetAmountAvailable = remainingFillableAssetAmounts[index];
|
||||
const shouldIncludeOrder = assetAmountAvailable.gt(constants.ZERO_AMOUNT);
|
||||
// if there is no assetAmountAvailable do not append order to resultOrders
|
||||
// if we have exceeded the total amount we want to fill set remainingFillAmount to 0
|
||||
return {
|
||||
resultOrders: shouldIncludeOrder ? _.concat(resultOrders, order) : resultOrders,
|
||||
ordersRemainingFillableAssetAmounts: shouldIncludeOrder
|
||||
? _.concat(ordersRemainingFillableAssetAmounts, assetAmountAvailable)
|
||||
: ordersRemainingFillableAssetAmounts,
|
||||
remainingFillAmount: BigNumber.max(
|
||||
constants.ZERO_AMOUNT,
|
||||
remainingFillAmount.minus(assetAmountAvailable),
|
||||
),
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
resultOrders: [] as T[],
|
||||
remainingFillAmount: totalFillAmount,
|
||||
ordersRemainingFillableAssetAmounts: [] as BigNumber[],
|
||||
},
|
||||
);
|
||||
|
||||
const {
|
||||
ordersRemainingFillableAssetAmounts: resultOrdersRemainingFillableAssetAmounts,
|
||||
...ordersAndRemainingFillAmount
|
||||
} = result;
|
||||
|
||||
if (operation === 'marketBuy') {
|
||||
return {
|
||||
...ordersAndRemainingFillAmount,
|
||||
ordersRemainingFillableMakerAssetAmounts: resultOrdersRemainingFillableAssetAmounts,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...ordersAndRemainingFillAmount,
|
||||
ordersRemainingFillableMakerAssetAmounts: resultOrdersRemainingFillableAssetAmounts,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,8 @@ export interface FindOrdersThatCoverTakerAssetFillAmountOpts {
|
||||
slippageBufferAmount?: BigNumber;
|
||||
}
|
||||
|
||||
export type MarketOperation = 'marketSell' | 'marketBuy';
|
||||
|
||||
/**
|
||||
* remainingFillableMakerAssetAmount: An array of BigNumbers corresponding to the `orders` parameter.
|
||||
* You can use `OrderStateUtils` `@0x/order-utils` to perform blockchain lookups for these values.
|
||||
|
Loading…
x
Reference in New Issue
Block a user