NEU: Helius übernimmt Light Protocol
Solana-Transaktionen mit dauerhaften Nonces senden
Blog/Entwicklung

Solana-Transaktionen: Dauerhafte Nonces (2023)

Leiter Developer RelationsHunter Davis auf LinkedIn
6 Min. Lesezeit

Einführung

Wenn du als Entwickler eine Transaktion auf Solana übermittelst, musst du den neuesten Blockhash über Solana RPC abrufen. Dieser wichtige Schritt schützt vor Replay-Angriffen. Sobald eine Transaktion mit einem bestimmten Blockhash signiert und übermittelt wurde, kann niemand denselben Hash verwenden, um sie erneut abzuspielen oder zu übermitteln.

Stell dir vor, du musst eine Transaktion übermitteln, die eine Signatur aus einem Offline-Cold-Storage oder einer Hardware-Wallet wie Ledger benötigt. Blockhashes laufen jedoch schnell ab, wodurch deine Transaktion ungültig werden kann. Hier kommen dauerhafte Nonces ins Spiel: Sie ermöglichen sichere Offline-Transaktionen.

Am Ende dieses Leitfadens verstehst du:

  1. Was eine dauerhafte Nonce ist.
  2. Welchen Zweck eine dauerhafte Nonce erfüllt.
  3. Wie du dauerhafte Nonces in Transaktionen verwendest.

Transaktionen: Voraussetzungen

Bevor du beginnst, stelle Folgendes sicher:

  • Du verfügst über grundlegende JavaScript-Kenntnisse.
  • NodeJS ist installiert.
  • Solana CLI ist installiert.
  • Git ist installiert.

Umgebung einrichten

  1. Klone unser Beispiel-Repository mit vorhandenen Hilfsfunktionen:
Code
git clone
  1. Wechsle in den Projektordner und installiere npm:
Code
cd durable-noncenpm install
  1. Wechsle im Ordner „nonce“ in den Ordner „wallets“. Dort speichern wir unsere lokalen Schlüssel für die Tests:
Code
cd wallets
  1. Erstelle mit der installierten Solana CLI eine Wallet für das zahlende Schlüsselpaar:
Code
solana-keygen new -o ./wallet.json
  1. Lege diese Wallet nun in der CLI als deine Wallet fest, um Solana per Airdrop zu erhalten:
Code
solana config set --keypair ./wallet.json
  1. Jetzt kannst du mit folgendem Befehl Solana per Airdrop an diese Adresse senden:
Code
solana aidrop 2
  1. Außerdem müssen wir eine weitere Wallet für die Nonce-Autorität erstellen und aufladen:
Code
solana-keygen new -o ./nonceAuth.json
  1. An den erzeugten öffentlichen Schlüssel können wir über eine Faucet-Website 1 SOL per Airdrop senden: hier.

Nachdem unsere Umgebung eingerichtet ist, können wir mit den nächsten Schritten fortfahren.

Was ist eine dauerhafte Nonce?

Ein Konto für dauerhafte Nonces auf Solana lässt sich mit einem Schließfach vergleichen. Wenn du dieses Konto anlegst, weist Solana ihm einen eindeutigen, stabilen Code zu, der als „dauerhafte Nonce“ bezeichnet wird. Anders als typische Nonces, die sich mit jeder Transaktion ändern, bleibt diese Nonce unverändert und dient als konsistente Referenz.

Das ist besonders für „Offline“-Transaktionen nützlich. Beim Erstellen einer Transaktion verweist du auf die Nonce deines Kontos. Solana gleicht sie mit dem gespeicherten Wert ab. Stimmen beide überein, wird die Transaktion freigegeben. Ein Konto für dauerhafte Nonces dient somit sowohl zur Speicherung als auch zur Validierung. Es gewährleistet die Authentizität von Transaktionen und unterstützt zugleich die hohe Geschwindigkeit sowie Offline-Szenarien im Solana-Netzwerk.

