Skip to main content

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

Delegation instructions transfer account ownership to the MagicBlock delegation program, enabling accounts to be processed on Ephemeral Rollups (ER). This page documents delegation patterns across Anchor and native Rust programs.

Anchor Delegation Pattern

Using the #[delegate] Macro

The #[delegate] macro automatically generates the required delegation accounts and helper methods.
anchor-counter/programs/anchor-counter/src/lib.rs
use ephemeral_rollups_sdk::anchor::{delegate};
use ephemeral_rollups_sdk::cpi::DelegateConfig;

#[delegate]
#[derive(Accounts)]
pub struct DelegateInput<'info> {
    pub payer: Signer<'info>,
    /// CHECK The pda to delegate
    #[account(mut, del)]
    pub pda: AccountInfo<'info>,
}

Delegate Instruction Implementation

The delegate instruction uses the generated delegate_pda method:
anchor-counter/programs/anchor-counter/src/lib.rs
pub fn delegate(ctx: Context<DelegateInput>) -> Result<()> {
    ctx.accounts.delegate_pda(
        &ctx.accounts.payer,
        &[COUNTER_SEED],
        DelegateConfig {
            // Optionally set a specific validator from the first remaining account
            validator: ctx.remaining_accounts.first().map(|acc| acc.key()),
            ..Default::default()
        },
    )?;
    Ok(())
}

DelegateInput Accounts

payer
Signer
required
The account paying for delegation buffer and record creation
pda
AccountInfo
required
The PDA account to delegate. Must be marked with #[account(mut, del)]
system_program
Program
System program (auto-generated by #[delegate] macro)
owner_program
Program
The program that owns the PDA (auto-generated by #[delegate] macro)
delegation_buffer
AccountInfo
Buffer account for storing delegated account data (auto-generated)
delegation_record
AccountInfo
Record tracking the delegation state (auto-generated)
delegation_metadata
AccountInfo
Metadata about the delegation (auto-generated)
delegation_program
Program
The MagicBlock delegation program (auto-generated)

DelegateConfig Parameters

pda_seeds

pda_seeds
&[&[u8]]
required
The seeds used to derive the PDA. Required for the delegation program to verify PDA ownership.
&[COUNTER_SEED]  // Example: &[b"counter"]

validator

validator
Option<Pubkey>
Optional validator public key to specify which Ephemeral Rollup validator should process this account.
  • If None, uses the default validator
  • Can be provided via remaining_accounts in Anchor
validator: ctx.remaining_accounts.first().map(|acc| acc.key())

commit_frequency_ms

commit_frequency_ms
Option<u32>
Optional commit frequency in milliseconds. Controls how often the ER automatically commits account state back to the base layer.
  • If None, uses default commit frequency
  • Specified in milliseconds
DelegateConfig {
    commit_frequency_ms: Some(5000), // Commit every 5 seconds
    ..Default::default()
}

Native Rust Delegation Pattern

Delegate Account Function

For native Rust programs, use the delegate_account function from the SDK:
rust-counter/src/processor.rs
use ephemeral_rollups_sdk::cpi::{
    delegate_account, DelegateAccounts, DelegateConfig,
};

pub fn process_delegate(_program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
    // Get accounts
    let account_info_iter = &mut accounts.iter();
    let initializer = 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();

    // Optional: client-provided validator or default validator
    let validator_pubkey: Option<Pubkey> = validator_account.map(|acc_info| acc_info.key.clone());

    // Prepare counter pda seeds
    let seed_1 = b"counter";
    let seed_2 = initializer.key.as_ref();
    let pda_seeds: &[&[u8]] = &[seed_1, seed_2];

    let delegate_accounts = DelegateAccounts {
        payer: initializer,
        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(())
}

Native Rust Required Accounts

initializer
Signer
required
The payer and signer for the delegation transaction
system_program
AccountInfo
required
The Solana system program
pda_to_delegate
AccountInfo
required
The PDA account being delegated
owner_program
AccountInfo
required
The program that owns the PDA being delegated
delegation_buffer
AccountInfo
required
Buffer account for storing the delegated account’s data
delegation_record
AccountInfo
required
Record account tracking the delegation state
delegation_metadata
AccountInfo
required
Metadata account for the delegation
delegation_program
AccountInfo
required
The MagicBlock delegation program
validator_account
AccountInfo
Optional validator account for specifying the ER validator

Pinocchio Delegation Pattern

Using Pinocchio SDK

The Pinocchio framework provides its own delegation function:
pinocchio-counter/src/processor.rs
use ephemeral_rollups_pinocchio::instruction::delegate_account;
use ephemeral_rollups_pinocchio::types::DelegateConfig;

pub fn process_delegate(
    _program_id: &Address,
    accounts: &[AccountView],
    bump: u8,
) -> ProgramResult {
    let [initializer, pda_to_delegate, owner_program, delegation_buffer, 
         delegation_record, delegation_metadata, _delegation_program, 
         system_program, rest @ ..] = accounts
    else {
        return Err(ProgramError::NotEnoughAccountKeys);
    };
    
    let validator = rest.first().map(|account| *account.address());

    let seed_1 = b"counter";
    let seed_2 = initializer.address().as_ref();
    let seeds: &[&[u8]] = &[seed_1, seed_2];

    let delegate_config = DelegateConfig {
        validator,
        ..Default::default()
    };

    delegate_account(
        &[
            initializer,
            pda_to_delegate,
            owner_program,
            delegation_buffer,
            delegation_record,
            delegation_metadata,
            system_program,
        ],
        seeds,
        bump,
        delegate_config,
    )?;

    Ok(())
}
The Pinocchio pattern requires an explicit bump parameter for PDA derivation.

Best Practices

  1. Always validate PDA seeds - Ensure the PDA can be properly derived before delegation
  2. Specify validator when needed - Use the validator parameter for targeting specific ER nodes
  3. Use remaining_accounts for flexibility - Pass optional validator through remaining accounts
  4. Set appropriate commit frequency - Balance between data freshness and transaction costs
  • Commit Instructions - Manual commit and undelegation
  • Program Instructions - Other program-specific operations

Build docs developers (and LLMs) love