BetalingenGeavanceerde betalingen

Uitgestelde uitvoering

Elke Solana-transactie bevat een recente blockhash—een verwijzing naar een recente netwerkstatus die bewijst dat de transactie "nu" is aangemaakt. Het netwerk weigert elke transactie met een blockhash ouder dan ~150 blokken (~60-90 seconden), waardoor replay-aanvallen en verouderde inzendingen worden voorkomen. Dit werkt perfect voor realtime betalingen. Maar het breekt workflows die een tijdsverschil tussen ondertekening en indiening nodig hebben, zoals:

ScenarioWaarom standaard transacties falen
Treasury-operatiesCFO in Tokyo ondertekent, controller in NYC keurt goed—90 seconden is niet genoeg
Compliance-workflowsTransacties hebben juridische/compliance-beoordeling nodig voor uitvoering
Cold storage-ondertekeningAir-gapped machines vereisen handmatige overdracht van ondertekende transacties
BatchvoorbereidingBereid salarisbetalingen of uitkeringen voor tijdens kantooruren, voer 's nachts uit
Multi-sig coördinatieMeerdere goedkeurders in verschillende tijdzones
Geplande betalingenPlan betalingen die op een toekomstige datum worden uitgevoerd

In traditionele financiën verloopt een ondertekende cheque niet na 90 seconden. Bepaalde blockchain-operaties zouden dat ook niet moeten doen. Durable nonces lossen dit op door de recente blockhash te vervangen door een opgeslagen, persistente waarde die alleen vooruitgaat wanneer je deze gebruikt—waardoor je transacties krijgt die geldig blijven totdat je klaar bent om ze in te dienen.

Hoe het werkt

In plaats van een recente blockhash (geldig ~150 blokken), gebruik je een nonce-account, een speciaal account dat een unieke waarde opslaat die in plaats van een blockhash kan worden gebruikt. Elke transactie die deze nonce gebruikt, moet deze "verhogen" als eerste instructie. Elke nonce-waarde kan slechts voor één transactie worden gebruikt.

Durable Nonce
Standard Blockhash

Het nonce-account kost ~0,0015 SOL voor rent-vrijstelling. Eén nonce-account = één lopende transactie tegelijk. Voor parallelle workflows maak je meerdere nonce-accounts aan.

Een nonce-account aanmaken

Het aanmaken van een nonce-account vereist twee instructies in één transactie:

  1. Maak het account aan met getCreateAccountInstruction van het System Program
  2. Initialiseer het als nonce met getInitializeNonceAccountInstruction

Genereer keypair

Genereer een nieuw keypair om te gebruiken als het nonce-accountadres en bereken de benodigde ruimte en rent.

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

Maak account-instructie

Maak het account aan dat eigendom is van het System Program met voldoende lamports voor rent-vrijstelling.

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

Initialiseer nonce-instructie

Initialiseer het account als een nonce-account en stel de autoriteit in die het kan verhogen.

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

Bouw transactie

Bouw een transactie met beide instructies.

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

Onderteken en verzend

Onderteken en verstuur de transactie om het nonce-account aan te maken en te initialiseren.

Genereer keypair

Genereer een nieuw keypair om te gebruiken als het nonce-accountadres en bereken de benodigde ruimte en rent.

Maak account-instructie

Maak het account aan dat eigendom is van het System Program met voldoende lamports voor rent-vrijstelling.

Initialiseer nonce-instructie

Initialiseer het account als een nonce-account en stel de autoriteit in die het kan verhogen.

Bouw transactie

Bouw een transactie met beide instructies.

Onderteken en verzend

Onderteken en verstuur de transactie om het nonce-account aan te maken en te initialiseren.

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

Een uitgestelde transactie bouwen

In plaats van een recente blockhash, gebruik je de blockhash van het nonce-account als de levensduur van de transactie.

Haal de nonce op

Haal de gegevens op uit het nonce-account. Gebruik de blockhash van het nonce-account als de levensduur van de transactie.

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

Maak een overboekingsinstructie

Maak de instructie voor je betaling. Dit voorbeeld toont een token-overboeking.

Bouw transactie met duurzame nonce

Gebruik setTransactionMessageLifetimeUsingDurableNonce die de nonce instelt als de blockhash en automatisch de advance nonce-instructie vooraan plaatst.

Onderteken transactie

Onderteken de transactie. Deze gebruikt nu de duurzame nonce in plaats van een standaard blockhash.

Haal de nonce op

Haal de gegevens op uit het nonce-account. Gebruik de blockhash van het nonce-account als de levensduur van de transactie.

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

Maak een overboekingsinstructie

Maak de instructie voor je betaling. Dit voorbeeld toont een token-overboeking.

Bouw transactie met duurzame nonce

Gebruik setTransactionMessageLifetimeUsingDurableNonce die de nonce instelt als de blockhash en automatisch de advance nonce-instructie vooraan plaatst.

Onderteken transactie

Onderteken de transactie. Deze gebruikt nu de duurzame nonce in plaats van een standaard blockhash.

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

Transactie opslaan of verzenden

Codeer de transactie na ondertekening voor opslag. Verzend deze naar het netwerk wanneer je klaar bent.

Coderen voor opslag

Codeer de ondertekende transactie naar base64. Sla deze waarde op in je database.

Transactie verzenden

Verzend de ondertekende transactie wanneer je klaar bent. De transactie blijft geldig totdat de nonce wordt verhoogd.

Coderen voor opslag

Codeer de ondertekende transactie naar base64. Sla deze waarde op in je database.

Transactie verzenden

Verzend de ondertekende transactie wanneer je klaar bent. De transactie blijft geldig totdat de nonce wordt verhoogd.

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.

Een lopende transactie ongeldig maken

Elk nonce-account blockhash kan slechts één keer worden gebruikt. Om een lopende transactie ongeldig te maken of het nonce-account voor te bereiden op hergebruik, verhoog je deze handmatig:

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

Dit genereert een nieuwe nonce-waarde, waardoor elke transactie die met de oude waarde is ondertekend permanent ongeldig wordt.

Workflow voor goedkeuring door meerdere partijen

Deserialiseer de transactie om extra handtekeningen toe te voegen, en serialiseer vervolgens opnieuw voor opslag of indiening:

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

De transactie kan worden geserialiseerd, opgeslagen en doorgegeven tussen goedkeurders. Zodra alle vereiste handtekeningen zijn verzameld, dien je deze in bij het netwerk.

Overwegingen voor productie

Nonce-accountbeheer:

  • Maak een pool van nonce-accounts voor parallelle transactievoorbereiding
  • Houd bij welke nonces "in gebruik" zijn (met ondertekende transacties in behandeling)
  • Implementeer nonce-recycling nadat transacties zijn ingediend of afgebroken

Beveiliging:

  • De nonce-autoriteit bepaalt of transacties ongeldig kunnen worden gemaakt. Overweeg om de nonce-autoriteit te scheiden van transactieondertekenaars voor extra controle en functiescheiding
  • Iedereen met de geserialiseerde transactiebytes kan deze naar het netwerk verzenden

Gerelateerde bronnen

Is this page helpful?

Inhoudsopgave

Pagina Bewerken

Beheerd door

© 2026 Solana Foundation.
Alle rechten voorbehouden.
Blijf Verbonden