> ## Documentation Index
> Fetch the complete documentation index at: https://docs.magicblock.gg/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Learn how to request and consume Solana VRF randomness onchain using the MagicBlock VRF SDK.

***

<Tip>
  **Building with an AI coding agent?** Install the MagicBlock Dev Skill to give your agent MagicBlock-specific patterns — delegation flows, Magic Actions, cranks, VRF, and more.

  Quick install for Claude Code:

  ```bash theme={null}
  npx skills add https://github.com/magicblock-labs/magicblock-dev-skill
  ```

  Using Cursor, Codex, Windsurf, Cline, or another agent? See the [AI Dev Skill](/pages/overview/additional-information/ai-dev-skill) page for all install targets.
</Tip>

### Quick Access

Check out basic VRF examples:

<CardGroup cols={2}>
  <Card title="GitHub" icon="github" href="https://github.com/magicblock-labs/magicblock-engine-examples/tree/main/roll-dice/anchor" iconType="duotone">
    Repo for roll dice example
  </Card>

  <Card title="VRF dApp" icon="dice" href="https://roll-dice.magicblock.app/" iconType="duotone">
    Roll a dice onchain
  </Card>

  <Card title="Delegated VRF dApp" icon="bolt" href="https://roll-dice.magicblock.app/delegated" iconType="duotone">
    Roll a dice within 100 ms onchain
  </Card>
</CardGroup>

***

<Note>
  Need the product overview first? Start with the <a href="/pages/verifiable-randomness-functions-vrfs/introduction/solana-vrf">Solana VRF</a> landing page, then follow this quickstart.
</Note>

## Step-By-Step Guide

Any Solana program can request and consume verifiable randomness onchain within seconds using the MagicBlock VRF SDK. By the end of this guide, you'll have a working example that rolls a dice using verifiable randomness.

<Steps>
  <Step title={<a href="#1-write-program">Write your program</a>}>
    Write your Solana program as you normally.
  </Step>

  <Step
    title={
  <a href="#code-snippets">
    Add request and consume randomness instructions.
  </a>
}
  >
    Add CPI hooks that request and consume randomness via callback from a
    verified oracle.
  </Step>

  <Step title={<a href="#3-deploy">Deploy your program on Solana</a>}>
    Deploy your Solana program using Anchor CLI.
  </Step>

  <Step title={<a href="#4-test">Execute transactions for onchain randomness.</a>}>
    Send transactions to generate and consume randomness onchain.
  </Step>
</Steps>

***

## Roll Dice Example

<img
  src="https://mintcdn.com/magicblock-42/iteauKFqxDKE2Vln/images/gifs/vrf-roll-dice-420w.gif?s=9250f6e10d1a713f3c3d02f990f3a1b4"
  alt="Roll Dice GIF"
  style={{
width: "100%",
maxWidth: "420px",
height: "auto",
objectFit: "contain",
borderRadius: "8px",
}}
  width="420"
  height="420"
  data-path="images/gifs/vrf-roll-dice-420w.gif"
/>

The following software packages may be required, other versions may also be compatible:

