# ARCHITECTURE.md — System Map

Compressed overview for AI agents. Read this first, drill into source for details.

## Contract Architecture

```
Kraiken.sol (ERC-20 token)
├── liquidityManager: address (set once, immutable after)
│   └── LiquidityManager.sol (ThreePositionStrategy)
│       ├── optimizer: Optimizer (private immutable ref)
│       ├── pool: IUniswapV3Pool
│       ├── kraiken: Kraiken
│       └── Positions: Floor, Anchor, Discovery
├── stakingPool: address
│   └── Stake.sol
│       ├── Staking positions with tax rates
│       ├── Snatch mechanics (competitive staking)
│       └── getPercentageStaked(), getAverageTaxRate()
└── feeDestination: address (protocol revenue — both WETH and KRK fees go HERE, not back to holders)

Optimizer.sol (UUPS Upgradeable Proxy)
├── Reads: stake.getPercentageStaked(), stake.getAverageTaxRate()
├── Computes: sentiment → 4 liquidity params
├── Versions: Optimizer, OptimizerV2, OptimizerV3, OptimizerV3Push3
└── Admin: single address, set at initialize()
```

## Key Relationships

- **Kraiken → LiquidityManager**: set once via `setLiquidityManager()`, reverts if already set
- **LiquidityManager → Optimizer**: `private immutable` — baked into constructor, never changes
- **LiquidityManager → Kraiken**: exclusive minting/burning rights
- **Optimizer → Stake**: reads sentiment data (% staked, avg tax rate)
- **Optimizer upgrades**: UUPS proxy, admin-only `_authorizeUpgrade()`
- **feeDestination receives both WETH and KRK fees**: during `recenter()`, Uniswap V3 fee collection produces both tokens. WETH fees AND KRK fees are forwarded to `feeDestination` (see `LiquidityManager._scrapePositions()`).
- **feeDestination is a conditional-lock (not set-once)**: `setFeeDestination()` (deployer-only) allows repeated changes while the destination is an EOA, enabling staged deployment and testing. The moment a contract address is set, `feeDestinationLocked` is set to `true` and no further changes are allowed. A CREATE2 guard also blocks re-assignment if the current destination has since acquired bytecode. This differs from Kraiken's `liquidityManager`/`stakingPool` which are strictly set-once.
- **feeDestination KRK excluded from outstanding supply**: `_getOutstandingSupply()` subtracts `kraiken.balanceOf(feeDestination)` before computing scarcity, because protocol-held KRK cannot be sold into the floor and should not inflate the supply count. This subtraction only occurs when `feeDestination != address(0) && feeDestination != address(this)` (see `LiquidityManager.sol:324`); when feeDestination is unset or is LM itself the balance is not subtracted.
- **Staking pool KRK excluded from outstanding supply**: `_getOutstandingSupply()` also subtracts `kraiken.balanceOf(stakingPoolAddr)`, because staked KRK is locked and similarly cannot be sold into the floor. This subtraction only occurs when `stakingPoolAddr != address(0)` (see `LiquidityManager._getOutstandingSupply()`); when the staking pool is unset the balance is not subtracted.

## Three-Position Strategy

All managed by LiquidityManager via ThreePositionStrategy abstract:

| Position | Purpose | Behavior |
|----------|---------|----------|
| **Floor** | Safety net | Deep liquidity at VWAP-adjusted prices |
| **Anchor** | Price discovery | Near current price, width set by Optimizer |
| **Discovery** | Fee capture | Borders anchor, ~3x price range (11000 tick spacing) |

**Recenter** = atomic repositioning of all three positions. Triggered by anyone, automated by txnBot.

**Recenter constraints** (enforced on-chain):
- **60-second cooldown**: `MIN_RECENTER_INTERVAL = 60` (`LiquidityManager.sol:61`). A second recenter cannot succeed until at least 60 seconds have elapsed since the last one.
- **300-second TWAP window**: `PRICE_STABILITY_INTERVAL = 300` (`PriceOracle.sol:14`). `recenter()` validates the current tick against a 5-minute TWAP average (±`MAX_TICK_DEVIATION = 50` ticks). The pool must have at least 300 seconds of observation history; a fallback to a 60 000-second window is used if recent data are unavailable.

## Optimizer Parameters

`getLiquidityParams()` returns 4 values:
1. `capitalInefficiency` (0 to 1e18) — capital buffer level
2. `anchorShare` (0 to 1e18) — % allocated to anchor position
3. `anchorWidth` (ticks) — width of anchor position
4. `discoveryDepth` (0 to 1e18) — depth of discovery position

Sentiment calculation: `sentiment = f(averageTaxRate, percentageStaked)`
- High sentiment (bull) → wider discovery, more fee revenue for protocol treasury
- Holder value comes from asymmetric slippage (structural ETH accumulation), NOT from fee reinvestment
- Low sentiment (bear) → tight around floor, maximum protection

