메인 콘텐츠로 건너뛰기

빠른 접근

예시 보기:

GitHub

Anchor 구현

dApp(출시 예정)

지금 사용하기
MagicBlock의 Private Ephemeral Rollup은 노드 수준 IP 지오펜싱, OFAC 제재 목록, 제한된 관할권을 기준으로 컴플라이언스를 입구에서 강제하며, 어떤 트랜잭션도 수락되거나 실행되기 전에 적용합니다. 자세히 보기

단계별 가이드

MagicBlock의 Permission Program ACLseoPoyC3cBqoUtkbjZ4aDrkurZW86v19pXz2XQnp1 및 Delegation Program DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh을 활용하는 권한 및 위임 훅으로 프로그램을 확장하세요.
1

프로그램 작성

평소처럼 Solana 프로그램을 작성합니다.
2

프로그램의 권한 훅을 통해 제한 추가

접근 제어를 생성하고 관리하는 권한 훅을 추가합니다. 자세히 보기.
3

프로그램에 위임 훅 추가

권한과 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
4

Solana에 프로그램 배포

Anchor CLI를 사용해 Solana 프로그램을 배포합니다.
5

클라이언트에 인가 구현

사용자 메시지에 서명해 TEE 엔드포인트에서 인가 토큰을 가져옵니다.
6

트랜잭션 실행 및 프라이버시 테스트

인가 토큰을 요청하고 기밀 트랜잭션을 전송합니다.

가위바위보 예제

다음 소프트웨어 패키지가 필요할 수 있으며, 다른 버전도 호환될 수 있습니다.
소프트웨어버전설치 가이드
Solana2.3.13Solana 설치
Rust1.85.0Rust 설치
Anchor0.32.1Anchor 설치
Node24.10.0Node 설치
최신 Permission Program은 >=0.8.0 SDK 버전을 요구합니다. 자세한 내용은 마이그레이션 가이드를 참고하세요.

코드 스니펫

플레이어가 선택을 하고 결과를 공개하는 간단한 가위바위보 프로그램:
#[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

⬆️ 맨 위로

접근 제어

세밀한 접근 제어

온체인 프라이버시

프라이버시 메커니즘과 개념

인가

인가 프레임워크

컴플라이언스 프레임워크

컴플라이언스 기준 및 가이드라인

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를 위한 저지연 온체인 가격 피드에 접근하세요.