跳转到主要内容

快速访问

Magic Actions Example

在 GitHub 上查看参考实现

1) 创建 action 指令

update_leaderboard 指令会在提交后立即于基础层执行。
// program instruction
pub fn update_leaderboard(ctx: Context<UpdateLeaderboard>) -> Result<()> {
    let leaderboard = &mut ctx.accounts.leaderboard;
    let counter_info = &mut ctx.accounts.counter.to_account_info();
    let mut data: &[u8] = &counter_info.try_borrow_data()?;
    let counter = Counter::try_deserialize(&mut data)?;

    if counter.count > leaderboard.high_score {
        leaderboard.high_score = counter.count;
    }

    msg!(
        "Leaderboard updated! High score: {}",
        leaderboard.high_score
    );
    Ok(())
}

// instruction context
#[action]
#[derive(Accounts)]
pub struct UpdateLeaderboard<'info> {
    #[account(mut, seeds = [LEADERBOARD_SEED], bump)]
    pub leaderboard: Account<'info, Leaderboard>,
    /// CHECK: PDA owner depends on: 1) Delegated: Delegation Program; 2) Undelegated: Your program ID
    pub counter: UncheckedAccount<'info>,
}

2) 构建带有 action 的提交指令

commit_and_update_leaderboard 提交指令在 ER 上执行,并安排带 action 的提交。
// commit action instruction on ER
pub fn commit_and_update_leaderboard(ctx: Context<CommitAndUpdateLeaderboard>) -> Result<()> {
    // Create action instruction
    let instruction_data =
        anchor_lang::InstructionData::data(&crate::instruction::UpdateLeaderboard {});
    let action_args = ActionArgs::new(instruction_data);
    let action_accounts = vec![
        ShortAccountMeta {
            pubkey: ctx.accounts.leaderboard.key(),
            is_writable: true,
        },
        ShortAccountMeta {
            pubkey: ctx.accounts.counter.key(),
            is_writable: false,
        },
    ];
    let action = CallHandler {
        destination_program: crate::ID,
        accounts: action_accounts,
        args: action_args,
        escrow_authority: ctx.accounts.payer.to_account_info(), // Signer authorized to pay transaction fees for action from escrow PDA
        compute_units: 200_000,
    };

    // Build commit and action instruction
    let magic_action = MagicInstructionBuilder {
        payer: ctx.accounts.payer.to_account_info(),
        magic_context: ctx.accounts.magic_context.to_account_info(),
        magic_program: ctx.accounts.magic_program.to_account_info(),
        magic_action: MagicAction::Commit(CommitType::WithHandler {
            commited_accounts: vec![ctx.accounts.counter.to_account_info()],
            call_handlers: vec![action],
        }),
    };

    // Invoke
    magic_action.build_and_invoke()?;

    Ok(())
}

// commit action context on ER
#[commit]
#[derive(Accounts)]
pub struct CommitAndUpdateLeaderboard<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,

    #[account(mut, seeds = [TEST_PDA_SEED], bump)]
    pub counter: Account<'info, Counter>,

    /// CHECK: Leaderboard PDA - not mut here, writable set in handler
    #[account(seeds = [LEADERBOARD_SEED], bump)]
    pub leaderboard: UncheckedAccount<'info>,

    /// CHECK: Your program ID
    pub program_id: AccountInfo<'info>,
}

执行多个 action

你可以提交多个账户并串联多个 action。各个 action 会按顺序执行。
let magic_builder = MagicInstructionBuilder {
    payer: ctx.accounts.payer.to_account_info(),
    magic_context: ctx.accounts.magic_context.to_account_info(),
    magic_program: ctx.accounts.magic_program.to_account_info(),
    magic_action: MagicAction::Commit(CommitType::WithHandler {
        commited_accounts: vec![ctx.accounts.counter.to_account_info(), ...],
        call_handlers: vec![call_handler, ...],
    }),
};

其他 action

Actions 也可以在取消委托时执行,或者在没有任何提交的情况下执行。
// Undelegate + Action
let magic_action = MagicInstructionBuilder {
    payer: ctx.accounts.payer.to_account_info(),
    magic_context: ctx.accounts.magic_context.to_account_info(),
    magic_program: ctx.accounts.magic_program.to_account_info(),
    magic_action: MagicAction::CommitAndUndelegate(CommitType::WithHandler {
        commited_accounts: vec![ctx.accounts.counter.to_account_info()],
        call_handlers: vec![action],
    }),
};

// Action only
let magic_action = MagicInstructionBuilder {
    payer: ctx.accounts.payer.to_account_info(),
    magic_context: ctx.accounts.magic_context.to_account_info(),
    magic_program: ctx.accounts.magic_program.to_account_info(),
    magic_action: MagicAction::BaseActions(vec![action]),
};