import { Fraction } from '@uniswap/sdk-core'; import { AlgebraIntegralV2CustomRouter, calculateAmountToSwap, fractionAbsoluteValue, } from '../../src/routers/algebra/algebra-integral-v2-router'; jest.mock('../../src/routers/base/base-router', () => { return { BaseRouter: jest.fn().mockImplementation(() => ({ getSwapAmount: jest .fn() .mockResolvedValue({ amountToSwap: 8n, zeroForOne: true }), })), }; }); const makePool = (withTickSpacing = false) => ({ globalState: jest .fn() .mockResolvedValue({ price: 79228162514264337593543950336n }), tickSpacing: withTickSpacing ? jest.fn().mockResolvedValue(15) : undefined, }); const makeQuoter = () => ({ callStatic: { quoteExactInputSingle: jest.fn().mockResolvedValue({ amountOut: 1n, sqrtPriceX96After: 2n, }), }, }); describe('Algebra integral v2 router helpers', () => { it('fractionAbsoluteValue returns positive numerator and denominator', () => { const frac = new Fraction(-5, 6); const abs = fractionAbsoluteValue(frac); expect(abs.numerator.toString()).toBe('5'); expect(abs.denominator.toString()).toBe('6'); }); it('calculateAmountToSwap handles zero and infinite ratios', () => { const zeroRatio = new Fraction(0, 1); expect(calculateAmountToSwap(zeroRatio, 1n, true, 4n, 1n)).toBe(4n); expect(() => calculateAmountToSwap(zeroRatio, 1n, false, 4n, 1n)).toThrow( 'wrong parameters', ); const infRatio = new Fraction(1, 0); expect(calculateAmountToSwap(infRatio, 1n, false, 4n, 1n)).toBe(1n); expect(() => calculateAmountToSwap(infRatio, 1n, true, 4n, 1n)).toThrow( 'wrong parameters', ); }); }); describe('AlgebraIntegralV2CustomRouter', () => { const positions = [{ lowerTick: -60, upperTick: 60, weight: 1 }]; it('returns zero when already balanced', async () => { const pool = makePool(); const quoter = makeQuoter(); const logger = { info: jest.fn() } as any; const router = new AlgebraIntegralV2CustomRouter(quoter as any, logger); const result = await router.getSwapAmount( pool as any, positions as any, 1n, 1n, '0x1', '0x2', 1, new Fraction(1, 1), ); expect(result.amountToSwap).toBe(0n); }); it('falls back to base router when legacy flow fails and passes extras', async () => { const pool = makePool(true); pool.globalState = jest.fn().mockRejectedValue(new Error('fail')); const quoter = makeQuoter(); const logger = { info: jest.fn() } as any; const router = new AlgebraIntegralV2CustomRouter(quoter as any, logger); const result = await router.getSwapAmount( pool as any, positions as any, 10n, 20n, '0x1', '0x2', 2, ); const baseRouterModule = require('../../src/routers/base/base-router'); const baseRouterInstance = baseRouterModule.BaseRouter.mock.results[0].value; const args = baseRouterInstance.getSwapAmount.mock.calls[0][0]; expect(args.engineExtras).toEqual({ tickSpacing: 15, deployer: expect.any(String), }); expect(result).toEqual({ amountToSwap: 8n, zeroForOne: true }); }); it('throws when fallback fails', async () => { const baseRouterModule = require('../../src/routers/base/base-router'); baseRouterModule.BaseRouter.mockImplementationOnce(() => ({ getSwapAmount: jest.fn().mockRejectedValue(new Error('fallback failed')), })); const pool = makePool(); pool.globalState = jest.fn().mockRejectedValue(new Error('fail')); const quoter = makeQuoter(); const logger = { info: jest.fn() } as any; const router = new AlgebraIntegralV2CustomRouter(quoter as any, logger); await expect( router.getSwapAmount( pool as any, positions as any, 10n, 20n, '0x1', '0x2', 2, ), ).rejects.toThrow('fallback failed'); }); });