ZahlungenErweiterte Zahlungen

Verzögerte Ausführung

Jede Solana-Transaktion enthält einen aktuellen Blockhash – ein Verweis auf einen aktuellen Netzwerkstatus, der beweist, dass die Transaktion "jetzt" erstellt wurde. Das Netzwerk lehnt jede Transaktion mit einem Blockhash ab, der älter als ca. 150 Blöcke (etwa 60–90 Sekunden) ist, um Replay-Angriffe und veraltete Einreichungen zu verhindern. Das funktioniert perfekt für Echtzeitzahlungen. Aber es scheitert bei Workflows, die eine Lücke zwischen Signatur und Einreichung benötigen, wie zum Beispiel:

SzenarioWarum Standard-Transaktionen scheitern
Treasury-OperationenCFO in Tokio signiert, Controller in NYC genehmigt – 90 Sekunden reichen nicht
Compliance-WorkflowsTransaktionen müssen vor der Ausführung rechtlich/compliance-geprüft werden
Cold Storage SigningAir-gapped Maschinen erfordern manuellen Transfer signierter Transaktionen
Batch-VorbereitungLohn- oder Auszahlungslisten tagsüber vorbereiten, nachts ausführen
Multi-Sig-KoordinationMehrere Genehmiger über verschiedene Zeitzonen hinweg
Geplante ZahlungenZahlungen zur Ausführung an einem zukünftigen Datum terminieren

Im traditionellen Finanzwesen verfällt ein unterschriebener Scheck nicht nach 90 Sekunden. Bestimmte Blockchain-Operationen sollten das auch nicht. Dauerhafte Nonces lösen dieses Problem, indem sie den aktuellen Blockhash durch einen gespeicherten, persistenten Wert ersetzen, der sich nur beim Verwenden weiterentwickelt – so bleiben Transaktionen gültig, bis Sie sie einreichen möchten.

Funktionsweise

Anstelle eines aktuellen Blockhash (gültig für ~150 Blöcke) verwenden Sie ein Nonce-Konto, ein spezielles Konto, das einen eindeutigen Wert speichert, der anstelle eines Blockhash verwendet werden kann. Jede Transaktion, die diesen Nonce verwendet, muss ihn als erste Anweisung "fortschreiten" lassen. Jeder Nonce-Wert kann nur für eine Transaktion verwendet werden.

Durable Nonce
Standard Blockhash

Das Nonce-Konto kostet ~0,0015 SOL für die Mietbefreiung. Ein Nonce-Konto = eine ausstehende Transaktion gleichzeitig. Für parallele Workflows erstellen Sie mehrere Nonce-Konten.

Ein Nonce-Konto erstellen

Das Erstellen eines Nonce-Kontos erfordert zwei Anweisungen in einer einzigen Transaktion:

  1. Erstellen Sie das Konto mit getCreateAccountInstruction aus dem System Program
  2. Initialisieren Sie es als Nonce mit getInitializeNonceAccountInstruction

Keypair generieren

Generieren Sie ein neues Keypair, das als Nonce-Kontoadresse verwendet wird, und berechnen Sie den erforderlichen Speicherplatz und die Miete.

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

Konto-Anweisung erstellen

Erstellen Sie das Konto im Besitz des System Program mit ausreichend Lamports für die Mietbefreiung.

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-Anweisung initialisieren

Initialisieren Sie das Konto als Nonce-Konto und legen Sie die Autorität fest, die es fortschreiten lassen kann.

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

Transaktion erstellen

Erstellen Sie eine Transaktion mit beiden Anweisungen.

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

Signieren und senden

Signieren und senden Sie die Transaktion, um das Nonce-Konto zu erstellen und zu initialisieren.

Keypair generieren

Generieren Sie ein neues Keypair, das als Nonce-Kontoadresse verwendet wird, und berechnen Sie den erforderlichen Speicherplatz und die Miete.

Konto-Anweisung erstellen

Erstellen Sie das Konto im Besitz des System Program mit ausreichend Lamports für die Mietbefreiung.

Nonce-Anweisung initialisieren

Initialisieren Sie das Konto als Nonce-Konto und legen Sie die Autorität fest, die es fortschreiten lassen kann.

Transaktion erstellen

Erstellen Sie eine Transaktion mit beiden Anweisungen.

Signieren und senden

Signieren und senden Sie die Transaktion, um das Nonce-Konto zu erstellen und zu initialisieren.

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

