Add require(shift == 0) guards to Optimizer.calculateParams and
OptimizerV3.calculateParams so non-zero shifts revert instead of being
silently discarded. OptimizerV3Push3 already had this guard.
Update IOptimizer.sol NatSpec to document that shift is reserved for
future use and must be 0 in all current implementations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add onchain/deployments-local.json to .gitignore so it is no longer tracked
- Remove the stale committed file from git
- Update fitness.sh to read LM address from forge broadcast JSON
(DeployLocal.sol's run-latest.json) instead of the potentially stale
deployments-local.json, matching the approach deploy-optimizer.sh already uses
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move overflow guard to the actual vulnerable site:
ThreePositionStrategy._computeFloorTickWithSignal() line 262 where
vwapX96 >> 32 is cast to int128 for _tickAtPriceRatio. Values
exceeding int128.max now skip mirror tick (fallback to scarcity/clamp)
instead of reverting.
Remove incorrect require from Optimizer._buildInputs() which guarded
a non-existent int256 cast path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add address(0) guard to fee transfer condition in _scrapePositions so
that when feeDestination is uninitialized, fees accrue as deployable
liquidity instead of reverting on safeTransfer to the zero address.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wrap mint_lp and burn_lp ops in _executeOp with try/catch to match
the soft-fail pattern used by buy, sell, stake, and unstake. Replace
burn_lp's require() with a soft return for out-of-range index validation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add recovery procedure documentation and automated recovery script for
when the VWAP bootstrap fails partway through (e.g. second recenter
reverts due to insufficient price movement).
- Add "Recovery from failed mid-sequence bootstrap" section to
docs/mainnet-bootstrap.md with diagnosis steps and manual recovery
- Create scripts/recover-bootstrap.sh to automate diagnosis and retry
- Add warning comments in BootstrapVWAPPhase2.s.sol, DeployBase.sol,
and bootstrap-common.sh referencing the recovery procedure
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract bear-mode default values (0, 3e17, 100, 3e17) into file-level
constants in IOptimizer.sol so both Optimizer._bearDefaults() and
LiquidityManager.recenter()'s catch block reference a single source of
truth instead of independent hardcoded literals.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The @return annotations were orphaned after _buildInputs() was inserted
between the NatSpec block and getLiquidityParams(). Move them to directly
precede getLiquidityParams() where they belong.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The pure override in OptimizerInputCapture could not write to storage,
and getLiquidityParams calls calculateParams via staticcall which
prevents both storage writes and event emissions.
Fix: extract the input-building normalization from getLiquidityParams
into _buildInputs() (internal view, behavior-preserving refactor).
The test harness now exposes _buildInputs() via getComputedInputs(),
allowing tests to assert actual normalized slot values.
Updated tests for pricePosition, timeSinceRecenter, volatility,
momentum, and utilizationRate to assert non-zero captured values.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Apply PRIVATE_KEY env-var fallback to UpgradeOptimizer.sol (missed in first pass)
- Add comment on zero-sentinel silent-fallback behaviour in all four scripts
- Remove spurious view modifier from BaseDeploy.run() (violated by vm.readFile)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Check PRIVATE_KEY env var first in BootstrapVWAPPhase2.s.sol, DeployBase.sol,
and BaseDeploy.sol; fall back to .secret seed-phrase file when unset.
This allows CI/CD environments to inject keys via environment variables
while preserving the existing local .secret workflow unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
Replace the two per-slot require checks with a loop over all 8 input slots
so future subclasses using slots 2-7 are protected from silent uint256 wrap.
Add testCalculateParamsRevertsOnNegativeMantissaSlots2to7 to verify the guard.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous guard blocked setFeeDestination when feeDestination.code.length > 0
but did not persist feeDestinationLocked — a revert undoes all state changes. An
attacker could CREATE2-deploy bytecode to the EOA fee destination, triggering the
block, then SELFDESTRUCT to clear the code, then call setFeeDestination again
successfully (lock was never committed).
Fix: detect bytecode at the current feeDestination first; if found, set
feeDestinationLocked = true and RETURN (not revert) so the storage write is
committed. A subsequent SELFDESTRUCT cannot undo a committed storage slot.
Updated NatSpec documents both the protection and the remaining limitation
(atomic CREATE2+SELFDESTRUCT in a single tx cannot be detected).
Added testSetFeeDestination_CREATE2BytecodeDetection_Locks covering:
set EOA → vm.etch (simulate CREATE2 deploy) → verify lock committed → vm.etch
empty (simulate selfdestruct) → verify setter still blocked.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Make V3_FACTORY injectable via vm.envOr("V3_FACTORY", DEFAULT_V3_FACTORY),
preserving the Base mainnet address as the default for existing fork runs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add fetch-datasets.sh wrapper that fetches HIGHER/WETH, DEGEN/WETH,
and TOSHI/WETH 30-day event caches via fetch-events.ts; reads
INFURA_API_KEY from env and fails with a helpful error if unset
- Update .gitignore from cache/ (whole dir) to cache/*.jsonl so the
pattern is precise to the generated data files; cache/ is already
covered by the repo-root .gitignore via its own cache/ rule
JSONL cache files are gitignored and must be generated locally by
running ./fetch-datasets.sh with INFURA_API_KEY set.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- M-2: update body to show current deployer-only setFeeDestination()
implementation and conditional locking; mark as partially resolved;
downgrade severity from Medium to Low; update conclusion entry
- I-1: mark as resolved — Recentered event declared at line 66 and
emitted at line 224 of LiquidityManager.sol
- I-2: correct VWAP direction (records on sells/ETH outflow, not buys);
update stale line reference from 146-158 to 177-191
- deployment.md §6.5: replace vague 'assess severity' step 1 with
concrete action (upgrade optimizer to bear defaults via §6.2)
- deployment.md §8 timeline: remove stale 'Set recenter access' row;
update 'First recenter' dependency
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix class-level NatSpec: use accurate wording (width computed from
anchorWidth param provided by Optimizer) instead of imprecise
LiquidityManager attribution
- Fix inline comment in _setAnchorPosition (same stale 1-100% claim)
- Update PRODUCT-TRUTH.md and ARCHITECTURE.md which had the same
incorrect 1-100% range claim
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the misleading "(1-100% width)" range claim from the ANCHOR NatSpec.
Anchor width enforcement lives in LiquidityManager, not this abstract, so
the comment is replaced with a note pointing to where enforcement actually occurs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Address review feedback:
- Add comment on FEE_DEST explaining why it differs from DeployBaseMainnet.sol:
on a Base mainnet fork, 0xf6a3...D9011 has contract code which triggers
feeDestinationLocked=true; keccak-derived address is a guaranteed EOA
- Expand bytecodes.txt EOF guard with a pipeline-mismatch warning log
- Fix STATE.md entry to use imperative verb form per project convention
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace Base Sepolia addresses with Base mainnet:
- V3_FACTORY: 0x33128a8fC17869897dcE68Ed026d694621f6FDfD
- SWAP_ROUTER: 0x2626664c2603336E57B271c5C0b26F421741e481
- NPM_ADDR: 0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1
- Fix FEE_DEST to keccak-derived EOA (0x8A9145E1...9383) to avoid
feeDestination lock triggered by contract code at the old address
- Add vm.warp(block.timestamp + 600) alongside vm.roll in bootstrap
retry loop so _isPriceStable() TWAP check accumulates 300s+ history
- Guard vm.readLine(bytecodesFile) with empty-string break to prevent
vm.parseBytes("") revert on trailing EOF line
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the MAX_ANCHOR_WIDTH=100 constant and the corresponding clamp on
anchorWidth in LiquidityManager.recenter(). The optimizer is now free to
choose any anchor width; evolution run 7 immediately exploited AW=153.
Update IOptimizer.sol NatSpec to reflect no clamping. Update the
testAnchorWidthAbove100IsClamped test to testAnchorWidthAbove100IsNotClamped,
asserting the tick range matches the full AW=150 width.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- docs/mainnet-bootstrap.md: fix Step 4c to use SwapRouter02 7-field
struct (no deadline field); the 8-field ABI was for SwapRouter v1 but
the address is SwapRouter02
- docs/mainnet-bootstrap.md: correct Step 1 to no longer falsely claim
that pre-bootstrap transactions succeed when Forge aborts on simulation
failure; Step 1 now reflects the try/catch behaviour added below
- docs/mainnet-bootstrap.md: Step 6 drops --private-key flag (Foundry
ignores it when vm.startBroadcast(privateKey) is called internally)
and documents that the .secret seed-phrase file must be present
- docs/mainnet-bootstrap.md: remove no-op `export LM_ADDRESS="$LM_ADDRESS"`
- docs/mainnet-bootstrap.md: cite exact line range (101-145) in
Troubleshooting workaround instead of informal marker description
- onchain/script/DeployBase.sol: wrap liquidityManager.recenter() and
seed buy in try/catch so a fresh-pool TWAP revert skips the inline
bootstrap with a warning rather than aborting the entire simulation
- onchain/script/DeployBase.sol: fix --fork-url to --rpc-url in the
post-deploy console.log hint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add docs/mainnet-bootstrap.md with the full two-phase bootstrap
sequence: pool init, 300 s TWAP warm-up wait, first recenter + seed
buy (exact cast commands), 60 s cooldown wait, second recenter via
BootstrapVWAPPhase2.s.sol, and verification/troubleshooting steps.
Update the inline bootstrap comment in DeployBase.sol to warn that the
attempt always reverts on a fresh pool and direct operators to the new
runbook.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Single-cycle attack extracts 21.3 ETH (2.13%) from 1000 ETH LM:
buy 31.9 ETH → recenter → sell all KRK
Key finding: thin pre-recenter positions allow massive price impact,
recenter rebuilds deep positions at manipulated price, sell through
deep positions recovers most ETH. IL crystallized during recenter.
This is the optimal single-buy amount — 31.95+ hits max tick,
<31 ETH extracts proportionally less.
- 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>
Add inline Basescan URL comment identifying V3_FACTORY as the Uniswap V3
Factory on Base mainnet, consistent with the existing comment style used
for NPM_ADDR in both files.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move the orphaned NatSpec block (originally for calculateParams) from
above getLiquidityParams to directly precede calculateParams, and give
getLiquidityParams only its own @inheritdoc block.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>