diff --git a/packages/sushi/src/router/data-fetcher.ts b/packages/sushi/src/router/data-fetcher.ts index e6069096f2..7f5e56294b 100644 --- a/packages/sushi/src/router/data-fetcher.ts +++ b/packages/sushi/src/router/data-fetcher.ts @@ -29,6 +29,18 @@ import { UniswapV2Provider } from './liquidity-providers/UniswapV2.js' import { UniswapV3Provider } from './liquidity-providers/UniswapV3.js' import type { PoolCode } from './pool-codes/index.js' +// options for data fetching, such as pinning block number and memoize +export type DataFetcherOptions = { + /** + * The pinned block number when getting onchain data + * this option is usefull for reproducing the route, + * price, etc of a certain block + */ + blockNumber?: bigint + /** Determines if memoizer should be used or not */ + memoize?: boolean +} + // TODO: Should be a mode on the config for DataFetcher const isTest = process.env['APP_ENV'] === 'test' || @@ -169,6 +181,7 @@ export class DataFetcher { currency0: Type, currency1: Type, excludePools?: Set, + options?: DataFetcherOptions, ): Promise { // console.log('PROVIDER COUNT', this.providers.length) // ensure that we only fetch the native wrap pools if the token is the native currency and wrapped native currency @@ -177,11 +190,16 @@ export class DataFetcher { (p) => p.getType() === LiquidityProviders.NativeWrap, ) if (provider) { - await provider.fetchPoolsForToken( - currency0.wrapped, - currency1.wrapped, - excludePools, - ) + try { + await provider.fetchPoolsForToken( + currency0.wrapped, + currency1.wrapped, + excludePools, + options, + ) + } catch { + /**/ + } } } else { const [token0, token1] = @@ -189,9 +207,9 @@ export class DataFetcher { currency0.wrapped.sortsBefore(currency1.wrapped) ? [currency0.wrapped, currency1.wrapped] : [currency1.wrapped, currency0.wrapped] - await Promise.all( + await Promise.allSettled( this.providers.map((p) => - p.fetchPoolsForToken(token0, token1, excludePools), + p.fetchPoolsForToken(token0, token1, excludePools, options), ), ) } diff --git a/packages/sushi/src/router/liquidity-providers/CurveProvider.ts b/packages/sushi/src/router/liquidity-providers/CurveProvider.ts index 837f850d44..4c80ac4bec 100644 --- a/packages/sushi/src/router/liquidity-providers/CurveProvider.ts +++ b/packages/sushi/src/router/liquidity-providers/CurveProvider.ts @@ -18,7 +18,9 @@ import { } from '../../currency/index.js' import { Native, Token, Type } from '../../currency/index.js' import { RToken, createCurvePoolsForMultipool } from '../../tines/index.js' +import { DataFetcherOptions } from '../data-fetcher.js' import { getCurrencyCombinations } from '../get-currency-combinations.js' +import { memoizer } from '../memoizer.js' import { CurvePoolCode } from '../pool-codes/CurvePool.js' import { PoolCode } from '../pool-codes/PoolCode.js' import { LiquidityProvider, LiquidityProviders } from './LiquidityProvider.js' @@ -379,9 +381,13 @@ export class CurveProvider extends LiquidityProvider { t0: Token, t1: Token, excludePools?: Set, + options?: DataFetcherOptions, ): Promise> { const pools: Map = new Map() let currencyCombinations = getCurrencyCombinations(this.chainId, t0, t1) + + const multicallMemoize = await memoizer.fn(this.client.multicall) + for (let i = 0; currencyCombinations.length > 0; ++i) { const calls = (CURVE_FACTORY_ADDRESSES[this.chainId] ?? []).flatMap( (factory) => @@ -397,12 +403,28 @@ export class CurveProvider extends LiquidityProvider { ] as const, })), ) - const newFoundPools = await this.client.multicall({ + const newfoundPoolsData = { multicallAddress: this.client.chain?.contracts?.multicall3 ?.address as '0x${string}', allowFailure: true, + blockNumber: options?.blockNumber, contracts: calls, - }) + } + const newFoundPools: ( + | { + error?: undefined + result: `0x${string}` + status: 'success' + } + | { + error: Error + result?: undefined + status: 'failure' + } + )[] = options?.memoize + ? ((await multicallMemoize(newfoundPoolsData)) as any) + : await this.client.multicall(newfoundPoolsData) + newFoundPools.forEach((pool, i) => { if ( pool.status === 'success' && @@ -429,12 +451,16 @@ export class CurveProvider extends LiquidityProvider { async getPoolRatio( pools: [string, [CurvePoolType, Type[]]][], + options?: DataFetcherOptions, ): Promise<(number[] | undefined)[]> { if (this.chainId === ChainId.ETHEREUM) { - const ratios = await this.client.multicall({ + const multicallMemoize = await memoizer.fn(this.client.multicall) + + const ratiosData = { multicallAddress: this.client.chain?.contracts?.multicall3 ?.address as '0x${string}', allowFailure: true, + blockNumber: options?.blockNumber, contracts: [ { address: '0xE95A203B1a91a908F9B9CE46459d101078c2c3cb', // ankr @@ -467,7 +493,11 @@ export class CurveProvider extends LiquidityProvider { functionName: 'exchangeRateCurrent', }, ], - }) + } as any + const ratios = options?.memoize + ? ((await multicallMemoize(ratiosData)) as any) + : await this.client.multicall(ratiosData) + return pools.map(([poolAddress]) => { // collection of freaks switch (poolAddress.toLowerCase()) { @@ -498,9 +528,10 @@ export class CurveProvider extends LiquidityProvider { async getCurvePoolCodes( pools: Map, + options?: DataFetcherOptions, ): Promise { const poolArray = Array.from(pools.entries()) - const poolsMulticall = < + const poolsMulticall = async < T extends ContractFunctionParameters< (typeof curvePoolABI)[keyof typeof curvePoolABI] >['functionName'], @@ -512,10 +543,12 @@ export class CurveProvider extends LiquidityProvider { T >['args'], ) => { - return this.client.multicall({ + const multicallMemoize = await memoizer.fn(this.client.multicall) + const data = { multicallAddress: this.client.chain?.contracts?.multicall3 ?.address as '0x${string}', allowFailure: true, + blockNumber: options?.blockNumber, contracts: poolArray.map(([address, [poolType]]) => ({ address: address as Address, // //chainId: this.chainId, @@ -523,7 +556,10 @@ export class CurveProvider extends LiquidityProvider { functionName: functionName, args, })) as any, - }) + } as any + return options?.memoize + ? (multicallMemoize(data) as any) + : this.client.multicall(data) } // const poolContract = getContract({ // address: poolAddress as '0x${string}', @@ -581,9 +617,10 @@ export class CurveProvider extends LiquidityProvider { t0: Token, t1: Token, excludePools?: Set, + options?: DataFetcherOptions, ): Promise { - const pools = await this.getPoolsForTokens(t0, t1, excludePools) - this.foundPools = await this.getCurvePoolCodes(pools) + const pools = await this.getPoolsForTokens(t0, t1, excludePools, options) + this.foundPools = await this.getCurvePoolCodes(pools, options) //console.log(JSON.stringify(this.foundPools, undefined, ' ')) } diff --git a/packages/sushi/src/router/liquidity-providers/LiquidityProvider.ts b/packages/sushi/src/router/liquidity-providers/LiquidityProvider.ts index 59e73d54d4..289465fb94 100644 --- a/packages/sushi/src/router/liquidity-providers/LiquidityProvider.ts +++ b/packages/sushi/src/router/liquidity-providers/LiquidityProvider.ts @@ -1,6 +1,7 @@ import { PublicClient } from 'viem' import { ChainId, chainShortName } from '../../chain/index.js' import type { Token } from '../../currency/index.js' +import { DataFetcherOptions } from '../data-fetcher.js' import type { PoolCode } from '../pool-codes/index.js' export enum LiquidityProviders { @@ -79,6 +80,7 @@ export abstract class LiquidityProvider { t0: Token, t1: Token, excludePools?: Set, + options?: DataFetcherOptions, ): Promise /** diff --git a/packages/sushi/src/router/liquidity-providers/Trident.ts b/packages/sushi/src/router/liquidity-providers/Trident.ts index bfaf64e0c2..c8620ff678 100644 --- a/packages/sushi/src/router/liquidity-providers/Trident.ts +++ b/packages/sushi/src/router/liquidity-providers/Trident.ts @@ -23,12 +23,14 @@ import { convertTokenToBento, toShareBI, } from '../../tines/index.js' +import { DataFetcherOptions } from '../data-fetcher.js' import { PoolResponse2, filterOnDemandPools, filterTopPools, mapToken, } from '../lib/api.js' +import { memoizer } from '../memoizer.js' import { BentoBridgePoolCode, BentoPoolCode, @@ -577,6 +579,7 @@ export class TridentProvider extends LiquidityProvider { t0: Token, t1: Token, excludePools?: Set, + options?: DataFetcherOptions, ): Promise { const topPoolAddresses = [ ...Array.from(this.topClassicPools.keys()), @@ -609,6 +612,7 @@ export class TridentProvider extends LiquidityProvider { this.chainId, t0, t1, + options, ) if (excludePools) onDemandClassicPools = (onDemandClassicPools as PoolResponse2[]).filter( @@ -723,105 +727,145 @@ export class TridentProvider extends LiquidityProvider { } }) - const classicReservePromise = this.client - .multicall({ - multicallAddress: this.client.chain?.contracts?.multicall3 - ?.address as Address, - allowFailure: true, - contracts: classicPoolCodesToCreate.map( - (pc) => - ({ - address: pc.pool.address as Address, - chainId: this.chainId, - abi: getReservesAbi, - functionName: 'getReserves', - }) as const, - ), - }) - .catch((e) => { - console.warn( - `${this.getLogPrefix()} - UPDATE: multicall failed, message: ${ - e.message - }`, + const multicallMemoize = await memoizer.fn(this.client.multicall) + + const classicReservesPromiseData = { + multicallAddress: this.client.chain?.contracts?.multicall3 + ?.address as Address, + allowFailure: true, + blockNumber: options?.blockNumber, + contracts: classicPoolCodesToCreate.map( + (pc) => + ({ + address: pc.pool.address as Address, + chainId: this.chainId, + abi: getReservesAbi, + functionName: 'getReserves', + }) as const, + ), + } + const classicReservePromise = options?.memoize + ? (multicallMemoize(classicReservesPromiseData) as Promise).catch( + (e) => { + console.warn( + `${this.getLogPrefix()} - UPDATE: multicall failed, message: ${ + e.message + }`, + ) + return undefined + }, ) - return undefined - }) + : this.client.multicall(classicReservesPromiseData).catch((e) => { + console.warn( + `${this.getLogPrefix()} - UPDATE: multicall failed, message: ${ + e.message + }`, + ) + return undefined + }) - const stableReservePromise = this.client - .multicall({ - multicallAddress: this.client.chain?.contracts?.multicall3 - ?.address as Address, - allowFailure: true, - contracts: stablePoolCodesToCreate.map( - (pc) => - ({ - address: pc.pool.address as Address, - chainId: this.chainId, - abi: getStableReservesAbi, - functionName: 'getReserves', - }) as const, - ), - }) - .catch((e) => { - console.warn( - `${this.getLogPrefix()} - UPDATE: multicall failed, message: ${ - e.message - }`, + const stableReservePromiseData = { + multicallAddress: this.client.chain?.contracts?.multicall3 + ?.address as Address, + allowFailure: true, + blockNumber: options?.blockNumber, + contracts: stablePoolCodesToCreate.map( + (pc) => + ({ + address: pc.pool.address as Address, + chainId: this.chainId, + abi: getStableReservesAbi, + functionName: 'getReserves', + }) as const, + ), + } + const stableReservePromise = options?.memoize + ? (multicallMemoize(stableReservePromiseData) as Promise).catch( + (e) => { + console.warn( + `${this.getLogPrefix()} - UPDATE: multicall failed, message: ${ + e.message + }`, + ) + return undefined + }, ) - return undefined - }) + : this.client.multicall(stableReservePromiseData).catch((e) => { + console.warn( + `${this.getLogPrefix()} - UPDATE: multicall failed, message: ${ + e.message + }`, + ) + return undefined + }) - const totalsPromise = this.client - .multicall({ - multicallAddress: this.client.chain?.contracts?.multicall3 - ?.address as Address, - allowFailure: true, - contracts: bridgesToCreate.map( - (b) => - ({ - args: [b.pool.token0.address as Address], - address: this.bentoBox[ - this.chainId as BentoBoxChainId - ] as Address, - chainId: this.chainId, - abi: totalsAbi, - functionName: 'totals', - }) as const, - ), - }) - .catch((e) => { - console.warn( - `${this.getLogPrefix()} - UPDATE: multicall failed, message: ${ - e.message - }`, - ) - return undefined - }) + const totalsPromiseData = { + multicallAddress: this.client.chain?.contracts?.multicall3 + ?.address as Address, + allowFailure: true, + blockNumber: options?.blockNumber, + contracts: bridgesToCreate.map( + (b) => + ({ + args: [b.pool.token0.address as Address], + address: this.bentoBox[this.chainId as BentoBoxChainId] as Address, + chainId: this.chainId, + abi: totalsAbi, + functionName: 'totals', + }) as const, + ), + } + const totalsPromise = options?.memoize + ? (multicallMemoize(totalsPromiseData) as Promise).catch((e) => { + console.warn( + `${this.getLogPrefix()} - UPDATE: multicall failed, message: ${ + e.message + }`, + ) + return undefined + }) + : this.client.multicall(totalsPromiseData).catch((e) => { + console.warn( + `${this.getLogPrefix()} - UPDATE: multicall failed, message: ${ + e.message + }`, + ) + return undefined + }) - const balancesPromise = this.client - .multicall({ - multicallAddress: this.client.chain?.contracts?.multicall3 - ?.address as Address, - allowFailure: true, - contracts: bridgesToCreate.map( - (b) => - ({ - args: [this.bentoBox[this.chainId as BentoBoxChainId] as Address], - address: b.pool.token0.address as Address, - chainId: this.chainId, - abi: balanceOfAbi, - functionName: 'balanceOf', - }) as const, - ), - }) - .catch((e) => { - console.warn( - `${this.getLogPrefix()} - UPDATE: multicall failed, message: ${ - e.message - }`, - ) - return undefined - }) + const balancesPromiseData = { + multicallAddress: this.client.chain?.contracts?.multicall3 + ?.address as Address, + allowFailure: true, + blockNumber: options?.blockNumber, + contracts: bridgesToCreate.map( + (b) => + ({ + args: [this.bentoBox[this.chainId as BentoBoxChainId] as Address], + address: b.pool.token0.address as Address, + chainId: this.chainId, + abi: balanceOfAbi, + functionName: 'balanceOf', + }) as const, + ), + } + const balancesPromise = options?.memoize + ? (multicallMemoize(balancesPromiseData) as Promise).catch((e) => { + console.warn( + `${this.getLogPrefix()} - UPDATE: multicall failed, message: ${ + e.message + }`, + ) + return undefined + }) + : this.client.multicall(balancesPromiseData).catch((e) => { + console.warn( + `${this.getLogPrefix()} - UPDATE: multicall failed, message: ${ + e.message + }`, + ) + return undefined + }) const [classicReserves, stableReserves, totals, balances] = await Promise.all([ @@ -1139,8 +1183,9 @@ export class TridentProvider extends LiquidityProvider { t0: Token, t1: Token, excludePools?: Set, + options?: DataFetcherOptions, ): Promise { - await this.getOnDemandPools(t0, t1, excludePools) + await this.getOnDemandPools(t0, t1, excludePools, options) } getCurrentPoolList(t0: Token, t1: Token): PoolCode[] { diff --git a/packages/sushi/src/router/liquidity-providers/UniswapV2Base.ts b/packages/sushi/src/router/liquidity-providers/UniswapV2Base.ts index d9d31ec9b7..e46987fb8d 100644 --- a/packages/sushi/src/router/liquidity-providers/UniswapV2Base.ts +++ b/packages/sushi/src/router/liquidity-providers/UniswapV2Base.ts @@ -9,6 +9,7 @@ import { } from '../../config/index.js' import { Token } from '../../currency/index.js' import { ConstantProductRPool, RToken } from '../../tines/index.js' +import { DataFetcherOptions } from '../data-fetcher.js' import { getCurrencyCombinations } from '../get-currency-combinations.js' import { PoolResponse2, @@ -16,6 +17,7 @@ import { filterTopPools, mapToken, } from '../lib/api.js' +import { memoizer } from '../memoizer.js' import { ConstantProductPoolCode, type PoolCode } from '../pool-codes/index.js' import { LiquidityProvider } from './LiquidityProvider.js' @@ -160,6 +162,7 @@ export abstract class UniswapV2BaseProvider extends LiquidityProvider { t0: Token, t1: Token, excludePools?: Set, + options?: DataFetcherOptions, ): Promise { const topPoolAddresses = Array.from(this.topPools.keys()) let pools = @@ -219,29 +222,40 @@ export abstract class UniswapV2BaseProvider extends LiquidityProvider { } }) - const reserves = await this.client - .multicall({ - multicallAddress: this.client.chain?.contracts?.multicall3 - ?.address as Address, - allowFailure: true, - contracts: poolCodesToCreate.map( - (poolCode) => - ({ - address: poolCode.pool.address as Address, - chainId: this.chainId, - abi: getReservesAbi, - functionName: 'getReserves', - }) as const, - ), - }) - .catch((e) => { - console.warn( - `${this.getLogPrefix()} - UPDATE: on-demand pools multicall failed, message: ${ - e.message - }`, - ) - return undefined - }) + const multicallMemoize = await memoizer.fn(this.client.multicall) + + const multicallData = { + multicallAddress: this.client.chain?.contracts?.multicall3 + ?.address as Address, + allowFailure: true, + blockNumber: options?.blockNumber, + contracts: poolCodesToCreate.map( + (poolCode) => + ({ + address: poolCode.pool.address as Address, + chainId: this.chainId, + abi: getReservesAbi, + functionName: 'getReserves', + }) as const, + ), + } + const reserves = options?.memoize + ? await (multicallMemoize(multicallData) as Promise).catch((e) => { + console.warn( + `${this.getLogPrefix()} - UPDATE: on-demand pools multicall failed, message: ${ + e.message + }`, + ) + return undefined + }) + : await this.client.multicall(multicallData).catch((e) => { + console.warn( + `${this.getLogPrefix()} - UPDATE: on-demand pools multicall failed, message: ${ + e.message + }`, + ) + return undefined + }) poolCodesToCreate.forEach((poolCode, i) => { const pool = poolCode.pool @@ -536,8 +550,9 @@ export abstract class UniswapV2BaseProvider extends LiquidityProvider { t0: Token, t1: Token, excludePools?: Set, + options?: DataFetcherOptions, ): Promise { - await this.getOnDemandPools(t0, t1, excludePools) + await this.getOnDemandPools(t0, t1, excludePools, options) } /** diff --git a/packages/sushi/src/router/liquidity-providers/UniswapV3Base.ts b/packages/sushi/src/router/liquidity-providers/UniswapV3Base.ts index c3ab1201b7..334f8328ab 100644 --- a/packages/sushi/src/router/liquidity-providers/UniswapV3Base.ts +++ b/packages/sushi/src/router/liquidity-providers/UniswapV3Base.ts @@ -5,7 +5,9 @@ import { SushiSwapV3FeeAmount, TICK_SPACINGS } from '../../config/index.js' import { Currency, Token, Type } from '../../currency/index.js' import { computeSushiSwapV3PoolAddress } from '../../pool/index.js' import { RToken, UniV3Pool } from '../../tines/index.js' +import { DataFetcherOptions } from '../data-fetcher.js' import { getCurrencyCombinations } from '../get-currency-combinations.js' +import { memoizer } from '../memoizer.js' import { type PoolCode, UniV3PoolCode } from '../pool-codes/index.js' import { LiquidityProvider } from './LiquidityProvider.js' @@ -78,70 +80,82 @@ export abstract class UniswapV3BaseProvider extends LiquidityProvider { t0: Token, t1: Token, excludePools?: Set | PoolFilter, + options?: DataFetcherOptions, ): Promise { let staticPools = this.getStaticPools(t0, t1) if (excludePools) staticPools = staticPools.filter((p) => !excludePools.has(p.address)) - const slot0 = await this.client - .multicall({ - multicallAddress: this.client.chain?.contracts?.multicall3 - ?.address as Address, - allowFailure: true, - contracts: staticPools.map( - (pool) => - ({ - address: pool.address as Address, - chainId: this.chainId, - abi: [ - { - inputs: [], - name: 'slot0', - outputs: [ - { - internalType: 'uint160', - name: 'sqrtPriceX96', - type: 'uint160', - }, - { internalType: 'int24', name: 'tick', type: 'int24' }, - { - internalType: 'uint16', - name: 'observationIndex', - type: 'uint16', - }, - { - internalType: 'uint16', - name: 'observationCardinality', - type: 'uint16', - }, - { - internalType: 'uint16', - name: 'observationCardinalityNext', - type: 'uint16', - }, - { - internalType: 'uint8', - name: 'feeProtocol', - type: 'uint8', - }, - { internalType: 'bool', name: 'unlocked', type: 'bool' }, - ], - stateMutability: 'view', - type: 'function', - }, - ], - functionName: 'slot0', - }) as const, - ), - }) - .catch((e) => { - console.warn( - `${this.getLogPrefix()} - INIT: multicall failed, message: ${ - e.message - }`, - ) - return undefined - }) + const multicallMemoize = await memoizer.fn(this.client.multicall) + + const slot0Data = { + multicallAddress: this.client.chain?.contracts?.multicall3 + ?.address as Address, + allowFailure: true, + blockNumber: options?.blockNumber, + contracts: staticPools.map( + (pool) => + ({ + address: pool.address as Address, + chainId: this.chainId, + abi: [ + { + inputs: [], + name: 'slot0', + outputs: [ + { + internalType: 'uint160', + name: 'sqrtPriceX96', + type: 'uint160', + }, + { internalType: 'int24', name: 'tick', type: 'int24' }, + { + internalType: 'uint16', + name: 'observationIndex', + type: 'uint16', + }, + { + internalType: 'uint16', + name: 'observationCardinality', + type: 'uint16', + }, + { + internalType: 'uint16', + name: 'observationCardinalityNext', + type: 'uint16', + }, + { + internalType: 'uint8', + name: 'feeProtocol', + type: 'uint8', + }, + { internalType: 'bool', name: 'unlocked', type: 'bool' }, + ], + stateMutability: 'view', + type: 'function', + }, + ], + functionName: 'slot0', + }) as const, + ), + } + const slot0 = options?.memoize + ? await (multicallMemoize(slot0Data) as Promise).catch((e) => { + console.warn( + `${this.getLogPrefix()} - INIT: multicall failed, message: ${ + e.message + }`, + ) + return undefined + }) + : await this.client.multicall(slot0Data).catch((e) => { + console.warn( + `${this.getLogPrefix()} - INIT: multicall failed, message: ${ + e.message + }`, + ) + return undefined + }) const existingPools: V3Pool[] = [] @@ -162,10 +176,11 @@ export abstract class UniswapV3BaseProvider extends LiquidityProvider { if (existingPools.length === 0) return - const liquidityContracts = this.client.multicall({ + const liquidityContractsData = { multicallAddress: this.client.chain?.contracts?.multicall3 ?.address as Address, allowFailure: true, + blockNumber: options?.blockNumber, contracts: existingPools.map( (pool) => ({ @@ -185,12 +200,29 @@ export abstract class UniswapV3BaseProvider extends LiquidityProvider { functionName: 'liquidity', }) as const, ), - }) - - const token0Contracts = this.client.multicall({ + } + const liquidityContracts: Promise< + ( + | { + error?: undefined + result: bigint + status: 'success' + } + | { + error: Error + result?: undefined + status: 'failure' + } + )[] + > = options?.memoize + ? (multicallMemoize(liquidityContractsData) as Promise) + : this.client.multicall(liquidityContractsData) + + const token0ContractsData = { multicallAddress: this.client.chain?.contracts?.multicall3 ?.address as Address, allowFailure: true, + blockNumber: options?.blockNumber, contracts: existingPools.map( (pool) => ({ @@ -201,12 +233,29 @@ export abstract class UniswapV3BaseProvider extends LiquidityProvider { functionName: 'balanceOf', }) as const, ), - }) - - const token1Contracts = this.client.multicall({ + } + const token0Contracts: Promise< + ( + | { + error: Error + result?: undefined + status: 'failure' + } + | { + error?: undefined + result: bigint + status: 'success' + } + )[] + > = options?.memoize + ? (multicallMemoize(token0ContractsData) as Promise) + : this.client.multicall(token0ContractsData) + + const token1ContractsData = { multicallAddress: this.client.chain?.contracts?.multicall3 ?.address as Address, allowFailure: true, + blockNumber: options?.blockNumber, contracts: existingPools.map( (pool) => ({ @@ -217,7 +266,23 @@ export abstract class UniswapV3BaseProvider extends LiquidityProvider { functionName: 'balanceOf', }) as const, ), - }) + } + const token1Contracts: Promise< + ( + | { + error?: undefined + result: bigint + status: 'success' + } + | { + error: Error + result?: undefined + status: 'failure' + } + )[] + > = options?.memoize + ? (multicallMemoize(token1ContractsData) as Promise) + : this.client.multicall(token1ContractsData) const minIndexes = existingPools.map((pool) => bitmapIndex( @@ -242,6 +307,7 @@ export abstract class UniswapV3BaseProvider extends LiquidityProvider { ).flatMap((j) => ({ chainId: this.chainId, address: this.tickLens[ + // @ts-ignore this.chainId as keyof typeof this.tickLens ] as Address, args: [pool.address, j] as const, @@ -251,12 +317,33 @@ export abstract class UniswapV3BaseProvider extends LiquidityProvider { })) }) - const ticksContracts = this.client.multicall({ + const ticksContractsData = { multicallAddress: this.client.chain?.contracts?.multicall3 ?.address as Address, allowFailure: true, contracts: wordList, - }) + blockNumber: options?.blockNumber, + } + const ticksContracts: Promise< + ( + | { + error?: undefined + result: readonly { + tick: number + liquidityNet: bigint + liquidityGross: bigint + }[] + status: 'success' + } + | { + error: Error + result?: undefined + status: 'failure' + } + )[] + > = options?.memoize + ? (multicallMemoize(ticksContractsData) as Promise) + : this.client.multicall(ticksContractsData) const [liquidityResults, token0Balances, token1Balances, tickResults] = await Promise.all([ diff --git a/packages/sushi/src/router/static-pool-fetcher/Trident.ts b/packages/sushi/src/router/static-pool-fetcher/Trident.ts index ecd3038b7e..80dd95d61a 100644 --- a/packages/sushi/src/router/static-pool-fetcher/Trident.ts +++ b/packages/sushi/src/router/static-pool-fetcher/Trident.ts @@ -11,7 +11,9 @@ import { TridentChainId, } from '../../config/index.js' import { Currency, Token } from '../../currency/index.js' +import { DataFetcherOptions } from '../data-fetcher.js' import { getCurrencyCombinations } from '../get-currency-combinations.js' +import { memoizer } from '../memoizer.js' export interface TridentStaticPool { address: Address @@ -27,10 +29,11 @@ export class TridentStaticPoolFetcher { chainId: ChainId, t1: Token, t2: Token, + options?: DataFetcherOptions, ): Promise<[TridentStaticPool[], TridentStaticPool[]]> { const pools = await Promise.all([ - this.getPools(client, chainId, t1, t2, 'CONSTANT_PRODUCT_POOL'), - this.getPools(client, chainId, t1, t2, 'STABLE_POOL'), + this.getPools(client, chainId, t1, t2, 'CONSTANT_PRODUCT_POOL', options), + this.getPools(client, chainId, t1, t2, 'STABLE_POOL', options), ]) return pools @@ -42,6 +45,7 @@ export class TridentStaticPoolFetcher { t1: Token, t2: Token, type: 'STABLE_POOL' | 'CONSTANT_PRODUCT_POOL', + options?: DataFetcherOptions, ) { const currencies = getCurrencyCombinations(chainId, t1, t2) @@ -59,9 +63,12 @@ export class TridentStaticPoolFetcher { chainId as TridentChainId ] as Address) - const callStatePoolsCount = await client.multicall({ + const multicallMemoize = await memoizer.fn(client.multicall) + + const callStatePoolsCountData = { multicallAddress: client.chain?.contracts?.multicall3?.address as Address, allowFailure: true, + blockNumber: options?.blockNumber, contracts: _pairsUniqueAddr.map( (el) => ({ @@ -72,7 +79,21 @@ export class TridentStaticPoolFetcher { args: el as [Address, Address], }) as const, ), - }) + } + const callStatePoolsCount: ( + | { + error?: undefined + result: bigint + status: 'success' + } + | { + error: Error + result?: undefined + status: 'failure' + } + )[] = options?.memoize + ? await (multicallMemoize(callStatePoolsCountData) as Promise) + : await client.multicall(callStatePoolsCountData) const callStatePoolsCountProcessed = callStatePoolsCount ?.map( @@ -104,9 +125,10 @@ export class TridentStaticPoolFetcher { .filter(([, length]) => length) .map(([i]) => [_pairsUnique[i]![0], _pairsUnique[i]![1]]) - const callStatePools = await client.multicall({ + const callStatePoolsData = { multicallAddress: client.chain?.contracts?.multicall3?.address as Address, allowFailure: true, + blockNumber: options?.blockNumber, contracts: callStatePoolsCountProcessed.map( (args) => ({ @@ -117,7 +139,21 @@ export class TridentStaticPoolFetcher { args, }) as const, ), - }) + } + const callStatePools: ( + | { + error?: undefined + result: readonly `0x${string}`[] + status: 'success' + } + | { + error: Error + result?: undefined + status: 'failure' + } + )[] = options?.memoize + ? await (multicallMemoize(callStatePoolsData) as Promise) + : await client.multicall(callStatePoolsData) const pools: TridentStaticPool[] = [] callStatePools.forEach((s, i) => { @@ -134,9 +170,10 @@ export class TridentStaticPoolFetcher { const poolsAddresses = pools.map((p) => p.address) - const fees = await client.multicall({ + const feesData = { multicallAddress: client.chain?.contracts?.multicall3?.address as Address, allowFailure: true, + blockNumber: options?.blockNumber, contracts: poolsAddresses.map( (address) => ({ @@ -146,7 +183,22 @@ export class TridentStaticPoolFetcher { functionName: 'swapFee', }) as const, ), - }) + } + const fees: ( + | { + error?: undefined + result: bigint + status: 'success' + } + | { + error: Error + result?: undefined + status: 'failure' + } + )[] = options?.memoize + ? await (multicallMemoize(feesData) as Promise) + : await client.multicall(feesData) + const results: TridentStaticPool[] = [] pools.forEach((p, i) => {