EIP.tools

EIP.tools

⚠️ DraftStandards Track: Core

EIP-8398: Native Account Abstraction Transactions

Add a native transaction for portable Keystore accounts

Authors
Created2026-08-27
Discussion Linkhttps://ethereum-magicians.org/t/composable-native-account-abstraction/29526
Requires
Referenced by

Markdown

https://raw.githubusercontent.com/ethereum/EIPs/8b...
Pull Request#12248PR closed (unmerged)

EIP-GPT summary

Contents
AbstractMotivationSpecificationOverviewAccount TypesAdoption ProfilesAuthenticatorsCanonical Authenticator SetAA Actor ScopeKeyed Transaction NoncesAA Transaction TypeField DefinitionsIntrinsic GasSignature FormatSignature PayloadPayer ModesTransaction MetadataAccount ChangesCreate EntryConfig Change EntryDelegation EntryExecution of Account ChangesExecutionCall ExecutionTransaction ContextPortabilityValidation FlowMempool AcceptanceBlock ExecutionRPC ExtensionsConstantsRationaleWhy a Native Transaction?Why Keyed Sequential Nonces?Why Reuse EIP-8401 Account Changes?Why Reserve Extension Values?Why No Value in Calls?Backwards CompatibilityReference ImplementationITransactionContext (Precompile)INonceManager (Precompile)Security ConsiderationsCopyright

Abstract

This proposal introduces an EIP-2718 transaction type for accounts using the portable Keystore defined by EIP-8401. It provides native custom authentication, call batching, gas sponsorship, account-change transport, and keyed sequential transaction nonces without requiring nodes to execute arbitrary wallet validation code.

Motivation

EIP-8401 separates account authority from transaction transport. Its actors, authenticators, deterministic accounts, and signed configuration changes work through ordinary EVM calls, including ERC-4337 deployments.

Chains can additionally provide a predictable native path. Each transaction declares its authenticator, allowing nodes to identify the validation work before execution. A bounded canonical authenticator set keeps validation enumerable, while the native envelope supplies batching, sponsorship, and parallel sequential nonce channels.

Advanced controls such as policy-gated actors, account locking, and nonce-free transactions are left to dependent extensions.

Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.

Overview

A transaction authenticates its sender and optional payer against the EIP-8401 Keystore. The protocol invokes the declared authenticator, resolves the returned actor configuration, applies this proposal's scope rules, consumes a keyed sequential nonce, and executes the declared calls.

The Keystore remains usable without this transaction type. Chains that do not implement this proposal use an EVM transport such as ERC-4337.

This proposal owns the transaction envelope, transaction-specific scope assignments, native validation and execution, keyed nonces, gas accounting, account-change transport, transaction context, receipts, and RPC behavior. EIP-8401 owns the persistent actor and configuration model.

Account Types

Account typeNative behavior
EOAA raw secp256k1 signature resolves the sender; an account with no code is auto-delegated to DEFAULT_ACCOUNT_ADDRESS
Existing smart accountThe account first imports or configures actors through EIP-8401
New smart accountA create entry invokes the EIP-8401 deterministic creation semantics before call execution

Adoption Profiles

The AA transaction is specified once but adopted under one of two profiles. A chain MUST declare which profile it activates; the profile fixes the parameters this specification otherwise leaves open: the intrinsic-gas schedule, how the enshrined canonical authenticator set evolves, and whether the AA transaction path accepts authenticators outside that set. The canonical set is accepted on the transaction path under both profiles, always at a constant enshrined validation cost, so a wallet targeting the canonical set behaves identically everywhere; the profiles differ only in what else is allowed and how it changes, never in transaction semantics.

L1 profile (normative schedule, permissive acceptance). For a base layer, where the gas schedule is the specification and consensus validity MUST NOT depend on any out-of-band configuration. Under this profile:

  • The intrinsic-gas schedule is normative. The component values in this document are protocol constants, identical for every node, and MAY change only through a hard fork. A node MUST NOT adopt a different schedule.
  • Canonical authenticators are enshrined on the validation path with a constant validation cost (their fixed sender_auth_cost / payer_auth_cost component values); no EVM runs to authenticate a canonical actor, and the enshrined set changes only through a hard fork.
  • Authenticator acceptance is permissive: the AA transaction path accepts any authenticator whose STATICCALL returns within MAX_AUTHENTICATION_GAS, canonical or not (see Authenticators). No allowlist is maintained; a base layer can afford this bounded, single-call validation.

Under the L1 profile, two independent implementations that agree on this document produce identical block validity.

L2 profile (configurable schedule, canonical-only path). For an L2 or any high-throughput EVM that wants to track a local cost model, move faster than L1 governance, and keep the transaction path to fixed-cost validation. Under this profile a chain MAY:

  • adopt a different intrinsic-gas schedule (for example to mirror a future EVM repricing), provided it is fixed under that chain's consensus and identical across its nodes;
  • extend the enshrined constant-cost canonical set it accepts through the companion ERC process and node configuration.

Authenticator acceptance is canonical-only: the AA transaction path accepts only the enshrined canonical set (fixed validation cost, no arbitrary STATICCALL on the hot path). Non-canonical authenticators remain registrable and usable through the EVM (config-change calls, recovery flows, ERC-4337), just not on the AA transaction path. An L2-profile chain is still internally consistent; wallets that need cross-chain portability SHOULD rely only on the canonical set and the recommended schedule.

