Thanh toánThanh toán nâng cao

Thực thi trì hoãn

Mỗi giao dịch Solana bao gồm một blockhash gần đây—một tham chiếu đến trạng thái mạng gần đây chứng minh giao dịch được tạo "bây giờ". Mạng từ chối bất kỳ giao dịch nào có blockhash cũ hơn ~150 khối (~60-90 giây), ngăn chặn các cuộc tấn công phát lại và gửi lỗi thời. Điều này hoạt động hoàn hảo cho thanh toán thời gian thực. Nhưng nó phá vỡ các quy trình cần khoảng cách giữa ký và gửi, chẳng hạn như:

Tình huốngTại sao giao dịch tiêu chuẩn thất bại
Vận hành kho bạcCFO ở Tokyo ký, Controller ở NYC phê duyệt—90 giây là không đủ
Quy trình tuân thủGiao dịch cần xem xét pháp lý/tuân thủ trước khi thực thi
Ký lưu trữ lạnhMáy cách ly không khí yêu cầu chuyển giao thủ công các giao dịch đã ký
Chuẩn bị hàng loạtChuẩn bị bảng lương hoặc giải ngân trong giờ làm việc, thực thi qua đêm
Phối hợp đa chữ kýNhiều người phê duyệt trên các múi giờ khác nhau
Thanh toán theo lịchLên lịch thanh toán để thực thi vào ngày tương lai

Trong tài chính truyền thống, một tấm séc đã ký không hết hạn trong 90 giây. Một số hoạt động blockchain cũng không nên như vậy. Durable nonces giải quyết vấn đề này bằng cách thay thế blockhash gần đây bằng một giá trị được lưu trữ, bền vững chỉ tiến lên khi bạn sử dụng nó—mang lại cho bạn các giao dịch vẫn hợp lệ cho đến khi bạn sẵn sàng gửi.

Cách hoạt động

Thay vì sử dụng blockhash gần đây (có hiệu lực ~150 block), bạn sử dụng tài khoản nonce, một tài khoản đặc biệt lưu trữ giá trị duy nhất có thể được sử dụng thay cho blockhash. Mỗi giao dịch sử dụng nonce này phải "nâng cấp" nó như là lệnh đầu tiên. Mỗi giá trị nonce chỉ có thể được sử dụng cho một giao dịch.

Durable Nonce
Standard Blockhash

Tài khoản nonce tốn ~0.0015 SOL để miễn phí lưu trữ. Một tài khoản nonce = một giao dịch đang chờ xử lý tại một thời điểm. Đối với quy trình song song, hãy tạo nhiều tài khoản nonce.

Tạo tài khoản nonce

Việc tạo tài khoản nonce yêu cầu hai lệnh trong một giao dịch duy nhất:

  1. Tạo tài khoản sử dụng getCreateAccountInstruction từ System Program
  2. Khởi tạo nó như một nonce sử dụng getInitializeNonceAccountInstruction

Tạo keypair

Tạo một keypair mới để sử dụng làm địa chỉ tài khoản nonce và tính toán không gian cần thiết cũng như phí lưu trữ.

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

Tạo lệnh tạo tài khoản

Tạo tài khoản thuộc sở hữu của System Program với đủ lamport để miễn phí lưu trữ.

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

Khởi tạo lệnh nonce

Khởi tạo tài khoản như một tài khoản nonce, thiết lập quyền quản lý có thể nâng cấp nó.

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

Xây dựng giao dịch

Xây dựng một giao dịch với cả hai lệnh.

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

Ký và gửi

Ký và gửi giao dịch để tạo và khởi tạo tài khoản nonce.

Tạo keypair

Tạo một keypair mới để sử dụng làm địa chỉ tài khoản nonce và tính toán không gian cần thiết cũng như phí lưu trữ.

Tạo lệnh tạo tài khoản

Tạo tài khoản thuộc sở hữu của System Program với đủ lamport để miễn phí lưu trữ.

Khởi tạo lệnh nonce

Khởi tạo tài khoản như một tài khoản nonce, thiết lập quyền quản lý có thể nâng cấp nó.

Xây dựng giao dịch

Xây dựng một giao dịch với cả hai lệnh.

Ký và gửi

Ký và gửi giao dịch để tạo và khởi tạo tài khoản nonce.

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

