المعاملات

للتفاعل مع شبكة سولانا، يجب عليك إرسال معاملة. يمكنك التفكير في المعاملة كمظروف يحتوي على عدة نماذج. كل نموذج هو تعليمة تخبر الشبكة بما يجب القيام به. إرسال المعاملة يشبه إرسال المظروف بالبريد حتى يمكن معالجة النماذج.

يوضح المثال أدناه نسخة مبسطة من معاملتين. عند معالجة المعاملة الأولى، ستنفذ تعليمة واحدة. عند معالجة المعاملة الثانية، ستنفذ ثلاث تعليمات بترتيب متسلسل: أولاً التعليمة 1، تليها التعليمة 2، ثم التعليمة 3.

المعاملات ذرية: إذا فشلت تعليمة واحدة، ستفشل المعاملة بأكملها ولن تحدث أي تغييرات.

رسم توضيحي مبسط يوضح معاملتينرسم توضيحي مبسط يوضح معاملتين

تتكون Transaction من المعلومات التالية:

  • signatures: مصفوفة من التوقيعات
  • message: معلومات المعاملة، بما في ذلك قائمة التعليمات المراد معالجتها
Transaction
pub struct Transaction {
#[wasm_bindgen(skip)]
#[serde(with = "short_vec")]
pub signatures: Vec<Signature>,
#[wasm_bindgen(skip)]
pub message: Message,
}

رسم توضيحي يوضح جزأي المعاملةرسم توضيحي يوضح جزأي المعاملة

تحتوي المعاملات على حد إجمالي للحجم يبلغ 1232 بايت. يشمل هذا الحد كلاً من مصفوفة signatures وبنية message.

تم تصميم هذا الحد لتجنب تجزئة الحزم على البنية التحتية النموذجية للإنترنت. في حين أن IPv6 يدعم وحدات نقل البيانات (MTUs) الأكبر من 9000 بايت، إلا أن معظم أجهزة توجيه الإنترنت تستخدم وحدة نقل بيانات افتراضية تبلغ 1500 بايت (إيثرنت قياسي). لضمان ملاءمة المعاملات ضمن حزمة واحدة دون تجزئة، تستخدم سولانا 1280 بايت (الحد الأدنى لوحدة نقل البيانات المطلوبة لـ IPv6) ناقص 48 بايت لرؤوس الشبكة (40 بايت IPv6 + 8 بايت رأس التجزئة/UDP)، مما يؤدي إلى حد حجم المعاملة البالغ 1232 بايت.

رسم توضيحي يوضح تنسيق المعاملة وحدود الحجمرسم توضيحي يوضح تنسيق المعاملة وحدود الحجم

التوقيعات

تحتوي مصفوفة signatures الخاصة بالمعاملة على بنيات Signature. كل Signature يبلغ حجمه 64 بايت ويتم إنشاؤه من خلال توقيع Message الخاص بالمعاملة باستخدام المفتاح الخاص للحساب. يجب توفير توقيع لكل حساب موقّع مضمّن في أي من تعليمات المعاملة.

التوقيع الأول ينتمي إلى الحساب الذي سيدفع الرسوم الأساسية للمعاملة وهو توقيع المعاملة. يمكن استخدام توقيع المعاملة للبحث عن تفاصيل المعاملة على الشبكة.

الرسالة

يعتبر message الخاص بالمعاملة بنية Message تحتوي على المعلومات التالية:

لتوفير المساحة، لا تقوم المعاملة بتخزين الأذونات لكل حساب بشكل فردي. بدلاً من ذلك، يتم تحديد أذونات الحساب باستخدام header و account_keys.

Message
pub struct Message {
/// The message header, identifying signed and read-only `account_keys`.
pub header: MessageHeader,
/// All the account keys used by this transaction.
#[serde(with = "short_vec")]
pub account_keys: Vec<Pubkey>,
/// The id of a recent ledger entry.
pub recent_blockhash: Hash,
/// Programs that will be executed in sequence and committed in
/// one atomic transaction if all succeed.
#[serde(with = "short_vec")]
pub instructions: Vec<CompiledInstruction>,
}

الرأس

يعتبر header الخاص بالرسالة بنية MessageHeader. يحتوي على المعلومات التالية:

  • num_required_signatures: العدد الإجمالي للتوقيعات المطلوبة من قبل المعاملة
  • num_readonly_signed_accounts: العدد الإجمالي للحسابات للقراءة فقط التي تتطلب توقيعات
  • num_readonly_unsigned_accounts: العدد الإجمالي للحسابات للقراءة فقط التي لا تتطلب توقيعات