The gas-schedule policy and the acceptance policy are independent choices; the two profiles name the common pairings, and a chain MAY combine them differently. What is universal is the canonical set: every compliant chain accepts it on the AA transaction path at a constant enshrined cost, so a wallet can rely on canonical authenticators working everywhere. Additional authenticators may be registered on any chain (for example, recovery flows), but are usable on the AA transaction path only where a chain's policy is permissive; otherwise they are reached through the EVM.

Throughout this specification, "recommended" values and schedules mean required under the L1 profile and default-but-overridable under the L2 profile; the normative structure (the gas-formula components, the authenticator interface, the canonical set) holds under both.

Authenticators

The authenticator interface, actor binding, expiry, implicit address-bound actor, and persistent configuration are defined by EIP-8401.

Canonical Authenticator Set

Canonical authenticators are enshrined on the validation path with constant validation costs. Their native execution MUST produce the same result as their EIP-8401 authenticator contracts.

Whether an authenticator outside the canonical set is accepted on the transaction path is the chain's acceptance policy:

  • A permissive chain authenticates it with one STATICCALL bounded by MAX_AUTHENTICATION_GAS.
  • A canonical-only chain rejects it on the native transaction path. The actor remains usable through an EVM transport.

The canonical set and deterministic contract addresses are catalogued in a companion ERC. Every compliant chain MUST accept the canonical set.

AA Actor Scope

EIP-8401 stores a uint16 scope word and defines scope == 0x0000 as admin. This proposal assigns:

BitValueNameAuthority
00x01SENDERMay originate a native AA transaction
10x02ReservedCompanion extension
20x04NONCEMay consume a sequential transaction nonce
30x08SELF_PAYERMay pay the account's own gas
40x10SPONSOR_PAYERMay pay gas for another account
5–15ReservedFuture pure grants

For sender authentication, require admin or SENDER. A restricted sender using a sequential transaction nonce additionally requires NONCE. For self-payment, require admin or SELF_PAYER. For sponsorship, require admin or SPONSOR_PAYER.

Unknown and reserved bits grant no transaction authority.

Keyed Transaction Nonces

Nonce state is managed by the precompile at NONCE_MANAGER_ADDRESS. The transaction's nonce_key selects an independent sequential channel, and nonce_sequence is the expected sequence.

nonce_keyMeaning
0Standard sequential channel
1 through NONCE_KEY_MAX - 1Wallet-defined parallel sequential channels
NONCE_KEY_MAXReserved for a companion extension

For every key defined by this proposal, nonce_sequence MUST equal the current sequence for (sender, nonce_key). Inclusion increments that sequence regardless of call success. A restricted actor requires NONCE; an admin may use any sequential key.

Mempool replacement candidates share (sender, nonce_key, nonce_sequence).

AA Transaction Type

A new EIP-2718 transaction with type AA_TX_TYPE:

AA_TX_TYPE || rlp([
  chain_id,
  sender,             // Sender address (20 bytes) | empty for EOA signature
  nonce_key,          // uint256: nonce channel selector
  nonce_sequence,     // uint64: sequence number
  valid_after,        // uint64: Unix timestamp (ms or s, auto-detected); 0 = no lower bound
  valid_before,       // uint64: Unix timestamp (ms or s, auto-detected); 0 = no expiry
  max_priority_fee_per_gas,
  max_fee_per_gas,
  gas_limit,
  account_changes,    // Account creation, config change, and/or delegation operations | empty
  calls,              // [[call, ...], ...] where call = rlp([to, data]) | empty
  metadata,           // opaque attribution/annotation bytes | empty
  payer,              // empty = sender-paid, payer_address = specific payer
  sender_auth,        // raw ECDSA r||s||v (EOA, sender empty) | authenticator || data (configured actor)
  payer_auth          // empty = self-pay | authenticator || data = sponsored (same format as sender_auth)
])

call = rlp([to, data])   // to: address, data: bytes

Field Definitions

FieldDescription
chain_idChain ID per EIP-155
senderSending account address. Required (non-empty) for configured actor signatures. Empty for EOA signatures; address recovered via ecrecover. The presence or absence of sender is the sole distinguisher between EOA and configured actor signatures.
nonce_keyuint256 nonce channel selector. 0 is the standard sequential channel, 1 through NONCE_KEY_MAX - 1 are wallet-defined parallel sequential channels, and NONCE_KEY_MAX is reserved for an extension.
nonce_sequenceuint64 expected sequence within nonce_key. Must match the current sequence for (sender, nonce_key) and is incremented after inclusion regardless of execution outcome.
valid_afteruint64 Unix timestamp, milliseconds or seconds (auto-detected per Timestamp Normalization); encoded as a canonical minimal-length RLP integer. After normalization to milliseconds, transaction invalid when block.timestamp * 1000 < valid_after. A value of 0 means no lower bound. Note: many mempools will not hold a not-yet-active transaction and MAY reject one whose valid_after is in the future, so it is best used for near-term activation rather than long-dated scheduling.
valid_beforeuint64 Unix timestamp, milliseconds or seconds (auto-detected per Timestamp Normalization); encoded as a canonical minimal-length RLP integer. After normalization to milliseconds, transaction invalid when block.timestamp * 1000 > valid_before. A value of 0 means no expiry.
max_priority_fee_per_gasPriority fee per gas unit (EIP-1559)
max_fee_per_gasMaximum fee per gas unit (EIP-1559)
gas_limitMaximum gas budget for sender-intrinsic gas (intrinsic gas excluding payer authentication) and call execution (see Intrinsic Gas). Payer authentication is metered separately and does not draw from gas_limit
account_changesEmpty: No account changes. Non-empty: Array of typed entries: create (type 0x00) for account deployment, config change (type 0x01) for actor management, and delegation (type 0x02) for code delegation. See Account Changes
callsEmpty: No calls. Non-empty: Array of call phases; see Call Execution
metadataEmpty: No metadata. Non-empty: Opaque attribution or annotation bytes. See Transaction Metadata
payerGas payer identity. Empty: Sender pays. 20-byte address: This specific payer required. See Payer Modes
sender_authSee Signature Format
payer_authPayer authorization. Empty: self-pay. Non-empty: authenticator || data, same format as sender_auth. See Payer Modes

Millisecond window. The transaction window (valid_after/valid_before) is denominated in milliseconds, while on-chain actor expiries are seconds; this split is deliberate and there is no second-denominated variant of these fields. Because block.timestamp has second granularity, the effective activation/expiry resolution equals the chain's block-timestamp resolution (1 second on most chains today); the millisecond field exists for off-chain clock compatibility (e.g. Date.now(), sequencer clocks) and forward-compatibility with sub-second block times, not for finer resolution than the chain itself provides. Can easily be scaled on ingress.

Timestamp Normalization. valid_after and valid_before MAY be expressed in either seconds or milliseconds; a node accepts both at the same time and decides the unit per value, not per node or per chain. There is no mode flag: the unit is a fixed function of the value against the threshold TIMESTAMP_MS_THRESHOLD = 100_000_000_000 (10^11), applied independently to each field:

  • a value below TIMESTAMP_MS_THRESHOLD is always interpreted as seconds and normalized to milliseconds as value * 1000;
  • a value at or above TIMESTAMP_MS_THRESHOLD is always interpreted as milliseconds and used as-is;
  • a 0 value means "no bound" and is not normalized.

This is unambiguous for every realistic value: a 64-bit Unix timestamp in seconds does not reach 10^11 until the year 5138, while any Unix timestamp in milliseconds has exceeded 10^11 since 1973. Because the rule is a pure function of the value, every node derives the same normalized timestamp, so this is consensus-safe (no node-local configuration affects block validity). All validity-window comparisons in this specification operate on the normalized millisecond value; wherever valid_after or valid_before appears in a comparison below, it denotes the normalized value.

Intrinsic Gas

Intrinsic gas follows the standard Ethereum meaning: the total cost to include the transaction. As in standard transactions it accounts for signature authentication, here both the sender's authentication (sender_auth_cost) and the payer's (payer_auth_cost) are included.

Full-EVM metering model (normative). The reference cost of an AA transaction is what its work costs when metered as ordinary EVM execution, over four phases:

  1. Account changes. Apply account_changes in order: a create entry calls createAccount and config-change entries call applySignedAccountChanges (both metered as ordinary Keystore contract calls), and a delegation entry performs a standard EIP-7702 delegation, metered at the standard delegation cost.
  2. Validation. Authenticate the transaction: STATICCALL the sender_auth authenticator, and (when sponsored) the payer_auth authenticator, each followed by the single actor_config SLOAD that resolves and authorizes the actor. Metered as ordinary STATICCALL execution.
  3. Execution. Dispatch the transaction's calls, metered as ordinary EVM execution.
  4. Bound. The transaction's total metered gas is bounded by effective_gas_limit (defined below), which is the gas limit for the transaction as a whole.

This model is the normative reference for results: whatever pricing a chain uses, a transaction MUST be accepted, authenticated, and executed to the same outcome and state changes that phases 1–3 produce under ordinary EVM. A chain MAY implement phases 1 and 2 (account-change application and authentication) as native code where applicable (for the enshrined canonical authenticator set and the Keystore reads it depends on), running no EVM for that work and instead charging a fixed gas cost per authenticator and per account-change operation (see Portability). The native path MUST match the Full-EVM model in results, but it need not match it in gas: its fixed schedule is its own consensus-fixed pricing, not a re-derivation of what EVM metering would charge. Whether phases 1–2 are metered pure-EVM or priced by a native fixed schedule is decided per chain (see Adoption Profiles); phase 3 is always ordinary EVM execution.

The per-component values below are that native fixed schedule for phases 1–2. They approximate the EVM access and data-availability costs (EIP-2929 / EIP-2028) at the time of writing, but a native chain charges them as flat costs rather than metering the underlying EVM. Their normative status follows the chain's adoption profile:

  • L1 profile: the values below are protocol constants (the gas schedule is part of the specification), identical for every node and changeable only through a hard fork. A chain MUST NOT adopt a different intrinsic-gas schedule under this profile.
  • L2 profile: the values below are a recommended default. A chain MAY reprice them (for example to track a future EVM repricing or a local cost model), provided the schedule is fixed under that chain's consensus and identical across its nodes.

The formula's structure (its components and what each one accounts for) is normative under both profiles; only the absolute numbers may vary, and only under the L2 profile.

intrinsic_gas = AA_BASE_COST + tx_payload_cost + nonce_cost + bytecode_cost + account_changes_cost + auto_delegation_cost + sender_auth_cost + payer_auth_cost

Sender-intrinsic gas is intrinsic gas excluding payer authentication and is bounded by gas_limit. For self-pay transactions payer_auth_cost is 0, so sender-intrinsic gas equals intrinsic gas:

sender_intrinsic_gas = intrinsic_gas - payer_auth_cost
                     = AA_BASE_COST + tx_payload_cost + nonce_cost + bytecode_cost + account_changes_cost + auto_delegation_cost + sender_auth_cost