Xây dựng giao dịch hoãn lại

Thay vì blockhash gần đây, hãy sử dụng blockhash của tài khoản nonce làm thời gian sống của giao dịch.

Lấy nonce

Lấy dữ liệu từ tài khoản nonce. Sử dụng blockhash từ tài khoản nonce làm thời gian sống của giao dịch.

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

Tạo chỉ thị chuyển khoản

Tạo chỉ thị cho khoản thanh toán của bạn. Ví dụ này cho thấy việc chuyển token.

Xây dựng giao dịch với durable nonce

Sử dụng setTransactionMessageLifetimeUsingDurableNonce để đặt nonce làm blockhash và tự động thêm chỉ thị advance nonce vào đầu.

Ký giao dịch

Ký giao dịch. Bây giờ nó sử dụng durable nonce thay vì blockhash tiêu chuẩn.

Lấy nonce

Lấy dữ liệu từ tài khoản nonce. Sử dụng blockhash từ tài khoản nonce làm thời gian sống của giao dịch.

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

Tạo chỉ thị chuyển khoản

Tạo chỉ thị cho khoản thanh toán của bạn. Ví dụ này cho thấy việc chuyển token.

Xây dựng giao dịch với durable nonce

Sử dụng setTransactionMessageLifetimeUsingDurableNonce để đặt nonce làm blockhash và tự động thêm chỉ thị advance nonce vào đầu.

Ký giao dịch

Ký giao dịch. Bây giờ nó sử dụng durable nonce thay vì blockhash tiêu chuẩn.

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

Lưu trữ hoặc gửi giao dịch

Sau khi ký, mã hóa giao dịch để lưu trữ. Khi sẵn sàng, gửi nó đến mạng lưới.

Mã hóa để lưu trữ

Mã hóa giao dịch đã ký sang base64. Lưu giá trị này vào cơ sở dữ liệu của bạn.

Gửi giao dịch

Gửi giao dịch đã ký khi sẵn sàng. Giao dịch vẫn hợp lệ cho đến khi nonce được nâng cấp.

Mã hóa để lưu trữ

Mã hóa giao dịch đã ký sang base64. Lưu giá trị này vào cơ sở dữ liệu của bạn.

Gửi giao dịch

Gửi giao dịch đã ký khi sẵn sàng. Giao dịch vẫn hợp lệ cho đến khi nonce được nâng cấp.

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.

Vô hiệu hóa giao dịch đang chờ xử lý

Mỗi tài khoản nonce blockhash chỉ có thể được sử dụng một lần. Để vô hiệu hóa giao dịch đang chờ xử lý hoặc chuẩn bị tài khoản nonce để sử dụng lại, hãy nâng cấp nó theo cách thủ công:

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

Điều này tạo ra một giá trị nonce mới, làm cho bất kỳ giao dịch nào được ký bằng giá trị cũ trở nên vô hiệu vĩnh viễn.

Quy trình phê duyệt đa bên

Giải mã hóa giao dịch để thêm chữ ký bổ sung, sau đó mã hóa lại để lưu trữ hoặc gửi:

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

Giao dịch có thể được mã hóa, lưu trữ và chuyển giữa các bên phê duyệt. Sau khi thu thập đủ tất cả chữ ký cần thiết, hãy gửi lên mạng lưới.

Các cân nhắc khi triển khai

Quản lý tài khoản nonce:

  • Tạo một nhóm các tài khoản nonce để chuẩn bị giao dịch song song
  • Theo dõi các nonce đang "được sử dụng" (có giao dịch đã ký đang chờ xử lý)
  • Triển khai tái sử dụng nonce sau khi giao dịch được gửi hoặc bị hủy bỏ

Bảo mật:

  • Quyền hạn nonce kiểm soát việc giao dịch có thể bị vô hiệu hóa hay không. Hãy xem xét tách quyền hạn nonce khỏi người ký giao dịch để có thêm quyền kiểm soát và phân tách nhiệm vụ
  • Bất kỳ ai có byte giao dịch được tuần tự hóa đều có thể gửi nó lên mạng

Tài nguyên liên quan

Is this page helpful?

Mục lục

Chỉnh sửa trang

Quản lý bởi

© 2026 Solana Foundation.
Đã đăng ký bản quyền.
Kết nối