Dauerhafte Nonces lassen sich für verschiedene Anwendungsfälle nutzen, darunter:

  • Geplante Transaktionen: Du kannst Transaktionen so einrichten, dass sie zu einem festgelegten zukünftigen Zeitpunkt ausgeführt werden. Dauerhafte Nonces sorgen dafür, dass diese geplanten Transaktionen sicher ausgeführt werden.
  • Multisig-Wallets: Bei Wallets mit mehreren Signaturen bieten dauerhafte Nonces eine zusätzliche Sicherheits- und Koordinationsebene für mehrere Unterzeichner.
  • Programme mit zukünftigen Interaktionen: Einige Programme auf Solana müssen in bestimmten Abständen mit anderen Programmen oder Diensten interagieren. Dauerhafte Nonces helfen dabei, die Integrität dieser Interaktionen zu wahren.
  • Interaktion mit anderen Blockchains: Wenn Solana mit anderen Blockchains interagiert, tragen dauerhafte Nonces dazu bei, die Gültigkeit von Cross-Chain-Transaktionen sicherzustellen.

Jetzt können wir mit unserem Beispiel beginnen.

Solana-Transaktionen: Implementierungsschritte

Schritt 1: Abhängigkeiten und Konstanten einrichten

In diesem Schritt importierst du die erforderlichen Module und Hilfsfunktionen. Außerdem definierst du Konstanten und Schlüsselpaare für das Beispiel. Diese Abhängigkeiten und Konstanten werden während des gesamten Transaktionsprozesses verwendet.

Code
import {
  Connection,
  Keypair,
  LAMPORTS_PER_SOL,
  NonceAccount,
  NONCE_ACCOUNT_LENGTH,
  SystemProgram,
  Transaction,
} from "@solana/web3.js";
import { encodeAndWriteTransaction, loadWallet, readAndDecodeTransaction } from "./utils";

const nonceAuthKeypair = loadWallet('./wallets/nonceAuth.json');
const nonceKeypair = Keypair.generate();
const senderKeypair = loadWallet('./wallets/wallet.json');
const connection = new Connection("https://devnet.helius-rpc.com/?api-key=");
const waitTime = 120000;
const TranferAmount = LAMPORTS_PER_SOL * 0.01;

Schritt 2: Die Funktion sendTransaction erstellen

Die Funktion sendTransaction steuert den Versand einer Transaktion mit einer dauerhaften Nonce. Sie übernimmt das Erstellen und Bestätigen der Nonce sowie das Ausführen der Transaktion.

Code
async function sendTransaction() {
	console.log("Starting Nonce Transaction")
  try {
    const nonceCreationTxSig = await nonce();
    const confirmationStatus = await connection.confirmTransaction(nonceCreationTxSig);
    if (!confirmationStatus.value.err) {
      console.log("Nonce account creation confirmed.");

      const nonce = await getNonce();
      await createTx(nonce);
      await signOffline(waitTime);
      await executeTx();
    } else {
      console.error("Nonce account creation transaction failed:", confirmationStatus.value.err);
    }
  } catch (error) {
    console.error(error);
  }
}

Schritt 3: Die Funktion nonce erstellen

Die Funktion nonce erstellt und initialisiert das Konto für die dauerhafte Nonce. Dazu berechnet sie die für das Konto erforderliche Rent, ruft den neuesten Blockhash ab und erstellt Transaktionen, um das Nonce-Konto anzulegen und zu initialisieren.

  1. Bevor wir das Nonce-Konto erstellen, müssen wir die erforderliche Rent für die Speicherung der Kontodaten berechnen und den neuesten Blockhash abrufen.