Intrinsic gas is charged before calls run. Since payer_auth_cost is metered outside gas_limit, the gas available to calls is:

execution_gas_available = gas_limit - sender_intrinsic_gas

payer_auth_cost is metered separately and charged to the payer on top of gas_limit; it does not draw from gas_limit and cannot reduce the gas available to calls. This isolation is required: payer_auth is excluded from both the sender and payer signature hashes (see Signature Payload) and is selected unilaterally by the payer. If it consumed gas_limit, a payer could choose an expensive authenticator to starve calls and alter execution behavior. Keeping it separate makes the gas available to calls a function of sender-signed fields alone. For the same reason the data-availability cost of the serialized payer_auth bytes is charged in payer_auth_cost rather than tx_payload_cost: those bytes are chosen by the payer and excluded from the signed hashes, so their cost is metered outside gas_limit and never reduces the sender's execution budget. The payer reimburses payer_auth_cost regardless (the payer pays all gas).

Because payer_auth_cost sits outside gas_limit, total gasUsed can exceed gas_limit on a sponsored transaction. The overrun is bounded by an effective gas limit:

effective_gas_limit = gas_limit + MAX_AUTHENTICATION_GAS

MAX_AUTHENTICATION_GAS is the ceiling on a single authenticator's execution: an authenticator outside the canonical set is STATICCALLed with at most this much gas, and it also bounds how much the payer's authenticator (metered outside gas_limit) may add on top of the sender-signed gas_limit. Its suggested value is 100,000 gas. A transaction whose resolved payer_auth_cost exceeds MAX_AUTHENTICATION_GAS is rejected at Mempool Acceptance rather than metered, so gasUsed <= effective_gas_limit always holds and block-gas accounting stays bounded. For self-pay transactions payer_auth_cost is 0 and effective_gas_limit == gas_limit. Under the L1 profile MAX_AUTHENTICATION_GAS is fixed under consensus; under the L2 profile a chain MAY choose its own value (see Adoption Profiles).

sender_auth_cost, by contrast, is included in gas_limit: the sender chooses its own authenticator, and both sender and payer sign over gas_limit, so both commit to the full sender-side budget before inclusion.

sender_auth_cost: authenticator execution + 1 SLOAD (actor_config).

payer_auth_cost: 0 for self-pay (payer empty). Otherwise, the same sender_auth_cost model applies to the payer's authenticator (authenticator execution + 1 actor_config SLOAD), plus the data-availability cost of the serialized payer_auth field itself (16 gas per non-zero byte, 4 per zero byte, per EIP-2028). These payer_auth bytes are excluded from tx_payload_cost and metered here instead, so the entire payer-authentication cost, execution and bytes alike, sits outside gas_limit and is paid by the payer.

ComponentValue
AA_BASE_COST15,000 gas: the fixed per-transaction overhead of the AA path, analogous to the standard 21,000 base cost but excluding the components broken out separately below (signature authentication is sender_auth_cost/payer_auth_cost; calldata is tx_payload_cost). It covers transaction decoding, sender/actor resolution and the actor_config access accounting common to every AA transaction, nonce dispatch, and receipt assembly. Like the other values it is a protocol constant under the L1 profile that only an L2-profile chain MAY reprice (see Adoption Profiles)
tx_payload_costStandard per-byte cost over the RLP-serialized transaction excluding the payer_auth field: 16 gas per non-zero byte, 4 gas per zero byte, consistent with EIP-2028. Ensures all sender-signed fields (account_changes, sender_auth, calls, metadata, etc.) are charged for data availability. The payer_auth bytes are chosen unilaterally by the payer and excluded from the signed hashes, so their byte cost is metered in payer_auth_cost (outside gas_limit) rather than here
nonce_cost22,100 gas for first use of a nonce_key (cold SLOAD + SSTORE set), otherwise 5,000 gas (cold SLOAD + warm SSTORE reset)
bytecode_cost0 if no create entry in account_changes. Otherwise: 32,000 (deployment base) + code deposit cost (200 gas per deployed byte). Byte costs for code are covered by tx_payload_cost; the create entry's initial-actor slot writes are covered by account_changes_cost
account_changes_costPer applied create entry: one actor_config slot write per initial actor (22,100 gas each: cold SLOAD + SSTORE set). Per applied config change entry: authentication cost plus the storage writes for each mutated slot. Per applied delegation entry: 4,600 gas (indicator deposit, 200 × 23 bytes). Per skipped config-change entry: 2,100 gas (sequence SLOAD). 0 when there is no create, config-change, or delegation entry.
auto_delegation_costDelegation indicator deposit: 4,600 gas (200 × 23 bytes for the 0xef0100 || address indicator) when a code-less sender is auto-delegated to DEFAULT_ACCOUNT_ADDRESS (Block Execution step 4). 0 otherwise.

Signature Format

Signature format is determined by the sender field:

EOA signature (sender empty): Raw 65-byte ECDSA signature (r || s || v). The sender address is recovered via ecrecover.

Configured actor signature (sender set):

authenticator (20 bytes) || data

The first 20 bytes identify the authenticator address. When the authenticator is K1_AUTHENTICATOR, data is raw ECDSA (r || s || v) and the protocol handles ecrecover natively. For all other authenticators, data is authenticator-specific; each authenticator defines its own wire format.

