BotTx|Documentation

Advanced

Durable Nonce & Multi-Path Submission

Sign once, submit many times. Durable Nonce lets you send the same transaction through BoltTx and any other relays in parallel — whichever lands first wins, and the nonce guarantees the transaction can only land once.

The problem with regular blockhash

Standard Solana transactions reference a recent blockhash that expires after 150 slots (~60 seconds at 400ms per slot). If you want to fan out the same signed transaction across multiple delivery paths for redundancy, the blockhash can expire before your slower paths even start — and re-signing with a fresh blockhash changes the signature, defeating the purpose.

How Durable Nonce fixes it

No expiration

Durable Nonce transactions use a stored on-chain nonce instead of a recent blockhash, so they stay valid until you explicitly advance the nonce.

Exactly-once landing

The nonce advances atomically when any copy of the transaction lands. All other copies automatically fail — there is no risk of double-execution.

One signature, many paths

Sign the transaction once, then submit the identical bytes to BoltTx plus any number of other relays in parallel. The fastest path wins.

Step 1 — Create a Nonce account

A Nonce account is a small on-chain account (80 bytes) that stores your current durable nonce. Create it once per wallet — the CLI's default funding of 0.0015 SOL is comfortably above the current rent-exempt minimum (the exact minimum depends on the cluster rent config; query getMinimumBalanceForRentExemption(80) to be precise). Your main wallet is the nonce authority.

Solana CLI
# Generate a new keypair that will identify the nonce account
solana-keygen new -o nonce-account.json

# Create the nonce account, funded with 0.0015 SOL for rent exemption
solana -k sender.json create-nonce-account nonce-account.json 0.0015

# Query the current nonce value
solana nonce nonce-account.json

Step 2 — Submit to BoltTx + other relays in parallel

Build one transaction with the same nonce, sign it once, then fan it out. The example below submits to BoltTx and any secondary relay (your own RPC, a Jito endpoint, etc.) concurrently.

Python
import asyncio, base64
from solders.pubkey import Pubkey
from solders.keypair import Keypair
from solders.message import Message
from solders.transaction import Transaction
from solders.system_program import transfer, TransferParams
from solana.rpc.async_api import AsyncClient

async def submit_with_durable_nonce(
    sender: Keypair,
    nonce_account: Pubkey,
    receiver: Pubkey,
    tip_addr: Pubkey,
):
    # 1. Fetch the current nonce value from the nonce account
    rpc = AsyncClient("https://api.mainnet-beta.solana.com")
    acc = await rpc.get_account_info(nonce_account)
    nonce_hash = acc.value.data[40:72]  # NonceState layout offset
    await rpc.close()

    # 2. Build the transaction with nonce + your instructions + BoltTx tip
    instructions = [
        transfer(TransferParams(
            from_pubkey=sender.pubkey(),
            to_pubkey=receiver,
            lamports=1_000,
        )),
        transfer(TransferParams(
            from_pubkey=sender.pubkey(),
            to_pubkey=tip_addr,
            lamports=800_000,  # 0.0008 SOL — BoltTx Starter tip
        )),
    ]
    message = Message.new_with_nonce(
        instructions,
        payer=sender.pubkey(),
        nonce_account_pubkey=nonce_account,
        nonce_authority_pubkey=sender.pubkey(),
    )

    # 3. Sign ONCE with the nonce hash
    tx = Transaction.new_unsigned(message)
    tx.sign([sender], nonce_hash)
    signed_b64 = base64.b64encode(bytes(tx)).decode()

    # 4. Fan out to BoltTx + any secondary relay concurrently.
    #    The nonce guarantees only one copy actually lands.
    import aiohttp
    async with aiohttp.ClientSession() as http:
        bolttx_task = http.post(
            "https://bolttx.io/v1/send",
            headers={"Authorization": "Bearer YOUR_API_KEY"},
            json={"transaction": signed_b64},
        )
        # Secondary path — your own RPC, Jito, etc.
        backup_task = http.post(
            "https://YOUR-BACKUP-RELAY/sendTransaction",
            json={
                "jsonrpc": "2.0", "id": 1, "method": "sendTransaction",
                "params": [signed_b64, {"encoding": "base64"}],
            },
        )

        results = await asyncio.gather(
            bolttx_task, backup_task, return_exceptions=True
        )
        for i, r in enumerate(results):
            label = ["BoltTx", "Backup"][i]
            if isinstance(r, Exception):
                print(f"{label}: failed — {r}")
            else:
                print(f"{label}: HTTP {r.status}")

Step 3 — Handle the results

After any copy lands, the nonce advances on-chain. Fetch the updated nonce before sending the next transaction. Only one of the parallel submissions will succeed with an actual slot — the others will be rejected with BlockhashNotFound (Agave returns the same TransactionError::BlockhashNotFound it uses for expired blockhashes, because the stored nonce no longer matches the recent_blockhash field of the transaction). This is exactly the behavior you want: exactly-once execution with maximum redundancy.

Important caveats

  • Every Durable Nonce transaction must have nonceAdvance as its first instruction. Most Solana SDKs do this automatically when you use new_with_nonce / createNonceAccount helpers — double-check if you build messages manually.
  • Durable Nonce transactions cost the same tip as regular transactions on BoltTx. The redundancy is about landing reliability, not fee savings.
  • Do not submit the same nonce transaction to fewer than 2 paths — there's no point. Reserve this pattern for cases where landing reliability matters more than simplicity.

Further reading