المعاملات

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

يوضح المثال أدناه نسخة مبسطة من معاملتين. عند معالجة المعاملة الأولى، ستنفذ تعليمة واحدة. عند معالجة المعاملة الثانية، ستنفذ ثلاث تعليمات بترتيب متسلسل: أولاً التعليمة 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 الخاصة بالرسالة قيمة تجزئة تعمل كطابع زمني للمعاملة وتمنع المعاملات المكررة. تنتهي صلاحية كتلة التجزئة بعد 150 كتلة. (ما يعادل دقيقة واحدة - بافتراض أن كل كتلة تستغرق 400 مللي ثانية.) بعد انتهاء صلاحية الكتلة، تنتهي صلاحية المعاملة ولا يمكن معالجتها.

تتيح لك طريقة RPC getLatestBlockhash الحصول على كتلة التجزئة الحالية وارتفاع آخر كتلة ستكون فيها كتلة التجزئة صالحة.

التعليمات

تعتبر 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?

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

تعديل الصفحة

تدار بواسطة

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