From cb6d6e22922dc36c7f286a073041f3167b120d04 Mon Sep 17 00:00:00 2001 From: openhands Date: Thu, 12 Mar 2026 18:21:27 +0000 Subject: [PATCH 1/4] fix: LM recenter() return semantics undocumented (#570) Add NatSpec to recenter() documenting that the function always reverts on failure (never silently returns false), listing all four revert conditions, and clarifying that both true/false return values represent a successfully-executed recenter with the value indicating price direction (up vs down relative to previous anchor centre). Also fix StrategyExecutor.maybeRecenter() to capture the isUp return value from lm.recenter() and include it in the log output, making price direction visible in backtesting replays. Co-Authored-By: Claude Sonnet 4.6 --- .../script/backtesting/StrategyExecutor.sol | 8 ++++++-- onchain/src/LiquidityManager.sol | 18 ++++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/onchain/script/backtesting/StrategyExecutor.sol b/onchain/script/backtesting/StrategyExecutor.sol index ed78b1d..50f22b0 100644 --- a/onchain/script/backtesting/StrategyExecutor.sol +++ b/onchain/script/backtesting/StrategyExecutor.sol @@ -115,10 +115,12 @@ contract StrategyExecutor { lastRecenterBlock = blockNum; bool success; + bool isUp; string memory failReason; - try lm.recenter() { + try lm.recenter() returns (bool _isUp) { success = true; + isUp = _isUp; totalRecenters++; } catch Error(string memory reason) { failReason = reason; @@ -173,6 +175,7 @@ contract StrategyExecutor { _logRecenter( blockNum, + isUp, fLiqPre, fLoPre, fHiPre, @@ -228,6 +231,7 @@ contract StrategyExecutor { function _logRecenter( uint256 blockNum, + bool isUp, uint128 fLiqPre, int24 fLoPre, int24 fHiPre, @@ -252,7 +256,7 @@ contract StrategyExecutor { internal view { - console2.log(string.concat("=== Recenter #", totalRecenters.str(), " @ block ", blockNum.str(), " ===")); + console2.log(string.concat("=== Recenter #", totalRecenters.str(), " @ block ", blockNum.str(), " direction=", isUp ? "UP" : "DOWN", " ===")); console2.log(string.concat(" Floor pre: tick [", int256(fLoPre).istr(), ", ", int256(fHiPre).istr(), "] liq=", uint256(fLiqPre).str())); console2.log(string.concat(" Anchor pre: tick [", int256(aLoPre).istr(), ", ", int256(aHiPre).istr(), "] liq=", uint256(aLiqPre).str())); console2.log(string.concat(" Disc pre: tick [", int256(dLoPre).istr(), ", ", int256(dHiPre).istr(), "] liq=", uint256(dLiqPre).str())); diff --git a/onchain/src/LiquidityManager.sol b/onchain/src/LiquidityManager.sol index 91e2787..7041307 100644 --- a/onchain/src/LiquidityManager.sol +++ b/onchain/src/LiquidityManager.sol @@ -149,8 +149,22 @@ contract LiquidityManager is ThreePositionStrategy, PriceOracle { recenterAccess = address(0); } - /// @notice Adjusts liquidity positions in response to price movements - /// @return isUp True if price moved up (relative to token ordering) + /// @notice Adjusts liquidity positions in response to price movements. + /// This function either completes a full recenter (removing all positions, + /// recording VWAP where applicable, and redeploying liquidity) or reverts — + /// it never returns silently without acting. + /// + /// @dev Revert conditions (no silent false return for failure): + /// - "access denied" — recenterAccess is set and caller is not that address + /// - "recenter cooldown" — recenterAccess is unset and MIN_RECENTER_INTERVAL has not elapsed + /// - "price deviated from oracle" — recenterAccess is unset and price is outside TWAP bounds + /// - "amplitude not reached." — anchor position exists but price has not moved far enough + /// from the anchor centre to warrant repositioning + /// + /// @return isUp True if the current tick is above the previous anchor centre + /// (price moved up in pool terms); false if it moved down or if no anchor + /// position existed prior to this recenter (bootstrap case). + /// Both values indicate a successful, fully-executed recenter. function recenter() external returns (bool isUp) { (, int24 currentTick,,,,,) = pool.slot0(); From c1a0086d28caccdcca5eed11308332b9082970ef Mon Sep 17 00:00:00 2001 From: openhands Date: Thu, 12 Mar 2026 18:33:34 +0000 Subject: [PATCH 2/4] ci: retrigger after infra failure From 54976184bc1b9da89d8e9a029e56ee246641945a Mon Sep 17 00:00:00 2001 From: openhands Date: Thu, 12 Mar 2026 19:12:46 +0000 Subject: [PATCH 3/4] fix: correct isUp NatSpec and bootstrap log direction (#570) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the @return NatSpec for recenter() isUp: the previous description was wrong for the token0=WETH ordering (claimed tick above center, but the actual check is currentTick < centerTick when token0isWeth). The correct invariant is isUp=true ↔ KRK price in ETH rose (buy event / net ETH inflow), regardless of token ordering. Also address review nit: StrategyExecutor._logRecenter() now logs 'direction=BOOTSTRAP' instead of 'direction=DOWN' when no anchor position existed before the recenter (aLiqPre==0), eliminating the misleading directional label on the first recenter. Co-Authored-By: Claude Sonnet 4.6 --- onchain/script/backtesting/StrategyExecutor.sol | 4 +++- onchain/src/LiquidityManager.sol | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/onchain/script/backtesting/StrategyExecutor.sol b/onchain/script/backtesting/StrategyExecutor.sol index 50f22b0..14708f5 100644 --- a/onchain/script/backtesting/StrategyExecutor.sol +++ b/onchain/script/backtesting/StrategyExecutor.sol @@ -176,6 +176,7 @@ contract StrategyExecutor { _logRecenter( blockNum, isUp, + aLiqPre == 0, // bootstrap: no anchor existed before this recenter fLiqPre, fLoPre, fHiPre, @@ -232,6 +233,7 @@ contract StrategyExecutor { function _logRecenter( uint256 blockNum, bool isUp, + bool isBootstrap, uint128 fLiqPre, int24 fLoPre, int24 fHiPre, @@ -256,7 +258,7 @@ contract StrategyExecutor { internal view { - console2.log(string.concat("=== Recenter #", totalRecenters.str(), " @ block ", blockNum.str(), " direction=", isUp ? "UP" : "DOWN", " ===")); + console2.log(string.concat("=== Recenter #", totalRecenters.str(), " @ block ", blockNum.str(), " direction=", isBootstrap ? "BOOTSTRAP" : (isUp ? "UP" : "DOWN"), " ===")); console2.log(string.concat(" Floor pre: tick [", int256(fLoPre).istr(), ", ", int256(fHiPre).istr(), "] liq=", uint256(fLiqPre).str())); console2.log(string.concat(" Anchor pre: tick [", int256(aLoPre).istr(), ", ", int256(aHiPre).istr(), "] liq=", uint256(aLiqPre).str())); console2.log(string.concat(" Disc pre: tick [", int256(dLoPre).istr(), ", ", int256(dHiPre).istr(), "] liq=", uint256(dLiqPre).str())); diff --git a/onchain/src/LiquidityManager.sol b/onchain/src/LiquidityManager.sol index 7041307..36ca718 100644 --- a/onchain/src/LiquidityManager.sol +++ b/onchain/src/LiquidityManager.sol @@ -161,9 +161,10 @@ contract LiquidityManager is ThreePositionStrategy, PriceOracle { /// - "amplitude not reached." — anchor position exists but price has not moved far enough /// from the anchor centre to warrant repositioning /// - /// @return isUp True if the current tick is above the previous anchor centre - /// (price moved up in pool terms); false if it moved down or if no anchor - /// position existed prior to this recenter (bootstrap case). + /// @return isUp True if the KRK price in ETH rose since the last recenter + /// (buy event / net ETH inflow), regardless of token0/token1 ordering. + /// False if the KRK price fell, or if no anchor position existed prior + /// to this recenter (bootstrap case, no directional reference point). /// Both values indicate a successful, fully-executed recenter. function recenter() external returns (bool isUp) { (, int24 currentTick,,,,,) = pool.slot0(); From 6ffbd8df335a78701ebbbcd6b1231941f5b760b7 Mon Sep 17 00:00:00 2001 From: openhands Date: Thu, 12 Mar 2026 20:16:39 +0000 Subject: [PATCH 4/4] ci: retrigger after infra failure