المدفوعاتالمدفوعات المتقدمة

التنفيذ المؤجل

تتضمن كل معاملة على سولانا قيمة تجزئة كتلة حديثة—وهي إشارة إلى حالة شبكة حديثة تثبت أن المعاملة تم إنشاؤها "الآن". ترفض الشبكة أي معاملة تحتوي على قيمة تجزئة كتلة أقدم من ~150 كتلة (~60-90 ثانية)، مما يمنع هجمات إعادة التشغيل والإرسالات القديمة. يعمل هذا بشكل مثالي للمدفوعات في الوقت الفعلي. لكنه يعطل سير العمل الذي يحتاج إلى فجوة بين التوقيع والإرسال، مثل:

السيناريولماذا تفشل المعاملات القياسية
عمليات الخزينةالمدير المالي في طوكيو يوقّع، المراقب في نيويورك يوافق—90 ثانية ليست كافية
سير عمل الامتثالتحتاج المعاملات إلى مراجعة قانونية/امتثال قبل التنفيذ
التوقيع من التخزين الباردتتطلب الأجهزة المعزولة عن الشبكة نقلًا يدويًا للمعاملات الموقعة
إعداد الدفعاتإعداد كشوف الرواتب أو الصرفيات خلال ساعات العمل، وتنفيذها ليلاً
تنسيق التوقيع المتعددعدة موافقين عبر مناطق زمنية مختلفة
المدفوعات المجدولةجدولة المدفوعات لتنفيذها في تاريخ مستقبلي

في التمويل التقليدي، لا تنتهي صلاحية الشيك الموقع في 90 ثانية. بعض عمليات البلوكشين لا ينبغي أن تنتهي صلاحيتها أيضًا. القيم الفريدة الدائمة تحل هذه المشكلة عن طريق استبدال قيمة تجزئة الكتلة الحديثة بقيمة مخزنة ومستمرة لا تتقدم إلا عند استخدامها—مما يمنحك معاملات تظل صالحة حتى تصبح جاهزًا لإرسالها.

كيف يعمل

بدلاً من blockhash حديث (صالح لحوالي 150 كتلة)، تستخدم حساب nonce، حساب خاص يخزن قيمة فريدة يمكن استخدامها بدلاً من blockhash. يجب على كل معاملة تستخدم هذا nonce "تقديمه" كأول تعليمة. يمكن استخدام كل قيمة nonce لمعاملة واحدة فقط.

Durable Nonce
Standard Blockhash

يكلف حساب nonce حوالي 0.0015 SOL للإعفاء من الإيجار. حساب nonce واحد = معاملة معلقة واحدة في كل مرة. لسير العمل المتوازي، قم بإنشاء عدة حسابات nonce.

إنشاء حساب Nonce

يتطلب إنشاء حساب nonce تعليمتين في معاملة واحدة:

  1. إنشاء الحساب باستخدام getCreateAccountInstruction من System Program
  2. تهيئته كـ nonce باستخدام getInitializeNonceAccountInstruction

توليد Keypair

قم بتوليد keypair جديد لاستخدامه كعنوان حساب nonce واحسب المساحة المطلوبة والإيجار.

Create Nonce Account
const nonceKeypair = await generateKeyPairSigner();
const nonceSpace = BigInt(getNonceSize());
const nonceRent = await rpc
.getMinimumBalanceForRentExemption(nonceSpace)
.send();

تعليمة إنشاء الحساب

قم بإنشاء الحساب المملوك من قبل System Program مع ما يكفي من lamports للإعفاء من الإيجار.

Create Nonce Account
const nonceKeypair = await generateKeyPairSigner();
const nonceSpace = BigInt(getNonceSize());
const nonceRent = await rpc
.getMinimumBalanceForRentExemption(nonceSpace)
.send();
const createNonceAccountIx = getCreateAccountInstruction({
payer: sender,
newAccount: nonceKeypair,
lamports: nonceRent,
space: nonceSpace,
programAddress: SYSTEM_PROGRAM_ADDRESS
});

