Skip to main content
Skill — Copyable reference implementation. Use as-is or customize. See Skills Philosophy.

Nonce Manager

When sending multiple Ethereum transactions, each requires a unique sequential nonce. Concurrent submissions cause collisions. Failed transactions create gaps. The NonceManager solves this.

The Problem

Race condition: Each call fetches nonce from chain (e.g., 5), then all three try to use nonce 5. Nonce gap: If tx with nonce 5 fails, tx with nonce 6 is stuck forever.

Quick Start

How It Works

The key insight from ethers v6 and viem:
Increment delta BEFORE awaiting chain nonce
This allows concurrent callers to get unique nonces without blocking.

State Management

API Reference

createNonceManager(options?)

Creates a new nonce manager instance.

manager.consume(params)

Get and increment nonce atomically. Primary method for sending transactions.

manager.get(params)

Get next nonce without incrementing. For dry-run or estimation.

manager.increment(params)

Increment delta manually. Use when you know a tx will be sent.

manager.reset(params)

Clear cached state. Forces refetch on next get.

manager.recycle(params)

Decrement delta after tx failure. Allows nonce reuse.

manager.getDelta(params)

Get current pending tx count. For debugging.

Concurrent Transactions Pattern

Signer Wrapper Pattern

Like ethers NonceManager, wrap a signer for automatic nonce management:

Error Recovery

Transaction Failure

Stuck Transaction

If a transaction is stuck in mempool, your options:
  1. Wait - It may eventually confirm
  2. Speed up - Send replacement with same nonce, higher gas
  3. Cancel - Send 0-value tx to self with same nonce

Nonce Gap Recovery

If you have a gap (e.g., nonces 5, 7 but not 6):

Multi-Chain Support

Nonces are scoped by address + chainId:

Testing with In-Memory Source

Comparison with Libraries

ethers v6 NonceManager

  • Wraps signer
  • increment() / reset() methods
  • Tightly coupled to signer interface

viem nonceManager

  • Attached to account
  • consume() / get() / reset() methods
  • Decoupled, pluggable sources

Voltaire (this implementation)

  • Both patterns supported
  • recycle() for failure recovery
  • getDelta() for debugging
  • Works with any provider interface