本文详细介绍了Solana编程中的跨程序调用(CPI)概念及其重要性,并提供了如何使用CPI的指导,包括使用Solana的invoke方法和Anchor框架创建CPI的示例代码。文章结构清晰,包含多段实例代码,并且提供了丰富的资源链接,适合对Solana开发感兴趣的读者。
在 Solana 编程中,跨程序调用(Cross Program Invocation,CPI)是一种调用另一个 Solana 程序中的函数的方法。CPI 是一种强大的工具,可以让你通过允许你从其他程序调用函数来构建更复杂的程序。在本指南中,你将学习什么是 CPI,如何使用它们,以及如何使用 Anchor 创建它们。
推荐阅读:Solana 基础参考指南
CPI 允许 Solana 上的任何程序调用其他程序,这在使程序更具组合性方面是非常重要的。组合性实际上是将不同的程序(或其他程序中的元素)组合在一起,为最终用户创造独特价值的能力。使用 CPI 就像在你的程序中调用 API 方法一样——它针对特定程序的特定指令。你可以使用 CPI 的可能性无穷无尽,但以下是一些示例:
这些只是一些示例;不要局限于这些。CPI 是一种强大的工具,可以以多种方式使用,以为最终用户创造独特的价值。
要使用 CPI,你需要以下信息:
调用 CPI 需要使用 Solana crate 的 invoke
或 invoke_signed
方法来执行 CPI:
invoke
在交易由原始交易签名者(而不是 PDA)签名时使用pub fn invoke(
instruction: &Instruction,
account_infos: &[AccountInfo<'_>]
) -> ProgramResult
invoke_signed
在交易由 PDA(通过传递正确的种子生成)签名时使用pub fn invoke_signed(
instruction: &Instruction,
account_infos: &[AccountInfo<'_>],
signers_seeds: &[&[&[u8]]]
) -> ProgramResult
下面是使用 invoke
方法的 CPI 示例:
use borsh::BorshDeserialize;
use cross_program_invocatio_native_lever::SetPowerStatus;
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint,
entrypoint::ProgramResult,
instruction::{AccountMeta, Instruction},
program::invoke,
pubkey::Pubkey,
};
entrypoint!(pull_lever);
fn pull_lever(
_program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
let accounts_iter = &mut accounts.iter();
let power = next_account_info(accounts_iter)?;
let lever_program = next_account_info(accounts_iter)?;
let set_power_status_instruction = SetPowerStatus::try_from_slice(instruction_data)?;
let ix = Instruction::new_with_borsh(
*lever_program.key, // 我们的杠杆程序的 ID
&set_power_status_instruction, // 传递指令
vec![AccountMeta::new(*power.key, false)], // 仅是另一个程序所需的账户
);
invoke(&ix, &[power.clone()])
}
在这个示例中,pull_lever
函数定义了一条指令 ix
,它传递了 lever_program
程序的公钥、指令数据(set_power_status_instruction
),以及一个账户元数据向量(在此情况下,仅为 power
账户)。然后调用 invoke
方法以指令和 power
账户执行 CPI。
尽管你可以使用前面概述的方法在 Anchor 中创建 CPI,但 Anchor 使得 CPI 的使用变得更加简单。与 Anchor 依赖 Context 以简化对程序的调用账户类似,Anchor 也使用 CpiContext 结构来精简调用 CPI 的过程。可以使用 new
或 new_with_signer
方法(如果使用 PDA)创建 CpiContext:
// 如果使用交易签名者
CpiContext::new(cpi_program, cpi_accounts)
// 如果使用 PDA
CpiContext::new_with_signer(cpi_program, cpi_accounts, seeds)
cpi_program
是你想要调用的程序的程序 ID,cpi_accounts
是指令所需的账户。如果你使用 PDA,还必须传递种子以推导 PDA。
一旦创建了 CpiContext,你就可以调用程序的导入方法来执行 CPI:
use program_to_call;
program_to_call::function_to_call(cpi_context, instruction_data)
下面是使用 Anchor 的相同 pull_lever
指令的样子:
use lever::cpi::accounts::SetPowerStatus;
use lever::program::Lever;
use lever::{self, PowerStatus};
//...
pub fn pull_lever(ctx: Context<PullLever>, name: String) -> anchor_lang::Result<()> {
lever::cpi::switch_power(
CpiContext::new(
ctx.accounts.lever_program.to_account_info(),
SetPowerStatus {
power: ctx.accounts.power.to_account_info(),
},
),
name,
)
}
*来源:Solana 开发者示例程序
我们在 lever::cpi
模块上使用 switch_power
方法来调用 CPI。此方法接受一个 CpiContext 和指令数据(在此情况下为 name
)。
在 Anchor 中需要注意的一件重要事项是,我们需要在 Cargo.toml
文件中将 lever
程序添加为依赖项。我们将使用 features = ["cpi"]
来使用不仅是 lever 的类型,还包括其指令构建器和 cpi 函数。通过启用 cpi 功能,手动程序能够访问 lever::cpi
模块。Anchor 会自动生成此模块,其中包含为程序量身定制的指令构建器和 CPI 辅助功能。
[dependencies]
anchor-lang = "0.28.0"
lever = { path = "../lever", features = ["cpi"] }
就是这样!如果你想构建一个带有 CPI 的示例程序,请查看我们的指南:如何使用 Anchor 转移 SOL 和 SPL 代币,它将引导你使用 CPI 到 SPL 代币程序转移代币。你还可以查看下面的资源和示例部分以获得更多示例。
CPI 是使你的程序更具组合性并为最终用户创造独特价值的好方法。你现在拥有构建优秀作品的知识和工具!如果你遇到困难、想要提问或只是想谈谈你正在构建的内容,请在 Discord 或 Twitter 上与我们联系!
让我们知道如果你有任何反馈或新主题的请求。我们很乐意听到你的意见。
- 原文链接: quicknode.com/guides/sol...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!