MessageHeader
pub struct MessageHeader {
/// The number of signatures required for this message to be considered
/// valid. The signers of those signatures must match the first
/// `num_required_signatures` of [`Message::account_keys`].
pub num_required_signatures: u8,
/// The last `num_readonly_signed_accounts` of the signed keys are read-only
/// accounts.
pub num_readonly_signed_accounts: u8,
/// The last `num_readonly_unsigned_accounts` of the unsigned keys are
/// read-only accounts.
pub num_readonly_unsigned_accounts: u8,
}

رسم توضيحي يوضح الأجزاء الثلاثة لرأس الرسالةرسم توضيحي يوضح الأجزاء الثلاثة لرأس الرسالة

عناوين الحسابات

إن account_keys الخاص بالرسالة هو مصفوفة من عناوين الحسابات، يتم إرسالها بتنسيق مصفوفة مضغوطة. يشير بادئة المصفوفة إلى طولها. كل عنصر في المصفوفة هو مفتاح عام، يشير إلى حساب تستخدمه التعليمات الخاصة به. يجب أن تكون مصفوفة accounts_keys كاملة ومرتبة بشكل صارم، على النحو التالي:

  1. موقّع + قابل للكتابة
  2. موقّع + للقراءة فقط
  3. ليس موقّعًا + قابل للكتابة
  4. ليس موقّعًا + للقراءة فقط

يسمح الترتيب الصارم بدمج مصفوفة account_keys مع المعلومات الموجودة في header الخاص بالرسالة لتحديد الأذونات لكل حساب.

رسم توضيحي يوضح ترتيب مصفوفة عناوين الحساباترسم توضيحي يوضح ترتيب مصفوفة عناوين الحسابات

كتلة التجزئة الأخيرة

إن recent_blockhash الخاص بالرسالة هو قيمة تجزئة تعمل كطابع زمني للمعاملة وتمنع المعاملات المكررة. تنتهي صلاحية blockhash بعد 150 كتلة. (ما يعادل دقيقة واحدة—بافتراض أن كل كتلة تستغرق 400 ميلي ثانية.) بعد انتهاء صلاحية الكتلة، تنتهي صلاحية المعاملة ولا يمكن معالجتها.

تتيح لك طريقة getLatestBlockhash في RPC الحصول على blockhash الحالي وآخر ارتفاع للكتلة الذي سيكون عنده blockhash صالحاً.

التعليمات

إن instructions الخاص بالرسالة هو مصفوفة من جميع التعليمات المراد معالجتها، يتم إرسالها بتنسيق مصفوفة مضغوطة. تشير بادئة المصفوفة إلى طولها. كل عنصر في المصفوفة هو بنية CompiledInstruction وتتضمن المعلومات التالية:

  1. program_id_index: فهرس يشير إلى عنوان في مصفوفة account_keys. تشير هذه القيمة إلى عنوان البرنامج الذي يعالج التعليمات.
  2. accounts: مصفوفة من الفهارس تشير إلى عناوين في مصفوفة account_keys. يشير كل فهرس إلى عنوان حساب مطلوب لهذه التعليمات.
  3. data: مصفوفة بايتات تحدد التعليمات المراد استدعاؤها على البرنامج. كما تتضمن أي بيانات إضافية مطلوبة للتعليمات. (على سبيل المثال، وسائط الدالة)
CompiledInstruction
pub struct CompiledInstruction {
/// Index into the transaction keys array indicating the program account that executes this instruction.
pub program_id_index: u8,
/// Ordered indices into the transaction keys array indicating which accounts to pass to the program.
#[serde(with = "short_vec")]
pub accounts: Vec<u8>,
/// The program input data.
#[serde(with = "short_vec")]
pub data: Vec<u8>,
}

مصفوفة مضغوطة من التعليماتمصفوفة مضغوطة من التعليمات

مثال على بنية المعاملة

يوضح المثال التالي بنية معاملة تحتوي على تعليمة واحدة لتحويل SOL.

