Skip to content

feat: EIP-8130 (native account abstraction) + ERC-8168 payer services - #5004

Draft
chunter-cb wants to merge 79 commits into
wevm:mainfrom
chunter-cb:feat/eip-8130-production
Draft

feat: EIP-8130 (native account abstraction) + ERC-8168 payer services#5004
chunter-cb wants to merge 79 commits into
wevm:mainfrom
chunter-cb:feat/eip-8130-production

Conversation

@chunter-cb

Copy link
Copy Markdown

Summary

Adds two new (additive) modules to viem for EIP-8130 native account abstraction and its ERC-8168 payer-service companion. The change is almost entirely additive (+16.6k / -8 across ~112 files); no existing public APIs are modified.

  • viem/eip8130 — accounts, actions, and utilities for the EIP-8130 AA_TX_TYPE (0x79) native-AA transaction: smart-account creation, actor management (authorize/revoke, scopes, expiry), session keys, sub-accounts, receipts/metadata, and a portable ERC-4337 path (via bundler + EntryPoint) for chains without native 8130 support.
  • viem/eip8168 — payer-service client for sponsoring EIP-8130 transactions (discover/aggregate payer sources, carry account changes through sponsored calls).

Both modules are documented under site/pages/eip8130/** and are wired into the docs sidebar.

What's included

  • Accounts: newSmartAccount, toSmartAccount, toAccount (address-only / delegated EOA modes), key builders (key.k1, key.p256, key.webAuthn, key.trustedExecutor, ...).
  • Actions: estimateGas, getActorConfig, getConfigSequence, getTransaction, getTransactionReceipt, waitForTransactionReceipt, isActor, lock/policy/session reads, etc.
  • Utilities: transaction (de)serialization for type 0x79, actor-change hashing/signing, address computation (CREATE2), signed-actor-changes signature envelope.
  • Core touch points (minimal): getTransaction / waitForTransactionReceipt learn to flatten the nested 0x79 response body.

Testing

  • Unit tests colocated across src/eip8130/** (encoding, factory args, signing, nonce/scope logic).
  • A network-gated Base Sepolia ERC-4337 e2e (scripts/eip8130, run via test/vitest.eip8130.config.ts) exercises create-on-first-use, a follow-up userOp, and authorize/revoke actor changes end-to-end against a bundler.

Status / notes

  • Opened as a draft: EIP-8130 is not yet frozen, so this lands as viem/eip8130 for iteration. The intent is a clean path to promote once the spec + canonical contracts stabilize.
  • Synced to latest main (merge, not rebase) to preserve history; only trivial config conflicts (package.json knip list, scripts/tsconfig.json excludes) were resolved.

chunter-cb added 30 commits June 9, 2026 14:44
Adds a ground-up `viem/experimental/eip8130` module implementing the
EIP-8130 (`AA_TX_TYPE` = 0x7b) wire format:

- serializeTransaction8130 / parseTransaction8130 (round-trip)
- sender (AA_TX_TYPE) and payer (AA_PAYER_TYPE) signature hashes
- account_changes encoders/decoders: create, config (actor changes with
  scope/expiry/policy), and delegation entries
- 2D nonce, nonce-free mode, and self-pay/sponsored payer modes
- constants, actor/authenticator types, and structural assertions
Adds signTransaction8130 producing sender_auth (EOA raw signature or
ECRECOVER_AUTHENTICATOR || signature for configured actors) and, for
sponsored transactions, payer_auth bound to the resolved sender.
Adds computeAddress8130 (and the DEPLOYMENT_HEADER builder) for create
entries: actors_commitment over sorted [actorId || authenticator], an
effective salt over user_salt, and CREATE2 derivation against
ACCOUNT_CONFIG_ADDRESS. Address constants are placeholders pending the
canonical base/eip-8130 deployment and are overridable per call.
Adds account-configuration support:
- canonical authenticator set constants (k1 native sentinel; p256,
  passkey, delegate placeholders) and actorIdFromAddress
- hashActorChanges8130 (SignedActorChanges ABI digest) and
  signActorChanges8130, returning a ready config account-change entry
- IAccountConfiguration / IAuthenticator / precompile ABIs

Shares the actor-change `data` encoder with the transaction serializer.
Enables routing the same account between 8130 and non-8130 chains:
- is8130Enabled chain registry + predicate (AA_TX_TYPE vs ERC-4337)
- toFactoryArgs8130 / encodeCreateAccountData: ERC-4337 factory args
  via the AccountConfiguration contract (address matches computeAddress8130)
- encodeApplySignedActorChangesData: portable any-chain config-change path
Lets the same EIP-8130 account run on non-8130 chains via a bundlerClient,
backed by the canonical BackwardCompatibleERC4337Account wallet:
- toSmartAccount8130: encodeCalls via executeBatch(Call[]), getFactoryArgs
  via AccountConfiguration.createAccount, getAddress via computeAddress8130,
  and signatures in authenticator||data form for authenticateActor validation
- erc4337AccountAbi for the wallet (executeBatch/validateUserOp/isValidSignature)
- erc1167Bytecode helper for the minimal-proxy deployment code
…alls)

High-level surface over the 8130 primitives:
- to8130Account: create()/change()/delegate()/signTransaction() lifecycle
- key builders (k1/p256/passkey/delegate) + actorIdFromPublicKey
  (keccak256(x||y)) and scope/policy helpers (toScope, authorizeActor,
  revokeActor, encodePolicyData)
- sendCalls8130 / prepareTransaction8130: prepare (fees + nonce), sign,
  serialize and submit an AA_TX_TYPE transaction
Ties payer sponsorship into the 8130 transaction flow:
- createPayerClient: payer_* JSON-RPC (getTerms/sendTransaction/
  signTransaction/getBalance/getSponsorshipOptions/getCapabilities)
- buildSponsoredCalls: phase construction from terms (full sponsorship,
  token payment, required calls) + ERC-20 transfer encoding
- sendSponsoredCalls: getTerms -> build -> prepare -> sender co-sign
  (payer_auth empty) -> payer_sendTransaction / payer_signTransaction
- full ERC-8168 types + error codes; viem/experimental/eip8168 subpath
- add eip8130Deployments registry (AccountConfiguration, account impls,
  authenticator contracts) with getEip8130Deployment(chainId)
- replace placeholder accountConfigAddress / defaultAccountAddress and
  canonical p256/passkey/delegate authenticators with the deployed values
…tion

The deployed AccountConfiguration contract decodes an authorizeActor change's
`data` via abi.decode(data, (ActorConfig, bytes)), but our codec used RLP per the
EIP-8130 text. RLP data reverts at abi.decode, so applySignedActorChanges could
not land on-chain.

Consolidate on a single ABI encoder/decoder (actorChangeData.ts) used by the
config-change digest, the applySignedActorChanges calldata, and the native-wire
serialize/parse paths. Validated end-to-end on Base Sepolia: account creation and
a P-256 session-key authorization both land and read back as expected.

Adds gated (PRIVATE_KEY) integration scripts for the two on-chain flows.
toSmartAccount8130 now signs the raw userOpHash (no EIP-191 prefix) to
match on-chain validateUserOp, with a pluggable `sign` and configurable
`stubData` for non-k1 authenticators. Adds Base Sepolia scripts that drive
ERC-4337 flows, including self-bundled create+execute via EntryPoint.handleOps
(AccountConfiguration as the factory, no staking).
Add encodeSignedActorChangesSignature (+ signedActorChangesMagic): builds a
BackwardCompatibleERC4337Account userOp signature carrying SignedActorChanges[]
(abi.encode(magic, [{ changes, auth }, ...])). Applying the signed change chain
during validateUserOp authorizes the op — no separate op signature. Sets apply
in order, enabling chained rotations. Adds a Base Sepolia script that creates an
account and authorizes a P-256 actor in the validation phase in one self-bundled
userOp.
New Deploy.s.sol + CreateAccounts.s.sol run:
  AccountConfiguration: 0xb0198a714872EE5bfDF829e7986DB5C5899a6b50
  DefaultAccount:        0x124b52d5D57a76ed064c414975beA11Beffe0251
  DefaultHighRateAccount:0x13dD0F222cCF60B7C08a95C2d1FcC85A38DD675D
  ERC4337Account:        0x9748aeA1e1762E50a4d8927777FeDB63A2Ef06C0
  DelegateAuthenticator: 0xE67D299Ff3F0a185398B6C5a28998696969265d7
…ployment

The deployed BackwardCompatibleERC4337Account decodes the signed-actor-changes
signature as (bytes32 magic, SignedActorChanges[] changeSets, bytes opAuth) and
authenticates the op via the trailing opAuth blob over userOpHash. Re-add the
opAuth parameter to encodeSignedActorChangesSignature to match, and pass the
correct AccountConfiguration address through the self-bundle scripts.

- constants: point accountConfigAddress / defaultAccountAddress at the latest
  Base Sepolia deployment
- signedActorChangesSignature: add required opAuth arg + update tests
- scripts: pass accountConfigAddress explicitly; selfBundleRotateP256 signs
  userOpHash as opAuth and uses sequence 1 (createAccount seeds localSequence=1)
…d tx demo

Move the EIP-8130 dev/integration scripts into a dedicated scripts/eip8130/
folder and collapse the vitest include to a single glob so new scripts are
picked up automatically. Add build8130Transaction: an offline demo that builds,
signs, serializes, and parses an EIP-8130 transaction, printing the JSON object,
the RLP envelope, and the decoded 13-field wire layout. The demo highlights that
a call is only { to, data } (no per-call value).
Add optional `value` to `AaCall` and `encodeWalletCalls`, which routes
value-bearing phases through the account's `executeBatch` (pluggable via
`encodeExecute`) while passing value-less calls through as `[to, data]`.
Wire into `sendCalls8130` and reject non-zero `value` at serialization.
- Rename payer_getSponsorshipOptions → payer_getOptions (method + types)
- Rename maxCost → paymentAmount in PayerTokenOption and buildSponsoredCalls
- Fix SponsorshipOption: type 'full_sponsorship' → 'sponsored', tokenSymbol
  → symbol, estimatedCost → estimatedAmount; rename to PayerOption
- Fix error codes: -32600–32608 range → -32001–32009 (JSON-RPC impl-defined
  range; the old codes conflicted with JSON-RPC 2.0 protocol errors)
- Add overhead to PayerGasEstimate; add usdRate + refund to PayerTokenOption
- Add RefundPolicy type; add feeRecipient, callPolicy, refund to
  PayerChainCapabilities; remove non-spec requiredChainId from PayerConditions
- Add payer_fillTransaction method to PayerClient
- Fix expiry semantics: terms.expiry is relative (seconds from now); convert
  to absolute on-chain timestamp in sendSponsoredCalls, clamped to
  conditions.maxExpiry / minExpiry
- Update tests: use relative durations, freeze time for deterministic expiry
  assertions, add payer_getOptions mapping test
Add EIP-8130-aware JSON-RPC actions matching the base node RPC surface:
- getTransactionCount8130: read the 2D channel nonce via the
  eth_getTransactionCount nonce_key extension. The NonceManager precompile
  is not callable via eth_call, so this replaces the reverting readContract
  path in prepareTransaction8130.
- estimateGas8130: eth_estimateGas for AA_TX_TYPE with declared sender/payer
  auth scheme + size and an optional sponsoring payer (MAX_AUTH_SIZE guarded).
- getTransactionReceipt8130 + parseEip8130ReceiptFields/allPhasesSucceeded:
  surface payer, phaseStatuses, and metadata off the receipt (graceful when
  absent on older nodes).

Also brings in the session-policy helpers (policies.ts), signer adapters
(signers.ts), serialization/parse alignment, and the vibenet devnet
deployment with their tests.
Update the ERC-8168 module to the revised spec: offer-based terms with a
top-level gasEstimate, the sponsored/sponsored_declined/token discriminants,
the single -32000 payerRejected error envelope with a string data.code, and
count/asset balance limits. Adds parsePayerError and refreshes the tests.
Reads the local and multichain config-change sequences from the
AccountConfiguration system contract. Always supply the active
chain's deployment address so vibenet and Base Sepolia use the
correct contract rather than a hardcoded constant.
…esses

- getPayerSignatureHash8130: include `payer` field in the RLP body to
  match the Rust node's payer_signature_hash (which encodes all fields
  including the payer slot). Previously the payer field was excluded,
  producing a hash mismatch that caused "actor is not bound" errors.

- deployments: replace stale per-chain address objects with a single
  canonicalEip8130Deployment derived from solc 0.8.33 via Deploy.s.sol.
  Both baseSepoliaDeployment and vibenetDevnetDeployment now reference
  the same canonical set (accountConfiguration 0xC659…, P256 0x3AE1…,
  etc.). The execution client enshrines these addresses; using any other
  accountConfiguration causes "create address mismatch" on account
  creation.
…e 0x7b formatting

- Add getTransaction8130 action: fetches an EIP-8130 tx by hash,
  flattens the nested `tx` body returned by the node, injects the
  request hash (absent from the RPC response), and returns a fully-typed
  Transaction8130 object with all EIP-8130 fields.

- Add waitForTransactionReceipt8130 action: polls until an EIP-8130 tx
  is mined using getTransactionReceipt8130 (which surfaces the
  EIP-8130-specific receipt fields). Skips the replacement-detection
  path since EIP-8130 uses 2D nonces.

- getTransaction: flatten type 0x7b responses inline so the standard
  path also works for EIP-8130 txs (injects hash, maps nested body
  fields to the expected flat shape).

- waitForTransactionReceipt: skip candidates with no hash — can occur
  when a block contains a type 0x7b tx that the generic parser can't
  fully deserialize.

- formatters/transaction: register '0x7b' -> 'eip8130' in transactionType.

- Add vibenet 6-part integration test covering: EOA plain tx, EOA owner
  change, EOA with new key, smart account create, smart account tx, and
  smart account key rotation.
…& 8)

Tests 7 and 8 cover the full P256 lifecycle on vibenet:
  7. P256 smart account — createAccount + ETH send (P-256 signer)
  8. P256 smart account — follow-up send with same P-256 key (no redeploy)

Uses toP256Signer + ox/P256.randomPrivateKey to generate a fresh key each
run and verifies end-to-end: fund → create → send → balance check.
Add accountChanges + calls parameters to EstimateGas8130Parameters.
When present, the request is sent as a complete EIP-8130 tx body so the
node routes through the real executor simulation (required for create
account-change pricing and accurate per-phase call cost).

When omitted, the existing simplified mode is used unchanged (backward
compatible). The serialize helper converts typed AaAccountChange objects
to the plain JSON the node's eth_estimateGas deserialiser expects.
Forward senderAuthScheme/senderAuthSize to the node even when
accountChanges/calls are present, so the node can price intrinsic auth
gas correctly for accounts not yet deployed on-chain (where it can't
infer the scheme from account state).
…t, key.webAuthn

- key.webAuthn: alias for key.passkey to match deployment naming convention
- newSmartAccount8130(params): factory that auto-derives actor from signer type
  (K1/P256/WebAuthn detected from publicKey + authenticator), generates a random
  salt, defaults code to canonical DefaultAccount, and exposes createChange as a
  property for convenient first-tx usage
- toEoa8130Account(signer): wraps a K1 EOA for EIP-8130 txs using the implicit
  self-actor path (raw 65-byte sig, no authenticator prefix, no smart contract)
- Export canonicalEip8130Deployment for downstream consumers
…EOAs

Split To8130AccountParameters into two shapes:
- Smart account (userSalt + code + initialActors): derives CREATE2 address,
  exposes create()
- Delegated EOA (address only): binds to a known address, no salt/code/actors
  needed, create() throws with a clear message directing to delegate(impl)

This fixes the DX smell where EOA delegation required dummy userSalt/code/
initialActors. Now: to8130Account({ signer, address: eoaAddress }) is all
that's needed for the delegated-EOA path; the first tx uses delegate(impl)
in accountChanges and change([...]) to add actors.
…change + sign

Adds `delegate(target)` and `change(actorChanges, opts)` to
`toEoa8130Account` so a bare EOA can build its first-tx delegation and
actor-config changes without needing a second account handle.

`signTransaction` still uses the implicit self-actor path: no `from`
field, raw 65-byte K1 signature, sender recovered via ecrecover.
Resolves the sender (`from`) of an EIP-8130 transaction: returns
`transaction.from` when present, otherwise (EOA path, where the wire
omits `from`) recovers it via ecrecover over the sender signature hash
computed with `from` empty — matching the node.

Lets relayers / payers bind to the resolved sender for EOA-path txs.
Track the new AccountConfiguration deployment and default canonical accounts to ERC-1167 proxies over DefaultAccount, avoiding stale unaudited example implementations.
# Conflicts:
#	src/experimental/eip8130/accounts/to8130Account.ts
#	src/experimental/eip8130/actions/sendCalls.ts
#	src/experimental/eip8130/errors.ts
Detect when a signed expiry has passed while waiting for inclusion and
throw TransactionExpiredError. Thread the resolved expiry via onTransaction
on sendCalls8130 and sendSponsoredCalls so callers do not rely on pending
eth_getTransactionByHash.
The node rule is: an actor must use nonce-free (expiring) mode only if it is
non-admin AND lacks SCOPE_NONCE. Admin actors (scope 0x00) and SCOPE_NONCE
actors may use ordered (sequenced) OR nonce-free nonces.

canUseSequencedNonce / isNoncelessOnly wrongly treated admin (scope 0) as
nonceless-only because bit 0x04 was unset, forcing admin owners into
nonce-free/expiring mode (and throwing NonceScopeError on a sequenced
nonceKey). Fix the predicate so admin is allowed ordered nonces; sends now
default to ordered (expiry-free) for admin owners. Also auto-default the
expiry for any nonce-free send (including admin/SCOPE_NONCE actors opting in)
rather than throwing. Update NonceScopeError text and doc comments, and fix
the nonce integration test mock to answer eth_call (actor-not-bound → declared
scope fallback).
Bring the experimental EIP-8130 module to parity with the finalized
Keystore.sol contracts:

- Update canonical CREATE2 addresses to the per-contract vanity-mined
  0x8130… deployments (Keystore, account impls, authenticators, policies).
- Switch counterfactual address derivation to the leaves-then-list
  `_computeActorsCommitment` scheme with `scope` widened to uint16.
- Replace the transaction `expiry` field with `validAfter`/`validBefore`
  (unix milliseconds) across serialize/parse/hash/assert and consumers.
- Adopt the SignedAccountChanges / AccountChange(changeType, payload)
  model with the full ChangeType enum (authorize/revoke/incrementLocalEpoch
  /lock/unlock), ABI-encoded payloads, channel+sequence, and the reworked
  digest and applySignedAccountChanges calldata.
- Rebuild lock.ts as lockChange()/unlockChange() batch ops.
- Drop redundant `8130` suffixes from module filenames.
- Refresh colocated unit tests, golden vectors, docs, and scripts.
- actorId: right-align address-derived IDs to bytes32(uint256(uint160))
  to match the finalized Keystore (was left-aligned bytes32(bytes20)),
  which unblocks the EVM applySignedAccountChanges path.
- isActor: derive from getActorConfig (authenticator != zero) since the
  contract no longer exposes isActor.
- getPolicy: read via the combined getActor accessor.
- abis: drop removed isActor/getPolicy, add getActor.
- sendCalls: thread validAfter through prepareTransaction.
Promote EIP-8130 (native account abstraction) and ERC-8168 (payer
sponsorship) from `viem/experimental/*` to top-level `viem/eip8130` /
`viem/eip8168` for a production-ready, upstream-submittable surface.

- move src/experimental/eip813{0,8}* -> src/{eip8130,eip8168} and rewrite
  every internal import specifier
- add ./eip8130 + ./eip8168 export and typesVersions maps (drop the
  experimental entries); update knip entry globs
- repoint scripts, the eip8130 vitest config, and site docs/sidebar to the
  new paths
- refresh unit tests for the finalized Keystore reads (getActorConfig /
  getActor), right-aligned actorId, and validBefore (unix ms)
- biome format + organize-imports across both modules; drop dead
  noExplicitAny suppressions

Validation: full build, publint --strict + attw (viem/eip8130, viem/eip8168
all green), and the eip8130/eip8168 unit suite (123 tests) all pass.
- scripts README: drop "experimental/temporary" framing; point at the docs
- buildTransaction demo: print the real 15-field wire layout
  (valid_after/valid_before/metadata) instead of the stale 13-field/expiry list
- receipts guide: use `validBefore` (unix ms), matching waitForTransactionReceipt
  (the tx has no `expiry` field)
- fix stale `viem/experimental` doc-imports -> `viem/eip8130`
A wallet may reach a payer several ways at once — the chain's node RPC, a
block builder integrated with the sequencer (e.g. flashblocks), an app
endpoint, or a wallet-injected payer. Add a discovery surface that queries
them all in parallel and presents one PayerClient.

- createAggregatePayerClient({ payers }): fans `getTerms` out with
  Promise.allSettled (a slow/failing source never blocks the rest), merges
  offers best-first in source order, and routes `payer_sendTransaction` /
  `payer_signTransaction` back to the source that offered the tx's `payer`.
  It is itself a PayerClient, so it drops into sendSponsoredCalls unchanged.
- toChainPayerClient(client): adapt the wallet's execution client into a
  PayerClient when the chain/builder serves `payer_*` natively.
- hasChainPayerService + payerServiceChainIds registry (mirrors is8130Enabled),
  no core Chain type change.
- unit tests (parallel merge, source-order, failure skip, tx.payer routing,
  balance concat) + a "Discovering payers" docs section.
… production typecheck

- newSmartAccount: `proxy: 'erc1167' | 'upgradeable'` (default `upgradeable`,
  which requires an explicit UUPS implementation until one is enshrined) plus
  `admins`/`extraActors` for the initial actor set. Widen the `signer` type so a
  standard K1 `privateKeyToAccount` (SEC1 hex `publicKey`) typechecks — the
  primary documented usage previously did not compile.
- Update canonical EIP-8130 addresses to the regenerated vanity set (Keystore,
  DefaultAccount, high-rate account, authenticators, policy manager + session).
- Docs: document the proxy/admins API and correct the ERC-4337 portable-path
  section (`accounts.erc4337` is intentionally out of scope / not enshrined;
  supply your own deployed implementation).
- Typecheck: exclude the manual, network-gated `scripts/eip8130` demos from
  `check:types` (they run via their own vitest config); fix test type looseness
  (call `to` literals typed as `0x${string}`, PaymentOption token-variant cast).

eip8130 + eip8168 sources and unit tests (137) are type-clean; publint reports
no problems and attw is green for `viem/eip8130` and `viem/eip8168`.
base/eip-8130 wevm#79 skips expired unsequenced (JIT) `AuthorizeActor` grants
instead of reverting and removes the `ExpiredChange` error. That is a
contract-side behavior change with no viem surface — viem never documented the
revert path — so no client logic changes.

The bytecode change regenerated the `0x8130` vanity CREATE2 addresses (verified
via `forge script Deploy.s.sol --sig addresses()`):

- Keystore, DefaultAccount, CanonicalHighRatePayerAccount
- DelegateAuthenticator, PolicyManager, SessionPolicy
- P256 / WebAuthn authenticators unchanged

Updates `deployments.ts` + `constants.ts` to the new set (all valid EIP-55) and
refreshes the `commitmentOf` reference vector (depends on the SessionPolicy
address). eip8130 + eip8168 unit tests (137) pass.
`incrementLocalEpoch` (ChangeType 0x02) was fully plumbed (type, wire byte,
encode/parse/gas) and already accepted by `account.change`, but had no
ergonomic builder like `authorizeActor` / `revokeActor` — callers had to
hand-write `{ changeType: 0x02 }`. Add a named, discoverable, typed builder
plus a serialize<->parse round-trip test, and document it in the owner-rotation
guide as a "revoke all pending" epoch kill-switch.

Reads already expose the epoch via `getConfigSequence` (`localEpoch`); lock /
unlock already ship `lockChange` / `unlockChange` builders in `lock.ts`.
The local config sequence is a packed `localEpoch << 32 | localSequence` word
that can exceed Number.MAX_SAFE_INTEGER, and `account.change` takes a `bigint`.
Drop the lossy `Number(sequence)` in the rotate-owners examples.
Lowers a `wallet_grantPermissions` request (7715 permissions + expiry) onto
EIP-8130 session-key primitives:

- `toSessionPolicyConfig(permissions)`: pure mapping of native/erc20 transfer
  and contract-call permissions (+ token-allowance / rate-limit policies) to a
  `SessionPolicyConfig`. rate-limit `interval` becomes the cap's reset `period`
  (recurring allowance); gas-limit is left to the payer/8168 layer; unbounded
  transfers and custom permissions/policies throw.
- `toSessionPolicy(params)`: binds the lowered config to an account via
  `defineSessionPolicy`, mapping the 7715 `expiry` to `validUntil`.

Exported from viem/eip8130, unit-tested, and documented in session-keys.
Extends the ERC-7715 adapter into a full wallet-side fulfillment that returns
the ready-to-sign authorizeActor change, always POLICY-gated:

- `role: 'session'` (default) → k1 actor, acts via PolicyManager.execute.
- `role: 'pull'` → external-pull sentinel actor (EXTERNAL_POLICY_AUTHENTICATOR),
  draws via PolicyManager.executeFor from its own address.

A single ERC-7715 `expiry` drives both the actor authorization and the policy
binding's validUntil so they can't drift.

Supporting additions:
- `externalPolicyAuthenticator` constant (keccak256("externalPolicyCaller")).
- `key.externalPull(address)` actor builder.
- `SessionPolicy.executeForCall` (PolicyManager.executeFor encoder).

Exported from viem/eip8130, unit-tested (session + pull), and documented.
fulfillGrantPermissions is now an action (client, parameters): it reads the
account and, if the policy manager isn't yet a trusted-executor actor, folds
that one-time registration into the returned `changes` batch (managerChange
first, then the grantee change). One `account.change(changes)` now both
provisions the manager and authorizes the grantee.

- adds `managerChange` + `changes` to the return; `assumeManagerRegistered`
  option skips the on-chain read.
- tests cover registered / unregistered / skip paths for session + pull.
…account

Fulfills a wallet_addSubAccount (type: 'create') request as a distinct 8130
smart account (own address, asset isolation) whose initial owner set is the
requested keys plus key.delegate(parent). The parent-control link is installed
at creation (in createChange) — no separate change tx — and the returned handle
signs for the sub-account with the parent's signer via the delegate
authenticator.

- maps ERC-7895 key types → owner actors (address→k1, p256/webcrypto→p256,
  webauthn→webAuthn); rejects duplicate/colliding actor ids.
- returns the account handle + createChange + parentActor + ERC-7895 response.
- unit-tested and documented in sub-accounts.
…nt keys

- eip8130Capabilities / eip8130CapabilitiesByChain: wallet-side descriptor for
  wallet_getCapabilities advertising exactly what the adapters support — atomic
  batches, ERC-7715 grants (permissions/policy/signer types), and ERC-7895
  sub-accounts (key types); optional paymasterService (ERC-8168). custom and
  gas-limit are deliberately not advertised (can't be lowered to a SessionPolicy).
- fulfillAddSubAccount: add keyScope / keyPolicy so requested keys can be
  registered as scoped (optionally policy-gated) session-key actors instead of
  full co-owners, keeping the parent the sole unrestricted owner.

Both exported from viem/eip8130, unit-tested, and documented.
…mption)

Adds the ERC-7715 redemption path so a granted key's calls can be routed
through the policy manager with no wallet-side storage:

- toPermissionsContext / parsePermissionsContext: encode a grant into an opaque,
  self-describing context (account, role, actor, full policy binding) and decode
  it back into a rebound SessionPolicy (recomputes the same commitment).
- routePermissionedCalls({ context, calls }): wraps each action as
  session.executeCall (session key, dispatched as the account) or
  executeForCall (external pull actor).
- fulfillGrantPermissions now also returns `permissionsContext`.

Exported from viem/eip8130, round-trip + routing tests, and documented.
…-closed

Update canonical SessionPolicy to 0x813070914C530d030f4Efd8Fa99C18e836435e55
(re-mined after base/eip-8130#80; PolicyManager unchanged). Document that
native ETH is fail-closed: a session key can only move value with a
zero-address tokenLimit; absent means no ETH, not unlimited.
…ction

# Conflicts:
#	package.json
#	scripts/tsconfig.json
@vercel

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

@chunter-cb is attempting to deploy a commit to the Wevm Team on Vercel.

A member of the Team first needs to authorize it.

@changeset-bot

changeset-bot Bot commented Aug 13, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 4282bb2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

soheimam added a commit to base/skills that referenced this pull request Aug 13, 2026
The 8130 modules aren't on npm yet (upstream PR wevm/viem#5004 is still a
draft), so they must be built from the chunter-cb/viem feat/eip-8130-production
fork. Doing the clone→pnpm build→npm --install-links dance by hand is
error-prone, so bundle it as scripts/setup-viem-8130.sh (idempotent, branch
overridable via env). Rewire SKILL.md to lead with the script and keep the
manual steps in a collapsible "what it does and why" section. Script header
notes the whole thing collapses to `npm install viem@latest` once the PR ships.

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
Revert incidental `on-chain` -> `onchain` wording changes in tempo and
circle-usdc pages/tests that were unrelated to the EIP-8130 work, so the
upstream PR stays scoped to the new eip8130/eip8168 modules.
Remove `scripts/eip8130/**`, `scripts/smoke-estimate-sender-actor.mjs`, and
the dedicated `test/vitest.eip8130.config.ts`, and revert `scripts/tsconfig.json`
to upstream. These were deep-relative-import, credential/testnet-gated dev
harnesses excluded from CI and `check:types`, with no upstream precedent under
`scripts/`. CI-covered behavior remains fully tested by the colocated
`src/eip8130/**/*.test.ts` suites. Harnesses remain recoverable in git history.
soheimam added a commit to base/skills that referenced this pull request Aug 14, 2026
…haviour (#149)

* updated vibenet skill

* vibenet: correct endpoints, add account lifecycle, fix verified-wrong guidance

Verified live against the devnet by building a Next.js app end to end.

Endpoints: API host moved vibes.base.org -> api.vibes.base.org (the bare host
302s to an HTML page, which viem reports as "Unrecognized token '<'"). Add
faucet/status, chain-health and explorer. rpc.vibes.base.org is CORS-enabled,
so the browser proxy is optional rather than required.

Lifecycle: new section covering counterfactual -> deployed. There is no deploy
step; the first transaction creates the account. Sponsored is the shortest path
(no faucet). Read deployment from eth_getCode, never optimistic local state.

Corrections, all reproduced live:
- sendSponsoredCalls resolves with { transactionHash }; the previous text said
  the opposite and the declared return type is wrong.
- A bad config sequence is rejected at broadcast, not silently applied. The
  real trap is the inverse: a change can apply on a tx reporting status 0x0.
- The end-to-end example imported core viem helpers from the 8130 module,
  which does not export them, so it could not compile.
- npm file: installs need --install-links or Turbopack cannot resolve viem.

New gotchas: payer validation lags ~1 block after deploy ("actor is not
bound"); "no backend is currently healthy" means the devnet is halted; a
value-bearing call to a never-funded address reverts.

* docs: frame vibenet as Base Vibes in README, add example usage

Adds vibenet prompt examples alongside the existing ones, plus a worked
gasless-onboarding snippet showing the counterfactual-to-deployed flow with an
ERC-8168 payer. The snippet was compiled and run live against the devnet
(deployed at a zero balance, status 0x1).

* updated viem package

* vibenet: add setup-viem-8130.sh installer, lead install with it

The 8130 modules aren't on npm yet (upstream PR wevm/viem#5004 is still a
draft), so they must be built from the chunter-cb/viem feat/eip-8130-production
fork. Doing the clone→pnpm build→npm --install-links dance by hand is
error-prone, so bundle it as scripts/setup-viem-8130.sh (idempotent, branch
overridable via env). Rewire SKILL.md to lead with the script and keep the
manual steps in a collapsible "what it does and why" section. Script header
notes the whole thing collapses to `npm install viem@latest` once the PR ships.

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

* updated readme removed examples

---------

Co-authored-by: Claude <noreply@anthropic.com>
…cal changes

- getConfigSequence now defaults `accountConfiguration` to the canonical
  (enshrined) keystore, matching every sibling read (getLockStatus, isLocked,
  getActorConfig, getPolicy, isActor). There is a single keystore, so callers no
  longer pass it.
- Add first-class support for unsequenced (JIT) local changes: the
  `unsequencedLocalHalf` sentinel (type(uint32).max, mirrors Keystore.UNSEQUENCED)
  and `unsequencedLocalSequence(localEpoch)` helper that packs
  `localEpoch << 32 | UNSEQUENCED` for signing epoch-bound, sequence-less local
  changes. Exported from viem/eip8130 with a unit test.
- Docs: drop the now-redundant keystore address from getConfigSequence examples.
… overrides)

The keystore (AccountConfiguration) is enshrined in the execution client and
identical on every chain — using any other address derives a different account
address and fails the create tx. So it is not configurable.

- Rename the `accountConfigAddress` constant to `keystoreAddress` and document it
  as the fixed, enshrined keystore.
- Drop the `accountConfiguration` / `accountConfigAddress` override params from
  every read (getLockStatus, isLocked, getActorConfig, getPolicy, isActor,
  getConfigSequence), the permissions helper, the account builders
  (toAccount/newSmartAccount/toSmartAccount/subAccounts), and the encoders
  (computeAddress, toFactoryArgs). All now reference `keystoreAddress` directly.
- Remove the `accountConfigAddress` property from the account object; sendCalls
  reads the keystore constant instead.
- Remove `accountConfiguration` from the `Eip8130Deployment` record (it is the
  fixed `keystoreAddress`, not a per-chain address).
- Update tests + docs; drop the now-impossible override cases.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant