cleaned up tests
This commit is contained in:
parent
79c26e3c31
commit
3a239b6cbf
1 changed files with 84 additions and 102 deletions
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue