# KRAIKEN Mainnet Deployment Runbook

**Target chain:** Base (L2)
**Contract version:** V2 (OptimizerV3 w/ directional VWAP)

---

## 1. Pre-Deployment Checklist

- [ ] All tests pass: `cd onchain && forge test`
- [ ] Gas snapshot baseline: `forge snapshot`
- [ ] Security review complete (see `analysis/SECURITY_REVIEW.md`)
- [ ] Storage layout verified for UUPS upgrade (see `analysis/STORAGE_LAYOUT.md`)
- [ ] Floor ratchet mitigation status confirmed (branch `fix/floor-ratchet`)
- [ ] Multisig wallet ready for `feeDestination` (Gnosis Safe on Base)
- [ ] Deployer wallet funded with sufficient ETH for gas (~0.05 ETH)
- [ ] LiquidityManager funding wallet ready (initial ETH seed for pool positions)
- [ ] `.secret` seed phrase file present in `onchain/` (deployer account)
- [ ] Base RPC endpoint configured and tested
- [ ] Etherscan/Basescan API key ready for contract verification
- [ ] kraiken-lib version updated: `COMPATIBLE_CONTRACT_VERSIONS` includes `2`

---

## 2. Contract Deployment Order

All contracts are deployed in a single broadcast transaction via `DeployBaseMainnet.sol`:

```
1. Kraiken token (ERC20 + ERC20Permit)
2. Stake contract (Kraiken address, feeDestination)
3. Kraiken.setStakingPool(Stake)
4. Uniswap V3 Pool (create or use existing, FEE=10000)
5. Pool initialization (1 cent starting price)
6. OptimizerV3 implementation + ERC1967Proxy
7. LiquidityManager (factory, WETH, Kraiken, OptimizerProxy)
8. LiquidityManager.setFeeDestination(multisig)
9. Kraiken.setLiquidityManager(LiquidityManager)
```

### Deploy Command

```bash
cd onchain

# Verify configuration first
cat script/DeployBaseMainnet.sol  # Check feeDest, weth, v3Factory

# Dry run (no broadcast)
forge script script/DeployBaseMainnet.sol \
  --rpc-url $BASE_RPC \
  --sender $(cast wallet address --mnemonic "$(cat .secret)")

# Live deployment
forge script script/DeployBaseMainnet.sol \
  --rpc-url $BASE_RPC \
  --broadcast \
  --verify \
  --etherscan-api-key $BASESCAN_API_KEY \
  --slow
```

**Critical:** The `--slow` flag submits transactions one at a time, waiting for confirmation. This prevents nonce issues on Base.

### Record Deployment Addresses

After deployment, save all addresses from console output:
```bash
# Update deployments file
cat >> deployments-mainnet.json << 'EOF'
{
  "chain": "base",
  "chainId": 8453,
  "kraiken": "0x...",
  "stake": "0x...",
  "pool": "0x...",
  "liquidityManager": "0x...",
  "optimizerProxy": "0x...",
  "optimizerImpl": "0x...",
  "feeDestination": "0x...",
  "deployer": "0x...",
  "deployedAt": "2026-XX-XX",
  "txHash": "0x..."
}
EOF
```

---

## 3. Post-Deployment Setup

### 3.1 Fund LiquidityManager

The LM needs ETH to create initial positions:

```bash
# Send ETH to LiquidityManager (unwrapped — it will wrap to WETH internally)
cast send $LIQUIDITY_MANAGER --value 10ether \
  --rpc-url $BASE_RPC \
  --mnemonic "$(cat .secret)"
```

### 3.2 Trigger First Recenter

`recenter()` is permissionless — any address may call it. The 60-second cooldown (`MIN_RECENTER_INTERVAL`) and TWAP oracle check are always enforced.

```bash
# Wait for pool to accumulate some TWAP history (~5 minutes of trades)
# Anyone can trigger the first recenter; txnBot will take over ongoing calls
cast send $LIQUIDITY_MANAGER "recenter()" \
  --rpc-url $BASE_RPC \
  --from $TXNBOT_ADDRESS
```

### 3.4 Configure txnBot

Update `services/txnBot/` configuration for Base mainnet:
- Set `LIQUIDITY_MANAGER` address
- Set `KRAIKEN` address
- Set RPC to Base mainnet
- Deploy txnBot service

### 3.5 Configure Ponder Indexer

```bash
# Update kraiken-lib/src/version.ts
export const COMPATIBLE_CONTRACT_VERSIONS = [2];

# Update Ponder config for Base mainnet addresses
# Set PONDER_NETWORK=BASE in environment
```

### 3.6 Update Frontend

- Update contract addresses in web-app configuration
- Update kraiken-lib ABIs: `cd onchain && forge build` then rebuild kraiken-lib
- Deploy frontend to production

---

## 4. Optimizer Upgrade Procedure

If upgrading an existing Optimizer proxy to OptimizerV3:

```bash
cd onchain

# Set proxy address
export OPTIMIZER_PROXY=0x...

# Dry run
forge script script/UpgradeOptimizer.sol \
  --rpc-url $BASE_RPC

# Execute upgrade
forge script script/UpgradeOptimizer.sol \
  --rpc-url $BASE_RPC \
  --broadcast \
  --verify \
  --etherscan-api-key $BASESCAN_API_KEY

# Verify post-upgrade
cast call $OPTIMIZER_PROXY "getLiquidityParams()" --rpc-url $BASE_RPC
```

**Expected output:** Bear-mode defaults (CI=0, AS=0.3e18, AW=100, DD=0.3e18) since staking will be <91%.

---

## 5. Verification Steps

