CPIs με υπογράφοντες PDA

Περίληψη

Χρησιμοποιήστε το invoke_signed όταν το πρόγραμμα κλήσης πρέπει να υπογράψει εκ μέρους ενός PDA που κατέχει. Το runtime παράγει τα δημόσια κλειδιά PDA από τους παρεχόμενους σπόρους υπογραφής και τα προσθέτει στο σύνολο των έγκυρων υπογραφόντων πριν από τον έλεγχο προνομίων.

CPIs με υπογράφοντες PDA

Όταν ένα CPI απαιτεί υπογράφοντα PDA, χρησιμοποιήστε το invoke_signed με τους σπόρους υπογραφής που χρησιμοποιούνται για την παραγωγή του PDA. Για λεπτομέρειες σχετικά με το πώς το runtime επαληθεύει τις υπογραφές PDA, δείτε PDA Signing.

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

Τα παρακάτω παραδείγματα πραγματοποιούν ένα CPI με υπογράφοντες PDA χρησιμοποιώντας Anchor και Native Rust. Κάθε παράδειγμα περιλαμβάνει μία μόνο εντολή για τη μεταφορά SOL από ένα PDA σε έναν λογαριασμό παραλήπτη, χρησιμοποιώντας ένα CPI υπογεγραμμένο από το PDA.

Anchor

Τα ακόλουθα παραδείγματα δείχνουν δύο προσεγγίσεις για την υλοποίηση CPIs σε ένα πρόγραμμα Anchor. Τα παραδείγματα είναι λειτουργικά ισοδύναμα, αλλά επιδεικνύουν διαφορετικά επίπεδα αφαίρεσης.

  • Παράδειγμα 1: Χρησιμοποιεί το CpiContext του Anchor και βοηθητική συνάρτηση.
  • Παράδειγμα 2: Χρησιμοποιεί τη συνάρτηση system_instruction::transfer από το crate solana_program. (Το παράδειγμα 1 είναι μια αφαίρεση αυτής της υλοποίησης.)
  • Παράδειγμα 3: Κατασκευάζει την εντολή CPI χειροκίνητα. Αυτή η προσέγγιση είναι χρήσιμη όταν δεν υπάρχει διαθέσιμο crate για να βοηθήσει στην κατασκευή της εντολής που θέλετε να επικαλεστείτε.
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

Το παρακάτω παράδειγμα πραγματοποιεί ένα CPI με υπογράφοντες PDA από ένα πρόγραμμα γραμμένο σε Native Rust. Περιλαμβάνει μία μόνο εντολή που μεταφέρει SOL από έναν λογαριασμό PDA σε έναν άλλο. Το CPI υπογράφεται από τον λογαριασμό PDA. (Το αρχείο δοκιμής χρησιμοποιεί το LiteSVM για να δοκιμάσει το πρόγραμμα.)

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?

Πίνακας Περιεχομένων

Επεξεργασία Σελίδας

Διαχειρίζεται από

© 2026 Ίδρυμα Solana.
Με επιφύλαξη παντός δικαιώματος.
Συνδεθείτε