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.

estimateGas8130ts
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.

First transactionts
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.

Subsequent sendts
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.

onTransaction + expiry waitts
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 | undefined
const 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.

Atomic batchts
const hash = await sendCalls8130(client, {
account,
calls: [
{ to: tokenA, data: approveData },
{ to: router, data: swapData },
],
gas: 400_000n,
})

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.

Payer co-signts
const hash = await sendCalls8130(client, {
account,
calls: [{ to: recipient, data }],
gas: 250_000n,
payer: { account: sponsor },
})

Lower-level control

  • prepareTransaction8130 fills chain id, nonce sequence, and EIP-1559 fees.
  • account.signTransaction produces auth blobs and a serialized envelope for sendRawTransaction.

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.