Run these checks after deployment to confirm everything is wired correctly:

```bash
# 1. Kraiken token
cast call $KRAIKEN "VERSION()" --rpc-url $BASE_RPC          # Should return 2
cast call $KRAIKEN "peripheryContracts()" --rpc-url $BASE_RPC  # LM + Stake addresses

# 2. LiquidityManager
cast call $LM "feeDestination()" --rpc-url $BASE_RPC       # Should be multisig
cast call $LM "lastRecenterTime()" --rpc-url $BASE_RPC     # Should be non-zero after first recenter
cast call $LM "positions(0)" --rpc-url $BASE_RPC           # Floor position (after recenter)
cast call $LM "positions(1)" --rpc-url $BASE_RPC           # Anchor position
cast call $LM "positions(2)" --rpc-url $BASE_RPC           # Discovery position

# 3. OptimizerV3 (through proxy)
cast call $OPTIMIZER "getLiquidityParams()" --rpc-url $BASE_RPC

# 4. Pool state
cast call $POOL "slot0()" --rpc-url $BASE_RPC              # Current tick, price
cast call $POOL "liquidity()" --rpc-url $BASE_RPC          # Total liquidity

# 5. Stake contract
cast call $STAKE "nextPositionId()" --rpc-url $BASE_RPC    # Should be 0 initially

# 6. ETH balance
cast balance $LM --rpc-url $BASE_RPC                       # Should show funded amount
```

---

## 6. Emergency Procedures

### 6.1 Pause Recentering

**NOTE:** `recenter()` is permissionless — there is no access-control switch to block it. The only mechanism that prevents a recenter is the 60-second `MIN_RECENTER_INTERVAL` cooldown and the TWAP oracle check. There is no admin function to revoke or grant access.

In an attack scenario the most effective response is to upgrade or replace the contract (see §6.3 / §6.4). Existing positions remain in place and continue earning fees regardless of recenter activity.

### 6.2 Upgrade Optimizer to Safe Defaults

Deploy a minimal "safe" optimizer that always returns bear parameters:

```bash
# Deploy SafeOptimizer with hardcoded bear params
# Upgrade proxy to SafeOptimizer
OPTIMIZER_PROXY=$OPTIMIZER forge script script/UpgradeOptimizer.sol \
  --rpc-url $BASE_RPC --broadcast
```

### 6.3 Emergency Parameter Override

If the optimizer needs temporary override, deploy a new implementation with hardcoded safe parameters:
- CI=0, AS=30% (0.3e18), AW=100, DD=0.3e18 (bear defaults)
- These were verified safe across all 1050 parameter sweep combinations

### 6.4 Rollback Plan

**There is no rollback for deployed contracts.** Mitigation options:
- Upgrade optimizer proxy to revert to V1/V2 logic
- Revoke recenter access to freeze positions
- The LiquidityManager itself is NOT upgradeable (by design — immutable control)
- In worst case: deploy entirely new contract set, migrate liquidity

### 6.5 Known Attack Response: Floor Ratchet

If floor ratchet extraction is detected (rapid recenters + floor tick creeping toward current price):
1. **Immediately** upgrade the optimizer to safe bear-mode defaults (§6.2) — this maximises floor distance (AW=100 → 7000-tick clearance) and makes ratchet extraction significantly harder while a patched LiquidityManager is prepared. Note: there is no access-control switch on `recenter()`; the 60s cooldown is the only rate limiter
2. Assess floor position state via `positions(0)`
3. Deploy patched LiquidityManager if fix is ready
4. Current mitigation: bear-mode parameters (AW=100) create 7000-tick floor distance, making ratchet extraction significantly harder

---

## 7. Monitoring Setup

### On-Chain Monitoring

Track these metrics via Ponder or direct RPC polling:

| Metric | How | Alert Threshold |
|--------|-----|-----------------|
| Floor tick distance | `positions(0).tickLower - currentTick` | < 2000 ticks |
| Recenter frequency | Count `recenter()` calls per hour | > 10/hour |
| LM ETH balance | `address(LM).balance + WETH.balanceOf(LM)` | < 1 ETH (most ETH is in pool positions) |
| VWAP drift | `getVWAP()` vs current price | > 50% divergence |
| Optimizer mode | `getLiquidityParams()` return values | Unexpected bull in low-staking |
| Fee revenue | WETH transfers to feeDestination | Sudden drop to 0 |

### Off-Chain Monitoring

- txnBot health: `GET /api/txn/status` — should return healthy
- Ponder indexing: `GET /api/graphql` — query `stats` entity
- Frontend version check: `useVersionCheck()` composable validates contract VERSION

### Alerting Triggers

1. **Critical:** Floor position liquidity = 0 (no floor protection)
2. **Critical:** recenter() reverts for > 1 hour
3. **High:** > 20 recenters in 1 hour (potential manipulation)
4. **Medium:** VWAP compression triggered (high cumulative volume)
5. **Low:** Optimizer returns bull mode (verify staking metrics justify it)

---

## 8. Deployment Timeline

| Step | Duration | Dependency |
|------|----------|------------|
| Deploy contracts | ~2 min | Funded deployer wallet |
| Verify on Basescan | ~5 min | Deployment complete |
| Fund LiquidityManager | ~1 min | Deployment complete |
| Wait for TWAP history | ~5-10 min | Pool initialized |
| First recenter | ~1 min | TWAP history accumulated |
| Deploy txnBot | ~5 min | Addresses configured |
| Deploy Ponder | ~10 min | Addresses + kraiken-lib updated |
| Deploy frontend | ~5 min | Ponder running |
| **Total** | **~30-40 min** | |
