Randomness Tutorial

Example Code: The complete working example for this tutorial is available at sb-on-demand-examples/solana/randomness/coin-fliparrow-up-right

This tutorial demonstrates how to integrate verifiable randomness into your Solana program using Switchboard's commit-reveal pattern. You'll build a provably fair coin flip game.

Version source of truth: SDK Version Matrix

Why Verifiable Randomness?

On-chain randomness is hard. Naive approaches fail because:

  • Block hashes are predictable: Validators can see future block data

  • Timestamps are manipulable: Validators control block timestamps

  • External sources are untrusted: Off-chain randomness can be faked

Switchboard solves this with a commit-reveal pattern where neither party knows the outcome until after commitment.

How It Works

1. COMMIT    →    2. GENERATE    →    3. REVEAL
   Player            Oracle             Settlement
   commits to        generates          Player reveals
   slothash          randomness         and uses value
  1. Commit: Player commits to using a specific Solana slothash

  2. Generate: Oracle generates randomness based on the committed slot

  3. Reveal: Player reveals the randomness and uses it in their program

This is secure because:

  • The player commits before knowing the randomness

  • The oracle generates randomness after commitment

  • Neither party can manipulate the outcome

What You'll Build

A coin flip game where:

  • Player guesses heads or tails

  • Switchboard generates verifiable random outcome

  • Winner receives double their wager

Prerequisites

  • Rust and Cargo installed

  • Anchor framework 0.31.1

  • Solana CLI installed and configured

  • Node.js and pnpm

  • A Solana keypair with SOL

Key Concepts

Randomness Account

A dedicated Solana account that stores:

  • The committed slothash

  • The seed slot

  • The revealed random value (after revelation)

RandomnessAccountData

The on-chain struct for parsing randomness state:

Note: Randomness is not read from quote.feeds(). For Solana commit-reveal, call RandomnessAccountData::get_value(clock.slot), which returns a 32-byte randomness value (for example, random_bytes[0] % 2 for a coin flip).

Slot-Based Freshness

Randomness must be used within a specific slot window:

Collateral on Commit (Critical!)

Always take payment when committing, not when revealing.

Why? If you take payment on reveal, a malicious user could:

  1. Commit to randomness

  2. Wait for reveal

  3. Only reveal if they won (selective revelation attack)

The On-Chain Program

Dependencies

Program Structure

Account Structures

Error Codes

The TypeScript Client

Setup

Create Randomness Account

Commit Phase

Reveal Phase

Retry Logic

Network issues can cause commit/reveal to fail. Add retry logic:

Running the Example

1. Clone the Repository

2. Install Dependencies

3. Point Solana CLI at Devnet

4. Run the Example Against the Preconfigured Devnet Program

The checked-in Anchor.toml already includes a devnet program ID for sb_randomness, so you can run the example directly:

5. Deploy Your Own Program (Optional)

If you want to deploy your own instance instead of using the preconfigured devnet program:

The example script resolves the example program ID from Anchor.toml. To override that at runtime, set SB_RANDOMNESS_PROGRAM_ID:

Expected Output

Security Best Practices

1. Always Take Collateral at Commit Time

2. Validate Slot Freshness

3. Verify Randomness Account Reference

4. Check Randomness Not Already Revealed

Use Cases

Gaming & Gambling

  • Casino games (dice, slots, roulette)

  • Provably fair betting

  • Skill-based games with random elements

NFT Minting

  • Random trait assignment

  • Fair rarity distribution

  • Blind box reveals

Lotteries

  • Ticket drawing

  • Raffle winners

  • Prize distribution

Fair Distribution

  • Airdrop selection

  • Whitelist randomization

  • Token allocation

Advanced Topics

Reusing Randomness Accounts

Save the keypair to reuse across sessions:

Multiple Random Values

The revealed value is 32 bytes. Use different bytes for different outcomes:

Next Steps

Last updated