Whitepaper · the formal treatment

The economy, proven on paper.

The complete formal description of Gacha Royale: every mechanic, every derivation, the solvency argument, and the step-by-step verification guide. For the friendly version, read the docs; this is the document an auditor reads.

A chance game with the economy published. Version 1.0 · August 2026 · devnet


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, escrowed in full 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 is fully collateralized at all times; after every instruction the program holds at least the sum of all escrowed exits and all unclaimed fee drip, and new spins are refused before that property could fail. 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)
LAUNCH_FLOAT 30 SOL (30,000,000,000)

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. Solvency by construction

Define pool equity as pool balance minus all liabilities (escrows plus unclaimed drip). Every settlement moves equity by exactly the same amount:

In Escrowed Dripped Equity move
Win +0.300 −0.357 −0.0765 −0.1335
Miss +0.210 −0.0765 +0.1335

The two moves are equal and opposite; at fair odds the pool is a martingale. This antisymmetry is enforced by tests with zero tolerance, in units of 10⁻¹² lamports.

An unbiased ±0.1335 SOL walk will eventually exceed any fixed buffer, so collateralization cannot rest on the 30 SOL launch float alone. It does not. The program refuses a new spin unless current equity covers the worst case for every spin already pending plus the new one — equity ≥ 0.1335 × (pending

  • 1). When a win streak exhausts the buffer the game stops accepting spins, misses or a float top-up restore capacity, and settlement of everything already paid continues throughout. Top-ups are permissionless and one-way; nothing added to the pool is ever withdrawable by anyone but players.

Three structural facts complete the argument. Exits are escrowed at slot creation, not at kick — the promise is funded before it is made. Spin payments are held outside the pool until settlement — an unsettled spin cannot be lent against. And drip is accounted by a scaled accumulator with an explicit carry — division residue rolls forward instead of leaking.

5. Verification guide

Everything in sections 3 and 4 is checkable by a reader with an RPC endpoint. Current deployment (devnet, at 1/10 denomination — 0.03 SOL spins; every ratio identical):

program        Fg17YQbibJNuGBPxAzNHW4R95yK3qSyWDAzayiChD78u
orao vrf       VRFzZoJdhFWL8rkvu87LpKM3RbcVezpMEc6X5GVDr7y
rpc            https://api.devnet.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; the inputs are in its SpinRequested event. ORAO authorities each sign the seed (RFC-8032 ed25519 — deterministic, so an oracle cannot grind outcomes, only decline to answer); the on-chain randomness is the XOR of the signatures. The repository ships a standalone script that re-derives the seed, fetches the fulfillment transactions, verifies each signature against ORAO's registered authorities, re-XORs, and decodes the outcome:

python3 scripts/verify_spin.py \
  --request <SPIN_REQUEST_PDA> --payer <WALLET> --nonce 0 \
  --rpc https://api.devnet.solana.com

(b) Verify solvency. Read the global state account (PDA seed "global") and the pool (PDA seed "pool"). Check pool lamports ≥ rent floor + pool_balance, and pool_balance ≥ escrow_liability + ⌈drip_liability⌉. The program also performs this check after every instruction that touches the pool.

solana account -u devnet <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.

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 ORAO's three proof-of-authority oracles. They cannot bias outcomes — ed25519 signatures over a fixed seed are deterministic — but they can fail to answer, and fulfillment requires all three. The timeout-and-refund path exists precisely for this; measured mainnet fulfillment latency was 17–40 seconds at research time.

Operational notes. ORAO's on-chain program is closed-source and unaudited to our knowledge; its fee and authority set are operator-mutable and are read at request time. Devnet runs at 1/10 denomination for soak testing; mainnet parameters are exactly those in section 3.

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 is escrowed from the pool at the moment the slot is created, and the solvency invariant means that escrow is always fully backed. It is not a promise about unlucky spins, which pay zero.

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 escrow is locked all the same, and drip is divided among fewer slots — early slots are substantially overpaid relative to steady state.

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.