Guide
Nonces & throughput
An EIP-8130 nonce is 2D: a channel key plus a sequence. Sequenced channels give independent ordered lanes for parallelism; the reserved nonce-free channel drops sequencing entirely and leans on expiry. Viem's nonce helper produces the right fields for each.
Three strategies
The nonce helper returns { nonceKey, nonceSequence?, validBefore? } that you spread straight into sendTransaction or prepareTransactionRequest.
import { nonce, sendTransaction } from 'viem/eip8130'
// Standard single-file ordering (channel 0):await sendTransaction(client, { account, calls, gas, ...nonce.sequential() })
// A specific parallel channel (its own counter):await sendTransaction(client, { account, calls, gas, ...nonce.channel(1n) })
// A fresh random channel per send (fire-and-forget):await sendTransaction(client, { account, calls, gas, ...nonce.randomChannel() })
// Nonce-free: no counter, replay-protected by expiry alone:await sendTransaction(client, { account, calls, gas, ...nonce.nonceless({ expiresIn: 600 }) })Parallel channels
Each 2D channel maintains its own sequential counter, so transactions in different channels are independent and may be mined out of order relative to one another. Use a fixed channel(key) for a stable lane, or randomChannel() for collision-free fire-and-forget sends. The next sequence is read from the node when you do not supply one.
// Two independent channels can be mined in either order.await sendTransaction(client, { account, calls: a, gas, ...nonce.channel(1n) })await sendTransaction(client, { account, calls: b, gas, ...nonce.channel(2n) })
// Each channel's next sequence is read from the node automatically.Nonce-free (expiring) mode
The reserved nonceKeyMax channel reads and increments no counter, so a transaction is not ordered against any other; replay protection comes solely from validBefore, a Unix timestamp in milliseconds evaluated against block.timestamp * 1000. It is ideal for fully parallel, retry-safe sends. The deadline must fall within the chain's nonceFreeMaxExpiryWindow; viem derives it from Date.now() by default (override via expiresIn/validBefore). The mechanics and the fee-invariant replay_id are covered in 2D nonces & nonceless.
import { nonce, sendTransaction } from 'viem/eip8130'
// Not ordered against anything; valid until a near-future expiry.const hash = await sendTransaction(client, { account, calls, gas: 250_000n, ...nonce.nonceless({ expiresIn: 600 }),})Reading a sequence manually
sendTransaction reads the next sequence for you. When you need it directly (e.g. to build and batch several sends yourself), read the per-channel counter with getTransactionCount (nonce-free channels have no counter and return an error).
import { getTransactionCount } from 'viem/eip8130'
const sequence = await getTransactionCount(client, { address: account.address, nonceKey: 1n, // per-channel counter})