feat: asset-swapper tweak the gas schedule + return decimals (#34)
* feat: asset-swapper Return decimals from sampler in quote * feat: asset-swapper tweak the gas schedule * fix lint * CHANGELOG
This commit is contained in:
parent
3133c509f9
commit
4f82543bdf
@ -1,4 +1,17 @@
|
||||
[
|
||||
{
|
||||
"version": "5.2.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Update Gas schedules",
|
||||
"pr": 34
|
||||
},
|
||||
{
|
||||
"note": "Return the maker/taker token decimals from the sampler as part of the `SwapQuote`",
|
||||
"pr": 34
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "5.1.1",
|
||||
"changes": [
|
||||
|
@ -64,7 +64,7 @@
|
||||
"@0x/dev-utils": "^4.0.1",
|
||||
"@0x/json-schemas": "^5.3.3",
|
||||
"@0x/order-utils": "^10.4.6",
|
||||
"@0x/orderbook": "^2.2.7",
|
||||
"@0x/orderbook": "0xProject/gitpkg-registry#0x-orderbook-v2.2.7-e10a81023",
|
||||
"@0x/quote-server": "^3.1.0",
|
||||
"@0x/types": "^3.3.0",
|
||||
"@0x/typescript-typings": "^5.1.5",
|
||||
|
@ -503,6 +503,8 @@ export class SwapQuoter {
|
||||
return {
|
||||
bids: getMarketDepthSide(bids),
|
||||
asks: getMarketDepthSide(asks),
|
||||
makerTokenDecimals: asks.makerTokenDecimals,
|
||||
takerTokenDecimals: asks.takerTokenDecimals,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -188,6 +188,8 @@ export interface SwapQuoteBase {
|
||||
sourceBreakdown: SwapQuoteOrdersBreakdown;
|
||||
quoteReport?: QuoteReport;
|
||||
isTwoHop: boolean;
|
||||
makerTokenDecimals: number;
|
||||
takerTokenDecimals: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -429,13 +429,20 @@ export const BRIDGE_ADDRESSES_BY_CHAIN: { [chainId in ChainId]: BridgeContractAd
|
||||
[ChainId.Ganache]: EMPTY_BRIDGE_ADDRESSES,
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculated gross gas cost of the underlying exchange.
|
||||
* The cost of switching from one source to another, assuming
|
||||
* we are in the middle of a transaction.
|
||||
* I.e remove the overhead cost of ExchangeProxy (130k) and
|
||||
* the ethereum transaction cost (21k)
|
||||
*/
|
||||
// tslint:disable:custom-no-magic-numbers
|
||||
export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
|
||||
[ERC20BridgeSource.Native]: () => 150e3,
|
||||
[ERC20BridgeSource.Uniswap]: () => 90e3,
|
||||
[ERC20BridgeSource.LiquidityProvider]: () => 140e3,
|
||||
[ERC20BridgeSource.Eth2Dai]: () => 400e3,
|
||||
[ERC20BridgeSource.Kyber]: () => 500e3,
|
||||
[ERC20BridgeSource.Kyber]: () => 450e3,
|
||||
[ERC20BridgeSource.Curve]: fillData => {
|
||||
const poolAddress = (fillData as CurveFillData).pool.poolAddress.toLowerCase();
|
||||
switch (poolAddress) {
|
||||
@ -452,12 +459,14 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
|
||||
case POOLS.curve_BUSD:
|
||||
return 850e3;
|
||||
// Metapools
|
||||
case POOLS.curve_GUSD:
|
||||
case POOLS.curve_HUSD:
|
||||
case POOLS.curve_USDN:
|
||||
case POOLS.curve_mUSD:
|
||||
return 300e3;
|
||||
case POOLS.curve_GUSD:
|
||||
case POOLS.curve_HUSD:
|
||||
return 310e3;
|
||||
case POOLS.curve_tBTC:
|
||||
return 650e3;
|
||||
return 370e3;
|
||||
default:
|
||||
throw new Error(`Unrecognized Curve address: ${poolAddress}`);
|
||||
}
|
||||
@ -482,11 +491,11 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
|
||||
return gas;
|
||||
},
|
||||
[ERC20BridgeSource.Balancer]: () => 120e3,
|
||||
[ERC20BridgeSource.Cream]: () => 300e3,
|
||||
[ERC20BridgeSource.Cream]: () => 120e3,
|
||||
[ERC20BridgeSource.MStable]: () => 700e3,
|
||||
[ERC20BridgeSource.Mooniswap]: () => 220e3,
|
||||
[ERC20BridgeSource.Mooniswap]: () => 130e3,
|
||||
[ERC20BridgeSource.Swerve]: () => 150e3,
|
||||
[ERC20BridgeSource.Shell]: () => 300e3,
|
||||
[ERC20BridgeSource.Shell]: () => 170e3,
|
||||
[ERC20BridgeSource.MultiHop]: (fillData?: FillData) => {
|
||||
const firstHop = (fillData as MultiHopFillData).firstHopSource;
|
||||
const secondHop = (fillData as MultiHopFillData).secondHopSource;
|
||||
@ -501,7 +510,7 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
|
||||
const isSellBase = (fillData as DODOFillData).isSellBase;
|
||||
// Sell base is cheaper as it is natively supported
|
||||
// sell quote requires additional calculation and overhead
|
||||
return isSellBase ? 440e3 : 540e3;
|
||||
return isSellBase ? 180e3 : 300e3;
|
||||
},
|
||||
[ERC20BridgeSource.SnowSwap]: fillData => {
|
||||
switch ((fillData as SnowSwapFillData).pool.poolAddress.toLowerCase()) {
|
||||
|
@ -4,7 +4,7 @@ import { BigNumber, NULL_ADDRESS } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { AssetSwapperContractAddresses, MarketOperation, Omit } from '../../types';
|
||||
import { AssetSwapperContractAddresses, MarketOperation } from '../../types';
|
||||
import { QuoteRequestor } from '../quote_requestor';
|
||||
import { getPriceAwareRFQRolloutFlags } from '../utils';
|
||||
|
||||
@ -38,7 +38,6 @@ import {
|
||||
GenerateOptimizedOrdersOpts,
|
||||
GetMarketOrdersOpts,
|
||||
MarketSideLiquidity,
|
||||
OptimizedMarketOrder,
|
||||
OptimizerResult,
|
||||
OptimizerResultWithReport,
|
||||
OrderDomain,
|
||||
@ -406,7 +405,7 @@ export class MarketOperationUtils {
|
||||
batchNativeOrders: SignedOrder[][],
|
||||
makerAmounts: BigNumber[],
|
||||
opts?: Partial<GetMarketOrdersOpts>,
|
||||
): Promise<Array<OptimizedMarketOrder[] | undefined>> {
|
||||
): Promise<Array<OptimizerResult | undefined>> {
|
||||
if (batchNativeOrders.length === 0) {
|
||||
throw new Error(AggregationError.EmptyOrders);
|
||||
}
|
||||
@ -437,12 +436,16 @@ export class MarketOperationUtils {
|
||||
[makerAmounts[i]],
|
||||
),
|
||||
),
|
||||
...batchNativeOrders.map(orders =>
|
||||
this._sampler.getTokenDecimals(getNativeOrderTokens(orders[0])[0], getNativeOrderTokens(orders[0])[1]),
|
||||
),
|
||||
];
|
||||
|
||||
const executeResults = await this._sampler.executeBatchAsync(ops);
|
||||
const batchOrderFillableAmounts = executeResults.splice(0, batchNativeOrders.length) as BigNumber[][];
|
||||
const batchEthToTakerAssetRate = executeResults.splice(0, batchNativeOrders.length) as BigNumber[];
|
||||
const batchDexQuotes = executeResults.splice(0, batchNativeOrders.length) as DexSample[][][];
|
||||
const batchTokenDecimals = executeResults.splice(0, batchNativeOrders.length) as number[][];
|
||||
const ethToInputRate = ZERO_AMOUNT;
|
||||
|
||||
return Promise.all(
|
||||
@ -456,7 +459,7 @@ export class MarketOperationUtils {
|
||||
const dexQuotes = batchDexQuotes[i];
|
||||
const makerAmount = makerAmounts[i];
|
||||
try {
|
||||
const { optimizedOrders } = await this._generateOptimizedOrdersAsync(
|
||||
const optimizerResult = await this._generateOptimizedOrdersAsync(
|
||||
{
|
||||
side: MarketOperation.Buy,
|
||||
nativeOrders,
|
||||
@ -470,6 +473,8 @@ export class MarketOperationUtils {
|
||||
outputToken: takerToken,
|
||||
twoHopQuotes: [],
|
||||
quoteSourceFilters,
|
||||
makerTokenDecimals: batchTokenDecimals[i][0],
|
||||
takerTokenDecimals: batchTokenDecimals[i][1],
|
||||
},
|
||||
{
|
||||
bridgeSlippage: _opts.bridgeSlippage,
|
||||
@ -479,7 +484,7 @@ export class MarketOperationUtils {
|
||||
allowFallback: _opts.allowFallback,
|
||||
},
|
||||
);
|
||||
return optimizedOrders;
|
||||
return optimizerResult;
|
||||
} catch (e) {
|
||||
// It's possible for one of the pairs to have no path
|
||||
// rather than throw NO_OPTIMAL_PATH we return undefined
|
||||
@ -490,7 +495,7 @@ export class MarketOperationUtils {
|
||||
}
|
||||
|
||||
public async _generateOptimizedOrdersAsync(
|
||||
marketSideLiquidity: Omit<MarketSideLiquidity, 'makerTokenDecimals' | 'takerTokenDecimals'>,
|
||||
marketSideLiquidity: MarketSideLiquidity,
|
||||
opts: GenerateOptimizedOrdersOpts,
|
||||
): Promise<OptimizerResult> {
|
||||
const {
|
||||
@ -553,6 +558,7 @@ export class MarketOperationUtils {
|
||||
optimizedOrders: twoHopOrders,
|
||||
liquidityDelivered: bestTwoHopQuote,
|
||||
sourceFlags: SOURCE_FLAGS[ERC20BridgeSource.MultiHop],
|
||||
marketSideLiquidity,
|
||||
};
|
||||
}
|
||||
|
||||
@ -582,6 +588,7 @@ export class MarketOperationUtils {
|
||||
optimizedOrders: collapsedPath.orders,
|
||||
liquidityDelivered: collapsedPath.collapsedFills as CollapsedFill[],
|
||||
sourceFlags: collapsedPath.sourceFlags,
|
||||
marketSideLiquidity,
|
||||
};
|
||||
}
|
||||
|
||||
@ -606,7 +613,7 @@ export class MarketOperationUtils {
|
||||
side === MarketOperation.Sell
|
||||
? this.getMarketSellLiquidityAsync.bind(this)
|
||||
: this.getMarketBuyLiquidityAsync.bind(this);
|
||||
const marketSideLiquidity = await marketLiquidityFnAsync(nativeOrders, amount, _opts);
|
||||
const marketSideLiquidity: MarketSideLiquidity = await marketLiquidityFnAsync(nativeOrders, amount, _opts);
|
||||
let optimizerResult: OptimizerResult | undefined;
|
||||
try {
|
||||
optimizerResult = await this._generateOptimizedOrdersAsync(marketSideLiquidity, optimizerOpts);
|
||||
|
@ -339,6 +339,7 @@ export interface OptimizerResult {
|
||||
optimizedOrders: OptimizedMarketOrder[];
|
||||
sourceFlags: number;
|
||||
liquidityDelivered: CollapsedFill[] | DexSample<MultiHopFillData>;
|
||||
marketSideLiquidity: MarketSideLiquidity;
|
||||
}
|
||||
|
||||
export interface OptimizerResultWithReport extends OptimizerResult {
|
||||
@ -350,6 +351,8 @@ export type MarketDepthSide = Array<Array<DexSample<FillData>>>;
|
||||
export interface MarketDepth {
|
||||
bids: MarketDepthSide;
|
||||
asks: MarketDepthSide;
|
||||
makerTokenDecimals: number;
|
||||
takerTokenDecimals: number;
|
||||
}
|
||||
|
||||
export interface MarketSideLiquidity {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { AssetProxyId, SignedOrder } from '@0x/types';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
@ -17,7 +16,6 @@ import {
|
||||
|
||||
import { MarketOperationUtils } from './market_operation_utils';
|
||||
import { SOURCE_FLAGS } from './market_operation_utils/constants';
|
||||
import { convertNativeOrderToFullyFillableOptimizedOrders } from './market_operation_utils/orders';
|
||||
import {
|
||||
ERC20BridgeSource,
|
||||
FeeSchedule,
|
||||
@ -89,24 +87,26 @@ export class SwapQuoteCalculator {
|
||||
operation: MarketOperation,
|
||||
opts: CalculateSwapQuoteOpts,
|
||||
): Promise<Array<SwapQuote | undefined>> {
|
||||
const batchSignedOrders = await this._marketOperationUtils.getBatchMarketBuyOrdersAsync(
|
||||
const optimizerResults = await this._marketOperationUtils.getBatchMarketBuyOrdersAsync(
|
||||
batchPrunedOrders,
|
||||
assetFillAmounts,
|
||||
opts,
|
||||
);
|
||||
|
||||
const batchSwapQuotes = await Promise.all(
|
||||
batchSignedOrders.map(async (orders, i) => {
|
||||
if (orders) {
|
||||
optimizerResults.map(async (result, i) => {
|
||||
if (result) {
|
||||
const { makerAssetData, takerAssetData } = batchPrunedOrders[i][0];
|
||||
return createSwapQuote(
|
||||
makerAssetData,
|
||||
takerAssetData,
|
||||
orders,
|
||||
result.optimizedOrders,
|
||||
operation,
|
||||
assetFillAmounts[i],
|
||||
gasPrice,
|
||||
opts.gasSchedule,
|
||||
result.marketSideLiquidity.makerTokenDecimals,
|
||||
result.marketSideLiquidity.takerTokenDecimals,
|
||||
);
|
||||
} else {
|
||||
return undefined;
|
||||
@ -122,7 +122,7 @@ export class SwapQuoteCalculator {
|
||||
operation: MarketOperation,
|
||||
opts: CalculateSwapQuoteOpts,
|
||||
): Promise<SwapQuote> {
|
||||
// checks if maker asset is ERC721 or ERC20 and taker asset is ERC20
|
||||
// checks if maker asset is ERC20 and taker asset is ERC20
|
||||
if (!isSupportedAssetDataInOrders(prunedOrders)) {
|
||||
throw Error(SwapQuoterError.AssetDataUnsupported);
|
||||
}
|
||||
@ -131,6 +131,8 @@ export class SwapQuoteCalculator {
|
||||
let optimizedOrders: OptimizedMarketOrder[];
|
||||
let quoteReport: QuoteReport | undefined;
|
||||
let sourceFlags: number = 0;
|
||||
let makerTokenDecimals: number;
|
||||
let takerTokenDecimals: number;
|
||||
|
||||
// Scale fees by gas price.
|
||||
const _opts: GetMarketOrdersOpts = {
|
||||
@ -141,34 +143,16 @@ export class SwapQuoteCalculator {
|
||||
exchangeProxyOverhead: flags => gasPrice.times(opts.exchangeProxyOverhead(flags)),
|
||||
};
|
||||
|
||||
const firstOrderMakerAssetData = !!prunedOrders[0]
|
||||
? assetDataUtils.decodeAssetDataOrThrow(prunedOrders[0].makerAssetData)
|
||||
: { assetProxyId: '' };
|
||||
const result =
|
||||
operation === MarketOperation.Buy
|
||||
? await this._marketOperationUtils.getMarketBuyOrdersAsync(prunedOrders, assetFillAmount, _opts)
|
||||
: await this._marketOperationUtils.getMarketSellOrdersAsync(prunedOrders, assetFillAmount, _opts);
|
||||
|
||||
if (firstOrderMakerAssetData.assetProxyId === AssetProxyId.ERC721) {
|
||||
// HACK: to conform ERC721 orders to the output of market operation utils, assumes complete fillable
|
||||
optimizedOrders = prunedOrders.map(o => convertNativeOrderToFullyFillableOptimizedOrders(o));
|
||||
} else {
|
||||
if (operation === MarketOperation.Buy) {
|
||||
const buyResult = await this._marketOperationUtils.getMarketBuyOrdersAsync(
|
||||
prunedOrders,
|
||||
assetFillAmount,
|
||||
_opts,
|
||||
);
|
||||
optimizedOrders = buyResult.optimizedOrders;
|
||||
quoteReport = buyResult.quoteReport;
|
||||
sourceFlags = buyResult.sourceFlags;
|
||||
} else {
|
||||
const sellResult = await this._marketOperationUtils.getMarketSellOrdersAsync(
|
||||
prunedOrders,
|
||||
assetFillAmount,
|
||||
_opts,
|
||||
);
|
||||
optimizedOrders = sellResult.optimizedOrders;
|
||||
quoteReport = sellResult.quoteReport;
|
||||
sourceFlags = sellResult.sourceFlags;
|
||||
}
|
||||
}
|
||||
optimizedOrders = result.optimizedOrders;
|
||||
quoteReport = result.quoteReport;
|
||||
sourceFlags = result.sourceFlags;
|
||||
makerTokenDecimals = result.marketSideLiquidity.makerTokenDecimals;
|
||||
takerTokenDecimals = result.marketSideLiquidity.takerTokenDecimals;
|
||||
|
||||
// assetData information for the result
|
||||
const { makerAssetData, takerAssetData } = prunedOrders[0];
|
||||
@ -182,6 +166,8 @@ export class SwapQuoteCalculator {
|
||||
assetFillAmount,
|
||||
gasPrice,
|
||||
opts.gasSchedule,
|
||||
makerTokenDecimals,
|
||||
takerTokenDecimals,
|
||||
quoteReport,
|
||||
)
|
||||
: createSwapQuote(
|
||||
@ -192,6 +178,8 @@ export class SwapQuoteCalculator {
|
||||
assetFillAmount,
|
||||
gasPrice,
|
||||
opts.gasSchedule,
|
||||
makerTokenDecimals,
|
||||
takerTokenDecimals,
|
||||
quoteReport,
|
||||
);
|
||||
// Use the raw gas, not scaled by gas price
|
||||
@ -210,6 +198,8 @@ function createSwapQuote(
|
||||
assetFillAmount: BigNumber,
|
||||
gasPrice: BigNumber,
|
||||
gasSchedule: FeeSchedule,
|
||||
makerTokenDecimals: number,
|
||||
takerTokenDecimals: number,
|
||||
quoteReport?: QuoteReport,
|
||||
): SwapQuote {
|
||||
const bestCaseFillResult = simulateBestCaseFill({
|
||||
@ -245,12 +235,16 @@ function createSwapQuote(
|
||||
...quoteBase,
|
||||
type: MarketOperation.Buy,
|
||||
makerAssetFillAmount: assetFillAmount,
|
||||
makerTokenDecimals,
|
||||
takerTokenDecimals,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...quoteBase,
|
||||
type: MarketOperation.Sell,
|
||||
takerAssetFillAmount: assetFillAmount,
|
||||
makerTokenDecimals,
|
||||
takerTokenDecimals,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -263,6 +257,8 @@ function createTwoHopSwapQuote(
|
||||
assetFillAmount: BigNumber,
|
||||
gasPrice: BigNumber,
|
||||
gasSchedule: FeeSchedule,
|
||||
makerTokenDecimals: number,
|
||||
takerTokenDecimals: number,
|
||||
quoteReport?: QuoteReport,
|
||||
): SwapQuote {
|
||||
const [firstHopOrder, secondHopOrder] = optimizedOrders;
|
||||
@ -312,12 +308,16 @@ function createTwoHopSwapQuote(
|
||||
...quoteBase,
|
||||
type: MarketOperation.Buy,
|
||||
makerAssetFillAmount: assetFillAmount,
|
||||
makerTokenDecimals,
|
||||
takerTokenDecimals,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...quoteBase,
|
||||
type: MarketOperation.Sell,
|
||||
takerAssetFillAmount: assetFillAmount,
|
||||
makerTokenDecimals,
|
||||
takerTokenDecimals,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -33,8 +33,7 @@ export function isSupportedAssetDataInOrders(orders: SignedOrder[]): boolean {
|
||||
const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(o.takerAssetData);
|
||||
const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(o.makerAssetData);
|
||||
return (
|
||||
(makerAssetData.assetProxyId === AssetProxyId.ERC20 ||
|
||||
makerAssetData.assetProxyId === AssetProxyId.ERC721) &&
|
||||
makerAssetData.assetProxyId === AssetProxyId.ERC20 &&
|
||||
takerAssetData.assetProxyId === AssetProxyId.ERC20 &&
|
||||
firstOrderMakerAssetData.assetProxyId === makerAssetData.assetProxyId
|
||||
); // checks that all native order maker assets are of the same type
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { ERC20BridgeSource } from '../../src';
|
||||
import { constants } from '../../src/constants';
|
||||
@ -47,12 +46,16 @@ export async function getFullyFillableSwapQuoteWithNoFeesAsync(
|
||||
...quoteBase,
|
||||
type: MarketOperation.Buy,
|
||||
makerAssetFillAmount,
|
||||
makerTokenDecimals: 18,
|
||||
takerTokenDecimals: 18,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...quoteBase,
|
||||
type: MarketOperation.Sell,
|
||||
takerAssetFillAmount: totalTakerAssetAmount,
|
||||
makerTokenDecimals: 18,
|
||||
takerTokenDecimals: 18,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user