> ## Documentation Index
> Fetch the complete documentation index at: https://magicblock-42-supermarioblock-patch-3.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Enable privacy in any Solana program state account through MagicBlock's ER and Trusted Execution Environment on Intel TDX.

***

### Quick Access

Check out example:

<CardGroup cols={2}>
  <Card title="GitHub" icon="github" href="https://github.com/magicblock-labs/magicblock-engine-examples/tree/main/anchor-rock-paper-scissor" iconType="duotone">
    Anchor Implementation
  </Card>

  <Card title="dApp (Coming soon!)" icon="shield-check" href="https://private-payments.magicblock.app" iconType="duotone" disabled>
    Play Now
  </Card>
</CardGroup>

<Note>
  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](/pages/private-ephemeral-rollups-pers/introduction/compliance-framework)
</Note>

***

## 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`:

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

  <Step
    title={
  <a href="#2-restrict">
    Add restrictions through permission hooks in your program.
  </a>
}
  >
    Add permission hooks that create and manage access control, [see
    details](/pages/private-ephemeral-rollups-pers/how-to-guide/access-control).
  </Step>

  <Step title={<a href="#3-delegate">Add delegation hooks in your program</a>}>
    Add delegation hooks for permissions and permissioned accounts to enforce
    restrictions.

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

      **Mainnet**

      <ul>
        <li>
          Asia (as.magicblock.app):{" "}
          <code>MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57</code>
        </li>

        <li>
          EU (eu.magicblock.app):{" "}
          <code>MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e</code>
        </li>

        <li>
          US (us.magicblock.app):{" "}
          <code>MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd</code>
        </li>

        <li>
          TEE (mainnet-tee.magicblock.app):{" "}
          <code>MTEWGuqxUpYZGFJQcp8tLN7x5v9BSeoFHYWQQ3n3xzo</code>
        </li>
      </ul>

      **Devnet**

      <ul>
        <li>
          Asia (devnet-as.magicblock.app):{" "}
          <code>MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57</code>
        </li>

        <li>
          EU (devnet-eu.magicblock.app):{" "}
          <code>MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e</code>
        </li>

        <li>
          US (devnet-us.magicblock.app):{" "}
          <code>MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd</code>
        </li>

        <li>
          TEE (tee.magicblock.app):{" "}
          <code>FnE6VJT5QNZdedZPnCoLsARgBwoE6DeJNjBs2H1gySXA</code>
        </li>
      </ul>

      **Localnet**

      <ul>
        <li>
          Local ER (localhost:7799):{" "}
          <code>mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev</code>
        </li>
      </ul>
    </Note>
  </Step>

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

  <Step title={<a href="#5-authorize">Implement authorization in your client</a>}>
    Sign user message to retrieve authorization token from TEE endpoint.
  </Step>

  <Step title={<a href="#6-test">Execute transactions and test privacy</a>}>
    Request for authorization token and send confidential transactions.
  </Step>
</Steps>

***

## Rock-Paper-Scissor Example

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

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

