Understanding Nonces on Ethereum
Ordering, Replay Protection, and What Goes Wrong

What Is a Nonce?

Every Ethereum account has a nonce: a counter that starts at zero and increments by one each time the account sends a transaction. When you sign a transaction, the nonce is included in the signed data. The network will only accept the transaction if the nonce matches exactly what it expects next for your account. If your account has sent 5 transactions, the next one must have nonce 5 (since counting starts at 0). Not 4, not 6, exactly 5.

This simple counter solves two critical problems: it orders your transactions and it prevents replay attacks. Without nonces, someone could take a signed transaction you already sent and broadcast it again, draining your account by replaying the same transfer over and over.

How Nonces Work (Happy Path) Account 0x71C7... nonce: 0 Tx nonce: 0 send 1 ETH Tx nonce: 1 swap token Tx nonce: 2 approve Tx nonce: 3 transfer on chain strictly ordered: 0, 1, 2, 3 ... no gaps, no repeats

Why Nonces Exist

The nonce solves two problems at once:

1. Transaction Ordering

Ethereum guarantees that transactions from the same account are processed in nonce order. If you send three transactions with nonces 0, 1, and 2, they will execute in exactly that order, regardless of when they arrive at different nodes. This is critical for operations that depend on sequence, like approving a token and then transferring it.

2. Replay Protection

Because each nonce can only be used once, a signed transaction cannot be broadcast again after it has been confirmed. If someone captures your signed transaction and tries to replay it, the network will reject it because that nonce has already been consumed. Without this, a single signed "send 1 ETH" transaction could be replayed indefinitely.

The Nonce Gap Problem

The nonce must be exactly the next expected value. If your account's current nonce is 5 and you submit a transaction with nonce 7 (skipping 6), the node will not reject it outright. Instead, it will hold the transaction in the mempool, waiting for nonce 6 to appear. But nonce 6 never comes, so nonce 7 and everything after it is stuck. This is called a nonce gap, and it is one of the most common issues when building applications that send transactions programmatically.

Nonce gaps typically happen when:

What Happens With a Gap? Account nonce: 5 expects nonce 5 nonce: 5 confirmed nonce: 7 stuck! where is nonce 6? nonce: 8 stuck! queued in mempool waiting for nonce 6 to be submitted first

Replacing and Cancelling Transactions

If a transaction is stuck in the mempool (typically because the gas price was too low), you can replace it by sending a new transaction with the same nonce but a higher gas price. The node will drop the old transaction and accept the new one. Most wallets expose this as a "Speed Up" feature.

To cancel a pending transaction, you send a transaction to your own address with the same nonce and a value of 0 ETH (but a higher gas price). This effectively fills the nonce slot with a no-op, and the original transaction is discarded.

Replacing a Stuck Transaction Original Tx nonce: 6, 2 gwei stuck (gas too low) same nonce higher gas Replacement Tx nonce: 6, 10 gwei replaces the original Confirmed! nonce 6 filled queue unblocked nonce 7, 8... now process To cancel: send 0 ETH to yourself with the same nonce Two types of nonces on Ethereum: 1. Account nonce (tx count) -- what this post covers 2. Contract nonce (used for CREATE address derivation)
Go
// Replace a stuck transaction (same nonce, higher gas)
tx := types.NewTransaction(
    6,                // same nonce as the stuck tx
    toAddress,
    value,
    gasLimit,
    newHigherGasPrice, // must be at least 10% higher
    data,
)

// Cancel: send 0 ETH to yourself with the same nonce
cancelTx := types.NewTransaction(
    6,                // same nonce
    myOwnAddress,      // send to yourself
    big.NewInt(0),     // 0 ETH
    21000,            // minimum gas for a simple transfer
    evenHigherGasPrice,
    nil,
)

Nonce Management in Production

When you build a backend service that sends transactions (a relayer, a bot, an automated system), nonce management becomes one of the hardest problems to get right. You cannot simply call eth_getTransactionCount before each transaction, because if you send two transactions in quick succession, the second call might return the same count as the first (the first transaction has not been mined yet).

Common strategies include:

Go
// Get the pending nonce (includes unconfirmed txs in the mempool)
nonce, err := client.PendingNonceAt(ctx, account)

// vs. confirmed nonce (only counts mined transactions)
nonce, err := client.NonceAt(ctx, account, nil)

// The difference between the two tells you how many txs are pending
pending := pendingNonce - confirmedNonce

Contract Nonces

There is a second type of nonce on Ethereum that is less well known. Every contract account also has a nonce, which increments each time the contract deploys another contract using the CREATE opcode. This nonce is used to deterministically compute the address of the newly created contract: address = keccak256(rlp([sender, nonce]))[12:]. This is different from CREATE2, which uses a salt instead of a nonce and produces addresses that do not depend on the deployment order. You can read more about contract deployment in the smart contracts post.

For production-level strategies on how to handle nonces in backend services that send concurrent transactions through multiple nodes, see Nonce Management Patterns.

For the full picture of how transactions travel through the network after the nonce is set, see Sending Transactions on Ethereum.