## Push3 Seed Pool

The evolutionary optimizer runs from `tools/push3-evolution/`. Active seeds are tracked in `tools/push3-evolution/seeds/manifest.jsonl` — one JSON object per line (JSONL format).

### `manifest.jsonl` field reference

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `file` | string | ✓ | Filename relative to `seeds/` (e.g. `optimizer_v3.push3`) |
| `origin` | `"hand-written"` \| `"evolved"` \| `"llm"` | ✓ | How the seed was produced |
| `date` | string (`YYYY-MM-DD`) | ✓ | ISO 8601 date the entry was added to the manifest |
| `fitness` | integer \| null | — | Raw fitness score (wei-scale integer). `null` when the seed has not yet been evaluated or the score has been invalidated |
| `fitness_flags` | string \| null | — | Comma-separated flags that qualify or invalidate the fitness value (e.g. `token_value_inflation,processExecIf_fix`). `null` when no flags apply |
| `run` | string \| null | — | Zero-padded run identifier from which the seed was admitted (e.g. `"007"`). `null` for `hand-written` and `llm` seeds |
| `generation` | integer \| null | — | Generation index within the run at which this candidate was produced. `null` for `hand-written` and `llm` seeds |
| `note` | string \| null | — | Human-readable description of the seed strategy or noteworthy behaviour |

The full machine-readable definition is in `tools/push3-evolution/seeds/manifest.schema.json` (JSON Schema draft 2020-12). `additionalProperties` is `false` — unknown fields are rejected. Only `file`, `origin`, and `date` are required; all other fields are optional but must match the types above when present.

## Stack

### On-chain
- Solidity, Foundry toolchain
- Uniswap V3 for liquidity positions
- OpenZeppelin for UUPS proxy, Initializable
- Base L2 (deployment target)

### Indexer
- **Ponder** (`services/ponder/`) — indexes on-chain events
- Schema: `services/ponder/ponder.schema.ts`
- Stats table with 168-slot ring buffer (7d × 24h × 4 segments)
- Ring buffer segments: [ethReserve, minted, burned, tax] (slot 3 being changed to holderCount)
- GraphQL API at port 42069

### Landing Page
- Vue 3 + Vite (`landing/`)
- `@wagmi/vue` for wallet connection (WalletButton, WalletCard)
- `@tanstack/vue-query` — required peer dep for `@wagmi/vue`; provides TanStack Query context for Wagmi's reactive hooks
- `@harb/web3` shared composables (`useAccount`, `useConnect`, `useDisconnect`, `useTokenBalance`)
- Three variants: HomeView (default), HomeViewOffensive (degens), HomeViewMixed
- Docs section: HowItWorks, Tokenomics, Staking, LiquidityManagement, AIAgent, FAQ
- LiveStats component polls Ponder GraphQL every 30s

### Staking Web App
- Vue 3 (`web-app/`)
- Password-protected (multiple passwords in LoginView.vue)
- ProtocolStatsCard shows real-time protocol metrics

### Infrastructure
- Docker Compose on 8GB VPS
- Woodpecker CI at ci.niovi.voyage
- Codeberg repo: johba/harb (private)
- Container registry: registry.niovi.voyage

## Directory Map

```
harb/
├── onchain/           # Solidity contracts + Foundry
│   ├── src/           # Contract source
│   ├── test/          # Forge tests
│   └── foundry.toml   # via_ir = true required
├── services/
│   ├── ponder/        # Indexer service
│   │   ├── ponder.schema.ts
│   │   ├── src/
│   │   │   ├── helpers/stats.ts  # Ring buffer logic
│   │   │   ├── lm.ts             # LiquidityManager indexing
│   │   │   └── stake.ts          # Stake indexing
│   └── txnBot/        # Automation bot: calls recenter() and payTax() on profitable opportunities
├── landing/           # Landing page (Vue 3)
│   ├── src/
│   │   ├── components/  # LiveStats, KFooter, WalletCard, etc.
│   │   ├── views/       # HomeView variants, docs pages
│   │   └── router/
├── web-app/           # Staking app (Vue 3)
│   ├── src/
│   │   ├── components/  # ProtocolStatsCard, etc.
│   │   └── views/       # LoginView, StakeView, etc.
├── kraiken-lib/       # Shared TypeScript helpers (bigint math, ABIs, encoding) for frontend and indexer
│   └── src/           # abis, format, ids, position, snatch, staking, subgraph, taxRates, version
├── containers/        # Docker configs, entrypoints
├── tools/             # Developer utilities
│   ├── push3-transpiler/  # Compiles Push3 programs to Solidity Optimizer
│   ├── push3-evolution/   # Evolutionary optimizer: fitness, mutation, crossover, seed generation
│   └── deploy-optimizer.sh  # Script to deploy a new Optimizer version
├── docs/              # This file, PRODUCT-TRUTH.md
└── .woodpecker/       # CI pipeline configs
```