تعليمة تهيئة Nonce

قم بتهيئة الحساب كحساب nonce، مع تعيين الصلاحية التي يمكنها تقديمه.

Create Nonce Account
const nonceKeypair = await generateKeyPairSigner();
const nonceSpace = BigInt(getNonceSize());
const nonceRent = await rpc
.getMinimumBalanceForRentExemption(nonceSpace)
.send();
const createNonceAccountIx = getCreateAccountInstruction({
payer: sender,
newAccount: nonceKeypair,
lamports: nonceRent,
space: nonceSpace,
programAddress: SYSTEM_PROGRAM_ADDRESS
});
const initNonceIx = getInitializeNonceAccountInstruction({
nonceAccount: nonceKeypair.address,
nonceAuthority: sender.address
});

بناء المعاملة

قم ببناء معاملة تحتوي على كلتا التعليمتين.

Create Nonce Account
const nonceKeypair = await generateKeyPairSigner();
const nonceSpace = BigInt(getNonceSize());
const nonceRent = await rpc
.getMinimumBalanceForRentExemption(nonceSpace)
.send();
const createNonceAccountIx = getCreateAccountInstruction({
payer: sender,
newAccount: nonceKeypair,
lamports: nonceRent,
space: nonceSpace,
programAddress: SYSTEM_PROGRAM_ADDRESS
});
const initNonceIx = getInitializeNonceAccountInstruction({
nonceAccount: nonceKeypair.address,
nonceAuthority: sender.address
});
const { value: blockhash } = await rpc.getLatestBlockhash().send();
const createNonceTx = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(sender, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx),
(tx) =>
appendTransactionMessageInstructions(
[createNonceAccountIx, initNonceIx],
tx
)
);

التوقيع والإرسال

قم بتوقيع وإرسال المعاملة لإنشاء وتهيئة حساب nonce.

Create Nonce Account
const nonceKeypair = await generateKeyPairSigner();
const nonceSpace = BigInt(getNonceSize());
const nonceRent = await rpc
.getMinimumBalanceForRentExemption(nonceSpace)
.send();
const createNonceAccountIx = getCreateAccountInstruction({
payer: sender,
newAccount: nonceKeypair,
lamports: nonceRent,
space: nonceSpace,
programAddress: SYSTEM_PROGRAM_ADDRESS
});
const initNonceIx = getInitializeNonceAccountInstruction({
nonceAccount: nonceKeypair.address,
nonceAuthority: sender.address
});
const { value: blockhash } = await rpc.getLatestBlockhash().send();
const createNonceTx = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(sender, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx),
(tx) =>
appendTransactionMessageInstructions(
[createNonceAccountIx, initNonceIx],
tx
)
);
const signedCreateNonceTx =
await signTransactionMessageWithSigners(createNonceTx);
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(
signedCreateNonceTx,
{ commitment: "confirmed" }
);

توليد Keypair

قم بتوليد keypair جديد لاستخدامه كعنوان حساب nonce واحسب المساحة المطلوبة والإيجار.

تعليمة إنشاء الحساب

قم بإنشاء الحساب المملوك من قبل System Program مع ما يكفي من lamports للإعفاء من الإيجار.

تعليمة تهيئة Nonce

قم بتهيئة الحساب كحساب nonce، مع تعيين الصلاحية التي يمكنها تقديمه.

بناء المعاملة

قم ببناء معاملة تحتوي على كلتا التعليمتين.

التوقيع والإرسال

قم بتوقيع وإرسال المعاملة لإنشاء وتهيئة حساب nonce.

Create Nonce Account
const nonceKeypair = await generateKeyPairSigner();
const nonceSpace = BigInt(getNonceSize());
const nonceRent = await rpc
.getMinimumBalanceForRentExemption(nonceSpace)
.send();

بناء معاملة مؤجلة

