PagamentiPagamenti avanzati

Esecuzione differita

Ogni transazione Solana include un blockhash recente, un riferimento a uno stato recente della rete che dimostra che la transazione è stata creata "ora". La rete rifiuta qualsiasi transazione con un blockhash più vecchio di ~150 blocchi (~60-90 secondi), prevenendo attacchi replay e invii obsoleti. Questo funziona perfettamente per i pagamenti in tempo reale. Ma interrompe i flussi di lavoro che richiedono un intervallo tra firma e invio, come:

ScenarioPerché le transazioni standard falliscono
Operazioni di tesoreriaIl CFO a Tokyo firma, il Controller a NYC approva: 90 secondi non bastano
Flussi di conformitàLe transazioni richiedono revisione legale/di conformità prima dell'esecuzione
Firma da cold storageLe macchine air-gapped richiedono trasferimento manuale delle transazioni firmate
Preparazione batchPrepara buste paga o erogazioni durante l'orario lavorativo, esegui durante la notte
Coordinamento multi-firmaPiù approvatori in fusi orari diversi
Pagamenti programmatiProgramma pagamenti da eseguire in una data futura

Nella finanza tradizionale, un assegno firmato non scade in 90 secondi. Anche certe operazioni blockchain non dovrebbero. I nonce durevoli risolvono questo problema sostituendo il blockhash recente con un valore persistente memorizzato che avanza solo quando lo usi, offrendoti transazioni che rimangono valide finché non sei pronto a inviarle.

Come funziona

Invece di un blockhash recente (valido per ~150 blocchi), utilizzi un account nonce, un account speciale che memorizza un valore univoco che può essere utilizzato al posto di un blockhash. Ogni transazione che utilizza questo nonce deve "avanzarlo" come prima istruzione. Ogni valore nonce può essere utilizzato solo per una transazione.

Durable Nonce
Standard Blockhash

L'account nonce costa ~0.0015 SOL per l'esenzione dal rent. Un account nonce = una transazione in sospeso alla volta. Per flussi di lavoro paralleli, crea più account nonce.

Creare un account nonce

La creazione di un account nonce richiede due istruzioni in una singola transazione:

  1. Creare l'account utilizzando getCreateAccountInstruction dal System Program
  2. Inizializzarlo come nonce utilizzando getInitializeNonceAccountInstruction

Generare keypair

Genera una nuova keypair da utilizzare come indirizzo dell'account nonce e calcola lo spazio richiesto e il rent.

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

Istruzione di creazione account

Crea l'account di proprietà del System Program con abbastanza lamport per l'esenzione dal 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
});

Istruzione di inizializzazione nonce

Inizializza l'account come account nonce, impostando l'autorità che può avanzarlo.

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

Costruire la transazione

Costruisci una transazione con entrambe le istruzioni.

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

Firmare e inviare

Firma e invia la transazione per creare e inizializzare l'account nonce.

Generare keypair

Genera una nuova keypair da utilizzare come indirizzo dell'account nonce e calcola lo spazio richiesto e il rent.

Istruzione di creazione account

Crea l'account di proprietà del System Program con abbastanza lamport per l'esenzione dal rent.

Istruzione di inizializzazione nonce

Inizializza l'account come account nonce, impostando l'autorità che può avanzarlo.

Costruire la transazione

Costruisci una transazione con entrambe le istruzioni.

Firmare e inviare

Firma e invia la transazione per creare e inizializzare l'account nonce.

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

Costruire una transazione differita

Invece di un blockhash recente, usa il blockhash dell'account nonce come durata della transazione.

Recupera il nonce

Recupera i dati dall'account nonce. Usa il blockhash dall'account nonce come durata della transazione.

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

Crea istruzione di trasferimento

Crea l'istruzione per il tuo pagamento. Questo esempio mostra un trasferimento di token.

Costruisci transazione con nonce durevole

Usa setTransactionMessageLifetimeUsingDurableNonce che imposta il nonce come blockhash e antepone automaticamente l'istruzione di avanzamento del nonce.

Firma transazione

Firma la transazione. Ora usa il nonce durevole invece di un blockhash standard.

Recupera il nonce

Recupera i dati dall'account nonce. Usa il blockhash dall'account nonce come durata della transazione.

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

Crea istruzione di trasferimento

Crea l'istruzione per il tuo pagamento. Questo esempio mostra un trasferimento di token.

Costruisci transazione con nonce durevole

Usa setTransactionMessageLifetimeUsingDurableNonce che imposta il nonce come blockhash e antepone automaticamente l'istruzione di avanzamento del nonce.

Firma transazione

Firma la transazione. Ora usa il nonce durevole invece di un blockhash standard.

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

Memorizzare o inviare la transazione

Dopo la firma, codifica la transazione per la memorizzazione. Quando sei pronto, inviala alla rete.

Codificare per la memorizzazione

Codifica la transazione firmata in base64. Memorizza questo valore nel tuo database.

Inviare la transazione

Invia la transazione firmata quando sei pronto. La transazione rimane valida finché il nonce non viene avanzato.

Codificare per la memorizzazione

Codifica la transazione firmata in base64. Memorizza questo valore nel tuo database.

Inviare la transazione

Invia la transazione firmata quando sei pronto. La transazione rimane valida finché il nonce non viene avanzato.

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.

Invalidare una transazione in sospeso

Ogni account nonce blockhash può essere usato solo una volta. Per invalidare una transazione in sospeso o preparare l'account nonce per il riutilizzo, avanzalo manualmente:

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

Questo genera un nuovo valore nonce, rendendo permanentemente invalida qualsiasi transazione firmata con il vecchio valore.

Flusso di lavoro per approvazione multi-parte

Deserializza la transazione per aggiungere firme aggiuntive, quindi serializza nuovamente per la memorizzazione o l'invio:

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

La transazione può essere serializzata, memorizzata e passata tra gli approvatori. Una volta raccolte tutte le firme richieste, invia alla rete.

Considerazioni per la produzione

Gestione degli account nonce:

  • Crea un pool di account nonce per la preparazione parallela delle transazioni
  • Tieni traccia di quali nonce sono "in uso" (hanno transazioni firmate in sospeso)
  • Implementa il riciclo dei nonce dopo l'invio o l'abbandono delle transazioni

Sicurezza:

  • L'autorità nonce controlla se le transazioni possono essere invalidate. Considera di separare l'autorità nonce dai firmatari delle transazioni per un controllo aggiuntivo e separazione dei compiti
  • Chiunque abbia i byte della transazione serializzata può inviarla alla rete

Risorse correlate

Is this page helpful?

Indice

Modifica Pagina

Gestito da

© 2026 Solana Foundation.
Tutti i diritti riservati.
Rimani Connesso