Validation
  1. Resolve sender: If sender empty, ecrecover derives the sender address (EOA path) with actorId = bytes32(uint256(uint160(sender))). If sender set, read the first 20 bytes of sender_auth as the authenticator address.
  2. Authenticate: Route by authenticator address. For the EOA path (sender empty), ecrecover was already performed in step 1. For K1_AUTHENTICATOR (address(1)), the protocol natively ecrecovers from data (as r || s || v), returning actorId = bytes32(uint256(uint160(recovered_address))). For all other authenticators, call authenticator.authenticate(hash, data) via STATICCALL, returning actorId (or bytes32(0) for invalid). address(0) is never a valid authenticator selector (it is the empty actor_config sentinel).
  3. Authorize: Self-actor (native secp256k1) rule: if authentication in step 2 used the native secp256k1 path (the EOA path or K1_AUTHENTICATOR) and actorId == bytes32(uint256(uint160(sender))), resolve the self-actor from the inline default-EOA config in the packed account-state slot: reject if DEFAULT_EOA_REVOKED is set; otherwise take scope and expiry from the inline fields (all-zero = unrestricted, non-expiring full owner, i.e. admin). Otherwise SLOAD actor_config(sender, actorId), reject if its reserved bytes are nonzero (version gate, see EIP-8401 storage), and require that the stored authenticator address matches the effective authenticator (this covers every other actor, including a non-secp256k1 self). In either case, if the resolved expiry is non-zero, also require block.timestamp <= expiry; an expired actor is rejected.
  4. Check scope: Read the resolved scope byte (from the inline default-EOA config for the secp256k1 self-actor, or actor_config otherwise) and check it against the context being authorized per AA Actor Scope. For sender_auth, require admin or SENDER. Payer scope is checked when the payer is resolved (see Validation Flow step 6).
  5. Check nonce scope: For sender-context transactions, require the resolved actor be admin or carry NONCE.

Signature Payload

Sender and payer use different type bytes for domain separation, preventing signature reuse attacks:

Sender signature hash, all tx fields through payer, excluding sender_auth and payer_auth:

keccak256(AA_TX_TYPE || rlp([
  chain_id, sender, nonce_key, nonce_sequence, valid_after, valid_before,
  max_priority_fee_per_gas, max_fee_per_gas, gas_limit,
  account_changes, calls, metadata,
  payer
]))

Payer signature hash, all tx fields through payer, excluding sender_auth and payer_auth:

keccak256(AA_PAYER_TYPE || rlp([
  chain_id, sender, nonce_key, nonce_sequence, valid_after, valid_before,
  max_priority_fee_per_gas, max_fee_per_gas, gas_limit,
  account_changes, calls, metadata,
  payer
]))

The sender field in the payer signature hash MUST be the resolved sender address. In the EOA path (sender empty in the transaction wire format), the recovered sender address (from sender_auth ecrecover, see Validation step 1) MUST be substituted into the sender position before computing this hash; it MUST NOT be encoded as the empty wire-format value. This binds the payer's signature to the specific resolved sender and prevents cross-sender replay of payer signatures (see Payer Security). The payer field MUST also be included so the payer's signature is bound to the account being charged; without it, a payer authorization could be redirected to any other account that authorizes the same actor with SPONSOR_PAYER scope (e.g., via the EIP-8401 delegate authenticator).

Payer Modes

Gas payment and sponsorship are controlled by two independent fields:

payer, the sender's commitment regarding the gas payer, included in the sender's signed hash:

ValueModeDescription
emptySelf-paySender pays their own gas
payer_address (20 bytes)SponsoredSender binds tx to a specific sponsor

payer_auth uses the same authenticator || data format as sender_auth:

payerpayer_authPayer AddressValidation
emptyemptysenderSelf-pay: no separate payer_auth; the resolved sender actor MUST be admin or carry SELF_PAYER
sender addressauthenticator (20) || datasenderSelf-pay via a dedicated gas key: payer account == sender. The resolved payer actor MUST be admin or carry SELF_PAYER
other addressauthenticator (20) || datapayer fieldSponsored: the resolved payer actor MUST be admin or carry SPONSOR_PAYER

Transaction Metadata

The metadata field is optional opaque bytes for attaching attribution or annotation data to a transaction, for example builder/app attribution, a payment reference or memo, or a commitment to off-chain data. Legacy transactions carry such data as a "data suffix" appended to tx.input; because calls replace the single input blob, metadata provides the equivalent home.

metadata is part of the signed transaction (covered by both the sender and payer signature payloads) and is charged per byte through tx_payload_cost like any other transaction bytes. It does not affect validation or execution. Off-chain consumers (indexers, explorers) read it directly from the transaction. Producers MAY use any encoding.

Account Changes

The account_changes field transports EIP-8401 operations and native code delegation before calls execute. Entries are applied in order and are atomic per phase.

TypeNameMeaning
0x00CreateCreate the sender through EIP-8401
0x01Config changeApply an EIP-8401 signed configuration batch
0x02DelegationSet EIP-7702 delegation for the sender

Nodes SHOULD enforce a configurable mempool limit on configuration entries.

Create Entry

rlp([
  0x00,
  user_salt,
  code,
  initial_actors
])

Each initial actor is [actorId, authenticator, scope]. The actor set, account address, deployment header, and storage initialization MUST follow EIP-8401.

Validation requires:

  1. A canonical, strictly sorted EIP-8401 initial actor set.
  2. Runtime code does not exceed the EIP-170 maximum.
  3. The derived address equals the resolved transaction sender.
  4. The destination is fresh: zero code size and zero account nonce.
  5. sender_auth resolves to an initial actor bound to the declared authenticator.