| Software   | Version | Installation Guide                                              |
| ---------- | ------- | --------------------------------------------------------------- |
| **Solana** | 3.1.9   | [Install Solana](https://docs.anza.xyz/cli/install)             |
| **Rust**   | 1.89.0  | [Install Rust](https://www.rust-lang.org/tools/install)         |
| **Anchor** | 1.0.2   | [Install Anchor](https://www.anchor-lang.com/docs/installation) |
| **Node**   | 24.10.0 | [Install Node](https://nodejs.org/en/download/current)          |

### Code Snippets

<Tabs>
  <Tab title="1. Write program">
    A simple roll dice program where player initialize state account to store, request and consume randomness:

    ```rust theme={null}
    pub const PLAYER: &[u8] = b"playerd";

    #[program]
    pub mod random_dice {
        use super::*;

        pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
            msg!(
                "Initializing player account: {:?}",
                ctx.accounts.player.key()
            );
            Ok(())
        }

        // ... Additional instructions will be added here
    }

    /// Context for initializing player
    #[derive(Accounts)]
    pub struct Initialize<'info> {
        #[account(mut)]
        pub payer: Signer<'info>,
        #[account(init_if_needed, payer = payer, space = 8 + 1, seeds = [PLAYER, payer.key().to_bytes().as_slice()], bump)]
        pub player: Account<'info, Player>,
        pub system_program: Program<'info, System>,
    }

    /// Player struct
    #[account]
    pub struct Player {
        pub last_result: u8,
    }
    ```

    [⬆️ Back to Top](#code-snippets)
  </Tab>

  <Tab title="2. Request & Consume Randomness">
    1. Add `ephemeral-rollups-sdk` with the `anchor` and `vrf` features to your program

    ```bash theme={null}
    cargo add ephemeral-rollups-sdk --features anchor,vrf
    ```

    Import the `vrf` and `vrf_callback` macros, `create_request_scoped_randomness_ix`, `RequestRandomnessParams`, and `SerializableAccountMeta`:

    ```rust theme={null}
    use ephemeral_rollups_sdk::anchor::{vrf, vrf_callback};
    use ephemeral_rollups_sdk::vrf::instructions::{create_request_scoped_randomness_ix, RequestRandomnessParams};
    use ephemeral_rollups_sdk::vrf::types::SerializableAccountMeta;
    ```

    2. Add instructions `roll_dice` to request randomness and `callback_roll_dice` to consume randomness, along with its context:

    ```rust theme={null}
    use ephemeral_rollups_sdk::{
        anchor::{vrf, vrf_callback},
        vrf::{
            self,
            instructions::{create_request_scoped_randomness_ix, RequestRandomnessParams},
            types::SerializableAccountMeta,
        },
    };

    #[program]
    pub mod random_dice {
        use super::*;

        // ... `initialize` instruction

        // Request Randomness
        pub fn roll_dice(ctx: Context<DoRollDiceCtx>, client_seed: u8) -> Result<()> {
            msg!("Requesting randomness...");
            let ix = create_request_scoped_randomness_ix(RequestRandomnessParams {
                payer: ctx.accounts.payer.key(),
                oracle_queue: ctx.accounts.oracle_queue.key(),
                callback_program_id: ID,
                callback_discriminator: instruction::CallbackRollDice::DISCRIMINATOR.to_vec(),
                caller_seed: [client_seed; 32],
                // Specify any account that is required by the callback
                accounts_metas: Some(vec![SerializableAccountMeta {
                    pubkey: ctx.accounts.player.key(),
                    is_signer: false,
                    is_writable: true,
                }]),
                callback_args: Some(vec![client_seed]),
                ..Default::default()
            });
            ctx.accounts
                .invoke_signed_vrf(&ctx.accounts.payer.to_account_info(), &ix)?;
            Ok(())
        }

        // Consume Randomness
        pub fn callback_roll_dice(
            ctx: Context<CallbackRollDiceCtx>,
            randomness: [u8; 32],
            client_seed: u8,
        ) -> Result<()> {
            msg!("client_seed={}", client_seed);
            let rnd_u8 = vrf::rnd::random_u8_with_range(&randomness, 1, 6);
            msg!("Consuming random number: {:?}", rnd_u8);
            let player = &mut ctx.accounts.player;
            player.last_result = rnd_u8; // Update the player's last result
            Ok(())
        }
    }

    #[vrf]
    #[derive(Accounts)]
    pub struct DoRollDiceCtx<'info> {
        #[account(mut)]
        pub payer: Signer<'info>,
        #[account(seeds = [PLAYER, payer.key().to_bytes().as_slice()], bump)]
        pub player: Account<'info, Player>,
        /// CHECK: The oracle queue
        #[account(
            mut,
            constraint =
                oracle_queue.key() == vrf::consts::DEFAULT_QUEUE ||                // Devnet
                oracle_queue.key() == vrf::consts::DEFAULT_TEST_QUEUE ||           // Local
                oracle_queue.key() == vrf::consts::DEFAULT_EPHEMERAL_QUEUE ||      // ER Devnet
                oracle_queue.key() == vrf::consts::DEFAULT_EPHEMERAL_TEST_QUEUE    // ER Local
        )]
        pub oracle_queue: UncheckedAccount<'info>,
    }

    // `#[vrf_callback]` enforces that only the VRF program (via CPI) can invoke the
    // callback — omitting it leaves the callback spoofable by any caller.
    #[vrf_callback]
    #[derive(Accounts)]
    pub struct CallbackRollDiceCtx<'info> {
        #[account(mut)]
        pub player: Account<'info, Player>,
    }

    // ... Other context and account struct.
    ```

    <Note>
      **VRF SDK constants** (`ephemeral_rollups_sdk::vrf::consts`) — reference these instead of hardcoding addresses, both in your program and in client/test code:

      | Constant                       | Purpose                           | Address                                        |
      | ------------------------------ | --------------------------------- | ---------------------------------------------- |
      | `VRF_PROGRAM_ID`               | VRF program                       | `Vrf1RNUjXmQGjmQrQLvJHs9SNkvDJEsRVFPkfSQUwGz`  |
      | `VRF_PROGRAM_IDENTITY`         | Callback signer PDA               | `9irBy75QS2BN81FUgXuHcjqceJJRuc9oDkAe8TKVvvAw` |
      | `DEFAULT_QUEUE`                | Base-layer queue (mainnet/devnet) | `Cuj97ggrhhidhbu39TijNVqE74xvKJ69gDervRUXAxGh` |
      | `DEFAULT_EPHEMERAL_QUEUE`      | ER queue (mainnet/devnet)         | `5hBR571xnXppuCPveTrctfTU7tJLSN94nq7kv7FRK5Tc` |
      | `DEFAULT_TEST_QUEUE`           | Base-layer queue (localnet)       | `GKE6d7iv8kCBrsxr78W3xVdjGLLLJnxsGiuzrsZCGEvb` |
      | `DEFAULT_EPHEMERAL_TEST_QUEUE` | ER queue (localnet)               | `Sc9MJUngNbQXSXGP3F67KvKwVnhaYn6kcioxXNVowYT`  |

      Pass the queue that matches where your transaction runs as `oracle_queue`. Mainnet and Devnet share the same queue addresses; localnet uses the test queues.
    </Note>

    > `Request Randomness` is the process of generating a random `hashId` with the relevant callback instruction for the verified oracles to be triggered.

    > `Consume Randomness` is the process of using the verifiable randomness by your program which is provided and triggered through verified oracle.

    [⬆️ Back to Top](#code-snippets)
  </Tab>

  <Tab title="3. Deploy">
    Now you’re program is upgraded and ready! Build and deploy to the desired
    cluster:

    ```bash theme={null}
    anchor build && anchor deploy
    ```

    [⬆️ Back to Top](#code-snippets)
  </Tab>

  <Tab title="4. Test">
    Ready to execute transactions for onchain randomness!

    ```bash theme={null}
    anchor test --skip-build --skip-deploy --skip-local-validator
    ```

    <Note>
      **VRF SDK constants** (`ephemeral_rollups_sdk::vrf::consts`) — reference these instead of hardcoding addresses, in both your program and client/test code:

      | Constant                       | Purpose                           | Address                                        |
      | ------------------------------ | --------------------------------- | ---------------------------------------------- |
      | `VRF_PROGRAM_ID`               | VRF program                       | `Vrf1RNUjXmQGjmQrQLvJHs9SNkvDJEsRVFPkfSQUwGz`  |
      | `VRF_PROGRAM_IDENTITY`         | Callback signer PDA               | `9irBy75QS2BN81FUgXuHcjqceJJRuc9oDkAe8TKVvvAw` |
      | `DEFAULT_QUEUE`                | Base-layer queue (mainnet/devnet) | `Cuj97ggrhhidhbu39TijNVqE74xvKJ69gDervRUXAxGh` |
      | `DEFAULT_EPHEMERAL_QUEUE`      | ER queue (mainnet/devnet)         | `5hBR571xnXppuCPveTrctfTU7tJLSN94nq7kv7FRK5Tc` |
      | `DEFAULT_TEST_QUEUE`           | Base-layer queue (localnet)       | `GKE6d7iv8kCBrsxr78W3xVdjGLLLJnxsGiuzrsZCGEvb` |
      | `DEFAULT_EPHEMERAL_TEST_QUEUE` | ER queue (localnet)               | `Sc9MJUngNbQXSXGP3F67KvKwVnhaYn6kcioxXNVowYT`  |

      Pass the queue that matches where your transaction runs as `oracle_queue`. Mainnet and Devnet share the same queue addresses; localnet uses the test queues.
    </Note>

    Run the following test:

    ```typescript theme={null}
    import * as anchor from "@coral-xyz/anchor";
    import { Program, web3 } from "@coral-xyz/anchor";
    import { RandomDice } from "../target/types/random_dice";
    import { PublicKey } from "@solana/web3.js";

    // Devnet base-layer VRF queue (override with VRF_BASE_QUEUE for local runs)
    const DEFAULT_BASE_QUEUE = new PublicKey(
      process.env.VRF_BASE_QUEUE || "Cuj97ggrhhidhbu39TijNVqE74xvKJ69gDervRUXAxGh",
    );

    describe("roll-dice", () => {
      anchor.setProvider(anchor.AnchorProvider.env());
      const provider = anchor.getProvider() as anchor.AnchorProvider;
      const program = anchor.workspace.RandomDice as Program<RandomDice>;

      const playerPda = web3.PublicKey.findProgramAddressSync(
        [Buffer.from("playerd"), provider.publicKey!.toBytes()],
        program.programId,
      )[0];

      it("Initialized player!", async () => {
        const tx = await program.methods
          .initialize()
          .rpc({ skipPreflight: true, commitment: "confirmed" });
        console.log("Your transaction signature", tx);
      });

      it("Do Roll Dice!", async function () {
        // The base-chain callback can take up to 10s, so raise Mocha's timeout.
        this.timeout(20_000);

        // Generate the seed BEFORE subscribing so the handler closes over it.
        // The program logs "client_seed=N" inside callback_roll_dice — we match
        // on that exact substring to pin the callback to our specific request.
        const clientSeed = Math.floor(Math.random() * 256);
        const seedTag = `client_seed=${clientSeed}`;

        // Pre-arm a one-shot promise that the onLogs handler resolves with the
        // matching signature. No polling — we just await it, racing a timeout.
        let resolveSig!: (sig: string) => void;
        const sigPromise = new Promise<string>((r) => {
          resolveSig = r;
        });
        const callbackSubId = provider.connection.onLogs(
          program.programId,
          (info) => {
            if (
              !info.err &&
              info.logs.some((l) => l.includes("CallbackRollDice")) &&
              info.logs.some((l) => l.includes(seedTag))
            ) {
              resolveSig(info.signature);
            }
          },
          "confirmed",
        );

        try {
          const tx = await program.methods
            .rollDice(clientSeed)
            .accounts({ oracleQueue: DEFAULT_BASE_QUEUE })
            .rpc({ skipPreflight: true, commitment: "confirmed" });
          console.log("rollDice tx:", tx);

          // Base-chain VRF response is slower than ER (~1-5s typical) so 10s timeout.
          const sig = await Promise.race([
            sigPromise,
            new Promise<null>((r) => setTimeout(() => r(null), 10_000)),
          ]);
          if (!sig) throw new Error("callbackRollDice not observed within 10s.");
          console.log("callbackRollDice tx:", sig);

          const player = await program.account.player.fetch(playerPda, "processed");
          console.log("player:", player);
        } finally {
          await provider.connection.removeOnLogsListener(callbackSubId);
        }
      });
    });
    ```

    [⬆️ Back to Top](#code-snippets)
  </Tab>
</Tabs>

***

<Note>
  Want to run VRF end to end on your machine? Use the <a href="/pages/ephemeral-rollups-ers/how-to-guide/local-development">Local Development</a> guide for the fully local stack, the Surfpool alternative, and the local <code>vrf-oracle</code> flow.
</Note>

***

## Solana Explorer

Get insights about your transactions and accounts on Solana:

<CardGroup cols={2}>
  <Card title="Solana Explorer" icon="search" href="https://explorer.solana.com/" iconType="duotone">
    Official Solana Explorer
  </Card>

  <Card title="Solscan" icon="searchengin" href="https://solscan.io/" iconType="duotone">
    Explore Solana Blockchain
  </Card>
</CardGroup>

## Solana RPC Providers

Send transactions and requests through existing RPC providers:

<CardGroup cols={2}>
  <Card title="Solana" icon="star" href="https://solana.com/docs/references/clusters#on-a-high-level" iconType="duotone">
    Free Public Nodes
  </Card>

  <Card title="Helius" icon="sun" href="https://www.helius.dev/solana-rpc-nodes" iconType="duotone">
    Free Shared Nodes
  </Card>

  <Card title="Triton" icon="crystal-ball" href="https://triton.one/solana" iconType="duotone">
    Dedicated High-Performance Nodes
  </Card>
</CardGroup>

## Solana Validator Dashboard

Find real-time updates on Solana's validator infrastructure:

<CardGroup cols={2}>
  <Card title="Solana Beach" icon="wave" href="https://solanabeach.io/" iconType="duotone">
    Get Validator Insights
  </Card>

  <Card title="Validators App" icon="cloud-binary" href="https://www.validators.app/" iconType="duotone">
    Discover Validator Metrics
  </Card>
</CardGroup>

## Server Status Subscriptions

Subscribe to Solana's and MagicBlock's server status:

<CardGroup cols={2}>
  <Card title="Solana Status" icon="server" href="https://status.solana.com/" iconType="duotone">
    Subscribe to Solana Server Updates
  </Card>

  <Card title="MagicBlock Status" icon="heart-pulse" href="/pages/overview/additional-information/system-status" iconType="duotone">
    Subscribe to MagicBlock Server Status
  </Card>
</CardGroup>

***

## MagicBlock Products

<CardGroup cols={2}>
  <Card title="Ephemeral Rollup (ER)" icon="bolt" href="/pages/ephemeral-rollups-ers/how-to-guide/quickstart" iconType="duotone">
    Execute real-time, zero-fee transactions securely on Solana.
  </Card>

  <Card title="Private Ephemeral Rollup (PER)" icon="shield-check" href="/pages/private-ephemeral-rollups-pers/how-to-guide/quickstart" iconType="duotone">
    Protect sensitive data with compliance — built on top of Ephemeral Rollups.
  </Card>

  <Card title="Ephemeral SPL Token" icon="coins" href="/pages/ephemeral-spl-token/overview" iconType="duotone">
    Move SPL tokens at rollup speed — public or private transfers, swaps, and private payments for trading and DeFi apps.
  </Card>

  <Card title="Prediction Markets & Trading" icon="chart-line" href="/pages/solutions/prediction-markets" iconType="duotone">
    Combine real-time execution, session keys, token custody, price feeds, automation, and settlement.
  </Card>

  <Card title="Solana VRF" icon="dice" href="/pages/verifiable-randomness-functions-vrfs/introduction/solana-vrf" iconType="duotone">
    Add provably fair onchain randomness to games, raffles, and real-time apps.
  </Card>

  <Card title="Pricing Oracle" icon="waveform" href="/pages/tools/oracle/introduction" iconType="duotone">
    Access low-latency onchain price feeds for trading and DeFi.
  </Card>
</CardGroup>

***
