From dfa0357736b1d8bd067f24ea0b4e06f560313397 Mon Sep 17 00:00:00 2001 From: jsy1218 <91580504+jsy1218@users.noreply.github.com> Date: Thu, 11 Apr 2024 17:57:04 -0700 Subject: [PATCH] feat: support routing-api to shadow request quoter v2 on Polygon Mumbai and Sepolia (#510) * new polygon and mainnet v3 quoter address * integ-test fork after quoter v2 deploy block * update the fork block to avoid invalid nonce * add metrics to prep for shadow on-chain quote provider * remove new quoter addresses so that they can be passed in as overrides * add polygon specific override * define default as shared static readonly variables * don't use static readonly variables * remove the metrics prefix underscore as it discontinues the existing onchain quoter metrics * add comments explaining the rationale behind which default quote params are defined as constants and which params are not * add on-chain quotes return type, so as to be used in routing-api * change to polygon mumbai and sepolia deployed quoter address --- src/providers/on-chain-quote-provider.ts | 92 +++++------ src/routers/alpha-router/alpha-router.ts | 43 ++++-- src/util/addresses.ts | 6 + src/util/onchainQuoteProviderConfigs.ts | 144 ++++++++++++++++++ .../alpha-router.integration.test.ts | 4 +- 5 files changed, 229 insertions(+), 60 deletions(-) create mode 100644 src/util/onchainQuoteProviderConfigs.ts diff --git a/src/providers/on-chain-quote-provider.ts b/src/providers/on-chain-quote-provider.ts index ef6fb552e..abb2b0868 100644 --- a/src/providers/on-chain-quote-provider.ts +++ b/src/providers/on-chain-quote-provider.ts @@ -21,6 +21,10 @@ import { } from '../util/addresses'; import { CurrencyAmount } from '../util/amounts'; import { log } from '../util/log'; +import { + DEFAULT_BLOCK_NUMBER_CONFIGS, + DEFAULT_SUCCESS_RATE_FAILURE_OVERRIDES, +} from '../util/onchainQuoteProviderConfigs'; import { routeToString } from '../util/routes'; import { Result } from './multicall-provider'; @@ -92,6 +96,14 @@ export type RouteWithQuotes = [ AmountQuote[] ]; +/** + * Final consolidated return type of all on-chain quotes. + */ +export type OnChainQuotes = { + routesWithQuotes: RouteWithQuotes[]; + blockNumber: BigNumber; +}; + type QuoteBatchSuccess = { status: 'success'; inputs: [string, string][]; @@ -141,10 +153,7 @@ export interface IOnChainQuoteProvider { amountIns: CurrencyAmount[], routes: TRoute[], providerConfig?: ProviderConfig - ): Promise<{ - routesWithQuotes: RouteWithQuotes[]; - blockNumber: BigNumber; - }>; + ): Promise>; /** * For every route, gets ane exactOut quote for every amount provided. @@ -160,10 +169,7 @@ export interface IOnChainQuoteProvider { amountOuts: CurrencyAmount[], routes: TRoute[], providerConfig?: ProviderConfig - ): Promise<{ - routesWithQuotes: RouteWithQuotes[]; - blockNumber: BigNumber; - }>; + ): Promise>; } /** @@ -264,12 +270,16 @@ export class OnChainQuoteProvider implements IOnChainQuoteProvider { * @param successRateFailureOverrides The parameters for retries when we fail to get quotes. * @param blockNumberConfig Parameters for adjusting which block we get quotes from, and how to handle block header not found errors. * @param [quoterAddressOverride] Overrides the address of the quoter contract to use. + * @param metricsPrefix metrics prefix to differentiate between different instances of the quote provider. */ constructor( protected chainId: ChainId, protected provider: BaseProvider, // Only supports Uniswap Multicall as it needs the gas limitting functionality. protected multicall2Provider: UniswapMulticallProvider, + // retryOptions, batchParams, and gasErrorFailureOverride are always override in alpha-router + // so below default values are always not going to be picked up in prod. + // So we will not extract out below default values into constants. protected retryOptions: QuoteRetryOptions = { retries: DEFAULT_BATCH_RETRIES, minTimeout: 25, @@ -284,15 +294,13 @@ export class OnChainQuoteProvider implements IOnChainQuoteProvider { gasLimitOverride: 1_500_000, multicallChunk: 100, }, - protected successRateFailureOverrides: FailureOverrides = { - gasLimitOverride: 1_300_000, - multicallChunk: 110, - }, - protected blockNumberConfig: BlockNumberConfig = { - baseBlockOffset: 0, - rollback: { enabled: false }, - }, - protected quoterAddressOverride?: string + // successRateFailureOverrides and blockNumberConfig are not always override in alpha-router. + // So we will extract out below default values into constants. + // In alpha-router default case, we will also define the constants with same values as below. + protected successRateFailureOverrides: FailureOverrides = DEFAULT_SUCCESS_RATE_FAILURE_OVERRIDES, + protected blockNumberConfig: BlockNumberConfig = DEFAULT_BLOCK_NUMBER_CONFIGS, + protected quoterAddressOverride?: string, + protected metricsPrefix?: string ) {} private getQuoterAddress(useMixedRouteQuoter: boolean): string { @@ -317,10 +325,7 @@ export class OnChainQuoteProvider implements IOnChainQuoteProvider { amountIns: CurrencyAmount[], routes: TRoute[], providerConfig?: ProviderConfig - ): Promise<{ - routesWithQuotes: RouteWithQuotes[]; - blockNumber: BigNumber; - }> { + ): Promise> { return this.getQuotesManyData( amountIns, routes, @@ -333,10 +338,7 @@ export class OnChainQuoteProvider implements IOnChainQuoteProvider { amountOuts: CurrencyAmount[], routes: TRoute[], providerConfig?: ProviderConfig - ): Promise<{ - routesWithQuotes: RouteWithQuotes[]; - blockNumber: BigNumber; - }> { + ): Promise> { return this.getQuotesManyData( amountOuts, routes, @@ -352,10 +354,7 @@ export class OnChainQuoteProvider implements IOnChainQuoteProvider { routes: TRoute[], functionName: 'quoteExactInput' | 'quoteExactOutput', _providerConfig?: ProviderConfig - ): Promise<{ - routesWithQuotes: RouteWithQuotes[]; - blockNumber: BigNumber; - }> { + ): Promise> { const useMixedRouteQuoter = routes.some((route) => route.protocol === Protocol.V2) || routes.some((route) => route.protocol === Protocol.MIXED); @@ -420,9 +419,13 @@ export class OnChainQuoteProvider implements IOnChainQuoteProvider { } and block number: ${await providerConfig.blockNumber} [Original before offset: ${originalBlockNumber}].` ); - metric.putMetric('QuoteBatchSize', inputs.length, MetricLoggerUnit.Count); metric.putMetric( - `QuoteBatchSize_${ID_TO_NETWORK_NAME(this.chainId)}`, + `${this.metricsPrefix}QuoteBatchSize`, + inputs.length, + MetricLoggerUnit.Count + ); + metric.putMetric( + `${this.metricsPrefix}QuoteBatchSize_${ID_TO_NETWORK_NAME(this.chainId)}`, inputs.length, MetricLoggerUnit.Count ); @@ -593,7 +596,7 @@ export class OnChainQuoteProvider implements IOnChainQuoteProvider { if (error instanceof BlockConflictError) { if (!haveRetriedForBlockConflictError) { metric.putMetric( - 'QuoteBlockConflictErrorRetry', + `${this.metricsPrefix}QuoteBlockConflictErrorRetry`, 1, MetricLoggerUnit.Count ); @@ -604,7 +607,7 @@ export class OnChainQuoteProvider implements IOnChainQuoteProvider { } else if (error instanceof ProviderBlockHeaderError) { if (!haveRetriedForBlockHeader) { metric.putMetric( - 'QuoteBlockHeaderNotFoundRetry', + `${this.metricsPrefix}QuoteBlockHeaderNotFoundRetry`, 1, MetricLoggerUnit.Count ); @@ -644,7 +647,7 @@ export class OnChainQuoteProvider implements IOnChainQuoteProvider { } else if (error instanceof ProviderTimeoutError) { if (!haveRetriedForTimeout) { metric.putMetric( - 'QuoteTimeoutRetry', + `${this.metricsPrefix}QuoteTimeoutRetry`, 1, MetricLoggerUnit.Count ); @@ -653,7 +656,7 @@ export class OnChainQuoteProvider implements IOnChainQuoteProvider { } else if (error instanceof ProviderGasError) { if (!haveRetriedForOutOfGas) { metric.putMetric( - 'QuoteOutOfGasExceptionRetry', + `${this.metricsPrefix}QuoteOutOfGasExceptionRetry`, 1, MetricLoggerUnit.Count ); @@ -665,7 +668,7 @@ export class OnChainQuoteProvider implements IOnChainQuoteProvider { } else if (error instanceof SuccessRateError) { if (!haveRetriedForSuccessRate) { metric.putMetric( - 'QuoteSuccessRateRetry', + `${this.metricsPrefix}QuoteSuccessRateRetry`, 1, MetricLoggerUnit.Count ); @@ -681,7 +684,7 @@ export class OnChainQuoteProvider implements IOnChainQuoteProvider { } else { if (!haveRetriedForUnknownReason) { metric.putMetric( - 'QuoteUnknownReasonRetry', + `${this.metricsPrefix}QuoteUnknownReasonRetry`, 1, MetricLoggerUnit.Count ); @@ -770,31 +773,31 @@ export class OnChainQuoteProvider implements IOnChainQuoteProvider { ); metric.putMetric( - 'QuoteApproxGasUsedPerSuccessfulCall', + `${this.metricsPrefix}QuoteApproxGasUsedPerSuccessfulCall`, approxGasUsedPerSuccessCall, MetricLoggerUnit.Count ); metric.putMetric( - 'QuoteNumRetryLoops', + `${this.metricsPrefix}QuoteNumRetryLoops`, finalAttemptNumber - 1, MetricLoggerUnit.Count ); metric.putMetric( - 'QuoteTotalCallsToProvider', + `${this.metricsPrefix}QuoteTotalCallsToProvider`, totalCallsMade, MetricLoggerUnit.Count ); metric.putMetric( - 'QuoteExpectedCallsToProvider', + `${this.metricsPrefix}QuoteExpectedCallsToProvider`, expectedCallsMade, MetricLoggerUnit.Count ); metric.putMetric( - 'QuoteNumRetriedCalls', + `${this.metricsPrefix}QuoteNumRetriedCalls`, totalCallsMade - expectedCallsMade, MetricLoggerUnit.Count ); @@ -812,7 +815,10 @@ export class OnChainQuoteProvider implements IOnChainQuoteProvider { } attempt loops. Total calls made to provider: ${totalCallsMade}. Have retried for timeout: ${haveRetriedForTimeout}` ); - return { routesWithQuotes: routesQuotes, blockNumber }; + return { + routesWithQuotes: routesQuotes, + blockNumber, + } as OnChainQuotes; } private partitionQuotes( diff --git a/src/routers/alpha-router/alpha-router.ts b/src/routers/alpha-router/alpha-router.ts index 6279c3372..3c75aff11 100644 --- a/src/routers/alpha-router/alpha-router.ts +++ b/src/routers/alpha-router/alpha-router.ts @@ -98,6 +98,17 @@ import { buildTrade, } from '../../util/methodParameters'; import { metric, MetricLoggerUnit } from '../../util/metric'; +import { + BATCH_PARAMS, + BLOCK_NUMBER_CONFIGS, + DEFAULT_BATCH_PARAMS, DEFAULT_BLOCK_NUMBER_CONFIGS, + DEFAULT_GAS_ERROR_FAILURE_OVERRIDES, + DEFAULT_RETRY_OPTIONS, + DEFAULT_SUCCESS_RATE_FAILURE_OVERRIDES, + GAS_ERROR_FAILURE_OVERRIDES, + RETRY_OPTIONS, + SUCCESS_RATE_FAILURE_OVERRIDES +} from '../../util/onchainQuoteProviderConfigs'; import { UNSUPPORTED_TOKENS } from '../../util/unsupported-tokens'; import { IRouter, @@ -626,25 +637,29 @@ export class AlphaRouter } ); break; + case ChainId.POLYGON_MUMBAI: + case ChainId.SEPOLIA: + this.onChainQuoteProvider = new OnChainQuoteProvider( + chainId, + provider, + this.multicall2Provider, + RETRY_OPTIONS[chainId], + BATCH_PARAMS[chainId], + GAS_ERROR_FAILURE_OVERRIDES[chainId], + SUCCESS_RATE_FAILURE_OVERRIDES[chainId], + BLOCK_NUMBER_CONFIGS[chainId] + ); + break; default: this.onChainQuoteProvider = new OnChainQuoteProvider( chainId, provider, this.multicall2Provider, - { - retries: 2, - minTimeout: 100, - maxTimeout: 1000, - }, - { - multicallChunk: 210, - gasLimitPerCall: 705_000, - quoteMinSuccessRate: 0.15, - }, - { - gasLimitOverride: 2_000_000, - multicallChunk: 70, - } + DEFAULT_RETRY_OPTIONS, + DEFAULT_BATCH_PARAMS, + DEFAULT_GAS_ERROR_FAILURE_OVERRIDES, + DEFAULT_SUCCESS_RATE_FAILURE_OVERRIDES, + DEFAULT_BLOCK_NUMBER_CONFIGS, ); break; } diff --git a/src/util/addresses.ts b/src/util/addresses.ts index 332b92bfe..6f8d2290d 100644 --- a/src/util/addresses.ts +++ b/src/util/addresses.ts @@ -60,6 +60,12 @@ export const QUOTER_V2_ADDRESSES: AddressMap = { // TODO: Gnosis + Moonbeam contracts to be deployed }; +export const NEW_QUOTER_V2_ADDRESSES: AddressMap = { + ...constructSameAddressMap('0x61fFE014bA17989E743c5F6cB21bF9697530B21e'), + [ChainId.POLYGON_MUMBAI]: '0x60e06b92bC94a665036C26feC5FF2A92E2d04c5f', + [ChainId.SEPOLIA]: '0x6650ab818c0a7efa72fc1404a878fef1fec8e058', +}; + export const MIXED_ROUTE_QUOTER_V1_ADDRESSES: AddressMap = { [ChainId.MAINNET]: CHAIN_TO_ADDRESSES_MAP[ChainId.MAINNET].v1MixedRouteQuoterAddress, diff --git a/src/util/onchainQuoteProviderConfigs.ts b/src/util/onchainQuoteProviderConfigs.ts new file mode 100644 index 000000000..927712a69 --- /dev/null +++ b/src/util/onchainQuoteProviderConfigs.ts @@ -0,0 +1,144 @@ +import { ChainId } from '@uniswap/sdk-core'; + +import { + BatchParams, + BlockNumberConfig, + FailureOverrides, + QuoteRetryOptions +} from '../providers'; + +export const NETWORKS_WITH_SAME_RETRY_OPTIONS = [ChainId.POLYGON]; + +export function constructSameRetryOptionsMap( + retryOptions: T, + additionalNetworks: ChainId[] = [] +): { [chainId: number]: T } { + return NETWORKS_WITH_SAME_RETRY_OPTIONS.concat(additionalNetworks).reduce<{ + [chainId: number]: T; + }>((memo, chainId) => { + memo[chainId] = retryOptions; + return memo; + }, {}); +} + +export const DEFAULT_RETRY_OPTIONS: QuoteRetryOptions = { + retries: 2, + minTimeout: 100, + maxTimeout: 1000, +}; + +export const RETRY_OPTIONS = { + ...constructSameRetryOptionsMap(DEFAULT_RETRY_OPTIONS), +}; + +export const NETWORKS_WITH_SAME_BATCH_PARAMS = [ChainId.POLYGON]; + +export function constructSameBatchParamsMap( + batchParams: T, + additionalNetworks: ChainId[] = [] +): { [chainId: number]: T } { + return NETWORKS_WITH_SAME_BATCH_PARAMS.concat(additionalNetworks).reduce<{ + [chainId: number]: T; + }>((memo, chainId) => { + memo[chainId] = batchParams; + return memo; + }, {}); +} + +export const DEFAULT_BATCH_PARAMS: BatchParams = { + multicallChunk: 210, + gasLimitPerCall: 705_000, + quoteMinSuccessRate: 0.15, +}; + +export const BATCH_PARAMS = { + ...constructSameBatchParamsMap(DEFAULT_BATCH_PARAMS), +}; + +export const NETWORKS_WITH_SAME_GAS_ERROR_FAILURE_OVERRIDES = [ChainId.POLYGON]; + +export function constructSameGasErrorFailureOverridesMap< + T extends FailureOverrides +>( + gasErrorFailureOverrides: T, + additionalNetworks: ChainId[] = [] +): { [chainId: number]: T } { + return NETWORKS_WITH_SAME_GAS_ERROR_FAILURE_OVERRIDES.concat( + additionalNetworks + ).reduce<{ + [chainId: number]: T; + }>((memo, chainId) => { + memo[chainId] = gasErrorFailureOverrides; + return memo; + }, {}); +} + +export const DEFAULT_GAS_ERROR_FAILURE_OVERRIDES: FailureOverrides = { + gasLimitOverride: 2_000_000, + multicallChunk: 70, +}; + +export const GAS_ERROR_FAILURE_OVERRIDES = { + ...constructSameGasErrorFailureOverridesMap( + DEFAULT_GAS_ERROR_FAILURE_OVERRIDES + ), +}; + +export const NETWORKS_WITH_SAME_SUCCESS_RATE_FAILURE_OVERRIDES = [ + ChainId.POLYGON, +]; + +export function constructSameSuccessRateFailureOverridesMap< + T extends FailureOverrides +>( + successRateFailureOverrides: T, + additionalNetworks: ChainId[] = [] +): { [chainId: number]: T } { + return NETWORKS_WITH_SAME_SUCCESS_RATE_FAILURE_OVERRIDES.concat( + additionalNetworks + ).reduce<{ + [chainId: number]: T; + }>((memo, chainId) => { + memo[chainId] = successRateFailureOverrides; + return memo; + }, {}); +} + +export const DEFAULT_SUCCESS_RATE_FAILURE_OVERRIDES: FailureOverrides = +{ + gasLimitOverride: 1_300_000, + multicallChunk: 110, +}; + +export const SUCCESS_RATE_FAILURE_OVERRIDES = { + ...constructSameSuccessRateFailureOverridesMap( + DEFAULT_SUCCESS_RATE_FAILURE_OVERRIDES + ), +}; + +export const NETWORKS_WITH_SAME_BLOCK_NUMBER_CONFIGS = [ChainId.POLYGON]; + +export function constructSameBlockNumberConfigsMap( + blockNumberConfigs: T, + additionalNetworks: ChainId[] = [] +): { [chainId: number]: T } { + return NETWORKS_WITH_SAME_BLOCK_NUMBER_CONFIGS.concat( + additionalNetworks + ).reduce<{ + [chainId: number]: T; + }>((memo, chainId) => { + memo[chainId] = blockNumberConfigs; + return memo; + }, {}); +} + +export const DEFAULT_BLOCK_NUMBER_CONFIGS: BlockNumberConfig = { + baseBlockOffset: 0, + rollback: { enabled: false }, +}; + +export const BLOCK_NUMBER_CONFIGS = { + ...constructSameBlockNumberConfigsMap( + DEFAULT_BLOCK_NUMBER_CONFIGS + ), +}; diff --git a/test/integ/routers/alpha-router/alpha-router.integration.test.ts b/test/integ/routers/alpha-router/alpha-router.integration.test.ts index 45cd785cd..857e103d5 100644 --- a/test/integ/routers/alpha-router/alpha-router.integration.test.ts +++ b/test/integ/routers/alpha-router/alpha-router.integration.test.ts @@ -109,9 +109,7 @@ import { } from '../../../test-util/mock-data'; import { WHALES } from '../../../test-util/whales'; -// TODO: this should be at a later block that's aware of universal router v1.3 0x3F6328669a86bef431Dc6F9201A5B90F7975a023 deployed at block 18222746. We can use later block, e.g. at block 18318644 -// TODO: permit-related tests will fail during hardfork swap execution when changing to later block. Investigate why. -const FORK_BLOCK = 19022742; +const FORK_BLOCK = 19472074; const UNIVERSAL_ROUTER_ADDRESS = UNIVERSAL_ROUTER_ADDRESS_BY_CHAIN(1); const SLIPPAGE = new Percent(15, 100); // 5% or 10_000? const LARGE_SLIPPAGE = new Percent(45, 100); // 5% or 10_000?