From 99a5600d7faf240f0d569ef7524bca54cd023dc1 Mon Sep 17 00:00:00 2001 From: rouzwelt Date: Fri, 10 Jan 2025 21:27:22 +0000 Subject: [PATCH] init --- .../liquidity-providers/AlgebraV1Base.ts | 77 ++---------- .../liquidity-providers/UniswapV3Base.ts | 114 +++++++++++++----- 2 files changed, 94 insertions(+), 97 deletions(-) diff --git a/packages/sushi/src/router/liquidity-providers/AlgebraV1Base.ts b/packages/sushi/src/router/liquidity-providers/AlgebraV1Base.ts index bd6958be91..4510ca1a64 100644 --- a/packages/sushi/src/router/liquidity-providers/AlgebraV1Base.ts +++ b/packages/sushi/src/router/liquidity-providers/AlgebraV1Base.ts @@ -12,12 +12,10 @@ import { DataFetcherOptions } from '../data-fetcher.js' import { getCurrencyCombinations } from '../get-currency-combinations.js' import { memoizer } from '../memoizer.js' import { - NUMBER_OF_SURROUNDING_TICKS, PoolFilter, StaticPoolUniV3, UniswapV3BaseProvider, V3Pool, - bitmapIndex, } from './UniswapV3Base.js' export abstract class AlgebraV1BaseProvider extends UniswapV3BaseProvider { @@ -182,16 +180,14 @@ export abstract class AlgebraV1BaseProvider extends UniswapV3BaseProvider { staticPools.forEach((pool, i) => { if (globalState === undefined || !globalState[i]) return - let thisPoolTickSpacing = this.DEFAULT_TICK_SPACING - if (poolsTickSpacing !== undefined && Array.isArray(poolsTickSpacing)) { - if (poolsTickSpacing[i] !== undefined) { - const ts = poolsTickSpacing[i] - if (typeof ts === 'number') { - thisPoolTickSpacing = ts - } else { - if (ts?.status === 'success') { - thisPoolTickSpacing = ts.result - } + let tickSpacing = this.DEFAULT_TICK_SPACING + if (poolsTickSpacing?.[i] !== undefined) { + const ts = poolsTickSpacing[i] + if (typeof ts === 'number') { + tickSpacing = ts + } else { + if (ts?.status === 'success') { + tickSpacing = ts.result } } } @@ -201,72 +197,21 @@ export abstract class AlgebraV1BaseProvider extends UniswapV3BaseProvider { return const fee = globalState[i]!.result?.[2] // fee if (!fee) return - const activeTick = - Math.floor(tick / thisPoolTickSpacing) * thisPoolTickSpacing + const activeTick = this.getActiveTick(tick, tickSpacing) if (typeof activeTick !== 'number') return - this.TICK_SPACINGS[pool.address.toLowerCase()] = thisPoolTickSpacing existingPools.push({ ...pool, fee, sqrtPriceX96, activeTick, + tickSpacing, + ticks: new Map(), }) }) return existingPools } - override getIndexes(existingPools: V3Pool[]): [number[], number[]] { - const minIndexes = existingPools.map((pool) => - bitmapIndex( - pool.activeTick - NUMBER_OF_SURROUNDING_TICKS, - this.TICK_SPACINGS[pool.address.toLowerCase()]!, - ), - ) - const maxIndexes = existingPools.map((pool) => - bitmapIndex( - pool.activeTick + NUMBER_OF_SURROUNDING_TICKS, - this.TICK_SPACINGS[pool.address.toLowerCase()]!, - ), - ) - return [minIndexes, maxIndexes] - } - - override handleTickBoundries( - i: number, - pool: V3Pool, - poolTicks: { - index: number - DLiquidity: bigint - }[], - minIndexes: number[], - maxIndexes: number[], - ) { - const lowerUnknownTick = - minIndexes[i]! * this.TICK_SPACINGS[pool.address.toLowerCase()]! * 256 - - this.TICK_SPACINGS[pool.address.toLowerCase()]! - console.assert( - poolTicks.length === 0 || lowerUnknownTick < poolTicks[0]!.index, - 'Error 236: unexpected min tick index', - ) - poolTicks.unshift({ - index: lowerUnknownTick, - DLiquidity: 0n, - }) - const upperUnknownTick = - (maxIndexes[i]! + 1) * - this.TICK_SPACINGS[pool.address.toLowerCase()]! * - 256 - console.assert( - poolTicks[poolTicks.length - 1]!.index < upperUnknownTick, - 'Error 244: unexpected max tick index', - ) - poolTicks.push({ - index: upperUnknownTick, - DLiquidity: 0n, - }) - } - override getStaticPools(t1: Token, t2: Token): StaticPoolUniV3[] { const allCombinations = getCurrencyCombinations(this.chainId, t1, t2) const currencyCombinations: [Token, Token][] = [] diff --git a/packages/sushi/src/router/liquidity-providers/UniswapV3Base.ts b/packages/sushi/src/router/liquidity-providers/UniswapV3Base.ts index 49fb2c80ba..46342c8718 100644 --- a/packages/sushi/src/router/liquidity-providers/UniswapV3Base.ts +++ b/packages/sushi/src/router/liquidity-providers/UniswapV3Base.ts @@ -4,7 +4,7 @@ import { ChainId } from '../../chain/index.js' 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 { CLTick, RToken, UniV3Pool } from '../../tines/index.js' import { DataFetcherOptions } from '../data-fetcher.js' import { getCurrencyCombinations } from '../get-currency-combinations.js' import { memoizer } from '../memoizer.js' @@ -36,8 +36,20 @@ export interface V3Pool { fee: UniV3FeeType[keyof UniV3FeeType] sqrtPriceX96: bigint activeTick: number + tickSpacing: number + ticks: Map } +export const tickSpacingAbi = [ + { + inputs: [], + name: 'tickSpacing', + outputs: [{ internalType: 'int24', name: '', type: 'int24' }], + stateMutability: 'view', + type: 'function', + }, +] as const + export const NUMBER_OF_SURROUNDING_TICKS = 1000 // 10% price impact export const bitmapIndex = (tick: number, tickSpacing: number) => { @@ -83,13 +95,9 @@ export abstract class UniswapV3BaseProvider extends LiquidityProvider { } } - getActiveTick = ( - tickCurrent: number, - feeAmount: UniV3FeeType[keyof UniV3FeeType], - ) => - typeof tickCurrent === 'number' && feeAmount - ? Math.floor(tickCurrent / this.TICK_SPACINGS[feeAmount]!) * - this.TICK_SPACINGS[feeAmount]! + getActiveTick = (tickCurrent: number, tickSpacing?: number) => + typeof tickCurrent === 'number' && typeof tickSpacing === 'number' + ? Math.floor(tickCurrent / tickSpacing) * tickSpacing : undefined async fetchPoolData( @@ -173,6 +181,39 @@ export abstract class UniswapV3BaseProvider extends LiquidityProvider { return undefined }) + const tickSpacingData = { + 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: tickSpacingAbi, + functionName: 'tickSpacing', + }) as const, + ), + } + const tickSpacings = options?.memoize + ? await (multicallMemoize(tickSpacingData) as Promise).catch((e) => { + console.warn( + `${this.getLogPrefix()} - INIT: multicall failed, message: ${ + e.message + }`, + ) + return undefined + }) + : await this.client.multicall(tickSpacingData).catch((e) => { + console.warn( + `${this.getLogPrefix()} - INIT: multicall failed, message: ${ + e.message + }`, + ) + return undefined + }) + const existingPools: V3Pool[] = [] staticPools.forEach((pool, i) => { @@ -181,12 +222,25 @@ export abstract class UniswapV3BaseProvider extends LiquidityProvider { const tick = slot0[i]!.result?.[1] if (!sqrtPriceX96 || sqrtPriceX96 === 0n || typeof tick !== 'number') return - const activeTick = this.getActiveTick(tick, pool.fee) + let tickSpacing = this.TICK_SPACINGS[pool.fee]! + if (typeof tickSpacings?.[i] !== 'undefined') { + const ts = tickSpacings[i] + if (typeof ts === 'number') { + tickSpacing = ts + } else { + if (ts?.status === 'success') { + tickSpacing = ts.result + } + } + } + const activeTick = this.getActiveTick(tick, tickSpacing) if (typeof activeTick !== 'number') return existingPools.push({ ...pool, sqrtPriceX96, activeTick, + tickSpacing, + ticks: new Map(), }) }) @@ -197,31 +251,33 @@ export abstract class UniswapV3BaseProvider extends LiquidityProvider { const minIndexes = existingPools.map((pool) => bitmapIndex( pool.activeTick - NUMBER_OF_SURROUNDING_TICKS, - this.TICK_SPACINGS[pool.fee]!, + pool.tickSpacing, ), ) const maxIndexes = existingPools.map((pool) => bitmapIndex( pool.activeTick + NUMBER_OF_SURROUNDING_TICKS, - this.TICK_SPACINGS[pool.fee]!, + pool.tickSpacing, ), ) return [minIndexes, maxIndexes] } - handleTickBoundries( - i: number, - pool: V3Pool, - poolTicks: { - index: number - DLiquidity: bigint - }[], - minIndexes: number[], - maxIndexes: number[], - ) { + handleTickBoundries(tick: number, pool: V3Pool): CLTick[] { + const currentTickIndex = bitmapIndex(tick, pool.tickSpacing) + if (!pool.ticks.has(currentTickIndex)) return [] + let minIndex + let maxIndex + for (minIndex = currentTickIndex; pool.ticks.has(minIndex); --minIndex); + for (maxIndex = currentTickIndex + 1; pool.ticks.has(maxIndex); ++maxIndex); + if (maxIndex - minIndex <= 1) return [] + + let poolTicks: CLTick[] = [] + for (let i = minIndex + 1; i < maxIndex; ++i) + poolTicks = poolTicks.concat(pool.ticks.get(i)!) + const lowerUnknownTick = - minIndexes[i]! * this.TICK_SPACINGS[pool.fee]! * 256 - - this.TICK_SPACINGS[pool.fee]! + (minIndex + 1) * pool.tickSpacing * 256 - pool.tickSpacing console.assert( poolTicks.length === 0 || lowerUnknownTick < poolTicks[0]!.index, 'Error 236: unexpected min tick index', @@ -230,8 +286,7 @@ export abstract class UniswapV3BaseProvider extends LiquidityProvider { index: lowerUnknownTick, DLiquidity: 0n, }) - const upperUnknownTick = - (maxIndexes[i]! + 1) * this.TICK_SPACINGS[pool.fee]! * 256 + const upperUnknownTick = maxIndex * pool.tickSpacing * 256 console.assert( poolTicks[poolTicks.length - 1]!.index < upperUnknownTick, 'Error 244: unexpected max tick index', @@ -240,6 +295,8 @@ export abstract class UniswapV3BaseProvider extends LiquidityProvider { index: upperUnknownTick, DLiquidity: 0n, }) + + return poolTicks } async fetchPoolsForToken( @@ -448,12 +505,7 @@ export abstract class UniswapV3BaseProvider extends LiquidityProvider { ) return - const poolTicks = ticks[i]!.map((tick) => ({ - index: tick.tick, - DLiquidity: tick.liquidityNet, - })).sort((a, b) => a.index - b.index) - - this.handleTickBoundries(i, pool, poolTicks, minIndexes, maxIndexes) + const poolTicks = this.handleTickBoundries(pool.activeTick, pool) //console.log(pool.fee, TICK_SPACINGS[pool.fee], pool.activeTick, minIndexes[i], maxIndexes[i], poolTicks) const v3Pool = new UniV3Pool(