import {
createSolanaRpc,
generateKeyPairSigner,
lamports,
createTransactionMessage,
setTransactionMessageFeePayerSigner,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstructions,
pipe,
signTransactionMessageWithSigners,
getCompiledTransactionMessageDecoder
} from "@solana/kit";
import { getTransferSolInstruction } from "@solana-program/system";
const rpc = createSolanaRpc("http://localhost:8899");
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
// Generate sender and recipient keypairs
const sender = await generateKeyPairSigner();
const recipient = await generateKeyPairSigner();
// Define the amount to transfer
const LAMPORTS_PER_SOL = 1_000_000_000n;
const transferAmount = lamports(LAMPORTS_PER_SOL / 100n); // 0.01 SOL
// Create a transfer instruction for transferring SOL from sender to recipient
const transferInstruction = getTransferSolInstruction({
source: sender,
destination: recipient.address,
amount: transferAmount
});
// Create transaction message
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(sender, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
(tx) => appendTransactionMessageInstructions([transferInstruction], tx)
);
const signedTransaction =
await signTransactionMessageWithSigners(transactionMessage);
// Decode the messageBytes
const compiledTransactionMessage =
getCompiledTransactionMessageDecoder().decode(signedTransaction.messageBytes);
console.log(JSON.stringify(compiledTransactionMessage, null, 2));
Console
Click to execute the code.

يوضح الكود أدناه الناتج من مقتطفات الكود السابقة. يختلف التنسيق بين مجموعات أدوات التطوير (SDKs)، ولكن لاحظ أن كل تعليمة تحتوي على نفس المعلومات المطلوبة.

{
"version": 0,
"header": {
"numSignerAccounts": 1,
"numReadonlySignerAccounts": 0,
"numReadonlyNonSignerAccounts": 1
},
"staticAccounts": [
"HoCy8p5xxDDYTYWEbQZasEjVNM5rxvidx8AfyqA4ywBa",
"5T388jBjovy7d8mQ3emHxMDTbUF8b7nWvAnSiP3EAdFL",
"11111111111111111111111111111111"
],
"lifetimeToken": "EGCWPUEXhqHJWYBfDirq3mHZb4qDpATmYqBZMBy9TBC1",
"instructions": [
{
"programAddressIndex": 2,
"accountIndices": [0, 1],
"data": {
"0": 2,
"1": 0,
"2": 0,
"3": 0,
"4": 128,
"5": 150,
"6": 152,
"7": 0,
"8": 0,
"9": 0,
"10": 0,
"11": 0
}
}
]
}

بعد إرسال المعاملة، يمكنك استرداد تفاصيلها باستخدام توقيع المعاملة وطريقة RPC getTransaction. سيكون للاستجابة بنية مشابهة للمقتطف التالي.

يمكنك أيضًا العثور على المعاملة باستخدام مستكشف سولانا.

Transaction Data
{
"blockTime": 1745196488,
"meta": {
"computeUnitsConsumed": 150,
"err": null,
"fee": 5000,
"innerInstructions": [],
"loadedAddresses": {
"readonly": [],
"writable": []
},
"logMessages": [
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
],
"postBalances": [989995000, 10000000, 1],
"postTokenBalances": [],
"preBalances": [1000000000, 0, 1],
"preTokenBalances": [],
"rewards": [],
"status": {
"Ok": null
}
},
"slot": 13049,
"transaction": {
"message": {
"header": {
"numReadonlySignedAccounts": 0,
"numReadonlyUnsignedAccounts": 1,
"numRequiredSignatures": 1
},
"accountKeys": [
"8PLdpLxkuv9Nt8w3XcGXvNa663LXDjSrSNon4EK7QSjQ",
"7GLg7bqgLBv1HVWXKgWAm6YoPf1LoWnyWGABbgk487Ma",
"11111111111111111111111111111111"
],
"recentBlockhash": "7ZCxc2SDhzV2bYgEQqdxTpweYJkpwshVSDtXuY7uPtjf",
"instructions": [
{
"accounts": [0, 1],
"data": "3Bxs4NN8M2Yn4TLb",
"programIdIndex": 2,
"stackHeight": null
}
],
"indexToProgramIds": {}
},
"signatures": [
"3jUKrQp1UGq5ih6FTDUUt2kkqUfoG2o4kY5T1DoVHK2tXXDLdxJSXzuJGY4JPoRivgbi45U2bc7LZfMa6C4R3szX"
]
},
"version": "legacy"
}

Is this page helpful?

جدول المحتويات

تعديل الصفحة

تدار بواسطة

© 2026 مؤسسة سولانا.
جميع الحقوق محفوظة.
تواصل معنا