Code
async function nonce() {
  const rent = await connection.getMinimumBalanceForRentExemption(NONCE_ACCOUNT_LENGTH);
  const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
  1. Jetzt erstellen wir eine Transaktion, die das Nonce-Konto anlegt. Dabei verwenden wir die Anweisung SystemProgram.createAccount, um Speicherplatz für das Nonce-Konto zuzuweisen.
Code
const createNonceTx = new Transaction().add(
    SystemProgram.createAccount({
      fromPubkey: nonceAuthKeypair.publicKey,
      newAccountPubkey: nonceKeypair.publicKey,
      lamports: rent,
      space: NONCE_ACCOUNT_LENGTH,
      programId: SystemProgram.programId,
    })
  );
  1. Wir signieren die Transaktion mit den Schlüsselpaaren der Autorität und senden sie an das Solana-Netzwerk. Diese Transaktion erstellt das Konto für die dauerhafte Nonce.
Code
createNonceTx.feePayer = nonceAuthKeypair.publicKey;
  createNonceTx.recentBlockhash = blockhash;
  createNonceTx.lastValidBlockHeight = lastValidBlockHeight;
  createNonceTx.sign(nonceAuthKeypair, nonceKeypair);

  try {
    const signature = await connection.sendRawTransaction(createNonceTx.serialize(), { skipPreflight: false, preflightCommitment: "single" });
  1. Nach dem Senden bestätigen wir den Status der Transaktion, um sicherzustellen, dass das Nonce-Konto erfolgreich erstellt wurde.
Code
const confirmationStatus = await connection.confirmTransaction(signature);
    if (confirmationStatus.value.err) {
      throw new Error("Nonce account creation transaction failed: " + confirmationStatus.value.err);
    }
    console.log("Nonce account created:", signature);
  1. Damit wir das Nonce-Konto vollständig nutzen können, müssen wir seinen Wert initialisieren. Dazu erstellen wir eine neue Transaktion, die die Anweisung SystemProgram.nonceInitialize ausführt.
Code
// Initialize the nonce value within the account
    const initializeNonceTx = new Transaction().add(
      SystemProgram.nonceInitialize({
        noncePubkey: nonceKeypair.publicKey,
        authorizedPubkey: nonceAuthKeypair.publicKey,
      })
    );
  1. Wie im vorherigen Schritt signieren wir die Transaktion und senden sie an das Netzwerk, um das Nonce-Konto zu initialisieren.
Code
const { blockhash: initBlockhash, lastValidBlockHeight: initLastValidBlockHeight } = await connection.getLatestBlockhash();
    initializeNonceTx.feePayer = nonceAuthKeypair.publicKey;
    initializeNonceTx.recentBlockhash = initBlockhash;
    initializeNonceTx.lastValidBlockHeight = initLastValidBlockHeight;
    initializeNonceTx.sign(nonceAuthKeypair);

    const initSignature = await connection.sendRawTransaction(initializeNonceTx.serialize(), { skipPreflight: false, preflightCommitment: "single" });
  1. Abschließend bestätigen wir den Status der Initialisierungstransaktion, um sicherzustellen, dass das Nonce-Konto korrekt initialisiert wurde.
Code
const initConfirmationStatus = await connection.confirmTransaction(initSignature);
    if (initConfirmationStatus.value.err) {
      throw new Error("Nonce initialization transaction failed: " + initConfirmationStatus.value.err);
    }
    console.log("Nonce initialized:", initSignature);
    return initSignature;
  } catch (error) {
    console.error("Failed in createNonce function: ", error);
    throw error;
  }
}

Die vollständige Funktion sollte ungefähr so aussehen:

Code
async function nonce() {
  // For creating the nonce account
  const rent = await connection.getMinimumBalanceForRentExemption(
    NONCE_ACCOUNT_LENGTH
  );
  const { blockhash, lastValidBlockHeight } =
    await connection.getLatestBlockhash();
  const createNonceTx = new Transaction().add(
    SystemProgram.createAccount({
      fromPubkey: nonceAuthKeypair.publicKey,
      newAccountPubkey: nonceKeypair.publicKey,
      lamports: rent,
      space: NONCE_ACCOUNT_LENGTH,
      programId: SystemProgram.programId,
    })
  );

  createNonceTx.feePayer = nonceAuthKeypair.publicKey;
  createNonceTx.recentBlockhash = blockhash;
  createNonceTx.lastValidBlockHeight = lastValidBlockHeight;
  createNonceTx.sign(nonceAuthKeypair, nonceKeypair);

  try {
    // Create the nonce account
    const signature = await connection.sendRawTransaction(
      createNonceTx.serialize(),
      { skipPreflight: false, preflightCommitment: "single" }
    );
    const confirmationStatus = await connection.confirmTransaction(signature);
    if (confirmationStatus.value.err) {
      throw new Error(
        "Nonce account creation transaction failed: " +
          confirmationStatus.value.err
      );
    }
    console.log("Nonce account created:", signature);

    // Now, initialize the nonce
    const initializeNonceTx = new Transaction().add(
      SystemProgram.nonceInitialize({
        noncePubkey: nonceKeypair.publicKey,
        authorizedPubkey: nonceAuthKeypair.publicKey,
      })
    );

    const {
      blockhash: initBlockhash,
      lastValidBlockHeight: initLastValidBlockHeight,
    } = await connection.getLatestBlockhash();
    initializeNonceTx.feePayer = nonceAuthKeypair.publicKey;
    initializeNonceTx.recentBlockhash = initBlockhash;
    initializeNonceTx.lastValidBlockHeight = initLastValidBlockHeight;
    initializeNonceTx.sign(nonceAuthKeypair); // Only sign with nonceAuthKeypair

    const initSignature = await connection.sendRawTransaction(
      initializeNonceTx.serialize(),
      { skipPreflight: false, preflightCommitment: "single" }
    );
    const initConfirmationStatus = await connection.confirmTransaction(
      initSignature
    );
    if (initConfirmationStatus.value.err) {
      throw new Error(
        "Nonce initialization transaction failed: " +
          initConfirmationStatus.value.err
      );
    }
    console.log("Nonce initialized:", initSignature);
    return initSignature;
  } catch (error) {
    console.error("Failed in createNonce function: ", error);
    throw error;
  }
}

