Odds and ticketing
Your chance of winning is proportional to how much you hold. Every token is one ticket, so a wallet with twice the balance has exactly twice the chance.
The formula
At draw time the keeper takes the frozen snapshot of eligible wallets and lays their balances end to end as a single number line. Each wallet is assigned a contiguous, half-open ticket range:
weight(w) = balance(w) // in base units
totalTickets = Σ weight(w) for all eligible w
ticketStart(w) = Σ weight(v) for every v before w
ticketEnd(w) = ticketStart(w) + weight(w)
odds(w) = weight(w) / totalTicketsThe winner is the single wallet whose range contains the winning ticket, found by binary search:
winningTicket ∈ [0, totalTickets)
winner = the w where ticketStart(w) ≤ winningTicket < ticketEnd(w)Because the ranges are contiguous and half-open, they tile the line with no gaps and no overlaps. Exactly one wallet can win, and every ticket belongs to someone.
A worked example
Three eligible wallets:
| Wallet | Balance | Ticket range | Odds |
|---|---|---|---|
| A | 10,000 | [0, 10,000) | 12.5% |
| B | 50,000 | [10,000, 60,000) | 62.5% |
| C | 20,000 | [60,000, 80,000) | 25.0% |
Total tickets: 80,000. If the winning ticket is 43,912 the winner is B, because 10,000 ≤ 43,912 < 60,000. B holds five times what A holds and has exactly five times the chance.
Why balances, not wallets
Weighting by balance is the only option that resists splitting. If every eligible wallet got one ticket regardless of size, anyone could take 10.0K tokens, spread them across a hundred wallets, and multiply their odds a hundredfold at no cost. Weighting by balance makes that pointless: a hundred wallets holding a hundredth each have precisely the same total odds as one wallet holding the lot.
Precision
Balances are handled as arbitrary-precision integers in base units, never as floating point numbers. A token with six decimals and a large supply overflows a double long before it overflows the arithmetic used here, so a whale's balance is never silently rounded and a dust holder's tickets are never lost.
The displayed odds percentage is rounded for readability only. The draw itself uses the raw integers.
Uniformity of the roll
The winning ticket is drawn from a 256-bit hash. Reducing that to the ticket range with a plain modulo would very slightly favour low tickets, because 2256 is not an exact multiple of the total. Instead the keeper uses rejection sampling: it discards any hash landing in the short final partial block and re-derives with an incremented label. The result is exactly uniform.
RANGE = 2^256
limit = RANGE - (RANGE mod totalTickets) // largest unbiased multiple
for attempt = 0, 1, 2, ...
v = sha256(serverSeed:clientSeed:"ticket:" + attempt)
if v < limit: return v mod totalTicketsThe probability of even one rejection is negligible, but the loop makes the distribution provably flat rather than approximately flat.
How the stock is chosen
Independently of the winner, and with the same rejection sampling, the keeper draws a stock uniformly from the names that can absorb this round's pot. Every eligible name has an equal chance. A deeper name is not more likely than a shallower one.
The eligible set is filtered by live routing depth: the keeper quotes the full pot into each candidate and keeps only those where price impact stays under 3%. A large pot therefore draws from fewer, deeper names. See Stocks in rotation.
Checking your own odds
Connect a wallet on the main board and the position bar shows your balance, your live odds and your winnings. Odds shown during an open round are based on the keeper's most recent snapshot, so they move as other people buy and sell. Only the snapshot taken at draw time decides the round.