Eine verzögerte Transaktion erstellen

Anstelle eines aktuellen Blockhashs verwenden Sie den blockhash des Nonce-Kontos als Lebensdauer der Transaktion.

Nonce abrufen

Rufen Sie die Daten vom Nonce-Konto ab. Verwenden Sie den blockhash vom Nonce-Konto als Lebensdauer der Transaktion.

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

Transfer-Anweisung erstellen

Erstellen Sie die Anweisung für Ihre Zahlung. Dieses Beispiel zeigt einen Token-Transfer.

Transaktion mit dauerhaftem Nonce erstellen

Verwenden Sie setTransactionMessageLifetimeUsingDurableNonce, das den Nonce als Blockhash festlegt und automatisch die Advance-Nonce-Anweisung voranstellt.

Transaktion signieren

Signieren Sie die Transaktion. Sie verwendet jetzt den dauerhaften Nonce anstelle eines Standard-Blockhashs.

Nonce abrufen

Rufen Sie die Daten vom Nonce-Konto ab. Verwenden Sie den blockhash vom Nonce-Konto als Lebensdauer der Transaktion.

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

Transfer-Anweisung erstellen

Erstellen Sie die Anweisung für Ihre Zahlung. Dieses Beispiel zeigt einen Token-Transfer.

Transaktion mit dauerhaftem Nonce erstellen

Verwenden Sie setTransactionMessageLifetimeUsingDurableNonce, das den Nonce als Blockhash festlegt und automatisch die Advance-Nonce-Anweisung voranstellt.

Transaktion signieren

Signieren Sie die Transaktion. Sie verwendet jetzt den dauerhaften Nonce anstelle eines Standard-Blockhashs.

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

Transaktion speichern oder senden

Nach dem Signieren kodieren Sie die Transaktion zur Speicherung. Wenn bereit, senden Sie sie an das Netzwerk.

Für Speicherung kodieren

Kodieren Sie die signierte Transaktion in Base64. Speichern Sie diesen Wert in Ihrer Datenbank.

Transaktion senden

Senden Sie die signierte Transaktion, wenn bereit. Die Transaktion bleibt gültig, bis der Nonce fortgeschritten wird.

Für Speicherung kodieren

Kodieren Sie die signierte Transaktion in Base64. Speichern Sie diesen Wert in Ihrer Datenbank.

Transaktion senden

Senden Sie die signierte Transaktion, wenn bereit. Die Transaktion bleibt gültig, bis der Nonce fortgeschritten wird.

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.

Ausstehende Transaktion ungültig machen

Jedes Nonce-Konto blockhash kann nur einmal verwendet werden. Um eine ausstehende Transaktion ungültig zu machen oder das Nonce-Konto für die Wiederverwendung vorzubereiten, schreiten Sie es manuell fort:

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

Dies erzeugt einen neuen Nonce-Wert und macht jede mit dem alten Wert signierte Transaktion dauerhaft ungültig.

Workflow für mehrseitige Genehmigung

Deserialisieren Sie die Transaktion, um zusätzliche Signaturen hinzuzufügen, und serialisieren Sie sie dann erneut zur Speicherung oder Übermittlung:

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

Die Transaktion kann serialisiert, gespeichert und zwischen Genehmigern weitergegeben werden. Sobald alle erforderlichen Signaturen gesammelt wurden, übermitteln Sie sie an das Netzwerk.

Überlegungen für den Produktivbetrieb

Nonce-Kontenverwaltung:

  • Erstellen Sie einen Pool von Nonce-Konten für die parallele Transaktionsvorbereitung
  • Verfolgen Sie, welche Nonces „in Verwendung" sind (ausstehende signierte Transaktionen haben)
  • Implementieren Sie Nonce-Recycling, nachdem Transaktionen übermittelt oder abgebrochen wurden

Sicherheit:

  • Die Nonce-Autorität kontrolliert, ob Transaktionen ungültig gemacht werden können. Erwägen Sie, die Nonce-Autorität von den Transaktionsunterzeichnern zu trennen, um zusätzliche Kontrolle und Aufgabentrennung zu erreichen
  • Jeder mit den serialisierten Transaktionsbytes kann diese an das Netzwerk übermitteln

Verwandte Ressourcen

Is this page helpful?

Inhaltsverzeichnis

Seite bearbeiten

Verwaltet von

© 2026 Solana Foundation.
Alle Rechte vorbehalten.
Verbinden Sie sich