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. Tell Viem which chains run EIP-8130 natively with register8130Chains; the account actions then pick the native path where available and fall back to ERC-4337 elsewhere.
import { defineChain } from 'viem'import { is8130Enabled, register8130Chains } from 'viem/experimental/eip8130'
export const vibenet = defineChain({ 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 chainPortable to non-8130 chains
On a chain without native EIP-8130, wrap the account as a Viem ERC-4337 Smart Account with toSmartAccount8130. Same address, same owner, driven through a bundler and EntryPoint. The full setup (initial actors, factory data) lives in Creating accounts.
import { toSmartAccount8130 } from 'viem/experimental/eip8130'
// On a non-8130 chain, drive the SAME address via ERC-4337 (bundler + EntryPoint).const smartAccount = await toSmartAccount8130({ 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).
import { actorScope, authorizeActor, getConfigSequence8130, getEip8130Deployment, key,} from 'viem/experimental/eip8130'
const { accountConfiguration } = getEip8130Deployment(client.chain.id)!const { multichain: sequence } = await getConfigSequence8130(client, { accountConfiguration, account: account.address,})
// chainId 0 => the signed change is valid on ANY chain.const portableChange = await account.change( [authorizeActor(key.p256({ x, y }), { scope: actorScope.sender })], { chainId: 0, sequence: Number(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 encodeApplySignedActorChangesData:
import { encodeApplySignedActorChangesData } from 'viem/experimental/eip8130'
// Apply the same signed change on any chain as a plain EVM call.const data = encodeApplySignedActorChangesData({ account: account.address, chainId: 0, actorChanges: portableChange.actorChanges, auth: portableChange.auth,})