# Architecture

Stockpot is a small set of contracts around one idea: **deposits earn yield in
an audited vault; yield and fees fill a pot; a provably-checkable weekly draw
pays winners in tokenized stocks.**

The principal path is deliberately not ours: it is a near-byte-faithful fork of
PoolTogether V5's audited PrizeVault, with PT's TwabController and TwabRewards
adopted wholesale (all MIT, pinned at audited versions — see
[PrizeVault](/contracts/prize-vault) for provenance and deltas).

## Contract map

```mermaid
flowchart TB
    subgraph PT["PoolTogether V5 components (unmodified)"]
        TC[TwabController<br/>onchain TWAB accounting]
        TR[TwabRewards<br/>claimable interest]
    end

    subgraph Stockpot
        PV[PrizeVault<br/>PT v5 fork — holds all principal]
        DM[DrawManager<br/>pots, commit-reveal draws, claims]
        PB[PrizeBuyer<br/>stateless swapper]
        BS[BoostStaking<br/>staked-$POT TWAB]
        PFC[PonsFeeCollector<br/>planned]
    end

    SV[Steakhouse USDG vault<br/>Morpho, ERC-4626]
    E[DiceEntropy oracle]
    U[Uniswap SwapRouter]
    K[Keeper]

    PV -->|deposits USDG| SV
    PV -.->|share balances| TC
    PV -->|20% fee shares| TR
    PV -->|80% yield skim| K
    PFC -->|$POT pool fees| K
    K -->|fundPot ETH| DM
    DM <-->|entropy request / callback| E
    DM -->|buyFor on claim| PB
    PB -->|swap| U
```

| Contract | Role | Provenance |
|---|---|---|
| [`PrizeVault`](/contracts/prize-vault) | ERC-4626 vault holding **all user principal**; shares 1:1 with USDG; yield split 20/80 | **PT v5 fork** (audited upstream; documented deltas) |
| `TwabController` | Time-weighted average balance accounting for vault shares — draw weights are onchain-verifiable | **PT v5, unmodified** |
| `TwabRewards` | Distributes the depositor 20% as weekly claimable interest, pro-rata by deposit TWAB | **PT v5, unmodified** |
| [`DrawManager`](/contracts/draw-manager) | Draw engine: pots, commit-reveal + oracle randomness, merkle-verified claims, rake | Stockpot |
| [`BoostStaking`](/contracts/boost-staking) | Checkpointed staked-$POT TWAB feeding the odds boost | Stockpot |
| [`PrizeBuyer`](/contracts/prize-buyer) | ETH → prize-stock-token swap at claim time | Stockpot |
| `PonsFeeCollector` | Routes $POT trading fees from the Pons locker into the pot (WETH side) and burns the $POT side | Stockpot (planned) |

## Design principles

### The principal path is immutable — and forked, not written

The PrizeVault has no proxy and no upgrade path, ever. Stockpot cannot
upgrade, replace, or take control of the contract holding your deposit.
Because this is the one contract that must not fail, it is a minimal-delta
fork of PoolTogether V5's audited PrizeVault rather than bespoke code: the
audited accounting (1:1 shares, dust collection, yield buffer, loss handling)
is byte-identical, and every Stockpot change is marked and diffable against
the vendored upstream.

### Shares are 1:1, interest is claimable

A share always represents exactly one deposited USDG unit — share price never
moves. Depositor earnings (20% of realized yield) accrue as **fee shares** in
the audited fee mechanism and are distributed weekly through TwabRewards as
claimable interest. The other 80% is skimmed by the keeper, converted, and
funds the pot. The skim is bounded by the vault's yield accounting: it
structurally cannot touch principal.

### Onchain weights, offchain assembly

Vault shares are TWAB-accounted in the TwabController, so every depositor's
draw weight is **computable and verifiable from chain state** — no event
replay, no trust in the keeper's arithmetic. The keeper's weights script only
applies the boost curve and assembles the merkle tree; a public
`verify draw N` command reproduces any published root.

### Two-party randomness

Chainlink VRF is not available on Robinhood Chain. Draws combine the keeper's
hash-committed seed with a random number bought from the **DiceEntropy**
oracle at commit time. Winning tickets are derived from the combined entropy
by rejection sampling — exactly uniform over `[0, totalWeight)`, no modulo
bias. Biasing a draw requires the keeper and the oracle operator to collude;
either party withholding alone only stalls the round into a void-and-rollover.

## Actors

| Actor | Powers | Limits |
|---|---|---|
| **Depositor** | Deposit (under the cap), withdraw any time, claim interest, self-claim a win | — |
| **Keeper** | Skims the 80%, funds the pot, publishes weights root, commits & reveals draws, creates weekly interest promotions | Cannot touch principal (vault accounting); cannot forge a winner (merkle-bound); cannot grind the seed; stalling forfeits the round |
| **Vault owner** | Adjust deposit cap, set skimmer/fee recipient, sweep non-core reward tokens | Cannot pause withdrawals (no code path); cannot upgrade the vault (no proxy); sweep cannot touch the asset, yield-vault shares, or vault shares |
| **DrawManager owner** | Set treasury, prize buyer, registrars; update token configs | Rake hard-capped at 20% |
| **DiceEntropy oracle** | Supplies the independent half of each round's entropy | Never sees the keeper's seed before committing its own; withholding only stalls the round |
| **Anyone** | `fundPot` (sponsor a pot), `voidRound` after a missed reveal, settle a claim with a valid proof, verify weights and draws from chain state | — |

## Value flow

```mermaid
sequenceDiagram
    participant D as Depositor
    participant PV as PrizeVault
    participant SV as Steakhouse vault
    participant K as Keeper
    participant TR as TwabRewards
    participant DM as DrawManager
    participant W as Winner

    D->>PV: deposit USDG (1:1 shares)
    PV->>SV: assets earn yield
    Note over PV: realized yield accrues above totalDebt
    K->>PV: skim 80% (transferTokensOut)
    Note over PV: 20% accrues as fee shares
    K->>TR: weekly promotion (claimable interest)
    K->>DM: convert + fundPot (ETH)
    Note over DM: pot grows all week
    K->>DM: commitDraw / revealDraw
    W->>DM: claimPrize (merkle proof + stock choice)
    DM->>W: stock tokens via PrizeBuyer
```
