fix: address AI review findings for #706 recenterAccess removal

- DeployBase.sol: remove broken inline second recenter() (would always
  revert with 'recenter cooldown' in same Forge broadcast); replace with
  operator instructions to run the new BootstrapVWAPPhase2.s.sol script
  at least 60 s after deployment
- BootstrapVWAPPhase2.s.sol: new script for the second VWAP bootstrap
  recenter on Base mainnet deployments
- StrategyExecutor.sol: update stale docstring that still described the
  removed recenterAccess bypass; reflect permissionless model with vm.warp
- TestBase.sol: remove vestigial recenterCaller parameter from all four
  setupEnvironment* functions (parameter was silently ignored after
  setRecenterAccess was removed); update all callers across six test files
- bootstrap-common.sh: fix misleading retry recenter in
  seed_application_state() — add evm_increaseTime 61 before evm_mine so
  the recenter cooldown actually clears and the retry can succeed

All 210 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
openhands 2026-03-14 09:15:48 +00:00
parent 9b53f409b7
commit 0d3aee15b4
11 changed files with 71 additions and 42 deletions

View file

@ -0,0 +1,45 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;
import { LiquidityManager } from "../src/LiquidityManager.sol";
import "forge-std/Script.sol";
/**
* @title BootstrapVWAPPhase2
* @notice Second phase of the VWAP bootstrap for Base mainnet deployments.
*
* Run this script >= 60 seconds after DeployBase (or DeployBaseMainnet/DeployBaseSepolia)
* finishes. The first recenter() sets lastRecenterTime; the 60-second cooldown must
* elapse before this second recenter() can succeed.
*
* What this does:
* - Calls liquidityManager.recenter() a second time.
* - At this point cumulativeVolume == 0 (bootstrap path) and the seed buy has
* generated ethFee > 0, so recenter() records the VWAP anchor.
* - Asserts cumulativeVolume > 0 to confirm bootstrap success.
*
* Usage:
* export LM_ADDRESS=<deployed LiquidityManager address>
* forge script script/BootstrapVWAPPhase2.s.sol --tc BootstrapVWAPPhase2 \
* --fork-url $BASE_RPC --broadcast
*/
contract BootstrapVWAPPhase2 is Script {
function run() public {
address lmAddress = vm.envAddress("LM_ADDRESS");
LiquidityManager lm = LiquidityManager(payable(lmAddress));
string memory seedPhrase = vm.readFile(".secret");
uint256 privateKey = vm.deriveKey(seedPhrase, 0);
vm.startBroadcast(privateKey);
console.log("Running VWAP bootstrap phase 2 on LiquidityManager:", lmAddress);
lm.recenter();
uint256 cumVol = lm.cumulativeVolume();
require(cumVol > 0, "VWAP bootstrap failed: cumulativeVolume is still 0");
console.log("VWAP bootstrapped successfully. cumulativeVolume:", cumVol);
vm.stopBroadcast();
}
}

View file

@ -137,12 +137,9 @@ contract DeployBase is Script {
console.log("Seed buy executed -> fee generated in anchor position");
// Step 4: Second recenter records VWAP (bootstrap path + ethFee > 0).
// NOTE: Must be called >= 60s after the first recenter (cooldown) AND
// >= 300s after pool init so TWAP has settled at post-buy price.
// On Base mainnet, mine/wait ~5 min between Step 2 and Step 4.
liquidityManager.recenter();
require(liquidityManager.cumulativeVolume() > 0, "VWAP bootstrap failed: cumulativeVolume is 0");
console.log("VWAP bootstrapped -> cumulativeVolume:", liquidityManager.cumulativeVolume());
// Cannot be called in the same Forge broadcast as Step 2 recenter() enforces a
// 60-second cooldown and there is no time-warp mechanism in a live broadcast.
// Run BootstrapVWAPPhase2.s.sol at least 60 seconds after this script completes.
console.log("\n=== Deployment Complete ===");
console.log("Kraiken:", address(kraiken));
@ -151,8 +148,11 @@ contract DeployBase is Script {
console.log("LiquidityManager:", address(liquidityManager));
console.log("Optimizer:", optimizerAddress);
console.log("\nPost-deploy steps:");
console.log(" 1. Fund LiquidityManager with operational ETH (VWAP already bootstrapped)");
console.log(" 2. recenter() is now permissionless - any address (e.g. txnBot) can call it.");
console.log(" 1. Wait >= 60 s after this script finishes.");
console.log(" 2. Run: forge script script/BootstrapVWAPPhase2.s.sol --tc BootstrapVWAPPhase2 --fork-url <RPC> --broadcast");
console.log(" This performs the second recenter that records cumulativeVolume > 0.");
console.log(" 3. Fund LiquidityManager with operational ETH.");
console.log(" 4. recenter() is permissionless - any address (e.g. txnBot) can call it.");
vm.stopBroadcast();
}

View file

@ -24,9 +24,9 @@ import { console2 } from "forge-std/console2.sol";
* notified on every block (for time-in-range) and on each successful recenter
* (for position lifecycle and fee/IL accounting).
*
* Access model: StrategyExecutor must be set as recenterAccess on the LM so that
* the cooldown and TWAP price-stability checks are bypassed in the simulation
* (vm.warp advances simulated time, not real oracle state).
* Access model: recenter() is permissionless no special access grant is required.
* EventReplayer advances block.timestamp via vm.warp, so the 60-second cooldown and
* the 300-second TWAP window pass normally during simulation.
*
* TODO(#319): The negligible-impact assumption means we replay historical events
* as-is without accounting for KrAIken's own liquidity affecting swap outcomes.