Підсумок
Використовуйте invoke для CPI, де всі необхідні підписувачі вже
підписали оригінальну транзакцію. Підпис PDA не потрібен. Привілеї підписувача
та запису поширюються від викликача до викликаного.
CPI без підписувачів PDA
Коли CPI не потребує підписувачів PDA, використовується функція
invoke.
Функція invoke викликає функцію invoke_signed з порожнім масивом
signers_seeds. Порожній масив підписувачів вказує на те, що PDA для
підпису не потрібні.
pub fn invoke(instruction: &Instruction, account_infos: &[AccountInfo]) -> ProgramResult {invoke_signed(instruction, account_infos, &[])}
Наведені нижче приклади виконують CPI за допомогою Anchor та Native Rust. Вони включають одну інструкцію, яка переказує SOL з одного акаунта на інший.
Anchor
Наступні приклади демонструють два підходи до реалізації CPI у програмі Anchor. Приклади функціонально еквівалентні, але демонструють різні рівні абстракції.
- Приклад 1: використовує
CpiContextта допоміжну функцію Anchor. - Приклад 2: використовує функцію
system_instruction::transferз крейтаsolana_program. - Приклад 3: конструює інструкцію CPI вручну. Цей підхід корисний, коли не існує крейта для побудови інструкції, яку ви хочете викликати.
use anchor_lang::prelude::*;use anchor_lang::system_program::{transfer, Transfer};declare_id!("9AvUNHjxscdkiKQ8tUn12QCMXtcnbR9BVGq3ULNzFMRi");#[program]pub mod cpi {use super::*;pub fn sol_transfer(ctx: Context<SolTransfer>, amount: u64) -> Result<()> {let from_pubkey = ctx.accounts.sender.to_account_info();let to_pubkey = ctx.accounts.recipient.to_account_info();let program_id = ctx.accounts.system_program.to_account_info();let cpi_context = CpiContext::new(program_id,Transfer {from: from_pubkey,to: to_pubkey,},);transfer(cpi_context, amount)?;Ok(())}}#[derive(Accounts)]pub struct SolTransfer<'info> {#[account(mut)]sender: Signer<'info>,#[account(mut)]recipient: SystemAccount<'info>,system_program: Program<'info, System>,}
Rust
Наступний приклад показує, як виконати CPI з програми, написаної на Native Rust. Він включає одну інструкцію, яка переказує SOL з одного акаунта на інший. (Тестовий файл використовує LiteSVM для тестування програми.)
use borsh::BorshDeserialize;use solana_program::{account_info::AccountInfo,entrypoint,entrypoint::ProgramResult,program::invoke,program_error::ProgramError,pubkey::Pubkey,system_instruction,};// Declare program entrypointentrypoint!(process_instruction);// Define program instructions#[derive(BorshDeserialize)]enum ProgramInstruction {SolTransfer { amount: u64 },}impl ProgramInstruction {fn unpack(input: &[u8]) -> Result<Self, ProgramError> {Self::try_from_slice(input).map_err(|_| ProgramError::InvalidInstructionData)}}pub fn process_instruction(_program_id: &Pubkey,accounts: &[AccountInfo],instruction_data: &[u8],) -> ProgramResult {// Deserialize instruction datalet instruction = ProgramInstruction::unpack(instruction_data)?;// Process instructionmatch instruction {ProgramInstruction::SolTransfer { amount } => {// Parse accountslet [sender_info, recipient_info, system_program_info] = accounts else {return Err(ProgramError::NotEnoughAccountKeys);};// Verify the sender is a signerif !sender_info.is_signer {return Err(ProgramError::MissingRequiredSignature);}// Create and invoke the transfer instructionlet transfer_ix = system_instruction::transfer(sender_info.key,recipient_info.key,amount,);invoke(&transfer_ix,&[sender_info.clone(),recipient_info.clone(),system_program_info.clone(),],)?;Ok(())}}}
Is this page helpful?