feat: OptimizerV3 with direct 2D staking-to-LP parameter mapping
Core protocol changes for launch readiness: - OptimizerV3: binary bear/bull mapping from (staking%, avgTax) — avoids exploitable AW 30-90 kill zone. Bear: AS=30%, AW=100, CI=0, DD=0.3e18. Bull: AS=100%, AW=20, CI=0, DD=1e18. UUPS upgradeable with __gap[48]. - Directional VWAP: only records prices on ETH inflow (buys), preventing sell-side dilution of price memory - Floor formula: unified max(scarcity, mirror, clamp) — VWAP mirror uses distance from adjusted VWAP as floor distance, no branching - PriceOracle (M-1 fix): correct fallback TWAP divisor (60000s, not 300s) - Access control (M-2 fix): deployer-only guard on one-time setters - Recenter rate limit (M-3 fix): 60-second cooldown for open recenters - Safe fallback params: recenter() optimizer-failure defaults changed from exploitable CI=50%/AW=50 to safe bear-mode CI=0/AW=100 - Recentered event for monitoring and indexing - VERSION bump to 2, kraiken-lib COMPATIBLE_CONTRACT_VERSIONS updated Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
21857ae8ca
commit
85350caf52
38 changed files with 3793 additions and 205 deletions
306
docs/DEPLOYMENT_RUNBOOK.md
Normal file
306
docs/DEPLOYMENT_RUNBOOK.md
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
# 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 Set Recenter Access
|
||||
|
||||
Restrict `recenter()` to the txnBot address:
|
||||
|
||||
```bash
|
||||
# Must be called by feeDestination (multisig)
|
||||
cast send $LIQUIDITY_MANAGER "setRecenterAccess(address)" $TXNBOT_ADDRESS \
|
||||
--rpc-url $BASE_RPC \
|
||||
--mnemonic "$(cat .secret)" # or via multisig
|
||||
```
|
||||
|
||||
### 3.3 Trigger First Recenter
|
||||
|
||||
```bash
|
||||
# Wait for pool to accumulate some TWAP history (~5 minutes of trades)
|
||||
# Then trigger first recenter (must be called by recenterAccess)
|
||||
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 "recenterAccess()" --rpc-url $BASE_RPC # Should be txnBot
|
||||
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
|
||||
|
||||
**WARNING:** `revokeRecenterAccess()` does NOT pause recentering. It makes `recenter()` permissionless (anyone can call it with 60-second cooldown + TWAP check). In an attack scenario, this would make things worse.
|
||||
|
||||
To truly lock out recenters, set `recenterAccess` to a burn address that no one controls:
|
||||
|
||||
```bash
|
||||
# Called by feeDestination (multisig) — sets access to a dead address
|
||||
cast send $LM "setRecenterAccess(address)" 0x000000000000000000000000000000000000dEaD \
|
||||
--rpc-url $BASE_RPC
|
||||
```
|
||||
|
||||
This leaves existing positions in place but prevents any new recenters. LP positions continue earning fees. To resume, call `setRecenterAccess()` with the txnBot address again.
|
||||
|
||||
### 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** set recenter access to burn address (`0xdEaD`) — do NOT use `revokeRecenterAccess()` as it makes recenter permissionless
|
||||
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 |
|
||||
| Set recenter access | ~1 min | feeDestination set (multisig) |
|
||||
| Wait for TWAP history | ~5-10 min | Pool initialized |
|
||||
| First recenter | ~1 min | TWAP history + recenter access |
|
||||
| Deploy txnBot | ~5 min | Addresses configured |
|
||||
| Deploy Ponder | ~10 min | Addresses + kraiken-lib updated |
|
||||
| Deploy frontend | ~5 min | Ponder running |
|
||||
| **Total** | **~30-40 min** | |
|
||||
Loading…
Add table
Add a link
Reference in a new issue