クイックアクセス
Magic Actions Example
GitHub でリファレンス実装を見る
1) アクション命令を作成する
update_leaderboard 命令はコミット完了直後にベースレイヤーで実行されます。アカウントコンテキストに付与された #[action] 属性により、コミット後アクションから呼び出し可能であることが示されます。
// 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) アクション付きコミット命令を構築する
commit_and_update_leaderboard コミット命令は ER 上で実行されます。MagicIntentBundleBuilder を使用してコミットとコミット後アクションの両方を magic_context に予約します — ER トランザクションがベースレイヤーへ確定されるとき、両者は同時に適用されます。
// commit action instruction on ER
pub fn commit_and_update_leaderboard(ctx: Context<CommitAndUpdateLeaderboard>) -> Result<()> {
// Build the post-commit action that updates the leaderboard on base layer
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,
// Signer that pays transaction fees for the action from its escrow PDA
escrow_authority: ctx.accounts.payer.to_account_info(),
compute_units: 200_000,
};
// Schedule commit + post-commit action on magic_context
MagicIntentBundleBuilder::new(
ctx.accounts.payer.to_account_info(),
ctx.accounts.magic_context.to_account_info(),
ctx.accounts.magic_program.to_account_info(),
)
.commit(&[ctx.accounts.counter.to_account_info()])
.add_post_commit_actions([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 = [COUNTER_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>,
}
複数のアクションを実行する
複数のアカウントをコミットし、複数のアクションを 1 回の呼び出しで連鎖させることができます。アクションはadd_post_commit_actions に渡された順序で順次実行されます。
// Chain several actions — they execute sequentially on base layer after the commit lands.
MagicIntentBundleBuilder::new(
ctx.accounts.payer.to_account_info(),
ctx.accounts.magic_context.to_account_info(),
ctx.accounts.magic_program.to_account_info(),
)
.commit(&[
ctx.accounts.counter.to_account_info(),
// ... additional committed accounts
])
.add_post_commit_actions([action_1, action_2, action_3])
.build_and_invoke()?;
委任解除と一緒にアクションを実行する
アクションは委任解除にも連鎖できます — counter のコミット、委任解除、アクションすべてが同一の ER トランザクション内でアトミックに実行されます。// Commit, undelegate, AND execute actions — all atomically on base layer after the ER transaction seals.
MagicIntentBundleBuilder::new(
ctx.accounts.payer.to_account_info(),
ctx.accounts.magic_context.to_account_info(),
ctx.accounts.magic_program.to_account_info(),
)
.commit_and_undelegate(&[ctx.accounts.counter.to_account_info()])
.add_post_commit_actions([action])
.build_and_invoke()?;

