CPI dengan penanda tangan PDA

Ringkasan

Gunakan invoke_signed ketika program pemanggil perlu menandatangani atas nama PDA yang dimilikinya. Runtime menurunkan pubkey PDA dari signer seeds yang disediakan dan menambahkannya ke set penanda tangan yang valid sebelum pemeriksaan privilege.

CPI dengan penanda tangan PDA

Ketika CPI memerlukan penanda tangan PDA, gunakan invoke_signed dengan signer seeds yang digunakan untuk menurunkan PDA. Untuk detail tentang bagaimana runtime memverifikasi tanda tangan PDA, lihat 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)
}

Contoh di bawah ini membuat CPI dengan penanda tangan PDA menggunakan Anchor dan Native Rust. Setiap contoh mencakup satu instruksi untuk mentransfer SOL dari PDA ke akun penerima, menggunakan CPI yang ditandatangani oleh PDA.

Anchor

Contoh berikut menunjukkan dua pendekatan untuk mengimplementasikan CPI dalam program Anchor. Contoh-contoh ini secara fungsional setara, tetapi mendemonstrasikan tingkat abstraksi yang berbeda.

  • Contoh 1: Menggunakan CpiContext dan fungsi helper Anchor.
  • Contoh 2: Menggunakan fungsi system_instruction::transfer dari crate solana_program. (Contoh 1 adalah abstraksi dari implementasi ini.)
  • Contoh 3: Membangun instruksi CPI secara manual. Pendekatan ini berguna ketika tidak ada crate yang tersedia untuk membantu membangun instruksi yang ingin Anda invoke.
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

Contoh di bawah ini melakukan CPI dengan penandatangan PDA dari program yang ditulis dalam Native Rust. Ini mencakup satu instruksi yang mentransfer SOL dari akun PDA ke akun lain. CPI ditandatangani oleh akun PDA. (File pengujian menggunakan LiteSVM untuk menguji program.)

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?

Daftar Isi

Edit Halaman

Dikelola oleh

© 2026 Yayasan Solana.
Semua hak dilindungi.
Terhubung