Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

Asset-swapper: Fallback orders + refactors #2513

Merged
merged 14 commits into from
Mar 12, 2020
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions packages/asset-swapper/CHANGELOG.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@
{
"note": "Add support for private liquidity providers",
"pr": 2505
},
{
"note": "Big refactor of market operation utils",
"pr": 2513
},
{
"note": "Remove `dustFractionThreshold`, `noConflicts` options.",
"pr": 2513
},
{
"note": "Revamp fill optimization algorithm",
"pr": 2513
},
{
"note": "Add fallback orders to quotes via `allowFallback` option.",
"pr": 2513
}
]
},
Expand Down
49 changes: 3 additions & 46 deletions packages/asset-swapper/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import {
SwapQuoterOpts,
} from './types';

import { constants as marketOperationUtilConstants } from './utils/market_operation_utils/constants';
import { ERC20BridgeSource } from './utils/market_operation_utils/types';
import { DEFAULT_GET_MARKET_ORDERS_OPTS } from './utils/market_operation_utils/constants';

const ETH_GAS_STATION_API_BASE_URL = 'https://ethgasstation.info';
const NULL_BYTES = '0x';
Expand Down Expand Up @@ -43,7 +42,7 @@ const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
orderRefreshIntervalMs: 10000, // 10 seconds
},
...DEFAULT_ORDER_PRUNER_OPTS,
samplerGasLimit: 59e6,
samplerGasLimit: 250e6,
dekz marked this conversation as resolved.
Show resolved Hide resolved
};

