← All posts
Mar 16, 2026 / 10 min read / Guide

How to Use Nonce Accounts on Solana

Every Solana transaction includes a recent blockhash that acts as a deadline. If the transaction doesn't land before that blockhash expires (roughly 60 seconds), it's gone. Durable nonce accounts remove that constraint entirely, and they unlock a powerful pattern: sending the same signed transaction to multiple providers at once and letting the fastest one land it.

The problem with blockhashes

Normally, when you build a Solana transaction, you fetch a recent blockhash from an RPC node and include it in the transaction. This blockhash serves two purposes: it prevents replay attacks (each transaction can only land once) and it gives the transaction a TTL of about 60 seconds.

That 60 second window creates problems. If you're building a transaction offline, or signing it through a multisig flow, or queuing it for later execution, the blockhash might expire before the transaction ever gets submitted. You'd have to rebuild and re-sign the whole thing.

More importantly for high performance use cases, the blockhash ties your transaction to a single point in time. If you want to send the same transaction to multiple endpoints to maximize your chance of landing, you can't reuse the same signed transaction across providers that might process it at different times.

What nonce accounts solve

A durable nonce account is an on-chain account that stores a nonce value. Instead of using a recent blockhash, your transaction uses this stored nonce as its "blockhash" field. The nonce doesn't expire with time. It only changes when it gets consumed by a transaction.

This gives you two things:

The multi-provider pattern

This is where nonce accounts really shine. Say you want to maximize your transaction landing rate by sending it through multiple transaction delivery services at the same time. Without a nonce, every provider that successfully forwards your transaction to a leader will land a separate copy. That means your priority fees get spent multiple times, and if the transaction involves a transfer, you're sending it more than once.

With a nonce, you can build the same transaction using a single nonce value, send it to as many providers as you want, and only the fastest one will actually land. The rest get rejected because the nonce has already been consumed. You only pay fees once.

The pattern looks like this:

  1. Create and fund a nonce account
  2. Build a separate transaction for each provider, all using the same nonce value as the blockhash
  3. Sign and send each one to its respective provider
  4. The fastest provider lands its transaction, the nonce gets consumed, and every other transaction using that nonce is automatically rejected

One set of fees. No duplicate transactions. No expiry pressure. Just fire and forget across every channel you have.

Creating a nonce account

Using the Solana CLI

The fastest way to get started is with the CLI. Generate a new keypair for the nonce account and create it on chain:

terminal
# Generate a keypair for the nonce account
solana-keygen new -o nonce.json --no-bip39-passphrase

# Create the nonce account (costs ~0.0015 SOL for rent)
solana create-nonce-account nonce.json 0.0015

# View the nonce value
solana nonce nonce.json

The create-nonce-account command does three things in a single transaction: creates the account, allocates space for the nonce data (80 bytes), and initializes it with a nonce value. The authority defaults to your wallet keypair.

Using TypeScript

If you need to create nonce accounts programmatically, here's how to do it with @solana/web3.js:

create-nonce.ts
import {
  Connection, Keypair, SystemProgram,
  Transaction, NONCE_ACCOUNT_LENGTH,
} from "@solana/web3.js";

const connection = new Connection("https://api.mainnet-beta.solana.com");
const payer = loadKeypair("./wallet.json");
const nonceKeypair = Keypair.generate();

// Calculate rent exemption for nonce account (80 bytes)
const rent = await connection.getMinimumBalanceForRentExemption(
  NONCE_ACCOUNT_LENGTH
);

const tx = new Transaction().add(
  // Create the account
  SystemProgram.createAccount({
    fromPubkey: payer.publicKey,
    newAccountPubkey: nonceKeypair.publicKey,
    lamports: rent,
    space: NONCE_ACCOUNT_LENGTH,
    programId: SystemProgram.programId,
  }),
  // Initialize it as a nonce account
  SystemProgram.nonceInitialize({
    noncePubkey: nonceKeypair.publicKey,
    authorizedPubkey: payer.publicKey,
  })
);

await sendAndConfirmTransaction(connection, tx, [payer, nonceKeypair]);

console.log("Nonce account:", nonceKeypair.publicKey.toBase58());

Save the nonce account's keypair somewhere safe. You'll reference its public key every time you build a transaction with it.

Building transactions with a nonce

Using a nonce account in a transaction requires two changes from the normal flow:

  1. Use the stored nonce value as the transaction's recentBlockhash
  2. Add a nonceAdvance instruction as the very first instruction in the transaction

The advance instruction is what consumes the nonce. The Solana runtime checks for this instruction and validates the nonce before processing the rest of the transaction. If the nonce has already been consumed, the entire transaction is rejected.

send-with-nonce.ts
import {
  Connection, PublicKey, SystemProgram,
  Transaction, NonceAccount,
} from "@solana/web3.js";

const NONCE_PUBKEY = new PublicKey("<your-nonce-account>");

// Fetch the current nonce value
const nonceAccountInfo = await connection.getAccountInfo(NONCE_PUBKEY);
const nonceAccount = NonceAccount.fromAccountData(nonceAccountInfo.data);
const nonceValue = nonceAccount.nonce;

