Generated by AI I first came across this EIP earlier this year while looking at Ethereum’s roadmap (https://strawmap.org/), where the Hetoga upgrade had “Generated by AI I first came across this EIP earlier this year while looking at Ethereum’s roadmap (https://strawmap.org/), where the Hetoga upgrade had “

Native Account Abstraction — EIP-8141 Frame Transaction

2026/05/25 17:10
32 min read
For feedback or concerns regarding this content, please contact us at crypto.news@mexc.com
Generated by AI

I first came across this EIP earlier this year while looking at Ethereum’s roadmap (https://strawmap.org/), where the Hetoga upgrade had “Native AA” listed. That immediately caught my attention — account abstraction, something that had been talked about for so long, was suddenly going live! But about a week later, the block that originally said “Native AA” was changed to “frame transaction”.

Introduction: The Evolution of Account Abstraction

Over the past few years, Account Abstraction (AA) has been a constant focus — from the earliest EIP-86, to the widely deployed ERC-4337, to the EOA delegation scheme of EIP-3074, and finally to the elegant EIP-7702. Each of these solutions has been a workaround layered on top of the existing EOA / contract architecture, because achieving native account abstraction (Native AA) requires changes to the protocol itself — a massive undertaking. But years of upgrades have given the execution layer more capabilities and greater flexibility for different transaction types (such as EIP-2718), and with the looming threat from quantum computing, we’ve finally reached the endpoint of this evolutionary path — EIP-8141 Frame Transaction.

ERC-4337 was the first solution to see large-scale adoption. It took a pragmatic route — leaving the protocol layer untouched and building an entire layer of scaffolding on top. It introduced a separate mempool and a “Bundler” network: users package their operations as a UserOperation, and the bundler collects and verifies these operations before batching them on-chain as a single ordinary EOA transaction. This architecture made features like multisig wallets, social recovery, and passkey login much easier to implement and more widely available, and the burden on contract wallet developers was much lighter — they no longer had to build the infrastructure themselves. But the infrastructure itself is still heavy: developing bundlers and paymasters is time-consuming, maintaining them is costly, and ultimately, users have to rely on services from large providers.

EIP-7702 is an elegant transitional step that lets an EOA “borrow” a smart contract’s code to execute at its own address, without changing the fundamental nature of the account. As a result, existing EOAs can enjoy batch transactions, gas sponsorship, and other benefits without migrating to a new address. Without the quantum threat, this solution would be enough to cover most current AA needs. But since transactions remain EOA at their core, the underlying ECDSA keypair still faces the quantum threat.

EIP-8141 is the endpoint of this evolution. It modifies Ethereum’s underlying architecture directly, introducing a brand new transaction type — Frame Transaction. The protocol natively replaces the role of bundlers, ECDSA signatures are no longer the only credential for an account, and post-quantum cryptography is formally introduced into the system for the first time.

This article starts with the core architecture of Frame Transaction, then walks through its execution modes, authorization mechanism, and gas accounting. Finally, a concrete example shows how it changes everyday on-chain interactions.

The Core Architecture of frame transaction

One transaction, multiple frames

To understand why Frame Transaction looks the way it does, we first need to see how traditional transactions are structured.

A traditional EOA transaction has this structure: from (who is acting), to (where to execute), data (what to do). This structure compresses three things — "who is acting", "what to execute", and "who pays" — into a single transaction, and all three are controlled by the same private key. This is fine in the simplest cases. But once you need to separate them — say, letting someone else pay your gas, or doing several things in one transaction — it starts to break down.

EIP-8141’s design choice is to go back to fundamentals: split “verification”, “execution”, and “payment” into separate frames, each with its own role, defined directly by the protocol. A Frame Transaction can contain an array of frames — currently up to 64. Each frame can be thought of as an independent sub-transaction — similar to how a MultiCall contract executes multiple calls in one go. The fields of a Frame Transaction are defined as follows:

[chain_id, nonce, sender, frames, max_priority_fee_per_gas, max_fee_per_gas, max_fee_per_blob_gas, blob_versioned_hashes]frames = [
[mode, flags, target, gas_limit, value, data],
[mode, flags, target, gas_limit, value, data],

]

The transaction type code is FRAME_TX_TYPE = 0x06. Within the overall structure, the parts worth paying attention to are sender and frames — the rest is similar to a regular transaction.

# Before we start: clarifying what “address” means #

Before diving into the details, I want to clarify what each “address” represents, to avoid mixing up the roles.

As shown on Etherscan, looking at From and To:

  • Traditional transaction: To is the execution destination. For example, a USDC transfer has the USDC contract as the destination; a Uniswap trade has Uniswap's contract as the destination. The transaction itself has no From field — From is recovered from the signature.
  • EIP-8141: To is still the execution destination, but a single Frame Transaction can contain multiple frames, and each frame has its own execution destination (frame.target). To is therefore no longer a single destination but multiple. From is decoupled from the signature and is taken directly from tx.sender. As a result, we can directly specify a contract address — making the contract itself the transaction sender (From).

Now, from the contract’s perspective:

  • Traditional transaction: tx.origin is the sender of the transaction — the same as the From field above. If an EOA initiated it, that's the EOA itself; if it's relayed, it's the relayer's address. msg.sender is simply the immediate caller.
  • EIP-8141: The situation differs depending on the mode. Let’s first look at normal execution (SENDER mode). As mentioned above, From is tx.sender, so tx.origin equals tx.sender. In execution mode, the frame's caller is also tx.sender. As a result, the target contract (e.g., the USDC contract) sees msg.sender as the user's address — exactly the same as when a traditional EOA calls USDC. Because of this, existing DeFi contracts need no modification to support Frame Transaction — Uniswap and Aave don't even need to know Frame Transaction exists.

But there’s an important change: the semantics of the ORIGIN opcode are redefined as "the caller of the current frame", no longer the traditional "EOA that initiated the entire transaction". So the value also differs across modes:

  • SENDER mode (normal execution above): ORIGIN = tx.sender
  • DEFAULT / VERIFY mode: ORIGIN = ENTRY_POINT (0xaa)

This is a change that affects contract implementations. Any contract that relies on tx.origin == msg.sender to check "is this a direct EOA call" will be affected. Under EIP-8141, tx.sender itself can be a contract account, and this check can no longer distinguish "EOA-initiated" from "contract-account-initiated".

# Other new structural details #

Dedicated flags field: A control flag field used to configure special behaviors for the frame. Bits 0 and 1 set the approval scope, and bit 2 (value 0x04) sets atomic batch. Approval scope and atomic batch are covered in more detail later.

Native value field: The initial version didn't have this field — it was added after community feedback. With this field, a frame can support ETH transfers, but there's an important restriction: only SENDER mode frames can carry a non-zero value. ETH can now be transferred directly within a SENDER frame, so contract accounts no longer need to implement complex ETH-handling logic themselves — significantly reducing the burden on developers.

Default target: Each frame has its own target. If it's null, it defaults to tx.sender.

Receipt structure changes: Since a Frame Transaction doesn’t have to be paid by the transaction sender (a paymaster can sponsor it), and the payer can’t be statically inferred from the transaction, EIP-8141 explicitly adds a payer field to the receipt.

Each frame also has its own sub-receipt recording execution status (success/failure), actual gas used, and emitted logs. This allows block explorers to clearly display the result of each frame.

Three Execution Modes

A frame transaction has three modes, corresponding to three different roles and trust boundaries in account abstraction.

# VERIFY mode: read-only validation #

This mode is for verifying signatures and confirming the validity of the transaction. It cannot write any state — like STATICCALL, it can only read and compute. The design here is conceptually the same as ERC-4337: during the validation phase, state cannot be modified and external dynamic information cannot be read — preventing cases where off-chain simulation succeeds but on-chain execution fails. After validation succeeds, you need to call APPROVE to set payer_approved or sender_approved.

Background: How does sig_hash work in a traditional transaction?

Before getting into why VERIFY frame’s data has these particular design choices, we need to understand how Ethereum handles transaction signatures at the lower level. We don't sign a raw data blob (like the entire transaction content) directly. Instead, we follow these three steps:

  1. Serialization and hashing: First, all the transaction’s parameters (such as nonce, to, value, gas_limit, data, etc.) are RLP-encoded, and then a keccak256 hash is computed. The output of this hash is the transaction's sig_hash (signature hash).
  2. Signing with the private key: The user signs this sig_hash with their private key, producing the signature (the familiar v, r, s values).
  3. Broadcasting: The computed signature is attached to form a complete transaction, which is then broadcast to the network.

In the traditional transaction structure, v, r, s are three separate fields. When computing sig_hash, these three fields are left blank — the hash is computed, the transaction is signed, and then the values are filled in. In a traditional transaction, "the data being signed" and "where the signature is stored (v, r, s)" are kept separate.

In EIP-8141, however, the protocol no longer hard-codes fixed v, r, s fields. This is to support arbitrary signature verification logic — multisig, passkey, or future post-quantum schemes. All signature data and sponsor authorization credentials must now be packed as parameters into the VERIFY frame's data field, for the contract to parse. Because the signature now lives inside data, the field is automatically omitted when computing sig_hash. There are three reasons for this:

  • Circular dependency: The data field of a VERIFY frame contains the signature itself. When computing "the hash to be signed", this field cannot be determined, so it must be omitted.
  • Gas sponsorship workflow: Excluding data from the hash computation enables a workflow that doesn't depend on signing order. The user can sign the transaction first, and the paymaster can add its own authorization data afterward without requiring the user to re-sign. Meanwhile, frame.target (which specifies who does the verification, e.g., the paymaster's address) is still included in the signature hash, so malicious nodes cannot swap out the paymaster during transmission.
  • Future signature aggregation: Core developer meetings have specifically discussed restructuring the EIP encoding. The goal is to ensure that when post-quantum signature aggregation happens in the future, the aggregated signature doesn’t change the transaction hash.

This brings an important security implication: the user’s signature does not cover data, so the contents of data can be tampered with. Charging parameters like exchange rates and fee caps can't be supplied by the user through data. The paymaster needs to provide and sign these itself, so the on-chain contract can verify them and prevent tampering.

# SENDER mode: executing as the user #

This mode is just a regular contract call, with the caller being tx.sender. When any contract is called, the target contract sees msg.sender as the user's address. This is the key to backwards compatibility: contracts like Uniswap and Aave don't need to know frame transaction exists at all.

But SENDER mode has a strict prerequisite: by the time the frame is executed, sender_approved must already be true, otherwise the entire transaction is invalid. This constraint prevents an obvious impersonation attack: without this rule, anyone could construct a frame transaction with an arbitrary address in tx.sender, and use a SENDER frame to impersonate that address when calling contracts. The mandatory order of VERIFY first (authenticate sender identity via signature), then SENDER (execute as the sender) ensures that execution must rest on an explicit authorization.

# DEFAULT mode: the protocol’s neutral executor #

This mode is also a regular contract call, but the caller is ENTRY_POINT (address(0xaa)) — a virtual address defined by the protocol, not a deployed contract. It exists to provide a neutral execution identity for operations that aren't the user and shouldn't have any self-interest of their own. The two typical uses are:

  • Account deployment (deploy frame): deploys a smart account contract at the tx.sender address for the first time. At this point the sender hasn't been authenticated yet (the deploy frame must come before the VERIFY frame — see the validation prefix section later), so the caller is set to ENTRY_POINT rather than tx.sender itself.
  • Paymaster post-op: After all SENDER frames have executed, the paymaster can perform cleanup work like refund settlement, converting ERC-20 to ETH, and so on. The caller here is ENTRY_POINT rather than tx.sender, so off-chain indexers won't mistake the cleanup for a user operation.

Here is the simple table to summarize the three modes.

Summary of three modes

How this differs from ERC-4337’s EntryPoint

  • ERC-4337’s EntryPoint is a real deployed contract. The bundler passes UserOperation to it, and it implements validation and execution at the contract layer.
  • EIP-8141’s ENTRY_POINT is a protocol-defined virtual address — no code, can't be CALLed, only used as a caller identity marker. The entire validation and execution logic is handled by the protocol itself.

The Core: APPROVE Opcode

Now that we’ve covered the three modes, APPROVE is the key that ties them together.

First, a definition: resolved_target is frame.target if set, otherwise tx.sender.

APPROVE has a scope parameter (bits 0 and 1 of frame.flags), which determines what's being authorized in this call:

  • APPROVE_PAYMENT (0x1): sponsor authorization — gas fees are deducted from this address, sets payer_approved = true.
  • APPROVE_EXECUTION (0x2): user authorization — verifies the user's signature; once verified, subsequent frames are treated as called by this user, and sender_approved = true is set. Additionally, resolved_target must be tx.sender: since APPROVE is being called by a VERIFY frame to verify the user's authorization, the execution target has to be tx.sender.
  • APPROVE_PAYMENT_AND_EXECUTION (0x3): sets both to true (an atomic operation, not equivalent to calling APPROVE twice).

EIP-8141 separates the roles of user and sponsor (similar to ERC-4337’s paymaster design), so authorization is split accordingly. At the end of verification, the APPROVE opcode sets the authorization state for both roles. Its behavior is similar to RETURN — it successfully terminates the current frame's execution. Additionally, payer_approved and sender_approved are global across the transaction and cannot be set repeatedly — setting one to true when it's already true will revert.

If the sponsor’s balance is insufficient, the entire frame reverts. After all frames finish executing, any unused gas is refunded to the sponsor. This is a “pre-charge, then refund” approach, consistent with how traditional transactions deduct gas_limit × gas_price upfront and refund the difference afterward.

Similarly, APPROVE(APPROVE_PAYMENT_AND_EXECUTION) does all of the above and also sets sender_approved = true.

# Execution order constraint #

Authorization has a strict order: sender_approved = true must be set first to confirm the user's identity, before the paymaster can be authorized to set payer_approved = true. Otherwise the transaction reverts. Furthermore, setting payer_approved triggers three actions at the same time:

  • tx.sender's nonce is incremented.
  • The maximum possible fee for the transaction is pre-charged from the sponsor’s address.
  • payer_approved is set to true.

Why this order matters:

  • For the mempool: When the user’s signature verification fails, the node can immediately discard the entire transaction without executing the paymaster verification, avoiding wasted compute from DoS attacks.
  • For the paymaster: When it calls APPROVE(APPROVE_PAYMENT), it can be certain that the user's signature verification has passed, and doesn't have to bear the risk of "paying for a transaction with an invalid signature".

# Caller constraint #

There’s a restriction when calling APPROVE: the address executing APPROVE must itself be the frame’s target execution address — i.e., ADDRESS == resolved_target. Otherwise it reverts. In other words, you can't set frame.target to contract A and then use STATICCALL into contract B to call APPROVE from there. This prevents accidentally triggering APPROVE in a malicious contract that grants authorizations on your behalf. But DELEGATECALL is fine — it stays in the caller's context, so ADDRESS is still the target itself.

To summarize so far, the flow of a Frame Transaction looks roughly like this:

Frame Transaction Flow

The next question is how this design integrates with the current execution layer — and what to do about EOA users.

Default Code and Arbitrary Signature Verification Logic

Seamless upgrade for EOAs: Default Code

The core architecture of Frame Transaction is complete and elegant for smart contract accounts, but there’s a practical issue: the vast majority of users on Ethereum today are EOAs, with no contracts deployed at their addresses. If Frame Transaction required the target address to have a contract for signature verification, every EOA user would have to deploy an account contract before using the new features — a huge migration cost.

EIP-8141 solves this with “Default Code”. When a user has neither a contract nor an EIP-7702 delegation, the protocol doesn’t reject the call. Instead, it executes a piece of logic predefined at the protocol layer — as if the address had invisibly deployed a standard “mini verification contract”.

The key point about default code: EOAs don’t need to deploy any contracts or migrate assets to a new address to enjoy all the benefits of Frame Transaction — gas sponsorship, batch operations, paying gas in ERC-20, passkey login, and so on. For EOA users, the only difference is that their wallet provider needs to support Frame Transaction.

Top-level logic of default code

# DEFAULT branch: revert directly #

An EOA’s default code doesn’t support DEFAULT mode. A DEFAULT frame’s caller is ENTRY_POINT, typically used for paymaster post-ops — not a scenario an EOA would trigger.

# VERIFYbranch: signature type determined by data#

The first byte:

  • 0x00 for secp256k1, corresponding to traditional wallet signatures.
  • 0x01 for P256 (secp256r1), corresponding to passkey / Secure Enclave signatures on phones.

Default code natively supports P256 — the signature scheme behind today’s most popular passkey. When a user creates a passkey on their phone, the Secure Enclave automatically generates a P256 keypair, allowing the passkey public/private keys to derive an address directly. From that point on, the only credential for this account is the P256 private key stored in hardware — no seed phrase needed at all.

Diagram 2: Default code VERIFY branch logic

It’s worth noting that EIP-8141 itself doesn’t directly introduce post-quantum (PQ) secure signature schemes. The real PQ significance of EIP-8141 is: the VERIFY frame can execute arbitrary EVM code for signature verification. Today it can be secp256k1 or P256; tomorrow it can be Dilithium or Falcon or other PQ schemes. When PQ signature precompiled contracts are introduced in the future, contract accounts only need to update their own verification logic — no protocol upgrade required. This is what the spec calls a “native off-ramp from ECDSA”: handing the power to choose the signature scheme from the protocol down to the account.

# SENDER branch: batch operations for EOAs #

For an EOA, a SENDER frame is more than just “transferring”. frame.data is an RLP-encoded call list [[target, value, data], …], allowing multiple calls to be executed in sequence within a single frame. As a result, EOAs can do batch operations (such as approve + swap in one go) without deploying any contracts.

Diagram 3: Default code SENDER branch logic

A Closer Look at the Execution Mechanism: Gas, Transient Storage, and Revert Boundaries

New opcodes: frame-level introspection for contracts

Traditional EVM contracts can only see information about “the current call”: CALLDATA, CALLER, CALLVALUE. In a frame transaction, the contract in each frame may need a wider view. The paymaster needs to know the maximum fee for the entire transaction, the verification contract needs to get the sig_hash, and post-quantum schemes need to efficiently load 2KB+ of signature data. EIP-8141 introduces several new opcodes. In addition to APPROVE covered above, there are also TXPARAM, FRAMEDATALOAD, FRAMEDATACOPY, and FRAMEPARAM.

# TXPARAM (0xb0) / FRAMEPARAM (0xb3): reading transaction- and frame-level information #

These two opcodes give contracts powerful introspection capabilities:

  • TXPARAM: Reads transaction-level information, such as tx.sender, the precomputed sig_hash, the max fee cap of the entire transaction, or the total number of frames.
  • FRAMEPARAM: Reads data from a specific frame. By passing in a frame index, a contract can read another frame's target, gas_limit, value, or even execution status (success or failure — only readable for frames that have already executed, since status is meaningless beforehand).

This lets a contract read other frames. For example, a post-op DEFAULT frame can use FRAMEPARAM to check whether the third SENDER frame executed successfully and decide whether to issue a refund. TXPARAM's ability to directly read the cached sig_hash saves the enormous gas cost of manually RLP-encoding within the EVM, making it the single most important opcode for implementing verification contracts.

# FRAMEDATALOAD (0xb1) / FRAMEDATACOPY (0xb2): cross-frame data reading #

These two opcodes mirror the semantics of CALLDATALOAD and CALLDATACOPY, with the difference that you can specify which frame's data to read, not just the current frame. There are two design motivations. First, the paymaster's VERIFY frame needs to read the SENDER frame's data to confirm that the operation the user actually wants to execute matches its payment conditions — for example, to confirm that the SENDER frame's calldata really is transfer(sponsor, N USDC) and not some other unauthorized operation. Second, post-quantum signature data is large — a full CRYSTALS-Dilithium signature is about 2.4KB — and FRAMEDATACOPY can be used to copy a VERIFY frame's own data into memory.

Gas accounting model

Gas accounting in frame transaction differs fundamentally from traditional transactions in one place: each frame’s gas budget is fixed at transaction construction time and can’t be borrowed across frames.

The gas calculation for a frame transaction:

tx_gas_limit = 15,000 // Fixed base cost (FRAME_TX_INTRINSIC_COST)
+ len(frames) × 475 // Fixed per-frame overhead (FRAME_TX_PER_FRAME_COST)
+ calldata_cost(rlp(frames)) // Calldata cost of frames themselves
+ sum(frame.gas_limit for all frames) // Sum of each frame's execution budget

The 475 gas fixed overhead per frame breaks down into two parts: 100 for setting up the CALL execution context, and 375 (the G_log write cost) for each frame producing its own receipt sub-entry ([status, gas_used, logs]). The implication: each frame leaves its own execution record on-chain, and you can clearly see from the receipt which frame succeeded, which frame reverted, and how much gas each one used.

Why must each frame’s gas_limit be independent, with no borrowing between frames? This is a deliberate design decision, so that each frame's gas consumption can be calculated at transaction construction time. The paymaster can then accurately estimate the maximum cost of the entire transaction at the first VERIFY stage (before any SENDER frame has executed), without the fee estimate going off because one frame "accidentally borrows" gas from another.

After all frames finish executing, the refund is calculated uniformly:

This refund goes back to the payer, and the leftover gas is returned to the block’s gas pool.

Transient storage boundary: a frame is not just a CALL

A frame is conceptually an EVM CALL, but there are some differences in the details. There’s an important difference between a frame and aCALL in how they handle transient storage and warm/cold state. Transient storage is cleared across frames, but warm/cold state is still shared across frames:

Differences between CALL and frame

# The meaning of clearing transient storage #

Each frame is an independent execution unit, to keep each frame’s behavior predictable. But there’s a security pitfall to be especially careful of. After EIP-1153, many contracts replaced their SSTORE-based reentrancy guards with TSTORE, because it's much cheaper. Under Frame Transaction, transient storage is cleared between frames. As a result, if both frame 1 and frame 2 of a transaction call the same contract with a transient guard, the two calls will be treated as independent "first calls" and won't trigger the reentrancy guard.

In most contexts this doesn’t constitute a security vulnerability — reentrancy was originally about reentering within the same call stack, and frames are independent call stacks by design. But if a contract’s logic relies on the assumption that “this can only execute once per transaction”, it needs to switch back to SSTORE.

# Warm/cold state shared across frames #

This design is for gas efficiency. If frame 0 has already read a storage slot (making it warm), then frame 1 reading the same slot only pays the warm read cost (100 gas) rather than the cold read cost (2,100 gas). This is consistent with how multiple reads of the same slot work within a traditional transaction — developers don’t need to handle it specially.

Revert boundary: developer-defined

The revert semantics of traditional transactions are very simple: any call layer reverts, and the entire transaction rolls back. Frame Transaction breaks this default, leaving the revert boundary up to the developer to decide.

Default: independent frames

If you do nothing, each SENDER frame’s revert only affects itself. If frame 2 reverts, frame 2’s state changes are discarded, and frame 3 still executes as usual. The entire transaction still goes on-chain. This behavior is useful for certain scenarios — for example, ‘try multiple routes, succeed if any one works’, or post-op refunds that shouldn’t roll back the entire user operation just because of some edge case

Opt-in: atomic batch

When you need multiple operations bundled as all-or-nothing, you can set bit 2 of the flags field on a SENDER frame. A consecutive run of SENDER frames with this flag set, plus the first SENDER frame after them that doesn't have this flag, together form an atomic group.

How an atomic batch executes:

  • Take a snapshot of the entire state before entering the group
  • Execute the frames in the group in sequence
  • If any frame reverts, roll back to the snapshot (discarding all state changes in the group), and skip the remaining frames in the group
  • The entire transaction still goes on-chain

For example, suppose group A is frames 0 and 1, and group B is frames 2, 3, and 4. If frame 3 reverts, the state of group B (frames 2, 3, 4) is fully rolled back, but the results of group A are completely unaffected. This design allows multiple independent atomic groups to exist within a single transaction.

Frame revert in an atomic batch

Native Paymaster and Mempool Safety

From bundlers to direct node verification: a new DoS problem

ERC-4337’s bundler architecture is cumbersome, but it has a hidden benefit: the bundler is a party with skin in the game — it won’t accept transactions that make it lose money, so it’s incentivized to strictly filter problematic UserOperations. The DoS risk of verification logic is borne by the bundler, and never reaches the nodes.

Frame Transaction goes directly into the public mempool, and this protective layer disappears. Nodes now have to execute arbitrary VERIFY contract code themselves to determine whether a transaction is valid. This immediately opens up an attack vector: an attacker can craft a transaction whose verification logic “looks legitimate but is expensive to execute” and submit it to the mempool, forcing the node to waste compute on it.

Even more dangerous is the mass invalidation attack. If the validity of a thousand pending transactions all depends on the same storage slot (such as a paymaster’s balance), an attacker only needs to send one transaction to zero out that slot, and all thousand transactions become invalid at once — meaning the node validated them a thousand times for nothing.

To address this, EIP-8141 introduces the concept of a validation prefix. Before entering the public mempool, a transaction must pass the validation prefix rules, which only allow a small set of behaviors.

Validation Prefix: only what comes before “payment confirmation”

What nodes actually need to protect against is “all execution that happens before fee payment is confirmed”, because that’s the part the node executes for free. If this part can be abused, it becomes a DoS vector. Once payer_approved == true, the node has confirmed that someone will pay, and subsequent execution can be arbitrarily complex — the miner is compensated for the work.

Definition of validation prefix: the shortest prefix of frames whose successful execution sets payer_approved = true.

Once payer_approved is set to true at the end of a frame, that frame is the last frame of the validation prefix. Subsequent frames (like user_op, post_op) fall outside the validation prefix and aren't subject to the mempool's protection rules.

In short: once a sponsor is found and payment is confirmed, subsequent frames follow the same rules as ordinary transactions. To avoid burning too much compute before finding the sponsor, we want this prefix to be as short as possible. Currently only the following four scenarios are supported:

  • self_verify: VERIFY frame, calling APPROVE(0x3) (authorize both execution and payment)
  • only_verify: VERIFY frame, calling APPROVE(0x2) (authorize execution only)
  • pay: VERIFY frame, calling APPROVE(0x1) (authorize payment only)
  • deploy: DEFAULT frame, calling the EIP-7997 deterministic factory to deploy the account contract at the sender address

Excluding contract deployment, these scenarios only set one of two flags: payer_approved = true or sender_approved = true. Additionally, the total gas cap of the validation prefix is MAX_VERIFY_GAS = 100,000. This caps the simulation cost for nodes — even if an attacker designs verification logic that burns a lot of gas, the most they can waste is 100k gas per transaction.

# Front-running risk of the deploy frame #

One important caveat: the deploy frame happens before any verification, when tx.sender hasn't been authenticated yet. Anyone can therefore front-run the deployment to tx.sender using the same deterministic factory, initcode, and salt. For this reason, EIP-8141's security guidelines specify:

  • The initcode used for the deploy frame must be “safe for anyone to deploy”.
  • Wallets should handle the case where the deploy has already happened (whether through the user’s own transaction or by a front-runner) and remove the deploy frame when resubmitting.

Validation prefix execution rules: forbidden opcodes

As the table above shows, the validation prefix is essentially the prerequisite setup before the transaction starts (deploy, pay) or verification (self_verify, only_verify).

Going back to the VERIFY frame section above, anything that “depends on environmental state” is forbidden. If verification logic uses TIMESTAMP, a transaction that validated yesterday could fail today because the timestamp passed some deadline. An attacker could flood the mempool with such transactions just before a deadline, and once the time passes, all of them become invalid — wasting node compute. The same applies to BLOCKHASH, NUMBER, COINBASE, ORIGIN, and other environment-related opcodes.

BALANCE and SELFBALANCE: If the paymaster's verification logic reads its own ETH balance, then any transaction that changes the paymaster's balance (including ordinary transfers) could simultaneously invalidate a large number of pending transactions, so it can't be relied on for verification.

SLOAD is allowed, but only for reading tx.sender's own storage — not the storage of any other address (including any address reached through CALL or DELEGATECALL), because external storage can change at any time.

SSTORE, TLOAD, and TSTORE are completely forbidden in the validation prefix. The validation stage can't write any state, the same as STATICCALL. This applies not just to the VERIFY frame — the entire prefix is treated as read-only during node simulation.

Two paths for paymasters: Canonical vs Non-canonical

We just said the pay frame can’t read the paymaster’s own storage — so how does the paymaster confirm it has enough ETH to cover the gas? This contradiction is one of the most interesting parts of the mempool design. EIP-8141’s solution is to split paymasters into two categories, each solving this problem with a different mechanism:

Canonical Paymaster: the node keeps the books

A canonical paymaster is a protocol-standard implementation that anyone can deploy, but the pay frame’s runtime code must match the protocol specification exactly. When the node recognizes a canonical paymaster, the validation prefix rules don’t apply to the pay frame. Instead, the node maintains a local ledger:

available_balance = state.balance(paymaster)
- reserved_pending_cost(paymaster) // Fees reserved for other pending transactions
- pending_withdrawal_amount(paymaster) // Amount in withdrawal (with lock-up period)

Only transactions where available_balance >= tx_max_cost are accepted into the mempool. For each accepted transaction, reserved_pending_cost increases by that transaction's max cost; when the transaction is included on-chain or evicted, the reservation is released. This ledger is local to the node and never goes on-chain, but it's enough to prevent the paymaster from over-committing. This design lets a canonical paymaster sponsor gas for an unlimited number of accounts, making it the right tool for dApps and wallet providers offering "gasless experiences" at scale.

Non-canonical Paymaster: flexibility at the cost of quantity limits

Any contract that doesn’t conform to the canonical paymaster spec is treated as a non-canonical paymaster. The node can’t maintain a balance ledger for it, since the paymaster’s withdrawal logic is unknown. Instead, it applies a blunt but effective rule: each non-canonical paymaster can have at most one pending transaction in the public mempool.

This restriction ensures that even if a non-canonical paymaster’s balance suddenly drops to zero, at most one transaction is affected, and mass invalidation is impossible.

The main use case for the non-canonical paymaster is the “gas account” pattern: the user’s everyday account doesn’t need to hold ETH — every transaction pays gas fees with stablecoins like USDC, while a separate EOA dedicated to holding ETH covers the gas. Since every EOA supports default code, this gas account doesn’t need to deploy any contract to act as a paymaster. Its default code verifies the ECDSA signature directly in the VERIFY frame and calls APPROVE(APPROVE_PAYMENT) — no extra infrastructure needed. The only restriction is that this "gas account" can have at most one transaction waiting in the mempool at any given time, but for personal use cases that's more than enough.

A concrete example: EOA paying gas in USDC, doing a swap on Uniswap

By now we have all the pieces to understand Frame Transaction. Let’s assemble them with a concrete scenario — the focus isn’t just “what is a frame”, but “why each frame is designed this way”.

The user’s wallet only has USDC, no ETH, and wants to complete a swap on Uniswap. Under ERC-4337, this required off-chain coordination between the bundler and the paymaster. Under EIP-8141, this is packaged into a single Frame Transaction:

  • frame 0 (VERIFY): The user account’s default code executes, verifies the ECDSA signature, and calls APPROVE(APPROVE_EXECUTION). Subsequent SENDER frames are now authorized.
  • frame 1 (VERIFY, target = Sponsor): The sponsor contract acts as the paymaster. There’s a subtle point here: the sponsor needs to confirm that “the user will actually pay”, but the VERIFY frame can’t change state. One implementation option is to use FRAMEDATALOAD to read frame 2's calldata, confirm that frame 2 is indeed transfer(Sponsor, N USDC) and that the amount N is sufficient to cover the fee, and then call APPROVE(APPROVE_PAYMENT). This way the sponsor has confirmed that the user's next frame will indeed transfer the funds, before authorizing payment. (How exactly to implement this isn't specified in the EIP — having the user call approve first and then transferFrom in frame 2 also works.)
  • frame 2 (SENDER, target = USDC): Calls USDC.transfer(Sponsor, estimated_fee) as the user — this is the user paying the sponsor.
  • frame 3 (SENDER, target = Uniswap): Calls Uniswap.swap(...) as the user. The Uniswap Router sees msg.sender as the user's address — Uniswap won't realize this is a Frame Transaction.
  • frame 4 (DEFAULT, target = Sponsor): The caller of this frame is ENTRY_POINT (address(0xaa)). The sponsor contract performs settlement here: it calculates the difference between the pre-charged estimate and actual usage, refunding the excess USDC to the user.
    Note: we can’t set this DEFAULT frame’s target directly to USDC for the refund, because in DEFAULT mode the target contract sees msg.sender as ENTRY_POINT, and the USDC contract would reject it due to insufficient permissions. So the refund logic must be implemented as a function on the sponsor contract. This frame calls into the sponsor, which then internally calls USDC to issue the refund.
EOA paying gas in USDC, doing a swap on Uniswap

Closing

From the early days of EIP-86, through ERC-4337’s architecture, to EIP-7702’s elegant transition — Ethereum’s native account abstraction is drawing ever closer.

For contract developers, EIP-8141 cuts both ways. The good news is that existing contracts hardly need any changes — Frame Transaction’s SENDER frame keeps msg.sender as the user's address, so Uniswap and Aave don't need to know anything has changed. The bad news is that some long-standing security assumptions need to be reexamined:

  • The pattern of relying on tx.origin == msg.sender to check for "direct EOA calls" no longer works. ORIGIN now represents the frame caller, and tx.sender itself can be a contract account.
  • Designs that rely on TSTORE for cross-frame state tracking or reentrancy guards will have vulnerabilities, and need to switch to SSTORE or transaction-level identifiers.
  • The premise that “signature verification is always ECDSA” no longer holds — code needs to accommodate new schemes, including P256 Passkey and future PQ schemes.

For account infrastructure developers (wallets, paymaster service providers), EIP-8141 is an opportunity to migrate from ERC-4337’s application-layer approach to protocol-native support. It also brings new engineering challenges: canonical paymaster version management, deploy frame retry logic, and cross-chain account consistency.

EIP-8141 has been moving fast: officially proposed as a draft on January 29, 2026, with EOA default code support added on March 5. At the ACDE meeting on March 26, the proposal received “Considered for Inclusion” (CFI) status and is planned to be included in the Hegota upgrade, which is expected to start in the second half of 2026. Major clients (Geth, Erigon, Nimbus) are voicing support, but the core developer team still has concerns — questions remain around mempool stability, transaction encoding complexity, and other details. Looking forward to seeing it ship in the Hegota upgrade!

References

  • https://eips.ethereum.org/EIPS/eip-8141
  • https://ethereum-magicians.org/t/eip-8141-frame-transaction/27617
  • Gemini Deep Research

Native Account Abstraction — EIP-8141 Frame Transaction was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story.

Market Opportunity
Gensyn Logo
Gensyn Price(AI)
$0.0319
$0.0319$0.0319
-0.25%
USD
Gensyn (AI) Live Price Chart

AI Strategy: Powered 24/7

AI Strategy: Powered 24/7AI Strategy: Powered 24/7

Generate automated strategies using natural language

Disclaimer: The articles reposted on this site are sourced from public platforms and are provided for informational purposes only. They do not necessarily reflect the views of MEXC. All rights remain with the original authors. If you believe any content infringes on third-party rights, please contact crypto.news@mexc.com for removal. MEXC makes no guarantees regarding the accuracy, completeness, or timeliness of the content and is not responsible for any actions taken based on the information provided. The content does not constitute financial, legal, or other professional advice, nor should it be considered a recommendation or endorsement by MEXC.

No Chart Skills? Still Profit

No Chart Skills? Still ProfitNo Chart Skills? Still Profit

Copy top traders in 3s with auto trading!