added options, features for asset-swapper
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
OrdersAndFillableAmounts,
|
||||
SwapQuoteRequestOpts,
|
||||
SwapQuoterOpts,
|
||||
SwapQuoteUtilsOpts,
|
||||
} from './types';
|
||||
|
||||
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
@@ -21,6 +22,10 @@ const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
|
||||
expiryBufferMs: 120000, // 2 minutes
|
||||
};
|
||||
|
||||
const DEFAULT_SWAP_QUOTE_UTILS_OPTS: SwapQuoteUtilsOpts = {
|
||||
networkId: MAINNET_NETWORK_ID,
|
||||
};
|
||||
|
||||
const DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS: ForwarderSwapQuoteGetOutputOpts = {
|
||||
feePercentage: 0,
|
||||
feeRecipient: NULL_ADDRESS,
|
||||
@@ -30,6 +35,7 @@ const DEFAULT_FORWARDER_SWAP_QUOTE_EXECUTE_OPTS: ForwarderSwapQuoteExecutionOpts
|
||||
|
||||
const DEFAULT_SWAP_QUOTE_REQUEST_OPTS: SwapQuoteRequestOpts = {
|
||||
shouldForceOrderRefresh: false,
|
||||
shouldDisableRequestingFeeOrders: false,
|
||||
slippagePercentage: 0.2, // 20% slippage protection,
|
||||
};
|
||||
|
||||
@@ -56,4 +62,5 @@ export const constants = {
|
||||
EMPTY_ORDERS_AND_FILLABLE_AMOUNTS,
|
||||
DEFAULT_PER_PAGE,
|
||||
DEFAULT_LIQUIDITY_REQUEST_OPTS,
|
||||
DEFAULT_SWAP_QUOTE_UTILS_OPTS,
|
||||
};
|
||||
|
@@ -23,6 +23,7 @@ export {
|
||||
export { SignedOrder } from '@0x/types';
|
||||
export { BigNumber } from '@0x/utils';
|
||||
|
||||
export { SwapQuoteUtils } from './utils/swap_quote_utils';
|
||||
export { SwapQuoteConsumer } from './quote_consumers/swap_quote_consumer';
|
||||
export { SwapQuoter } from './swap_quoter';
|
||||
export { InsufficientAssetLiquidityError } from './errors';
|
||||
@@ -33,6 +34,7 @@ export { StandardRelayerAPIOrderProvider } from './order_providers/standard_rela
|
||||
export {
|
||||
SwapQuoterError,
|
||||
SwapQuoterOpts,
|
||||
SwapQuoteUtilsOpts,
|
||||
SwapQuote,
|
||||
SwapQuoteConsumerOpts,
|
||||
CalldataInfo,
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { ContractWrappers, ContractWrappersError, ForwarderWrapperError } from '@0x/contract-wrappers';
|
||||
import { calldataOptimizationUtils } from '@0x/contract-wrappers/src/utils/calldata_optimization_utils';
|
||||
import { MarketOperation } from '@0x/types';
|
||||
import { AbiEncoder, providerUtils } from '@0x/utils';
|
||||
import { SupportedProvider, ZeroExProvider } from '@0x/web3-wrapper';
|
||||
@@ -75,6 +76,8 @@ export class ExchangeSwapQuoteConsumer implements SwapQuoteConsumerBase<Exchange
|
||||
|
||||
const { orders } = quote;
|
||||
|
||||
const optimizedOrders = calldataOptimizationUtils.optimizeForwarderOrders(orders);
|
||||
|
||||
const signatures = _.map(orders, o => o.signature);
|
||||
|
||||
let params: ExchangeSmartContractParams;
|
||||
@@ -84,7 +87,7 @@ export class ExchangeSwapQuoteConsumer implements SwapQuoteConsumerBase<Exchange
|
||||
const { makerAssetFillAmount } = quote;
|
||||
|
||||
params = {
|
||||
orders,
|
||||
orders: optimizedOrders,
|
||||
signatures,
|
||||
makerAssetFillAmount,
|
||||
type: MarketOperation.Buy,
|
||||
@@ -95,7 +98,7 @@ export class ExchangeSwapQuoteConsumer implements SwapQuoteConsumerBase<Exchange
|
||||
const { takerAssetFillAmount } = quote;
|
||||
|
||||
params = {
|
||||
orders,
|
||||
orders: optimizedOrders,
|
||||
signatures,
|
||||
takerAssetFillAmount,
|
||||
type: MarketOperation.Sell,
|
||||
|
@@ -1,10 +1,12 @@
|
||||
import { ContractWrappers, ContractWrappersError, ForwarderWrapperError } from '@0x/contract-wrappers';
|
||||
import { calldataOptimizationUtils } from '@0x/contract-wrappers/src/utils/calldata_optimization_utils';
|
||||
import { MarketOperation } from '@0x/types';
|
||||
import { AbiEncoder, providerUtils } from '@0x/utils';
|
||||
import { SupportedProvider, ZeroExProvider } from '@0x/web3-wrapper';
|
||||
import { MethodAbi } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
|
||||
import { constants } from '../constants';
|
||||
import {
|
||||
CalldataInfo,
|
||||
@@ -101,6 +103,12 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumerBase<Forward
|
||||
|
||||
const { orders, feeOrders, worstCaseQuoteInfo } = quoteWithAffiliateFee;
|
||||
|
||||
// lowercase input addresses
|
||||
const normalizedFeeRecipientAddress = feeRecipient.toLowerCase();
|
||||
// optimize orders
|
||||
const optimizedOrders = calldataOptimizationUtils.optimizeForwarderOrders(orders);
|
||||
const optimizedFeeOrders = calldataOptimizationUtils.optimizeForwarderFeeOrders(feeOrders);
|
||||
|
||||
const signatures = _.map(orders, o => o.signature);
|
||||
const feeSignatures = _.map(feeOrders, o => o.signature);
|
||||
|
||||
@@ -113,25 +121,25 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumerBase<Forward
|
||||
const { makerAssetFillAmount } = quoteWithAffiliateFee;
|
||||
|
||||
params = {
|
||||
orders,
|
||||
orders: optimizedOrders,
|
||||
makerAssetFillAmount,
|
||||
signatures,
|
||||
feeOrders,
|
||||
feeOrders: optimizedFeeOrders,
|
||||
feeSignatures,
|
||||
feePercentage,
|
||||
feeRecipient,
|
||||
feeRecipient: normalizedFeeRecipientAddress,
|
||||
type: MarketOperation.Buy,
|
||||
};
|
||||
|
||||
methodName = 'marketBuyOrdersWithEth';
|
||||
} else {
|
||||
params = {
|
||||
orders,
|
||||
orders: optimizedOrders,
|
||||
signatures,
|
||||
feeOrders,
|
||||
feeOrders: optimizedFeeOrders,
|
||||
feeSignatures,
|
||||
feePercentage,
|
||||
feeRecipient,
|
||||
feeRecipient: normalizedFeeRecipientAddress,
|
||||
type: MarketOperation.Sell,
|
||||
};
|
||||
methodName = 'marketSellOrdersWithEth';
|
||||
|
@@ -6,6 +6,7 @@ import * as _ from 'lodash';
|
||||
import { constants } from '../constants';
|
||||
import {
|
||||
CalldataInfo,
|
||||
ConsumerType,
|
||||
SmartContractParams,
|
||||
SmartContractParamsInfo,
|
||||
SwapQuote,
|
||||
@@ -74,13 +75,19 @@ export class SwapQuoteConsumer implements SwapQuoteConsumerBase<SmartContractPar
|
||||
quote: SwapQuote,
|
||||
opts: Partial<SwapQuoteGetOutputOpts>,
|
||||
): Promise<SwapQuoteConsumerBase<SmartContractParams>> {
|
||||
return swapQuoteConsumerUtils.getConsumerForSwapQuoteAsync(
|
||||
quote,
|
||||
this._contractWrappers,
|
||||
this.provider,
|
||||
this._exchangeConsumer,
|
||||
this._forwarderConsumer,
|
||||
opts,
|
||||
);
|
||||
}
|
||||
if (opts.useConsumerType === ConsumerType.Exchange) {
|
||||
return this._exchangeConsumer;
|
||||
} else if (opts.useConsumerType === ConsumerType.Forwarder) {
|
||||
return this._forwarderConsumer;
|
||||
} else {
|
||||
return swapQuoteConsumerUtils.getConsumerForSwapQuoteAsync(
|
||||
quote,
|
||||
this._contractWrappers,
|
||||
this.provider,
|
||||
this._exchangeConsumer,
|
||||
this._forwarderConsumer,
|
||||
opts,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -391,7 +391,7 @@ export class SwapQuoter {
|
||||
marketOperation: MarketOperation,
|
||||
options: Partial<SwapQuoteRequestOpts>,
|
||||
): Promise<SwapQuote> {
|
||||
const { shouldForceOrderRefresh, slippagePercentage } = _.merge(
|
||||
const { shouldForceOrderRefresh, slippagePercentage, shouldDisableRequestingFeeOrders } = _.merge(
|
||||
{},
|
||||
constants.DEFAULT_SWAP_QUOTE_REQUEST_OPTS,
|
||||
options,
|
||||
@@ -406,7 +406,7 @@ export class SwapQuoter {
|
||||
// if the requested assetData is ZRX, don't get the fee info
|
||||
const [ordersAndFillableAmounts, feeOrdersAndFillableAmounts] = await Promise.all([
|
||||
this.getOrdersAndFillableAmountsAsync(makerAssetData, takerAssetData, shouldForceOrderRefresh),
|
||||
isMakerAssetZrxToken
|
||||
shouldDisableRequestingFeeOrders || isMakerAssetZrxToken
|
||||
? Promise.resolve(constants.EMPTY_ORDERS_AND_FILLABLE_AMOUNTS)
|
||||
: this.getOrdersAndFillableAmountsAsync(zrxTokenAssetData, takerAssetData, shouldForceOrderRefresh),
|
||||
shouldForceOrderRefresh,
|
||||
@@ -429,6 +429,7 @@ export class SwapQuoter {
|
||||
assetFillAmount,
|
||||
slippagePercentage,
|
||||
isMakerAssetZrxToken,
|
||||
shouldDisableRequestingFeeOrders,
|
||||
);
|
||||
} else {
|
||||
swapQuote = swapQuoteCalculator.calculateMarketSellSwapQuote(
|
||||
@@ -437,6 +438,7 @@ export class SwapQuoter {
|
||||
assetFillAmount,
|
||||
slippagePercentage,
|
||||
isMakerAssetZrxToken,
|
||||
shouldDisableRequestingFeeOrders,
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -94,6 +94,10 @@ export interface ExchangeMarketSellSmartContractParams extends SmartContractPara
|
||||
type: MarketOperation.Sell;
|
||||
}
|
||||
|
||||
export enum ConsumerType {
|
||||
Forwarder, Exchange,
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents all the parameters to interface with 0x exchange contracts' marketSell and marketBuy functions.
|
||||
*/
|
||||
@@ -157,6 +161,10 @@ export interface SwapQuoteConsumerOpts {
|
||||
networkId: number;
|
||||
}
|
||||
|
||||
export interface SwapQuoteUtilsOpts {
|
||||
networkId: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the options provided to a generic SwapQuoteConsumer
|
||||
*/
|
||||
@@ -198,6 +206,7 @@ export type SwapQuote = MarketBuySwapQuote | MarketSellSwapQuote;
|
||||
*/
|
||||
export interface SwapQuoteGetOutputOpts extends ForwarderSwapQuoteGetOutputOpts {
|
||||
takerAddress?: string;
|
||||
useConsumerType?: ConsumerType;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -262,6 +271,7 @@ export interface SwapQuoteInfo {
|
||||
*/
|
||||
export interface SwapQuoteRequestOpts {
|
||||
shouldForceOrderRefresh: boolean;
|
||||
shouldDisableRequestingFeeOrders: boolean;
|
||||
slippagePercentage: number;
|
||||
}
|
||||
|
||||
|
@@ -22,6 +22,7 @@ export const swapQuoteCalculator = {
|
||||
takerAssetFillAmount: BigNumber,
|
||||
slippagePercentage: number,
|
||||
isMakerAssetZrxToken: boolean,
|
||||
shouldDisableFeeOrderCalculations: boolean,
|
||||
): MarketSellSwapQuote {
|
||||
return calculateSwapQuote(
|
||||
ordersAndFillableAmounts,
|
||||
@@ -29,7 +30,9 @@ export const swapQuoteCalculator = {
|
||||
takerAssetFillAmount,
|
||||
slippagePercentage,
|
||||
isMakerAssetZrxToken,
|
||||
shouldDisableFeeOrderCalculations,
|
||||
MarketOperation.Sell,
|
||||
|
||||
) as MarketSellSwapQuote;
|
||||
},
|
||||
calculateMarketBuySwapQuote(
|
||||
@@ -38,6 +41,7 @@ export const swapQuoteCalculator = {
|
||||
makerAssetFillAmount: BigNumber,
|
||||
slippagePercentage: number,
|
||||
isMakerAssetZrxToken: boolean,
|
||||
shouldDisableFeeOrderCalculations: boolean,
|
||||
): MarketBuySwapQuote {
|
||||
return calculateSwapQuote(
|
||||
ordersAndFillableAmounts,
|
||||
@@ -45,6 +49,7 @@ export const swapQuoteCalculator = {
|
||||
makerAssetFillAmount,
|
||||
slippagePercentage,
|
||||
isMakerAssetZrxToken,
|
||||
shouldDisableFeeOrderCalculations,
|
||||
MarketOperation.Buy,
|
||||
) as MarketBuySwapQuote;
|
||||
},
|
||||
@@ -56,6 +61,7 @@ function calculateSwapQuote(
|
||||
assetFillAmount: BigNumber,
|
||||
slippagePercentage: number,
|
||||
isMakerAssetZrxToken: boolean,
|
||||
shouldDisableFeeOrderCalculations: boolean,
|
||||
marketOperation: MarketOperation,
|
||||
): SwapQuote {
|
||||
const orders = ordersAndFillableAmounts.orders;
|
||||
@@ -128,7 +134,7 @@ function calculateSwapQuote(
|
||||
// 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) {
|
||||
if (!shouldDisableFeeOrderCalculations && !isMakerAssetZrxToken) {
|
||||
const feeOrdersAndRemainingFeeAmount = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(
|
||||
resultOrders,
|
||||
feeOrders,
|
||||
@@ -165,6 +171,7 @@ function calculateSwapQuote(
|
||||
trimmedFeeOrdersAndFillableAmounts,
|
||||
assetFillAmount,
|
||||
isMakerAssetZrxToken,
|
||||
shouldDisableFeeOrderCalculations,
|
||||
marketOperation,
|
||||
);
|
||||
// in order to calculate the maxRate, reverse the ordersAndFillableAmounts such that they are sorted from worst rate to best rate
|
||||
@@ -173,6 +180,7 @@ function calculateSwapQuote(
|
||||
reverseOrdersAndFillableAmounts(trimmedFeeOrdersAndFillableAmounts),
|
||||
assetFillAmount,
|
||||
isMakerAssetZrxToken,
|
||||
shouldDisableFeeOrderCalculations,
|
||||
marketOperation,
|
||||
);
|
||||
|
||||
@@ -205,6 +213,7 @@ function calculateQuoteInfo(
|
||||
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
|
||||
tokenAmount: BigNumber,
|
||||
isMakerAssetZrxToken: boolean,
|
||||
shouldDisableFeeOrderCalculations: boolean,
|
||||
marketOperation: MarketOperation,
|
||||
): SwapQuoteInfo {
|
||||
// find the total eth and zrx needed to buy assetAmount from the resultOrders from left to right
|
||||
@@ -212,7 +221,7 @@ function calculateQuoteInfo(
|
||||
let takerTokenAmount = marketOperation === MarketOperation.Sell ? tokenAmount : constants.ZERO_AMOUNT;
|
||||
let zrxTakerTokenAmount = constants.ZERO_AMOUNT;
|
||||
|
||||
if (isMakerAssetZrxToken) {
|
||||
if (!shouldDisableFeeOrderCalculations && isMakerAssetZrxToken) {
|
||||
if (marketOperation === MarketOperation.Buy) {
|
||||
takerTokenAmount = findTakerTokenAmountNeededToBuyZrx(ordersAndFillableAmounts, makerTokenAmount);
|
||||
} else {
|
||||
@@ -221,7 +230,7 @@ function calculateQuoteInfo(
|
||||
takerTokenAmount,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
} else if (!shouldDisableFeeOrderCalculations) {
|
||||
const findTokenAndZrxAmount =
|
||||
marketOperation === MarketOperation.Buy
|
||||
? findTakerTokenAndZrxAmountNeededToBuyAsset
|
||||
|
36
packages/asset-swapper/src/utils/swap_quote_utils.ts
Normal file
36
packages/asset-swapper/src/utils/swap_quote_utils.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { ContractWrappers, SupportedProvider, ZeroExProvider } from '@0x/contract-wrappers';
|
||||
import { providerUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { constants } from '../constants';
|
||||
import { SwapQuote, SwapQuoteUtilsOpts } from '../types';
|
||||
import { assert } from '../utils/assert';
|
||||
|
||||
export class SwapQuoteUtils {
|
||||
public readonly provider: ZeroExProvider;
|
||||
public readonly networkId: number;
|
||||
|
||||
private readonly _contractWrappers: ContractWrappers;
|
||||
|
||||
constructor(supportedProvider: SupportedProvider, options: Partial<SwapQuoteUtilsOpts> = {}) {
|
||||
const { networkId } = _.merge({}, constants.DEFAULT_SWAP_QUOTE_UTILS_OPTS, options);
|
||||
assert.isNumber('networkId', networkId);
|
||||
|
||||
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||
this.provider = provider;
|
||||
this.networkId = networkId;
|
||||
this._contractWrappers = new ContractWrappers(this.provider, {
|
||||
networkId,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public async isTakerAddressAllowanceEnoughForBestAndWorstQuoteInfoAsync(swapQuote: SwapQuote, takerAddress: string): Promise<[boolean, boolean]> {
|
||||
const orderValidatorWrapper = this._contractWrappers.orderValidator;
|
||||
const balanceAndAllowance = await orderValidatorWrapper.getBalanceAndAllowanceAsync(takerAddress, swapQuote.takerAssetData);
|
||||
return [
|
||||
balanceAndAllowance.allowance.isGreaterThanOrEqualTo(swapQuote.bestCaseQuoteInfo.totalTakerTokenAmount),
|
||||
balanceAndAllowance.allowance.isGreaterThanOrEqualTo(swapQuote.worstCaseQuoteInfo.totalTakerTokenAmount),
|
||||
];
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user