Permission Program
On-chain Permission Management (Coming soon)
Ephemeral Rollups SDK
SDK for Private Ephemeral Rollups
Overview
Private Ephemeral Rollups are Ephemeral Rollups that enable fine-grained permission over permissioned accounts in a Trusted Execution Environment with compliance at its heart. Each permission account maintains a list of members with specific flags that determine what actions they can perform.Key Concepts
- Permission Account: A PDA that stores access control rules for a specific account
- Members: Addresses granted specific permissions via flags
- Flags: Bitmasks that define what a member can do (authority, view logs, view balances, etc.)
- Public Permissions: When members are set to
None, the permissioned account becomes temporarily visible
Member Flags
Member flags define fine-grained permissions for each member. Flags can be combined using bitwise OR to grant multiple permissions.- Rust SDK
- Pinocchio
- Web3.js
- Kit
Copy
Ask AI
use ephemeral_rollups_sdk::access_control::structs::{
AUTHORITY_FLAG, // Member can directly modify the permission
TX_LOGS_FLAG, // Member can view transaction logs
TX_BALANCES_FLAG, // Member can view account balances
TX_MESSAGE_FLAG, // Member can view transaction messages
ACCOUNT_SIGNATURES_FLAG, // Member can view account signatures
};
// Set multiple flags by combining them with bitwise OR
let flags = AUTHORITY_FLAG | TX_LOGS_FLAG;
// AUTHORITY_FLAG allows a member to:
// - Modify permission settings
// - Add/remove other members
// - Update member flags
// TX_LOGS_FLAG allows a member to:
// - View transaction execution logs
// - Access transaction details
// TX_BALANCES_FLAG allows a member to:
// - View account balance changes
// - Monitor token transfers
// TX_MESSAGE_FLAG allows a member to:
// - View transaction message data
// ACCOUNT_SIGNATURES_FLAG allows a member to:
// - View account signatures
Copy
Ask AI
use ephemeral_rollups_pinocchio::types::MemberFlags;
// MemberFlags provides fine-grained permission control
// Set flags individually or combine multiple flags
pub const AUTHORITY: u8 = 1 << 0; // Member can directly modify the permission
pub const TX_LOGS: u8 = 1 << 1; // Member can view transaction logs
pub const TX_BALANCES: u8 = 1 << 2; // Member can view account balances
pub const TX_MESSAGE: u8 = 1 << 3; // Member can view transaction messages
pub const ACCOUNT_SIGNATURES: u8 = 1 << 4; // Member can view account signatures
// Create and set flags
let mut flags = MemberFlags::new();
flags.set(MemberFlags::AUTHORITY);
flags.set(MemberFlags::TX_LOGS);
// Check if a flag is set
if flags.has(MemberFlags::AUTHORITY) {
// Member can modify permissions
}
// AUTHORITY flag allows a member to:
// - Modify permission settings
// - Add/remove other members
// - Update member flags
// TX_LOGS flag allows a member to:
// - View transaction execution logs
// - Access transaction details
// TX_BALANCES flag allows a member to:
// - View account balance changes
// - Monitor token transfers
// TX_MESSAGE flag allows a member to:
// - View transaction message data
// ACCOUNT_SIGNATURES flag allows a member to:
// - View account signatures
Copy
Ask AI
import {
AUTHORITY_FLAG, // Member can directly modify the permission
TX_LOGS_FLAG, // Member can view transaction logs
TX_BALANCES_FLAG, // Member can view account balances
TX_MESSAGE_FLAG, // Member can view transaction messages
ACCOUNT_SIGNATURES_FLAG, // Member can view account signatures
} from "@magicblock-labs/ephemeral-rollups-sdk";
// Set multiple flags by combining them with bitwise OR
const flags = AUTHORITY_FLAG | TX_LOGS_FLAG;
// AUTHORITY_FLAG allows a member to:
// - Modify permission settings
// - Add/remove other members
// - Update member flags
// TX_LOGS_FLAG allows a member to:
// - View transaction execution logs
// - Access transaction details
// TX_BALANCES_FLAG allows a member to:
// - View account balance changes
// - Monitor token transfers
// TX_MESSAGE_FLAG allows a member to:
// - View transaction message data
// ACCOUNT_SIGNATURES_FLAG allows a member to:
// - View account signatures
// Check if a flag is present in a member
function hasFlagPermission(memberFlags: number, flag: number): boolean {
return (memberFlags & flag) !== 0;
}
const isAuthority = hasFlagPermission(flags, AUTHORITY_FLAG);
const canSeeLogs = hasFlagPermission(flags, TX_LOGS_FLAG);
Copy
Ask AI
import {
AUTHORITY_FLAG, // Member can directly modify the permission
TX_LOGS_FLAG, // Member can view transaction logs
TX_BALANCES_FLAG, // Member can view account balances
TX_MESSAGE_FLAG, // Member can view transaction messages
ACCOUNT_SIGNATURES_FLAG, // Member can view account signatures
isAuthority,
canSeeTxLogs,
canSeeTxBalances,
canSeeTxMessages,
canSeeAccountSignatures,
type Member,
} from "@magicblock-labs/ephemeral-rollups-sdk";
// Set multiple flags by combining them with bitwise OR
const flags = AUTHORITY_FLAG | TX_LOGS_FLAG;
// AUTHORITY_FLAG allows a member to:
// - Modify permission settings
// - Add/remove other members
// - Update member flags
// TX_LOGS_FLAG allows a member to:
// - View transaction execution logs
// - Access transaction details
// TX_BALANCES_FLAG allows a member to:
// - View account balance changes
// - Monitor token transfers
// TX_MESSAGE_FLAG allows a member to:
// - View transaction message data
// ACCOUNT_SIGNATURES_FLAG allows a member to:
// - View account signatures
// Helper functions to check permissions
const member: Member = {
flags,
pubkey: userAddress,
};
const canModifyPermission = isAuthority(member, userAddress);
const canViewLogs = canSeeTxLogs(member, userAddress);
const canViewBalances = canSeeTxBalances(member, userAddress);
const canViewMessages = canSeeTxMessages(member, userAddress);
const canViewSignatures = canSeeAccountSignatures(member, userAddress);
Permission Lifecycle
The typical lifecycle of a permissioned account requires interaction with MagicBlock’s Permission ProgramACLseoPoyC3cBqoUtkbjZ4aDrkurZW86v19pXz2XQnp1 and Delegation Program DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh:
1
Initialize a new permission account with initial members and their flags.
2
Delegate the permission to Private Ephemeral Rollup to enable enforcement
and real-time access control.
These public validators are supported for development. Make sure to add the specific ER validator in your instruction when delegating:
- 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):
mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev
3
Add, remove, or modify member permissions as needed. Updates can be made in
real-time on Private Ephemeral Rollup.
4
Before making requests, verify TEE RPC integrity and obtain an authorization
token. Only members with appropriate flags can access or modify the account
state.
5
Sync the final state back to Solana and return the account to Base Layer
control.
6
Close the permission account and reclaim its lamports when no longer needed.
Permission Operations
- 1. Create
- 2. Delegate
- 3. Update
- 4. Request
- 5. Commit & Undelegate
- 6. Close
Once you’ve created your program, you can add permission and delegation hooks to control access to your accounts. For example, see Quickstart.Create a new permission account with initial members and their flags via MagicBlock’s Permission Program Use Cases:
ACLseoPoyC3cBqoUtkbjZ4aDrkurZW86v19pXz2XQnp1.- Rust SDK
- Pinocchio
- Kit
- Web3.js
Copy
Ask AI
use ephemeral_rollups_sdk::access_control::{
instructions::CreatePermissionCpiBuilder,
structs::{
Member,
MembersArgs,
AUTHORITY_FLAG, // Member can directly modify the permission
TX_LOGS_FLAG, // Member can view transaction logs
TX_BALANCES_FLAG // Member can view account balances
}
};
let members = Some(vec![
Member {
// AUTHORITY_FLAG allows this member to modify permission settings
// TX_LOGS_FLAG allows viewing transaction logs
flags: AUTHORITY_FLAG | TX_LOGS_FLAG,
pubkey: *payer.key,
},
]);
// Note: Either the authority or permissioned_account can sign the transaction
// The signer depends on who has AUTHORITY_FLAG in the members list
CreatePermissionCpiBuilder::new(&permission_program)
.permissioned_account(&permissioned_account)
.permission(&permission)
.payer(&payer)
.system_program(&system_program)
.args(MembersArgs { members })
.invoke_signed(&[seed_refs.as_slice()])?;
Copy
Ask AI
use ephemeral_rollups_pinocchio::instruction::create_permission;
use ephemeral_rollups_pinocchio::types::{
Member,
MemberFlags,
MembersArgs,
};
use pinocchio::AccountView;
// Create members with specific flags
// AUTHORITY - Member can directly modify the permission
// TX_LOGS - Member can view transaction logs
// TX_BALANCES - Member can view account balances
// TX_MESSAGE - Member can view transaction messages
// ACCOUNT_SIGNATURES - Member can view account signatures
let mut flags = MemberFlags::new();
flags.set(MemberFlags::AUTHORITY);
flags.set(MemberFlags::TX_LOGS);
let member = Member {
flags,
pubkey: payer_address,
};
let members = vec![member];
let members_args = MembersArgs {
members: Some(&members),
};
// Prepare accounts: [permissioned_account, permission, payer, system_program]
let accounts: &[&AccountView] = &[
&permissioned_account,
&permission,
&payer,
&system_program,
];
// Create the permission
// Pass signer_seeds if permissioned_account is a PDA owned by your program
// Pass None if permissioned_account is an on-curve signer
create_permission(
accounts,
&permission_program_id,
members_args,
pda_signer_seeds, // Or None if permissioned_account is on-curve
)?;
Copy
Ask AI
import {
createCreatePermissionInstruction,
type Member,
AUTHORITY_FLAG, // Member can directly modify the permission
TX_LOGS_FLAG, // Member can view transaction logs
TX_BALANCES_FLAG, // Member can view account balances
} from "@magicblock-labs/ephemeral-rollups-sdk";
import { transaction, sendAndConfirmTransaction } from "@solana/kit";
const members: Member[] = [
{
// AUTHORITY_FLAG allows this member to modify permission settings
// TX_LOGS_FLAG allows viewing transaction logs
flags: AUTHORITY_FLAG | TX_LOGS_FLAG,
pubkey: payer.address,
},
];
// Either the authority or permissioned_account can sign the transaction
// The signer depends on who has AUTHORITY_FLAG in the members list
const createPermissionIx = await createCreatePermissionInstruction(
{
permissionedAccount: permissionedAccount.address,
payer: payer.address,
},
{
members,
}
);
const tx = transaction([createPermissionIx]);
const signature = await sendAndConfirmTransaction(
client,
tx,
[payer]
);
console.log("TX:", signature);
Copy
Ask AI
import {
Member,
AUTHORITY_FLAG, // Member can directly modify the permission
TX_LOGS_FLAG, // Member can view transaction logs
TX_BALANCES_FLAG, // Member can view account balances
createCreatePermissionInstruction,
} from "@magicblock-labs/ephemeral-rollups-sdk";
import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js";
const members: Member[] = [
{
// AUTHORITY_FLAG allows this member to modify permission settings
// TX_LOGS_FLAG allows viewing transaction logs
flags: AUTHORITY_FLAG | TX_LOGS_FLAG,
pubkey: payer.publicKey,
},
];
// Either the authority or permissioned_account can sign the transaction
// The signer depends on who has AUTHORITY_FLAG in the members list
const createPermissionIx = createCreatePermissionInstruction(
{
permissionedAccount: permissionedAccount.publicKey,
payer: payer.publicKey,
},
{
members,
}
);
const tx = new Transaction().add(createPermissionIx);
const txSig = await sendAndConfirmTransaction(connection, tx, [payer]);
console.log("TX:", txSig);
- Initialize access control for a new delegated account
- Set up authority members and their permissions
- Define who can view transaction details
Delegate a permissioned account to enable low-latency Private Ephemeral Rollup execution via MagicBlock’s Delegation Program Use Cases:
DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh.These public validators are supported for development. Make sure to add the specific ER validator in your instruction when delegating:
- 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):
mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev
- Rust SDK
- Pinocchio
- Kit
- Web3.js
Copy
Ask AI
use ephemeral_rollups_sdk::access_control::{
instructions::DelegatePermissionCpiBuilder
};
// Delegate the permission to enable low-latency Ephemeral Rollup execution
// Either authority (with AUTHORITY_FLAG) or permissioned_account can authorize
DelegatePermissionCpiBuilder::new(&permission_program)
.payer(&payer) // Pays for account creation
.authority(&payer, false) // Authority signer (has AUTHORITY_FLAG)
.permissioned_account(&permissioned_account, true) // or permissioned_account can sign
.permission(&permission)
.system_program(&system_program)
.owner_program(&owner_program)
.delegation_buffer(&delegation_buffer)
.delegation_record(&delegation_record)
.delegation_metadata(&delegation_metadata)
.delegation_program(&delegation_program)
.validator(&validator) // Validator that will execute the delegated state
.invoke_signed(&[seed_refs.as_slice()])?;
Copy
Ask AI
use ephemeral_rollups_pinocchio::instruction::delegate_permission;
use pinocchio::AccountView;
pub fn process_delegate_permission(
accounts: &[&AccountView],
permission_program: &pinocchio::Address,
authority_is_signer: bool,
permissioned_account_is_signer: bool,
) -> pinocchio::ProgramResult {
// Accounts: [payer, authority, permissioned_account, permission, system_program,
// owner_program, delegation_buffer, delegation_record,
// delegation_metadata, delegation_program, validator (optional)]
// Pass signer_seeds if permissioned_account is a PDA owned by your program
delegate_permission(
accounts,
permission_program,
authority_is_signer,
permissioned_account_is_signer,
signer_seeds, // Or None if permissioned_account is on-curve
)?;
Ok(())
}
Copy
Ask AI
import {
createDelegatePermissionInstruction,
PERMISSION_PROGRAM_ID,
} from "@magicblock-labs/ephemeral-rollups-sdk";
import { transaction, sendAndConfirmTransaction } from "@solana/kit";
// Delegate the permission to enable low-latency Ephemeral Rollup execution
// Either authority (with AUTHORITY_FLAG) or permissioned_account can authorize
const delegatePermissionIx = await createDelegatePermissionInstruction(
{
payer: payer.address, // Pays for account creation
authority: [payer.address, true], // Authority signer (has AUTHORITY_FLAG)
permissionedAccount: [permissionedAccount.address, false], // or permissioned_account can sign
ownerProgram: PERMISSION_PROGRAM_ID,
validator, // Validator that will execute the delegated state
}
);
const tx = transaction([delegatePermissionIx]);
const signature = await sendAndConfirmTransaction(
client,
tx,
[payer]
);
console.log("TX:", signature);
Copy
Ask AI
import {
PERMISSION_PROGRAM_ID,
createDelegatePermissionInstruction,
} from "@magicblock-labs/ephemeral-rollups-sdk";
import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js";
// Delegate the permission to enable low-latency Ephemeral Rollup execution
// Either authority (with AUTHORITY_FLAG) or permissioned_account can authorize
const delegatePermissionIx = createDelegatePermissionInstruction({
payer: payer.publicKey, // Pays for account creation
authority: [payer.publicKey, true], // Authority signer (has AUTHORITY_FLAG)
permissionedAccount: [permissionedAccount.publicKey, false], // or permissioned_account can sign
ownerProgram: PERMISSION_PROGRAM_ID,
validator, // Validator that will execute the delegated state
});
const tx = new Transaction().add(delegatePermissionIx);
const txSig = await sendAndConfirmTransaction(connection, tx, [payer]);
console.log("TX:", txSig);
- Enable ER execution for a permissioned account
- Designate a specific validator to execute state changes
- Set up delegation for real-time transaction processing
- Either the authority or permissioned_account must sign based on AUTHORITY_FLAG
- The validator processes transactions at ER speed
- Commit frequency determines how often state syncs to Solana
- Once delegated to Private Ephemeral Rollup, the permission is enforced - only members with appropriate flags can access or modify the account state
Modify existing members or their flags in a permission account via MagicBlock’s Permission Program Use Cases:
ACLseoPoyC3cBqoUtkbjZ4aDrkurZW86v19pXz2XQnp1.- Rust SDK
- Pinocchio
- Kit
- Web3.js
Copy
Ask AI
use ephemeral_rollups_sdk::access_control::{
instructions::UpdatePermissionCpiBuilder,
structs::MembersArgs
};
// Update permission and modify members
// Either authority or permissioned_account must sign based on AUTHORITY_FLAG
UpdatePermissionCpiBuilder::new(&permission_program)
.authority(&payer, true) // authority can sign if they have AUTHORITY_FLAG
.permissioned_account(&permissioned_account, true) // or permissioned_account can sign
.permission(&permission)
// Setting members to None makes the permission public (temporarily visible)
// This is useful for transitional states during delegation/undeligation
.args(MembersArgs { members: None })
.invoke_signed(&[seed_refs.as_slice()])?;
Copy
Ask AI
use ephemeral_rollups_pinocchio::instruction::update_permission;
use ephemeral_rollups_pinocchio::types::{
Member,
MemberFlags,
MembersArgs,
};
use pinocchio::AccountView;
// Update permission with new member flags
// Either authority or permissioned_account must sign based on AUTHORITY_FLAG
let mut new_flags = MemberFlags::new();
new_flags.set(MemberFlags::TX_LOGS);
new_flags.set(MemberFlags::TX_BALANCES);
let updated_member = Member {
flags: new_flags,
pubkey: user_address,
};
let members = vec![updated_member];
let members_args = MembersArgs {
members: Some(&members),
};
// Prepare accounts: [authority, permissioned_account, permission]
let accounts: &[&AccountView] = &[
&authority,
&permissioned_account,
&permission,
];
// Update the permission
// Setting members to None makes the permission public (temporarily visible)
// Pass signer_seeds if permissioned_account is a PDA owned by your program
update_permission(
accounts,
&permission_program_id,
authority.is_signer(),
permissioned_account.is_signer(),
members_args,
signer_seeds, // Or None if permissioned_account is on-curve
)?;
Copy
Ask AI
import {
createUpdatePermissionInstruction,
type Member,
} from "@magicblock-labs/ephemeral-rollups-sdk";
import { transaction, sendAndConfirmTransaction } from "@solana/kit";
// Update permission and modify members
// Either authority or permissioned_account must sign based on AUTHORITY_FLAG
const updatePermissionIx = await createUpdatePermissionInstruction(
{
authority: [payer.address, true], // authority can sign if they have AUTHORITY_FLAG
permissionedAccount: [permissionedAccount.address, false], // or permissioned_account can sign
},
{
// Setting members to empty array or None makes the permission public (temporarily visible)
// This is useful for transitional states during delegation/undeligation
members: [],
}
);
const tx = transaction([updatePermissionIx]);
const signature = await sendAndConfirmTransaction(
client,
tx,
[payer]
);
console.log("TX:", signature);
Copy
Ask AI
import { createUpdatePermissionInstruction } from "@magicblock-labs/ephemeral-rollups-sdk";
import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js";
// Update permission and modify members
// Either authority or permissioned_account must sign based on AUTHORITY_FLAG
const updatePermissionIx = createUpdatePermissionInstruction(
{
authority: [payer.publicKey, true], // authority can sign if they have AUTHORITY_FLAG
permissionedAccount: [permissionedAccount.publicKey, false], // or permissioned_account can sign
},
{
// Setting members to empty array or None makes the permission public (temporarily visible)
// This is useful for transitional states during delegation/undeligation
members: [],
}
);
const tx = new Transaction().add(updatePermissionIx);
const txSig = await sendAndConfirmTransaction(connection, tx, [payer]);
console.log("TX:", txSig);
- Add new members to a permission group
- Revoke permissions by removing members
- Change member flags to grant/restrict access
- Set members to
Noneto make account temporarily visible
When making requests to Private Ephemeral Rollup, you must first establish authorization:Authorization Steps:Account Visibility:
Private Ephemeral Rollup (devnet) endpoint:
https://tee.magicblock.app?token= {authToken}. Replace {authToken} with your authorization token obtained
from the TEE RPC to send requests.- Verify TEE RPC Integrity: Verify the TEE RPC server runs on genuine Intel TDX hardware using its TDX quote and Intel-issued attestation certificates
- Request Authorization Token: Sign a challenge message to receive an authorization token
- Create Connection: Pass the authorization token inside header or as query parameter
- Kit
- Web3.js
Copy
Ask AI
import {
verifyTeeRpcIntegrity,
getAuthToken,
} from "@magicblock-labs/ephemeral-rollups-sdk";
import { Connection } from "@magicblock-labs/ephemeral-rollups-kit";
import * as nacl from "tweetnacl";
const teeUrl = "https://tee.magicblock.app";
const teeWsUrl = "wss://tee.magicblock.app";
// 1. Verify the integrity of TEE RPC
const isVerified = await verifyTeeRpcIntegrity(teeUrl);
// 2. Get AuthToken before making request to TEE
const authToken = await getAuthToken(
teeUrl,
userPubkey,
(message: Uint8Array) =>
Promise.resolve(nacl.sign.detached(message, userSecretKey))
);
// 3. Create connection with TEE using authorization token
const teeUserUrl = `${teeUrl}?token=${authToken.token}`;
const teeUserWsUrl = `${teeWsUrl}?token=${authToken.token}`;
const ephemeralConnection = await Connection.create(
teeUserUrl,
teeUserWsUrl
);
Copy
Ask AI
import {
verifyTeeRpcIntegrity,
getAuthToken,
} from "@magicblock-labs/ephemeral-rollups-sdk";
import { Connection, Keypair } from "@solana/web3.js";
import * as nacl from "tweetnacl";
const teeUrl = "https://tee.magicblock.app";
const teeWsUrl = "wss://tee.magicblock.app";
// 1. Verify the integrity of TEE RPC
const isVerified = await verifyTeeRpcIntegrity(teeUrl);
// 2. Get AuthToken before making request to TEE
const authToken = await getAuthToken(
teeUrl,
userKeypair.publicKey,
(message: Uint8Array) =>
Promise.resolve(nacl.sign.detached(message, userKeypair.secretKey))
);
// 3. Create connection with TEE
const teeUserUrl = `${teeUrl}?token=${authToken.token}`;
const teeUserWsUrl = `${teeWsUrl}?token=${authToken.token}`;
const connection = new Connection(
teeUserUrl,
{
wsEndpoint: teeUserWsUrl,
}
);
- By default: All accounts are transparent and accessible to any authorized user
- With permissions: Once an account is permissioned through the permission system, access control rules are enforced during every request
- Only members with appropriate flags can access or modify permissioned accounts and transactions
- Requests are executed in real-time on the validator
- Member flags determine what each user can do
- Permissions can be updated dynamically during execution
- Updates take effect immediately on the Private Ephemeral Rollup
The latest permission state on Private Ephemeral Rollup enforces access control. For closing permission account, update permission members before undelegating permission to Solana.⬆️ Back to Top
- Rust SDK
- Pinocchio
- Kit
- Web3.js
Copy
Ask AI
use ephemeral_rollups_sdk::access_control::{
instructions::CommitAndUndelegatePermissionCpiBuilder
};
CommitAndUndelegatePermissionCpiBuilder::new(&permission_program)
.authority(&payer, false)
.permissioned_account(&permissioned_account, true)
.permission(&permission)
.magic_program(&magic_program)
.magic_context(&magic_context)
.invoke_signed(&[seed_refs.as_slice()])?;
Copy
Ask AI
use ephemeral_rollups_pinocchio::instruction::commit_and_undelegate_permission;
use pinocchio::AccountView;
pub fn process_commit_and_undelegate_permission(
accounts: &[&AccountView],
permission_program: &pinocchio::Address,
authority_is_signer: bool,
permissioned_account_is_signer: bool,
) -> pinocchio::ProgramResult {
// Accounts: [authority, permissioned_account, permission, magic_program, magic_context]
// Pass signer_seeds if permissioned_account is a PDA owned by your program
commit_and_undelegate_permission(
accounts,
permission_program,
authority_is_signer,
permissioned_account_is_signer,
signer_seeds, // Or None if permissioned_account is on-curve
)?;
Ok(())
}
Copy
Ask AI
import {
createCommitAndUndelegatePermissionInstruction,
} from "@magicblock-labs/ephemeral-rollups-sdk";
import { transaction, sendAndConfirmTransaction } from "@solana/kit";
const commitAndUndelegatePermissionIx =
await createCommitAndUndelegatePermissionInstruction({
authority: [payer.address, true],
permissionedAccount: [permissionedAccount.address, false],
});
const tx = transaction([commitAndUndelegatePermissionIx]);
const signature = await sendAndConfirmTransaction(
client,
tx,
[payer]
);
console.log("TX:", signature);
Copy
Ask AI
import { createCommitAndUndelegatePermissionInstruction } from "@magicblock-labs/ephemeral-rollups-sdk";
import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js";
const commitAndUndelegatePermissionIx =
createCommitAndUndelegatePermissionInstruction({
authority: [payer.publicKey, true],
permissionedAccount: [permissionedAccount.publicKey, false],
});
const tx = new Transaction().add(commitAndUndelegatePermissionIx);
const txSig = await sendAndConfirmTransaction(connection, tx, [payer]);
console.log("TX:", txSig);
Close a permissioned account and reclaim its lamports on Solana.Use Cases:
- Rust SDK
- Pinocchio
- Kit
- Web3.js
Copy
Ask AI
use ephemeral_rollups_sdk::access_control::{
instructions::ClosePermissionCpiBuilder
};
// Close the permission account and reclaim lamports
// IMPORTANT: The permission must be undelegated to Solana first
// If delegated, call commit_and_undelegate before closing
ClosePermissionCpiBuilder::new(&permission_program)
.payer(&payer) // Receives reclaimed lamports
.authority(&payer, false) // Authority signer (has AUTHORITY_FLAG)
.permissioned_account(&permissioned_account, true) // or permissioned_account can sign
.permission(&permission) // The permission account to close
.invoke_signed(&[seed_refs.as_slice()])?;
Copy
Ask AI
use ephemeral_rollups_pinocchio::instruction::close_permission;
use pinocchio::AccountView;
pub fn process_close_permission(
accounts: &[&AccountView],
permission_program: &pinocchio::Address,
authority_is_signer: bool,
permissioned_account_is_signer: bool,
) -> pinocchio::ProgramResult {
// Accounts: [payer, authority, permissioned_account, permission]
// IMPORTANT: The permission must be undelegated to Solana first
// If the permission is still delegated, this operation will fail
// Pass signer_seeds if permissioned_account is a PDA owned by your program
close_permission(
accounts,
permission_program,
authority_is_signer,
permissioned_account_is_signer,
signer_seeds, // Or None if permissioned_account is on-curve
)?;
// The permission account lamports are transferred to the payer
Ok(())
}
Copy
Ask AI
import {
createClosePermissionInstruction,
} from "@magicblock-labs/ephemeral-rollups-sdk";
import { transaction, sendAndConfirmTransaction } from "@solana/kit";
// Close the permission account and reclaim lamports
// IMPORTANT: The permission must be undelegated to Solana first
// If delegated, call commit_and_undelegate before closing
const closePermissionIx = await createClosePermissionInstruction({
payer: payer.address, // Receives reclaimed lamports
authority: [payer.address, true], // Authority signer (has AUTHORITY_FLAG)
permissionedAccount: [permissionedAccount.address, false], // or permissioned_account can sign
});
const tx = transaction([closePermissionIx]);
const signature = await sendAndConfirmTransaction(
client,
tx,
[payer]
);
console.log("TX:", signature);
Copy
Ask AI
import { createClosePermissionInstruction } from "@magicblock-labs/ephemeral-rollups-sdk";
import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js";
// Close the permission account and reclaim lamports
// IMPORTANT: The permission must be undelegated to Solana first
// If delegated, call commit_and_undelegate before closing
const closePermissionIx = createClosePermissionInstruction({
payer: payer.publicKey, // Receives reclaimed lamports
authority: [payer.publicKey, true], // Authority signer (has AUTHORITY_FLAG)
permissionedAccount: [permissionedAccount.publicKey, false], // or permissioned_account can sign
});
const tx = new Transaction().add(closePermissionIx);
const txSig = await sendAndConfirmTransaction(connection, tx, [payer]);
console.log("TX:", txSig);
- Clean up unused permission accounts
- Reclaim SOL from closed accounts
- The permission must be undelegated to Solana first
- If delegated, call
commit_and_undelegatebefore closing - Lamports are returned to the payer
Best Practices
- Authority Management: Always assign AUTHORITY_FLAG to at least one trusted member
- Least Privilege: Grant only necessary flags to each member
- Real-time Updates: Permissions can be updated in real-time on Private Ephemeral Rollup without undelegating, allowing dynamic access control adjustments
- Cleanup: Undelegate and close unused permission accounts to free SOL
Security Considerations
- Signer Validation: Only members with AUTHORITY_FLAG or program with permissioned account can authorize changes
- Public Accounts: Setting members to
Nonemakes the account publicly visible - Access Auditing: Use member flags to audit and control access

