From e3c699b7eb2a4d7fc5160ba49cdfec9a23fdeec0 Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 18 Mar 2026 21:25:12 +0000 Subject: [PATCH 1/3] fix: No events on fee destination state changes (#958) Add FeeDestinationSet and FeeDestinationLocked events to LiquidityManager, emitted on every setFeeDestination() call and lock engagement respectively. Update tests to assert both events are emitted in all code paths. Co-Authored-By: Claude Sonnet 4.6 --- onchain/src/LiquidityManager.sol | 9 +++++++++ onchain/test/LiquidityManager.t.sol | 27 ++++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/onchain/src/LiquidityManager.sol b/onchain/src/LiquidityManager.sol index 34cae67..92ce730 100644 --- a/onchain/src/LiquidityManager.sol +++ b/onchain/src/LiquidityManager.sol @@ -65,6 +65,12 @@ contract LiquidityManager is ThreePositionStrategy, PriceOracle { /// @notice Emitted on each successful recenter for monitoring and indexing event Recentered(int24 indexed currentTick, bool indexed isUp); + /// @notice Emitted whenever feeDestination is updated + event FeeDestinationSet(address indexed newDest); + + /// @notice Emitted when the fee destination lock is permanently engaged + event FeeDestinationLocked(address indexed dest); + /// @notice Custom errors error ZeroAddressInSetter(); @@ -139,12 +145,15 @@ contract LiquidityManager is ThreePositionStrategy, PriceOracle { // to storage. A subsequent SELFDESTRUCT clears the bytecode but cannot undo this write. if (!feeDestinationLocked && feeDestination != address(0) && feeDestination.code.length > 0) { feeDestinationLocked = true; + emit FeeDestinationLocked(feeDestination); return; } require(!feeDestinationLocked, "fee destination locked"); feeDestination = feeDestination_; + emit FeeDestinationSet(feeDestination_); if (feeDestination_.code.length > 0) { feeDestinationLocked = true; + emit FeeDestinationLocked(feeDestination_); } } diff --git a/onchain/test/LiquidityManager.t.sol b/onchain/test/LiquidityManager.t.sol index 8c75a79..0c0e630 100644 --- a/onchain/test/LiquidityManager.t.sol +++ b/onchain/test/LiquidityManager.t.sol @@ -979,9 +979,15 @@ contract LiquidityManagerTest is UniSwapHelper { */ function testSetFeeDestinationEOA_MultipleAllowed() public { LiquidityManager freshLm = new LiquidityManager(address(factory), address(weth), address(harberg), address(optimizer)); - freshLm.setFeeDestination(makeAddr("firstFee")); + address firstFee = makeAddr("firstFee"); + address secondFee = makeAddr("secondFee"); + vm.expectEmit(true, false, false, false, address(freshLm)); + emit LiquidityManager.FeeDestinationSet(firstFee); + freshLm.setFeeDestination(firstFee); assertFalse(freshLm.feeDestinationLocked(), "should not be locked after EOA set"); - freshLm.setFeeDestination(makeAddr("secondFee")); + vm.expectEmit(true, false, false, false, address(freshLm)); + emit LiquidityManager.FeeDestinationSet(secondFee); + freshLm.setFeeDestination(secondFee); assertFalse(freshLm.feeDestinationLocked(), "should still not be locked after second EOA set"); } @@ -991,6 +997,10 @@ contract LiquidityManagerTest is UniSwapHelper { function testSetFeeDestinationContract_Locks() public { LiquidityManager freshLm = new LiquidityManager(address(factory), address(weth), address(harberg), address(optimizer)); // address(harberg) is a deployed contract + vm.expectEmit(true, false, false, false, address(freshLm)); + emit LiquidityManager.FeeDestinationSet(address(harberg)); + vm.expectEmit(true, false, false, false, address(freshLm)); + emit LiquidityManager.FeeDestinationLocked(address(harberg)); freshLm.setFeeDestination(address(harberg)); assertTrue(freshLm.feeDestinationLocked(), "should be locked after contract set"); assertEq(freshLm.feeDestination(), address(harberg)); @@ -1001,10 +1011,17 @@ contract LiquidityManagerTest is UniSwapHelper { */ function testSetFeeDestinationEOAToContract_Locks() public { LiquidityManager freshLm = new LiquidityManager(address(factory), address(weth), address(harberg), address(optimizer)); + address treasuryEOA = makeAddr("treasuryEOA"); // Step 1: set to an EOA during setup — allowed, not locked - freshLm.setFeeDestination(makeAddr("treasuryEOA")); + vm.expectEmit(true, false, false, false, address(freshLm)); + emit LiquidityManager.FeeDestinationSet(treasuryEOA); + freshLm.setFeeDestination(treasuryEOA); assertFalse(freshLm.feeDestinationLocked(), "not locked after EOA set"); // Step 2: upgrade to treasury contract once it is deployed — locks permanently + vm.expectEmit(true, false, false, false, address(freshLm)); + emit LiquidityManager.FeeDestinationSet(address(harberg)); + vm.expectEmit(true, false, false, false, address(freshLm)); + emit LiquidityManager.FeeDestinationLocked(address(harberg)); freshLm.setFeeDestination(address(harberg)); assertTrue(freshLm.feeDestinationLocked(), "locked after contract set"); assertEq(freshLm.feeDestination(), address(harberg)); @@ -1033,6 +1050,8 @@ contract LiquidityManagerTest is UniSwapHelper { address eoaAddr = makeAddr("precomputedEOA"); // Step 1: set to EOA — allowed, lock stays false + vm.expectEmit(true, false, false, false, address(freshLm)); + emit LiquidityManager.FeeDestinationSet(eoaAddr); freshLm.setFeeDestination(eoaAddr); assertFalse(freshLm.feeDestinationLocked(), "not locked after EOA set"); @@ -1043,6 +1062,8 @@ contract LiquidityManagerTest is UniSwapHelper { // Step 3: calling setFeeDestination detects bytecode at the current destination and // commits feeDestinationLocked = true WITHOUT reverting, so the write survives // a later SELFDESTRUCT. feeDestination itself is not changed. + vm.expectEmit(true, false, false, false, address(freshLm)); + emit LiquidityManager.FeeDestinationLocked(eoaAddr); freshLm.setFeeDestination(makeAddr("attacker")); assertTrue(freshLm.feeDestinationLocked(), "locked after bytecode detected at current feeDestination"); assertEq(freshLm.feeDestination(), eoaAddr, "feeDestination must not change when defensive lock triggers"); From d08388240d357b7c5e5c599ae71c2c373bb3ba5c Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 18 Mar 2026 21:36:25 +0000 Subject: [PATCH 2/3] ci: retrigger after infra failure (#958) From f33d5e932d3fa71ed5a516a25fbe06e9f43aa9cd Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 18 Mar 2026 22:06:13 +0000 Subject: [PATCH 3/3] fix: address review feedback for #958 - Document new LiquidityManager events in kraiken-lib/src/version.ts per AGENTS.md pre-PR checklist item 6 (Kraiken VERSION unchanged; no ponder subscriber impact) - Add vm.expectEmit assertions to testSetFeeDestinationLocked_Reverts for the setup call that now emits FeeDestinationSet + FeeDestinationLocked Co-Authored-By: Claude Sonnet 4.6 --- kraiken-lib/src/version.ts | 4 ++++ onchain/test/LiquidityManager.t.sol | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/kraiken-lib/src/version.ts b/kraiken-lib/src/version.ts index 33763a5..5ffa71d 100644 --- a/kraiken-lib/src/version.ts +++ b/kraiken-lib/src/version.ts @@ -25,6 +25,10 @@ export const STACK_META_ID = 'stack-meta'; * Version History: * - v1: Initial deployment (30-tier TAX_RATES, index-based staking) * - v2: OptimizerV3, VWAP mirror floor, directional VWAP recording + * + * LiquidityManager event additions (no Kraiken VERSION bump): + * - FeeDestinationSet(address indexed newDest) — emitted on every setFeeDestination() assignment + * - FeeDestinationLocked(address indexed dest) — emitted when the fee destination lock engages */ export const COMPATIBLE_CONTRACT_VERSIONS = [1, 2]; diff --git a/onchain/test/LiquidityManager.t.sol b/onchain/test/LiquidityManager.t.sol index 0c0e630..b092f14 100644 --- a/onchain/test/LiquidityManager.t.sol +++ b/onchain/test/LiquidityManager.t.sol @@ -1035,6 +1035,10 @@ contract LiquidityManagerTest is UniSwapHelper { */ function testSetFeeDestinationLocked_Reverts() public { LiquidityManager freshLm = new LiquidityManager(address(factory), address(weth), address(harberg), address(optimizer)); + vm.expectEmit(true, false, false, false, address(freshLm)); + emit LiquidityManager.FeeDestinationSet(address(harberg)); + vm.expectEmit(true, false, false, false, address(freshLm)); + emit LiquidityManager.FeeDestinationLocked(address(harberg)); freshLm.setFeeDestination(address(harberg)); vm.expectRevert("fee destination locked"); freshLm.setFeeDestination(makeAddr("anyAddr"));