Why
A protocol-native preview of what this project's application-layer AA demos (session keys, atomic batching) do today with smart contracts and delegation — EIP-8141 proposes moving those same properties into Ethereum's base transaction format itself.
How it works
Not a working demo by necessity: EIP-8141 defines a new transaction type where a single transaction carries a sequence of frames (a VERIFY frame for signature/fee authorization, then one or more EXECUTE frames) instead of one implicit call — but no client or RPC can send this transaction type yet, since it requires execution-layer support the network doesn't have. As of writing it's only "considered for inclusion" in a future fork, so this stays a diagram/explainer page rather than a live demo.
The ACDE readout, and the two kinds of "no" (2026-08-28)
The call was set to conclude between this proposal and EIP-8130, and as of writing no public readout confirms an outcome. What the developer timeline did in that gap is the useful part: the argument moved off the content and onto the weight. The recurring sentence was "the direction is right, but this is too heavy for the current stage" — and that is a schedule objection, not a technical one.
The distinction is worth holding onto because the two look identical from outside and demand opposite responses:
| The objection | What it actually says | What you do next |
|---|---|---|
| "This is wrong" | the design does not achieve the goal | change the design |
| "This is not now" | the design is right and too large to land here | split the scope |
Hearing the second and redesigning is wasted work; hearing the first and merely deferring is worse. The question to ask in the room is which one you are being given — and the tell is usually whether the objection survives when you make the proposal smaller.
Review clarification
The basic concept, from zero
Ethereum has two kinds of accounts, and only the dumb one can act. An EOA can start transactions, but its rules are frozen into the protocol: one ECDSA key, one sequential nonce, fees paid by the sender in ETH, one call per transaction — lose the key, lose the account. A contract account is fully programmable but cannot initiate anything. Account abstraction is the project of erasing that split: let the account that acts also be programmable — in who may sign, how fees are paid, and what one transaction may contain.
Today's AA is an emulation built one layer up: ERC-4337 rebuilt a transaction system out of contracts (UserOperations, bundlers, an EntryPoint), and EIP-7702 lets an EOA borrow contract code. It works, at the price of extra actors, extra gas, and extra trust surfaces. EIP-8141 proposes ending the emulation: a new base transaction type made of frames — one VERIFY frame ("here is the proof this is authorized, and how fees get paid") followed by one or more EXECUTE frames (the calls, atomically: all or nothing). The two jobs every transaction always had — prove it's allowed, then do things — become explicit, separate, programmable parts of the format itself.
The same action, three generations of code
Today, the node's checks are hard-coded and unchangeable:
assert(ecrecover(tx.sig) == tx.from); // exactly one ECDSA key
assert(tx.nonce == account.nonce); // exactly one nonce
assert(balance[tx.from] >= gas * price); // sender pays, ETH only
call(tx.to, tx.data); // exactly one call
ERC-4337 replays those checks inside a contract — EntryPoint.handleOps loops over UserOperations calling account.validateUserOp and then account.execute, with a bundler carrying the batch and a paymaster optionally paying. Every hop is the emulation tax.
Under EIP-8141 the node itself runs the loop the EntryPoint used to fake:
tx = { type: FRAME_TX, frames: [
{ kind: VERIFY, target: myAccount,
input: { scheme: "p256-passkey", proof, feeToken: USDC } },
{ kind: EXECUTE, target: USDC, data: approve(dex, 100e6) },
{ kind: EXECUTE, target: dex, data: swap(USDC, ETH, 100e6) },
]}
and the account's own verify code is where the features live — a session key, natively:
function verify(Proof p, Frame[] fs) external view returns (bool) {
if (p.signer == owner) return checkSig(p);
Session s = sessions[p.signer]; // a temporary key
require(block.timestamp < s.expiry); // ...that expires
require(allCallsTo(fs, s.allowedContract)); // ...only this app
require(totalValue(fs) <= s.perTxLimit); // ...small amounts
return checkSig(p);
}
Who it makes happy
| Who | Today's pain | With frame transactions |
|---|---|---|
| Users | Seed phrase or bust; approve-then-swap leaves a dangling approval; need ETH first | Passkey as the native key; approve+swap atomic; fees in tokens or sponsored |
| Wallet builders | Run or rent bundler + paymaster infrastructure | Those services largely disappear; the chain validates directly |
| dApp/game/agent builders | Session keys need a smart-wallet stack per provider | Bounded delegation is a base-layer feature |
| The protocol | A parallel 4337 mempool; EntryPoint as a shared choke point | One canonical mempool, no single trusted contract |
Concrete situations it improves: the dangling-approve accident (two EXECUTE frames make approval-without-swap impossible); onboarding without ETH (a sponsor pays inside VERIFY); lost-key recovery as account logic instead of a custodian's feature; the post-quantum migration (signature schemes become account code to upgrade, not protocol constants to fork); and agent commerce (an AI agent holding a session key with expiry, an allowed contract, and a per-transaction limit is delegation with a chosen blast radius).
The honest cost — and most of why ACDE said "too heavy": the VERIFY frame is arbitrary code deciding mempool validity before anyone has paid, so it must be strictly gas-bounded and cheaply re-checkable, or invalid transactions become a free DoS vector.
"Then ERC-4337 could be sunsetted?" (jay, 2026-09-03)
Right direction, wrong tense. 4337 was always the stopgap — enshrinement was the stated endgame, and 8141's VERIFY/EXECUTE split is essentially validateUserOp/execute promoted into the transaction format. So 4337 doesn't get killed; it gets absorbed — the concepts survive as the contracts empty out. But the sunset is a decade-scale decay, for four reasons: nothing on Ethereum is ever removed (the EntryPoint is immortal, and millions of deployed and counterfactual smart-account addresses don't migrate themselves); the native-AA competition is unsettled (8141 vs 8130 vs Tempo — a workaround with users beats a proposal without a fork date); L2s break the symmetry (4337 works identically on every EVM chain today, while native AA lands chain by chain — cross-chain wallets may be its last stronghold); and the bundler/paymaster vendors will pivot to the new rails rather than defend the old ones, accelerating the very sunset that obsoletes them. As strategy — don't build a moat out of 4337 plumbing — the sentence is already true. As a forecast — traffic to zero — add ten years and an asterisk.
Progress (2026-09-08)
Vitalik's 9/6 Frames update reported movement: on the 8/27 call the proposal went CFI → SFI (considered → scheduled for inclusion), slotted for Hegotá (targeted 2027 Q2) alongside FOCIL. SFI is an assignment, not a completion — the tell to watch after the 9/10 devnet-priority list is whether a devnet actually attaches to 8141. Two framing notes. The headline 'pay gas in stablecoins' is a consequence, not the design: the design separates the three jobs that today share one signer — authorization, gas payment, execution — and stablecoin fees are what fall out once gas payment is its own strand inside VERIFY. And the competition is asymmetric on timing: rival EIP-8130 targets a Base deployment in September, so the L2 implementation becomes real before the L1 standard does — a workaround with users, again, outrunning a proposal with a fork date.
The same runtime-vs-spec point the invariant card makes applies to the objection here: liquid-issuance-not-authorization is why an enshrined VERIFY frame must be strictly gas-bounded and cheaply re-checkable — arbitrary validity code that runs before anyone pays is a premise the network cannot afford to trust unconditionally.
Related code
# ERC-8141 — a transaction carries a sequence of frames instead of one implicit call:
# a VERIFY frame (signature/fee authorization) followed by one or more EXECUTE frames.
# Simulates the frame structure and runs it in sequence, aborting if VERIFY fails.
from dataclasses import dataclass, field
from typing import Callable
@dataclass
class Frame:
kind: str # "VERIFY" or "EXECUTE"
description: str
run: Callable[[dict], bool]
@dataclass
class FrameTransaction:
frames: list[Frame] = field(default_factory=list)
def execute(self, context: dict) -> bool:
for frame in self.frames:
print(f" [{frame.kind}] {frame.description} ...", end=" ")
ok = frame.run(context)
print("OK" if ok else "FAILED")
if frame.kind == "VERIFY" and not ok:
print(" -> VERIFY frame failed: transaction aborted before any EXECUTE frame runs.")
return False
if frame.kind == "EXECUTE" and not ok:
print(" -> EXECUTE frame failed: transaction reverts.")
return False
return True
def verify_signature_and_fee(ctx: dict) -> bool:
return ctx.get("signature_valid", False) and ctx.get("balance", 0) >= ctx.get("max_fee", 0)
def execute_transfer(ctx: dict) -> bool:
ctx["balance"] -= ctx["transfer_amount"]
return ctx["balance"] >= 0
def execute_approve(ctx: dict) -> bool:
ctx["allowance"] = ctx.get("allowance", 0) + ctx["approve_amount"]
return True
def build_transaction() -> FrameTransaction:
return FrameTransaction(frames=[
Frame("VERIFY", "signature + fee authorization", verify_signature_and_fee),
Frame("EXECUTE", "transfer 50 tokens", execute_transfer),
Frame("EXECUTE", "approve spender for 20 tokens", execute_approve),
])
if __name__ == "__main__":
print("EIP-8141 Frame Transaction — VERIFY then EXECUTE, EXECUTE, ...\n")
print("Case 1: valid signature, sufficient balance")
ctx = {"signature_valid": True, "balance": 100, "max_fee": 5, "transfer_amount": 50, "approve_amount": 20}
ok = build_transaction().execute(ctx)
print(f"Transaction succeeded: {ok}, final state: {ctx}\n")
print("Case 2: invalid signature — VERIFY frame blocks all EXECUTE frames")
ctx = {"signature_valid": False, "balance": 100, "max_fee": 5, "transfer_amount": 50, "approve_amount": 20}
ok = build_transaction().execute(ctx)
print(f"Transaction succeeded: {ok}")
Where it lands in Jayverse
- Rabbit: track 8141's progress, don't redesign around it yet. With the proposal at SFI targeting Hegotá (~2027 Q2), keep rabbit's ERC-4337/EIP-7702 session-key and mandate stack as the near-term plan — treat native frame transactions as a decade-scale migration to watch, not a reason to pause current AA work.
- Rabbit: prototype a session key as a VERIFY frame when a testnet exists. Once any client exposes frame transactions on a public testnet, build a minimal session-key mandate (expiry, allowed contract, per-tx limit) as VERIFY-frame logic — it maps directly onto rabbit's existing mandate model and is the cheapest way to validate the migration path early.
- Devnet: gas-bound any custom validateUserOp-style check the same way. Since an enshrined VERIFY frame must be strictly gas-bounded and cheaply re-checkable to avoid a free DoS vector, apply that same bound-and-recheck discipline to rabbit's own account-validation code running on devnet today.
Key expressions
| Expression | 뜻 · 쓰이는 자리 |
|---|---|
| considered for inclusion | 포함이 검토 중인 단계 · 제안이 아직 채택 전, 논의 대상일 뿐임을 표현. "it's only "considered for inclusion" in a future fork" |
| readout | (회의 후) 결과 요약 보고 · 콜이 끝난 뒤 공개되는 결론 요약을 가리킴. "no public readout confirms an outcome" |
| sunset (동사) | 단계적으로 폐지하다, 퇴장시키다 · 오래된 시스템을 서서히 없앤다는 뜻. "Then ERC-4337 could be sunsetted?" |
| stopgap | 임시방편, 땜질용 조치 · 영구적 해법이 나올 때까지 쓰는 수단. "4337 was always the stopgap" |
| at the price of | ~을 대가로 치르고 · 이득을 얻는 동시에 지불하는 비용을 말할 때. "at the price of extra actors, extra gas" |
| dangling approval | 붕 뜬(미완료) 승인 상태 · approve만 되고 후속 실행은 안 된 상태. "leaves a dangling approval" |
| seed phrase or bust | 시드 문구 아니면 끝장 · "~or bust"는 양자택일을 강조하는 구어체. "Seed phrase or bust" |
| outrun | ~을 앞지르다, 추월하다 · 경쟁 구도에서 한쪽이 다른 쪽보다 빨리 실현될 때. "outrunning a proposal with a fork date" |
| moat | (경쟁 우위의) 해자, 진입장벽 · 사업 전략에서 방어선을 비유. "don't build a moat out of 4337 plumbing" |
| fall out (of) | ~에서 자연스럽게 파생되다/도출되다 · 설계의 부산물로 생기는 결과. "stablecoin fees are what fall out" |
| AA | 계정 추상화(Account Abstraction) · 계정이 서명·수수료 지불·실행 방식을 프로그래밍 가능하게 만드는 개념. "Account abstraction is the project of erasing that split" |
| EOA | 외부 소유 계정(Externally Owned Account) · 개인키로 제어되는 기본 이더리움 계정, 스스로 실행은 못함. "An EOA can start transactions, but its rules are frozen" |
| ECDSA | 타원곡선 전자서명 알고리즘(Elliptic Curve Digital Signature Algorithm) · EOA가 쓰는 유일한 서명 방식을 가리킴. "one ECDSA key, one sequential nonce" |
| ERC-4337 | 계정 추상화 표준(Ethereum Request for Comments 4337) · 컨트랙트로 AA를 흉내내는 현재 표준, 번들러·페이마스터를 도입. "ERC-4337 rebuilt a transaction system out of contracts" |
| EIP-7702 | 이더리움 개선 제안 7702(Ethereum Improvement Proposal 7702) · EOA가 컨트랙트 코드를 빌려 쓰게 하는 제안. "EIP-7702 lets an EOA borrow contract code" |
| ACDE | 이더리움 실행계층 코어 개발자 콜(All Core Devs – Execution) · 프로토콜 변경을 논의하는 공식 회의체. "the ACDE readout, and the two kinds" |
| CFI → SFI | 포함 검토 → 포함 예정(Considered For Inclusion → Scheduled For Inclusion) · 이더리움 로드맵에서 제안의 채택 단계 전환. "the proposal went CFI → SFI" |
| FOCIL | 강제 포함 리스트(Fork-Choice enforced Inclusion List) · 검열 저항을 위한 별도 로드맵 제안, 8141과 같은 시기 배치 예정. "slotted for Hegotá (targeted 2027 Q2) alongside FOCIL" |