Documentation Index Fetch the complete documentation index at: https://mintlify.com/magicblock-labs/magicblock-engine-examples/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The Rust SDK (ephemeral-rollups-sdk) provides on-chain program utilities for integrating with MagicBlock Ephemeral Rollups. It includes delegation functions, commit operations, Anchor macros, and CPI helpers.
Installation
Add to your Cargo.toml:
Anchor Programs
Native Programs
[ dependencies ]
ephemeral-rollups-sdk = { version = "0.6.5" , features = [ "anchor" , "disable-realloc" ] }
Or use cargo:
cargo add ephemeral-rollups-sdk --features anchor,disable-realloc
Features
Enables Anchor framework integration with macros and helpers
Disables automatic account reallocation during delegation (recommended for production)
Anchor Integration
Imports
use ephemeral_rollups_sdk :: anchor :: {commit, delegate, ephemeral};
use ephemeral_rollups_sdk :: cpi :: DelegateConfig ;
use ephemeral_rollups_sdk :: ephem :: {commit_accounts, commit_and_undelegate_accounts};
Macros
#[ephemeral]
Marks a program module as ephemeral-enabled, allowing it to run on both Solana and Ephemeral Rollups.
use anchor_lang :: prelude ::* ;
use ephemeral_rollups_sdk :: anchor :: ephemeral;
declare_id! ( "9RPwaXayVZHna1BYuRS4cLPJZuNGU1uS5V3heXB7v6Qi" );
#[ephemeral]
#[program]
pub mod anchor_counter {
use super ::* ;
pub fn initialize ( ctx : Context < Initialize >) -> Result <()> {
let counter = & mut ctx . accounts . counter;
counter . count = 0 ;
Ok (())
}
pub fn increment ( ctx : Context < Increment >) -> Result <()> {
let counter = & mut ctx . accounts . counter;
counter . count += 1 ;
Ok (())
}
}
Apply to #[program] modules to enable Ephemeral Rollups compatibility
#[delegate]
Adds delegation functionality to an Anchor account context struct.
use ephemeral_rollups_sdk :: anchor :: delegate;
#[delegate]
#[derive( Accounts )]
pub struct DelegateInput <' info > {
pub payer : Signer <' info >,
/// CHECK The pda to delegate
#[account( mut , del)]
pub pda : AccountInfo <' info >,
}
This macro generates a delegate_pda method on the context:
pub fn delegate ( ctx : Context < DelegateInput >) -> Result <()> {
ctx . accounts . delegate_pda (
& ctx . accounts . payer,
& [ COUNTER_SEED ],
DelegateConfig {
validator : ctx . remaining_accounts . first () . map ( | acc | acc . key ()),
.. Default :: default ()
},
) ? ;
Ok (())
}
Apply to Anchor Accounts structs containing accounts to delegate. Mark delegated accounts with #[account(mut, del)]
#[commit]
Adds commit functionality to an Anchor account context struct.
use ephemeral_rollups_sdk :: anchor :: commit;
#[commit]
#[derive( Accounts )]
pub struct IncrementAndCommit <' info > {
#[account( mut )]
pub payer : Signer <' info >,
#[account( mut , seeds = [ COUNTER_SEED ], bump )]
pub counter : Account <' info , Counter >,
}
This automatically includes the required magic_program and magic_context accounts:
pub fn commit ( ctx : Context < IncrementAndCommit >) -> Result <()> {
commit_accounts (
& ctx . accounts . payer,
vec! [ & ctx . accounts . counter . to_account_info ()],
& ctx . accounts . magic_context,
& ctx . accounts . magic_program,
) ? ;
Ok (())
}
Apply to Anchor Accounts structs to automatically include magic program and context accounts required for commits
CPI Functions (Native Programs)
delegate_account
Delegates an account to the Ephemeral Rollups delegation program via CPI.
use ephemeral_rollups_sdk :: cpi :: {
delegate_account, DelegateAccounts , DelegateConfig
};
use solana_program :: {
account_info :: {next_account_info, AccountInfo },
entrypoint :: ProgramResult ,
pubkey :: Pubkey ,
};
pub fn process_delegate ( program_id : & Pubkey , accounts : & [ AccountInfo ]) -> ProgramResult {
let account_info_iter = & mut accounts . iter ();
let payer = next_account_info ( account_info_iter ) ? ;
let system_program = next_account_info ( account_info_iter ) ? ;
let pda_to_delegate = next_account_info ( account_info_iter ) ? ;
let owner_program = next_account_info ( account_info_iter ) ? ;
let delegation_buffer = next_account_info ( account_info_iter ) ? ;
let delegation_record = next_account_info ( account_info_iter ) ? ;
let delegation_metadata = next_account_info ( account_info_iter ) ? ;
let delegation_program = next_account_info ( account_info_iter ) ? ;
let validator_account = account_info_iter . next ();
let validator_pubkey : Option < Pubkey > =
validator_account . map ( | acc_info | acc_info . key . clone ());
// Prepare PDA seeds
let seed_1 = b"counter" ;
let seed_2 = payer . key . as_ref ();
let pda_seeds : & [ & [ u8 ]] = & [ seed_1 , seed_2 ];
let delegate_accounts = DelegateAccounts {
payer ,
pda : pda_to_delegate ,
owner_program ,
buffer : delegation_buffer ,
delegation_record ,
delegation_metadata ,
delegation_program ,
system_program ,
};
let delegate_config = DelegateConfig {
validator : validator_pubkey ,
.. Default :: default ()
};
delegate_account ( delegate_accounts , pda_seeds , delegate_config ) ? ;
Ok (())
}
Struct containing all required account infos for delegation
The seeds used to derive the PDA being delegated
Configuration for the delegation operation
DelegateAccounts
Struct containing all accounts required for delegation.
pub struct DelegateAccounts <' a , ' info > {
pub payer : & ' a AccountInfo <' info >,
pub pda : & ' a AccountInfo <' info >,
pub owner_program : & ' a AccountInfo <' info >,
pub buffer : & ' a AccountInfo <' info >,
pub delegation_record : & ' a AccountInfo <' info >,
pub delegation_metadata : & ' a AccountInfo <' info >,
pub delegation_program : & ' a AccountInfo <' info >,
pub system_program : & ' a AccountInfo <' info >,
}
The account paying for delegation costs (rent, fees)
The PDA account being delegated to the Ephemeral Rollup
The program that owns the PDA being delegated
The delegation buffer PDA (stores delegated account data)
The delegation record PDA (tracks delegation state)
The delegation metadata PDA (additional delegation info)
The delegation program account
The Solana system program
DelegateConfig
Configuration struct for delegation operations.
pub struct DelegateConfig {
pub validator : Option < Pubkey >,
pub commit_frequency_ms : Option < u32 >,
}
impl Default for DelegateConfig {
fn default () -> Self {
Self {
validator : None ,
commit_frequency_ms : None ,
}
}
}
Optional specific validator to delegate to. If None, uses the default ER validator
Optional commit frequency in milliseconds. If None, uses default frequency
Example with custom validator:
let delegate_config = DelegateConfig {
validator : Some ( Pubkey :: from_str ( "mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev" ) . unwrap ()),
.. Default :: default ()
};
Example with remaining accounts (Anchor):
pub fn delegate ( ctx : Context < DelegateInput >) -> Result <()> {
ctx . accounts . delegate_pda (
& ctx . accounts . payer,
& [ COUNTER_SEED ],
DelegateConfig {
// Use first remaining account as validator if provided
validator : ctx . remaining_accounts . first () . map ( | acc | acc . key ()),
.. Default :: default ()
},
) ? ;
Ok (())
}
undelegate_account
Undelegates an account from the Ephemeral Rollups delegation program via CPI.
use ephemeral_rollups_sdk :: cpi :: undelegate_account;
pub fn process_undelegate (
program_id : & Pubkey ,
accounts : & [ AccountInfo ],
pda_seeds : Vec < Vec < u8 >>,
) -> ProgramResult {
let account_info_iter = & mut accounts . iter ();
let delegated_pda = next_account_info ( account_info_iter ) ? ;
let delegation_buffer = next_account_info ( account_info_iter ) ? ;
let payer = next_account_info ( account_info_iter ) ? ;
let system_program = next_account_info ( account_info_iter ) ? ;
undelegate_account (
delegated_pda ,
program_id ,
delegation_buffer ,
payer ,
system_program ,
pda_seeds ,
) ? ;
Ok (())
}
The PDA account to undelegate
The program that owns the delegated PDA
The delegation buffer account
The account paying for undelegation
The Solana system program
The seeds used to derive the PDA
Commit Functions
commit_accounts
Commits account state from Ephemeral Rollup back to Solana base layer.
use ephemeral_rollups_sdk :: ephem :: commit_accounts;
pub fn process_commit ( _program_id : & Pubkey , accounts : & [ AccountInfo ]) -> ProgramResult {
let account_info_iter = & mut accounts . iter ();
let payer = next_account_info ( account_info_iter ) ? ;
let counter_account = next_account_info ( account_info_iter ) ? ;
let magic_program = next_account_info ( account_info_iter ) ? ;
let magic_context = next_account_info ( account_info_iter ) ? ;
if ! payer . is_signer {
return Err ( ProgramError :: MissingRequiredSignature );
}
commit_accounts (
payer ,
vec! [ counter_account ],
magic_context ,
magic_program ,
) ? ;
Ok (())
}
The account paying for the commit operation
accounts
Vec<&AccountInfo>
required
Vector of accounts to commit back to base layer
The magic context account (required for commits)
The magic program account (required for commits)
Anchor example:
pub fn commit ( ctx : Context < IncrementAndCommit >) -> Result <()> {
commit_accounts (
& ctx . accounts . payer,
vec! [ & ctx . accounts . counter . to_account_info ()],
& ctx . accounts . magic_context,
& ctx . accounts . magic_program,
) ? ;
Ok (())
}
commit_and_undelegate_accounts
Commits account state and undelegates in a single operation.
use ephemeral_rollups_sdk :: ephem :: commit_and_undelegate_accounts;
pub fn process_commit_and_undelegate (
_program_id : & Pubkey ,
accounts : & [ AccountInfo ],
) -> ProgramResult {
let account_info_iter = & mut accounts . iter ();
let payer = next_account_info ( account_info_iter ) ? ;
let counter_account = next_account_info ( account_info_iter ) ? ;
let magic_program = next_account_info ( account_info_iter ) ? ;
let magic_context = next_account_info ( account_info_iter ) ? ;
if ! payer . is_signer {
return Err ( ProgramError :: MissingRequiredSignature );
}
commit_and_undelegate_accounts (
payer ,
vec! [ counter_account ],
magic_context ,
magic_program ,
) ? ;
Ok (())
}
The account paying for the operation
accounts
Vec<&AccountInfo>
required
Vector of accounts to commit and undelegate
The magic context account
The magic program account
Anchor example:
pub fn undelegate ( ctx : Context < IncrementAndCommit >) -> Result <()> {
commit_and_undelegate_accounts (
& ctx . accounts . payer,
vec! [ & ctx . accounts . counter . to_account_info ()],
& ctx . accounts . magic_context,
& ctx . accounts . magic_program,
) ? ;
Ok (())
}
With Anchor account serialization:
pub fn increment_and_undelegate ( ctx : Context < IncrementAndCommit >) -> Result <()> {
let counter = & mut ctx . accounts . counter;
counter . count += 1 ;
msg! ( "PDA {} count: {}" , counter . key (), counter . count);
// Serialize the Anchor counter account before committing
counter . exit ( & crate :: ID ) ? ;
commit_and_undelegate_accounts (
& ctx . accounts . payer,
vec! [ & ctx . accounts . counter . to_account_info ()],
& ctx . accounts . magic_context,
& ctx . accounts . magic_program,
) ? ;
Ok (())
}
Complete Examples
Anchor Program with Delegation
use anchor_lang :: prelude ::* ;
use ephemeral_rollups_sdk :: anchor :: {commit, delegate, ephemeral};
use ephemeral_rollups_sdk :: cpi :: DelegateConfig ;
use ephemeral_rollups_sdk :: ephem :: {commit_accounts, commit_and_undelegate_accounts};
declare_id! ( "9RPwaXayVZHna1BYuRS4cLPJZuNGU1uS5V3heXB7v6Qi" );
pub const COUNTER_SEED : & [ u8 ] = b"counter" ;
#[ephemeral]
#[program]
pub mod anchor_counter {
use super ::* ;
pub fn initialize ( ctx : Context < Initialize >) -> Result <()> {
let counter = & mut ctx . accounts . counter;
counter . count = 0 ;
msg! ( "PDA {} count: {}" , counter . key (), counter . count);
Ok (())
}
pub fn increment ( ctx : Context < Increment >) -> Result <()> {
let counter = & mut ctx . accounts . counter;
counter . count += 1 ;
msg! ( "PDA {} count: {}" , counter . key (), counter . count);
Ok (())
}
pub fn delegate ( ctx : Context < DelegateInput >) -> Result <()> {
ctx . accounts . delegate_pda (
& ctx . accounts . payer,
& [ COUNTER_SEED ],
DelegateConfig {
validator : ctx . remaining_accounts . first () . map ( | acc | acc . key ()),
.. Default :: default ()
},
) ? ;
Ok (())
}
pub fn commit ( ctx : Context < IncrementAndCommit >) -> Result <()> {
commit_accounts (
& ctx . accounts . payer,
vec! [ & ctx . accounts . counter . to_account_info ()],
& ctx . accounts . magic_context,
& ctx . accounts . magic_program,
) ? ;
Ok (())
}
pub fn undelegate ( ctx : Context < IncrementAndCommit >) -> Result <()> {
commit_and_undelegate_accounts (
& ctx . accounts . payer,
vec! [ & ctx . accounts . counter . to_account_info ()],
& ctx . accounts . magic_context,
& ctx . accounts . magic_program,
) ? ;
Ok (())
}
}
#[derive( Accounts )]
pub struct Initialize <' info > {
#[account(init_if_needed, payer = user, space = 8 + 8, seeds = [ COUNTER_SEED ], bump )]
pub counter : Account <' info , Counter >,
#[account( mut )]
pub user : Signer <' info >,
pub system_program : Program <' info , System >,
}
#[delegate]
#[derive( Accounts )]
pub struct DelegateInput <' info > {
pub payer : Signer <' info >,
/// CHECK The pda to delegate
#[account( mut , del)]
pub pda : AccountInfo <' info >,
}
#[derive( Accounts )]
pub struct Increment <' info > {
#[account( mut , seeds = [ COUNTER_SEED ], bump )]
pub counter : Account <' info , Counter >,
}
#[commit]
#[derive( Accounts )]
pub struct IncrementAndCommit <' info > {
#[account( mut )]
pub payer : Signer <' info >,
#[account( mut , seeds = [ COUNTER_SEED ], bump )]
pub counter : Account <' info , Counter >,
}
#[account]
pub struct Counter {
pub count : u64 ,
}
Native Program with CPI
use borsh :: { BorshDeserialize , BorshSerialize };
use solana_program :: {
account_info :: {next_account_info, AccountInfo },
entrypoint :: ProgramResult ,
pubkey :: Pubkey ,
};
use ephemeral_rollups_sdk :: cpi :: {
delegate_account, DelegateAccounts , DelegateConfig ,
};
use ephemeral_rollups_sdk :: ephem :: {commit_accounts, commit_and_undelegate_accounts};
pub fn process_instruction (
program_id : & Pubkey ,
accounts : & [ AccountInfo ],
instruction_data : & [ u8 ],
) -> ProgramResult {
match instruction_data [ 0 ] {
0 => process_initialize ( program_id , accounts ),
1 => process_increment ( program_id , accounts ),
2 => process_delegate ( program_id , accounts ),
3 => process_commit ( program_id , accounts ),
4 => process_commit_and_undelegate ( program_id , accounts ),
_ => Err ( ProgramError :: InvalidInstructionData ),
}
}
pub fn process_delegate ( program_id : & Pubkey , accounts : & [ AccountInfo ]) -> ProgramResult {
let account_info_iter = & mut accounts . iter ();
let payer = next_account_info ( account_info_iter ) ? ;
let system_program = next_account_info ( account_info_iter ) ? ;
let pda_to_delegate = next_account_info ( account_info_iter ) ? ;
let owner_program = next_account_info ( account_info_iter ) ? ;
let delegation_buffer = next_account_info ( account_info_iter ) ? ;
let delegation_record = next_account_info ( account_info_iter ) ? ;
let delegation_metadata = next_account_info ( account_info_iter ) ? ;
let delegation_program = next_account_info ( account_info_iter ) ? ;
let seed_1 = b"counter" ;
let seed_2 = payer . key . as_ref ();
let pda_seeds : & [ & [ u8 ]] = & [ seed_1 , seed_2 ];
let delegate_accounts = DelegateAccounts {
payer ,
pda : pda_to_delegate ,
owner_program ,
buffer : delegation_buffer ,
delegation_record ,
delegation_metadata ,
delegation_program ,
system_program ,
};
delegate_account ( delegate_accounts , pda_seeds , DelegateConfig :: default ()) ? ;
Ok (())
}
pub fn process_commit ( program_id : & Pubkey , accounts : & [ AccountInfo ]) -> ProgramResult {
let account_info_iter = & mut accounts . iter ();
let payer = next_account_info ( account_info_iter ) ? ;
let counter_account = next_account_info ( account_info_iter ) ? ;
let magic_program = next_account_info ( account_info_iter ) ? ;
let magic_context = next_account_info ( account_info_iter ) ? ;
commit_accounts (
payer ,
vec! [ counter_account ],
magic_context ,
magic_program ,
) ? ;
Ok (())
}
VRF Integration
For programs using verifiable random functions with Ephemeral Rollups:
use ephemeral_vrf_sdk :: anchor :: vrf;
use ephemeral_vrf_sdk :: instructions :: {create_request_randomness_ix, RequestRandomnessParams };
use ephemeral_vrf_sdk :: types :: SerializableAccountMeta ;
#[vrf]
#[derive( Accounts )]
pub struct DoRollDiceDelegatedCtx <' info > {
#[account( mut )]
pub payer : Signer <' info >,
#[account(seeds = [ PLAYER_SEED , payer . key() . to_bytes() . as_slice()], bump )]
pub player : Account <' info , Player >,
/// CHECK: The oracle queue
#[account( mut , address = ephemeral_vrf_sdk :: consts :: DEFAULT_EPHEMERAL_QUEUE )]
pub oracle_queue : AccountInfo <' info >,
}
pub fn roll_dice_delegated ( ctx : Context < DoRollDiceDelegatedCtx >, client_seed : u8 ) -> Result <()> {
let ix = create_request_randomness_ix ( RequestRandomnessParams {
payer : ctx . accounts . payer . key (),
oracle_queue : ctx . accounts . oracle_queue . key (),
callback_program_id : ID ,
callback_discriminator : instruction :: CallbackRollDice :: DISCRIMINATOR . to_vec (),
caller_seed : [ client_seed ; 32 ],
accounts_metas : Some ( vec! [ SerializableAccountMeta {
pubkey : ctx . accounts . player . key (),
is_signer : false ,
is_writable : true ,
}]),
.. Default :: default ()
});
ctx . accounts . invoke_signed_vrf ( & ctx . accounts . payer . to_account_info (), & ix ) ? ;
Ok (())
}
Version Compatibility
SDK Version : 0.6.5
Anchor : 0.32.1+ (when using anchor feature)
Solana : 1.18+
Best Practices
Always use disable-realloc feature in production to prevent unexpected account size changes
Serialize Anchor accounts before committing using .exit() method
Include validator in remaining_accounts for localnet testing
Use #[ephemeral] macro on all program modules that need ER support
Call commit operations only from ER , not from base layer