Guide

Multichain

An EIP-8130 account has one deterministic address on every chain. Where the chain supports EIP-8130 natively you transact directly; elsewhere the same address is driven through ERC-4337. Actor changes can be signed once and replayed everywhere.

Same address everywhere

The address is a deterministic function of the wallet code, salt, and initial actors, so it is identical across chains. Native EIP-8130 is live on Vibenet (chain id 84538453). Tell Viem which chains run EIP-8130 natively with register8130Chains, and spread eip8130ChainConfig into the chain so core client.sendTransaction submits native AA transactions (type 0x79); elsewhere the same address falls back to ERC-4337.

Register native chainsts
import { defineChain } from 'viem'
import {
eip8130ChainConfig,
is8130Enabled,
register8130Chains,
} from 'viem/eip8130'
export const vibenet = defineChain({
...eip8130ChainConfig,
id: 84_538_453,
name: 'Vibenet Devnet',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: { default: { http: ['https://rpc.vibes.base.org'] } },
})
// Mark chains where EIP-8130 is native (Vibenet today).
register8130Chains(vibenet.id)
is8130Enabled(client.chain.id) // → true on a native chain
// With eip8130ChainConfig spread in, core client.sendTransaction submits a
// native AA transaction (type 0x79) for an EIP-8130 account.
const hash = await client.sendTransaction({
account,
calls: [{ to: recipient, data }],
})

Portable to non-8130 chains

On a chain without native EIP-8130, wrap the account as a Viem ERC-4337 Smart Account with toSmartAccount. Same address, same owner, driven through a bundler and EntryPoint. The full setup (initial actors, factory data) lives in Creating accounts.

ERC-4337 fallbackts
import { toSmartAccount } from 'viem/eip8130'
// On a non-8130 chain, drive the SAME address via ERC-4337 (bundler + EntryPoint).
const smartAccount = await toSmartAccount({
client, // pointed at the non-8130 chain
owner, // the controlling signer
address, // the account's canonical address
})

Replayable actor changes

A config change signed against chainId 0 is valid on any chain, so you can rotate keys once and land the change everywhere. It is ordered by a separate multichain sequence (distinct from the per-chain local one).

Sign a portable changets
import {
actorScope,
authorizeActor,
getConfigSequence,
key,
} from 'viem/eip8130'
// The multichain channel has its own sequence, distinct from the per-chain local one.
const { multichain: sequence } = await getConfigSequence(client, {
account: account.address,
})
// The 'multichain' channel binds chainId 0 => the signed change is valid on ANY chain.
const portableChange = await account.change(
[authorizeActor(key.p256({ x, y }), { scope: actorScope.operator })],
{ channel: 'multichain', sequence },
)

Land it by including portableChange in a transaction's accountChanges on an 8130 chain, or apply it as a plain EVM call on any chain with encodeApplySignedAccountChangesData:

Apply anywherets
import { encodeApplySignedAccountChangesData } from 'viem/eip8130'
// Apply the same signed change on any chain as a plain EVM call.
const data = encodeApplySignedAccountChangesData({
account: account.address,
channel: portableChange.channel, // 'multichain'
sequence: portableChange.sequence,
changes: portableChange.changes,
signature: portableChange.signature,
})