jest.mock('ethers', () => { return { Contract: jest.fn().mockImplementation(() => ({ getSlot0: jest.fn().mockResolvedValue({ sqrtPriceX96: 123n, tick: 12 }), })), Provider: class {}, }; }); jest.mock( '@uniswap/v4-sdk', () => ({ Pool: { getPoolId: jest.fn().mockReturnValue('pool-id'), }, }), { virtual: true }, ); jest.mock('../../../../artifacts/abis/StateView.json', () => ({ abi: [] }), { virtual: true, }); jest.mock('@uniswap/sdk-core', () => ({ Token: jest.fn().mockImplementation(() => ({})), })); import { UniswapV3QuoteEngine } from '../../src/engines/uniswap-v3-quote-engine'; import { AlgebraQuoteEngine } from '../../src/engines/algebra-quote-engine'; import { Algebra19IntegralQuoteEngine } from '../../src/engines/algebra19-integral-quote-engine'; import { AerodromeQuoteEngine } from '../../src/engines/aerodrome-quote-engine'; import { ThickV2QuoteEngine } from '../../src/engines/thickv2-quote-engine'; import { UniswapV4QuoteEngine } from '../../src/engines/uniswap-v4-quote-engine'; const makeQuoter = (response: Record) => ({ getFunction: jest.fn().mockReturnValue({ staticCall: jest.fn().mockResolvedValue(response), }), }); describe('Quote engines runtime behavior', () => { it('UniswapV3QuoteEngine requires fee and returns bigint results', async () => { const quoter = makeQuoter({ amountOut: 10n, sqrtPriceX96After: 20n, amountIn: 30n, }); const engine = new UniswapV3QuoteEngine(quoter as any); await expect( engine.quoteExactInputSingle({ tokenIn: '0x1', tokenOut: '0x2', amountIn: 1n, }), ).rejects.toThrow('fee is required'); const input = await engine.quoteExactInputSingle({ tokenIn: '0x1', tokenOut: '0x2', amountIn: 1n, fee: 3000, }); expect(input.amountOut).toBe(10n); expect(input.sqrtPriceX96After).toBe(20n); await expect( engine.quoteExactOutputSingle({ tokenIn: '0x1', tokenOut: '0x2', amountOut: 2n, }), ).rejects.toThrow('fee is required'); const output = await engine.quoteExactOutputSingle({ tokenIn: '0x1', tokenOut: '0x2', amountOut: 2n, fee: 3000, }); expect(output.amountIn).toBe(30n); expect(output.sqrtPriceX96After).toBe(20n); }); it('AlgebraQuoteEngine resolves limit sqrt price and pool state', async () => { const quoter = makeQuoter({ amountOut: 11n, sqrtPriceX96After: 22n }); const pool = { globalState: jest.fn().mockResolvedValue({ sqrtPriceX96: 100n, tick: 5, }), }; const engine = new AlgebraQuoteEngine(quoter as any, pool as any); const quote = await engine.quoteExactInputSingle({ tokenIn: '0x1', tokenOut: '0x2', amountIn: 1n, extras: { limit_sqrt_price: 9n }, }); expect(quote.amountOut).toBe(11n); expect(quote.sqrtPriceX96After).toBe(22n); const state = await engine.getCurrentPoolState(); expect(state.sqrtPriceX96).toBe(100n); expect(state.tick).toBe(5); }); it('Algebra19IntegralQuoteEngine resolves deployer and pool state', async () => { const quoter = makeQuoter({ amountOut: 12n, sqrtPriceX96After: 24n }); const pool = { slot0: jest.fn().mockResolvedValue({ price: 200n, currentTick: 7 }), }; const engine = new Algebra19IntegralQuoteEngine( quoter as any, pool as any, '0xdefault', ); const quote = await engine.quoteExactInputSingle({ tokenIn: '0x1', tokenOut: '0x2', amountIn: 1n, extras: { deployer: '0xcustom' }, }); expect(quote.amountOut).toBe(12n); expect(quote.sqrtPriceX96After).toBe(24n); const state = await engine.getCurrentPoolState(); expect(state.sqrtPriceX96).toBe(200n); expect(state.tick).toBe(7); }); it('AerodromeQuoteEngine resolves tick spacing from pool or extras', async () => { const quoter = makeQuoter({ amountOut: 13n, sqrtPriceX96After: 26n }); const pool = { slot0: jest.fn().mockResolvedValue({ sqrtPriceX96: 150n, tick: 4 }), tickSpacing: jest.fn().mockResolvedValue(15n), }; const engine = new AerodromeQuoteEngine(quoter as any, pool as any); const quote = await engine.quoteExactInputSingle({ tokenIn: '0x1', tokenOut: '0x2', amountIn: 1n, extras: {}, }); expect(quote.amountOut).toBe(13n); const quoteWithExtras = await engine.quoteExactInputSingle({ tokenIn: '0x1', tokenOut: '0x2', amountIn: 1n, extras: { tickSpacing: 20 }, }); expect(quoteWithExtras.sqrtPriceX96After).toBe(26n); const state = await engine.getCurrentPoolState(); expect(state.sqrtPriceX96).toBe(150n); expect(state.tick).toBe(4); }); it('ThickV2QuoteEngine validates tick spacing and reads pool state', async () => { const quoter = makeQuoter({ amountOut: 14n, sqrtPriceX96After: 28n }); const pool = { slot0: jest.fn().mockResolvedValue({ sqrtPrice: 160n, tick: 3, }), }; const engine = new ThickV2QuoteEngine(quoter as any, pool as any); await expect( engine.quoteExactInputSingle({ tokenIn: '0x1', tokenOut: '0x2', amountIn: 1n, extras: { tickSpacing: 'bad' }, }), ).rejects.toThrow('Cannot convert bad to a BigInt'); const quote = await engine.quoteExactInputSingle({ tokenIn: '0x1', tokenOut: '0x2', amountIn: 1n, extras: { tickSpacing: 10 }, }); expect(quote.amountOut).toBe(14n); const state = await engine.getCurrentPoolState(); expect(state.sqrtPriceX96).toBe(160n); expect(state.tick).toBe(3); }); it('UniswapV4QuoteEngine supports quotes and state view', async () => { const quoter = makeQuoter({ amountOut: 15n, sqrtPriceX96: 32n, amountIn: 40n, sqrtPriceX96After: 33n, }); const engine = new UniswapV4QuoteEngine( quoter as any, { currency0: '0x1', currency1: '0x2', fee: 3000, tickSpacing: 10, hooks: '0x0', }, 1, '0xstate', {} as any, ); const input = await engine.quoteExactInputSingle({ tokenIn: '0x1', tokenOut: '0x2', amountIn: 1n, extras: { hookData: '0x' }, }); expect(input.amountOut).toBe(15n); expect(input.sqrtPriceX96After).toBe(32n); const output = await engine.quoteExactOutputSingle({ tokenIn: '0x1', tokenOut: '0x2', amountOut: 2n, extras: { hookData: '0x' }, }); expect(output.amountIn).toBe(40n); expect(output.sqrtPriceX96After).toBe(33n); const state = await engine.getCurrentPoolState(); expect(state.sqrtPriceX96).toBe(123n); expect(state.tick).toBe(12); }); });