Payouts are in dry-run until go-live. Draws and verify still use the real formula.

Draw

Bag lock and the draw

Two minutes before each scheduled bell, the ticket bag freezes. The draw keeper then runs formula closing-bell-draw-v1 and publishes a receipt. Humans do not pick wallets.

Bag lock

Default SNAPSHOT_LEAD_SECONDS=120. At lock time the keeper freezes each wallet's ticket balance for that window. Buys and sells after lock do not change weights for the upcoming ring. Odds you saw earlier were live against a moving bag. The draw uses the frozen bag only.

Plain English

  1. Lock the bag (ticket balances per wallet).
  2. Cap each wallet's weight at 10% of tickets out (see Odds and the 10% cap).
  3. Build a seed from public inputs: block hash at snapshot, window id, and pot balance.
  4. Walk the capped weights with that seed until one wallet is selected. The frozen bag is also hashed for the receipt so anyone can check it.
  5. Publish the receipt (winner, seed, weights, formula id). Anyone can recompute it on /verify.

Technical steps (closing-bell-draw-v1)

Shared implementation lives in packages/fairness and is imported by the backend keeper. High level:

1. Cap each wallet:
     weight = min(tickets, floor(ticketsOut * ODDS_CAP_BPS / 10000))
2. Sort entrants: weight DESC, then address ASC.
3. Seed material (UTF-8), then keccak256:
     lowercase(blockhashAtSnapshot) | windowId | potBalance
     (potBalance uses toFixed(8) when passed as a number)
4. cursor = seed mod totalWeight; walk weights until the pick lands.
5. Emit receipt: formulaId closing-bell-draw-v1, seed, winner, weights.
   Snapshot hash (separate): sort address ASC, join "address:tickets"
   with newlines, keccak256 UTF-8.
  • Formula id string: closing-bell-draw-v1.
  • Same package powers the keeper and the Verify a ring page. MATCH means the receipt recomputes identically.
  • This is a published deterministic formula over public inputs. It is not a VRF product claim and not a "trustless" marketing slogan.

When a ring is skipped

If pot is below MIN_POT_GME, or total capped weight is zero, the keeper skips payout and announces. Tickets still wipe for that window per product rules when the window closes.