빠른 접근
예시 보기:GitHub
Anchor 구현
dApp(출시 예정)
지금 사용하기
MagicBlock의 Private Ephemeral Rollup은 노드 수준 IP 지오펜싱,
OFAC 제재 목록, 제한된 관할권을 기준으로 컴플라이언스를 입구에서 강제하며,
어떤 트랜잭션도 수락되거나 실행되기 전에 적용합니다.
자세히 보기
단계별 가이드
MagicBlock의 Permission ProgramACLseoPoyC3cBqoUtkbjZ4aDrkurZW86v19pXz2XQnp1 및 Delegation Program DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh을 활용하는 권한 및 위임 훅으로 프로그램을 확장하세요.
평소처럼 Solana 프로그램을 작성합니다.
접근 제어를 생성하고 관리하는 권한 훅을 추가합니다. 자세히 보기.
권한과 permissioned accounts에 대한 위임 훅을 추가해 제한을 강제합니다.
이 공용 검증자들은 개발용으로 지원됩니다. 위임 명령에 해당 ER 검증자를 반드시 추가하세요.
메인넷- 아시아 (as.magicblock.app):
MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57 - EU (eu.magicblock.app):
MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e - 미국 (us.magicblock.app):
MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd - TEE (mainnet-tee.magicblock.app):
MTEWGuqxUpYZGFJQcp8tLN7x5v9BSeoFHYWQQ3n3xzo
- 아시아 (devnet-as.magicblock.app):
MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57 - EU (devnet-eu.magicblock.app):
MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e - 미국 (devnet-us.magicblock.app):
MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd - TEE (devnet-tee.magicblock.app):
FnE6VJT5QNZdedZPnCoLsARgBwoE6DeJNjBs2H1gySXA
- 로컬 ER (localhost:7799):
mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev
Anchor CLI를 사용해 Solana 프로그램을 배포합니다.
사용자 메시지에 서명해 TEE 엔드포인트에서 인가 토큰을 가져옵니다.
인가 토큰을 요청하고 기밀 트랜잭션을 전송합니다.
가위바위보 예제
다음 소프트웨어 패키지가 필요할 수 있으며, 다른 버전도 호환될 수 있습니다.최신 Permission Program은
>=0.8.0 SDK 버전을 요구합니다.
자세한 내용은 마이그레이션 가이드를 참고하세요.
코드 스니펫
- 1. 프로그램 작성
- 2. 제한 추가
- 3. 위임
- 4. 배포
- 5. 인가
- 6. 테스트
플레이어가 선택을 하고 결과를 공개하는 간단한 가위바위보 프로그램:⬆️ 맨 위로
#[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
MagicBlock의 Permission Program ⬆️ 맨 위로
ACLseoPoyC3cBqoUtkbjZ4aDrkurZW86v19pXz2XQnp1을 통해 멤버가 포함된 계정 수준 권한을 생성하고 업데이트합니다. 자세히 보기:#[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 ...
MagicBlock의 Delegation Program ⬆️ 맨 위로
DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh을 통해 TEE 검증기에 위임하여 계정의 프라이버시를 강제합니다. permission과 permissioned_account 모두 위임되어야 합니다.이 공용 검증자들은 개발용으로 지원됩니다. 위임 명령에 해당 ER 검증자를 반드시 추가하세요.
메인넷- 아시아 (as.magicblock.app):
MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57 - EU (eu.magicblock.app):
MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e - 미국 (us.magicblock.app):
MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd - TEE (mainnet-tee.magicblock.app):
MTEWGuqxUpYZGFJQcp8tLN7x5v9BSeoFHYWQQ3n3xzo
- 아시아 (devnet-as.magicblock.app):
MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57 - EU (devnet-eu.magicblock.app):
MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e - 미국 (devnet-us.magicblock.app):
MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd - TEE (devnet-tee.magicblock.app):
FnE6VJT5QNZdedZPnCoLsARgBwoE6DeJNjBs2H1gySXA
- 로컬 ER (localhost:7799):
mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev
#[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(),
]
}
}
}
TEE 내부의 ER RPC와 상호작용을 설정합니다:⬆️ 맨 위로
https://pccs.phala.network/tdx/certification/v4를 통해 TEE RPC 무결성을 검증합니다- 사용자가 TEE 엔드포인트와 상호작용할 수 있도록 인가 토큰을 요청합니다
Web3.js
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))
);
Private Ephemeral Rollup 연결로 프로그램을 테스트합니다:⬆️ 맨 위로
https://devnet-tee.magicblock.app?token=${token}이 공용 검증자들은 개발용으로 지원됩니다. 위임 명령에 해당 ER 검증자를 반드시 추가하세요.
메인넷- 아시아 (as.magicblock.app):
MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57 - EU (eu.magicblock.app):
MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e - 미국 (us.magicblock.app):
MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd - TEE (mainnet-tee.magicblock.app):
MTEWGuqxUpYZGFJQcp8tLN7x5v9BSeoFHYWQQ3n3xzo
- 아시아 (devnet-as.magicblock.app):
MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57 - EU (devnet-eu.magicblock.app):
MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e - 미국 (devnet-us.magicblock.app):
MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd - TEE (devnet-tee.magicblock.app):
FnE6VJT5QNZdedZPnCoLsARgBwoE6DeJNjBs2H1gySXA
- 로컬 ER (localhost:7799):
mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev
빠른 접근
예시 보기:GitHub
Anchor 구현
dApp(출시 예정)
지금 사용하기
접근 제어
세밀한 접근 제어
온체인 프라이버시
프라이버시 메커니즘과 개념
인가
인가 프레임워크
컴플라이언스 프레임워크
컴플라이언스 기준 및 가이드라인
Solana 익스플로러
Solana에서의 트랜잭션과 계정 정보를 확인해 보세요.Solana 익스플로러
공식 Solana 익스플로러
Solscan
Solana 블록체인 살펴보기
Solana RPC 제공업체
기존 RPC 제공업체를 통해 트랜잭션과 요청을 전송하세요.Solana
Free Public Nodes
Helius
Free Shared Nodes
Triton
Dedicated High-Performance Nodes
Solana 검증자 대시보드
Solana 검증자 인프라의 실시간 업데이트를 확인하세요.Solana Beach
Get Validator Insights
Validators App
Discover Validator Metrics
서버 상태
Solana와 MagicBlock의 서버 상태를 확인해 보세요.Solana Status
Subscribe to Solana Server Updates
MagicBlock Status
Subscribe to MagicBlock Server Status
MagicBlock 제품
에페메럴 롤업(ER)
Solana에서 실시간 무수수료 트랜잭션을 안전하게 실행하세요.
프라이빗 에페메럴 롤업(PER)
프라이버시 보존 연산으로 민감한 데이터를 보호하세요.
프라이빗 결제 API
Solana에서 안전하고 규정을 준수하는 비공개 송금을 구현하세요.
검증 가능한 랜덤 함수(VRF)
증명 가능한 공정한 랜덤성을 온체인에서 직접 생성하세요.
가격 오라클
트레이딩과 DeFi를 위한 저지연 온체인 가격 피드에 접근하세요.

