From 3a239b6cbfcc840c1b7831f548432c078fa706b0 Mon Sep 17 00:00:00 2001 From: giteadmin Date: Sun, 6 Jul 2025 10:29:34 +0200 Subject: [PATCH] cleaned up tests --- onchain/test/LiquidityManager.t.sol | 186 +++++++++++++--------------- 1 file changed, 84 insertions(+), 102 deletions(-) diff --git a/onchain/test/LiquidityManager.t.sol b/onchain/test/LiquidityManager.t.sol index 581d811..9865a65 100644 --- a/onchain/test/LiquidityManager.t.sol +++ b/onchain/test/LiquidityManager.t.sol @@ -1,6 +1,16 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.19; +/** + * @title LiquidityManager Test Suite + * @notice Comprehensive tests for the LiquidityManager contract including: + * - Extreme price condition handling + * - Protocol death scenarios + * - Liquidity recentering operations + * - Edge case classification and recovery + * @dev Uses setUp() pattern for consistent test initialization + */ + import "forge-std/Test.sol"; import "@aperture/uni-v3-lib/TickMath.sol"; import {LiquidityAmounts} from "@aperture/uni-v3-lib/LiquidityAmounts.sol"; @@ -20,11 +30,12 @@ import {UniswapTestBase} from "./helpers/UniswapTestBase.sol"; import "../src/Optimizer.sol"; import "../test/mocks/MockOptimizer.sol"; +// Test constants address constant TAX_POOL = address(2); -// default fee of 1% -uint24 constant FEE = uint24(10_000); +uint24 constant FEE = uint24(10_000); // 1% fee int24 constant TICK_SPACING = 200; int24 constant ANCHOR_SPACING = 5 * TICK_SPACING; +int24 constant EXTREME_PRICE_MARGIN = 12000; // Safety margin from tick boundaries // Dummy.sol contract Dummy { @@ -32,6 +43,13 @@ contract Dummy { } contract LiquidityManagerTest is UniswapTestBase, CSVManager { + + // Setup configuration + bool constant DEFAULT_TOKEN0_IS_WETH = false; + uint256 constant DEFAULT_ACCOUNT_BALANCE = 300 ether; + + // Flag to skip automatic setUp for tests that need custom setup + bool private _skipAutoSetup; using UniswapHelpers for IUniswapV3Pool; using CSVHelper for *; @@ -110,13 +128,13 @@ contract LiquidityManagerTest is UniswapTestBase, CSVManager { (, int24 currentTick, , , , , ) = pool.slot0(); // Handle extreme expensive HARB (near MAX_TICK) - perform swap first - if (currentTick >= TickMath.MAX_TICK - 12000) { + if (currentTick >= TickMath.MAX_TICK - EXTREME_PRICE_MARGIN) { console.log("Detected extremely expensive HARB, performing normalizing swap..."); _performNormalizingSwap(currentTick, true); // true = expensive HARB } // Handle extreme cheap HARB (near MIN_TICK) - perform swap first - if (currentTick <= TickMath.MIN_TICK + 12000) { + if (currentTick <= TickMath.MIN_TICK + EXTREME_PRICE_MARGIN) { console.log("Detected extremely cheap HARB, performing normalizing swap..."); _performNormalizingSwap(currentTick, false); // false = cheap HARB } @@ -297,14 +315,10 @@ contract LiquidityManagerTest is UniswapTestBase, CSVManager { writeCSVToFile("./out/positions.csv"); // Write CSV to file } + /// @notice Tests overflow handling in cumulative calculations + /// @dev Simulates extreme values that could cause arithmetic overflow function testHandleCumulativeOverflow() public { - setUpCustomToken0(false); - vm.deal(account, 201 ether); - vm.prank(account); - weth.deposit{value: 201 ether}(); - - // Setup initial liquidity - recenter(false); + _setupCustom(false, 201 ether); vm.store( address(lm), @@ -339,18 +353,51 @@ contract LiquidityManagerTest is UniswapTestBase, CSVManager { assertTrue(calculatedPrice > 0 && calculatedPrice < 10**40, "Calculated price after wrap-around is not within a reasonable range"); } - function testExtremeExpensiveHarbHandling() public { - setUpCustomToken0(false); - vm.deal(account, 300 ether); + function setUp() public { + if (!_skipAutoSetup) { + _commonSetup(DEFAULT_TOKEN0_IS_WETH, DEFAULT_ACCOUNT_BALANCE); + } + } + + /// @notice Call this in tests that need custom setup to skip automatic setUp + function _skipSetup() internal { + _skipAutoSetup = true; + } + + /// @notice Grant recenter access for testing (commonly needed) + function _grantRecenterAccess() internal { + vm.prank(feeDestination); + lm.setRecenterAccess(address(this)); + } + + /// @notice Setup with custom parameters but standard flow + function _setupCustom(bool token0IsWeth, uint256 accountBalance) internal { + _skipSetup(); + _commonSetup(token0IsWeth, accountBalance); + } + + /// @notice Common setup for most tests + /// @param token0IsWeth Whether token0 should be WETH + /// @param accountBalance How much ETH to give to account + function _commonSetup(bool token0IsWeth, uint256 accountBalance) internal { + setUpCustomToken0(token0IsWeth); + + // Fund account and convert to WETH + vm.deal(account, accountBalance); vm.prank(account); - weth.deposit{value: 300 ether}(); - + weth.deposit{value: accountBalance}(); + // Grant recenter access to bypass oracle checks vm.prank(feeDestination); lm.setRecenterAccess(address(this)); - + // Setup initial liquidity recenter(false); + } + + /// @notice Tests handling of extremely expensive HARB prices near MAX_TICK + /// @dev Validates client-side price detection and normalization swaps + function testExtremeExpensiveHarbHandling() public { // Record initial state (, int24 initialTick, , , , , ) = pool.slot0(); @@ -366,7 +413,7 @@ contract LiquidityManagerTest is UniswapTestBase, CSVManager { // Test client-side detection and normalization console.log("\n=== PHASE 2: Test client-side normalization ==="); - if (postBuyTick >= TickMath.MAX_TICK - 12000) { + if (postBuyTick >= TickMath.MAX_TICK - EXTREME_PRICE_MARGIN) { console.log("[SUCCESS] Successfully pushed to extreme expensive range"); console.log("[SUCCESS] Client-side detection should trigger normalization swap"); } else { @@ -401,7 +448,7 @@ contract LiquidityManagerTest is UniswapTestBase, CSVManager { console.log("[SUCCESS] Client-side normalization: PASSED"); console.log("[SUCCESS] No arithmetic overflow: PASSED"); - assertTrue(true, "Extreme expensive HARB handling test completed successfully"); + // Test passes if we reach here without reverting } // Custom error types for better test diagnostics @@ -417,8 +464,7 @@ contract LiquidityManagerTest is UniswapTestBase, CSVManager { if (reason.length >= 4) { bytes4 selector = bytes4(reason); - // Log the selector for debugging - console.log("Error selector:", vm.toString(uint256(uint32(selector)))); + // Note: Error selector logged for debugging when needed if (selector == 0xae47f702) { // FullMulDivFailed() return (FailureType.ARITHMETIC_OVERFLOW, "FullMulDivFailed - arithmetic overflow in liquidity calculations"); @@ -468,18 +514,15 @@ contract LiquidityManagerTest is UniswapTestBase, CSVManager { return (FailureType.OTHER_ERROR, "Unclassified error"); } + /// @notice Helper to decode string errors from revert data function decodeStringError(bytes memory data) external pure returns (string memory) { return abi.decode(data, (string)); } + /// @notice Tests systematic classification of different failure modes + /// @dev Performs multiple trading cycles to trigger various edge cases function testEdgeCaseClassification() public { - setUpCustomToken0(false); - vm.deal(account, 20 ether); - vm.prank(account); - weth.deposit{value: 20 ether}(); - - // Setup initial liquidity - recenter(false); + _setupCustom(DEFAULT_TOKEN0_IS_WETH, 20 ether); uint256 successCount = 0; uint256 arithmeticOverflowCount = 0; @@ -549,21 +592,12 @@ contract LiquidityManagerTest is UniswapTestBase, CSVManager { console.log("Other errors:", vm.toString(otherErrorCount)); // Test should complete - assertTrue(true, "Edge case classification test completed"); + // Test passes if we reach here without reverting } + /// @notice Tests distinction between protocol death and recoverable edge cases + /// @dev Analyzes ETH reserves vs outstanding HARB to diagnose scenario type function testProtocolDeathVsEdgeCase() public { - setUpCustomToken0(false); - vm.deal(account, 300 ether); - vm.prank(account); - weth.deposit{value: 300 ether}(); - - // Grant recenter access to bypass oracle checks - vm.prank(feeDestination); - lm.setRecenterAccess(address(this)); - - // Setup initial liquidity - recenter(false); // Record initial state uint256 initialEthBalance = address(lm).balance + weth.balanceOf(address(lm)); @@ -596,9 +630,9 @@ contract LiquidityManagerTest is UniswapTestBase, CSVManager { // Diagnose the scenario type console.log("\n=== SCENARIO DIAGNOSIS ==="); - if (postBuyTick >= TickMath.MAX_TICK - 12000) { + if (postBuyTick >= TickMath.MAX_TICK - EXTREME_PRICE_MARGIN) { console.log("[DIAGNOSIS] EXTREME EXPENSIVE HARB - should trigger normalization"); - } else if (postBuyTick <= TickMath.MIN_TICK + 12000) { + } else if (postBuyTick <= TickMath.MIN_TICK + EXTREME_PRICE_MARGIN) { console.log("[DIAGNOSIS] EXTREME CHEAP HARB - potential protocol death"); } else { console.log("[DIAGNOSIS] NORMAL RANGE - may still have arithmetic issues"); @@ -620,74 +654,22 @@ contract LiquidityManagerTest is UniswapTestBase, CSVManager { console.log("Final tick:", vm.toString(finalTick)); console.log("[SUCCESS] Test completed successfully"); - assertTrue(true, "Protocol death vs edge case test completed"); + // Test passes if we reach here without reverting } - // function testScenarioB() public { - // setUpCustomToken0(false); - // vm.deal(account, 501 ether); - // vm.prank(account); - // weth.deposit{value: 501 ether}(); - - // uint256 traderBalanceBefore = weth.balanceOf(account); - - // // Setup initial liquidity - // recenter(false); - - // buy(25 ether); - - // recenter(false); - - // buy(45 ether); - - // recenter(false); - - // buy(80 ether); - - // recenter(false); - - // buy(120 ether); - - // recenter(false); - - // sell(harberg.balanceOf(account) / 4); - - // recenter(true); - - // sell(harberg.balanceOf(account) / 4); - - // recenter(true); - - // sell(harberg.balanceOf(account) / 4); - - // recenter(true); - - // sell(harberg.balanceOf(account)); - - // recenter(true); - - // writeCsv(); - // uint256 traderBalanceAfter = weth.balanceOf(account); - // console.log(traderBalanceBefore); - // console.log(traderBalanceAfter); - // assertGt(traderBalanceBefore, traderBalanceAfter, "trader should not have made profit"); - // revert(); - // } + /// @notice Fuzz test with random trading sequences to discover edge cases + /// @dev Tests protocol robustness under unpredictable trading patterns + /// @param numActions Number of buy/sell operations to perform + /// @param frequency Frequency parameter (for future use) + /// @param amounts Array of trade amounts to use function testScenarioFuzz(uint8 numActions, uint8 frequency, uint8[] calldata amounts) public { vm.assume(numActions > 5); vm.assume(frequency > 0); - vm.assume(frequency < 20); + vm.assume(frequency < 20); // Bound frequency parameter vm.assume(amounts.length >= numActions); - setUpCustomToken0(numActions % 2 == 0 ? true : false); - vm.deal(account, 20 ether); - vm.prank(account); - weth.deposit{value: 20 ether}(); - - - // Setup initial liquidity - recenter(false); + _setupCustom(numActions % 2 == 0 ? true : false, 20 ether); uint256 traderBalanceBefore = weth.balanceOf(account); uint8 f = 0;