在Solana上,交叉程序调用(CPI)指的是一个程序可以调用另一个程序的功能这种机制允许不同的程序相互协作,从而实现更复杂的功能和应用通过CPI,开发者可以在不同的智能合约之间传递信息和资产,使得在Solana生态系统内的操作更加灵活和强大

  • QuickNode
  • 发布于 2025-01-30 16:29
  • 阅读 16

本文详细介绍了Solana编程中的跨程序调用(CPI)概念及其重要性,并提供了如何使用CPI的指导,包括使用Solana的invoke方法和Anchor框架创建CPI的示例代码。文章结构清晰,包含多段实例代码,并且提供了丰富的资源链接,适合对Solana开发感兴趣的读者。

概述

在 Solana 编程中,跨程序调用(Cross Program Invocation,CPI)是一种调用另一个 Solana 程序中的函数的方法。CPI 是一种强大的工具,可以让你通过允许你从其他程序调用函数来构建更复杂的程序。在本指南中,你将学习什么是 CPI,如何使用它们,以及如何使用 Anchor 创建它们。

推荐阅读:Solana 基础参考指南

为什么 CPI 很重要?

CPI 允许 Solana 上的任何程序调用其他程序,这在使程序更具组合性方面是非常重要的。组合性实际上是将不同的程序(或其他程序中的元素)组合在一起,为最终用户创造独特价值的能力。使用 CPI 就像在你的程序中调用 API 方法一样——它针对特定程序的特定指令。你可以使用 CPI 的可能性无穷无尽,但以下是一些示例:

  • 一个 NFT 质押程序调用 CPI 到 Solana SPL 代币程序,以铸造可替代代币作为质押 NFT 的奖励
  • 一个游戏程序调用 CPI 到一个允许用户交换代币(游戏资产)的程序
  • 一个借贷协议调用 CPI 到一个流动性提供程序程序以借入代币

这些只是一些示例;不要局限于这些。CPI 是一种强大的工具,可以以多种方式使用,以为最终用户创造独特的价值。

如何使用 CPI

要使用 CPI,你需要以下信息:

  • 你想要调用的程序的指令,包括:
    • 你想要调用的程序的程序 ID
    • 指令所需的账户
    • 指令所需的任何数据
  • 指令所需账户的信息
  • 推导所需 PDA 的种子(如果使用 PDA 批准交易)

调用 CPI 需要使用 Solana crate 的 invokeinvoke_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()])
}

来源:Solana 开发者示例程序

在这个示例中,pull_lever 函数定义了一条指令 ix,它传递了 lever_program 程序的公钥、指令数据(set_power_status_instruction),以及一个账户元数据向量(在此情况下,仅为 power 账户)。然后调用 invoke 方法以指令和 power 账户执行 CPI。

使用 Anchor 创建 CPI

尽管你可以使用前面概述的方法在 Anchor 中创建 CPI,但 Anchor 使得 CPI 的使用变得更加简单。与 Anchor 依赖 Context 以简化对程序的调用账户类似,Anchor 也使用 CpiContext 结构来精简调用 CPI 的过程。可以使用 newnew_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 是使你的程序更具组合性并为最终用户创造独特价值的好方法。你现在拥有构建优秀作品的知识和工具!如果你遇到困难、想要提问或只是想谈谈你正在构建的内容,请在 DiscordTwitter 上与我们联系!

我们 ❤️ 反馈!

让我们知道如果你有任何反馈或新主题的请求。我们很乐意听到你的意见。

资源和示例

  • 原文链接: quicknode.com/guides/sol...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
QuickNode
QuickNode
江湖只有他的大名,没有他的介绍。