const DEFAULT_FORWARDER_EXTENSION_CONTRACT_OPTS: ForwarderExtensionContractOpts = {
Expand All @@ -59,48 +58,7 @@ const DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS: SwapQuoteGetOutputOpts = {
const DEFAULT_FORWARDER_SWAP_QUOTE_EXECUTE_OPTS: SwapQuoteExecutionOpts = DEFAULT_FORWARDER_SWAP_QUOTE_GET_OPTS;

const DEFAULT_SWAP_QUOTE_REQUEST_OPTS: SwapQuoteRequestOpts = {
...{
slippagePercentage: 0.2, // 20% slippage protection,
},
...marketOperationUtilConstants.DEFAULT_GET_MARKET_ORDERS_OPTS,
};

// Mainnet Curve configuration
const DEFAULT_CURVE_OPTS: { [source: string]: { version: number; curveAddress: string; tokens: string[] } } = {
[ERC20BridgeSource.CurveUsdcDai]: {
version: 1,
curveAddress: '0xa2b47e3d5c44877cca798226b7b8118f9bfb7a56',
tokens: ['0x6b175474e89094c44da98b954eedeac495271d0f', '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'],
},
[ERC20BridgeSource.CurveUsdcDaiUsdt]: {
version: 1,
curveAddress: '0x52ea46506b9cc5ef470c5bf89f17dc28bb35d85c',
tokens: [
'0x6b175474e89094c44da98b954eedeac495271d0f',
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
'0xdac17f958d2ee523a2206206994597c13d831ec7',
],
},
[ERC20BridgeSource.CurveUsdcDaiUsdtTusd]: {
version: 1,
curveAddress: '0x45f783cce6b7ff23b2ab2d70e416cdb7d6055f51',
tokens: [
'0x6b175474e89094c44da98b954eedeac495271d0f',
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
'0xdac17f958d2ee523a2206206994597c13d831ec7',
'0x0000000000085d4780b73119b644ae5ecd22b376',
],
},
[ERC20BridgeSource.CurveUsdcDaiUsdtBusd]: {
version: 1,
curveAddress: '0x79a8c46dea5ada233abaffd40f3a0a2b1e5a4f27',
tokens: [
'0x6b175474e89094c44da98b954eedeac495271d0f',
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
'0xdac17f958d2ee523a2206206994597c13d831ec7',
'0x4fabb145d64652a948d72533023f6e7a623c7c53',
],
},
...DEFAULT_GET_MARKET_ORDERS_OPTS,
};

export const constants = {
Expand All @@ -123,5 +81,4 @@ export const constants = {
PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS,
MARKET_UTILS_AMOUNT_BUFFER_PERCENTAGE,
BRIDGE_ASSET_DATA_PREFIX: '0xdc1600f3',
DEFAULT_CURVE_OPTS,
};
26 changes: 5 additions & 21 deletions packages/asset-swapper/src/swap_quoter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
import { assert } from './utils/assert';
import { calculateLiquidity } from './utils/calculate_liquidity';
import { MarketOperationUtils } from './utils/market_operation_utils';
import { dummyOrderUtils } from './utils/market_operation_utils/dummy_order_utils';
import { createDummyOrderForSampler } from './utils/market_operation_utils/orders';
import { DexOrderSampler } from './utils/market_operation_utils/sampler';
import { orderPrunerUtils } from './utils/order_prune_utils';
import { OrderStateUtils } from './utils/order_state_utils';
Expand Down Expand Up @@ -242,11 +242,7 @@ export class SwapQuoter {
): Promise<Array<MarketBuySwapQuote | undefined>> {
makerAssetBuyAmount.map((a, i) => assert.isBigNumber(`makerAssetBuyAmount[${i}]`, a));
let gasPrice: BigNumber;
const { slippagePercentage, ...calculateSwapQuoteOpts } = _.merge(
{},
constants.DEFAULT_SWAP_QUOTE_REQUEST_OPTS,
options,
);
const calculateSwapQuoteOpts = _.merge({}, constants.DEFAULT_SWAP_QUOTE_REQUEST_OPTS, options);
if (!!options.gasPrice) {
gasPrice = options.gasPrice;
assert.isBigNumber('gasPrice', gasPrice);
Expand All @@ -264,7 +260,7 @@ export class SwapQuoter {
);
if (prunedOrders.length === 0) {
return [
dummyOrderUtils.createDummyOrderForSampler(
createDummyOrderForSampler(
makerAssetDatas[i],
takerAssetData,
this._contractAddresses.uniswapBridge,
Expand All @@ -278,7 +274,6 @@ export class SwapQuoter {
const swapQuotes = await this._swapQuoteCalculator.calculateBatchMarketBuySwapQuoteAsync(
allPrunedOrders,
makerAssetBuyAmount,
slippagePercentage,
gasPrice,
calculateSwapQuoteOpts,
);
Expand Down Expand Up @@ -517,14 +512,9 @@ export class SwapQuoter {
marketOperation: MarketOperation,
options: Partial<SwapQuoteRequestOpts>,
): Promise<SwapQuote> {
const { slippagePercentage, ...calculateSwapQuoteOpts } = _.merge(
{},
constants.DEFAULT_SWAP_QUOTE_REQUEST_OPTS,
options,
);
const calculateSwapQuoteOpts = _.merge({}, constants.DEFAULT_SWAP_QUOTE_REQUEST_OPTS, options);
assert.isString('makerAssetData', makerAssetData);
assert.isString('takerAssetData', takerAssetData);
assert.isNumber('slippagePercentage', slippagePercentage);
let gasPrice: BigNumber;
if (!!options.gasPrice) {
gasPrice = options.gasPrice;
Expand All @@ -537,11 +527,7 @@ export class SwapQuoter {
// if no native orders, pass in a dummy order for the sampler to have required metadata for sampling
if (prunedOrders.length === 0) {
prunedOrders = [
dummyOrderUtils.createDummyOrderForSampler(
makerAssetData,
takerAssetData,
this._contractAddresses.uniswapBridge,
),
createDummyOrderForSampler(makerAssetData, takerAssetData, this._contractAddresses.uniswapBridge),
];
}

Expand All @@ -551,15 +537,13 @@ export class SwapQuoter {
swapQuote = await this._swapQuoteCalculator.calculateMarketBuySwapQuoteAsync(
prunedOrders,
assetFillAmount,
slippagePercentage,
gasPrice,
calculateSwapQuoteOpts,
);
} else {
swapQuote = await this._swapQuoteCalculator.calculateMarketSellSwapQuoteAsync(
prunedOrders,
assetFillAmount,
slippagePercentage,
gasPrice,
calculateSwapQuoteOpts,
);
Expand Down
3 changes: 2 additions & 1 deletion packages/asset-swapper/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,15 @@ export interface MarketBuySwapQuote extends SwapQuoteBase {
* totalTakerAssetAmount: The total amount of takerAsset required to complete the swap (filling orders, and paying takerFees).
* makerAssetAmount: The amount of makerAsset that will be acquired through the swap.
* protocolFeeInWeiAmount: The amount of ETH to pay (in WEI) as protocol fee to perform the swap for desired asset.
* gas: Amount of estimated gas needed to fill the quote.
*/
export interface SwapQuoteInfo {
feeTakerAssetAmount: BigNumber;
takerAssetAmount: BigNumber;
totalTakerAssetAmount: BigNumber;
makerAssetAmount: BigNumber;
protocolFeeInWeiAmount: BigNumber;
gas: number;
}

/**
Expand All @@ -190,7 +192,6 @@ export interface SwapQuoteOrdersBreakdown {
* gasPrice: gas price to determine protocolFee amount, default to ethGasStation fast amount
*/
export interface SwapQuoteRequestOpts extends CalculateSwapQuoteOpts {
slippagePercentage: number;
gasPrice?: BigNumber;
}

Expand Down
18 changes: 12 additions & 6 deletions packages/asset-swapper/src/utils/assert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import * as _ from 'lodash';

import { MarketOperation, OrderProviderRequest, SwapQuote, SwapQuoteInfo } from '../types';

import { utils } from './utils';
import {
isAssetDataEquivalent,
isExactAssetData,
isOrderTakerFeePayableWithMakerAsset,
isOrderTakerFeePayableWithTakerAsset,
} from './utils';

export const assert = {
...sharedAssert,
Expand Down Expand Up @@ -36,13 +41,13 @@ export const assert = {
): void {
_.every(orders, (order: SignedOrder, index: number) => {
assert.assert(
utils.isAssetDataEquivalent(takerAssetData, order.takerAssetData),
isAssetDataEquivalent(takerAssetData, order.takerAssetData),
`Expected ${variableName}[${index}].takerAssetData to be ${takerAssetData} but found ${
order.takerAssetData
}`,
);
assert.assert(
utils.isAssetDataEquivalent(makerAssetData, order.makerAssetData),
isAssetDataEquivalent(makerAssetData, order.makerAssetData),
`Expected ${variableName}[${index}].makerAssetData to be ${makerAssetData} but found ${
order.makerAssetData
}`,
Expand All @@ -53,8 +58,8 @@ export const assert = {
_.every(orders, (order: T, index: number) => {
assert.assert(
order.takerFee.isZero() ||
utils.isOrderTakerFeePayableWithTakerAsset(order) ||
utils.isOrderTakerFeePayableWithMakerAsset(order),
isOrderTakerFeePayableWithTakerAsset(order) ||
isOrderTakerFeePayableWithMakerAsset(order),
`Expected ${variableName}[${index}].takerFeeAssetData to be ${order.makerAssetData} or ${
order.takerAssetData
} but found ${order.takerFeeAssetData}`,
Expand All @@ -72,11 +77,12 @@ export const assert = {
},
isValidForwarderSignedOrder(variableName: string, order: SignedOrder, wethAssetData: string): void {
assert.assert(
utils.isExactAssetData(order.takerAssetData, wethAssetData),
isExactAssetData(order.takerAssetData, wethAssetData),
`Expected ${variableName} to have takerAssetData set as ${wethAssetData}, but is ${order.takerAssetData}`,
);
},
isValidSwapQuoteInfo(variableName: string, swapQuoteInfo: SwapQuoteInfo): void {
sharedAssert.isNumber(`${variableName}.gas`, swapQuoteInfo.gas);
sharedAssert.isBigNumber(`${variableName}.feeTakerAssetAmount`, swapQuoteInfo.feeTakerAssetAmount);
sharedAssert.isBigNumber(`${variableName}.totalTakerAssetAmount`, swapQuoteInfo.totalTakerAssetAmount);
sharedAssert.isBigNumber(`${variableName}.takerAssetAmount`, swapQuoteInfo.takerAssetAmount);
Expand Down
6 changes: 3 additions & 3 deletions packages/asset-swapper/src/utils/calculate_liquidity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ import { BigNumber } from '@0x/utils';

import { LiquidityForTakerMakerAssetDataPair, SignedOrderWithFillableAmounts } from '../types';

import { utils } from './utils';
import { isOrderTakerFeePayableWithMakerAsset, isOrderTakerFeePayableWithTakerAsset } from './utils';

export const calculateLiquidity = (
prunedOrders: SignedOrderWithFillableAmounts[],
): LiquidityForTakerMakerAssetDataPair => {
const liquidityInBigNumbers = prunedOrders.reduce(
(acc, order) => {
const fillableMakerAssetAmount = utils.isOrderTakerFeePayableWithMakerAsset(order)
const fillableMakerAssetAmount = isOrderTakerFeePayableWithMakerAsset(order)
? order.fillableMakerAssetAmount.minus(order.fillableTakerFeeAmount)
: order.fillableMakerAssetAmount;
const fillableTakerAssetAmount = utils.isOrderTakerFeePayableWithTakerAsset(order)
const fillableTakerAssetAmount = isOrderTakerFeePayableWithTakerAsset(order)
? order.fillableTakerAssetAmount.plus(order.fillableTakerFeeAmount)
: order.fillableTakerAssetAmount;
return {
Expand Down
6 changes: 3 additions & 3 deletions packages/asset-swapper/src/utils/fillable_amounts_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ import * as _ from 'lodash';

import { SignedOrderWithFillableAmounts } from '../types';

import { utils } from './utils';
import { isOrderTakerFeePayableWithMakerAsset, isOrderTakerFeePayableWithTakerAsset } from './utils';

export const fillableAmountsUtils = {
getTakerAssetAmountSwappedAfterOrderFees(order: SignedOrderWithFillableAmounts): BigNumber {
if (utils.isOrderTakerFeePayableWithTakerAsset(order)) {
if (isOrderTakerFeePayableWithTakerAsset(order)) {
return order.fillableTakerAssetAmount.plus(order.fillableTakerFeeAmount);
} else {
return order.fillableTakerAssetAmount;
}
},
getMakerAssetAmountSwappedAfterOrderFees(order: SignedOrderWithFillableAmounts): BigNumber {
if (utils.isOrderTakerFeePayableWithMakerAsset(order)) {
if (isOrderTakerFeePayableWithMakerAsset(order)) {
return order.fillableMakerAssetAmount.minus(order.fillableTakerFeeAmount);
} else {
return order.fillableMakerAssetAmount;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { BigNumber } from '@0x/utils';

import { ERC20BridgeSource, GetMarketOrdersOpts } from './types';

const INFINITE_TIMESTAMP_SEC = new BigNumber(2524604400);
// tslint:disable: custom-no-magic-numbers

/**
* Valid sources for market sell.
Expand All @@ -27,26 +27,66 @@ export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = {
// tslint:disable-next-line: custom-no-magic-numbers
runLimit: 2 ** 15,
excludedSources: [],
bridgeSlippage: 0.0005,
dustFractionThreshold: 0.0025,
numSamples: 13,
noConflicts: true,
bridgeSlippage: 0.005,
numSamples: 20,
sampleDistributionBase: 1.05,
fees: {},
feeSchedule: {},
gasSchedule: {},
allowFallback: true,
};

/**
* Sources to poll for ETH fee price estimates.
*/
export const FEE_QUOTE_SOURCES = SELL_SOURCES;

export const constants = {
INFINITE_TIMESTAMP_SEC,
SELL_SOURCES,
BUY_SOURCES,
DEFAULT_GET_MARKET_ORDERS_OPTS,
ERC20_PROXY_ID: '0xf47261b0',
FEE_QUOTE_SOURCES,
WALLET_SIGNATURE: '0x04',
ONE_ETHER: new BigNumber(1e18),
/**
* Mainnet Curve configuration
*/
export const DEFAULT_CURVE_OPTS: { [source: string]: { version: number; curveAddress: string; tokens: string[] } } = {
[ERC20BridgeSource.CurveUsdcDai]: {
version: 1,
curveAddress: '0xa2b47e3d5c44877cca798226b7b8118f9bfb7a56',
tokens: ['0x6b175474e89094c44da98b954eedeac495271d0f', '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'],
},
[ERC20BridgeSource.CurveUsdcDaiUsdt]: {
version: 1,
curveAddress: '0x52ea46506b9cc5ef470c5bf89f17dc28bb35d85c',
tokens: [
'0x6b175474e89094c44da98b954eedeac495271d0f',
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
'0xdac17f958d2ee523a2206206994597c13d831ec7',
],
},
[ERC20BridgeSource.CurveUsdcDaiUsdtTusd]: {
version: 1,
curveAddress: '0x45f783cce6b7ff23b2ab2d70e416cdb7d6055f51',
tokens: [
'0x6b175474e89094c44da98b954eedeac495271d0f',
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
'0xdac17f958d2ee523a2206206994597c13d831ec7',
'0x0000000000085d4780b73119b644ae5ecd22b376',
],
},
[ERC20BridgeSource.CurveUsdcDaiUsdtBusd]: {
version: 1,
curveAddress: '0x79a8c46dea5ada233abaffd40f3a0a2b1e5a4f27',
tokens: [
'0x6b175474e89094c44da98b954eedeac495271d0f',
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
'0xdac17f958d2ee523a2206206994597c13d831ec7',
'0x4fabb145d64652a948d72533023f6e7a623c7c53',
],
},
};

export const ERC20_PROXY_ID = '0xf47261b0';
export const WALLET_SIGNATURE = '0x04';
export const ONE_ETHER = new BigNumber(1e18);
export const NEGATIVE_INF = new BigNumber('-Infinity');
export const POSITIVE_INF = new BigNumber('Infinity');
export const ZERO_AMOUNT = new BigNumber(0);
export const ONE_HOUR_IN_SECONDS = 60 * 60;
export const ONE_SECOND_MS = 1000;
export const NULL_BYTES = '0x';
export const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
Loading