# KRAIKEN Floor & Optimizer Research Report **Period:** 2026-02-04 to 2026-02-12 **Branch:** `fix/floor-ratchet` (primary), `feat/optimizer-v2-validation` **Authors:** Johann (design, direction) + Clawy (implementation, analysis) --- ## Table of Contents 1. [Executive Summary](#1-executive-summary) 2. [Critical Bugs Found & Fixed](#2-critical-bugs-found--fixed) 3. [Floor Drain Vulnerability](#3-floor-drain-vulnerability) 4. [VWAP Mirror Defense](#4-vwap-mirror-defense) 5. [Parameter Space Safety Map](#5-parameter-space-safety-map) 6. [Fee Revenue Analysis](#6-fee-revenue-analysis) 7. [LP Competition Analysis](#7-lp-competition-analysis) 8. [Optimizer Evolution (V1 → V2 → V3)](#8-optimizer-evolution-v1--v2--v3) 9. [Staking Dynamics & Triangle Cycle](#9-staking-dynamics--triangle-cycle) 10. [Remaining Work](#10-remaining-work) 11. [Key Commits](#11-key-commits) 12. [Appendix: Fuzzing Infrastructure](#appendix-fuzzing-infrastructure) --- ## 1. Executive Summary ### What we discovered The KRAIKEN LiquidityManager had two critical math bugs and a fundamental floor placement vulnerability that allowed attackers to extract 100+ ETH per attack cycle. We fixed the bugs, designed a VWAP mirror defense, mapped the entire parameter safety space, and designed a new optimizer (V3) that uses Harberger staking signals to switch between safe and aggressive configurations. ### Key outcomes - **Two critical math bugs fixed** (sqrt inflation + missing supply accounting) - **Floor drain vulnerability understood and mitigated** via VWAP mirror + parameter-space defense - **Complete 2D safety frontier mapped**: only extreme configs (AS≤35%/AW=100 or AS≥50%/AW=20) are safe without HWM - **Fee revenue fully characterized**: CI has zero effect; only AS and AW matter - **Realistic LP competition modeled**: LM retains 55-65% of WETH fees with 200 ETH competing LP - **OptimizerV3 designed**: three-zone piecewise function from Harberger staking data → binary step between safe bear/bull configs - **Bear config optimized**: AS=30% AW=100 earns 48% more WETH fees than AS=10% while remaining safe ### Design philosophy (Johann) - Transaction fees are the product — locked ETH has no value - No magic numbers — CI should be the risk lever - Fix root causes, not symptoms - Parameter space provides safety; no position ratchets needed --- ## 2. Critical Bugs Found & Fixed ### Bug 1: sqrt inflation in `_setFloorPosition` (ThreePositionStrategy.sol) **Impact:** Made EthScarcity **permanently true**, forcing floor to always use scarcity path regardless of actual ETH balance. **Root cause:** The scarcity calculation used `FullMath.mulDiv(sqrtVwapX96, sqrtVwapX96, FixedPoint96.Q96)` which squares `sqrtVwapX96`, inflating `requiredEthForBuyback` by ~445×. **Fix:** Use `vwapX96` directly (already the squared value): `requiredEthForBuyback = FullMath.mulDiv(outstandingSupply, vwapX96, FixedPoint96.Q96)` ### Bug 2: `outstandingSupply()` missing pool balance **Impact:** `outstandingSupply()` only subtracted LM's own balance from total supply, but not KRK held by `feeDestination` and `stakingPool`. This inflated the supply figure, making scarcity trigger even more easily. **Fix:** Subtract `feeDestination` and `stakingPool` KRK balances from outstanding supply. **Combined commit:** `0e2104b`. 130 tests pass after fix. **Effect:** After fixing both bugs, CI works correctly as a solvency lever: - CI=0% → safest (floor furthest from current) - CI=100% → riskiest (floor closest) - Previously, CI had no effect because scarcity was permanently true. --- ## 3. Floor Drain Vulnerability ### Attack pattern 1. Attacker buys ~950 ETH of KRK (building position) 2. Executes ~2000 trades with 90% sells (buybias=10) 3. Each sell triggers a recenter that rebuilds the floor position 4. Floor walks progressively closer to current price via ~140 recenters 5. Final dump sweeps all floor + anchor positions → net profit +130-170 ETH ### Root cause: `floor = f(current_tick)` The floor position's absolute tick is always computed from the current tick: ``` floorTick = currentTick + distance(VWAP, CI, ...) ``` During sell-heavy trading, `currentTick` drops through recenters. Even if the VWAP distance grows, the absolute floor position still walks down because `current` drops faster than `distance` grows. ### What we tried (in order) | Approach | Result | Why it failed/worked | |----------|--------|---------------------| | Directional VWAP | Reduced profits 20× | Prevents VWAP dilution but doesn't prevent floor walking | | VWAP bootstrap | No improvement | 8/10 still profitable | | Anti-overlap clamp removal | Regression | Clamp is a correctness constraint (floor must hold WETH) | | Floor ratchet on `isUp` | Works but rejected | "Bandage, not elegant" — Johann | | VWAP mirror | Partial | Floor walks less but still walks | | Mirror + wide floor | No improvement | Width irrelevant when attacker sweeps everything | | Mirror + peak VWAP distance | CI=0% safe, CI>0% leaks | Distance is relative, not absolute | | Mirror + floor high-water mark | **135/135 safe** | Absolute position ratchet | | **Parameter space defense** | **Works without HWM** | Safe configs prevent floor walking by geometry | ### Why HWM was removed Johann's argument: HWM creates a one-way valve. If the optimizer changes CI during operations, HWM prevents the floor from moving back. This defeats the purpose of having CI as a risk lever. Safety should come from the parameter space, not from a position ratchet. --- ## 4. VWAP Mirror Defense ### Current floor formula (no HWM, no Scarcity/Abundance branching) ``` floorTick = max(scarcityTick, mirrorTick, clampTick) toward KRK-cheap side ``` **Three signals:** - **scarcityTick**: from `vwapX96` and ETH/supply ratio. Correct when ETH is scarce. - **mirrorTick**: `currentTick + |adjustedVwapTick - currentTick|` on KRK-cheap side. Reflects VWAP distance symmetrically. Uses `getAdjustedVWAP(CI)`. - **clampTick**: minimum distance from anchor edge. `anchorSpacing = 200 + (34 × 20 × AW / 100)` ticks. **Key properties:** - CI controls mirror distance through `getAdjustedVWAP(CI)` — no magic numbers - Selling naturally grows the mirror distance (current moves away from VWAP) - AW=100 → clamp = 7000 ticks minimum distance (provides safety floor) - Unified formula — no EthScarcity/EthAbundance branching ### VWAP recording (directional) - `LiquidityManager._scrapePositions(bool recordVWAP)` — only records on ETH inflow - `shouldRecordVWAP` flag: compares `lastRecenterTick` to current tick to detect direction - Records anchor midpoint price weighted by WETH fees - Bootstrap: always records when `cumulativeVolume == 0` - Commit `c97714c` (directional) + `ba018c4` (bootstrap) --- ## 5. Parameter Space Safety Map ### 2D Safe Frontier (AS × AW) Tested 29 (AS, AW) combinations adversarially (buybias=10, 2000 trades, CI=0%). Full results in `analysis/2D_FRONTIER_LOG.md` and `analysis/2d-frontier-results.csv`. ``` AW=100: AS ≤ 35% safe ✅ (clamp = 7000 ticks) AW= 90: AS ≤ 30% safe ✅ (clamp = 6320 ticks) AW= 80: AS ≤ 30% safe ✅ (clamp = 5640 ticks) AW= 60: ALL AS broken ❌ (clamp = 4280 ticks — too close) AW= 40: ALL AS broken ❌ (clamp = 2920 ticks) AW= 30: AS ≥ 80% safe ✅ (floor not under pressure with thin anchor) AW= 20: AS ≥ 50% safe ✅ (clamp = 1560 ticks but anchor is thin) ``` **The kill zone is AW 40-80 at most AS levels.** Only extreme configurations survive adversarial attack. ### Why only extremes work | Config | Why safe | |--------|----------| | AS low + AW high (bear) | Wide clamp (7000 ticks) prevents floor from reaching current. Thin anchor means less ETH available for attacker to trade against. | | AS high + AW low (bull) | Thin, concentrated anchor near current price. Floor far away with minimal ETH. Attacker can't drain deep anchor efficiently. | | AS mid + AW mid | Moderate anchor depth AND moderate clamp distance. Attacker can trade against enough ETH through the anchor while floor is close enough to drain. | ### Other parameter effects - **CI (capitalInefficiency)**: Zero effect on fee revenue. Pure risk lever for floor placement. CI=0% safest. - **DD (discoveryDepth)**: Zero effect on floor safety. Pure fee lever for discovery position liquidity. Tested 20/20 safe across DD 0 to 1e18 at AS=10% AW=100. - **Staking level**: r≈0 correlation with attack profitability. --- ## 6. Fee Revenue Analysis ### Core finding: CI has ZERO effect on fees Tested across CI=0%, 25%, 50%, 75%, 100% with identical results. CI only affects floor placement, which doesn't generate fees. ### AS × AW fee matrix (without background LP) | Config | WETH fees | KRK fees | Notes | |--------|-----------|----------|-------| | AS=10% AW=100 | 48 W | 1.3M K | Best WETH per ETH | | AS=30% AW=100 | 72 W | 1.7M K | **Optimal bear** | | AS=35% AW=100 | 72 W | 1.7M K | Edge of safe zone | | AS=100% AW=20 | 19 W | 9.2M K | Best KRK fees | **Two opposing forces:** - WETH fees ↑ with wider AW (more trades complete through range) - KRK fees ↑ with higher AS + narrower AW (concentrated liquidity captures more per-tick) ### Volatility effect High volatility → 5-8× more fees (more trading activity). Fee revenue is primarily driven by volume, not parameters. --- ## 7. LP Competition Analysis ### Background LP model `BackgroundLP.sol`: 5 stacked Gaussian positions at ±10/20/40/80/160 tick spacings centered on current tick. 200 ETH total (40 ETH/layer). Buys KRK from pool realistically. Rebalances every 10th recenter. Commit `e008b42` + `381b1cc`. ### Fee retention with competition | Config | WETH (no BG LP) | WETH (200 ETH BG LP) | Retained | |--------|-----------------|---------------------|----------| | AS=30% AW=100 | 72 W | 43 W | 60% | | AS=35% AW=100 | 72 W | 38 W | 52% | | AS=10% AW=100 | 48 W | 28 W | 58% | | AS=50% AW=150 | 75 W | 37 W | 49% | | AS=50% AW=200 | 68 W | 31 W | 41% | | AS=100% AW=20 | 19 W | 13 W | 66% | **Wider AW = more fee leakage.** The LM's liquidity is spread thinner → concentrated competitor captures disproportionate share. ### Real LP distribution data (Uniswap V3 mainnet, Feb 2026) Scanned 4 small-cap pools on mainnet: | Pool | #Ticks | ±10sp | ±50sp | Avg dist | Character | |------|--------|-------|-------|----------|-----------| | AZTEC/WETH 1% | 9 | 50% | 100% | 15 sp | Very concentrated | | wTAO/USDC 1% | 72 | 54% | 85% | 21 sp | Moderate, long tail | | wTAO/WETH 0.3% | 109 | 5% | 50% | 80 sp | Very spread | | LQTY/WETH 0.3% | 41 | 0% | 24% | 234 sp | Extremely spread | **Key insight:** 1% fee pools (same as KRAIKEN) have more concentrated LPs (avg ~18 spacings). Our Gaussian model at avg ~62 spacings is less concentrated than typical real competition for 1% fee pools. This means our fee estimates are **slightly optimistic** — real competing LPs would capture somewhat more fees. **Caveat:** KRAIKEN is unique. The LM owns the entire initial liquidity and rebalances every recenter. Competing LPs face a protocol-owned position that actively tracks price, which is very different from normal pools. --- ## 8. Optimizer Evolution (V1 → V2 → V3) ### V1 (Original) - Sentiment-based: bear → high AS, bull → low AS - CI, DD driven by sentiment - **Inverted**: high AS in bear = deep anchor = more ETH for attacker ### V2 (`src/OptimizerV2.sol`, commit `c13d4ca`) - CI = 0 always (no fee impact, pure safety) - AS: 100% (bull) → 10% (bear) — **corrected inversion** - AW: 20 (bull) → 100 (bear) - DD: proportional to sentiment - UUPS upgradeable - 131 tests pass ### V3 (`src/OptimizerV3.sol`, commit `44b8510` + `94446e7`) Uses on-chain Harberger staking data to determine market phase: **Inputs:** `percentageStaked` (0-100%), `averageTaxRate` (maps to index 0-29) **Tax rate array:** `[1, 3, 5, 8, 12, 18, 24, 30, 40, 50, 60, 80, 100, 130, 180, 250, 320, 420, 540, 700, 920, 1200, 1600, 2000, 2600, 3400, 4400, 5700, 7500, 9700]` **Three zones → score 0-200:** | Zone | Condition | Formula | Meaning | |------|-----------|---------|---------| | A | staked ≤ 39% | `100 - stakedPct² × effIdx / 3600` | Early filling, score ~100 | | B | 40-91% staked | `max(0, 120 - 8 × effIdx)` | Main operating range | | C | >91% staked | `max(0, 200 - deltaS³ × effIdx / 20)` | Euphoria zone, cubic snap | Where `effIdx = min(29, taxIdx + (taxIdx >= 14 ? 1 : 0))`, `deltaS = 100 - stakedPct` **Score → parameters (step function):** - Score ≤ 140 → **BEAR**: AS=30%, AW=100, CI=0 - Score ≥ 160 → **BULL**: AS=100%, AW=20, CI=0 - 140-160: narrow linear ramp **Parameter space distribution:** 94.3% bear, 0.5% transition, 5.2% bull. **Why step function:** Linear gradient through the score range would pass through vulnerable middle AW values (40-80). Step function snaps between proven-safe extremes. ### Pending: Direct 2D mapping (remove score intermediate) Johann directed removing the score variable. Score collapses useful 2D (staking%, avgTax) information into lossy 1D. **Direct rule:** `staked ≤ 91% → BEAR always`. `staked > 91% → BULL if deltaS³ × effIdx / 20 < 50, else BEAR`. At specific thresholds: - 97%+ staked: any tax → bull - 95% staked: tax ≤ 30% (idx ≤ 7) → bull - 92% staked: tax ≤ 3% (idx ≤ 1) → bull - <92%: always bear --- ## 9. Staking Dynamics & Triangle Cycle ### Harberger tax mechanics - Tax rate = **self-assessed defense price** (not conviction) - Snatching a position requires strictly higher tax rate - MAX_STAKE = 20% of KRK supply - TAX_FLOOR_DURATION = 3 days - High tax erodes position exponentially — cost of defense ### Triangle cycle model The staking system naturally traces a triangle in (staking%, avgTax) space: ``` 100% staked / \ / BULL \ fill up / (euphoria) \ collapse (bottom) / \ (hypotenuse) / BEAR \ /________________________\ 0% staked 0% staked low tax high tax ``` **Phase 1 — Bottom edge (fill up):** Staking grows 0→100%, tax starts low. Optimizer stays bear. Transitions to bull at ~95% staked. **Phase 2 — Right edge (snatching wars):** 100% staked, snatching wars push average tax rate up. `deltaS = 0` → score = 200 (always bull). Euphoria overwhelms tax cost. **Phase 3 — Hypotenuse (collapse):** Nervous exits. High tax + declining staking → cubic term in Zone C snaps score to 0 (bear) within 4-6% staking drop. Fast transition because `deltaS³` is cubic. **Game theory validates this trajectory** — no agent simulation needed (Johann's direction). Incentive structures force participants through this cycle. --- ## 10. Remaining Work ### Immediate - [ ] **Refactor OptimizerV3**: Remove score intermediate. Direct 2D (staking%, avgTax) → binary config. - [ ] **Fix run-v3-step-test.sh**: Parameter passing bug causes false positives (script bug, not V3 bug). - [ ] **Complete bear AS sweep**: Test AS=40-100% at AW=100 to confirm frontier. Fix PnL parsing. - [ ] **Re-validate after V3 refactor**: Run adversarial suite against refactored V3. ### Deferred - [ ] Update PR: `https://codeberg.org/johba/harb/compare/master...fix/floor-ratchet` - [ ] Bull→bear transition testing: What happens when optimizer switches mid-cycle? - [ ] Test V3 with real staking scenarios end-to-end - [ ] Consider if bear AS can ramp with staking confidence (AS=30% base → higher as staking grows) ### Won't do - ~~Agent-based staking simulation~~ — game theory argument sufficient - ~~HWM / position ratchet~~ — removed by design decision - ~~New fuzzing scripts~~ — extend existing infrastructure only --- ## 11. Key Commits | Commit | Description | Branch | |--------|-------------|--------| | `0e2104b` | sqrt + outstandingSupply bugfix | fix/floor-ratchet | | `c97714c` | Directional VWAP recording | fix/floor-ratchet | | `ba018c4` | VWAP bootstrap fix | fix/floor-ratchet | | `59b30a6` | Initial VWAP mirror | fix/floor-ratchet | | `49d15f0` | Revert to Version A (CI controls mirror) | fix/floor-ratchet | | `bf92977` | Floor HWM (later removed) | fix/floor-ratchet | | `c7ea6d0` | Cleanup (diagnostics, stale CSVs) | fix/floor-ratchet | | `e008b42` | Gaussian background LP | feat/optimizer-v2-validation | | `381b1cc` | Realistic BG LP funding | feat/optimizer-v2-validation | | `c13d4ca` | OptimizerV2 + HWM removal | feat/optimizer-v2-validation | | `7b83dd4` | BG LP rebalance fix (every 10th) | feat/optimizer-v2-validation | | `44b8510` | OptimizerV3: three-zone piecewise | feat/optimizer-v2-validation | | `94446e7` | OptimizerV3: step function | feat/optimizer-v2-validation | --- ## Appendix: Fuzzing Infrastructure ### Scripts | Script | Purpose | |--------|---------| | `analysis/run-fuzzing.sh` | Single-optimizer fuzzing, CSV per run | | `analysis/run-adversarial.sh` | Attack specific configs with varied strategies | | `analysis/run-v3-adversarial.sh` | Attack V3 with staking scenarios | | `analysis/run-v3-step-test.sh` | Test step function across parameter space (HAS BUG) | | `analysis/run-deep-search.sh` | Deep search across 4D parameter space | | `analysis/run-bglp-fee-test.sh` | Fee revenue with background LP competition | | `analysis/scan-final.py` | On-chain LP distribution scanner | | `analysis/clean-csvs.sh` | Clean generated CSV files | ### Solidity | Contract | Purpose | |----------|---------| | `StreamlinedFuzzing.s.sol` | Main fuzzing script. ConfigurableOptimizer, staking, BG LP, uncapped swaps. | | `ParameterSweepFuzzing.s.sol` | Multi-combo sweep in single execution | | `BullBearSweep.s.sol` | Deterministic bull→bear scenario | | `helpers/FuzzingBase.sol` | Shared infrastructure | | `helpers/BackgroundLP.sol` | Gaussian competing LP (5 layers, rebalances on recenters) | | `helpers/ConfigurableOptimizer.sol` | Test optimizer with env-var-driven params | ### Environment variables | Var | Default | Description | |-----|---------|-------------| | `CI_VALUE` | 0 | Capital inefficiency (0-1e18) | | `AS_VALUE` | 1e17 | Anchor share (0-1e18) | | `AW_VALUE` | 20 | Anchor width (0-200+) | | `DD_VALUE` | 5e17 | Discovery depth (0-1e18) | | `BUY_BIAS` | 50 | % of trades that are buys (0-100) | | `TRADES_PER_RUN` | 15 | Trades per run | | `FUZZING_RUNS` | 1 | Runs per invocation | | `BATCH_SEED` | 0 | Random seed | | `OPTIMIZER_CLASS` | BullMarketOptimizer | Which optimizer to use | | `UNCAPPED_SWAPS` | false | Use uncapped swap amounts | | `BG_LP_ETH_PER_LAYER` | 0 | ETH per BG LP layer (0 = disabled) | | `STAKING_LEVEL` | 0 | Staking % (0-100) | | `STAKING_TAX_RATE` | 3 | Tax rate index (0-29) | ### Constraints - **1 run per forge invocation**: EVM MemoryOOG after ~2 runs of 2000 trades. Loop in shell. - **VPS: 8GB RAM, no swap**: Cargo tests OOM. Use `CARGO_BUILD_JOBS=1`. - **Disk**: ~50% used. Run `clean-csvs.sh` periodically. - **Forge PATH**: `~/.foundry/bin/forge` (not in default PATH). ### Data files | File | Description | |------|-------------| | `analysis/2D_FRONTIER_LOG.md` | 29-combo (AS, AW) safety frontier | | `analysis/2d-frontier-results.csv` | Machine-readable frontier data | | `analysis/V3_FUZZING_LOG.md` | V3 adversarial test results | | `analysis/V3_STEP_LOG.md` | Step function test results | | `analysis/FUZZING_LOG.md` | General fuzzing log | | `analysis/AS_SWEEP_LOG.md` | AS sweep results | | `analysis/PARAMETER_SEARCH_RESULTS.md` | 4D parameter search |