The protocol places the supplied runtime code directly and initializes the EIP-8401 state. Its result MUST match the EIP-8401 createAccount EVM path.

Config Change Entry

rlp([
  0x01,
  channel,
  sequence,
  changes,
  auth
])

change = rlp([
  change_type,
  payload
])

This is the transaction encoding of EIP-8401 SignedAccountChanges. The operation payload is ABI-encoded as required by EIP-8401. Native application MUST match applySignedAccountChanges, including authorization, atomicity, expiry handling, storage, sequence consumption, events, and reverts.

The batch may use the EIP-8401 Local or Multichain channel. Its admin authorization is independent from sender_auth.

Delegation Entry

Delegation entries set an EIP-7702-style delegation indicator:

rlp([
  0x02,
  target
])

The sender MUST have authenticated through the native secp256k1 path, with the address-derived self actor and admin scope. A generic authenticator that returns the same actor ID does not qualify.

target == address(0) clears delegation. Otherwise target MUST contain deployed code and MUST NOT itself be delegated. Multiple delegation entries in one transaction are invalid.

Execution of Account Changes

Account changes execute before calls:

  1. Validate all entry encodings and transaction-level constraints.
  2. Apply create entries through EIP-8401 semantics.
  3. Apply configuration entries in order.
  4. Apply delegation entries.
  5. Revert the account-change phase atomically if any entry fails.

A successful account-change phase persists even when a later call phase fails, as specified below.

Execution

Call Execution

The protocol dispatches calls directly from sender to each call's to address:

ParameterValue
from (caller)sender (the sender)
tocall.to
tx.originsender
msg.sender at targetsender
msg.value0
datacall.data

Calls carry no ETH value. ETH transfers are initiated by the account's wallet bytecode via the CALL opcode (see Why No Value in Calls?).

Call Phases

calls is a two-level structure: an ordered array of phases, where each phase is an ordered array of individual calls ([[call, ...], [call, ...]]) — sequentially-committed atomic call groups. This gives two levels of atomicity where calls grouped within a phase are all-or-nothing, while phases commit independently in sequence.

Phases execute in order from a single gas pool (gas_limit). Within each phase, calls execute in order and are atomic so if any call in a phase reverts, all state changes for that phase are discarded and remaining phases are skipped. Completed phases persist and their state changes are committed and survive later phase reverts.

Common patterns:

  • Simple call: [[call]], one phase, one call (call = rlp([to, data]))
  • Atomic batch: [[call_a, call_b, call_c]], one phase, all-or-nothing
  • Sponsor + user: [[sponsor_payment], [user_action_a, user_action_b]], sponsor in phase 0 (committed), user actions in phase 1 (atomic, skipped if sponsor fails)

Transaction Context

The Transaction Context precompile at TX_CONTEXT_ADDRESS provides read-only access to the current AA transaction's metadata. It reads directly from the client's in-memory transaction state; protocol "writes" are effectively zero-cost. Gas is charged as a base cost plus 3 gas per 32 bytes of returned data, matching CALLDATACOPY pricing.

The static, sender-signed fields (sender, payer, calls, gas_limit) are populated during authentication and call execution, so a payer authenticator can inspect the transaction it is being asked to sponsor and decide on chain, at validation time, whether to pay. The authenticated actorId is resolved only after authentication succeeds, so it is available during execution only.

FunctionReturnsAvailable
getTransactionSender()address, the account executing calls (sender)Validation + Execution
getTransactionPayer()address, gas payer (sender for self-pay, payer for sponsored)Validation + Execution
getTransactionCalls()Call[][], the full calls array (Call = (address to, bytes data))Validation + Execution
getTransactionGasLimit()uint256, the sender-side gas budget (gas_limit) before intrinsic costs and call executionValidation + Execution
getTransactionSenderActorId()bytes32, authenticated actor's actorIdExecution only

Under the L1 profile this getter set and its validation-time availability are normative; under the L2 profile a chain MAY populate the precompile during execution only.

If the wallet needs the authenticator address or scope, it calls getActorConfig(account, actorId) on the Keystore contract. Keeping actor configuration out of the precompile limits the precompile to immutable transaction context.

Chains without this transaction type: No code at TX_CONTEXT_ADDRESS; STATICCALL returns zero/default values.

Portability

The same EIP-8401 account can use different transaction transports on different chains:

ComponentChain implementing this proposalChain without this transaction type
AuthorityEIP-8401 KeystoreSame EIP-8401 contract
AuthenticationNative or bounded STATICCALLAuthenticator contracts through EVM
Configurationaccount_changes or EVM callapplySignedAccountChanges
CreationNative create entryEIP-8401 CREATE2 factory
Transaction nonceNonce ManagerTransport-specific, such as ERC-4337 EntryPoint
ExecutionAA_TX_TYPEERC-4337 or wallet-specific

All EIP-8401 contracts SHOULD use deterministic addresses across chains. The native path changes transport and pricing, not account authority.

Validation Flow

Mempool Acceptance

  1. Parse and structurally validate sender_auth. Verify account_changes contains at most one create entry (type 0x00, must be first) and at most one delegation entry (type 0x02). Nodes SHOULD enforce a configurable limit on the number of config change entries (type 0x01).
  2. Resolve sender: if sender set, use it; if empty, ecrecover from sender_auth
  3. Determine effective actor state: a. If create entry present in account_changes: verify address derivation, code_size(sender) == 0, use initial_actors b. Else: read from Keystore storage
  4. If config change or delegation entries are present in account_changes, simulate applying config operations in sequence and skip already-applied entries. For delegation entries, verify code_size(sender) == 0 or an existing delegation designator.
  5. Validate sender_auth against resulting actor state (see Validation), require admin or SENDER for the sender context, and require admin or NONCE for sequential nonce use. Payer scope (SELF_PAYER / SPONSOR_PAYER) is checked in step 6, not here — the sender actor is not required to carry a payer bit. If delegation entries are present, the resolved actor must additionally hold admin EOA-actor delegation authority (see Delegation Entry).
  6. Resolve payer from payer and payer_auth:
    • payer empty and payer_auth empty: self-pay. Payer is sender; require the resolved sender actor be admin or carry SELF_PAYER. Reject if balance insufficient.
    • payer = sender (explicit) with payer_auth: self-pay via a dedicated gas key. Validate payer_auth against the account and require the resolved actor be admin or carry SELF_PAYER. Reject if balance insufficient.
    • payer = a different 20-byte address (sponsored): validate payer_auth against the payer and require the resolved actor be admin or carry SPONSOR_PAYER.
    • In every mode, reject the transaction if the resolved payer_auth_cost exceeds MAX_AUTHENTICATION_GAS, so total gas stays within effective_gas_limit = gas_limit + MAX_AUTHENTICATION_GAS (see Intrinsic Gas).
  7. Require nonce_key < NONCE_KEY_MAX and nonce_sequence == current_sequence(sender, nonce_key), then verify payer ETH balance and validity bounds. The transaction's validity window and the resolved actor's liveness MUST all hold: after normalizing valid_after/valid_before to milliseconds (see Timestamp Normalization), the transaction is valid only when valid_after == 0 || valid_after <= block.timestamp * 1000 and valid_before == 0 || block.timestamp * 1000 <= valid_before, and the resolved actor satisfies the EIP-8401 actor expiry rule. Any bound violation rejects the transaction. Nodes MAY also reject a transaction whose valid_before is too near to be reliably included, and MAY reject rather than hold one whose valid_after is not yet active.
  8. Mempool threshold: gas payer's pending count below node-configured limits.
  9. Treat a pending transaction with the same (sender, nonce_key, nonce_sequence) as a replacement candidate under the node's standard fee-bump rules.

Nodes recognize the enshrined canonical authenticator set and authenticate any other accepted authenticator via a single STATICCALL bounded by MAX_AUTHENTICATION_GAS, per the Canonical Authenticator Set rules.

Block Execution

  1. If account_changes contains delegation entries, require admin EOA-actor delegation authority (see Delegation Entry).
  2. ETH gas precharge from payer (sender for self-pay). The payer is precharged for the whole transaction, effective_gas_limit × max_fee_per_gas, which reserves gas_limit plus payer_auth_cost (payer authentication execution and its serialized payer_auth bytes, both metered outside gas_limit; see Intrinsic Gas). The block reserves effective_gas_limit against its gas target for the same reason. Transaction is invalid if the payer's balance cannot cover the precharge.
  3. Increment the Nonce Manager sequence for (sender, nonce_key).
  4. If code_size(sender) == 0 and no create entry and no delegation entry is present in account_changes, auto-delegate sender to DEFAULT_ACCOUNT_ADDRESS (set code to 0xef0100 || DEFAULT_ACCOUNT_ADDRESS). This delegation persists. A delegation entry that clears the indicator (target = address(0)) returns the account to code-less state, but auto-delegation re-applies on its next transaction: a code-less account cannot permanently opt out of auto-delegation without placing non-delegation bytecode (e.g. via a create entry).
  5. Process account_changes entries in order (see Execution of Account Changes).
  6. Set the authenticated actorId on the Transaction Context precompile. The static sender-signed fields (sender, payer, calls, gas_limit) are already exposed from validation onward; actorId becomes readable from this point (execution).
  7. Execute calls per Call Execution semantics.

Settlement charges the payer for gas actually used, sender-intrinsic gas plus executed calls (together bounded by gas_limit) plus payer_auth_cost, and refunds the remainder of the precharge. Because payer_auth_cost is metered outside gas_limit, it is charged in full and is never part of the refundable unused-gas_limit amount. For step 5, the protocol SHOULD inject log entries into the transaction receipt matching this proposal's events, following the protocol-injected log pattern established by EIP-7708. These protocol-injected logs are emitted only for AA transactions.

RPC Extensions

eth_getTransactionCount: Extended with an optional nonceKey parameter (uint256) to query keyed transaction nonce channels. Reads from the Nonce Manager precompile at NONCE_MANAGER_ADDRESS.

eth_getTransactionReceipt: AA transaction receipts include:

  • payer (address): Gas payer address (sender for self-pay, specified payer for sponsored).
  • status (uint8): 0x01 = all phases succeeded (or calls was empty), 0x00 = one or more phases reverted. Existing tools checking status == 1 remain correct for the success path. status == 0 does not imply "no state change." Committed earlier phases (the sponsor pattern relies on this), auto-delegation, applied account_changes, nonce consumption, and gas payment all persist through a later phase revert. Consumers MUST NOT treat a failed AA receipt as a no-op. gasUsed includes payer_auth_cost (see Intrinsic Gas).
  • phaseStatuses (uint8[]): Per-phase status array. Each entry is 0x01 (success) or 0x00 (reverted). Phases after a revert are not executed and reported as 0x00. Empty if calls was empty.