بدلاً من تجزئة كتلة حديثة، استخدم blockhash الخاص بحساب nonce كعمر افتراضي للمعاملة.

جلب قيمة nonce

اجلب البيانات من حساب nonce. استخدم blockhash من حساب nonce كعمر افتراضي للمعاملة.

Example Nonce Account Data
{
version: 1,
state: 1,
authority: 'HgjaL8artMtmntaQDVM2UBk3gppsYYERS4PkUhiaLZD1',
blockhash: '5U7seXqfgZx1uh5DFhdH1vyBhr7XGRrKxBAnJJTbbUa',
lamportsPerSignature: 5000n
}
Build Deferred Transaction
const { data: nonceData } = await fetchNonce(rpc, nonceKeypair.address);

إنشاء تعليمة التحويل

أنشئ التعليمة الخاصة بالدفع. يوضح هذا المثال تحويل رمز مميز.

Build Deferred Transaction
const { data: nonceData } = await fetchNonce(rpc, nonceKeypair.address);
const transferInstruction = getTransferInstruction({
source: senderAta,
destination: recipientAta,
authority: sender.address,
amount: 250_000n
});

بناء معاملة باستخدام nonce دائم

استخدم setTransactionMessageLifetimeUsingDurableNonce الذي يعين nonce كتجزئة كتلة ويضيف تلقائياً تعليمة تقديم nonce في البداية.

Build Deferred Transaction
const { data: nonceData } = await fetchNonce(rpc, nonceKeypair.address);
const transferInstruction = getTransferInstruction({
source: senderAta,
destination: recipientAta,
authority: sender.address,
amount: 250_000n
});
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(sender, tx),
(tx) =>
setTransactionMessageLifetimeUsingDurableNonce(
{
nonce: nonceData.blockhash as Nonce,
nonceAccountAddress: nonceKeypair.address,
nonceAuthorityAddress: nonceData.authority
},
tx
),
(tx) => appendTransactionMessageInstructions([transferInstruction], tx)
);

توقيع المعاملة

قم بتوقيع المعاملة. تستخدم الآن nonce الدائم بدلاً من تجزئة كتلة قياسية.

Build Deferred Transaction
const { data: nonceData } = await fetchNonce(rpc, nonceKeypair.address);
const transferInstruction = getTransferInstruction({
source: senderAta,
destination: recipientAta,
authority: sender.address,
amount: 250_000n
});
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(sender, tx),
(tx) =>
setTransactionMessageLifetimeUsingDurableNonce(
{
nonce: nonceData.blockhash as Nonce,
nonceAccountAddress: nonceKeypair.address,
nonceAuthorityAddress: nonceData.authority
},
tx
),
(tx) => appendTransactionMessageInstructions([transferInstruction], tx)
);
const signedTransaction =
await signTransactionMessageWithSigners(transactionMessage);

جلب قيمة nonce

اجلب البيانات من حساب nonce. استخدم blockhash من حساب nonce كعمر افتراضي للمعاملة.

Example Nonce Account Data
{
version: 1,
state: 1,
authority: 'HgjaL8artMtmntaQDVM2UBk3gppsYYERS4PkUhiaLZD1',
blockhash: '5U7seXqfgZx1uh5DFhdH1vyBhr7XGRrKxBAnJJTbbUa',
lamportsPerSignature: 5000n
}

إنشاء تعليمة التحويل

أنشئ التعليمة الخاصة بالدفع. يوضح هذا المثال تحويل رمز مميز.

بناء معاملة باستخدام nonce دائم

استخدم setTransactionMessageLifetimeUsingDurableNonce الذي يعين nonce كتجزئة كتلة ويضيف تلقائياً تعليمة تقديم nonce في البداية.

توقيع المعاملة

قم بتوقيع المعاملة. تستخدم الآن nonce الدائم بدلاً من تجزئة كتلة قياسية.

