Guide
Sending transactions
An EIP-8130 transaction is an AA_TX_TYPE (0x79) envelope. sendCalls8130 fills nonce and fees, encodes calls, signs sender_auth (and payer_auth when sponsored), serializes, and submits via eth_sendRawTransaction.
Estimate gas
Unlike legacy EVM txs, the gas budget for an 8130 transaction is node-computed and required up front. estimateGas8130 prices authentication from the shape of the auth blob (never a real signature), so you can estimate before signing. Pass a senderAuthVerifier hint (a canonical authenticator address) matching your signer.
import { parseEther } from 'viem'import { canonicalAuthenticators, estimateGas8130,} from 'viem/experimental/eip8130'
const gas = await estimateGas8130(client, { sender: account.address, // Include createChange only on the first (deploying) transaction. accountChanges: [account.createChange], // Each inner array is one atomic executeBatch phase. calls: [[{ to: recipient, value: parseEther('0.001') }]], senderAuthVerifier: canonicalAuthenticators.k1, // .p256 / .passkey})For a policy-gated actor (a session key), also pass senderActorId so the node simulates against the intended acting actor and resolves the right policy, instead of the account's self-actor.
Deploy on first use
Include account.createChange in accountChanges to deploy and run the first calls in one transaction.
import { parseEther } from 'viem'import { canonicalAuthenticators, estimateGas8130, sendCalls8130, waitForTransactionReceipt8130,} from 'viem/experimental/eip8130'
const calls = [{ to: recipient, value: parseEther('0.001') }]
const gas = await estimateGas8130(client, { sender: account.address, accountChanges: [account.createChange], calls: [calls], senderAuthVerifier: canonicalAuthenticators.k1,})
const hash = await sendCalls8130(client, { account, accountChanges: [account.createChange], // omit on later txs calls, gas: (gas * 120n) / 100n,})
const receipt = await waitForTransactionReceipt8130(client, { hash })receipt.eip8130.phaseStatuses // e.g. ['0x1']Follow-up transactions
Once deployed, drop account changes and pass calls only. The 2D nonce sequence is read automatically.
const gas = await estimateGas8130(client, { sender: account.address, calls: [[{ to: token, data: transferData }]],})
const hash = await sendCalls8130(client, { account, calls: [{ to: token, data: transferData }], gas: (gas * 120n) / 100n,})Transaction expiry tracking
Any EIP-8130 transaction with a non-zero expiry (sequenced or nonce-free) can no longer land once the chain's latest block timestamp passes it. Thread the signed expiry out of sendCalls8130 with onTransaction, then pass it to waitForTransactionReceipt8130. The wait fails fast with TransactionExpiredError instead of spinning until timeout.
import { sendCalls8130, TransactionExpiredError, waitForTransactionReceipt8130,} from 'viem/experimental/eip8130'
// Thread the signed expiry into the wait — reliable for any non-zero expiry// (sequenced or nonce-free), including auto-computed nonceless windows.let expiry: bigint | undefinedconst hash = await sendCalls8130(client, { account, calls, gas, onTransaction: (tx) => { expiry = tx.expiry },})
try { const receipt = await waitForTransactionReceipt8130(client, { hash, expiry }) receipt.eip8130.phaseStatuses} catch (e) { if (e instanceof TransactionExpiredError) { // Tx can never land — resubmit with a fresh expiry. } else { throw e }}Batching calls
A flat calls array runs as one atomic phase. Nested arrays control phases explicitly. The full model is in Calls & phases.
const hash = await sendCalls8130(client, { account, calls: [ { to: tokenA, data: approveData }, { to: router, data: swapData }, ], gas: 400_000n,})Sponsored (payer) gas
A payer account co-signs and pays gas: a key you hold or an ERC-8168 service. Token payment (e.g. USDV) typically uses an early call phase; details in Gas & payers.
const hash = await sendCalls8130(client, { account, calls: [{ to: recipient, data }], gas: 250_000n, payer: { account: sponsor },})Lower-level control
prepareTransaction8130fills chain id, nonce sequence, and EIP-1559 fees.account.signTransactionproduces auth blobs and a serialized envelope forsendRawTransaction.
Metadata
Transactions may carry an opaque, signed metadata field for attribution, memos, or off-chain commitments. It is covered by the same sender signature and never affects execution. See Metadata & attribution for the field and the experimental encoding.
- Full API: Viem. Sending a Transaction