diff --git a/STATE.md b/STATE.md index 9195512..6ae76e7 100644 --- a/STATE.md +++ b/STATE.md @@ -34,6 +34,7 @@ - [2026-03-14] llm_contrarian.push3 AW=150/250 clamped to 100 — three rounds unaddressed (#756) - [2026-03-14] bootstrap.sh hardcodes BASE_SEPOLIA_LOCAL_FORK even on mainnet forks (#746) - [2026-03-14] remove MAX_ANCHOR_WIDTH clamp in ThreePositionStrategy (#783) +- [2026-03-15] re-add MAX_ANCHOR_WIDTH=1233 guard at LiquidityManager call site; anchorWidth clamped before _setPositions, independent of Optimizer (#817) - [2026-03-14] increase CALCULATE_PARAMS_GAS_LIMIT from 200k to 500k (#782) - [2026-03-15] add evolution run 8 champion to seed pool (#781) - [2026-03-15] fix FitnessEvaluator.t.sol broken on Base mainnet fork (#780) diff --git a/onchain/src/LiquidityManager.sol b/onchain/src/LiquidityManager.sol index baa56ef..75d081c 100644 --- a/onchain/src/LiquidityManager.sol +++ b/onchain/src/LiquidityManager.sol @@ -204,7 +204,7 @@ contract LiquidityManager is ThreePositionStrategy, PriceOracle { PositionParams memory params = PositionParams({ capitalInefficiency: (capitalInefficiency > MAX_PARAM_SCALE) ? MAX_PARAM_SCALE : capitalInefficiency, anchorShare: (anchorShare > MAX_PARAM_SCALE) ? MAX_PARAM_SCALE : anchorShare, - anchorWidth: anchorWidth, + anchorWidth: (anchorWidth > MAX_ANCHOR_WIDTH) ? MAX_ANCHOR_WIDTH : anchorWidth, discoveryDepth: (discoveryDepth > MAX_PARAM_SCALE) ? MAX_PARAM_SCALE : discoveryDepth }); diff --git a/onchain/src/abstracts/ThreePositionStrategy.sol b/onchain/src/abstracts/ThreePositionStrategy.sol index 7bab68a..d3e2c0b 100644 --- a/onchain/src/abstracts/ThreePositionStrategy.sol +++ b/onchain/src/abstracts/ThreePositionStrategy.sol @@ -30,6 +30,10 @@ abstract contract ThreePositionStrategy is UniswapMath, VWAPTracker { int24 internal constant DISCOVERY_SPACING = 11_000; /// @notice Minimum discovery depth multiplier uint128 internal constant MIN_DISCOVERY_DEPTH = 200; + /// @notice Maximum safe anchorWidth: ensures 34 * MAX_ANCHOR_WIDTH * TICK_SPACING / 100 fits in int24 + /// @dev With TICK_SPACING=200: 34 * 1233 * 200 = 8,384,400 ≤ int24 max (8,388,607). + /// anchorWidth=1234 produces 8,391,200 which overflows int24 and reverts in Solidity 0.8. + uint24 internal constant MAX_ANCHOR_WIDTH = 1233; /// @notice The three liquidity position types enum Stage { diff --git a/onchain/test/abstracts/ThreePositionStrategy.t.sol b/onchain/test/abstracts/ThreePositionStrategy.t.sol index 884b289..cfc868d 100644 --- a/onchain/test/abstracts/ThreePositionStrategy.t.sol +++ b/onchain/test/abstracts/ThreePositionStrategy.t.sol @@ -473,4 +473,32 @@ contract ThreePositionStrategyTest is TestConstants { strategy.setPositions(CURRENT_TICK, extremeParams); assertEq(strategy.getMintedPositionsCount(), 3, "Should handle extreme parameters gracefully"); } + + // ======================================== + // ANCHOR WIDTH BOUNDS TESTS (#817) + // ======================================== + + function testAnchorWidthAtMaxBoundarySucceeds() public { + // MAX_ANCHOR_WIDTH = 1233: 34 * 1233 * 200 = 8,384,400 fits within int24 max (8,388,607) + ThreePositionStrategy.PositionParams memory params = getDefaultParams(); + params.anchorWidth = 1233; + + strategy.setAnchorPosition(CURRENT_TICK, 20 ether, params); + + MockThreePositionStrategy.MintedPosition memory pos = strategy.getMintedPosition(0); + assertTrue(pos.tickLower < pos.tickUpper, "tickLower must be less than tickUpper"); + assertTrue(pos.tickLower >= -887272, "tickLower must be >= MIN_TICK"); + assertTrue(pos.tickUpper <= 887272, "tickUpper must be <= MAX_TICK"); + assertGt(pos.liquidity, 0, "Anchor should have positive liquidity"); + } + + function testAnchorWidthAboveMaxReverts() public { + // anchorWidth = 1234: 34 * 1234 * 200 = 8,391,200 overflows int24 — must not silently produce + // an invalid tick range; Solidity 0.8 arithmetic check causes a panic revert instead. + ThreePositionStrategy.PositionParams memory params = getDefaultParams(); + params.anchorWidth = 1234; + + vm.expectRevert(); + strategy.setAnchorPosition(CURRENT_TICK, 20 ether, params); + } }