What EIP-8141 Can Do Today and How It Works
About this page
This is the primary reference for what EIP-8141 does today and how a frame transaction actually flows through the protocol. It's for readers who want the mechanism in detail: what a transaction looks like on the wire, what the EVM does with it, and what the mempool will and won't propagate.
Background assumed: comfortable reading an EIP, know what ECDSA and EOAs are. No prior exposure to ERC-4337, EIP-7702, or FOCIL needed; those are introduced where they come up.
Transaction Lifecycle
A frame transaction arrives at a node and goes through four conceptual stages:
- Admission. The node checks whether the transaction matches a shape the public mempool will propagate. The mempool only admits transactions whose validation prefix (the opening frames up to the point a payer is approved) fits one of four recognized patterns, stays under a gas cap, and reads only predictable state. If it doesn't fit, the transaction is still consensus-valid on-chain but has to reach a block builder through a private channel.
- Validation. The protocol runs the opening frames. These are the frames whose job is to prove "this transaction is authorized and someone is paying for it." Every validation frame must call the
APPROVEopcode before returning; otherwise the whole transaction is rejected. This is where signature checks, policy checks, and paymaster logic live. - Execution. Once a sender has been approved and a payer has been approved, the protocol runs the remaining frames as normal EVM calls. These are the frames that actually do what the user wanted: a token transfer, a swap, a contract deployment. Multiple execution frames can be grouped into an atomic batch so they all-or-none revert together.
- Settlement. After the last frame runs, the protocol computes gas used, refunds the payer, and writes per-frame receipt entries. The transaction receipt records which address paid, which frames succeeded, and the logs each frame produced.
The rest of this page is the mechanical detail behind these four stages.
Core Concept
EIP-8141 introduces Frame Transactions, a new transaction type (0x06) where a transaction consists of multiple frames, each with a purpose (verify identity, execute calls, deploy contracts). The protocol uses frame modes to reason about what each frame does, enabling safe mempool relay and flexible user-defined validation.
A key insight for understanding the current spec: EIP-8141 is two specs in one.
- The execution model says "validation and payment are programmable": any account code can verify any signature scheme and approve any payer, with arbitrary logic.
- The mempool model says "that programmability is only publicly relayable when it fits a small set of validation-prefix shapes and state-dependency rules."
The execution model defines what is possible; the mempool model defines what is propagatable. A frame transaction that doesn't match the mempool rules is still valid on-chain; it just can't be gossiped through the public p2p network and must reach a block builder through private channels.
Transaction Structure
[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], ...]sender: the 20-byte address of the account originating the transaction- Each frame has a
mode(lower 8 bits only), aflagsfield (approval scope + atomic batch), atargetaddress (or null for sender), a gas limit, avaluefield (ETH to transfer in the top-level frame call, non-zero only inSENDERframes), and arbitrary data - Up to 64 frames per transaction
Frame Modes
| Mode | Name | Behavior |
|---|---|---|
| 0 | DEFAULT | Called from ENTRY_POINT. Used for deployment or post-op tasks. |
| 1 | VERIFY | Read-only (STATICCALL semantics). Must call APPROVE. Used for authentication & payment authorization. |
| 2 | SENDER | Called from tx.sender. Requires prior sender_approved. Executes user's intended operations. |
Flags (separate field, introduced by PR #11521):
- Bits 0-1: Approval scope constraint (limits which
APPROVEscope can be used) - Bit 2: Atomic batch flag (groups consecutive SENDER frames into an all-or-nothing batch, SENDER only)
The APPROVE Mechanism
APPROVE is the central innovation. It's an opcode that:
- Terminates the current frame successfully (like
RETURN) - Updates transaction-scoped approval variables
Stack: [offset, length, scope] (with double-approval prevention: once a scope bit is set, it cannot be set again)
Scopes:
0x1: Approve payment — increments nonce, collects gas fees from the account, setspayer_approved = true0x2: Approve execution — setssender_approved = true(only valid whenframe.target == tx.sender)0x3: Approve both execution and payment
Security: Only frame.target can call APPROVE (ADDRESS == frame.target check). sender_approved must be true before payer_approved can be set.
Execution Flow
For each frame transaction:
- Check
tx.nonce == state[tx.sender].nonce - Initialize
sender_approved = false,payer_approved = false - For each frame, compute
resolved_target: ifframe.targetis null, usetx.sender; otherwise useframe.target - Execute each frame sequentially with
CALLVALUE = frame.valueat the top-level call (which is0except inSENDERframes):- DEFAULT mode: caller =
ENTRY_POINT, execute as regular call toresolved_target - VERIFY mode: caller =
ENTRY_POINT, execute as STATICCALL toresolved_target, must callAPPROVEor entire tx is invalid - SENDER mode: requires
sender_approved == true, caller =tx.sender, target =resolved_target. If caller lacks balance forframe.value, the frame reverts (ordinary CALL value-transfer semantics).
- DEFAULT mode: caller =
- After all frames: verify
payer_approved == true, refund unused gas to payer
EOA Default Code
When frame.target has no code, the protocol applies built-in "default code" behavior:
VERIFY mode:
- If
frame.target != tx.sender, revert - Read the approval scope from the flags:
scope = frame.flags & 3. Ifscope == 0, revert - Read first byte of
frame.dataassignature_type - If
0x0(SECP256K1): verify ECDSA signature(v, r, s)againstcompute_sig_hash(tx), enforce low-s, reject failedecrecover - If
0x1(P256): verify P256 signature(r, s, qx, qy), check address =keccak(0x04 | qx | qy)[12:](domain-separated), reject invalid public keys - Call
APPROVE(scope)
SENDER mode:
- If
frame.target != tx.sender, return successfully with empty data (matches an empty-code account; any top-levelframe.valuetransfer has already been applied by the frame call itself). - Otherwise, decode
frame.dataas RLP[[target, value, data], ...]and execute each call withmsg.sender = tx.sender.
DEFAULT mode: Reverts.
This means any EOA can use frame transactions today, no smart contract deployment needed.
Atomic Batching
Consecutive SENDER frames with bit 2 of flags set form an atomic batch:
Frame 0: SENDER (atomic flag set) ─┐
Frame 1: SENDER (flag not set) ─┘ Batch 1
Frame 2: SENDER (atomic flag set) ─┐
Frame 3: SENDER (atomic flag set) ─│ Batch 2
Frame 4: SENDER (flag not set) ─┘If any frame in a batch reverts, the state is restored to before the batch started, and remaining frames in the batch are skipped. This enables safe patterns like "approve + swap" where both must succeed.
Gas Accounting
tx_gas_limit = 15000 (intrinsic) + 475 * len(tx.frames) + calldata_cost(rlp(tx.frames)) + sum(frame.gas_limit)Each frame incurs a per-frame cost of FRAME_TX_PER_FRAME_COST (475 gas) on top of the intrinsic cost. Each frame has its own gas allocation. Unused gas is not carried to subsequent frames. After all frames execute, the total unused gas is refunded to the payer.
The total fee is:
tx_fee = tx_gas_limit * effective_gas_price + blob_feesCanonical Signature Hash
def compute_sig_hash(tx):
for frame in tx.frames:
if frame.mode == VERIFY:
frame.data = Bytes() # elide VERIFY data
return keccak(bytes([FRAME_TX_TYPE]) + rlp(tx))Since mode and flags are now separate fields (PR #11521), the mode check is a direct comparison without masking. The transaction-type prefix aligns EIP-8141 with the EIP-2718 typed-transaction convention and prevents cross-type signature replay (PR #11544, merged Apr 22). Non-VERIFY frame metadata, including a SENDER frame's value, remains covered by this hash.
VERIFY frame data is elided because:
- It contains the signature (can't be part of what's signed)
- Enables future signature aggregation: because VERIFY frames cannot change execution outcomes, a block builder could theoretically strip all VERIFY frames and append a succinct validity proof instead
- Allows sponsor data to be added after sender signs (the sponsor's VERIFY frame target is still covered by the hash)
Mempool Policy
The policy below is the restrictive tier of a two-tier mempool architecture. It is what clients ship first and what FOCIL nodes default to. An expansive tier (ERC-7562 / paymaster-extended) is intended to develop in parallel for use cases that exceed the restrictive policy (privacy protocols, multi-account paymasters).
The public mempool recognizes four validation prefixes:
Self Relay:
[self_verify] # basic
[deploy] → [self_verify] # with account deploymentCanonical Paymaster:
[only_verify] → [pay] # basic
[deploy] → [only_verify] → [pay] # with account deploymentRules enforced during validation prefix:
- Must match one of the four prefixes above
- Sum of validation prefix gas <= 100,000
- Banned opcodes (ORIGIN, TIMESTAMP, BLOCKHASH, CREATE, etc.)
- No state writes (except deterministic deployment)
- No storage reads outside
tx.sender - No calls to non-existent contracts or EIP-7702 delegations
Canonical paymaster: verified by runtime code match, uses reserved balance accounting. The canonical paymaster handles ETH-funded sponsorship only (the paymaster covers gas from its own ETH balance, with no token-level repayment flow). ERC-20 gas repayment is a separate design space with two independent patterns under EIP-8141: a live (offchain) variant that propagates through the public mempool as a non-canonical paymaster, and a permissionless (onchain) variant that does not. See Mempool Strategy → ERC-20 gas repayment: two paymaster patterns.
available = balance(paymaster) - reserved_pending_cost - pending_withdrawal_amountNon-canonical paymaster: limited to 1 pending tx per paymaster in the mempool.
Practical Use Cases
1. Simple EOA Transaction (gas self-paid)
| Frame | Mode | Target | Data |
|---|---|---|---|
| 0 | VERIFY | sender | ECDSA signature |
| 1 | SENDER | target | call data |
Frame 0 verifies the signature and calls APPROVE(0x3). Frame 1 executes.
2. Gas Sponsorship (ETH-funded, canonical paymaster)
| Frame | Mode | Target | Data |
|---|---|---|---|
| 0 | VERIFY | sender | Signature (approve execution) |
| 1 | VERIFY | canonical paymaster | Sponsor data (approve payment) |
| 2 | SENDER | target | User's call |
| 3 | DEFAULT | canonical paymaster | Post-op (settle gas refund) |
The canonical paymaster pays gas in ETH; the user pays nothing.
3. Atomic Approve + Swap
| Frame | Mode | Atomic | Target | Data |
|---|---|---|---|---|
| 0 | VERIFY | - | sender | Signature |
| 1 | SENDER | set | ERC-20 | approve(DEX, amount) |
| 2 | SENDER | not set | DEX | swap(...) |
If the swap reverts, the ERC-20 approval is also reverted.
4. Account Deployment + First Transaction
| Frame | Mode | Target | Data |
|---|---|---|---|
| 0 | DEFAULT | deployer | initcode + salt |
| 1 | VERIFY | sender | Signature |
| 2 | SENDER | target | User's call |
Frame 0 deploys the account; frames 1-2 validate and execute.
5. EOA Paying Gas in ERC-20s
| Frame | Mode | Target | Data |
|---|---|---|---|
| 0 | VERIFY | sender | (0, v, r, s) — approve execution |
| 1 | VERIFY | sponsor | Sponsor signature — approve payment |
| 2 | SENDER | ERC-20 | transfer(sponsor, fees) |
| 3 | SENDER | target | User's call |
The sponsor pays ETH gas; frame 2 repays the sponsor in ERC-20 tokens.
Mempool note: this example is the permissionless ERC-20 paymaster (onchain) variant, where the sponsor's VERIFY frame introspects the next SENDER frame to confirm the ERC-20 transfer before approving payment. That introspection reads the ERC-20 contract's storage and exceeds the restrictive tier, so this shape is consensus-valid on-chain but does not propagate through the public mempool; it routes through the expansive tier, a private mempool, or direct-to-builder submission. A separate live ERC-20 paymaster (offchain) pattern keeps a paymaster signing service in the loop and does propagate through the restrictive public mempool (as a non-canonical paymaster, subject to the 1-pending-tx cap). See Mempool Strategy → ERC-20 gas repayment: two paymaster patterns.
Key Design Properties
- No authorization list: Unlike EIP-7702, doesn't rely on ECDSA for delegation. Compatible with PQ crypto.
- No access list: Future optimizations covered by block-level access lists (EIP-7928).
- Per-frame
valuefield: SENDER frames may transfer ETH natively viaframe.value(added by PR #11534, Apr 16).VERIFYandDEFAULTframes must setvalue = 0, preservingSTATICCALL-like behavior inVERIFYand keepingENTRY_POINTfree from funding transfers. - ORIGIN returns frame caller: Changed from traditional tx.origin behavior (precedent set by EIP-7702).
- Transient storage cleared between frames: TSTORE/TLOAD state doesn't persist across frames.
- Warm/cold state shared across frames: Gas accounting for storage access is shared.
- Requires: EIP-1559, EIP-2718, EIP-4844, EIP-7997 (deterministic deployer).
Related Proposals
| Proposal | Relationship |
|---|---|
| ERC-4337 | 8141 is the native protocol successor, removing the bundler intermediary |
| EIP-7702 | Complementary; 7702 accounts can also use frame transactions. Note: 7702-delegated accounts cannot currently use default code signature verification (gap identified by DanielVF) |
| ERC-7562 | 8141's mempool rules are inspired by but simpler than 7562 (no staking/reputation) |
| EIP-8175 | Competing alternative: flat capabilities + programmable fee_auth, 4 new opcodes |
| EIP-8130 | Coinbase/Base's alternative: declared verifiers (no wallet code exec), 14 PRs, active development. See Competing Standards |
| EIP-7997 | Deterministic deployer, used for account deployment frames |
| EIP-7392 | Signature registry; interoperability PR #11455 was closed without merge on Apr 23 |
Key Takeaway
A frame transaction is a sequence of purpose-labeled sub-calls. The protocol runs validation frames first, refuses to proceed unless they each call APPROVE, then runs execution frames. Default code makes EOAs first-class users without deployment. The mempool only propagates transactions whose validation prefix fits a constrained shape, which is what lets a stateless node safely accept them.
Read Next
- EOA Support — what existing codeless accounts get for free, and how default code replaces EIP-7702 delegation for common cases.
- Feedback Evolution — how the spec got to its current shape through six phases of community review.
- Mempool Strategy — why the validation prefix is the way it is, and how the two-tier mempool handles everything that doesn't fit.
- Competing Standards — how EIP-8141 compares to EIP-8130, EIP-8175, EIP-8202, and the sibling proposals.