Tóm tắt
Sử dụng invoke_signed khi chương trình gọi cần ký thay mặt cho một PDA
mà nó sở hữu. Runtime sẽ suy ra các pubkey PDA từ các signer seeds được cung
cấp và thêm chúng vào tập hợp các signer hợp lệ trước khi kiểm tra quyền.
CPI với PDA signers
Khi một CPI yêu cầu PDA signer, hãy sử dụng
invoke_signed
với signer seeds được dùng để suy ra
PDA. Để biết chi tiết về cách runtime xác minh chữ ký PDA, hãy
xem PDA Signing.
pub fn invoke_signed(instruction: &Instruction,account_infos: &[AccountInfo],signers_seeds: &[&[&[u8]]],) -> ProgramResult {// --snip--invoke_signed_unchecked(instruction, account_infos, signers_seeds)}
Các ví dụ dưới đây thực hiện CPI với PDA signers bằng Anchor và Native Rust. Mỗi ví dụ bao gồm một instruction duy nhất để chuyển SOL từ một PDA đến một tài khoản người nhận, sử dụng CPI được ký bởi PDA.
Anchor
Các ví dụ sau đây trình bày hai cách tiếp cận để triển khai CPI trong một chương trình Anchor. Các ví dụ này tương đương về mặt chức năng, nhưng thể hiện các mức độ trừu tượng khác nhau.
- Ví dụ 1: Sử dụng
CpiContextvà hàm helper của Anchor. - Ví dụ 2: Sử dụng hàm
system_instruction::transfertừ cratesolana_program. (Ví dụ 1 là một trừu tượng hóa của cách triển khai này.) - Ví dụ 3: Xây dựng instruction CPI thủ công. Cách tiếp cận này hữu ích khi không có crate nào có sẵn để giúp xây dựng instruction mà bạn muốn gọi.
use anchor_lang::prelude::*;use anchor_lang::system_program::{transfer, Transfer};declare_id!("BrcdB9sV7z9DvF9rDHG263HUxXgJM3iCQdF36TcxbFEn");#[program]pub mod cpi {use super::*;pub fn sol_transfer(ctx: Context<SolTransfer>, amount: u64) -> Result<()> {let from_pubkey = ctx.accounts.pda_account.to_account_info();let to_pubkey = ctx.accounts.recipient.to_account_info();let program_id = ctx.accounts.system_program.to_account_info();let seed = to_pubkey.key();let bump_seed = ctx.bumps.pda_account;let signer_seeds: &[&[&[u8]]] = &[&[b"pda", seed.as_ref(), &[bump_seed]]];let cpi_context = CpiContext::new(program_id,Transfer {from: from_pubkey,to: to_pubkey,},).with_signer(signer_seeds);transfer(cpi_context, amount)?;Ok(())}}#[derive(Accounts)]pub struct SolTransfer<'info> {#[account(mut,seeds = [b"pda", recipient.key().as_ref()],bump,)]pda_account: SystemAccount<'info>,#[account(mut)]recipient: SystemAccount<'info>,system_program: Program<'info, System>,}
Rust
Ví dụ dưới đây thực hiện CPI với PDA signers từ một chương trình được viết bằng Native Rust. Nó bao gồm một lệnh duy nhất chuyển SOL từ tài khoản PDA sang tài khoản khác. CPI được ký bởi tài khoản PDA. (Tệp kiểm thử sử dụng LiteSVM để kiểm thử chương trình.)
use borsh::BorshDeserialize;use solana_program::{account_info::AccountInfo,entrypoint,entrypoint::ProgramResult,program::invoke_signed,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 [pda_account_info, recipient_info, system_program_info] = accounts else {return Err(ProgramError::NotEnoughAccountKeys);};// Derive PDA and verify it matches the account provided by clientlet recipient_pubkey = recipient_info.key;let seeds = &[b"pda", recipient_pubkey.as_ref()];let (expected_pda, bump_seed) = Pubkey::find_program_address(seeds, program_id);if expected_pda != *pda_account_info.key {return Err(ProgramError::InvalidArgument);}// Create the transfer instructionlet transfer_ix = system_instruction::transfer(pda_account_info.key,recipient_info.key,amount,);// Create signer seeds for PDAlet signer_seeds: &[&[&[u8]]] = &[&[b"pda", recipient_pubkey.as_ref(), &[bump_seed]]];// Invoke the transfer instruction with PDA as signerinvoke_signed(&transfer_ix,&[pda_account_info.clone(),recipient_info.clone(),system_program_info.clone(),],signer_seeds,)?;Ok(())}}}
Is this page helpful?