# Wheel of Forktune — published odds & luck tiers

## Core table (weights / 10000)

**80% loss · 20% any win. No jackpot (0%). No pity.**

| Outcome | Kind | Weight | % |
|---------|------|--------|---|
| loss | loss | 8000 | **80.00** |
| tokens_1 | tokens | 960 | 9.60 |
| tokens_3 | tokens | 450 | 4.50 |
| tokens_5 | tokens | 250 | 2.50 |
| cosmetic_c | cosmetic | 120 | 1.20 |
| cosmetic_u | cosmetic | 80 | 0.80 |
| cosmetic_r | cosmetic | 60 | 0.60 |
| cosmetic_e | cosmetic | 50 | 0.50 |
| cosmetic_l | cosmetic | 30 | 0.30 |

Pass holders use the same table (no jackpot bump).

Approximate token return EV: **~0.05 tokens/spin** vs **1 token/spin** cost — house retains margin before cosmetics.

## Provably fair draw

1. Server commits to `sha256(server_seed)` before spins.
2. Client supplies `client_seed`.
3. `raw_roll = HMAC-SHA256(server_seed, client_seed:nonce)` mapped to `[0,1)`.
4. Outcome is selected from the weight table using an **effective roll**.

Seed / nonce / commit-reveal are identical for every wallet. Luck never rewrites the HMAC.

## Disclosed luck tiers

Loyalty perks assigned via admin script into `wheel_user_flags` (not a silent env allowlist).

| Tier | Multiplier | Badge on spin card |
|------|------------|--------------------|
| `standard` | 1× | _(none)_ |
| `whale` | 2× | Whale Luck ×2 |
| `founder` | 3.5× | Founder's Luck ×3.5 |

**Formula (disclosed):**

```
effective_roll = raw_roll ^ (1 / multiplier)
```

Higher effective rolls land further down the published table (rarer win bands). Every spin response includes:

- `raw_roll` — HMAC value (auditable)
- `effective_roll` / `roll` — value used for table lookup
- `luck_tier`, `luck_multiplier`, `luck_badge` — when non-standard, the badge is shown in the UI

This is a **loyalty perk**, not equal odds for all wallets. It is intentional and documented.

## Assigning a tier

```bash
ADMIN_TOKEN=… docker compose exec -e ADMIN_TOKEN unfiltered \
  node scripts/set-wheel-luck-tier.js 0xYourWallet founder
ADMIN_TOKEN=… docker compose exec -e ADMIN_TOKEN unfiltered \
  node scripts/set-wheel-luck-tier.js --list
```

Script refuses without a matching `ADMIN_TOKEN` / `WHEEL_ADMIN_TOKEN` / `CREDITS_ADMIN_TOKEN` (docker shell alone is not enough).

## History / export (disclosure)

- `GET /api/wheel/history` — includes `luck_tier`, `luck_multiplier`, `luck_badge`, `raw_roll`, `effective_roll` on every row
- `GET /api/wheel/export` — same fields, larger default limit (receipt dump)
- `GET /api/wheel/feed` — public shared spins also include luck fields

## Analytics receipt (~1 week)

```bash
ADMIN_TOKEN=… docker compose exec -e ADMIN_TOKEN unfiltered \
  node scripts/wheel-luck-analytics.js
```

Groups spins by tier: `avg_raw`, `avg_eff`, `avg_lift`, win rate. Keep the output next to this doc if anyone questions the curve.

## What this is not

- Not a silent production bias behind “equal odds.”
- Not a change to Forge code credits or Council.
- Not a rewrite of the server seed.