Schritt 4: Die Funktion getNonce erstellen

Definiere die Funktion getNonce. Sie ruft den Nonce-Wert aus dem erstellten Nonce-Konto ab.

Code
async function getNonce() {
  const nonceAccount = await fetchNonceInfo();
  return nonceAccount.nonce;
}

Schritt 5: Die Funktion createTx erstellen

Definiere die Funktion createTx. Sie erstellt eine Beispieltransaktion, die sowohl die Anweisung zum Fortschreiben der Nonce als auch eine Übertragungsanweisung enthält. Die Funktion verwendet die zuvor abgerufene Nonce, um die Authentizität der Transaktion sicherzustellen.

Code
async function createTx(nonce) {
  const destination = Keypair.generate();

  const advanceNonceIx = SystemProgram.nonceAdvance({
    noncePubkey: nonceKeypair.publicKey,
    authorizedPubkey: nonceAuthKeypair.publicKey
  });

  const transferIx = SystemProgram.transfer({
    fromPubkey: senderKeypair.publicKey,
    toPubkey: destination.publicKey,
    lamports: TranferAmount,
  });

  const sampleTx = new Transaction();
  sampleTx.add(advanceNonceIx, transferIx);
  sampleTx.recentBlockhash = nonce; // Use the nonce fetched earlier
  sampleTx.feePayer = senderKeypair.publicKey;

  const serialisedTx = encodeAndWriteTransaction(sampleTx, "./unsignedTxn.json", false);
  return serialisedTx;
}

Schritt 6: Die Funktion signOffline erstellen

Definiere die Funktion signOffline. Sie signiert die Transaktion offline. Vor der Signatur simuliert sie eine Offline-Verzögerung und signiert die Transaktion anschließend mit den Schlüsselpaaren des Absenders und der Nonce-Autorität.

Code
async function createTx(nonce) {
  const destination = Keypair.generate();

  const advanceNonceIx = SystemProgram.nonceAdvance({
    noncePubkey: nonceKeypair.publicKey,
    authorizedPubkey: nonceAuthKeypair.publicKey
  });

  const transferIx = SystemProgram.transfer({
    fromPubkey: senderKeypair.publicKey,
    toPubkey: destination.publicKey,
    lamports: TranferAmount,
  });

  const sampleTx = new Transaction();
  sampleTx.add(advanceNonceIx, transferIx);
  sampleTx.recentBlockhash = nonce; // Use the nonce fetched earlier
  sampleTx.feePayer = senderKeypair.publicKey;

  const serialisedTx = encodeAndWriteTransaction(sampleTx, "./unsignedTxn.json", false);
  return serialisedTx;
}

Schritt 7: Die Funktion executeTx erstellen

Die Funktion executeTx sendet die signierte Transaktion zur Ausführung an das Solana-Netzwerk. Dies ist der letzte Schritt im Transaktionsprozess, bei dem die Transaktion an das Netzwerk übertragen wird.

Code
async function executeTx() {
  const signedTx = await readAndDecodeTransaction("./signedTxn.json");
  const sig = await connection.sendRawTransaction(signedTx.serialize());
  console.log("Tx sent: ", sig);
}

Schritt 8: Die Funktion fetchNonceInfo erstellen

Die Funktion fetchNonceInfo ruft Nonce-Informationen aus dem erstellten Nonce-Konto ab. Falls erforderlich, versucht sie dies bis zu dreimal. Dadurch bleibt die für die Transaktion verwendete Nonce aktuell und gültig.

