Merge pull request 'fix: fix: remove MAX_ANCHOR_WIDTH clamp in ThreePositionStrategy (#783)' (#785) from fix/issue-783 into master
This commit is contained in:
commit
fb92beea9d
4 changed files with 15 additions and 19 deletions
1
STATE.md
1
STATE.md
|
|
@ -33,3 +33,4 @@
|
|||
- [2026-03-14] bootstrap.sh anvil_setCode guard now targets correct feeDest 0xf6a3... (#760)
|
||||
- [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)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ interface IOptimizer {
|
|||
* @notice Returns the four liquidity parameters used by LiquidityManager.recenter().
|
||||
* @return capitalInefficiency Capital buffer level (0..1e18, clamped to MAX_PARAM_SCALE). CI=0 is safest.
|
||||
* @return anchorShare Fraction of non-floor ETH in anchor (0..1e18, clamped to MAX_PARAM_SCALE).
|
||||
* @return anchorWidth Anchor position width in tick units (uint24); max 100 ticks (MAX_ANCHOR_WIDTH), enforced by LiquidityManager.
|
||||
* @return anchorWidth Anchor position width in tick units (uint24); passed through to ThreePositionStrategy without clamping.
|
||||
* @return discoveryDepth Discovery liquidity density (0..1e18, clamped to MAX_PARAM_SCALE).
|
||||
*/
|
||||
function getLiquidityParams()
|
||||
|
|
|
|||
|
|
@ -34,10 +34,6 @@ contract LiquidityManager is ThreePositionStrategy, PriceOracle {
|
|||
/// @notice Uniswap V3 fee tier (1%) - 10,000 basis points
|
||||
uint24 internal constant FEE = uint24(10_000);
|
||||
|
||||
/// @notice Maximum anchor width (in ticks) accepted from the optimizer.
|
||||
/// Any optimizer-returned value above this ceiling is silently clamped down.
|
||||
uint24 internal constant MAX_ANCHOR_WIDTH = 100;
|
||||
|
||||
/// @notice Upper bound (inclusive) for scale-1 optimizer parameters: capitalInefficiency,
|
||||
/// anchorShare, and discoveryDepth. Values above this ceiling are silently clamped.
|
||||
uint256 internal constant MAX_PARAM_SCALE = 10 ** 18;
|
||||
|
|
@ -208,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 > MAX_ANCHOR_WIDTH) ? MAX_ANCHOR_WIDTH : anchorWidth,
|
||||
anchorWidth: anchorWidth,
|
||||
discoveryDepth: (discoveryDepth > MAX_PARAM_SCALE) ? MAX_PARAM_SCALE : discoveryDepth
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1118,17 +1118,16 @@ contract LiquidityManagerTest is UniSwapHelper {
|
|||
}
|
||||
|
||||
/**
|
||||
* @notice Optimizer returning anchorWidth > 100 is clamped to MAX_ANCHOR_WIDTH (100), not rejected.
|
||||
* @dev Verifies that LiquidityManager silently truncates oversized anchorWidth values from
|
||||
* the optimizer rather than reverting. The resulting anchor position tick range must
|
||||
* equal exactly 100 * TICK_SPACING ticks wide.
|
||||
* @notice Optimizer returning anchorWidth > 100 is passed through unchanged (no clamping).
|
||||
* @dev Verifies that LiquidityManager does NOT clamp oversized anchorWidth values from
|
||||
* the optimizer. The resulting anchor position tick range must match anchorWidth=150 exactly.
|
||||
*/
|
||||
function testAnchorWidthAbove100IsClamped() public {
|
||||
// Deploy a ConfigurableOptimizer that returns anchorWidth = 150 (above the 100-tick ceiling)
|
||||
function testAnchorWidthAbove100IsNotClamped() public {
|
||||
// Deploy a ConfigurableOptimizer that returns anchorWidth = 150
|
||||
ConfigurableOptimizer highWidthOptimizer = new ConfigurableOptimizer(
|
||||
0, // capitalInefficiency = 0 (safest)
|
||||
3e17, // anchorShare = 30%
|
||||
150, // anchorWidth = 150 — intentionally above MAX_ANCHOR_WIDTH
|
||||
150, // anchorWidth = 150
|
||||
3e17 // discoveryDepth = 30%
|
||||
);
|
||||
|
||||
|
|
@ -1146,20 +1145,20 @@ contract LiquidityManagerTest is UniSwapHelper {
|
|||
address(highWidthOptimizer)
|
||||
);
|
||||
|
||||
// recenter() must succeed (value clamped, not rejected)
|
||||
// recenter() must succeed
|
||||
vm.prank(RECENTER_CALLER);
|
||||
customLm.recenter();
|
||||
|
||||
// Anchor position must exist and its tick range must match anchorWidth=100 (clamped), not 150.
|
||||
// Anchor position must exist and its tick range must match anchorWidth=150 (not clamped to 100).
|
||||
// The anchor spacing formula is: anchorSpacing = TICK_SPACING + (34 * anchorWidth * TICK_SPACING / 100)
|
||||
// For anchorWidth=100: anchorSpacing = 200 + 34*100*200/100 = 7000; tickWidth = 2*7000 = 14000
|
||||
// For anchorWidth=150: anchorSpacing = 200 + 34*150*200/100 = 10400; tickWidth = 2*10400 = 20800
|
||||
(uint128 liquidity, int24 tickLower, int24 tickUpper) = customLm.positions(ThreePositionStrategy.Stage.ANCHOR);
|
||||
assertTrue(liquidity > 0, "anchor position should have been placed");
|
||||
int24 tickWidth = tickUpper - tickLower;
|
||||
int24 expectedClamped = 2 * (TICK_SPACING + (34 * int24(100) * TICK_SPACING / 100)); // 14000
|
||||
int24 unclamped150 = 2 * (TICK_SPACING + (34 * int24(150) * TICK_SPACING / 100)); // 20800
|
||||
assertEq(tickWidth, expectedClamped, "anchorWidth above 100 must be clamped to MAX_ANCHOR_WIDTH=100");
|
||||
assertTrue(tickWidth < unclamped150, "clamped width must be narrower than unclamped anchorWidth=150 would produce");
|
||||
int24 expectedUnclamped = 2 * (TICK_SPACING + (34 * int24(150) * TICK_SPACING / 100)); // 20800
|
||||
int24 oldClamped100 = 2 * (TICK_SPACING + (34 * int24(100) * TICK_SPACING / 100)); // 14000
|
||||
assertEq(tickWidth, expectedUnclamped, "anchorWidth=150 must not be clamped - tick range must match 150");
|
||||
assertTrue(tickWidth > oldClamped100, "unclamped width must be wider than the old MAX_ANCHOR_WIDTH=100 range");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue