Compare commits
16 Commits
@0x/contra
...
protocol@2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fdca24d4e | ||
|
|
42ec0b144e | ||
|
|
3f6ce78b46 | ||
|
|
c1300c1068 | ||
|
|
9a641cfab6 | ||
|
|
60345d4465 | ||
|
|
11dfea47a6 | ||
|
|
55e9dd39a2 | ||
|
|
1993929bed | ||
|
|
e1d81de517 | ||
|
|
a6b3a21635 | ||
|
|
fd59cdc2db | ||
|
|
98e11b5189 | ||
|
|
3bebc7cd62 | ||
|
|
56dab6ae8c | ||
|
|
285f98e9e9 |
@@ -1,4 +1,54 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "16.46.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Enable `Curve` ETH/CVX pool",
|
||||||
|
"pr": 394
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1641863395
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "16.45.2",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Handle 0 output samples and negative adjusted rate native orders in routing",
|
||||||
|
"pr": 387
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1641827361
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "16.45.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Update `Celo` intermediate tokens",
|
||||||
|
"pr": 390
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1641359319
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "16.45.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Capture router timings",
|
||||||
|
"pr": 388
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1641308410
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "16.44.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Update neon-router and use router estimated output amount",
|
||||||
|
"pr": 354
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1640778328
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "16.43.0",
|
"version": "16.43.0",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
|||||||
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v16.46.0 - _January 11, 2022_
|
||||||
|
|
||||||
|
* Enable `Curve` ETH/CVX pool (#394)
|
||||||
|
|
||||||
|
## v16.45.2 - _January 10, 2022_
|
||||||
|
|
||||||
|
* Handle 0 output samples and negative adjusted rate native orders in routing (#387)
|
||||||
|
|
||||||
|
## v16.45.1 - _January 5, 2022_
|
||||||
|
|
||||||
|
* Update `Celo` intermediate tokens (#390)
|
||||||
|
|
||||||
|
## v16.45.0 - _January 4, 2022_
|
||||||
|
|
||||||
|
* Capture router timings (#388)
|
||||||
|
|
||||||
|
## v16.44.0 - _December 29, 2021_
|
||||||
|
|
||||||
|
* Update neon-router and use router estimated output amount (#354)
|
||||||
|
|
||||||
## v16.43.0 - _December 24, 2021_
|
## v16.43.0 - _December 24, 2021_
|
||||||
|
|
||||||
* `UniswapV3` support for `Optimism` (#385)
|
* `UniswapV3` support for `Optimism` (#385)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/asset-swapper",
|
"name": "@0x/asset-swapper",
|
||||||
"version": "16.43.0",
|
"version": "16.46.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
"@0x/contracts-zero-ex": "^0.30.1",
|
"@0x/contracts-zero-ex": "^0.30.1",
|
||||||
"@0x/dev-utils": "^4.2.9",
|
"@0x/dev-utils": "^4.2.9",
|
||||||
"@0x/json-schemas": "^6.3.0",
|
"@0x/json-schemas": "^6.3.0",
|
||||||
"@0x/neon-router": "^0.2.1",
|
"@0x/neon-router": "^0.3.1",
|
||||||
"@0x/protocol-utils": "^1.10.1",
|
"@0x/protocol-utils": "^1.10.1",
|
||||||
"@0x/quote-server": "^6.0.6",
|
"@0x/quote-server": "^6.0.6",
|
||||||
"@0x/types": "^3.3.4",
|
"@0x/types": "^3.3.4",
|
||||||
|
|||||||
@@ -223,7 +223,17 @@ export async function returnQuoteFromAltMMAsync<ResponseT>(
|
|||||||
cancelToken,
|
cancelToken,
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
warningLogger(err, `Alt RFQ MM request failed`);
|
if (err.response) {
|
||||||
|
// request was made and market maker responded
|
||||||
|
warningLogger(
|
||||||
|
{ data: err.response.data, status: err.response.status, headers: err.response.headers },
|
||||||
|
`Alt RFQ MM request failed`,
|
||||||
|
);
|
||||||
|
} else if (err.request) {
|
||||||
|
warningLogger({}, 'Alt RFQ MM no response received');
|
||||||
|
} else {
|
||||||
|
warningLogger({ err: err.message }, 'Failed to construct Alt RFQ MM request');
|
||||||
|
}
|
||||||
throw new Error(`Alt RFQ MM request failed`);
|
throw new Error(`Alt RFQ MM request failed`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -462,6 +462,7 @@ export const MAINNET_TOKENS = {
|
|||||||
CRV: '0xd533a949740bb3306d119cc777fa900ba034cd52',
|
CRV: '0xd533a949740bb3306d119cc777fa900ba034cd52',
|
||||||
MIM: '0x99d8a9c45b2eca8864373a26d1459e3dff1e17f3',
|
MIM: '0x99d8a9c45b2eca8864373a26d1459e3dff1e17f3',
|
||||||
EURT: '0xc581b735a1688071a1746c968e0798d642ede491',
|
EURT: '0xc581b735a1688071a1746c968e0798d642ede491',
|
||||||
|
CVX: '0x4e3fbd56cd56c3e72c1403e103b45db9da5b9d2b',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BSC_TOKENS = {
|
export const BSC_TOKENS = {
|
||||||
@@ -510,9 +511,23 @@ export const AVALANCHE_TOKENS = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const CELO_TOKENS = {
|
export const CELO_TOKENS = {
|
||||||
WETH: '0xe919f65739c26a42616b7b8eedc6b5524d1e3ac4',
|
|
||||||
WCELO: '0x471ece3750da237f93b8e339c536989b8978a438',
|
WCELO: '0x471ece3750da237f93b8e339c536989b8978a438',
|
||||||
|
// Some of these tokens are Optics bridge? tokens which
|
||||||
|
// had an issue and migrated from v1 to v2
|
||||||
|
WETHv1: '0xe919f65739c26a42616b7b8eedc6b5524d1e3ac4',
|
||||||
|
WETH: '0x122013fd7df1c6f636a5bb8f03108e876548b455',
|
||||||
|
WBTC: '0xbaab46e28388d2779e6e31fd00cf0e5ad95e327b',
|
||||||
|
cUSD: '0x765de816845861e75a25fca122bb6898b8b1282a',
|
||||||
|
// ??
|
||||||
|
WBTCv1: '0xd629eb00deced2a080b7ec630ef6ac117e614f1b',
|
||||||
|
cETH: '0x2def4285787d58a2f811af24755a8150622f4361',
|
||||||
|
UBE: '0x00be915b9dcf56a3cbe739d9b9c202ca692409ec',
|
||||||
|
// Moolah
|
||||||
|
mCELO: '0x7d00cd74ff385c955ea3d79e47bf06bd7386387d',
|
||||||
mCUSD: '0x918146359264c492bd6934071c6bd31c854edbc3',
|
mCUSD: '0x918146359264c492bd6934071c6bd31c854edbc3',
|
||||||
|
mCEUR: '0xe273ad7ee11dcfaa87383ad5977ee1504ac07568',
|
||||||
|
amCUSD: '0x64defa3544c695db8c535d289d843a189aa26b98',
|
||||||
|
MOO: '0x17700282592d6917f6a73d0bf8accf4d578c131e',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FANTOM_TOKENS = {
|
export const FANTOM_TOKENS = {
|
||||||
@@ -578,6 +593,7 @@ export const CURVE_POOLS = {
|
|||||||
mim: '0x5a6a4d54456819380173272a5e8e9b9904bdf41b',
|
mim: '0x5a6a4d54456819380173272a5e8e9b9904bdf41b',
|
||||||
eurt: '0xfd5db7463a3ab53fd211b4af195c5bccc1a03890',
|
eurt: '0xfd5db7463a3ab53fd211b4af195c5bccc1a03890',
|
||||||
ethcrv: '0x8301ae4fc9c624d1d396cbdaa1ed877821d7c511',
|
ethcrv: '0x8301ae4fc9c624d1d396cbdaa1ed877821d7c511',
|
||||||
|
ethcvx: '0xb576491f1e6e5e62f1d8f26062ee822b40b0e0d4',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CURVE_V2_POOLS = {
|
export const CURVE_V2_POOLS = {
|
||||||
@@ -713,7 +729,7 @@ export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId<string[]>(
|
|||||||
AVALANCHE_TOKENS.USDC,
|
AVALANCHE_TOKENS.USDC,
|
||||||
],
|
],
|
||||||
[ChainId.Fantom]: [FANTOM_TOKENS.WFTM, FANTOM_TOKENS.WETH, FANTOM_TOKENS.DAI, FANTOM_TOKENS.USDC],
|
[ChainId.Fantom]: [FANTOM_TOKENS.WFTM, FANTOM_TOKENS.WETH, FANTOM_TOKENS.DAI, FANTOM_TOKENS.USDC],
|
||||||
[ChainId.Celo]: [CELO_TOKENS.mCUSD, CELO_TOKENS.WETH, CELO_TOKENS.WCELO],
|
[ChainId.Celo]: [CELO_TOKENS.WCELO, CELO_TOKENS.mCUSD, CELO_TOKENS.WETH, CELO_TOKENS.amCUSD, CELO_TOKENS.WBTC],
|
||||||
[ChainId.Optimism]: [OPTIMISM_TOKENS.WETH, OPTIMISM_TOKENS.DAI, OPTIMISM_TOKENS.USDC],
|
[ChainId.Optimism]: [OPTIMISM_TOKENS.WETH, OPTIMISM_TOKENS.DAI, OPTIMISM_TOKENS.USDC],
|
||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
@@ -1059,6 +1075,17 @@ export const CURVE_MAINNET_INFOS: { [name: string]: CurveInfo } = {
|
|||||||
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_uint256,
|
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_uint256,
|
||||||
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying_uint256,
|
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying_uint256,
|
||||||
},
|
},
|
||||||
|
[CURVE_POOLS.ethcvx]: {
|
||||||
|
...createCurveExchangePool({
|
||||||
|
// This pool uses ETH
|
||||||
|
tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.CVX],
|
||||||
|
pool: CURVE_POOLS.ethcvx,
|
||||||
|
gasSchedule: 350e3,
|
||||||
|
}),
|
||||||
|
// This pool has a custom get_dy and exchange selector with uint256
|
||||||
|
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_uint256,
|
||||||
|
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying_uint256,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CURVE_V2_MAINNET_INFOS: { [name: string]: CurveInfo } = {
|
export const CURVE_V2_MAINNET_INFOS: { [name: string]: CurveInfo } = {
|
||||||
@@ -2103,4 +2130,5 @@ export const DEFAULT_GET_MARKET_ORDERS_OPTS: Omit<GetMarketOrdersOpts, 'gasPrice
|
|||||||
shouldGenerateQuoteReport: true,
|
shouldGenerateQuoteReport: true,
|
||||||
shouldIncludePriceComparisonsReport: false,
|
shouldIncludePriceComparisonsReport: false,
|
||||||
tokenAdjacencyGraph: { default: [] },
|
tokenAdjacencyGraph: { default: [] },
|
||||||
|
neonRouterNumSamples: 14,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -443,6 +443,7 @@ export class MarketOperationUtils {
|
|||||||
feeSchedule: _opts.feeSchedule,
|
feeSchedule: _opts.feeSchedule,
|
||||||
allowFallback: _opts.allowFallback,
|
allowFallback: _opts.allowFallback,
|
||||||
gasPrice: _opts.gasPrice,
|
gasPrice: _opts.gasPrice,
|
||||||
|
neonRouterNumSamples: _opts.neonRouterNumSamples,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return optimizerResult;
|
return optimizerResult;
|
||||||
@@ -531,9 +532,18 @@ export class MarketOperationUtils {
|
|||||||
penaltyOpts,
|
penaltyOpts,
|
||||||
opts.feeSchedule,
|
opts.feeSchedule,
|
||||||
this._sampler.chainId,
|
this._sampler.chainId,
|
||||||
|
opts.neonRouterNumSamples,
|
||||||
|
opts.samplerMetrics,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
optimalPath = await findOptimalPathJSAsync(side, fills, inputAmount, opts.runLimit, penaltyOpts);
|
optimalPath = await findOptimalPathJSAsync(
|
||||||
|
side,
|
||||||
|
fills,
|
||||||
|
inputAmount,
|
||||||
|
opts.runLimit,
|
||||||
|
opts.samplerMetrics,
|
||||||
|
penaltyOpts,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const optimalPathRate = optimalPath ? optimalPath.adjustedRate() : ZERO_AMOUNT;
|
const optimalPathRate = optimalPath ? optimalPath.adjustedRate() : ZERO_AMOUNT;
|
||||||
@@ -596,6 +606,8 @@ export class MarketOperationUtils {
|
|||||||
allowFallback: _opts.allowFallback,
|
allowFallback: _opts.allowFallback,
|
||||||
exchangeProxyOverhead: _opts.exchangeProxyOverhead,
|
exchangeProxyOverhead: _opts.exchangeProxyOverhead,
|
||||||
gasPrice: _opts.gasPrice,
|
gasPrice: _opts.gasPrice,
|
||||||
|
neonRouterNumSamples: _opts.neonRouterNumSamples,
|
||||||
|
samplerMetrics: _opts.samplerMetrics,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (nativeOrders.length === 0) {
|
if (nativeOrders.length === 0) {
|
||||||
@@ -806,6 +818,8 @@ export class MarketOperationUtils {
|
|||||||
sturdyPenaltyOpts,
|
sturdyPenaltyOpts,
|
||||||
opts.feeSchedule,
|
opts.feeSchedule,
|
||||||
this._sampler.chainId,
|
this._sampler.chainId,
|
||||||
|
opts.neonRouterNumSamples,
|
||||||
|
undefined, // hack: set sampler metrics to undefined to avoid fallback timings
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const sturdyFills = fills.filter(p => p.length > 0 && !fragileSources.includes(p[0].source));
|
const sturdyFills = fills.filter(p => p.length > 0 && !fragileSources.includes(p[0].source));
|
||||||
@@ -814,6 +828,7 @@ export class MarketOperationUtils {
|
|||||||
sturdyFills,
|
sturdyFills,
|
||||||
inputAmount,
|
inputAmount,
|
||||||
opts.runLimit,
|
opts.runLimit,
|
||||||
|
undefined, // hack: set sampler metrics to undefined to avoid fallback timings
|
||||||
sturdyPenaltyOpts,
|
sturdyPenaltyOpts,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,21 @@
|
|||||||
import { assert } from '@0x/assert';
|
import { assert } from '@0x/assert';
|
||||||
import { ChainId } from '@0x/contract-addresses';
|
import { ChainId } from '@0x/contract-addresses';
|
||||||
import { OptimizerCapture, route, SerializedPath } from '@0x/neon-router';
|
import { OptimizerCapture, route, SerializedPath } from '@0x/neon-router';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber, hexUtils } from '@0x/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { performance } from 'perf_hooks';
|
import { performance } from 'perf_hooks';
|
||||||
|
|
||||||
import { DEFAULT_INFO_LOGGER } from '../../constants';
|
|
||||||
import { MarketOperation, NativeOrderWithFillableAmounts } from '../../types';
|
import { MarketOperation, NativeOrderWithFillableAmounts } from '../../types';
|
||||||
import { VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID } from '../market_operation_utils/constants';
|
import { VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID } from '../market_operation_utils/constants';
|
||||||
|
|
||||||
import { dexSamplesToFills, ethToOutputAmount, nativeOrdersToFills } from './fills';
|
import { dexSamplesToFills, ethToOutputAmount, nativeOrdersToFills } from './fills';
|
||||||
import { DEFAULT_PATH_PENALTY_OPTS, Path, PathPenaltyOpts } from './path';
|
import { DEFAULT_PATH_PENALTY_OPTS, Path, PathPenaltyOpts } from './path';
|
||||||
import { getRate } from './rate_utils';
|
import { getRate } from './rate_utils';
|
||||||
import { DexSample, ERC20BridgeSource, FeeSchedule, Fill, FillData } from './types';
|
import { DexSample, ERC20BridgeSource, FeeSchedule, Fill, FillData, SamplerMetrics } from './types';
|
||||||
|
|
||||||
// tslint:disable: prefer-for-of custom-no-magic-numbers completed-docs no-bitwise
|
// tslint:disable: prefer-for-of custom-no-magic-numbers completed-docs no-bitwise
|
||||||
|
|
||||||
const RUN_LIMIT_DECAY_FACTOR = 0.5;
|
const RUN_LIMIT_DECAY_FACTOR = 0.5;
|
||||||
const RUST_ROUTER_NUM_SAMPLES = 200;
|
|
||||||
const FILL_QUOTE_TRANSFORMER_GAS_OVERHEAD = new BigNumber(150e3);
|
const FILL_QUOTE_TRANSFORMER_GAS_OVERHEAD = new BigNumber(150e3);
|
||||||
// NOTE: The Rust router will panic with less than 3 samples
|
// NOTE: The Rust router will panic with less than 3 samples
|
||||||
const MIN_NUM_SAMPLE_INPUTS = 3;
|
const MIN_NUM_SAMPLE_INPUTS = 3;
|
||||||
@@ -69,21 +67,6 @@ function calculateOuputFee(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use linear interpolation to approximate the output
|
|
||||||
// at a certain input somewhere between the two samples
|
|
||||||
// See https://en.wikipedia.org/wiki/Linear_interpolation
|
|
||||||
const interpolateOutputFromSamples = (
|
|
||||||
left: { input: BigNumber; output: BigNumber },
|
|
||||||
right: { input: BigNumber; output: BigNumber },
|
|
||||||
targetInput: BigNumber,
|
|
||||||
): BigNumber =>
|
|
||||||
left.output.plus(
|
|
||||||
right.output
|
|
||||||
.minus(left.output)
|
|
||||||
.dividedBy(right.input.minus(left.input))
|
|
||||||
.times(targetInput.minus(left.input)),
|
|
||||||
);
|
|
||||||
|
|
||||||
function findRoutesAndCreateOptimalPath(
|
function findRoutesAndCreateOptimalPath(
|
||||||
side: MarketOperation,
|
side: MarketOperation,
|
||||||
samples: DexSample[][],
|
samples: DexSample[][],
|
||||||
@@ -91,29 +74,27 @@ function findRoutesAndCreateOptimalPath(
|
|||||||
input: BigNumber,
|
input: BigNumber,
|
||||||
opts: PathPenaltyOpts,
|
opts: PathPenaltyOpts,
|
||||||
fees: FeeSchedule,
|
fees: FeeSchedule,
|
||||||
|
neonRouterNumSamples: number,
|
||||||
): Path | undefined {
|
): Path | undefined {
|
||||||
const createFill = (sample: DexSample) =>
|
const createFill = (sample: DexSample): Fill | undefined => {
|
||||||
dexSamplesToFills(side, [sample], opts.outputAmountPerEth, opts.inputAmountPerEth, fees)[0];
|
const fills = dexSamplesToFills(side, [sample], opts.outputAmountPerEth, opts.inputAmountPerEth, fees);
|
||||||
// Track sample id's to integers (required by rust router)
|
// NOTE: If the sample has 0 output dexSamplesToFills will return [] because no fill can be created
|
||||||
const sampleIdLookup: { [key: string]: number } = {};
|
if (fills.length === 0) {
|
||||||
let sampleIdCounter = 0;
|
return undefined;
|
||||||
const sampleToId = (source: ERC20BridgeSource, index: number): number => {
|
|
||||||
const key = `${source}-${index}`;
|
|
||||||
if (sampleIdLookup[key]) {
|
|
||||||
return sampleIdLookup[key];
|
|
||||||
} else {
|
|
||||||
sampleIdLookup[key] = ++sampleIdCounter;
|
|
||||||
return sampleIdLookup[key];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return fills[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
const samplesAndNativeOrdersWithResults: Array<DexSample[] | NativeOrderWithFillableAmounts[]> = [];
|
const samplesAndNativeOrdersWithResults: Array<DexSample[] | NativeOrderWithFillableAmounts[]> = [];
|
||||||
const serializedPaths: SerializedPath[] = [];
|
const serializedPaths: SerializedPath[] = [];
|
||||||
|
const sampleSourcePathIds: string[] = [];
|
||||||
for (const singleSourceSamples of samples) {
|
for (const singleSourceSamples of samples) {
|
||||||
if (singleSourceSamples.length === 0) {
|
if (singleSourceSamples.length === 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sourcePathId = hexUtils.random();
|
||||||
const singleSourceSamplesWithOutput = [...singleSourceSamples];
|
const singleSourceSamplesWithOutput = [...singleSourceSamples];
|
||||||
for (let i = singleSourceSamples.length - 1; i >= 0; i--) {
|
for (let i = singleSourceSamples.length - 1; i >= 0; i--) {
|
||||||
if (singleSourceSamples[i].output.isZero()) {
|
if (singleSourceSamples[i].output.isZero()) {
|
||||||
@@ -131,7 +112,7 @@ function findRoutesAndCreateOptimalPath(
|
|||||||
// TODO(kimpers): Do we need to handle 0 entries, from eg Kyber?
|
// TODO(kimpers): Do we need to handle 0 entries, from eg Kyber?
|
||||||
const serializedPath = singleSourceSamplesWithOutput.reduce<SerializedPath>(
|
const serializedPath = singleSourceSamplesWithOutput.reduce<SerializedPath>(
|
||||||
(memo, sample, sampleIdx) => {
|
(memo, sample, sampleIdx) => {
|
||||||
memo.ids.push(sampleToId(sample.source, sampleIdx));
|
memo.ids.push(`${sample.source}-${serializedPaths.length}-${sampleIdx}`);
|
||||||
memo.inputs.push(sample.input.integerValue().toNumber());
|
memo.inputs.push(sample.input.integerValue().toNumber());
|
||||||
memo.outputs.push(sample.output.integerValue().toNumber());
|
memo.outputs.push(sample.output.integerValue().toNumber());
|
||||||
memo.outputFees.push(
|
memo.outputFees.push(
|
||||||
@@ -152,8 +133,10 @@ function findRoutesAndCreateOptimalPath(
|
|||||||
|
|
||||||
samplesAndNativeOrdersWithResults.push(singleSourceSamplesWithOutput);
|
samplesAndNativeOrdersWithResults.push(singleSourceSamplesWithOutput);
|
||||||
serializedPaths.push(serializedPath);
|
serializedPaths.push(serializedPath);
|
||||||
|
sampleSourcePathIds.push(sourcePathId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const nativeOrdersourcePathId = hexUtils.random();
|
||||||
for (const [idx, nativeOrder] of nativeOrders.entries()) {
|
for (const [idx, nativeOrder] of nativeOrders.entries()) {
|
||||||
const { input: normalizedOrderInput, output: normalizedOrderOutput } = nativeOrderToNormalizedAmounts(
|
const { input: normalizedOrderInput, output: normalizedOrderOutput } = nativeOrderToNormalizedAmounts(
|
||||||
side,
|
side,
|
||||||
@@ -164,32 +147,25 @@ function findRoutesAndCreateOptimalPath(
|
|||||||
if (normalizedOrderInput.isLessThanOrEqualTo(0) || normalizedOrderOutput.isLessThanOrEqualTo(0)) {
|
if (normalizedOrderInput.isLessThanOrEqualTo(0) || normalizedOrderOutput.isLessThanOrEqualTo(0)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACK: the router requires at minimum 3 samples as a basis for interpolation
|
|
||||||
const inputs = [
|
|
||||||
0,
|
|
||||||
normalizedOrderInput
|
|
||||||
.dividedBy(2)
|
|
||||||
.integerValue()
|
|
||||||
.toNumber(),
|
|
||||||
normalizedOrderInput.integerValue().toNumber(),
|
|
||||||
];
|
|
||||||
const outputs = [
|
|
||||||
0,
|
|
||||||
normalizedOrderOutput
|
|
||||||
.dividedBy(2)
|
|
||||||
.integerValue()
|
|
||||||
.toNumber(),
|
|
||||||
normalizedOrderOutput.integerValue().toNumber(),
|
|
||||||
];
|
|
||||||
// NOTE: same fee no matter if full or partial fill
|
|
||||||
const fee = calculateOuputFee(side, nativeOrder, opts.outputAmountPerEth, opts.inputAmountPerEth, fees)
|
const fee = calculateOuputFee(side, nativeOrder, opts.outputAmountPerEth, opts.inputAmountPerEth, fees)
|
||||||
.integerValue()
|
.integerValue()
|
||||||
.toNumber();
|
.toNumber();
|
||||||
const outputFees = [fee, fee, fee];
|
|
||||||
// NOTE: ids can be the same for all fake samples
|
// HACK: due to an issue with the Rust router interpolation we need to create exactly 13 samples from the native order
|
||||||
const id = sampleToId(ERC20BridgeSource.Native, idx);
|
const ids = [];
|
||||||
const ids = [id, id, id];
|
const inputs = [];
|
||||||
|
const outputs = [];
|
||||||
|
const outputFees = [];
|
||||||
|
for (let i = 1; i <= 13; i++) {
|
||||||
|
const fraction = i / 13;
|
||||||
|
const currentInput = BigNumber.min(normalizedOrderInput.times(fraction), normalizedOrderInput);
|
||||||
|
const currentOutput = BigNumber.min(normalizedOrderOutput.times(fraction), normalizedOrderOutput);
|
||||||
|
const id = `${ERC20BridgeSource.Native}-${serializedPaths.length}-${idx}-${i}`;
|
||||||
|
inputs.push(currentInput.integerValue().toNumber());
|
||||||
|
outputs.push(currentOutput.integerValue().toNumber());
|
||||||
|
outputFees.push(fee);
|
||||||
|
ids.push(id);
|
||||||
|
}
|
||||||
|
|
||||||
const serializedPath: SerializedPath = {
|
const serializedPath: SerializedPath = {
|
||||||
ids,
|
ids,
|
||||||
@@ -200,6 +176,7 @@ function findRoutesAndCreateOptimalPath(
|
|||||||
|
|
||||||
samplesAndNativeOrdersWithResults.push([nativeOrder]);
|
samplesAndNativeOrdersWithResults.push([nativeOrder]);
|
||||||
serializedPaths.push(serializedPath);
|
serializedPaths.push(serializedPath);
|
||||||
|
sampleSourcePathIds.push(nativeOrdersourcePathId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serializedPaths.length === 0) {
|
if (serializedPaths.length === 0) {
|
||||||
@@ -212,30 +189,33 @@ function findRoutesAndCreateOptimalPath(
|
|||||||
pathsIn: serializedPaths,
|
pathsIn: serializedPaths,
|
||||||
};
|
};
|
||||||
|
|
||||||
const before = performance.now();
|
|
||||||
const allSourcesRustRoute = new Float64Array(rustArgs.pathsIn.length);
|
const allSourcesRustRoute = new Float64Array(rustArgs.pathsIn.length);
|
||||||
route(rustArgs, allSourcesRustRoute, RUST_ROUTER_NUM_SAMPLES);
|
const strategySourcesOutputAmounts = new Float64Array(rustArgs.pathsIn.length);
|
||||||
DEFAULT_INFO_LOGGER(
|
route(rustArgs, allSourcesRustRoute, strategySourcesOutputAmounts, neonRouterNumSamples);
|
||||||
{ router: 'neon-router', performanceMs: performance.now() - before, type: 'real' },
|
|
||||||
'Rust router real routing performance',
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.assert(
|
assert.assert(
|
||||||
rustArgs.pathsIn.length === allSourcesRustRoute.length,
|
rustArgs.pathsIn.length === allSourcesRustRoute.length,
|
||||||
'different number of sources in the Router output than the input',
|
'different number of sources in the Router output than the input',
|
||||||
);
|
);
|
||||||
|
assert.assert(
|
||||||
|
rustArgs.pathsIn.length === strategySourcesOutputAmounts.length,
|
||||||
|
'different number of sources in the Router output amounts results than the input',
|
||||||
|
);
|
||||||
|
|
||||||
const routesAndSamples = _.zip(allSourcesRustRoute, samplesAndNativeOrdersWithResults);
|
const routesAndSamplesAndOutputs = _.zip(
|
||||||
|
allSourcesRustRoute,
|
||||||
|
samplesAndNativeOrdersWithResults,
|
||||||
|
strategySourcesOutputAmounts,
|
||||||
|
sampleSourcePathIds,
|
||||||
|
);
|
||||||
const adjustedFills: Fill[] = [];
|
const adjustedFills: Fill[] = [];
|
||||||
const totalRoutedAmount = BigNumber.sum(...allSourcesRustRoute);
|
const totalRoutedAmount = BigNumber.sum(...allSourcesRustRoute);
|
||||||
|
|
||||||
const scale = input.dividedBy(totalRoutedAmount);
|
const scale = input.dividedBy(totalRoutedAmount);
|
||||||
for (const [routeInput, routeSamplesAndNativeOrders] of routesAndSamples) {
|
for (const [routeInput, routeSamplesAndNativeOrders, outputAmount, sourcePathId] of routesAndSamplesAndOutputs) {
|
||||||
if (!routeInput || !routeSamplesAndNativeOrders) {
|
if (!routeInput || !routeSamplesAndNativeOrders || !outputAmount || !Number.isFinite(outputAmount)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// TODO(kimpers): [TKR-241] amounts are sometimes clipped in the router due to precisions loss for number/f64
|
// TODO(kimpers): [TKR-241] amounts are sometimes clipped in the router due to precision loss for number/f64
|
||||||
// we can work around it by scaling it and rounding up. However now we end up with a total amount of a couple base units too much
|
// we can work around it by scaling it and rounding up. However now we end up with a total amount of a couple base units too much
|
||||||
const rustInputAdjusted = BigNumber.min(
|
const rustInputAdjusted = BigNumber.min(
|
||||||
new BigNumber(routeInput).multipliedBy(scale).integerValue(BigNumber.ROUND_CEIL),
|
new BigNumber(routeInput).multipliedBy(scale).integerValue(BigNumber.ROUND_CEIL),
|
||||||
@@ -251,14 +231,21 @@ function findRoutesAndCreateOptimalPath(
|
|||||||
opts.outputAmountPerEth,
|
opts.outputAmountPerEth,
|
||||||
opts.inputAmountPerEth,
|
opts.inputAmountPerEth,
|
||||||
fees,
|
fees,
|
||||||
)[0];
|
)[0] as Fill | undefined;
|
||||||
// NOTE: For Limit/RFQ orders we are done here. No need to scale output
|
// Note: If the order has an adjusted rate of less than or equal to 0 it will be skipped
|
||||||
adjustedFills.push(nativeFill);
|
// and nativeFill will be `undefined`
|
||||||
|
if (nativeFill) {
|
||||||
|
// NOTE: For Limit/RFQ orders we are done here. No need to scale output
|
||||||
|
adjustedFills.push({ ...nativeFill, sourcePathId: sourcePathId ?? hexUtils.random() });
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: For DexSamples only
|
// NOTE: For DexSamples only
|
||||||
let fill = createFill(current);
|
let fill = createFill(current);
|
||||||
|
if (!fill) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const routeSamples = routeSamplesAndNativeOrders as Array<DexSample<FillData>>;
|
const routeSamples = routeSamplesAndNativeOrders as Array<DexSample<FillData>>;
|
||||||
// Descend to approach a closer fill for fillData which may not be consistent
|
// Descend to approach a closer fill for fillData which may not be consistent
|
||||||
// throughout the path (UniswapV3) and for a closer guesstimate at
|
// throughout the path (UniswapV3) and for a closer guesstimate at
|
||||||
@@ -267,49 +254,47 @@ function findRoutesAndCreateOptimalPath(
|
|||||||
assert.assert(routeSamples.length >= 1, 'Found no sample to use for source');
|
assert.assert(routeSamples.length >= 1, 'Found no sample to use for source');
|
||||||
for (let k = routeSamples.length - 1; k >= 0; k--) {
|
for (let k = routeSamples.length - 1; k >= 0; k--) {
|
||||||
if (k === 0) {
|
if (k === 0) {
|
||||||
fill = createFill(routeSamples[0]);
|
fill = createFill(routeSamples[0]) ?? fill;
|
||||||
}
|
}
|
||||||
if (rustInputAdjusted.isGreaterThan(routeSamples[k].input)) {
|
if (rustInputAdjusted.isGreaterThan(routeSamples[k].input)) {
|
||||||
// Between here and the previous fill
|
|
||||||
// HACK: Use the midpoint between the two
|
|
||||||
const left = routeSamples[k];
|
const left = routeSamples[k];
|
||||||
const right = routeSamples[k + 1];
|
const right = routeSamples[k + 1];
|
||||||
if (left && right) {
|
if (left && right) {
|
||||||
// Approximate how much output we get for the input with the surrounding samples
|
fill =
|
||||||
const interpolatedOutput = interpolateOutputFromSamples(
|
createFill({
|
||||||
left,
|
...right, // default to the greater (for gas used)
|
||||||
right,
|
input: rustInputAdjusted,
|
||||||
rustInputAdjusted,
|
output: new BigNumber(outputAmount),
|
||||||
).decimalPlaces(0, side === MarketOperation.Sell ? BigNumber.ROUND_FLOOR : BigNumber.ROUND_CEIL);
|
}) ?? fill;
|
||||||
|
|
||||||
fill = createFill({
|
|
||||||
...right, // default to the greater (for gas used)
|
|
||||||
input: rustInputAdjusted,
|
|
||||||
output: interpolatedOutput,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
assert.assert(Boolean(left || right), 'No valid sample to use');
|
assert.assert(Boolean(left || right), 'No valid sample to use');
|
||||||
fill = createFill(left || right);
|
fill = createFill(left || right) ?? fill;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const scaleOutput = (output: BigNumber) =>
|
// TODO(kimpers): remove once we have solved the rounding/precision loss issues in the Rust router
|
||||||
|
const scaleOutput = (fillInput: BigNumber, output: BigNumber) =>
|
||||||
output
|
output
|
||||||
.dividedBy(fill.input)
|
.dividedBy(fillInput)
|
||||||
.times(rustInputAdjusted)
|
.times(rustInputAdjusted)
|
||||||
.decimalPlaces(0, side === MarketOperation.Sell ? BigNumber.ROUND_FLOOR : BigNumber.ROUND_CEIL);
|
.decimalPlaces(0, side === MarketOperation.Sell ? BigNumber.ROUND_FLOOR : BigNumber.ROUND_CEIL);
|
||||||
adjustedFills.push({
|
adjustedFills.push({
|
||||||
...fill,
|
...fill,
|
||||||
input: rustInputAdjusted,
|
input: rustInputAdjusted,
|
||||||
output: scaleOutput(fill.output),
|
output: scaleOutput(fill.input, fill.output),
|
||||||
adjustedOutput: scaleOutput(fill.adjustedOutput),
|
adjustedOutput: scaleOutput(fill.input, fill.adjustedOutput),
|
||||||
index: 0,
|
index: 0,
|
||||||
parent: undefined,
|
parent: undefined,
|
||||||
|
sourcePathId: sourcePathId ?? hexUtils.random(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (adjustedFills.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
const pathFromRustInputs = Path.create(side, adjustedFills, input);
|
const pathFromRustInputs = Path.create(side, adjustedFills, input);
|
||||||
|
|
||||||
return pathFromRustInputs;
|
return pathFromRustInputs;
|
||||||
@@ -323,15 +308,27 @@ export function findOptimalRustPathFromSamples(
|
|||||||
opts: PathPenaltyOpts,
|
opts: PathPenaltyOpts,
|
||||||
fees: FeeSchedule,
|
fees: FeeSchedule,
|
||||||
chainId: ChainId,
|
chainId: ChainId,
|
||||||
|
neonRouterNumSamples: number,
|
||||||
|
samplerMetrics?: SamplerMetrics,
|
||||||
): Path | undefined {
|
): Path | undefined {
|
||||||
const before = performance.now();
|
const beforeAllTimeMs = performance.now();
|
||||||
const logPerformance = () =>
|
let beforeTimeMs = performance.now();
|
||||||
DEFAULT_INFO_LOGGER(
|
const allSourcesPath = findRoutesAndCreateOptimalPath(
|
||||||
{ router: 'neon-router', performanceMs: performance.now() - before, type: 'total' },
|
side,
|
||||||
'Rust router total routing performance',
|
samples,
|
||||||
);
|
nativeOrders,
|
||||||
|
input,
|
||||||
const allSourcesPath = findRoutesAndCreateOptimalPath(side, samples, nativeOrders, input, opts, fees);
|
opts,
|
||||||
|
fees,
|
||||||
|
neonRouterNumSamples,
|
||||||
|
);
|
||||||
|
// tslint:disable-next-line: no-unused-expression
|
||||||
|
samplerMetrics &&
|
||||||
|
samplerMetrics.logRouterDetails({
|
||||||
|
router: 'neon-router',
|
||||||
|
type: 'all',
|
||||||
|
timingMs: performance.now() - beforeTimeMs,
|
||||||
|
});
|
||||||
if (!allSourcesPath) {
|
if (!allSourcesPath) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -341,11 +338,27 @@ export function findOptimalRustPathFromSamples(
|
|||||||
// HACK(kimpers): The Rust router currently doesn't account for VIP sources correctly
|
// HACK(kimpers): The Rust router currently doesn't account for VIP sources correctly
|
||||||
// we need to try to route them in isolation and compare with the results all sources
|
// we need to try to route them in isolation and compare with the results all sources
|
||||||
if (vipSources.length > 0) {
|
if (vipSources.length > 0) {
|
||||||
|
beforeTimeMs = performance.now();
|
||||||
const vipSourcesSet = new Set(vipSources);
|
const vipSourcesSet = new Set(vipSources);
|
||||||
const vipSourcesSamples = samples.filter(s => s[0] && vipSourcesSet.has(s[0].source));
|
const vipSourcesSamples = samples.filter(s => s[0] && vipSourcesSet.has(s[0].source));
|
||||||
|
|
||||||
if (vipSourcesSamples.length > 0) {
|
if (vipSourcesSamples.length > 0) {
|
||||||
const vipSourcesPath = findRoutesAndCreateOptimalPath(side, vipSourcesSamples, [], input, opts, fees);
|
const vipSourcesPath = findRoutesAndCreateOptimalPath(
|
||||||
|
side,
|
||||||
|
vipSourcesSamples,
|
||||||
|
[],
|
||||||
|
input,
|
||||||
|
opts,
|
||||||
|
fees,
|
||||||
|
neonRouterNumSamples,
|
||||||
|
);
|
||||||
|
// tslint:disable-next-line: no-unused-expression
|
||||||
|
samplerMetrics &&
|
||||||
|
samplerMetrics.logRouterDetails({
|
||||||
|
router: 'neon-router',
|
||||||
|
type: 'vip',
|
||||||
|
timingMs: performance.now() - beforeTimeMs,
|
||||||
|
});
|
||||||
|
|
||||||
const { input: allSourcesInput, output: allSourcesOutput } = allSourcesPath.adjustedSize();
|
const { input: allSourcesInput, output: allSourcesOutput } = allSourcesPath.adjustedSize();
|
||||||
// NOTE: For sell quotes input is the taker asset and for buy quotes input is the maker asset
|
// NOTE: For sell quotes input is the taker asset and for buy quotes input is the maker asset
|
||||||
@@ -358,13 +371,18 @@ export function findOptimalRustPathFromSamples(
|
|||||||
const allSourcesAdjustedRateWithFqtOverhead = getRate(side, allSourcesInput, outputWithFqtOverhead);
|
const allSourcesAdjustedRateWithFqtOverhead = getRate(side, allSourcesInput, outputWithFqtOverhead);
|
||||||
|
|
||||||
if (vipSourcesPath?.adjustedRate().isGreaterThan(allSourcesAdjustedRateWithFqtOverhead)) {
|
if (vipSourcesPath?.adjustedRate().isGreaterThan(allSourcesAdjustedRateWithFqtOverhead)) {
|
||||||
logPerformance();
|
|
||||||
return vipSourcesPath;
|
return vipSourcesPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// tslint:disable-next-line: no-unused-expression
|
||||||
|
samplerMetrics &&
|
||||||
|
samplerMetrics.logRouterDetails({
|
||||||
|
router: 'neon-router',
|
||||||
|
type: 'total',
|
||||||
|
timingMs: performance.now() - beforeAllTimeMs,
|
||||||
|
});
|
||||||
|
|
||||||
logPerformance();
|
|
||||||
return allSourcesPath;
|
return allSourcesPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,8 +395,10 @@ export async function findOptimalPathJSAsync(
|
|||||||
fills: Fill[][],
|
fills: Fill[][],
|
||||||
targetInput: BigNumber,
|
targetInput: BigNumber,
|
||||||
runLimit: number = 2 ** 8,
|
runLimit: number = 2 ** 8,
|
||||||
|
samplerMetrics?: SamplerMetrics,
|
||||||
opts: PathPenaltyOpts = DEFAULT_PATH_PENALTY_OPTS,
|
opts: PathPenaltyOpts = DEFAULT_PATH_PENALTY_OPTS,
|
||||||
): Promise<Path | undefined> {
|
): Promise<Path | undefined> {
|
||||||
|
const beforeTimeMs = performance.now();
|
||||||
// Sort fill arrays by descending adjusted completed rate.
|
// Sort fill arrays by descending adjusted completed rate.
|
||||||
// Remove any paths which cannot impact the optimal path
|
// Remove any paths which cannot impact the optimal path
|
||||||
const sortedPaths = reducePaths(fillsToSortedPaths(fills, side, targetInput, opts), side);
|
const sortedPaths = reducePaths(fillsToSortedPaths(fills, side, targetInput, opts), side);
|
||||||
@@ -392,7 +412,15 @@ export async function findOptimalPathJSAsync(
|
|||||||
// Yield to event loop.
|
// Yield to event loop.
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
}
|
}
|
||||||
return optimalPath.isComplete() ? optimalPath : undefined;
|
const finalPath = optimalPath.isComplete() ? optimalPath : undefined;
|
||||||
|
// tslint:disable-next-line: no-unused-expression
|
||||||
|
samplerMetrics &&
|
||||||
|
samplerMetrics.logRouterDetails({
|
||||||
|
router: 'js',
|
||||||
|
type: 'total',
|
||||||
|
timingMs: performance.now() - beforeTimeMs,
|
||||||
|
});
|
||||||
|
return finalPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort fill arrays by descending adjusted completed rate.
|
// Sort fill arrays by descending adjusted completed rate.
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ import { BatchedOperation, ERC20BridgeSource, LiquidityProviderRegistry, TokenAd
|
|||||||
*/
|
*/
|
||||||
export function getSampleAmounts(maxFillAmount: BigNumber, numSamples: number, expBase: number = 1): BigNumber[] {
|
export function getSampleAmounts(maxFillAmount: BigNumber, numSamples: number, expBase: number = 1): BigNumber[] {
|
||||||
const distribution = [...Array<BigNumber>(numSamples)].map((_v, i) => new BigNumber(expBase).pow(i));
|
const distribution = [...Array<BigNumber>(numSamples)].map((_v, i) => new BigNumber(expBase).pow(i));
|
||||||
const stepSizes = distribution.map(d => d.div(BigNumber.sum(...distribution)));
|
const distributionSum = BigNumber.sum(...distribution);
|
||||||
|
const stepSizes = distribution.map(d => d.div(distributionSum));
|
||||||
const amounts = stepSizes.map((_s, i) => {
|
const amounts = stepSizes.map((_s, i) => {
|
||||||
if (i === numSamples - 1) {
|
if (i === numSamples - 1) {
|
||||||
return maxFillAmount;
|
return maxFillAmount;
|
||||||
|
|||||||
@@ -455,6 +455,10 @@ export interface GetMarketOrdersOpts {
|
|||||||
* Default: 1.25.
|
* Default: 1.25.
|
||||||
*/
|
*/
|
||||||
sampleDistributionBase: number;
|
sampleDistributionBase: number;
|
||||||
|
/**
|
||||||
|
* Number of samples to use when creating fill curves with neon-router
|
||||||
|
*/
|
||||||
|
neonRouterNumSamples: number;
|
||||||
/**
|
/**
|
||||||
* Fees for each liquidity source, expressed in gas.
|
* Fees for each liquidity source, expressed in gas.
|
||||||
*/
|
*/
|
||||||
@@ -514,6 +518,15 @@ export interface SamplerMetrics {
|
|||||||
* @param blockNumber block number of the sampler call
|
* @param blockNumber block number of the sampler call
|
||||||
*/
|
*/
|
||||||
logBlockNumber(blockNumber: BigNumber): void;
|
logBlockNumber(blockNumber: BigNumber): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs the routing timings
|
||||||
|
*
|
||||||
|
* @param data.router The router type (neon-router or js)
|
||||||
|
* @param data.type The type of timing being recorded (e.g total timing, all sources timing or vip timing)
|
||||||
|
* @param data.timingMs The timing in milliseconds
|
||||||
|
*/
|
||||||
|
logRouterDetails(data: { router: 'neon-router' | 'js'; type: 'all' | 'vip' | 'total'; timingMs: number }): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -599,6 +612,8 @@ export interface GenerateOptimizedOrdersOpts {
|
|||||||
allowFallback?: boolean;
|
allowFallback?: boolean;
|
||||||
shouldBatchBridgeOrders?: boolean;
|
shouldBatchBridgeOrders?: boolean;
|
||||||
gasPrice: BigNumber;
|
gasPrice: BigNumber;
|
||||||
|
neonRouterNumSamples: number;
|
||||||
|
samplerMetrics?: SamplerMetrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ComparisonPrice {
|
export interface ComparisonPrice {
|
||||||
|
|||||||
@@ -959,10 +959,10 @@
|
|||||||
typedoc "~0.16.11"
|
typedoc "~0.16.11"
|
||||||
yargs "^10.0.3"
|
yargs "^10.0.3"
|
||||||
|
|
||||||
"@0x/neon-router@^0.2.1":
|
"@0x/neon-router@^0.3.1":
|
||||||
version "0.2.1"
|
version "0.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@0x/neon-router/-/neon-router-0.2.1.tgz#23bb3cedc0eafd55a8ba6b6ea8a59ee4c538064b"
|
resolved "https://registry.yarnpkg.com/@0x/neon-router/-/neon-router-0.3.1.tgz#4ec13e750d1435357c4928d7f2521a2b4376f27e"
|
||||||
integrity sha512-feCCKuox4staZl8lxLY4nf5U256NcDHrgvSFra5cU/TUhoblLHb8F7eWAC9ygpukZUCVFLy13mExkFQHXlEOYw==
|
integrity sha512-M4ypTov9KyxsGJpYwobrld3Y2JOlR7U0XjR6BEQE2gQ1k3nie/1wNEI2J4ZjKw++RLDxdv/RCqhgA5VnINzjxA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@mapbox/node-pre-gyp" "^1.0.5"
|
"@mapbox/node-pre-gyp" "^1.0.5"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user