Code
async function fetchNonceInfo(retries = 3) {
  while (retries > 0) {
    const accountInfo = await connection.getAccountInfo(nonceKeypair.publicKey);
    if (accountInfo) {
      const nonceAccount = NonceAccount.fromAccountData(accountInfo.data);
      return nonceAccount;
    }
    retries--;
    if (retries > 0) {
      console.log(`Retry fetching nonce in 3 seconds. ${retries} retries left.`);
      await new Promise(res => setTimeout(res, 3000)); // wait for 3 seconds
    }
  }
  throw new Error("No account info found");
}

Schritt 9: Die Funktion sendTransaction aufrufen

Rufe abschließend die Funktion sendTransaction auf, um den Transaktionsprozess zu starten. Diese Funktion führt alle zuvor definierten Schritte zusammen, um eine Transaktion mit einer dauerhaften Nonce zu erstellen, zu signieren und auszuführen.

Code
ts-node main

Beim Ausführen von sendTransaction wird für eine erfolgreiche Transaktion eine Transaktionssignatur ausgegeben. Diese Signatur ist entscheidend, um die Transaktion im Solana-Netzwerk zu verfolgen und zu verifizieren.

Code
Tx written to ./unsignedTxn.jsonTx written to ./signedTxn.jsonTx sent:  64vBuSbN8SJZo74r8KoRFF6GJD7iszdckER2NkmFfYzHCN1H9Q3iC2Z3CP7NsoAgrP2jdyQrVeSzVx6vsbxNEE5U

Du hast jetzt erfolgreich eine dauerhafte Nonce in einer Transaktion verwendet!

Vollständiger Code

Code
import {
  Connection,
  Keypair,
  LAMPORTS_PER_SOL,
  NonceAccount,
  NONCE_ACCOUNT_LENGTH,
  SystemProgram,
  Transaction,
} from "@solana/web3.js";
import {
  encodeAndWriteTransaction,
  loadWallet,
  readAndDecodeTransaction,
} from "./utils";

const TranferAmount = LAMPORTS_PER_SOL * 0.01;

const nonceAuthKeypair = loadWallet("./wallets/nonceAuth.json");
const nonceKeypair = Keypair.generate();
const senderKeypair = loadWallet("./wallets/wallet.json");
const connection = new Connection(
  "https://devnet.helius-rpc.com/?api-key="
);

const waitTime = 120000;

async function sendTransaction() {
  try {
    // Create nonce and get its creation transaction signature
    const nonceCreationTxSig = await nonce();

    // Ensure nonce account creation is confirmed before moving forward
    const confirmationStatus = await connection.confirmTransaction(
      nonceCreationTxSig
    );
    if (!confirmationStatus.value.err) {
      console.log("Nonce account creation confirmed.");

      const nonce = await getNonce();
      await createTx(nonce);
      await signOffline(waitTime);
      await executeTx();
    } else {
      console.error(
        "Nonce account creation transaction failed:",
        confirmationStatus.value.err
      );
    }
  } catch (error) {
    console.error(error);
  }
}

async function nonce() {
  // For creating the nonce account
  const rent = await connection.getMinimumBalanceForRentExemption(
    NONCE_ACCOUNT_LENGTH
  );
  const { blockhash, lastValidBlockHeight } =
    await connection.getLatestBlockhash();
  const createNonceTx = new Transaction().add(
    SystemProgram.createAccount({
      fromPubkey: nonceAuthKeypair.publicKey,
      newAccountPubkey: nonceKeypair.publicKey,
      lamports: rent,
      space: NONCE_ACCOUNT_LENGTH,
      programId: SystemProgram.programId,
    })
  );

  createNonceTx.feePayer = nonceAuthKeypair.publicKey;
  createNonceTx.recentBlockhash = blockhash;
  createNonceTx.lastValidBlockHeight = lastValidBlockHeight;
  createNonceTx.sign(nonceAuthKeypair, nonceKeypair);

  try {
    // Create the nonce account
    const signature = await connection.sendRawTransaction(
      createNonceTx.serialize(),
      { skipPreflight: false, preflightCommitment: "single" }
    );
    const confirmationStatus = await connection.confirmTransaction(signature);
    if (confirmationStatus.value.err) {
      throw new Error(
        "Nonce account creation transaction failed: " +
          confirmationStatus.value.err
      );
    }
    console.log("Nonce account created:", signature);

    // Now, initialize the nonce
    const initializeNonceTx = new Transaction().add(
      SystemProgram.nonceInitialize({
        noncePubkey: nonceKeypair.publicKey,
        authorizedPubkey: nonceAuthKeypair.publicKey,
      })
    );

    const {
      blockhash: initBlockhash,
      lastValidBlockHeight: initLastValidBlockHeight,
    } = await connection.getLatestBlockhash();
    initializeNonceTx.feePayer = nonceAuthKeypair.publicKey;
    initializeNonceTx.recentBlockhash = initBlockhash;
    initializeNonceTx.lastValidBlockHeight = initLastValidBlockHeight;
    initializeNonceTx.sign(nonceAuthKeypair); // Only sign with nonceAuthKeypair

    const initSignature = await connection.sendRawTransaction(
      initializeNonceTx.serialize(),
      { skipPreflight: false, preflightCommitment: "single" }
    );
    const initConfirmationStatus = await connection.confirmTransaction(
      initSignature
    );
    if (initConfirmationStatus.value.err) {
      throw new Error(
        "Nonce initialization transaction failed: " +
          initConfirmationStatus.value.err
      );
    }
    console.log("Nonce initialized:", initSignature);
    return initSignature;
  } catch (error) {
    console.error("Failed in createNonce function: ", error);
    throw error;
  }
}

