Skip to main content

Quick Access

Check out example:

GitHub

Anchor Implementation

dApp (Coming soon!)

Play Now
MagicBlock’s Private Ephemeral Rollup enforces compliance based on node-level IP geofencing, OFAC-sanction list and restricted jurisdictions at ingress, before any transaction is accepted or executed. Find out more

Step-By-Step Guide

Build your program and upgrade it with permission and delegation hooks that utilize MagicBlock’s Permission Program ACLseoPoyC3cBqoUtkbjZ4aDrkurZW86v19pXz2XQnp1 and Delegation Program DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh:
1

Write your program

Write your Solana program as you normally.
2

Add restrictions through permission hooks in your program.

Add permission hooks that create and manage access control, see details.
3

Add delegation hooks in your program

Add delegation hooks for permissions and permissioned accounts to enforce restrictions.

These public validators are supported for development. Make sure to add the specific ER validator in your delegation instruction:

Mainnet
  • Asia (as.magicblock.app): MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57
  • EU (eu.magicblock.app): MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e
  • US (us.magicblock.app): MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd
  • TEE (mainnet-tee.magicblock.app): MTEWGuqxUpYZGFJQcp8tLN7x5v9BSeoFHYWQQ3n3xzo
Devnet
  • Asia (devnet-as.magicblock.app): MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57
  • EU (devnet-eu.magicblock.app): MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e
  • US (devnet-us.magicblock.app): MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd
  • TEE (devnet-tee.magicblock.app): FnE6VJT5QNZdedZPnCoLsARgBwoE6DeJNjBs2H1gySXA
Localnet
  • Local ER (localhost:7799): mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev
4

Deploy your program on Solana

Deploy your Solana program using Anchor CLI.
5

Implement authorization in your client

Sign user message to retrieve authorization token from TEE endpoint.
6

Execute transactions and test privacy

Request for authorization token and send confidential transactions.

Rock-Paper-Scissor Example

The following software packages may be required, other versions may also be compatible:
SoftwareVersionInstallation Guide
Solana2.3.13Install Solana
Rust1.85.0Install Rust
Anchor0.32.1Install Anchor
Node24.10.0Install Node
The latest Permission Program requires SDK version >=0.8.0. See migration guide for details.

Code Snippets

A simple rock-paper-scissor program where player can make choices and reveal the outcome:
#[program]
pub mod anchor_rock_paper_scissor {

    use super::*;

    // 1️⃣ Create and auto-join as Player 1
    pub fn create_game(ctx: Context<CreateGame>, game_id: u64) -> Result<()> {
        let game = &mut ctx.accounts.game;
        let player1 = ctx.accounts.player1.key();

        game.game_id = game_id;
        game.player1 = Some(player1);
        game.player2 = None;
        game.result = GameResult::None;

        msg!("Game ID: {}", game_id);
        msg!("Player 1 PDA: {}", player1);

        // initialize PlayerChoice for player 1
        let player_choice = &mut ctx.accounts.player_choice;
        player_choice.game_id = game_id;
        player_choice.player = player1;
        player_choice.choice = None;

        msg!("Game {} created and joined by {}", game_id, player1);

        Ok(())
    }

    // 2️⃣ Player 2 joins the game
    pub fn join_game(ctx: Context<JoinGame>, game_id: u64) -> Result<()> {
        let game = &mut ctx.accounts.game;
        let player = ctx.accounts.player.key();

        require!(game.player1 != Some(player), GameError::CannotJoinOwnGame);
        require!(game.player2.is_none(), GameError::GameFull);

        game.player2 = Some(player);

        // Create PlayerChoice PDA for player 2
        let player_choice = &mut ctx.accounts.player_choice;
        player_choice.game_id = game_id;
        player_choice.player = player;
        player_choice.choice = None;

        msg!("{} joined Game {} as player 2", player, game_id);
        Ok(())
    }

    // 3️⃣ Player makes a choice
    pub fn make_choice(ctx: Context<MakeChoice>, _game_id: u64, choice: Choice) -> Result<()> {
        let player_choice = &mut ctx.accounts.player_choice;
        require!(player_choice.choice.is_none(), GameError::AlreadyChose);

        player_choice.choice = choice.into();
        msg!(
            "Player {:?} made choice {:?}",
            player_choice.player,
            player_choice.choice
        );

        Ok(())
    }

    // 4️⃣ Reveal and record the winner
    pub fn reveal_winner(ctx: Context<RevealWinner>) -> Result<()> {
        let game = &mut ctx.accounts.game;
        let player1_choice = &ctx.accounts.player1_choice;
        let player2_choice = &ctx.accounts.player2_choice;

        // 1️⃣ Clone choices into game
        game.player1_choice = player1_choice.choice.clone().into();
        game.player2_choice = player2_choice.choice.clone().into();

        // 2️⃣ Ensure both players exist
        let player1 = game.player1.ok_or(GameError::MissingOpponent)?;
        let player2 = game.player2.ok_or(GameError::MissingOpponent)?;

        // 3️⃣ Ensure both players made a choice
        let choice1 = game
            .player1_choice
            .clone()
            .ok_or(GameError::MissingChoice)?;
        let choice2 = game
            .player2_choice
            .clone()
            .ok_or(GameError::MissingChoice)?;

        // 4️⃣ Determine winner based on choices
        game.result = match (choice1, choice2) {
            (Choice::Rock, Choice::Scissors)
            | (Choice::Paper, Choice::Rock)
            | (Choice::Scissors, Choice::Paper) => GameResult::Winner(player1),

            (Choice::Rock, Choice::Paper)
            | (Choice::Paper, Choice::Scissors)
            | (Choice::Scissors, Choice::Rock) => GameResult::Winner(player2),

            _ => GameResult::Tie,
        };

        msg!("Result: {:?}", &game.result);

        Ok(())
    }

}