Build Deferred Transaction
const { data: nonceData } = await fetchNonce(rpc, nonceKeypair.address);

تخزين أو إرسال المعاملة

بعد التوقيع، قم بترميز المعاملة للتخزين. عندما تكون جاهزًا، أرسلها إلى الشبكة.

الترميز للتخزين

قم بترميز المعاملة الموقعة إلى base64. قم بتخزين هذه القيمة في قاعدة البيانات الخاصة بك.

Store and Execute
const signedTransaction =
await signTransactionMessageWithSigners(transactionMessage);
const base64EncodedTransaction =
getBase64EncodedWireTransaction(signedTransaction);
// Store base64EncodedTransaction in your database

إرسال المعاملة

أرسل المعاملة الموقعة عندما تكون جاهزًا. تظل المعاملة صالحة حتى يتم تقديم nonce.

Store and Execute
const signedTransaction =
await signTransactionMessageWithSigners(transactionMessage);
const base64EncodedTransaction =
getBase64EncodedWireTransaction(signedTransaction);
// When ready to execute (could be days later):
await rpc
.sendTransaction(base64EncodedTransaction, { encoding: "base64" })
.send();

الترميز للتخزين

قم بترميز المعاملة الموقعة إلى base64. قم بتخزين هذه القيمة في قاعدة البيانات الخاصة بك.

إرسال المعاملة

أرسل المعاملة الموقعة عندما تكون جاهزًا. تظل المعاملة صالحة حتى يتم تقديم nonce.

Store and Execute
const signedTransaction =
await signTransactionMessageWithSigners(transactionMessage);
const base64EncodedTransaction =
getBase64EncodedWireTransaction(signedTransaction);
// Store base64EncodedTransaction in your database

عرض توضيحي