eth_estimateGas / eth_call: Accept the AA transaction fields (sender, nonceKey, accountChanges, calls, validAfter, validBefore, metadata, payer, senderAuth, payerAuth) alongside the standard request object and price the request as an AA_TX_TYPE transaction. Because authentication gas is determined by the auth blob's shape rather than a valid signature (see Intrinsic Gas), the request is priced from an unsigned representative blob; no signature is produced or verified. The sender is taken from sender or the standard from (equivalently) and if both are present they MUST be equal.

Constants

NameValueMeaning
AA_TX_TYPE0x79EIP-2718 transaction type
AA_PAYER_TYPE0x7APayer-signature domain separator
AA_BASE_COST15000Base intrinsic gas
NONCE_MANAGER_ADDRESS0x813000000000000000000000000000000000aa01Nonce Manager precompile
TX_CONTEXT_ADDRESS0x813000000000000000000000000000000000aa02Transaction Context precompile
DEFAULT_ACCOUNT_ADDRESSDeterministic deployment addressDefault delegated wallet implementation
NONCE_KEY_MAX2^256 - 1Reserved nonce key
TIMESTAMP_MS_THRESHOLD100_000_000_000Seconds/milliseconds threshold
MAX_AUTHENTICATION_GASChain parameterBound for one non-canonical authenticator

The EIP-8401 constants KEYSTORE_ADDRESS, K1_AUTHENTICATOR, CONTRACT_ESTABLISHED, and DEFAULT_EOA_REVOKED are used unchanged.

Rationale

Why a Native Transaction?

A native envelope exposes authentication and replay inputs before wallet execution. Nodes can bound validation and enumerate the state that invalidates a pending transaction without simulating arbitrary account code.

Why Keyed Sequential Nonces?

Independent keys allow unrelated workflows to proceed in parallel while retaining simple monotonic replay protection for every channel.

Why Reuse EIP-8401 Account Changes?

The same signed configuration batch can be relayed through an EVM call or transported natively. Wallet authority therefore does not depend on chain support for this transaction type.

Why Reserve Extension Values?

Reserving a scope bit and NONCE_KEY_MAX permits dependent standards to extend the same actor representation and transaction envelope without changing base transaction behavior.

Why No Value in Calls?

Each call executes with msg.value == 0. Accounts transfer ETH by calling an account implementation that explicitly performs the value transfer, keeping value movement visible in call data and compatible with policy extensions.

Backwards Compatibility

Existing EOAs, smart contracts, and ERC-4337 infrastructure continue to operate unchanged.

EIP-8401 accounts are usable without this proposal. Adoption adds a native transaction path but does not require account migration.

A transaction using a sequential nonce key below NONCE_KEY_MAX and no assigned extension values remains valid on chains implementing compatible extensions.

Reference Implementation

ITransactionContext (Precompile)

interface ITransactionContext {
    struct Call {
        address to;
        bytes data;
    }

    function getTransactionSender() external view returns (address);
    function getTransactionPayer() external view returns (address);
    function getTransactionCalls() external view returns (Call[][] memory);
    function getTransactionGasLimit() external view returns (uint256);
    function getTransactionSenderActorId() external view returns (bytes32);
}

Read-only. Gas is charged as a base cost plus 3 gas per 32 bytes of returned data. getTransactionSender, getTransactionPayer, getTransactionCalls, and getTransactionGasLimit read static sender-signed fields and are available during both authentication and call execution; getTransactionSenderActorId is available during execution only (the actor is resolved after authentication).

INonceManager (Precompile)

interface INonceManager {
    function getNonce(address account, uint256 nonceKey) external view returns (uint64);
}

Read-only. This proposal reads nonce keys below NONCE_KEY_MAX; the maximum value is reserved for an extension. The protocol manages nonce storage directly; there are no state-modifying functions. Gas is charged as a base cost plus a cold SLOAD (2,100 gas) for the first read of an (account, nonceKey) pair in the transaction, or a warm SLOAD (100 gas) for subsequent reads of the same slot, consistent with EIP-2929 access rules.

Security Considerations

Validation surface: Canonical authenticators are pure functions of the transaction hash, signature data, and EIP-8401 actor configuration. Their enumerable invalidators are actor changes and sequential nonce consumption. Permissive acceptance of non-canonical authenticators widens that surface to state read by those contracts.

Replay protection: The signature binds chain_id, nonce key, nonce sequence, and validity window. Inclusion increments the selected sequential channel regardless of call outcome.

Scope enforcement: The protocol must apply sender, nonce, and payer grants after authentication. Unknown and reserved bits grant nothing.

Authenticator binding: The actor ID returned by an authenticator must resolve to an EIP-8401 configuration bound to that authenticator.

Implicit actor: Only the raw EOA path or K1_AUTHENTICATOR may use EIP-8401's implicit address-bound actor.

Payer replay: The payer digest binds the resolved sender and payer. For a raw EOA transaction, validation substitutes the recovered sender address into the payer digest.

Account changes: Native creation and configuration must exactly match EIP-8401 contract results. Divergence would produce transport-dependent authority.

Delegation: Only native secp256k1 authentication of the admin self actor may authorize delegation. Arbitrary authenticators cannot claim this path by returning the account address.

Transaction context: Contracts must treat zero values from TX_CONTEXT_ADDRESS as absence of the native transaction context on unsupported chains.

Copyright and related rights waived via CC0.

EIP.tools

EIP.tools

Search, read, and map Ethereum improvement proposals, ERCs, RIPs, and CAIPs from one focused interface.

Farcaster
by @apoorveth