# Eligibility & Odds

**Deposit USDG and you're in every draw.** No registration, no tickets, no
gas. Odds are proportional to your **time-weighted average deposit** (TWAB)
over the draw window, with an optional boost (up to 2x) from staked $POT.

Only USDG depositors win draws. $POT holders and stakers do not win by
holding or staking — the token's utility is the boost and its trading fees
funding the pot (see [Tokenomics](/tokenomics)).

## Why time-weighted?

Weighting by instantaneous balance would let anyone deposit right before the
draw and withdraw right after ("draw sniping"). Time-weighting makes a dollar
deposited for one hour of the week worth 1/168th of a dollar deposited all
week — odds reward actual saving, and deposits stay sticky.

## Entries

```
tickets = depositTwab × boost(stakedPotTwab)
boost(s) = 1 + s / (s + K)        // smooth curve, capped at 2x; K is a published constant
```

* **Deposit TWAB is onchain.** PrizeVault shares are recorded in the
  PoolTogether V5 `TwabController` (adopted unmodified): every deposit,
  withdrawal, and transfer checkpoints a time-weighted observation. Anyone
  can read any depositor's exact TWAB for any finalized, period-aligned
  window with `getTwabBetween` — no event replay, no trusted computation.
* **Draw windows are period-aligned.** The TwabController accounts in 1-day
  periods; draw windows start and end on period boundaries, where TWAB
  queries are exact and manipulation-resistant.
* **Stake TWAB is onchain too.** [`BoostStaking`](/contracts/boost-staking)
  checkpoints staked-$POT balances the same way (simplified TwabController
  design). No market prices enter the weighting anywhere.
* **The script only assembles.** The keeper's open-source weights script
  reads both TWABs from chain state, applies the boost curve, excludes
  non-participants (the Pons pool and locker, treasury, dead addresses), and
  builds the merkle tree. Two independent runs produce an identical root.

### Leaf format

```solidity
leaf = keccak256(abi.encode(account, cumStart, weight));
```

Leaves are sorted by `cumStart` and **tile the interval `[0, totalWeight)`** —
depositor *i*'s range starts where depositor *i−1*'s ends. A ticket `t`
belongs to the unique leaf where `cumStart <= t < cumStart + weight`. Your
probability of winning is exactly `weight / totalWeight`.

## The boost (up to 2x)

Staking $POT is optional and only changes your odds multiplier — it never
changes your interest (the depositor 20% is proportional to deposit TWAB
alone). The boost curve uses the raw staked amount, not its price:

```
effectiveTickets = depositTwab × (1 + stakedTwab / (stakedTwab + K))
```

Early stakers with more $POT get a larger multiplier, approaching but never
exceeding 2x.

## Verifying a draw

Every input to the draw is public:

1. Read every depositor's TWAB for the draw window straight from the
   TwabController (`getTwabBetween`), and staked TWABs from BoostStaking.
   Run the published script → confirm the merkle root matches
   `DrawCommitted.weightsRoot`.
2. Check the seed against the commitment: `keccak256(seed) == seedCommitment`.
3. Read the oracle's random number from the round (`oracleRandom`, delivered
   by the DiceEntropy callback), recompute every prize slot's ticket from
   `keccak256(abi.encode(seed, oracleRandom, slot))` via the rejection sampler
   (uniform over `[0, totalWeight)`, no modulo bias — the exact algorithm is
   in `DrawManager._uniformTicket`, also exposed as the `slotTicket` view).
4. Locate each ticket in the sorted ranges → confirm each slot's winner
   matches its `PrizeClaimed` event.

Any mismatch is cryptographic proof of keeper misbehavior. Because deposit
TWAB is onchain, a dishonest weights root is not just detectable after the
fact — it is checkable by anyone before the draw reveals.