Demo
// Generate keypairs for sender and recipient
const sender = await generateKeyPairSigner();
const recipient = await generateKeyPairSigner();
console.log("Sender Address:", sender.address);
console.log("Recipient Address:", recipient.address);
// Demo Setup: Create RPC connection, mint, and token accounts
const { rpc, rpcSubscriptions, mint } = await demoSetup(sender, recipient);
// =============================================================================
// Step 1: Create a Nonce Account
// =============================================================================
const nonceKeypair = await generateKeyPairSigner();
console.log("\nNonce Account Address:", nonceKeypair.address);
const nonceSpace = BigInt(getNonceSize());
const nonceRent = await rpc
.getMinimumBalanceForRentExemption(nonceSpace)
.send();
// Instruction to create new account for the nonce
const createNonceAccountIx = getCreateAccountInstruction({
payer: sender,
newAccount: nonceKeypair,
lamports: nonceRent,
space: nonceSpace,
programAddress: SYSTEM_PROGRAM_ADDRESS
});
// Instruction to initialize the nonce account
const initNonceIx = getInitializeNonceAccountInstruction({
nonceAccount: nonceKeypair.address,
nonceAuthority: sender.address
});
// Build and send nonce account creation transaction
const { value: blockhash } = await rpc.getLatestBlockhash().send();
const createNonceTx = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(sender, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx),
(tx) =>
appendTransactionMessageInstructions(
[createNonceAccountIx, initNonceIx],
tx
)
);
const signedCreateNonceTx =
await signTransactionMessageWithSigners(createNonceTx);
assertIsTransactionWithBlockhashLifetime(signedCreateNonceTx);
await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(
signedCreateNonceTx,
{ commitment: "confirmed" }
);
console.log("Nonce Account created.");
// =============================================================================
// Step 2: Token Payment with Durable Nonce
// =============================================================================
// Fetch current nonce value from the nonce account
const { data: nonceData } = await fetchNonce(rpc, nonceKeypair.address);
console.log("Nonce Account data:", nonceData);
const [senderAta] = await findAssociatedTokenPda({
mint: mint.address,
owner: sender.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
const [recipientAta] = await findAssociatedTokenPda({
mint: mint.address,
owner: recipient.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
console.log("\nMint Address:", mint.address);
console.log("Sender Token Account:", senderAta);
console.log("Recipient Token Account:", recipientAta);
const transferInstruction = getTransferInstruction({
source: senderAta,
destination: recipientAta,
authority: sender.address,
amount: 250_000n // 0.25 tokens
});
// Create transaction message using durable nonce lifetime
// setTransactionMessageLifetimeUsingDurableNonce automatically prepends
// the AdvanceNonceAccount instruction
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(sender, tx),
(tx) =>
setTransactionMessageLifetimeUsingDurableNonce(
{
nonce: nonceData.blockhash as string as Nonce,
nonceAccountAddress: nonceKeypair.address,
nonceAuthorityAddress: nonceData.authority
},
tx
),
(tx) => appendTransactionMessageInstructions([transferInstruction], tx)
);
const signedTransaction =
await signTransactionMessageWithSigners(transactionMessage);
assertIsTransactionWithDurableNonceLifetime(signedTransaction);
const transactionSignature = getSignatureFromTransaction(signedTransaction);
// Encode the transaction to base64, optionally save and send at a later time
const base64EncodedTransaction =
getBase64EncodedWireTransaction(signedTransaction);
console.log("\nBase64 Encoded Transaction:", base64EncodedTransaction);
// Send the encoded transaction, blockhash does not expire
await rpc
.sendTransaction(base64EncodedTransaction, {
encoding: "base64",
skipPreflight: true
})
.send();
console.log("\n=== Token Payment with Durable Nonce Complete ===");
console.log("Transaction Signature:", transactionSignature);
// =============================================================================
// Demo Setup Helper Function
// =============================================================================
Console
Click to execute the code.

إبطال معاملة معلقة

كل حساب nonce blockhash يمكن استخدامه مرة واحدة فقط. لإبطال معاملة معلقة أو إعداد حساب nonce لإعادة الاستخدام، قم بتقديمه يدويًا:

import { getAdvanceNonceAccountInstruction } from "@solana-program/system";
// Submit this instruction (with a regular blockhash) to invalidate any pending transaction
getAdvanceNonceAccountInstruction({
nonceAccount: nonceAddress,
nonceAuthority
});

هذا ينشئ قيمة nonce جديدة، مما يجعل أي معاملة موقعة بالقيمة القديمة غير صالحة بشكل دائم.

سير عمل الموافقة متعدد الأطراف

قم بإلغاء تسلسل المعاملة لإضافة توقيعات إضافية، ثم قم بالتسلسل مرة أخرى للتخزين أو التقديم:

import {
getBase64Decoder,
getTransactionDecoder,
getBase64EncodedWireTransaction,
partiallySignTransaction
} from "@solana/kit";
// Deserialize the stored transaction
const txBytes = getBase64Decoder().decode(serializedString);
const partiallySignedTx = getTransactionDecoder().decode(txBytes);
// Each approver adds their signature
const fullySignedTx = await partiallySignTransaction(
[newSigner],
partiallySignedTx
);
// Serialize again for storage or submission
const serialized = getBase64EncodedWireTransaction(fullySignedTx);

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

اعتبارات الإنتاج

إدارة حسابات Nonce:

  • إنشاء مجموعة من حسابات nonce للتحضير المتوازي للمعاملات
  • تتبع حسابات nonce "قيد الاستخدام" (التي لديها معاملات موقعة معلقة)
  • تنفيذ إعادة تدوير nonce بعد إرسال المعاملات أو التخلي عنها

الأمان:

  • تتحكم صلاحية nonce في إمكانية إبطال المعاملات. يُنصح بفصل صلاحية nonce عن الموقعين على المعاملات للحصول على تحكم إضافي وفصل المهام
  • يمكن لأي شخص لديه بايتات المعاملة المتسلسلة إرسالها إلى الشبكة

الموارد ذات الصلة

Is this page helpful?

تدار بواسطة

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