Quick Access
Check out example:GitHub
Anchor Implementation
dApp (Coming soon!)
Play Now
Step-By-Step Guide
Build your program and upgrade it with permission and delegation hooks that utilize MagicBlock’s Permission ProgramACLseoPoyC3cBqoUtkbjZ4aDrkurZW86v19pXz2XQnp1 and Delegation Program DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh:
Add permission hooks that create and manage access control, see
details.
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
- Asia (devnet-as.magicblock.app):
MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57 - EU (devnet-eu.magicblock.app):
MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e - US (devnet-us.magicblock.app):
MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd - TEE (tee.magicblock.app):
FnE6VJT5QNZdedZPnCoLsARgBwoE6DeJNjBs2H1gySXA
- Local ER (localhost:7799):
mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev
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 |
| Rust | 1.85.0 | Install Rust |
| Anchor | 0.32.1 | Install Anchor |
| Node | 24.10.0 | Install Node |
The latest Permission Program requires SDK version
>=0.8.0. See
migration
guide for
details.
Code Snippets
- 1. Write program
- 2. Restrict
- 3. Delegate
- 4. Deploy
- 5. Authorize
- 6. Test
A simple rock-paper-scissor program where player can make choices and reveal the outcome:⬆️ Back to Top
Copy
Ask AI
#[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
Create and update account-level permissions with members through MagicBlock’s Permission Program ⬆️ Back to Top
ACLseoPoyC3cBqoUtkbjZ4aDrkurZW86v19pXz2XQnp1, see
details:Copy
Ask AI
#[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 ...
Enforce privacy of accounts through delegation to TEE validator via MagicBlock’s Delegation Program ⬆️ Back to Top
DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh, both permission and permissioned_account must be delegated.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
- Asia (devnet-as.magicblock.app):
MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57 - EU (devnet-eu.magicblock.app):
MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e - US (devnet-us.magicblock.app):
MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd - TEE (tee.magicblock.app):
FnE6VJT5QNZdedZPnCoLsARgBwoE6DeJNjBs2H1gySXA
- Local ER (localhost:7799):
mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev
Copy
Ask AI
#[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(),
]
}
}
}
Now you’re program is upgraded and ready! Build and deploy to the desired cluster:⬆️ Back to Top
Copy
Ask AI
anchor build && anchor deploy
Set up interaction with ER RPC in TEE:⬆️ Back to Top
- Verify integrity of TEE RPC via
https://pccs.phala.network/tdx/certification/v4 - Request an authorization token for user to interact with TEE endpoint
Web3.js
Copy
Ask AI
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))
);
Test your program with the Private Ephemeral Rollup connection:⬆️ Back to Top
https://tee.magicblock.app?token=${token}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
- Asia (devnet-as.magicblock.app):
MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57 - EU (devnet-eu.magicblock.app):
MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e - US (devnet-us.magicblock.app):
MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd - TEE (tee.magicblock.app):
FnE6VJT5QNZdedZPnCoLsARgBwoE6DeJNjBs2H1gySXA
- Local ER (localhost:7799):
mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev
Quick Access
Check out example:GitHub
Anchor Implementation
dApp (Coming soon!)
Play Now
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 RPC Providers
Send transactions and requests through existing RPC providers:Solana Validator Dashboard
Find real-time updates on Solana’s validator infrastructure: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.
Verifiable Randomness Function (VRF)
Generate provably fair randomness directly on-chain.

