A chance game with the economy published. Version 1.1 · August 2026 · Solana mainnet
1. Abstract
Gacha Wars is a game of chance on Solana in which every winning spin is guaranteed profitable: the win prize includes an exit payment of 0.35343 SOL, recorded in full and made senior at the moment the win is created, against a spin price of 0.3 SOL — a floor of +17.81% before any other income. The pool that backs this promise launched with zero operator capital and funds itself from play: whenever recorded claims exceed pool cash, one hundred percent of the operator's fees are permanently redirected into the pool until every claim is fully backed — players are structurally senior, and the house is paid last, after every player, every time. Randomness is an on-chain VRF whose proof any third party can re-verify from raw RPC data. The aggregate return to players is 84.405% of all SOL wagered, and the 15.595% retained by the operator is stated here, in the interface, and in the deployed constants, because it is derivable from the program either way. This paper specifies the mechanics, derives the economics in closed form, and provides the procedure by which any reader can verify every claim without trusting the authors.
2. Mechanics
Spin. A player pays SPIN_PRICE = 0.3 SOL. The payment is held in a per-spin request account — not the pool — while a VRF request is issued for a seed fixed by the spin itself. Spins settle strictly in the order they were made. Half of spins win; half miss.
Miss. 0.21 SOL joins the pool that collateralizes every outstanding promise. 0.09 SOL goes to the treasury as the operator fee. Nothing else happens.
Win. The full 0.3 SOL joins the pool, and 0.357 SOL is immediately escrowed against a new slot owned by the winner. The slot's exit is funded before the slot exists on the board.
Slot. A slot is a position on a 100-slot board with two income claims: the escrowed exit, and a share of the fee drip. Each settled spin distributes DRIP_PER_SPIN = 0.0765 SOL across all occupied slots, tracked by a fixed-point per-slot accumulator. Drip is claimable at any time.
Kick. Once the board is full, each winning spin draws one victim uniformly at random — position = randomness mod 100 — and the incoming winner takes its place. The kicked slot's owner is paid, in the same transaction: the exit of 0.35343 SOL (the 0.357 SOL escrow minus a 1% kick fee of 0.00357 SOL to the treasury) plus every lamport of accrued drip. A slot earns drip for the spin that kicks it; it was on the board when that spin was made.
Fill phase. Until 100 slots are occupied there are no kicks, and the 0.0765 SOL drip divides among however many slots exist — early slots earn an outsized share. This bonus is not a rule; it is the arithmetic of dividing a constant by a small number.
Timeout. If the VRF is never fulfilled, anyone may expire the queue-head request after the timeout, and the payer reclaims the full 0.3 SOL plus all rent. The payment never entered the pool, so no failure of the oracle, the operator, or the crank can strand it.
3. The mathematics
Parameters, as deployed (lamports in parentheses):
| Constant | Value |
|---|---|
| SPIN_PRICE (P) | 0.3 SOL (300,000,000) |
| WIN_PROBABILITY (w) | 1/2, exact |
| BOARD_SIZE (N) | 100 |
| MISS_TO_POOL | 0.21 SOL (210,000,000) |
| MISS_TO_TREASURY | 0.09 SOL (90,000,000) |
| WIN_TO_POOL | 0.30 SOL (300,000,000) |
| EXIT_ESCROW_GROSS (E) | 0.357 SOL (357,000,000) |
| KICK_FEE (1% of E) | 0.00357 SOL (3,570,000) |
| EXIT_PAID_NET (X) | 0.35343 SOL (353,430,000) |
| DRIP_PER_SPIN (d) | 0.0765 SOL (76,500,000) |
| Operator launch capital | 0 — the pool starts empty |
| MAX_PENDING_SPINS | 256 (queue-hygiene cap; never a funds gate) |
Lifetime. At a full board a slot is kicked by a given spin with probability k = w/N = 0.005. Lifetimes are geometric: mean 1/k = 200 spins; median ⌈ln ½ / ln(1−k)⌉ = 139 spins.
Drip income. A slot receives d/N per spin over a mean of 1/k = N/w spins, so mean drip income is d/w = 0.153 SOL. N cancels: expected values do not depend on board size, only pacing does.
Win value. The mean value of a win is
V = X + d/w = 0.35343 + 0.153 = 0.50643 SOL (+68.8% on the spin).
Return to player. RTP = wV/P = 0.5 × 0.50643 / 0.3 = 0.844050, exactly. Equivalently V = (1−t)P/w with total take t = 0.15595. The operator's expected fee per spin is (1−w)·0.09 + w·0.00357 = 0.046785 SOL = 15.595% of P. These are the same number arriving from opposite directions; the tests require them to agree to the lamport.
Outcome distribution for a winner (drip at the full-board rate of 0.000765 per spin survived; the floor row assumes the minimum, exit only):
| Percentile of lifetime | Spins survived | Total received | Return on 0.3 |
|---|---|---|---|
| floor | — | 0.35343 | +17.81% |
| 10th | 21 | 0.36950 | +23.2% |
| 25th | 57 | 0.39703 | +32.3% |
| median | 139 | 0.45976 | +53.3% |
| mean | 200 | 0.50643 | +68.8% |
| 75th | 277 | 0.56534 | +88.4% |
| 90th | 459 | 0.70457 | +134.9% |
| 99th | 919 | 1.05646 | +252.2% |
In practice the floor is never touched: a victim earns drip for its killing spin, so the minimum observed win at a full board is X + 0.000765 SOL (+18.07%).
Outcome mapping. With 32 bytes of VRF output r: win iff the first 8 bytes, read as a little-endian u64, are even — exactly probability 1/2. Victim position = the next 8 bytes as a little-endian u64, mod 100. The modulo bias is 2⁶⁴ mod 100 = 16 parts in 2⁶⁴, about 10⁻¹⁸ relative — stated here because a fairness claim that hides its ε is not one.
4. Players first, house last: the self-funding pool
Define pool equity as pool balance minus all recorded claims (escrows plus unclaimed drip, plus any queued exits — see below). Every settlement moves equity by exactly the same base amount:
| In | Escrowed | Dripped | Equity move | |
|---|---|---|---|---|
| Win | +0.300 | −0.357 | −0.0765 | −0.1335 |
| Miss | +0.210 | — | −0.0765 | +0.1335 (+ up to 0.09, below) |
The two base moves are equal and opposite; this antisymmetry is enforced by tests with zero tolerance, in units of 10⁻¹² lamports.
The pool launched with zero operator capital. Whenever recorded claims exceed pool cash, the gap — a single public on-chain number — commands the house's entire fee: the 0.09 SOL per miss and the 0.00357 SOL per kick are redirected into the pool and forfeited permanently (there is no receivable and no instruction that can ever claw them back) until the gap is zero. An underwater miss therefore repairs 0.2235 SOL against 0.1335 of damage per win — the gap is a reflected random walk with strong drift to closure, and the treasury reads zero income for its entire life. The operator's realized take is provably at or below the published 15.595%, forever.
Why this is safe without a float: cash only ever leaves the pool at kicks and drip claims, and kicks require a full board. Filling 100 slots takes roughly 200 spins of pure inflow (~50 SOL of revenue, ~35.7 SOL of it held against the exits it escrows), so by the time the first exit in history can come due, the pool is deep in cash. The gap during the fill phase is an accounting number, not missing payout money. Every kick pays its victim in the same transaction in every reachable state. In the astronomically remote alternative — pool cash short at a kick, requiring roughly 265 net excess wins against the fee drift, on the order of 10⁻⁴⁵ per underwater excursion — the exact amount becomes a numbered on-chain claim, strictly first-in-first- out, senior to every fee, paid from the very next revenue. The win settles identically either way; no amount is ever reduced, cancelled, or renegotiated, and a spin is never refused for lack of pool funds.
Three structural facts complete the argument. Exits are recorded and made senior at slot creation, not at kick — the promise is senior before it is made, and cash-funded to the extent of the pool at every instant. Spin payments are held outside the pool until settlement — an unsettled spin cannot be lent against, and its timeout refund path is untouched by any of this. And drip is accounted by a scaled accumulator with an explicit carry — division residue rolls forward instead of leaking. Top-ups remain permissionless and one-way; nothing added to the pool is ever withdrawable by anyone but players.
5. Verification guide
Everything in sections 3 and 4 is checkable by a reader with an RPC endpoint. Current deployment (Solana mainnet, at full denomination — 0.3 SOL spins):
program 5f1F3YFDnJsYqbcju31Mv2yNYtA3RnGidSMX2YggbavR
vrf Vrf1RNUjXmQGjmQrQLvJHs9SNkvDJEsRVFPkfSQUwGz
rpc https://api.mainnet-beta.solana.com
(a) Verify a spin's randomness. Each spin binds seed = sha256("gacha-wars-vrf-seed-v1", request PDA, payer, nonce) before any randomness exists, and records it on-chain the moment the player pays; the inputs are in its SpinRequested event. The randomness is produced by an RFC 9381 elliptic-curve VRF (Ristretto/Curve25519) whose proof is verified by the randomness program itself, on-chain, before the bytes are delivered — so validity is not something a reader must re-check off-chain, it is a precondition of the outcome existing at all.
Two further properties make delivery trustworthy without trusting anyone. Only the randomness program's own identity PDA may deliver an outcome into a spin, enforced by an address-and-signature check, so a forged result is unconstructible rather than merely detectable. And randomness is write-once: once recorded it can never be revised, by the operator or by the oracle.
Every settled spin republishes its randomness in the SpinSettled event, so the entire history replays from raw chain data and each outcome recomputes with one line of arithmetic:
outcome = u64::from_le_bytes(randomness[0..8]) % 2 # 0 -> seat, 1 -> none
victim = u64::from_le_bytes(randomness[8..16]) % 100 # full board only
(b) Verify backing and seniority. Read the global state account (PDA seed "global") and the pool (PDA seed "pool"). Check pool lamports ≥ rent floor + pool_balance — tracked cash is physically present, always. Compute gap = max(0, escrow_liability + ⌈drip_liability⌉ + queued exits − pool_balance); then check the seniority rule in the event stream: no MissSplit or SlotKicked event carries a lamport to the treasury while the post-state gap is positive — the fee's forfeited share appears in the event itself, and cumulative forfeitures are in global state (fees_forfeited). The subordination is replayable, not asserted.
solana account -u mainnet-beta <GLOBAL_PDA> --output json # decode per IDL
(c) Recompute realized RTP. Every lamport movement emits a CPI event (immune to log truncation). Replay SpinSettled, SlotKicked, DripClaimed and RefundClaimed from transaction history and compute (paid out + owed to live slots) / (spins × price). The reference indexer in the repository does exactly this and serves it at /stats; it holds no state that is not reconstructible by anyone.
6. Fees and the treasury
The operator's entire income is 0.09 SOL per miss and 0.00357 SOL per kick — 15.595% of turnover in expectation, the complement of the published RTP. Fees accrue to a treasury account that is a different account from the pool. The withdrawal instruction can move treasury fees only; it does not take the pool as an input, and no other instruction is authority-gated. The claim "the operator cannot touch player funds" is therefore checkable by reading the program's instruction inventory rather than by trusting a policy. Pause authority exists and blocks new spins only — never settlement, claims, kicks, expiry or refunds.
7. Risks
You lose money on average. The expected return of a 0.3 SOL spin is 0.253215 SOL. The guaranteed floor applies to winning spins only; half of spins win nothing. Play is entertainment with a known price, not investment.
Demand risk. The pool is funded by play, and exits trigger only on future winning spins at a full board. If play stops permanently, slots on the board keep their recorded claims but nothing triggers them — the same end-state as any design in which exits require future wins, disclosed here because the zero-capital launch makes every lamport in the pool player revenue rather than operator money. While any backing gap exists the house has collected nothing; anyone, including the operator, can voluntarily top the pool up, and claims never expire.
Smart-contract risk. The program may contain defects despite its invariant test suite. It has not yet completed an external audit; the audit preparation document in the repository lists the threat model and invariants an auditor should attack. Do not stake funds you cannot afford to lose to a software failure.
VRF liveness. Randomness is fulfilled by MagicBlock's ephemeral-vrf, a single-oracle service. It cannot bias outcomes — the proof is verified on-chain and the delivery path is authenticated and write-once — but it can fail to answer, and unlike a quorum design there is no second oracle to cover for it. The timeout-and-refund path exists precisely for this: an unfulfilled request is expirable by anyone after 300 seconds and refunds the player in full. Measured mainnet fulfillment latency is under one second (p50 0.6s, p95 1.1s over our own settled spins).
This is a deliberate trade made on 2026-08-07, replacing ORAO classic v2. ORAO required all three of its authorities to answer, and one of the three exceeded ten seconds on 32.5% of requests, producing a p90 of 34.8 seconds; its on-chain program is also closed-source and unaudited to our knowledge. The replacement is open source, independently audited, verified on-chain, and empirically faster and more reliable — at the cost of concentrating liveness in one operator rather than three. Both facts are stated here because a fairness claim that hides its trade-offs is not one.
8. Responsible play and eligibility
Players must be 18 or older and located where games of chance are lawful; both are attested on-chain at first spin, and the interface applies the operator's jurisdiction restrictions. A wallet may irreversibly exclude itself with the self_exclude instruction; the program refuses every subsequent spin from that wallet, permanently, and no authority can undo it. Spending limits, cool-downs and help resources are linked in the interface: if play stops being entertainment, stop — and see begambleaware.org or your local equivalent.
9. FAQ
Why publish the house edge? Because it is derivable from the deployed program whether we publish it or not. Printing 15.595% on the first page costs nothing except the option to obscure it later.
Is the +17.81% floor real? For winning spins, yes, structurally: the exit amount is fixed by deployed constants at the moment the slot is created, it is senior to every fee the house can ever collect, and in every reachable state it is paid in cash in the same transaction as the kick. It is not a promise about unlucky spins, which pay zero.
Isn't "funded by later spins" a Ponzi? The structure to check for is liabilities growing faster than revenue can cover, hidden behind promised yield. Here the opposite is published in closed form: expected revenue exceeds expected player claims by 15.595% per spin, no yield is promised beyond the posted odds, every claim is a public on-chain number, and the only party whose payment ever waits is the operator — whose fees are forfeited, not deferred, while any gap exists. The whole argument is replayable from events by anyone (§5).
Can the operator rug the pool? There is no instruction that moves pool funds to any authority. Withdrawal exists for the fee treasury only. Pause stops new spins, not exits, claims or refunds.
Can the crank cheat? The crank has no choices. Settlement is strictly first-in-first-out, the randomness account is bound at request time, the outcome and any victim are dictated by the randomness, and a settlement that disagrees with any of it fails. Anyone may run a crank; the operator's is a convenience.
What if the board never fills? During the fill phase there are no kicks, so exits pay out only after 100 cumulative wins. The exit is recorded and senior all the same, cash-backed to the extent of the pool at every instant, and drip is divided among fewer slots — early slots are substantially overpaid relative to steady state. A board that stops filling forever is the demand-death risk in §7: exits that never trigger, exactly as in any design where exits require future wins.
Where does my 0.3 SOL go, exactly? On a miss: 0.21 to the pool, 0.09 to the treasury. On a win: 0.30 to the pool, which simultaneously escrows 0.357 against your slot. Each settled spin also moves 0.0765 from pool equity to slot holders as drip. Nothing else moves; the events prove it.