استدعاءات البرامج المتقاطعة مع موقعي PDA

ملخص

استخدم invoke_signed عندما يحتاج البرنامج المستدعي للتوقيع نيابة عن PDA يمتلكه. يقوم وقت التشغيل باشتقاق مفاتيح PDA العامة من بذور الموقع المقدمة ويضيفها إلى مجموعة الموقعين الصالحين قبل التحقق من الصلاحيات.

استدعاءات البرامج المتقاطعة مع موقعي PDA

عندما يتطلب استدعاء برنامج متقاطع موقع PDA، استخدم invoke_signed مع بذور الموقع المستخدمة لاشتقاق PDA. للحصول على تفاصيل حول كيفية تحقق وقت التشغيل من توقيعات PDA، راجع توقيع PDA.

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

توضح الأمثلة أدناه كيفية إجراء استدعاء برنامج متقاطع مع موقعي PDA باستخدام Anchor و Native Rust. يتضمن كل مثال تعليمة واحدة لتحويل SOL من PDA إلى حساب مستلم، باستخدام استدعاء برنامج متقاطع موقع بواسطة PDA.

Anchor

توضح الأمثلة التالية طريقتين لتنفيذ استدعاءات البرامج المتقاطعة في برنامج Anchor. الأمثلة متكافئة وظيفياً، لكنها توضح مستويات مختلفة من التجريد.

  • المثال 1: يستخدم CpiContext من Anchor ودالة مساعدة.
  • المثال 2: يستخدم دالة system_instruction::transfer من حزمة solana_program. (المثال 1 هو تجريد لهذا التنفيذ.)
  • المثال 3: يبني تعليمة الاستدعاء المتقاطع يدوياً. هذا النهج مفيد عندما لا تتوفر حزمة للمساعدة في بناء التعليمة التي تريد استدعاءها.
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 من برنامج مكتوب بلغة 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 مؤسسة سولانا.
جميع الحقوق محفوظة.
تواصل معنا