async function getNonce() {
  const nonceAccount = await fetchNonceInfo();
  return nonceAccount.nonce;
}

async function createTx(nonce: string) {
  const destination = Keypair.generate();

  const advanceNonceIx = SystemProgram.nonceAdvance({
    noncePubkey: nonceKeypair.publicKey,
    authorizedPubkey: nonceAuthKeypair.publicKey,
  });

  const transferIx = SystemProgram.transfer({
    fromPubkey: senderKeypair.publicKey,
    toPubkey: destination.publicKey,
    lamports: TranferAmount,
  });

  const sampleTx = new Transaction();
  sampleTx.add(advanceNonceIx, transferIx);
  sampleTx.recentBlockhash = nonce; // Use the nonce fetched earlier
  sampleTx.feePayer = senderKeypair.publicKey;

  const serialisedTx = encodeAndWriteTransaction(
    sampleTx,
    "./unsigned.json",
    false
  );
  return serialisedTx;
}
async function signOffline(waitTime = 120000): Promise {
  await new Promise((resolve) => setTimeout(resolve, waitTime));
  const unsignedTx = readAndDecodeTransaction("./unsigned.json");
  unsignedTx.sign(senderKeypair, nonceAuthKeypair); // Sign with both keys
  const serialisedTx = encodeAndWriteTransaction(unsignedTx, "./signed.json");
  return serialisedTx;
}

async function executeTx() {
  const signedTx = readAndDecodeTransaction("./signed.json");
  const sig = await connection.sendRawTransaction(signedTx.serialize());
  console.log("      Tx sent: ", sig);
}

async function fetchNonceInfo(retries = 3): Promise {
  while (retries > 0) {
    const accountInfo = await connection.getAccountInfo(nonceKeypair.publicKey);
    if (accountInfo) {
      const nonceAccount = NonceAccount.fromAccountData(accountInfo.data);
      return nonceAccount;
    }
    retries--;
    if (retries > 0) {
      console.log(
        `Retry fetching nonce in 3 seconds. ${retries} retries left.`
      );
      await new Promise((res) => setTimeout(res, 3000)); // wait for 3 seconds
    }
  }
  throw new Error("No account info found");
}

sendTransaction();

Solana-Transaktionen: Helius RPC verwenden

Helius kann als leistungsstarker Vermittler für die Interaktion mit Solana RPC dienen. Das vereinfacht den Abruf der Blockhash-Informationen, die für dauerhafte Nonces erforderlich sind. Mit Helius kannst du den Lebenszyklus deiner Solana-Transaktionen zuverlässiger verwalten, insbesondere in Offline-Szenarien. Du erhältst einfachen Zugriff auf Blockhashes und kannst deine Anwendungen dadurch besser gegen abgelaufene Transaktionen absichern.

Zusammengefasst bieten dauerhafte Nonces in Solana-Transaktionen eine sichere und zuverlässige Möglichkeit, Offline-Transaktionen abzuwickeln und ihre Authentizität sicherzustellen. Mit den Schritten aus diesem Leitfaden können Entwickler dauerhafte Nonces in ihren Solana-Anwendungen implementieren und so Sicherheit und Flexibilität verbessern.

‍

‍

‍

Helius abonnieren

Bleib bei der Solana-Entwicklung auf dem Laufenden und erhalte Updates, wenn wir neue Beiträge veröffentlichen