#[derive(Accounts)]
#[instruction(game_id: u64)]
pub struct CreateGame<'info> {
    #[account(
        init_if_needed,
        payer = player1,
        space = 8 + Game::LEN,
        seeds = [GAME_SEED, &game_id.to_le_bytes()],
        bump
    )]
    pub game: Account<'info, Game>,

    #[account(
        init_if_needed,
        payer = player1,
        space = 8 + PlayerChoice::LEN,
        seeds = [PLAYER_CHOICE_SEED, &game_id.to_le_bytes(), player1.key().as_ref()],
        bump
    )]
    pub player_choice: Account<'info, PlayerChoice>,

    #[account(mut)]
    pub player1: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
#[instruction(game_id: u64)]
pub struct JoinGame<'info> {
    #[account(
        mut,
        seeds = [GAME_SEED, &game_id.to_le_bytes()],
        bump
    )]
    pub game: Account<'info, Game>,

    #[account(
        init_if_needed,
        payer = player,
        space = 8 + PlayerChoice::LEN,
        seeds = [PLAYER_CHOICE_SEED, &game_id.to_le_bytes(), player.key().as_ref()],
        bump
    )]
    pub player_choice: Account<'info, PlayerChoice>,

    #[account(mut)]
    pub player: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
#[instruction(game_id: u64)]
pub struct MakeChoice<'info> {
    #[account(
        mut,
        seeds = [PLAYER_CHOICE_SEED, &game_id.to_le_bytes(), player.key().as_ref()],
        bump
    )]
    pub player_choice: Account<'info, PlayerChoice>,

    #[account(mut)]
    pub player: Signer<'info>,
}

#[derive(Accounts)]
pub struct RevealWinner<'info> {
    #[account(mut, seeds = [GAME_SEED, &game.game_id.to_le_bytes()], bump)]
    pub game: Account<'info, Game>,

    /// Player1's choice PDA (derived automatically)
    #[account(
        mut,
        seeds = [PLAYER_CHOICE_SEED, &game.game_id.to_le_bytes(), game.player1.unwrap().as_ref()],
        bump
    )]
    pub player1_choice: Account<'info, PlayerChoice>,

    /// Player2's choice PDA (derived automatically)
    #[account(
        mut,
        seeds = [PLAYER_CHOICE_SEED, &game.game_id.to_le_bytes(), game.player2.unwrap().as_ref()],
        bump
    )]
    pub player2_choice: Account<'info, PlayerChoice>,
    #[account(mut)]
    pub payer: Signer<'info>,
}

#[account]
pub struct Game {
    pub game_id: u64,
    pub player1: Option<Pubkey>,
    pub player2: Option<Pubkey>,
    pub player1_choice: Option<Choice>,
    pub player2_choice: Option<Choice>,
    pub result: GameResult,
}
impl Game {
    pub const LEN: usize = 8                // game_id
        + (32 + 1) * 2                       // player1, player2
        + (1 + 1) * 2                        // player1_choice, player2_choice
        + (1 + 32); // result (1 byte tag + 32 bytes pubkey for Winner variant)
}

#[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq, Eq, Debug)]
pub enum GameResult {
    Winner(Pubkey),
    Tie,
    None,
}

#[account]
pub struct PlayerChoice {
    pub game_id: u64,
    pub player: Pubkey,
    pub choice: Option<Choice>,
}
impl PlayerChoice {
    pub const LEN: usize = 8 + 8 + 32 + 2;
}

#[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq, Eq, Debug)]
pub enum Choice {
    Rock,
    Paper,
    Scissors,
}

#[error_code]
pub enum GameError {
    #[msg("You already made your choice.")]
    AlreadyChose,
    #[msg("You cannot join your own game.")]
    CannotJoinOwnGame,
    #[msg("Both players must make a choice first.")]
    MissingChoice,
    #[msg("Opponent not found.")]
    MissingOpponent,
    #[msg("Game is already full.")]
    GameFull,
}

/// ... Other context and accounts for delegation and privacy

⬆️ Back to Top

Access Control

Fine-grained Access Control

On-chain Privacy

Privacy Mechanisms and Concepts

Authorization

Authorization Framework

Compliance Framework

Compliance Standards and Guidelines

Solana Explorer

Get insights about your transactions and accounts on Solana:

Solana Explorer

Official Solana Explorer

Solscan

Explore Solana Blockchain

Solana RPC Providers

Send transactions and requests through existing RPC providers:

Solana

Free Public Nodes

Helius

Free Shared Nodes

Triton

Dedicated High-Performance Nodes

Solana Validator Dashboard

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

Solana Beach

Get Validator Insights

Validators App

Discover Validator Metrics

Server Status

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

Solana Status

Subscribe to Solana Server Updates

MagicBlock Status

Subscribe to MagicBlock Server Status

MagicBlock Products

Ephemeral Rollup (ER)

Execute real-time, zero-fee transactions securely on Solana.

Private Ephemeral Rollup (PER)

Protect sensitive data with privacy-preserving computation.

Private Payment API

Make privately transfers on Solana sercurely and with compliance.

Verifiable Randomness Function (VRF)

Generate provably fair randomness directly on-chain.

Pricing Oracle

Access low-latency onchain price feeds for trading and DeFi.