跳转到主要内容

快速入口

查看其他实现中的基础计数器示例:

GitHub

Native Rust 实现

GitHub

Pinocchio 实现

Guide

本地开发


分步指南

构建你的程序,并通过 MagicBlock 的 Delegation Program DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh 加入 delegation hooks 完成升级:
1

编写程序并添加 delegation instructions

按照你平常的方式编写 Solana 程序。
2

在 Base Layer 上 Delegate PDA

添加 CPI hooks,使状态账户能够在 Ephemeral Rollup 会话中完成 delegate、commit 和 undelegate。

这些公共验证器可用于开发环境。请确保在你的委托指令中添加对应的 ER 验证器:

主网
  • 亚洲 (as.magicblock.app): MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57
  • 欧盟 (eu.magicblock.app): MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e
  • 美国 (us.magicblock.app): MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd
  • TEE (mainnet-tee.magicblock.app): MTEWGuqxUpYZGFJQcp8tLN7x5v9BSeoFHYWQQ3n3xzo
开发网
  • 亚洲 (devnet-as.magicblock.app): MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57
  • 欧盟 (devnet-eu.magicblock.app): MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e
  • 美国 (devnet-us.magicblock.app): MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd
  • TEE (devnet-tee.magicblock.app): FnE6VJT5QNZdedZPnCoLsARgBwoE6DeJNjBs2H1gySXA
本地网络
  • 本地 ER (localhost:7799): mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev
3

在 ER 上 Commit PDA

使用 Solana CLI 将程序直接部署到 Solana 上。
4

在 ER 上 Undelegate PDA

在链上和链下发送无需修改且符合 SVM RPC 规范的交易。

Counter 示例

你可能需要以下软件包,其他版本也可能兼容:
软件版本安装指南
Solana2.3.13安装 Solana
Rust1.85.0安装 Rust
Node24.10.0安装 Node

代码片段

该程序实现了两个主要 instruction:
  1. InitializeCounter:初始化并将计数器设为 0(在 Base Layer 调用)
  2. IncreaseCounter:将已初始化的计数器增加 X(可在 Base Layer 或 ER 调用)
该程序还为计数器的委托与取消委托实现了专用 instruction:
  1. Delegate:将计数器从 Base Layer 委托到 ER(在 Base Layer 调用)
  2. CommitAndUndelegate:安排将计数器从 ER 同步回 Base Layer,并在 ER 上取消委托(在 ER 调用)
  3. Commit:安排将计数器从 ER 同步回 Base Layer(在 ER 调用)
  4. Undelegate:在 Base Layer 上取消委托计数器(通过 validator CPI 在 Base Layer 调用)
The undelegation callback discriminator [196, 28, 41, 206, 48, 37, 51, 167] and its instruction processor must be specified in your program. This instruction triggered by Delegation Program reverts account ownership on the Base Layer after calling undelegation on ER.With [#ephemeral] Anchor macro from MagicBlock’s Ephemeral Rollup SDK, the undelegation callback discriminator and processor are injected into your program.
下面是程序的核心结构:
pub enum ProgramInstruction {
    InitializeCounter,
    IncreaseCounter {
        increase_by: u64
    },
    Delegate,
    CommitAndUndelegate,
    Commit,
    Undelegate {
        pda_seeds: Vec<Vec<u8>>
    }
}

#[derive(BorshDeserialize)]
struct IncreaseCounterPayload {
    increase_by: u64,
}

impl ProgramInstruction {
    pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
        // Ensure the input has at least 8 bytes for the variant
        if input.len() < 8 {
            return Err(ProgramError::InvalidInstructionData);
        }

        // Extract the first 8 bytes as variant
        let (variant_bytes, rest) = input.split_at(8);
        let mut variant = [0u8; 8];
        variant.copy_from_slice(variant_bytes);

        Ok(match variant {
            [0, 0, 0, 0, 0, 0, 0, 0] => Self::InitializeCounter,
            [1, 0, 0, 0, 0, 0, 0, 0] => {
                let payload = IncreaseCounterPayload::try_from_slice(rest)?;
                Self::IncreaseCounter {
                    increase_by: payload.increase_by,
                }
            },
            [2, 0, 0, 0, 0, 0, 0, 0] => Self::Delegate,
            [3, 0, 0, 0, 0, 0, 0, 0] => Self::CommitAndUndelegate,
            [4, 0, 0, 0, 0, 0, 0, 0] => Self::Commit,
            [196, 28, 41, 206, 48, 37, 51, 167] => {
                let pda_seeds: Vec<Vec<u8>> = Vec::<Vec<u8>>::try_from_slice(rest)?;
                Self::Undelegate {
                    pda_seeds
                }
            }
            _ => return Err(ProgramError::InvalidInstructionData),
        })
    }
}
你的 “Undelegate” instruction 必须使用完全一致的 discriminator。它不会 由你主动调用,而是由 Base Layer 上的 validator 在 ER 中完成账户 undelegate 后,通过 CPI 回调进入你的程序。
⬆️ Back to Top

进阶代码片段

当调整已委托 PDA 的大小时:
  • PDA 必须有足够的 lamports,才能在新账户大小下继续保持 rent-exempt。
  • 如果需要额外 lamports,则 payer account 也必须已委托,以补足差额。
  • PDA 必须由程序持有,且交易中必须包含转移 lamports 所需的 signer。
  • 使用 system_instruction::allocate
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct Counter {
    pub count: u64,
}

// Resize counter account
pub fn resize_counter_account(
    counter_acc: &AccountInfo,
    payer: &AccountInfo,
    program_id: &Pubkey,
    new_size: usize,
    bump: u8,
) -> ProgramResult {
    let rent = Rent::get()?;
    let lamports_required = rent.minimum_balance(new_size);

    let current_lamports = counter_acc.lamports();
    if lamports_required > current_lamports {
        let lamports_to_add = lamports_required - current_lamports;
        invoke_signed(
            &system_instruction::transfer(
                &payer.key,
                &counter_acc.key,
                lamports_to_add,
            ),
            &[payer.clone(), counter_acc.clone()],
            &[&[COUNTER_SEED, &[bump]]],
        )?;
    }

    // Allocate new size
    invoke_signed(
        &system_instruction::allocate(&counter_acc.key, new_size as u64),
        &[counter_acc.clone()],
        &[&[COUNTER_SEED, &[bump]]],
    )?;

    // Assign back to program
    invoke_signed(
        &system_instruction::assign(&counter_acc.key, program_id),
        &[counter_acc.clone()],
        &[&[COUNTER_SEED, &[bump]]],
    )?;

    msg!("Counter account resized to {} bytes", new_size);
    Ok(())
}
⬆️ Back to Top

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