// SPDX-License-Identifier: MIT pragma solidity 0.8.36; import {Test} from "forge-std/Test.sol"; import {TickMath} from "../src/libraries/TickMath.sol"; import {SqrtPriceMath} from "../src/libraries/SqrtPriceMath.sol"; import {SwapMath} from "../src/libraries/SwapMath.sol"; import {FullMath} from "../src/libraries/FullMath.sol"; contract ClMathTest is Test { uint160 private constant priceAtTickZero = 79228162514264337593543950336; function testSqrtRatioAtKnownTicks() public pure { assertEq(TickMath.getSqrtRatioAtTick(0), priceAtTickZero); assertEq(TickMath.getSqrtRatioAtTick(TickMath.minTick), TickMath.minSqrtRatio); assertEq(TickMath.getSqrtRatioAtTick(TickMath.maxTick), TickMath.maxSqrtRatio); } function testTickAtKnownRatios() public pure { assertEq(TickMath.getTickAtSqrtRatio(priceAtTickZero), 0); assertEq(TickMath.getTickAtSqrtRatio(TickMath.minSqrtRatio), TickMath.minTick); } function testSqrtRatioMonotonic() public pure { assertGt(TickMath.getSqrtRatioAtTick(1), TickMath.getSqrtRatioAtTick(0)); assertLt(TickMath.getSqrtRatioAtTick(-1), TickMath.getSqrtRatioAtTick(0)); assertGt(TickMath.getSqrtRatioAtTick(500000), TickMath.getSqrtRatioAtTick(499999)); } function testFuzzTickRoundTrip(int24 tick) public pure { tick = int24(bound(tick, TickMath.minTick, TickMath.maxTick)); uint160 ratio = TickMath.getSqrtRatioAtTick(tick); if (ratio >= TickMath.maxSqrtRatio) return; int24 recovered = TickMath.getTickAtSqrtRatio(ratio); assertEq(recovered, tick); } function testFuzzTickAtRatioWithinBounds(uint160 ratio) public pure { ratio = uint160(bound(ratio, TickMath.minSqrtRatio, TickMath.maxSqrtRatio - 1)); int24 tick = TickMath.getTickAtSqrtRatio(ratio); assertGe(TickMath.getSqrtRatioAtTick(tick), TickMath.minSqrtRatio == 0 ? 0 : 0); assertLe(TickMath.getSqrtRatioAtTick(tick), ratio); assertGt(TickMath.getSqrtRatioAtTick(tick + 1), ratio); } function testAmount1DeltaKnown() public pure { uint160 a = TickMath.getSqrtRatioAtTick(0); uint160 b = TickMath.getSqrtRatioAtTick(120); uint256 delta = SqrtPriceMath.getAmount1Delta(a, b, 1e18, false); assertEq(delta, FullMath.mulDiv(1e18, b - a, 0x1000000000000000000000000)); } function testAmount0DeltaSymmetric() public pure { uint160 a = TickMath.getSqrtRatioAtTick(-60); uint160 b = TickMath.getSqrtRatioAtTick(60); uint256 up = SqrtPriceMath.getAmount0Delta(a, b, 5e18, true); uint256 down = SqrtPriceMath.getAmount0Delta(a, b, 5e18, false); assertGe(up, down); assertLe(up - down, 1); } function testComputeSwapStepExactInReachesTarget() public pure { uint160 current = TickMath.getSqrtRatioAtTick(0); uint160 target = TickMath.getSqrtRatioAtTick(-600); (uint160 next, uint256 amountIn, uint256 amountOut, uint256 feeAmount) = SwapMath.computeSwapStep(current, target, 1e21, 1e24, 3000); assertEq(next, target); assertGt(amountIn, 0); assertGt(amountOut, 0); assertGt(feeAmount, 0); } function testComputeSwapStepPartialFill() public pure { uint160 current = TickMath.getSqrtRatioAtTick(0); uint160 target = TickMath.getSqrtRatioAtTick(-600); (uint160 next,, uint256 amountOut,) = SwapMath.computeSwapStep(current, target, 1e21, 1e15, 3000); assertGt(next, target); assertLt(next, current); assertGt(amountOut, 0); } function testFuzzComputeSwapStepFeeBound(uint128 liquidity, uint128 amount) public pure { liquidity = uint128(bound(liquidity, 1e12, 1e30)); int256 amountIn = int256(uint256(bound(amount, 1, 1e30))); uint160 current = TickMath.getSqrtRatioAtTick(0); uint160 target = TickMath.getSqrtRatioAtTick(-887000); (, uint256 stepIn,, uint256 feeAmount) = SwapMath.computeSwapStep(current, target, liquidity, amountIn, 3000); assertLe(stepIn + feeAmount, uint256(amountIn)); } }