VRF (Verifiable Random Function)
Our gacha machines use a Verifiable Random Function (VRF) to ensure every pull is provably fair and tamper-proof. Neither the server nor the user can manipulate the outcome - and anyone can verify this independently.
How it works
The VRF combines entropy from both the server and the client to generate a deterministic, verifiable random number. Here’s the complete flow:
sequenceDiagram participant User participant Client App participant Server participant Blockchain User->>Client App: Initiate gacha pull Client App->>Server: Request new pull session Server->>Server: Generate initialEntropy (nanoid) Server-->>Client App: Return pullId + initialEntropy Client App->>Client App: Generate clientEntropy (random) Client App->>Blockchain: Send payment with pullId|clientEntropy Blockchain-->>Server: Payment confirmed Server->>Server: Combine & sign entropy Server->>Server: Hash → finalEntropy Server->>Server: Convert to random number [1-10000] Server->>Server: Select prize based on tier ranges Server-->>Client App: Return prize + VRF proof data Client App->>Client App: Verify signature & hash integrity User->>User: Can independently verify result
The VRF process step-by-step
1. Initial entropy (server-side)
When you initiate a gacha pull, the server generates a random 10-character string using nanoid. This is committed to the database before you make your payment, preventing the server from changing it later.
2. Client entropy (your side)
Your device generates its own random data. This entropy is included in your payment transaction on the blockchain, creating an immutable record that the server cannot alter.
3. Combined entropy
The two entropy sources are concatenated:
combinedEntropy = initialEntropy + clientEntropy4. Signed combined entropy
The server signs the combined entropy using Ed25519 (via TweetNaCl):
signedCombinedEntropy = Ed25519.sign(combinedEntropy, serverSecretKey)This signature proves the server processed your specific entropy values.
5. Final entropy
The signature is hashed using SHA-256 to produce the final entropy:
finalEntropy = SHA256(signedCombinedEntropy)6. Random number generation
The final entropy is converted to a number in the range [1, 10000]:
randomNumber = (BigInt(finalEntropy) % 10000) + 1This gives us a uniformly distributed random number with 0.01% precision.
7. Prize tier selection
The random number maps to prize tiers based on configured probability ranges. Each gacha machine has its own tier configuration.
Why this is fair
flowchart TD
subgraph ServerCantCheat ["Server Cannot Cheat"]
A[Initial entropy committed<br/>before payment] --> B[Cannot change after<br/>seeing client entropy]
end
subgraph UserCantCheat ["User Cannot Cheat"]
C[Client entropy in<br/>blockchain transaction] --> D[Cannot change after<br/>seeing initial entropy]
end
subgraph Deterministic ["Result is Deterministic"]
E[Same inputs always<br/>produce same output] --> F[Anyone can verify<br/>the calculation]
end
B --> G[Fair Result]
D --> G
F --> G
Server cannot manipulate results because:
- Initial entropy is committed to the database before you pay
- The server doesn’t know your client entropy until after commitment
- Changing anything would invalidate the cryptographic signature
Users cannot manipulate results because:
- Client entropy is embedded in the blockchain transaction
- Once submitted, it cannot be changed
- The server’s initial entropy is already fixed
Results are verifiable because:
- All entropy values are returned to you after the pull
- The public key for signature verification is published
- Anyone can reproduce the exact calculation
Verifying your results
After each gacha pull, you receive the complete VRF proof data:
- Initial Entropy - The server’s random string (committed before your payment)
- Client Entropy - Your random data (from the blockchain transaction)
- Signed Combined Entropy - Server’s Ed25519 signature
- Final Entropy - SHA-256 hash of the signature
- Random Number - The derived number [1-10000]
- Prize Tier - Which tier your roll landed in
The app automatically verifies:
- ✓ The signature matches the public key
- ✓ The hash of the signature equals the final entropy
- ✓ The random number calculation is correct
You can also verify manually using any Ed25519 and SHA-256 implementation.
Technical specifications
| Component | Implementation |
|---|---|
| Initial entropy | nanoid(10) - 10-character random string |
| Signature algorithm | Ed25519 (TweetNaCl) |
| Hash function | SHA-256 |
| Random range | [1, 10000] inclusive |
| Precision | 0.01% (100 basis points) |
Public key
The VRF public key for signature verification:
i5ScBBvmGh6my9B/X+NpcUIlvRSWz7+U8M7/GprNzYk=This is a 32-byte Ed25519 public key encoded as base64. To use it for verification, decode from base64 to get the raw bytes, then use any Ed25519 implementation (such as TweetNaCl) to verify signatures against this key.