// Build the transaction
const tx = new Transaction();

// First instruction MUST be nonceAdvance
tx.add(
  SystemProgram.nonceAdvance({
    noncePubkey: NONCE_PUBKEY,
    authorizedPubkey: payer.publicKey,
  })
);

// Then add your actual instructions
tx.add(
  SystemProgram.transfer({
    fromPubkey: payer.publicKey,
    toPubkey: recipient,
    lamports: 1_000_000,
  })
);

// Use the nonce as the "blockhash"
tx.recentBlockhash = nonceValue;
tx.feePayer = payer.publicKey;

// Sign it once
tx.sign(payer);

Sending to multiple providers

Each provider may need different instructions (for example, Jito requires a tip transfer to a Jito tip account). So you build a separate transaction per provider, but they all share the same nonce value as their blockhash. Only the first one to land will succeed.

multi-send.ts
// Helper to build a nonce transaction with provider-specific instructions
function buildNonceTx(
  nonceValue: string,
  instructions: TransactionInstruction[],
) {
  const tx = new Transaction();

  // Nonce advance must always be first
  tx.add(
    SystemProgram.nonceAdvance({
      noncePubkey: NONCE_PUBKEY,
      authorizedPubkey: payer.publicKey,
    })
  );

  // Add the provider-specific instructions
  for (const ix of instructions) tx.add(ix);

  tx.recentBlockhash = nonceValue;
  tx.feePayer = payer.publicKey;
  tx.sign(payer);
  return tx;
}

// Build a unique transaction for each provider
const sends = [
  { url: `https://ams1.glaive.trade?key=${GLAIVE_KEY}`,
    tx: buildNonceTx(nonceValue, [transferIx, tipIx]) },
  { url: `https://mainnet.helius-rpc.com/?api-key=${HELIUS_KEY}`,
    tx: buildNonceTx(nonceValue, [transferIx, tipIx]) },
  { url: "https://api.mainnet-beta.solana.com",
    tx: buildNonceTx(nonceValue, [transferIx]) },
];

// Fire them all in parallel
await Promise.allSettled(
  sends.map(({ url, tx }) =>
    fetch(url, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        jsonrpc: "2.0", id: 1,
        method: "sendTransaction",
        params: [tx.serialize().toString("base64")],
      }),
    })
  )
);

// The fastest provider lands its transaction.
// All others are rejected because the nonce was already consumed.

This is completely safe. Each transaction is unique, but they all reference the same nonce. Once one of them lands and the nonce is consumed, every other transaction using that nonce becomes invalid. There is no risk of paying fees more than once.

Refreshing the nonce for the next transaction

After your transaction lands, the nonce value stored in the account has been consumed. Before you can use that nonce account again, you need to advance it to generate a new nonce value:

advance-nonce.ts
// Advance the nonce to get a fresh value for the next transaction
const advanceTx = new Transaction().add(
  SystemProgram.nonceAdvance({
    noncePubkey: NONCE_PUBKEY,
    authorizedPubkey: payer.publicKey,
  })
);

await sendAndConfirmTransaction(connection, advanceTx, [payer]);

// Fetch the new nonce value for your next transaction
const updatedInfo = await connection.getAccountInfo(NONCE_PUBKEY);
const newNonce = NonceAccount.fromAccountData(updatedInfo.data).nonce;

Alternatively, you can create a pool of nonce accounts and cycle through them. This avoids the round trip of advancing and fetching between transactions.

Things to watch out for

The advance instruction must be first

The nonceAdvance instruction must be the very first instruction in the transaction. If it's anywhere else, the runtime will treat the transaction as a regular blockhash transaction and the nonce will be ignored. Your transaction will then fail with a blockhash not found error since the nonce value isn't a valid recent blockhash.

The nonce authority must sign

The account set as the nonce authority during initialization must sign the transaction. This is the account that has permission to advance the nonce. If you set a different authority than your fee payer, make sure both keypairs sign.

Nonce accounts cost rent

Each nonce account requires ~0.0015 SOL for rent exemption. If you're building a pool of nonce accounts, budget accordingly. The accounts are 80 bytes, so the rent cost is minimal.

Failed transactions don't consume the nonce

If your transaction fails during execution (not due to the nonce, but because of an instruction error), the nonce is still consumed. However, if the transaction is rejected before execution (invalid signature, insufficient funds for fees), the nonce is not consumed and you can resubmit.

When to use nonce accounts

Nonce accounts add a small amount of overhead (one extra instruction, one account lookup), so they aren't necessary for every transaction. They're most valuable when:

For the multi-provider pattern in particular, nonce accounts turn what would be a risky fire-and-pray approach into a clean, deterministic strategy. Sign once, blast everywhere, and trust that the Solana runtime handles deduplication for you.


If you're looking for a fast transaction delivery provider to pair with your nonce account setup, get started with Glaive. Direct validator connections and intelligent fanout mean your nonce-based transactions reach leaders as fast as possible.