CPI's met PDA-ondertekenaars

Samenvatting

Gebruik invoke_signed wanneer het aanroepende programma namens een PDA die het bezit moet ondertekenen. De runtime leidt PDA-pubkeys af van de verstrekte ondertekeningsseeds en voegt ze toe aan de set geldige ondertekenaars voordat de rechtencontrole plaatsvindt.

CPI's met PDA-ondertekenaars

Wanneer een CPI een PDA-ondertekenaar vereist, gebruik invoke_signed met de ondertekeningsseeds die gebruikt worden om de PDA af te leiden. Voor details over hoe de runtime PDA-handtekeningen verifieert, zie PDA-ondertekening.

Invoke signed
pub fn invoke_signed(
instruction: &Instruction,
account_infos: &[AccountInfo],
signers_seeds: &[&[&[u8]]],
) -> ProgramResult {
// --snip--
invoke_signed_unchecked(instruction, account_infos, signers_seeds)
}

De onderstaande voorbeelden maken een CPI met PDA-ondertekenaars met behulp van Anchor en Native Rust. Elk voorbeeld bevat een enkele instructie om SOL over te maken van een PDA naar een ontvangeraccount, met behulp van een CPI ondertekend door de PDA.

Anchor

De volgende voorbeelden tonen twee benaderingen voor het implementeren van CPI's in een Anchor-programma. De voorbeelden zijn functioneel equivalent, maar demonstreren verschillende abstractieniveaus.

  • Voorbeeld 1: Gebruikt Anchor's CpiContext en helperfunctie.
  • Voorbeeld 2: Gebruikt de system_instruction::transfer-functie uit solana_program-crate. (Voorbeeld 1 is een abstractie van deze implementatie.)
  • Voorbeeld 3: Construeert de CPI-instructie handmatig. Deze benadering is nuttig wanneer er geen crate beschikbaar is om de instructie te bouwen die je wilt aanroepen.
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

Het onderstaande voorbeeld maakt een CPI met PDA-ondertekenaars vanuit een programma geschreven in Native Rust. Het bevat een enkele instructie die SOL overdraagt van een PDA-account naar een ander. De CPI wordt ondertekend door het PDA-account. (Het testbestand gebruikt LiteSVM om het programma te testen.)

use borsh::BorshDeserialize;
use solana_program::{
account_info::AccountInfo,
entrypoint,
entrypoint::ProgramResult,
program::invoke_signed,
program_error::ProgramError,
pubkey::Pubkey,
system_instruction,
};
// Declare program entrypoint
entrypoint!(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 data
let instruction = ProgramInstruction::unpack(instruction_data)?;
// Process instruction
match instruction {
ProgramInstruction::SolTransfer { amount } => {
// Parse accounts
let [pda_account_info, recipient_info, system_program_info] = accounts else {
return Err(ProgramError::NotEnoughAccountKeys);
};
// Derive PDA and verify it matches the account provided by client
let 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 instruction
let transfer_ix = system_instruction::transfer(
pda_account_info.key,
recipient_info.key,
amount,
);
// Create signer seeds for PDA
let signer_seeds: &[&[&[u8]]] = &[&[b"pda", recipient_pubkey.as_ref(), &[bump_seed]]];
// Invoke the transfer instruction with PDA as signer
invoke_signed(
&transfer_ix,
&[
pda_account_info.clone(),
recipient_info.clone(),
system_program_info.clone(),
],
signer_seeds,
)?;
Ok(())
}
}
}

Is this page helpful?

Inhoudsopgave

Pagina Bewerken

Beheerd door

© 2026 Solana Foundation.
Alle rechten voorbehouden.
Blijf Verbonden