Add table-driven Foundry tests for OptimizerV3.calculateParams covering:
- Bear regime at 0%, 1%, 50%, 91% staking (all tax rates)
- Bull/bear boundary at 92% with tax index transitions
- Bull/bear at 95% with penalty=50 exact boundary
- EffIdx shift behavior at 96% (taxIdx 13→14 discontinuity)
- Bull at 97% with max tax, 100% always bull
- Edge cases: all-zero inputs, zero tax at high staking
- Mantissa overflow guard
- Unused slots ignored
- Fuzz: no reverts, output always exactly bear or bull
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The production feeDest has contract bytecode on Base mainnet, not an EOA.
Fix the contradictory comment flagged in review.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Document the FEE_DEST derivation in DeployBaseMainnet.sol and explain
why FitnessEvaluator.t.sol intentionally uses a different address.
The production address (0xf6a3...D9011) is correct — it has contract
bytecode on Base mainnet, so setFeeDestination() locks it permanently.
The test uses a keccak-derived EOA (0x8A91...9383) to avoid the locking
behaviour breaking snapshot/revert cycles in fork tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixes#1055
## Changes
That notification is for the earlier background task which already completed — I retrieved its output and used it to diagnose and fix the failing test. The work is done.
Reviewed-on: https://codeberg.org/johba/harb/pulls/1080
Reviewed-by: Disinto_bot <disinto_bot@noreply.codeberg.org>
- getLiquidityParams() now reverts with "OptimizerV3Push3: not for production use" instead
of silently returning zeroed bear-mode defaults; LiquidityManager.recenter() already has
a try/catch fallback so backtesting is unaffected
- Added @custom:experimental NatSpec annotation to the contract marking it as a transpiler
harness / backtesting stub only
- DeployBase.sol now validates any pre-existing optimizer address by calling getLiquidityParams()
and reverting if it fails, blocking accidental wiring of OptimizerV3Push3 as a live optimizer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract transpiler output into OptimizerV3Push3Lib so both OptimizerV3
and OptimizerV3Push3 delegate to the same canonical copy. Future
transpiler changes now require only one edit.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>