멤버 플래그는 각 멤버에 대한 세밀한 권한을 정의합니다. 플래그는 비트 OR로 결합해 여러 권한을 동시에 부여할 수 있습니다.플래그 설명:
AUTHORITY: 멤버가 permission 설정을 업데이트하고 위임하며, 다른 멤버를 추가/제거하고 멤버 플래그를 업데이트할 수 있게 합니다.
TX_LOGS: 멤버가 트랜잭션 실행 로그를 볼 수 있게 합니다.
TX_BALANCES: 멤버가 계정 잔액 변화를 볼 수 있게 합니다.
TX_MESSAGE: 멤버가 트랜잭션 메시지 데이터를 볼 수 있게 합니다.
ACCOUNT_SIGNATURES: 멤버가 계정 서명을 볼 수 있게 합니다
Rust SDK
Pinocchio
Web3.js
Kit
use ephemeral_rollups_sdk::access_control::structs::{ Member, AUTHORITY_FLAG, TX_LOGS_FLAG, TX_BALANCES_FLAG, TX_MESSAGE_FLAG, ACCOUNT_SIGNATURES_FLAG,};// Set flags by combining them with bitwise ORlet flags = AUTHORITY_FLAG | TX_LOGS_FLAG;// Create a member with combined flagslet mut member = Member { flags, pubkey: user_pubkey,};// Check if member has a specific flag using bitwise ANDlet is_authority = (member.flags & AUTHORITY_FLAG) != 0;let can_see_logs = (member.flags & TX_LOGS_FLAG) != 0;// Use helper methods to set/remove flagsmember.set_flags(TX_BALANCES_FLAG); // Add a flagmember.remove_flags(TX_LOGS_FLAG); // Remove a flag
use ephemeral_rollups_pinocchio::types::{Member, MemberFlags};use pinocchio::Address;// Create and set flags using individual methodslet mut flags = MemberFlags::new();flags.set(MemberFlags::AUTHORITY);flags.set(MemberFlags::TX_LOGS);flags.set(MemberFlags::TX_BALANCES);// Create a member with flagslet member = Member { flags, pubkey: user_address,};// Remove a flagflags.remove(MemberFlags::TX_LOGS);// Create flags from individual boolean valueslet flags = MemberFlags::from_acl_flags( true, // authority true, // tx_logs false, // tx_balances true, // tx_message false, // account_signatures);// Convert flags to byte valuelet flag_byte = flags.to_acl_flag_byte();// Create flags from byte valuelet flags = MemberFlags::from_acl_flag_byte(flag_byte);
import { PublicKey } from "@solana/web3.js";import { AUTHORITY_FLAG, TX_LOGS_FLAG, TX_BALANCES_FLAG, TX_MESSAGE_FLAG, ACCOUNT_SIGNATURES_FLAG, type Member,} from "@magicblock-labs/ephemeral-rollups-sdk";// Set flags by combining them with bitwise ORconst flags = AUTHORITY_FLAG | TX_LOGS_FLAG;// Create a member with combined flagsconst member: Member = { flags, pubkey: new PublicKey(userAddress),};// Check if a flag is present using bitwise ANDconst isAuthority = (member.flags & AUTHORITY_FLAG) !== 0;const canSeeLogs = (member.flags & TX_LOGS_FLAG) !== 0;const canSeeBalances = (member.flags & TX_BALANCES_FLAG) !== 0;// Add a flag to existing flagsconst updatedFlags = member.flags | TX_BALANCES_FLAG;// Remove a flag from existing flagsconst removedFlags = member.flags & ~TX_LOGS_FLAG;
import { AUTHORITY_FLAG, TX_LOGS_FLAG, TX_BALANCES_FLAG, TX_MESSAGE_FLAG, ACCOUNT_SIGNATURES_FLAG, isAuthority, canSeeTxLogs, canSeeTxBalances, canSeeTxMessages, canSeeAccountSignatures, type Member,} from "@magicblock-labs/ephemeral-rollups-sdk";// Set flags by combining them with bitwise ORconst flags = AUTHORITY_FLAG | TX_LOGS_FLAG | TX_BALANCES_FLAG;// Create a member with combined flagsconst member: Member = { flags, pubkey: userAddress,};// Use helper functions to check specific permissionsconst canModifyPermission = isAuthority(member, userAddress);const canViewLogs = canSeeTxLogs(member, userAddress);const canViewBalances = canSeeTxBalances(member, userAddress);const canViewMessages = canSeeTxMessages(member, userAddress);const canViewSignatures = canSeeAccountSignatures(member, userAddress);// Add a flag to existing memberconst updatedFlags = member.flags | TX_MESSAGE_FLAG;// Remove a flag from existing memberconst removedFlags = member.flags & ~TX_LOGS_FLAG;
EphemeralPermission 계정은 전적으로 Ephemeral Rollup 위에서 동작하며, 렌트는
위임된 PDA가 부담합니다 — Base Layer에 permission account를 생성하거나 위임하거나
commit-and-undelegate할 필요가 없습니다. 전체 라이프사이클은 Create、Update、
Close 세 가지 CPI 작업으로 충분히 다룰 수 있으며, 모두 ER 위의 데이터 계정이
PDA로 서명하고 MagicBlock의 Permission Program
ACLseoPoyC3cBqoUtkbjZ4aDrkurZW86v19pXz2XQnp1을 통해 실행됩니다.
전제 조건 — 데이터 PDA 위임. 위임 대상은 데이터 계정뿐입니다(Base Layer에서
MagicBlock의 Delegation Program DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh을 통해).
위임 후 PDA는 자신의 프로그램 시드로 ER에서 세 가지 permission 작업 모두에 서명하고
ephemeral permission 렌트를 지불합니다 — 따라서 initialize 시점에 미리
충전해 두어야 합니다. 전체 흐름은
퀵스타트
를 참조하세요.
이 공용 검증자들은 개발용으로 지원됩니다. 위임 명령에 해당 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):
MTEWGuqxUpYZGFJQcp8tLN7x5v9BSeoFHYWQQ3n3xzo
로컬넷
로컬 ER (localhost:7799):
mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev
CreateEphemeralPermissionCpi로 ER 위에 새로운 EphemeralPermission 계정을
초기화합니다. Payer = 위임된 데이터 PDA로, 자신의 프로그램 시드로 서명하고
initialize 시점에 미리 충전된 lamports로 렌트를 지불합니다.
Anchor
Rust SDK
Pinocchio
Kit
Web3.js
use ephemeral_rollups_sdk::access_control::{ instructions::CreateEphemeralPermissionCpi, structs::{EphemeralMembersArgs, Member},};// Counter PDA pays for its own permission rent (it carries lamports onto the ER// after delegation and signs as PDA via seeds).let signers = [ COUNTER_SEED, ctx.accounts.counter.authority.as_ref(), &[ctx.bumps.counter],];CreateEphemeralPermissionCpi { payer: ctx.accounts.counter.to_account_info(), // pays ephemeral rent permissioned_account: ctx.accounts.counter.to_account_info(), // what the permission gates permission: ctx.accounts.permission.to_account_info(), vault: ctx.accounts.ephemeral_vault.to_account_info(), magic_program: ctx.accounts.magic_program.to_account_info(), permission_program: ctx.accounts.permission_program.to_account_info(), args: EphemeralMembersArgs { is_private: false, // start public — flip via UpdateEphemeralPermission members: vec![], },}.invoke_signed(&[&signers])?;
use ephemeral_rollups_sdk::access_control::{ instructions::CreateEphemeralPermissionCpi, structs::{EphemeralMembersArgs, Member},};// `permissioned_account` (here a counter PDA) signs as PDA via seeds; pass the// same seeds you used for `find_program_address` to derive it.let seeds: &[&[u8]] = &[ COUNTER_SEED, permissioned_account_authority.as_ref(), &[bump],];CreateEphemeralPermissionCpi { payer: &counter_account_info, // pays ephemeral rent permissioned_account: &counter_account_info, // what the permission gates permission: &permission_account_info, vault: &ephemeral_vault_account_info, magic_program: &magic_program_account_info, permission_program: &permission_program_account_info, args: EphemeralMembersArgs { is_private: false, // start public — flip via UpdateEphemeralPermission members: vec![], },}.invoke_signed(&[seeds])?;
use ephemeral_rollups_pinocchio::acl::{ CreateEphemeralPermission, EphemeralMembersArgs, Member,};use pinocchio::cpi::{Seed, Signer};// Buffer size: discriminator (8) + EphemeralMembersArgs body.// 64 bytes covers up to 1 member with slack for future Update calls.const PERMISSION_CPI_BUF: usize = 64;// PDA-signed CPI — the counter PDA pays rent and authorizes the permission.let bump_seed = [bump];let seeds_array: [Seed; 3] = [ Seed::from(b"counter"), Seed::from(authority.address().as_ref()), Seed::from(&bump_seed),];let signer = Signer::from(&seeds_array);let members: [Member; 0] = []; // start public; toggle via UpdateCreateEphemeralPermission { payer: counter_account, permissioned_account: counter_account, permission, vault, magic_program, permission_program, args: EphemeralMembersArgs { is_private: false, members: &members, },}.invoke_signed::<PERMISSION_CPI_BUF>(&[signer])?;
import { MAGIC_PROGRAM_ID, PERMISSION_PROGRAM_ID, EPHEMERAL_VAULT_ID,} from "@magicblock-labs/ephemeral-rollups-sdk";import { pipe, createTransactionMessage, appendTransactionMessageInstructions } from "@solana/kit";// EphemeralPermissions are created on the ER by the delegated PDA (via the// user-program's wrapper instruction). Submit to the ER connection, not base.const initIx = await counterProgram.methods .initPermission() .accountsPartial({ authority: tempKeypair.address, counter: counterPda, permission: permissionPda, permissionProgram: PERMISSION_PROGRAM_ID, ephemeralVault: EPHEMERAL_VAULT_ID, magicProgram: MAGIC_PROGRAM_ID, }) .instruction();const transactionMessage = pipe( createTransactionMessage({ version: 0 }), (tx) => appendTransactionMessageInstructions([initIx], tx),);const sig = await ephemeralConnection.sendAndConfirmTransaction( transactionMessage, [tempKeypair], { commitment: "confirmed" },);console.log("init_permission tx:", sig);
import { MAGIC_PROGRAM_ID, PERMISSION_PROGRAM_ID, EPHEMERAL_VAULT_ID,} from "@magicblock-labs/ephemeral-rollups-sdk";import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js";// EphemeralPermissions are created on the ER by the delegated PDA (via the// user-program's wrapper instruction). Submit to the ER connection, not base.const initIx = await counterProgram.methods .initPermission() .accountsPartial({ authority: tempKeypair.publicKey, counter: counterPda, permission: permissionPda, permissionProgram: PERMISSION_PROGRAM_ID, ephemeralVault: EPHEMERAL_VAULT_ID, magicProgram: MAGIC_PROGRAM_ID, }) .instruction();const tx = new Transaction().add(initIx);const sig = await sendAndConfirmTransaction(ephemeralConnection, tx, [tempKeypair]);console.log("init_permission tx:", sig);
사용 사례:
ER에서 새로 위임된 PDA의 접근 제어를 부트스트랩
공개 모드(is_private: false, 빈 멤버)로 시작해 나중에 Update로 조이기