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 { constants } from '../constants';
|
||||||
import {
|
import {
|
||||||
CalldataInfo,
|
CalldataInfo,
|
||||||
ExchangeMarketBuySmartContractParams,
|
ExchangeSmartContractParams,
|
||||||
ExchangeMarketSellSmartContractParams,
|
|
||||||
MarketBuySwapQuote,
|
|
||||||
MarketSellSwapQuote,
|
|
||||||
SmartContractParamsInfo,
|
SmartContractParamsInfo,
|
||||||
SwapQuote,
|
SwapQuote,
|
||||||
SwapQuoteConsumer,
|
SwapQuoteConsumerBase,
|
||||||
SwapQuoteConsumerError,
|
SwapQuoteConsumerError,
|
||||||
SwapQuoteConsumerOpts,
|
SwapQuoteConsumerOpts,
|
||||||
SwapQuoteExecutionOpts,
|
SwapQuoteExecutionOpts,
|
||||||
@ -23,8 +20,7 @@ import { assert } from '../utils/assert';
|
|||||||
import { swapQuoteConsumerUtils } from '../utils/swap_quote_consumer_utils';
|
import { swapQuoteConsumerUtils } from '../utils/swap_quote_consumer_utils';
|
||||||
import { utils } from '../utils/utils';
|
import { utils } from '../utils/utils';
|
||||||
|
|
||||||
export class ExchangeSwapQuoteConsumer
|
export class ExchangeSwapQuoteConsumer implements SwapQuoteConsumerBase<ExchangeSmartContractParams> {
|
||||||
implements SwapQuoteConsumer<ExchangeMarketBuySmartContractParams | ExchangeMarketSellSmartContractParams> {
|
|
||||||
public readonly provider: ZeroExProvider;
|
public readonly provider: ZeroExProvider;
|
||||||
public readonly networkId: number;
|
public readonly networkId: number;
|
||||||
|
|
||||||
@ -48,19 +44,18 @@ export class ExchangeSwapQuoteConsumer
|
|||||||
): Promise<CalldataInfo> {
|
): Promise<CalldataInfo> {
|
||||||
assert.isValidSwapQuote('quote', quote);
|
assert.isValidSwapQuote('quote', quote);
|
||||||
|
|
||||||
const consumableQuote = (quote as any) as (MarketBuySwapQuote | MarketSellSwapQuote);
|
const { to, methodAbi, ethAmount, params } = await this.getSmartContractParamsOrThrowAsync(quote, opts);
|
||||||
const smartContractParamsInfo = await this.getSmartContractParamsOrThrowAsync(consumableQuote, opts);
|
|
||||||
const { to, methodAbi, ethAmount } = smartContractParamsInfo;
|
|
||||||
|
|
||||||
const abiEncoder = new AbiEncoder.Method(methodAbi);
|
const abiEncoder = new AbiEncoder.Method(methodAbi);
|
||||||
|
|
||||||
|
const { orders, signatures } = params;
|
||||||
let args: any[];
|
let args: any[];
|
||||||
if (utils.isSwapQuoteMarketBuy(consumableQuote)) {
|
if (params.type === 'marketBuy') {
|
||||||
const marketBuyParams = (smartContractParamsInfo.params as any) as ExchangeMarketBuySmartContractParams;
|
const { makerAssetFillAmount } = params;
|
||||||
args = [marketBuyParams.orders, marketBuyParams.makerAssetFillAmount, marketBuyParams.signatures];
|
args = [orders, makerAssetFillAmount, signatures];
|
||||||
} else {
|
} else {
|
||||||
const marketSellParams = (smartContractParamsInfo.params as any) as ExchangeMarketSellSmartContractParams;
|
const { takerAssetFillAmount } = params;
|
||||||
args = [marketSellParams.orders, marketSellParams.takerAssetFillAmount, marketSellParams.signatures];
|
args = [orders, takerAssetFillAmount, signatures];
|
||||||
}
|
}
|
||||||
const calldataHexString = abiEncoder.encode(args);
|
const calldataHexString = abiEncoder.encode(args);
|
||||||
return {
|
return {
|
||||||
@ -73,36 +68,36 @@ export class ExchangeSwapQuoteConsumer
|
|||||||
|
|
||||||
public async getSmartContractParamsOrThrowAsync(
|
public async getSmartContractParamsOrThrowAsync(
|
||||||
quote: SwapQuote,
|
quote: SwapQuote,
|
||||||
opts: Partial<SwapQuoteGetOutputOpts>,
|
_opts: Partial<SwapQuoteGetOutputOpts>,
|
||||||
): Promise<SmartContractParamsInfo<ExchangeMarketBuySmartContractParams | ExchangeMarketSellSmartContractParams>> {
|
): Promise<SmartContractParamsInfo<ExchangeSmartContractParams>> {
|
||||||
assert.isValidSwapQuote('quote', quote);
|
assert.isValidSwapQuote('quote', quote);
|
||||||
|
|
||||||
const consumableQuote = (quote as any) as (MarketBuySwapQuote | MarketSellSwapQuote);
|
const { orders } = quote;
|
||||||
|
|
||||||
const { orders } = consumableQuote;
|
|
||||||
|
|
||||||
const signatures = _.map(orders, o => o.signature);
|
const signatures = _.map(orders, o => o.signature);
|
||||||
|
|
||||||
let params: ExchangeMarketBuySmartContractParams | ExchangeMarketSellSmartContractParams;
|
let params: ExchangeSmartContractParams;
|
||||||
let methodName: string;
|
let methodName: string;
|
||||||
|
|
||||||
if (utils.isSwapQuoteMarketBuy(consumableQuote)) {
|
if (quote.type === 'marketBuy') {
|
||||||
const { makerAssetFillAmount } = consumableQuote;
|
const { makerAssetFillAmount } = quote;
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
makerAssetFillAmount,
|
makerAssetFillAmount,
|
||||||
|
type: 'marketBuy',
|
||||||
};
|
};
|
||||||
|
|
||||||
methodName = 'marketBuyOrders';
|
methodName = 'marketBuyOrders';
|
||||||
} else {
|
} else {
|
||||||
const { takerAssetFillAmount } = consumableQuote;
|
const { takerAssetFillAmount } = quote;
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
orders,
|
orders,
|
||||||
signatures,
|
signatures,
|
||||||
takerAssetFillAmount,
|
takerAssetFillAmount,
|
||||||
|
type: 'marketSell',
|
||||||
};
|
};
|
||||||
|
|
||||||
methodName = 'marketSellOrders';
|
methodName = 'marketSellOrders';
|
||||||
@ -138,16 +133,14 @@ export class ExchangeSwapQuoteConsumer
|
|||||||
assert.isBigNumber('gasPrice', gasPrice);
|
assert.isBigNumber('gasPrice', gasPrice);
|
||||||
}
|
}
|
||||||
|
|
||||||
const consumableQuote = (quote as any) as (MarketBuySwapQuote | MarketSellSwapQuote);
|
const { orders } = quote;
|
||||||
|
|
||||||
const { orders } = consumableQuote;
|
|
||||||
|
|
||||||
const finalTakerAddress = await swapQuoteConsumerUtils.getTakerAddressOrThrowAsync(this.provider, opts);
|
const finalTakerAddress = await swapQuoteConsumerUtils.getTakerAddressOrThrowAsync(this.provider, opts);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let txHash: string;
|
let txHash: string;
|
||||||
if (utils.isSwapQuoteMarketBuy(consumableQuote)) {
|
if (quote.type === 'marketBuy') {
|
||||||
const { makerAssetFillAmount } = consumableQuote;
|
const { makerAssetFillAmount } = quote;
|
||||||
txHash = await this._contractWrappers.exchange.marketBuyOrdersNoThrowAsync(
|
txHash = await this._contractWrappers.exchange.marketBuyOrdersNoThrowAsync(
|
||||||
orders,
|
orders,
|
||||||
makerAssetFillAmount,
|
makerAssetFillAmount,
|
||||||
@ -159,7 +152,7 @@ export class ExchangeSwapQuoteConsumer
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const { takerAssetFillAmount } = consumableQuote;
|
const { takerAssetFillAmount } = quote;
|
||||||
txHash = await this._contractWrappers.exchange.marketSellOrdersNoThrowAsync(
|
txHash = await this._contractWrappers.exchange.marketSellOrdersNoThrowAsync(
|
||||||
orders,
|
orders,
|
||||||
takerAssetFillAmount,
|
takerAssetFillAmount,
|
||||||
|
@ -7,15 +7,12 @@ import * as _ from 'lodash';
|
|||||||
import { constants } from '../constants';
|
import { constants } from '../constants';
|
||||||
import {
|
import {
|
||||||
CalldataInfo,
|
CalldataInfo,
|
||||||
ForwarderMarketBuySmartContractParams,
|
ForwarderSmartContractParams,
|
||||||
ForwarderMarketSellSmartContractParams,
|
|
||||||
ForwarderSwapQuoteExecutionOpts,
|
ForwarderSwapQuoteExecutionOpts,
|
||||||
ForwarderSwapQuoteGetOutputOpts,
|
ForwarderSwapQuoteGetOutputOpts,
|
||||||
MarketBuySwapQuote,
|
|
||||||
MarketSellSwapQuote,
|
|
||||||
SmartContractParamsInfo,
|
SmartContractParamsInfo,
|
||||||
SwapQuote,
|
SwapQuote,
|
||||||
SwapQuoteConsumer,
|
SwapQuoteConsumerBase,
|
||||||
SwapQuoteConsumerError,
|
SwapQuoteConsumerError,
|
||||||
SwapQuoteConsumerOpts,
|
SwapQuoteConsumerOpts,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
@ -25,8 +22,7 @@ import { assetDataUtils } from '../utils/asset_data_utils';
|
|||||||
import { swapQuoteConsumerUtils } from '../utils/swap_quote_consumer_utils';
|
import { swapQuoteConsumerUtils } from '../utils/swap_quote_consumer_utils';
|
||||||
import { utils } from '../utils/utils';
|
import { utils } from '../utils/utils';
|
||||||
|
|
||||||
export class ForwarderSwapQuoteConsumer
|
export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumerBase<ForwarderSmartContractParams> {
|
||||||
implements SwapQuoteConsumer<ForwarderMarketBuySmartContractParams | ForwarderMarketSellSmartContractParams> {
|
|
||||||
public readonly provider: ZeroExProvider;
|
public readonly provider: ZeroExProvider;
|
||||||
public readonly networkId: number;
|
public readonly networkId: number;
|
||||||
|
|
||||||
@ -55,34 +51,18 @@ export class ForwarderSwapQuoteConsumer
|
|||||||
): Promise<CalldataInfo> {
|
): Promise<CalldataInfo> {
|
||||||
assert.isValidForwarderSwapQuote('quote', quote, this._getEtherTokenAssetDataOrThrow());
|
assert.isValidForwarderSwapQuote('quote', quote, this._getEtherTokenAssetDataOrThrow());
|
||||||
|
|
||||||
const consumableQuote = (quote as any) as (MarketBuySwapQuote | MarketSellSwapQuote);
|
const { to, methodAbi, ethAmount, params } = await this.getSmartContractParamsOrThrowAsync(quote, opts);
|
||||||
const smartContractParamsInfo = await this.getSmartContractParamsOrThrowAsync(consumableQuote, opts);
|
|
||||||
const { to, methodAbi, ethAmount } = smartContractParamsInfo;
|
|
||||||
|
|
||||||
const abiEncoder = new AbiEncoder.Method(methodAbi);
|
const abiEncoder = new AbiEncoder.Method(methodAbi);
|
||||||
|
|
||||||
|
const { orders, signatures, feeOrders, feeSignatures, feePercentage, feeRecipient } = params;
|
||||||
|
|
||||||
let args: any[];
|
let args: any[];
|
||||||
if (utils.isSwapQuoteMarketBuy(consumableQuote)) {
|
if (params.type === 'marketBuy') {
|
||||||
const marketBuyParams = (smartContractParamsInfo.params as any) as ForwarderMarketBuySmartContractParams;
|
const { makerAssetFillAmount } = params;
|
||||||
args = [
|
args = [orders, makerAssetFillAmount, signatures, feeOrders, feeSignatures, feePercentage, feeRecipient];
|
||||||
marketBuyParams.orders,
|
|
||||||
marketBuyParams.makerAssetFillAmount,
|
|
||||||
marketBuyParams.signatures,
|
|
||||||
marketBuyParams.feeOrders,
|
|
||||||
marketBuyParams.feeSignatures,
|
|
||||||
marketBuyParams.feePercentage,
|
|
||||||
marketBuyParams.feeRecipient,
|
|
||||||
];
|
|
||||||
} else {
|
} else {
|
||||||
const marketSellParams = (smartContractParamsInfo.params as any) as ForwarderMarketSellSmartContractParams;
|
args = [orders, signatures, feeOrders, feeSignatures, feePercentage, feeRecipient];
|
||||||
args = [
|
|
||||||
marketSellParams.orders,
|
|
||||||
marketSellParams.signatures,
|
|
||||||
marketSellParams.feeOrders,
|
|
||||||
marketSellParams.feeSignatures,
|
|
||||||
marketSellParams.feePercentage,
|
|
||||||
marketSellParams.feeRecipient,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
const calldataHexString = abiEncoder.encode(args);
|
const calldataHexString = abiEncoder.encode(args);
|
||||||
return {
|
return {
|
||||||
@ -101,9 +81,7 @@ export class ForwarderSwapQuoteConsumer
|
|||||||
public async getSmartContractParamsOrThrowAsync(
|
public async getSmartContractParamsOrThrowAsync(
|
||||||
quote: SwapQuote,
|
quote: SwapQuote,
|
||||||
opts: Partial<ForwarderSwapQuoteGetOutputOpts>,
|
opts: Partial<ForwarderSwapQuoteGetOutputOpts>,
|
||||||
): Promise<
|
): Promise<SmartContractParamsInfo<ForwarderSmartContractParams>> {
|
||||||
SmartContractParamsInfo<ForwarderMarketBuySmartContractParams | ForwarderMarketSellSmartContractParams>
|
|
||||||
> {
|
|
||||||
assert.isValidForwarderSwapQuote('quote', quote, this._getEtherTokenAssetDataOrThrow());
|
assert.isValidForwarderSwapQuote('quote', quote, this._getEtherTokenAssetDataOrThrow());
|
||||||
|
|
||||||
const { ethAmount, feeRecipient, feePercentage: unFormattedFeePercentage } = _.merge(
|
const { ethAmount, feeRecipient, feePercentage: unFormattedFeePercentage } = _.merge(
|
||||||
@ -118,27 +96,20 @@ export class ForwarderSwapQuoteConsumer
|
|||||||
assert.isBigNumber('ethAmount', ethAmount);
|
assert.isBigNumber('ethAmount', ethAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
const swapQuoteWithAffiliateFee = affiliateFeeUtils.getSwapQuoteWithAffiliateFee(
|
const quoteWithAffiliateFee = affiliateFeeUtils.getSwapQuoteWithAffiliateFee(quote, unFormattedFeePercentage);
|
||||||
quote,
|
|
||||||
unFormattedFeePercentage,
|
|
||||||
);
|
|
||||||
|
|
||||||
const consumableQuoteWithAffiliateFee = (swapQuoteWithAffiliateFee as any) as (
|
const { orders, feeOrders, worstCaseQuoteInfo } = quoteWithAffiliateFee;
|
||||||
| MarketBuySwapQuote
|
|
||||||
| MarketSellSwapQuote);
|
|
||||||
|
|
||||||
const { orders, feeOrders, worstCaseQuoteInfo } = swapQuoteWithAffiliateFee;
|
|
||||||
|
|
||||||
const signatures = _.map(orders, o => o.signature);
|
const signatures = _.map(orders, o => o.signature);
|
||||||
const feeSignatures = _.map(feeOrders, o => o.signature);
|
const feeSignatures = _.map(feeOrders, o => o.signature);
|
||||||
|
|
||||||
const feePercentage = utils.numberPercentageToEtherTokenAmountPercentage(unFormattedFeePercentage);
|
const feePercentage = utils.numberPercentageToEtherTokenAmountPercentage(unFormattedFeePercentage);
|
||||||
|
|
||||||
let params: ForwarderMarketBuySmartContractParams | ForwarderMarketSellSmartContractParams;
|
let params: ForwarderSmartContractParams;
|
||||||
let methodName: string;
|
let methodName: string;
|
||||||
|
|
||||||
if (utils.isSwapQuoteMarketBuy(consumableQuoteWithAffiliateFee)) {
|
if (quoteWithAffiliateFee.type === 'marketBuy') {
|
||||||
const { makerAssetFillAmount } = consumableQuoteWithAffiliateFee;
|
const { makerAssetFillAmount } = quoteWithAffiliateFee;
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
orders,
|
orders,
|
||||||
@ -148,20 +119,19 @@ export class ForwarderSwapQuoteConsumer
|
|||||||
feeSignatures,
|
feeSignatures,
|
||||||
feePercentage,
|
feePercentage,
|
||||||
feeRecipient,
|
feeRecipient,
|
||||||
|
type: 'marketBuy',
|
||||||
};
|
};
|
||||||
|
|
||||||
methodName = 'marketBuyOrdersWithEth';
|
methodName = 'marketBuyOrdersWithEth';
|
||||||
} else {
|
} else {
|
||||||
const { takerAssetFillAmount } = consumableQuoteWithAffiliateFee;
|
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
orders,
|
orders,
|
||||||
takerAssetFillAmount,
|
|
||||||
signatures,
|
signatures,
|
||||||
feeOrders,
|
feeOrders,
|
||||||
feeSignatures,
|
feeSignatures,
|
||||||
feePercentage,
|
feePercentage,
|
||||||
feeRecipient,
|
feeRecipient,
|
||||||
|
type: 'marketSell',
|
||||||
};
|
};
|
||||||
methodName = 'marketSellOrdersWithEth';
|
methodName = 'marketSellOrdersWithEth';
|
||||||
}
|
}
|
||||||
@ -210,20 +180,16 @@ export class ForwarderSwapQuoteConsumer
|
|||||||
assert.isBigNumber('gasPrice', gasPrice);
|
assert.isBigNumber('gasPrice', gasPrice);
|
||||||
}
|
}
|
||||||
|
|
||||||
const swapQuoteWithAffiliateFee = affiliateFeeUtils.getSwapQuoteWithAffiliateFee(quote, feePercentage);
|
const quoteWithAffiliateFee = affiliateFeeUtils.getSwapQuoteWithAffiliateFee(quote, feePercentage);
|
||||||
|
|
||||||
const consumableQuoteWithAffiliateFee = (swapQuoteWithAffiliateFee as any) as (
|
const { orders, feeOrders, worstCaseQuoteInfo } = quoteWithAffiliateFee;
|
||||||
| MarketBuySwapQuote
|
|
||||||
| MarketSellSwapQuote);
|
|
||||||
|
|
||||||
const { orders, feeOrders, worstCaseQuoteInfo } = consumableQuoteWithAffiliateFee;
|
|
||||||
|
|
||||||
const finalTakerAddress = await swapQuoteConsumerUtils.getTakerAddressOrThrowAsync(this.provider, opts);
|
const finalTakerAddress = await swapQuoteConsumerUtils.getTakerAddressOrThrowAsync(this.provider, opts);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let txHash: string;
|
let txHash: string;
|
||||||
if (utils.isSwapQuoteMarketBuy(consumableQuoteWithAffiliateFee)) {
|
if (quoteWithAffiliateFee.type === 'marketBuy') {
|
||||||
const { makerAssetFillAmount } = consumableQuoteWithAffiliateFee;
|
const { makerAssetFillAmount } = quoteWithAffiliateFee;
|
||||||
txHash = await this._contractWrappers.forwarder.marketBuyOrdersWithEthAsync(
|
txHash = await this._contractWrappers.forwarder.marketBuyOrdersWithEthAsync(
|
||||||
orders,
|
orders,
|
||||||
makerAssetFillAmount,
|
makerAssetFillAmount,
|
||||||
|
@ -66,46 +66,48 @@ export interface SmartContractParamsInfo<T> {
|
|||||||
methodAbi: MethodAbi;
|
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.
|
* 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.
|
* 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.
|
* signatures: An array of signatures that attest that the maker of the orders in fact made the orders.
|
||||||
*/
|
*/
|
||||||
export interface ExchangeMarketBuySmartContractParams {
|
export interface ExchangeMarketBuySmartContractParams extends SmartContractParamsBase {
|
||||||
orders: SignedOrder[];
|
|
||||||
makerAssetFillAmount: BigNumber;
|
makerAssetFillAmount: BigNumber;
|
||||||
signatures: string[];
|
type: 'marketBuy';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExchangeMarketSellSmartContractParams {
|
export interface ExchangeMarketSellSmartContractParams extends SmartContractParamsBase {
|
||||||
orders: SignedOrder[];
|
|
||||||
takerAssetFillAmount: BigNumber;
|
takerAssetFillAmount: BigNumber;
|
||||||
signatures: string[];
|
type: 'marketSell';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export type ExchangeSmartContractParams = ExchangeMarketBuySmartContractParams | ExchangeMarketSellSmartContractParams;
|
||||||
* 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.
|
export interface ForwarderSmartContractParamsBase {
|
||||||
* 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 {
|
|
||||||
feeOrders: SignedOrder[];
|
feeOrders: SignedOrder[];
|
||||||
feeSignatures: string[];
|
feeSignatures: string[];
|
||||||
feePercentage: BigNumber;
|
feePercentage: BigNumber;
|
||||||
feeRecipient: string;
|
feeRecipient: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ForwarderMarketSellSmartContractParams extends ExchangeMarketSellSmartContractParams {
|
export interface ForwarderMarketBuySmartContractParams
|
||||||
feeOrders: SignedOrder[];
|
extends ExchangeMarketBuySmartContractParams,
|
||||||
feeSignatures: string[];
|
ForwarderSmartContractParamsBase {}
|
||||||
feePercentage: BigNumber;
|
|
||||||
feeRecipient: string;
|
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)
|
* 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.
|
* 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.
|
* 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>;
|
getCalldataOrThrowAsync(quote: SwapQuote, opts: Partial<SwapQuoteGetOutputOpts>): Promise<CalldataInfo>;
|
||||||
getSmartContractParamsOrThrowAsync(
|
getSmartContractParamsOrThrowAsync(
|
||||||
quote: SwapQuote,
|
quote: SwapQuote,
|
||||||
@ -161,6 +163,8 @@ export interface ForwarderSwapQuoteGetOutputOpts extends SwapQuoteGetOutputOpts
|
|||||||
*/
|
*/
|
||||||
export interface ForwarderSwapQuoteExecutionOpts extends ForwarderSwapQuoteGetOutputOpts, SwapQuoteExecutionOpts {}
|
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).
|
* 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).
|
||||||
@ -170,7 +174,7 @@ export interface ForwarderSwapQuoteExecutionOpts extends ForwarderSwapQuoteGetOu
|
|||||||
* 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 SwapQuote {
|
export interface SwapQuoteBase {
|
||||||
takerAssetData: string;
|
takerAssetData: string;
|
||||||
makerAssetData: string;
|
makerAssetData: string;
|
||||||
orders: SignedOrder[];
|
orders: SignedOrder[];
|
||||||
@ -179,22 +183,25 @@ export interface SwapQuote {
|
|||||||
worstCaseQuoteInfo: SwapQuoteInfo;
|
worstCaseQuoteInfo: SwapQuoteInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarketSellSwapQuote extends SwapQuote {
|
export interface MarketSellSwapQuote extends SwapQuoteBase {
|
||||||
takerAssetFillAmount: BigNumber;
|
takerAssetFillAmount: BigNumber;
|
||||||
bestCaseQuoteInfo: SwapQuoteInfo;
|
type: 'marketSell';
|
||||||
worstCaseQuoteInfo: SwapQuoteInfo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarketBuySwapQuote extends SwapQuote {
|
export interface MarketBuySwapQuote extends SwapQuoteBase {
|
||||||
makerAssetFillAmount: BigNumber;
|
makerAssetFillAmount: BigNumber;
|
||||||
bestCaseQuoteInfo: SwapQuoteInfo;
|
type: 'marketBuy';
|
||||||
worstCaseQuoteInfo: SwapQuoteInfo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SwapQuoteWithAffiliateFee extends SwapQuote {
|
export interface SwapQuoteWithAffiliateFeeBase {
|
||||||
feePercentage: number;
|
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.
|
* 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.
|
* 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 { SignedOrder } from '@0x/types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { OrderProvider, OrderProviderRequest, SwapQuote, SwapQuoteConsumerError, SwapQuoteInfo } from '../types';
|
import { OrderProvider, OrderProviderRequest, SwapQuote, SwapQuoteInfo } from '../types';
|
||||||
import { utils } from '../utils/utils';
|
|
||||||
|
|
||||||
export const assert = {
|
export const assert = {
|
||||||
...sharedAssert,
|
...sharedAssert,
|
||||||
@ -15,12 +14,10 @@ export const assert = {
|
|||||||
sharedAssert.doesConformToSchema(`${variableName}.feeOrders`, swapQuote.feeOrders, schemas.signedOrdersSchema);
|
sharedAssert.doesConformToSchema(`${variableName}.feeOrders`, swapQuote.feeOrders, schemas.signedOrdersSchema);
|
||||||
assert.isValidSwapQuoteInfo(`${variableName}.bestCaseQuoteInfo`, swapQuote.bestCaseQuoteInfo);
|
assert.isValidSwapQuoteInfo(`${variableName}.bestCaseQuoteInfo`, swapQuote.bestCaseQuoteInfo);
|
||||||
assert.isValidSwapQuoteInfo(`${variableName}.worstCaseQuoteInfo`, swapQuote.worstCaseQuoteInfo);
|
assert.isValidSwapQuoteInfo(`${variableName}.worstCaseQuoteInfo`, swapQuote.worstCaseQuoteInfo);
|
||||||
if (utils.isSwapQuoteMarketBuy(swapQuote)) {
|
if (swapQuote.type === 'marketBuy') {
|
||||||
sharedAssert.isBigNumber(`${variableName}.makerAssetFillAmount`, swapQuote.makerAssetFillAmount);
|
sharedAssert.isBigNumber(`${variableName}.makerAssetFillAmount`, swapQuote.makerAssetFillAmount);
|
||||||
} else if (utils.isSwapQuoteMarketSell(swapQuote)) {
|
|
||||||
sharedAssert.isBigNumber(`${variableName}.takerAssetFillAmount`, swapQuote.takerAssetFillAmount);
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(SwapQuoteConsumerError.InvalidMarketSellOrMarketBuySwapQuote);
|
sharedAssert.isBigNumber(`${variableName}.takerAssetFillAmount`, swapQuote.takerAssetFillAmount);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isValidForwarderSwapQuote(variableName: string, swapQuote: SwapQuote, wethAssetData: string): void {
|
isValidForwarderSwapQuote(variableName: string, swapQuote: SwapQuote, wethAssetData: string): void {
|
||||||
|
@ -6,8 +6,10 @@ import { constants } from '../constants';
|
|||||||
import { InsufficientAssetLiquidityError } from '../errors';
|
import { InsufficientAssetLiquidityError } from '../errors';
|
||||||
import {
|
import {
|
||||||
MarketBuySwapQuote,
|
MarketBuySwapQuote,
|
||||||
|
MarketOperation,
|
||||||
MarketSellSwapQuote,
|
MarketSellSwapQuote,
|
||||||
OrdersAndFillableAmounts,
|
OrdersAndFillableAmounts,
|
||||||
|
SwapQuote,
|
||||||
SwapQuoteInfo,
|
SwapQuoteInfo,
|
||||||
SwapQuoterError,
|
SwapQuoterError,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
@ -21,6 +23,41 @@ export const swapQuoteCalculator = {
|
|||||||
slippagePercentage: number,
|
slippagePercentage: number,
|
||||||
isMakerAssetZrxToken: boolean,
|
isMakerAssetZrxToken: boolean,
|
||||||
): MarketSellSwapQuote {
|
): MarketSellSwapQuote {
|
||||||
|
return calculateSwapQuote(
|
||||||
|
ordersAndFillableAmounts,
|
||||||
|
feeOrdersAndFillableAmounts,
|
||||||
|
takerAssetFillAmount,
|
||||||
|
slippagePercentage,
|
||||||
|
isMakerAssetZrxToken,
|
||||||
|
'marketSell',
|
||||||
|
) as MarketSellSwapQuote;
|
||||||
|
},
|
||||||
|
calculateMarketBuySwapQuote(
|
||||||
|
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||||
|
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||||
|
makerAssetFillAmount: BigNumber,
|
||||||
|
slippagePercentage: number,
|
||||||
|
isMakerAssetZrxToken: boolean,
|
||||||
|
): MarketBuySwapQuote {
|
||||||
|
return calculateSwapQuote(
|
||||||
|
ordersAndFillableAmounts,
|
||||||
|
feeOrdersAndFillableAmounts,
|
||||||
|
makerAssetFillAmount,
|
||||||
|
slippagePercentage,
|
||||||
|
isMakerAssetZrxToken,
|
||||||
|
'marketBuy',
|
||||||
|
) as MarketBuySwapQuote;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function calculateSwapQuote(
|
||||||
|
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||||
|
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||||
|
assetFillAmount: BigNumber,
|
||||||
|
slippagePercentage: number,
|
||||||
|
isMakerAssetZrxToken: boolean,
|
||||||
|
marketOperation: MarketOperation,
|
||||||
|
): SwapQuote {
|
||||||
const orders = ordersAndFillableAmounts.orders;
|
const orders = ordersAndFillableAmounts.orders;
|
||||||
const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts;
|
const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts;
|
||||||
const remainingFillableTakerAssetAmounts = remainingFillableMakerAssetAmounts.map(
|
const remainingFillableTakerAssetAmounts = remainingFillableMakerAssetAmounts.map(
|
||||||
@ -30,26 +67,47 @@ export const swapQuoteCalculator = {
|
|||||||
);
|
);
|
||||||
const feeOrders = feeOrdersAndFillableAmounts.orders;
|
const feeOrders = feeOrdersAndFillableAmounts.orders;
|
||||||
const remainingFillableFeeAmounts = feeOrdersAndFillableAmounts.remainingFillableMakerAssetAmounts;
|
const remainingFillableFeeAmounts = feeOrdersAndFillableAmounts.remainingFillableMakerAssetAmounts;
|
||||||
const slippageBufferAmount = takerAssetFillAmount.multipliedBy(slippagePercentage).integerValue();
|
|
||||||
|
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)
|
// find the orders that cover the desired assetBuyAmount (with slippage)
|
||||||
const {
|
({
|
||||||
|
resultOrders,
|
||||||
|
remainingFillAmount,
|
||||||
|
ordersRemainingFillableMakerAssetAmounts,
|
||||||
|
} = marketUtils.findOrdersThatCoverMakerAssetFillAmount(orders, assetFillAmount, {
|
||||||
|
remainingFillableMakerAssetAmounts,
|
||||||
|
slippageBufferAmount,
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
let ordersRemainingFillableTakerAssetAmounts: BigNumber[];
|
||||||
|
// find the orders that cover the desired assetBuyAmount (with slippage)
|
||||||
|
({
|
||||||
resultOrders,
|
resultOrders,
|
||||||
remainingFillAmount,
|
remainingFillAmount,
|
||||||
ordersRemainingFillableTakerAssetAmounts,
|
ordersRemainingFillableTakerAssetAmounts,
|
||||||
} = marketUtils.findOrdersThatCoverTakerAssetFillAmount(orders, takerAssetFillAmount, {
|
} = marketUtils.findOrdersThatCoverTakerAssetFillAmount(orders, assetFillAmount, {
|
||||||
remainingFillableTakerAssetAmounts,
|
remainingFillableTakerAssetAmounts,
|
||||||
slippageBufferAmount,
|
slippageBufferAmount,
|
||||||
});
|
}));
|
||||||
const ordersRemainingFillableMakerAssetAmounts = _.map(
|
|
||||||
|
ordersRemainingFillableMakerAssetAmounts = _.map(
|
||||||
ordersRemainingFillableTakerAssetAmounts,
|
ordersRemainingFillableTakerAssetAmounts,
|
||||||
(takerAssetAmount: BigNumber, index: number) => {
|
(takerAssetAmount: BigNumber, index: number) => {
|
||||||
return orderCalculationUtils.getMakerFillAmount(resultOrders[index], takerAssetAmount);
|
return orderCalculationUtils.getMakerFillAmount(resultOrders[index], takerAssetAmount);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// if we do not have enough orders to cover the desired assetBuyAmount, throw
|
// if we do not have enough orders to cover the desired assetBuyAmount, throw
|
||||||
if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) {
|
if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) {
|
||||||
// We needed the amount they requested to buy, plus the amount for slippage
|
// We needed the amount they requested to buy, plus the amount for slippage
|
||||||
const totalAmountRequested = takerAssetFillAmount.plus(slippageBufferAmount);
|
const totalAmountRequested = assetFillAmount.plus(slippageBufferAmount);
|
||||||
const amountAbleToFill = totalAmountRequested.minus(remainingFillAmount);
|
const amountAbleToFill = totalAmountRequested.minus(remainingFillAmount);
|
||||||
// multiplierNeededWithSlippage represents what we need to multiply the assetBuyAmount by
|
// multiplierNeededWithSlippage represents what we need to multiply the assetBuyAmount by
|
||||||
// in order to get the total amount needed considering slippage
|
// in order to get the total amount needed considering slippage
|
||||||
@ -101,151 +159,81 @@ export const swapQuoteCalculator = {
|
|||||||
orders: resultFeeOrders,
|
orders: resultFeeOrders,
|
||||||
remainingFillableMakerAssetAmounts: feeOrdersRemainingFillableMakerAssetAmounts,
|
remainingFillableMakerAssetAmounts: feeOrdersRemainingFillableMakerAssetAmounts,
|
||||||
};
|
};
|
||||||
const bestCaseQuoteInfo = calculateMarketSellQuoteInfo(
|
|
||||||
|
const bestCaseQuoteInfo = calculateQuoteInfo(
|
||||||
trimmedOrdersAndFillableAmounts,
|
trimmedOrdersAndFillableAmounts,
|
||||||
trimmedFeeOrdersAndFillableAmounts,
|
trimmedFeeOrdersAndFillableAmounts,
|
||||||
takerAssetFillAmount,
|
assetFillAmount,
|
||||||
isMakerAssetZrxToken,
|
isMakerAssetZrxToken,
|
||||||
|
marketOperation,
|
||||||
);
|
);
|
||||||
// in order to calculate the maxRate, reverse the ordersAndFillableAmounts such that they are sorted from worst rate to best rate
|
// in order to calculate the maxRate, reverse the ordersAndFillableAmounts such that they are sorted from worst rate to best rate
|
||||||
const worstCaseQuoteInfo = calculateMarketSellQuoteInfo(
|
const worstCaseQuoteInfo = calculateQuoteInfo(
|
||||||
reverseOrdersAndFillableAmounts(trimmedOrdersAndFillableAmounts),
|
reverseOrdersAndFillableAmounts(trimmedOrdersAndFillableAmounts),
|
||||||
reverseOrdersAndFillableAmounts(trimmedFeeOrdersAndFillableAmounts),
|
reverseOrdersAndFillableAmounts(trimmedFeeOrdersAndFillableAmounts),
|
||||||
takerAssetFillAmount,
|
assetFillAmount,
|
||||||
isMakerAssetZrxToken,
|
isMakerAssetZrxToken,
|
||||||
|
marketOperation,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
const quoteBase = {
|
||||||
takerAssetData,
|
takerAssetData,
|
||||||
makerAssetData,
|
makerAssetData,
|
||||||
takerAssetFillAmount,
|
|
||||||
orders: resultOrders,
|
orders: resultOrders,
|
||||||
feeOrders: resultFeeOrders,
|
feeOrders: resultFeeOrders,
|
||||||
bestCaseQuoteInfo,
|
bestCaseQuoteInfo,
|
||||||
worstCaseQuoteInfo,
|
worstCaseQuoteInfo,
|
||||||
};
|
};
|
||||||
},
|
|
||||||
calculateMarketBuySwapQuote(
|
|
||||||
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
|
||||||
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
|
|
||||||
makerAssetFillAmount: BigNumber,
|
|
||||||
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,
|
|
||||||
makerAssetFillAmount,
|
|
||||||
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,
|
|
||||||
);
|
|
||||||
|
|
||||||
|
if (marketOperation === 'marketBuy') {
|
||||||
return {
|
return {
|
||||||
takerAssetData,
|
...quoteBase,
|
||||||
makerAssetData,
|
type: 'marketBuy',
|
||||||
makerAssetFillAmount,
|
makerAssetFillAmount: assetFillAmount,
|
||||||
orders: resultOrders,
|
|
||||||
feeOrders: resultFeeOrders,
|
|
||||||
bestCaseQuoteInfo,
|
|
||||||
worstCaseQuoteInfo,
|
|
||||||
};
|
};
|
||||||
},
|
} else {
|
||||||
};
|
return {
|
||||||
|
...quoteBase,
|
||||||
|
type: 'marketSell',
|
||||||
|
takerAssetFillAmount: assetFillAmount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function calculateMarketBuyQuoteInfo(
|
function calculateQuoteInfo(
|
||||||
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
ordersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||||
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
|
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||||
makerTokenAmount: BigNumber,
|
tokenAmount: BigNumber,
|
||||||
isMakerAssetZrxToken: boolean,
|
isMakerAssetZrxToken: boolean,
|
||||||
|
marketOperation: MarketOperation,
|
||||||
): SwapQuoteInfo {
|
): 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 makerTokenAmount = marketOperation === 'marketBuy' ? tokenAmount : constants.ZERO_AMOUNT;
|
||||||
|
let takerTokenAmount = marketOperation === 'marketSell' ? tokenAmount : constants.ZERO_AMOUNT;
|
||||||
let zrxTakerTokenAmount = constants.ZERO_AMOUNT;
|
let zrxTakerTokenAmount = constants.ZERO_AMOUNT;
|
||||||
|
|
||||||
if (isMakerAssetZrxToken) {
|
if (isMakerAssetZrxToken) {
|
||||||
|
if (marketOperation === 'marketBuy') {
|
||||||
takerTokenAmount = findTakerTokenAmountNeededToBuyZrx(ordersAndFillableAmounts, makerTokenAmount);
|
takerTokenAmount = findTakerTokenAmountNeededToBuyZrx(ordersAndFillableAmounts, makerTokenAmount);
|
||||||
} else {
|
} else {
|
||||||
// find eth and zrx amounts needed to buy
|
makerTokenAmount = findZrxTokenAmountFromSellingTakerTokenAmount(
|
||||||
const takerTokenAndZrxAmountToBuyAsset = findTakerTokenAndZrxAmountNeededToBuyAsset(
|
|
||||||
ordersAndFillableAmounts,
|
ordersAndFillableAmounts,
|
||||||
makerTokenAmount,
|
takerTokenAmount,
|
||||||
);
|
);
|
||||||
takerTokenAmount = takerTokenAndZrxAmountToBuyAsset[0];
|
}
|
||||||
const zrxAmountToBuyAsset = takerTokenAndZrxAmountToBuyAsset[1];
|
} else {
|
||||||
|
const findTokenAndZrxAmount =
|
||||||
|
marketOperation === 'marketBuy'
|
||||||
|
? findTakerTokenAndZrxAmountNeededToBuyAsset
|
||||||
|
: findMakerTokenAmountReceivedAndZrxAmountNeededToSellAsset;
|
||||||
|
// find eth and zrx amounts needed to buy
|
||||||
|
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
|
// find eth amount needed to buy zrx
|
||||||
zrxTakerTokenAmount = findTakerTokenAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset);
|
zrxTakerTokenAmount = findTakerTokenAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset);
|
||||||
}
|
}
|
||||||
@ -261,42 +249,6 @@ function calculateMarketBuyQuoteInfo(
|
|||||||
totalTakerTokenAmount,
|
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
|
// given an OrdersAndFillableAmounts, reverse the orders and remainingFillableMakerAssetAmounts properties
|
||||||
function reverseOrdersAndFillableAmounts(ordersAndFillableAmounts: OrdersAndFillableAmounts): OrdersAndFillableAmounts {
|
function reverseOrdersAndFillableAmounts(ordersAndFillableAmounts: OrdersAndFillableAmounts): OrdersAndFillableAmounts {
|
||||||
const ordersCopy = _.clone(ordersAndFillableAmounts.orders);
|
const ordersCopy = _.clone(ordersAndFillableAmounts.orders);
|
||||||
|
@ -4,7 +4,6 @@ import { AbiDefinition, ContractAbi, MethodAbi } from 'ethereum-types';
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { constants } from '../constants';
|
import { constants } from '../constants';
|
||||||
import { MarketBuySwapQuote, MarketSellSwapQuote, SwapQuote } from '../types';
|
|
||||||
|
|
||||||
// tslint:disable:no-unnecessary-type-assertion
|
// tslint:disable:no-unnecessary-type-assertion
|
||||||
export const utils = {
|
export const utils = {
|
||||||
@ -26,10 +25,4 @@ export const utils = {
|
|||||||
},
|
},
|
||||||
) as MethodAbi | undefined;
|
) 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 { BigNumber } from '@0x/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { MarketBuySwapQuote, MarketSellSwapQuote } from '../../src';
|
import { MarketOperation, SwapQuote } from '../../src/types';
|
||||||
|
|
||||||
const ZERO_BIG_NUMBER = new BigNumber(0);
|
const ZERO_BIG_NUMBER = new BigNumber(0);
|
||||||
|
|
||||||
@ -25,11 +25,12 @@ export const getSignedOrdersWithNoFees = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getFullyFillableMarketBuySwapQuoteWithNoFees = (
|
export const getFullyFillableSwapQuoteWithNoFees = (
|
||||||
makerAssetData: string,
|
makerAssetData: string,
|
||||||
takerAssetData: string,
|
takerAssetData: string,
|
||||||
orders: SignedOrder[],
|
orders: SignedOrder[],
|
||||||
): MarketBuySwapQuote => {
|
operation: MarketOperation,
|
||||||
|
): SwapQuote => {
|
||||||
const makerAssetFillAmount = _.reduce(
|
const makerAssetFillAmount = _.reduce(
|
||||||
orders,
|
orders,
|
||||||
(a: BigNumber, c: SignedOrder) => a.plus(c.makerAssetAmount),
|
(a: BigNumber, c: SignedOrder) => a.plus(c.makerAssetAmount),
|
||||||
@ -47,46 +48,26 @@ export const getFullyFillableMarketBuySwapQuoteWithNoFees = (
|
|||||||
totalTakerTokenAmount,
|
totalTakerTokenAmount,
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
const quoteBase = {
|
||||||
makerAssetData,
|
makerAssetData,
|
||||||
takerAssetData,
|
takerAssetData,
|
||||||
orders,
|
orders,
|
||||||
feeOrders: [],
|
feeOrders: [],
|
||||||
|
bestCaseQuoteInfo: quoteInfo,
|
||||||
|
worstCaseQuoteInfo: quoteInfo,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (operation === 'marketBuy') {
|
||||||
|
return {
|
||||||
|
...quoteBase,
|
||||||
|
type: 'marketBuy',
|
||||||
makerAssetFillAmount,
|
makerAssetFillAmount,
|
||||||
bestCaseQuoteInfo: quoteInfo,
|
|
||||||
worstCaseQuoteInfo: quoteInfo,
|
|
||||||
};
|
};
|
||||||
};
|
} else {
|
||||||
|
|
||||||
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 {
|
return {
|
||||||
makerAssetData,
|
...quoteBase,
|
||||||
takerAssetData,
|
type: 'marketSell',
|
||||||
orders,
|
|
||||||
feeOrders: [],
|
|
||||||
takerAssetFillAmount: totalTakerTokenAmount,
|
takerAssetFillAmount: totalTakerTokenAmount,
|
||||||
bestCaseQuoteInfo: quoteInfo,
|
|
||||||
worstCaseQuoteInfo: quoteInfo,
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
FindFeeOrdersThatCoverFeesForTargetOrdersOpts,
|
FindFeeOrdersThatCoverFeesForTargetOrdersOpts,
|
||||||
FindOrdersThatCoverMakerAssetFillAmountOpts,
|
FindOrdersThatCoverMakerAssetFillAmountOpts,
|
||||||
FindOrdersThatCoverTakerAssetFillAmountOpts,
|
FindOrdersThatCoverTakerAssetFillAmountOpts,
|
||||||
|
MarketOperation,
|
||||||
OrdersAndRemainingMakerFillAmount,
|
OrdersAndRemainingMakerFillAmount,
|
||||||
OrdersAndRemainingTakerFillAmount,
|
OrdersAndRemainingTakerFillAmount,
|
||||||
} from './types';
|
} from './types';
|
||||||
@ -20,60 +21,12 @@ export const marketUtils = {
|
|||||||
takerAssetFillAmount: BigNumber,
|
takerAssetFillAmount: BigNumber,
|
||||||
opts?: FindOrdersThatCoverTakerAssetFillAmountOpts,
|
opts?: FindOrdersThatCoverTakerAssetFillAmountOpts,
|
||||||
): OrdersAndRemainingTakerFillAmount<T> {
|
): OrdersAndRemainingTakerFillAmount<T> {
|
||||||
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
|
return findOrdersThatCoverAssetFillAmount(
|
||||||
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(
|
|
||||||
orders,
|
orders,
|
||||||
({ resultOrders, remainingFillAmount, ordersRemainingFillableTakerAssetAmounts }, order, index) => {
|
takerAssetFillAmount,
|
||||||
if (remainingFillAmount.isLessThanOrEqualTo(constants.ZERO_AMOUNT)) {
|
'marketSell',
|
||||||
return {
|
opts,
|
||||||
resultOrders,
|
) as OrdersAndRemainingTakerFillAmount<T>;
|
||||||
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;
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Takes an array of orders and returns a subset of those orders that has enough makerAssetAmount
|
* 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,
|
makerAssetFillAmount: BigNumber,
|
||||||
opts?: FindOrdersThatCoverMakerAssetFillAmountOpts,
|
opts?: FindOrdersThatCoverMakerAssetFillAmountOpts,
|
||||||
): OrdersAndRemainingMakerFillAmount<T> {
|
): OrdersAndRemainingMakerFillAmount<T> {
|
||||||
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
|
return findOrdersThatCoverAssetFillAmount(
|
||||||
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(
|
|
||||||
orders,
|
orders,
|
||||||
({ resultOrders, remainingFillAmount, ordersRemainingFillableMakerAssetAmounts }, order, index) => {
|
makerAssetFillAmount,
|
||||||
if (remainingFillAmount.isLessThanOrEqualTo(constants.ZERO_AMOUNT)) {
|
'marketBuy',
|
||||||
return {
|
opts,
|
||||||
resultOrders,
|
) as OrdersAndRemainingMakerFillAmount<T>;
|
||||||
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;
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Takes an array of orders and an array of feeOrders. Returns a subset of the feeOrders that has enough ZRX
|
* 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
|
// 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;
|
slippageBufferAmount?: BigNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MarketOperation = 'marketSell' | 'marketBuy';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* remainingFillableMakerAssetAmount: An array of BigNumbers corresponding to the `orders` parameter.
|
* remainingFillableMakerAssetAmount: An array of BigNumbers corresponding to the `orders` parameter.
|
||||||
* You can use `OrderStateUtils` `@0x/order-utils` to perform blockchain lookups for these values.
|
* You can use `OrderStateUtils` `@0x/order-utils` to perform blockchain lookups for these values.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user