Build on EIP-8130

Native account abstraction with viem. Create portable smart accounts, authorize scoped session-key policies, batch calls atomically, and sponsor gas with a payer.

Grab the skill

A ready-made skill file that teaches your AI coding agent the full EIP-8130 + payer API — accounts, session-key policies, batching, and gas sponsorship. Grab it and drop it into your project to scaffold 8130 flows on demand.

1. Get the tooling

The EIP-8130 support lives in a viem fork branch. Install it and import from the experimental entrypoints.

# Use the viem fork branch that ships the experimental module:
bun add "viem@github:chunter-cb/viem#feat/eip-8130"

# Then import from the experimental entrypoints:
import { newSmartAccount8130, sendCalls8130 } from "viem/experimental/eip8130";
import { createPayerClient } from "viem/experimental/eip8168";

2. Create an account and send a batch

The account address is deterministic — fund it first, then the first transaction deploys it and runs your calls in one atomic batch.

import { privateKeyToAccount, generatePrivateKey } from "viem/accounts";
import {
  createPublicClient, http, parseEther, toHex,
  newSmartAccount8130, sendCalls8130, estimateGas8130, encodeWalletCalls,
  waitForTransactionReceipt8130, allPhasesSucceeded,
} from "viem/experimental/eip8130";

const client = createPublicClient({
  chain: {
    id: 84538453, // vibenet devnet (Base Sepolia = 84532)
    name: "vibenet",
    nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
    rpcUrls: { default: { http: ["https://rpc.vibes.base.org"] } },
  },
  transport: http(),
});

// 1) Signer = LocalAccount (NOT key.k1 — that builds an actor identity).
const signer = privateKeyToAccount(generatePrivateKey());

// 2) Deterministic account — its address exists before any tx.
const account = newSmartAccount8130({ signer });

// 3) Fund account.address (faucet), then estimate + send. The FIRST tx
//    deploys the account AND runs the batch atomically.
const calls = [{ to: "0x…recipient", value: parseEther("0.001") }];
const wire = encodeWalletCalls({ account: account.address, calls: [calls] });
const gas = await estimateGas8130(client, {
  sender: account.address,
  accountChanges: [account.createChange],
  calls: wire,
});
const hash = await sendCalls8130(client, {
  account,
  accountChanges: [account.createChange], // omit on subsequent txs
  calls,
  dataSuffix: toHex("invoice #4242"),
  gas: (gas * 120n) / 100n,
});

// 4) Wait and verify every phase (create + each call) succeeded.
const receipt = await waitForTransactionReceipt8130(client, { hash });
if (!allPhasesSucceeded(receipt)) throw new Error("a phase reverted");

Next steps: authorize a policy-gated session actor (authorizeActor + defineSessionPolicy), sponsor gas (createPayerClient + sendSponsoredCalls), and read state (getActorConfig8130, getPolicy8130, getLockStatus8130).

Docs

Try it in the demo