<Note>
  The latest Permission Program requires SDK version
  [>=0.8.0](https://github.com/magicblock-labs/ephemeral-rollups-sdk). See
  [migration
  guide](https://github.com/magicblock-labs/ephemeral-rollups-sdk/pull/103) for
  details.
</Note>

### Code Snippets

<Tabs>
  <Tab title="1. Write program">
    A simple rock-paper-scissor program where player can make choices and reveal the outcome:

    ```rust theme={null}
    #[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](#code-snippets)
  </Tab>

  <Tab title="2. Restrict">
    Create and update account-level permissions with members through MagicBlock's Permission Program `ACLseoPoyC3cBqoUtkbjZ4aDrkurZW86v19pXz2XQnp1`, [see
    details](/pages/private-ephemeral-rollups-pers/how-to-guide/access-control):

    ```rust theme={null}
    #[program]
    pub mod anchor_rock_paper_scissor {

        use super::*;

        // 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;
            let permission_program = &ctx.accounts.permission_program.to_account_info();
            let permission_game = &ctx.accounts.permission_game.to_account_info();
            let permission1 = &ctx.accounts.permission1.to_account_info();
            let permission2 = &ctx.accounts.permission2.to_account_info();
            let magic_program = &ctx.accounts.magic_program.to_account_info();
            let magic_context = &ctx.accounts.magic_context.to_account_info();

            // 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,
            };

            // Update Permissions for reveal
            UpdatePermissionCpiBuilder::new(&permission_program)
                .permissioned_account(&game.to_account_info(), true)
                .authority(&game.to_account_info(), false)
                .permission(&permission_game.to_account_info())
                .args(MembersArgs { members: None })
                .invoke_signed(&[&[GAME_SEED, &game.game_id.to_le_bytes(), &[ctx.bumps.game]]])?;
            UpdatePermissionCpiBuilder::new(&permission_program)
                .permissioned_account(&player1_choice.to_account_info(), true)
                .authority(&player1_choice.to_account_info(), false)
                .permission(&permission1.to_account_info())
                .args(MembersArgs { members: None })
                .invoke_signed(&[&[
                    PLAYER_CHOICE_SEED,
                    &player1_choice.game_id.to_le_bytes(),
                    &player1_choice.player.as_ref(),
                    &[ctx.bumps.player1_choice],
                ]])?;
            UpdatePermissionCpiBuilder::new(&permission_program)
                .permissioned_account(&player2_choice.to_account_info(), true)
                .authority(&player2_choice.to_account_info(), false)
                .permission(&permission2.to_account_info())
                .args(MembersArgs { members: None })
                .invoke_signed(&[&[
                    PLAYER_CHOICE_SEED,
                    &player2_choice.game_id.to_le_bytes(),
                    &player2_choice.player.as_ref(),
                    &[ctx.bumps.player2_choice],
                ]])?;

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

        /// Creates a permission based on account type input.
        /// Derives the bump from the account type and seeds, then calls the permission program.
        pub fn create_permission(
            ctx: Context<CreatePermission>,
            account_type: AccountType,
            members: Option<Vec<Member>>,
        ) -> Result<()> {
            let CreatePermission {
                permissioned_account,
                permission,
                payer,
                permission_program,
                system_program,
            } = ctx.accounts;

            let seed_data = derive_seeds_from_account_type(&account_type);

            let (_, bump) = Pubkey::find_program_address(
                &seed_data.iter().map(|s| s.as_slice()).collect::<Vec<_>>(),
                &crate::ID,
            );

            let mut seeds = seed_data.clone();
            seeds.push(vec![bump]);
            let seed_refs: Vec<&[u8]> = seeds.iter().map(|s| s.as_slice()).collect();

            CreatePermissionCpiBuilder::new(&permission_program)
                .permissioned_account(&permissioned_account.to_account_info())
                .permission(&permission)
                .payer(&payer)
                .system_program(&system_program)
                .args(MembersArgs { members })
                .invoke_signed(&[seed_refs.as_slice()])?;
            Ok(())
        }
    }

    #[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>,
        /// CHECK: Checked by the permission program
        #[account(mut)]
        pub permission_game: UncheckedAccount<'info>,
        /// CHECK: Checked by the permission program
        #[account(mut)]
        pub permission1: UncheckedAccount<'info>,
        /// CHECK: Checked by the permission program
        #[account(mut)]
        pub permission2: UncheckedAccount<'info>,
        /// Anyone can trigger this
        #[account(mut)]
        pub payer: Signer<'info>,
        /// CHECK: PERMISSION PROGRAM
        #[account(address = PERMISSION_PROGRAM_ID)]
        pub permission_program: UncheckedAccount<'info>,
    }

    #[derive(Accounts)]
    pub struct CreatePermission<'info> {
        /// CHECK: Validated via permission program CPI
        pub permissioned_account: UncheckedAccount<'info>,
        /// CHECK: Checked by the permission program
        #[account(mut)]
        pub permission: UncheckedAccount<'info>,
        #[account(mut)]
        pub payer: Signer<'info>,
        /// CHECK: PERMISSION PROGRAM
        #[account(address = PERMISSION_PROGRAM_ID)]
        pub permission_program: UncheckedAccount<'info>,
        pub system_program: Program<'info, System>,
    }

    #[derive(AnchorSerialize, AnchorDeserialize, Clone)]
    pub enum AccountType {
        Game { game_id: u64 },
        PlayerChoice { game_id: u64, player: Pubkey },
    }

    fn derive_seeds_from_account_type(account_type: &AccountType) -> Vec<Vec<u8>> {
        match account_type {
            AccountType::Game { game_id } => {
                vec![GAME_SEED.to_vec(), game_id.to_le_bytes().to_vec()]
            }
            AccountType::PlayerChoice { game_id, player } => {
                vec![
                    PLAYER_CHOICE_SEED.to_vec(),
                    game_id.to_le_bytes().to_vec(),
                    player.to_bytes().to_vec(),
                ]
            }
        }
    }

    /// Other context and accounts ...
    ```

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

  <Tab title="3. Delegate">
    Enforce privacy of accounts through delegation to TEE validator via MagicBlock's Delegation Program `DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh`, both permission and permissioned\_account must be delegated.

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

      **Mainnet**

      <ul>
        <li>
          Asia (as.magicblock.app):{" "}
          <code>MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57</code>
        </li>

        <li>
          EU (eu.magicblock.app):{" "}
          <code>MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e</code>
        </li>

        <li>
          US (us.magicblock.app):{" "}
          <code>MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd</code>
        </li>

        <li>
          TEE (mainnet-tee.magicblock.app):{" "}
          <code>MTEWGuqxUpYZGFJQcp8tLN7x5v9BSeoFHYWQQ3n3xzo</code>
        </li>
      </ul>

      **Devnet**

      <ul>
        <li>
          Asia (devnet-as.magicblock.app):{" "}
          <code>MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57</code>
        </li>

        <li>
          EU (devnet-eu.magicblock.app):{" "}
          <code>MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e</code>
        </li>

        <li>
          US (devnet-us.magicblock.app):{" "}
          <code>MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd</code>
        </li>

        <li>
          TEE (tee.magicblock.app):{" "}
          <code>FnE6VJT5QNZdedZPnCoLsARgBwoE6DeJNjBs2H1gySXA</code>
        </li>
      </ul>

      **Localnet**

      <ul>
        <li>
          Local ER (localhost:7799):{" "}
          <code>mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev</code>
        </li>
      </ul>
    </Note>

    ```rust theme={null}
    #[ephemeral] // This adds undelegation instruction for ER validator
    #[program]
    pub mod anchor_rock_paper_scissor {

        use super::*;

        // Other instructions

        // 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;
            let permission_program = &ctx.accounts.permission_program.to_account_info();
            let permission_game = &ctx.accounts.permission_game.to_account_info();
            let permission1 = &ctx.accounts.permission1.to_account_info();
            let permission2 = &ctx.accounts.permission2.to_account_info();
            let magic_program = &ctx.accounts.magic_program.to_account_info();
            let magic_context = &ctx.accounts.magic_context.to_account_info();

            // 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,
            };

            UpdatePermissionCpiBuilder::new(&permission_program)
                .permissioned_account(&game.to_account_info(), true)
                .authority(&game.to_account_info(), false)
                .permission(&permission_game.to_account_info())
                .args(MembersArgs { members: None })
                .invoke_signed(&[&[GAME_SEED, &game.game_id.to_le_bytes(), &[ctx.bumps.game]]])?;
            UpdatePermissionCpiBuilder::new(&permission_program)
                .permissioned_account(&player1_choice.to_account_info(), true)
                .authority(&player1_choice.to_account_info(), false)
                .permission(&permission1.to_account_info())
                .args(MembersArgs { members: None })
                .invoke_signed(&[&[
                    PLAYER_CHOICE_SEED,
                    &player1_choice.game_id.to_le_bytes(),
                    &player1_choice.player.as_ref(),
                    &[ctx.bumps.player1_choice],
                ]])?;
            UpdatePermissionCpiBuilder::new(&permission_program)
                .permissioned_account(&player2_choice.to_account_info(), true)
                .authority(&player2_choice.to_account_info(), false)
                .permission(&permission2.to_account_info())
                .args(MembersArgs { members: None })
                .invoke_signed(&[&[
                    PLAYER_CHOICE_SEED,
                    &player2_choice.game_id.to_le_bytes(),
                    &player2_choice.player.as_ref(),
                    &[ctx.bumps.player2_choice],
                ]])?;

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

            game.exit(&crate::ID)?;

            commit_and_undelegate_accounts(
                &ctx.accounts.payer,
                vec![&game.to_account_info()],
                magic_context,
                magic_program,
            )?;

            Ok(())
        }

        /// Delegate account to the delegation program based on account type
        /// Set specific validator based on ER, see https://docs.magicblock.gg/pages/get-started/how-integrate-your-program/local-setup
        pub fn delegate_pda(ctx: Context<DelegatePda>, account_type: AccountType) -> Result<()> {
            let seed_data = derive_seeds_from_account_type(&account_type);
            let seeds_refs: Vec<&[u8]> = seed_data.iter().map(|s| s.as_slice()).collect();

            let validator = ctx.accounts.validator.as_ref().map(|v| v.key());
            ctx.accounts.delegate_pda(
                &ctx.accounts.payer,
                &seeds_refs,
                DelegateConfig {
                    validator,
                    ..Default::default()
                },
            )?;
            Ok(())
        }
    }

    #[commit] // adding magic_context and magic_program accounts to context
    #[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>,
        /// CHECK: Checked by the permission program
        #[account(mut)]
        pub permission_game: UncheckedAccount<'info>,
        /// CHECK: Checked by the permission program
        #[account(mut)]
        pub permission1: UncheckedAccount<'info>,
        /// CHECK: Checked by the permission program
        #[account(mut)]
        pub permission2: UncheckedAccount<'info>,
        /// Anyone can trigger this
        #[account(mut)]
        pub payer: Signer<'info>,
        /// CHECK: PERMISSION PROGRAM
        #[account(address = PERMISSION_PROGRAM_ID)]
        pub permission_program: UncheckedAccount<'info>,
    }

    /// Unified delegate PDA context
    #[delegate] // enable delegation
    #[derive(Accounts)]
    pub struct DelegatePda<'info> {
        /// CHECK: The PDA to delegate
        #[account(mut, del)]
        pub pda: AccountInfo<'info>,
        pub payer: Signer<'info>,
        /// CHECK: Checked by the delegate program
        pub validator: Option<AccountInfo<'info>>,
    }

    #[derive(AnchorSerialize, AnchorDeserialize, Clone)]
    pub enum AccountType {
        Game { game_id: u64 },
        PlayerChoice { game_id: u64, player: Pubkey },
    }

    fn derive_seeds_from_account_type(account_type: &AccountType) -> Vec<Vec<u8>> {
        match account_type {
            AccountType::Game { game_id } => {
                vec![GAME_SEED.to_vec(), game_id.to_le_bytes().to_vec()]
            }
            AccountType::PlayerChoice { game_id, player } => {
                vec![
                    PLAYER_CHOICE_SEED.to_vec(),
                    game_id.to_le_bytes().to_vec(),
                    player.to_bytes().to_vec(),
                ]
            }
        }
    }

    ```

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

  <Tab title="4. 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="5. Authorize">
    Set up interaction with ER RPC in TEE:

    1. Verify integrity of TEE RPC via `https://pccs.phala.network/tdx/certification/v4`
    2. Request an authorization token for user to interact with TEE endpoint

    ```typescript Web3.js theme={null}
    import {
      verifyTeeRpcIntegrity,
      getAuthToken,
    } from "@magicblock-labs/ephemeral-rollups-sdk";

    // Verify the integrity of TEE RPC
    const isVerified = await verifyTeeRpcIntegrity(EPHEMERAL_RPC_URL);

    // Get AuthToken before making request to TEE
    const token = await getAuthToken(
      EPHEMERAL_RPC_URL,
      wallet.publicKey,
      (message: Uint8Array) =>
        Promise.resolve(nacl.sign.detached(message, wallet.secretKey))
    );
    ```

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

  <Tab title="6. Test">
    Test your program with the Private Ephemeral Rollup connection:

    `https://tee.magicblock.app?token=${token}`

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

      **Mainnet**

      <ul>
        <li>
          Asia (as.magicblock.app):{" "}
          <code>MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57</code>
        </li>

        <li>
          EU (eu.magicblock.app):{" "}
          <code>MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e</code>
        </li>

        <li>
          US (us.magicblock.app):{" "}
          <code>MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd</code>
        </li>

        <li>
          TEE (mainnet-tee.magicblock.app):{" "}
          <code>MTEWGuqxUpYZGFJQcp8tLN7x5v9BSeoFHYWQQ3n3xzo</code>
        </li>
      </ul>

      **Devnet**

      <ul>
        <li>
          Asia (devnet-as.magicblock.app):{" "}
          <code>MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57</code>
        </li>

        <li>
          EU (devnet-eu.magicblock.app):{" "}
          <code>MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e</code>
        </li>

        <li>
          US (devnet-us.magicblock.app):{" "}
          <code>MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd</code>
        </li>

        <li>
          TEE (tee.magicblock.app):{" "}
          <code>FnE6VJT5QNZdedZPnCoLsARgBwoE6DeJNjBs2H1gySXA</code>
        </li>
      </ul>

      **Localnet**

      <ul>
        <li>
          Local ER (localhost:7799):{" "}
          <code>mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev</code>
        </li>
      </ul>
    </Note>

    ### Quick Access

    Check out example:

    <CardGroup cols={2}>
      <Card title="GitHub" icon="github" href="https://github.com/magicblock-labs/magicblock-engine-examples/tree/main/anchor-rock-paper-scissor" iconType="duotone">
        Anchor Implementation
      </Card>

      <Card title="dApp (Coming soon!)" icon="shield-check" href="https://private-payments.magicblock.app" iconType="duotone" disabled>
        Play Now
      </Card>
    </CardGroup>

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

***

<CardGroup cols={2}>
  <Card title="Access Control" icon="lock" href="/pages/private-ephemeral-rollups-pers/how-to-guide/access-control" iconType="duotone">
    Fine-grained Access Control
  </Card>

  <Card title="On-chain Privacy" icon="shield" href="/pages/private-ephemeral-rollups-pers/introduction/onchain-privacy" iconType="duotone">
    Privacy Mechanisms and Concepts
  </Card>

  <Card title="Authorization" icon="key" href="/pages/private-ephemeral-rollups-pers/introduction/authorization" iconType="duotone">
    Authorization Framework
  </Card>

  <Card title="Compliance Framework" icon="certificate" href="/pages/private-ephemeral-rollups-pers/introduction/compliance-framework" iconType="duotone">
    Compliance Standards and Guidelines
  </Card>
</CardGroup>

***

## 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

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/server-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 privacy-preserving computation.
  </Card>

  <Card title="Verifiable Randomness Function (VRF)" icon="dice" href="/pages/verifiable-randomness-functions-vrfs/how-to-guide/quickstart" iconType="duotone">
    Generate provably fair randomness directly on-chain.
  </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>

***
