PembayaranPembayaran lanjutan

Eksekusi tertunda

Setiap transaksi Solana menyertakan blockhash terkini—referensi ke status jaringan terkini yang membuktikan transaksi dibuat "sekarang." Jaringan menolak transaksi apa pun dengan blockhash yang lebih lama dari ~150 blok (~60-90 detik), mencegah serangan replay dan pengiriman yang kedaluwarsa. Ini bekerja sempurna untuk pembayaran real-time. Namun, ini merusak alur kerja yang memerlukan jeda antara penandatanganan dan pengiriman, seperti:

SkenarioMengapa transaksi standar gagal
Operasi treasuryCFO di Tokyo menandatangani, Controller di NYC menyetujui—90 detik tidak cukup
Alur kerja kepatuhanTransaksi memerlukan tinjauan hukum/kepatuhan sebelum eksekusi
Penandatanganan cold storageMesin air-gapped memerlukan transfer manual transaksi yang ditandatangani
Persiapan batchSiapkan payroll atau pencairan selama jam kerja, eksekusi di malam hari
Koordinasi multi-sigBeberapa approver di zona waktu berbeda
Pembayaran terjadwalJadwalkan pembayaran untuk dieksekusi di tanggal mendatang

Dalam keuangan tradisional, cek yang ditandatangani tidak kedaluwarsa dalam 90 detik. Operasi blockchain tertentu juga seharusnya tidak demikian. Durable nonces mengatasi ini dengan mengganti blockhash terkini dengan nilai tersimpan yang persisten yang hanya berubah saat Anda menggunakannya—memberi Anda transaksi yang tetap valid hingga Anda siap mengirimkannya.

Cara kerjanya

Alih-alih blockhash terbaru (valid ~150 blok), Anda menggunakan akun nonce, sebuah akun khusus yang menyimpan nilai unik yang dapat digunakan sebagai pengganti blockhash. Setiap transaksi yang menggunakan nonce ini harus "memajukannya" sebagai instruksi pertama. Setiap nilai nonce hanya dapat digunakan untuk satu transaksi.

Durable Nonce
Standard Blockhash

Akun nonce memerlukan biaya ~0.0015 SOL untuk pembebasan rent. Satu akun nonce = satu transaksi tertunda pada satu waktu. Untuk alur kerja paralel, buat beberapa akun nonce.

Membuat akun nonce

Membuat akun nonce memerlukan dua instruksi dalam satu transaksi:

  1. Buat akun menggunakan getCreateAccountInstruction dari System Program
  2. Inisialisasi sebagai nonce menggunakan getInitializeNonceAccountInstruction

Generate Keypair

Buat keypair baru untuk digunakan sebagai alamat akun nonce dan hitung ruang serta rent yang diperlukan.

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

Create Account Instruction

Buat akun yang dimiliki oleh System Program dengan lamport yang cukup untuk pembebasan rent.

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
});

Initialize Nonce Instruction

Inisialisasi akun sebagai akun nonce, tetapkan otoritas yang dapat memajukannya.

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
});

Build Transaction

Bangun transaksi dengan kedua instruksi.

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
)
);

Sign and Send

Tanda tangani dan kirim transaksi untuk membuat dan menginisialisasi akun nonce.

Generate Keypair

Buat keypair baru untuk digunakan sebagai alamat akun nonce dan hitung ruang serta rent yang diperlukan.

Create Account Instruction

Buat akun yang dimiliki oleh System Program dengan lamport yang cukup untuk pembebasan rent.

Initialize Nonce Instruction

Inisialisasi akun sebagai akun nonce, tetapkan otoritas yang dapat memajukannya.

Build Transaction

Bangun transaksi dengan kedua instruksi.

Sign and Send

Tanda tangani dan kirim transaksi untuk membuat dan menginisialisasi akun nonce.

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

Membangun transaksi tertunda

Alih-alih blockhash terkini, gunakan blockhash dari akun nonce sebagai lifetime transaksi.

Ambil nonce

Ambil data dari akun nonce. Gunakan blockhash dari akun nonce sebagai lifetime transaksi.

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

Buat instruksi transfer

Buat instruksi untuk pembayaran Anda. Contoh ini menunjukkan transfer token.

Bangun transaksi dengan durable nonce

Gunakan setTransactionMessageLifetimeUsingDurableNonce yang menetapkan nonce sebagai blockhash dan secara otomatis menambahkan instruksi advance nonce di awal.

Tanda tangani transaksi

Tanda tangani transaksi. Transaksi sekarang menggunakan durable nonce alih-alih blockhash standar.

Ambil nonce

Ambil data dari akun nonce. Gunakan blockhash dari akun nonce sebagai lifetime transaksi.

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

Buat instruksi transfer

Buat instruksi untuk pembayaran Anda. Contoh ini menunjukkan transfer token.

Bangun transaksi dengan durable nonce

Gunakan setTransactionMessageLifetimeUsingDurableNonce yang menetapkan nonce sebagai blockhash dan secara otomatis menambahkan instruksi advance nonce di awal.

Tanda tangani transaksi

Tanda tangani transaksi. Transaksi sekarang menggunakan durable nonce alih-alih blockhash standar.

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

Simpan atau kirim transaksi

Setelah menandatangani, encode transaksi untuk penyimpanan. Saat siap, kirim ke jaringan.

Encode untuk penyimpanan

Encode transaksi yang telah ditandatangani ke base64. Simpan nilai ini di database Anda.

Kirim transaksi

Kirim transaksi yang telah ditandatangani saat siap. Transaksi tetap valid hingga nonce dimajukan.

Encode untuk penyimpanan

Encode transaksi yang telah ditandatangani ke base64. Simpan nilai ini di database Anda.

Kirim transaksi

Kirim transaksi yang telah ditandatangani saat siap. Transaksi tetap valid hingga nonce dimajukan.

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

Demo

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.

Membatalkan transaksi tertunda

Setiap akun nonce blockhash hanya dapat digunakan sekali. Untuk membatalkan transaksi tertunda atau mempersiapkan akun nonce untuk digunakan kembali, majukan secara manual:

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

Ini menghasilkan nilai nonce baru, membuat transaksi apa pun yang ditandatangani dengan nilai lama menjadi tidak valid secara permanen.

Alur kerja persetujuan multi-pihak

Deserialisasi transaksi untuk menambahkan tanda tangan tambahan, kemudian serialisasi lagi untuk penyimpanan atau pengiriman:

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);

Transaksi dapat diserialisasi, disimpan, dan diteruskan antar pemberi persetujuan. Setelah semua tanda tangan yang diperlukan terkumpul, kirim ke jaringan.

Pertimbangan produksi

Manajemen akun nonce:

  • Buat kumpulan akun nonce untuk persiapan transaksi paralel
  • Lacak nonce mana yang "sedang digunakan" (memiliki transaksi tertanda yang tertunda)
  • Implementasikan daur ulang nonce setelah transaksi dikirim atau dibatalkan

Keamanan:

  • Otoritas nonce mengontrol apakah transaksi dapat dibatalkan. Pertimbangkan untuk memisahkan otoritas nonce dari penandatangan transaksi untuk kontrol tambahan dan pemisahan tugas
  • Siapa pun yang memiliki byte transaksi terserialisasi dapat mengirimkannya ke jaringan

Sumber daya terkait

Is this page helpful?

Daftar Isi

Edit Halaman

Dikelola oleh

© 2026 Yayasan Solana.
Semua hak dilindungi.
Terhubung