Riepilogo
Usa invoke_signed quando il programma chiamante deve firmare per conto
di un PDA di sua proprietà. Il runtime deriva le chiavi pubbliche PDA dai seed
del firmatario forniti e li aggiunge all'insieme dei firmatari validi prima
del controllo dei privilegi.
CPI con firmatari PDA
Quando una CPI richiede un firmatario PDA, usa
invoke_signed
con i seed del firmatario utilizzati per derivare il
PDA. Per dettagli su come il runtime verifica le firme PDA,
vedi firma PDA.
pub fn invoke_signed(instruction: &Instruction,account_infos: &[AccountInfo],signers_seeds: &[&[&[u8]]],) -> ProgramResult {// --snip--invoke_signed_unchecked(instruction, account_infos, signers_seeds)}
Gli esempi seguenti effettuano una CPI con firmatari PDA utilizzando Anchor e Rust nativo. Ogni esempio include una singola istruzione per trasferire SOL da un PDA a un account destinatario, utilizzando una CPI firmata dal PDA.
Anchor
Gli esempi seguenti mostrano due approcci per implementare le CPI in un programma Anchor. Gli esempi sono funzionalmente equivalenti, ma dimostrano diversi livelli di astrazione.
- Esempio 1: usa
CpiContextdi Anchor e la funzione helper. - Esempio 2: usa la funzione
system_instruction::transferdalla cratesolana_program. (L'esempio 1 è un'astrazione di questa implementazione.) - Esempio 3: costruisce l'istruzione CPI manualmente. Questo approccio è utile quando non è disponibile una crate per aiutare a costruire l'istruzione che vuoi invocare.
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
L'esempio seguente effettua una CPI con firmatari PDA da un programma scritto in Rust nativo. Include una singola istruzione che trasferisce SOL da un account PDA a un altro. La CPI è firmata dall'account PDA. (Il file di test utilizza LiteSVM per testare il programma.)
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?