import { Contract } from 'ethers'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore import { QuoterV2 } from '@uniswap/v3-periphery'; import { PoolState, QuoteEngine, QuoteExactInputSingleParams, QuoteExactInputSingleResult, } from './quote-engine'; export type AerodromeQuoteEngineExtras = { tickSpacing?: number | bigint; }; export class AerodromeQuoteEngine implements QuoteEngine { public quoter: QuoterV2; private pool?: Contract; constructor(quoter: QuoterV2, pool?: Contract) { this.quoter = quoter; this.pool = pool; } /** * Allows updating the pool reference used for fetching state information. */ setPool(pool: Contract): void { this.pool = pool; } async quoteExactInputSingle( params: QuoteExactInputSingleParams, ): Promise { const tickSpacing = await this.resolveTickSpacing(params); const quoteFn = this.quoter.getFunction('quoteExactInputSingle'); const q = await quoteFn.staticCall({ tokenIn: params.tokenIn, tokenOut: params.tokenOut, amountIn: params.amountIn, tickSpacing: Number(tickSpacing), // Convert BigInt to number for the call sqrtPriceLimitX96: params.sqrtPriceLimitX96 ?? 0, }); return { amountOut: BigInt(q.amountOut.toString()), sqrtPriceX96After: BigInt(q.sqrtPriceX96After.toString()), }; } async getCurrentPoolState(): Promise { if (!this.pool) { throw new Error('AerodromeQuoteEngine: pool contract not set'); } const slot0 = await (this.pool.slot0?.() ?? this.pool.globalState?.()); if (!slot0) { throw new Error('AerodromeQuoteEngine: unable to fetch pool state'); } let sqrtPriceX96Result: bigint; if (slot0.sqrtPriceX96) { sqrtPriceX96Result = BigInt(slot0.sqrtPriceX96.toString()); } else if (slot0.price) { sqrtPriceX96Result = BigInt(slot0.price.toString()); } else { throw new Error('AerodromeQuoteEngine: missing sqrt price in pool state'); } const tick = slot0.tick === undefined || slot0.tick === null ? undefined : Number(slot0.tick.toString()); return { sqrtPriceX96: sqrtPriceX96Result, tick: tick, }; } private async resolveTickSpacing( params: QuoteExactInputSingleParams, ): Promise { const extrasTickSpacing = (params.extras?.tickSpacing ?? params.extras?.['tick_spacing']) as number | bigint | undefined; if (extrasTickSpacing !== undefined) { return BigInt(extrasTickSpacing); } if (this.pool?.tickSpacing) { const ts = await this.pool.tickSpacing(); return BigInt(ts.toString()); } throw new Error( 'AerodromeQuoteEngine: tickSpacing is required via extras or pool reference', ); } }