在本节中,将通过添加 Cross Program Invocations (CPIs) 来更新上一节 PDA 中的 CRUD 程序。CPIs 是一种允许 Solana 程序相互调用的功能。
本教程还展示了在进行跨程序调用时,程序如何为 Program Derived Addresses (PDAs) "签名"。
需要修改 update 和 delete 指令,通过调用 System
Program 处理账户间的 SOL 转账。
本节的目的是通过使用 Anchor 框架在 Solana 程序中实现 CPIs 的过程,基于上一节中探讨的 PDA 概念进行构建。有关更多详细信息,请参阅 Cross Program Invocation 页面。
作为参考,此链接包含完成 PDA 和 CPI 部分后的最终代码。
本节的起始代码仅包括完成的 PDA 部分。
更新 Update 指令
首先,程序需要通过修改 Update 结构体和 update
函数,实现一个简单的“付费更新”机制。
首先,更新 lib.rs 文件,将 system_program 模块中的内容引入作用域。
use anchor_lang::system_program::{transfer, Transfer};
接下来,更新 Update 结构体,新增一个名为 vault_account
的账户。该账户由程序控制,在用户更新消息账户时接收 SOL。
#[account(mut,seeds = [b"vault", user.key().as_ref()],bump,)]pub vault_account: SystemAccount<'info>,
接下来,在 update 指令中添加 CPI 逻辑,将 0.001 SOL 从用户账户转入金库账户。
let transfer_accounts = Transfer {from: ctx.accounts.user.to_account_info(),to: ctx.accounts.vault_account.to_account_info(),};let cpi_context = CpiContext::new(ctx.accounts.system_program.to_account_info(),transfer_accounts,);transfer(cpi_context, 1_000_000)?;
重新构建程序。
$build
更新删除指令
现在,通过修改 Delete 结构体和 delete 函数,添加“删除时退款”机制。
首先,更新 Delete 结构体,加入
vault_account。这样,在用户关闭消息账户时,可以将金库中的所有 SOL 返还给用户。
#[account(mut,seeds = [b"vault", user.key().as_ref()],bump,)]pub vault_account: SystemAccount<'info>,
还需要添加 system_program,因为转账的 CPI 需要调用 System Program。
pub system_program: Program<'info, System>,
接下来,在 delete 指令中添加 CPI 逻辑,将 SOL 从金库账户转回用户账户。
let user_key = ctx.accounts.user.key();let signer_seeds: &[&[&[u8]]] =&[&[b"vault", user_key.as_ref(), &[ctx.bumps.vault_account]]];let transfer_accounts = Transfer {from: ctx.accounts.vault_account.to_account_info(),to: ctx.accounts.user.to_account_info(),};let cpi_context = CpiContext::new(ctx.accounts.system_program.to_account_info(),transfer_accounts,).with_signer(signer_seeds);transfer(cpi_context, ctx.accounts.vault_account.lamports())?;
注意,_ctx: Context<Delete> 变为
ctx: Context<Delete>,以便在函数体内使用 context。
重新构建程序。
$build
重新部署程序
在进行这些更改后,重新部署更新的程序。这确保了修改后的程序可以用于测试。在 Solana 上,更新程序只需将程序部署到相同的程序 ID 即可。
确保您的 Playground 钱包中有 devnet SOL。可以从 Solana Faucet 获取 devnet SOL。
$deploy
更新测试文件
接下来,更新 anchor.test.ts
文件,在指令中包含新的金库账户。这需要派生金库 PDA,并在 update 和 delete 指令调用中包含它。
派生 Vault PDA
首先,添加 Vault PDA 的派生:
const [vaultPda, vaultBump] = PublicKey.findProgramAddressSync([Buffer.from("vault"), wallet.publicKey.toBuffer()],program.programId);
修改更新测试
然后,更新 update 指令,将 vaultAccount 包含进去。
const transactionSignature = await program.methods.update(message).accounts({messageAccount: messagePda,vaultAccount: vaultPda}).rpc({ commitment: "confirmed" });
修改删除测试
然后,更新 delete 指令,将 vaultAccount 包含进去。
const transactionSignature = await program.methods.delete().accounts({messageAccount: messagePda,vaultAccount: vaultPda}).rpc({ commitment: "confirmed" });
下一步
恭喜您完成了 Solana 快速入门指南。您已经通过实践掌握了以下关键的 Solana 概念:
- 从账户中获取和读取数据
- 构建和发送交易
- 部署和更新 Solana 程序
- 使用程序派生地址 (PDAs)
- 进行跨程序调用 (CPIs)
为了加深对这些概念的理解,请查看 核心概念 文档,其中提供了本指南中涉及主题的详细解释。
探索更多示例
如果您更喜欢通过示例学习,请查看 程序示例库,其中包含各种示例程序。
Solana Playground 提供了一个便捷功能,允许您通过 GitHub 链接导入或查看项目。例如,打开此 Solana Playground 链接 以查看此 Github 仓库 中的 Anchor 项目。
点击 Import 按钮,输入项目名称,即可将其添加到你的 Solana
Playground 项目列表中。项目导入后,所有更改都会自动保存和持久化。
Is this page helpful?

