Account abstraction's pitch was always seductive and always kind of dead on arrival: throw away your wallet, deploy a contract, pay extra gas, and route everything through a thing called a bundler. Almost nobody did it. EIP-7702 won by asking for far less: keep your address, keep your key, and let your account borrow a contract's brain for the length of a transaction. Here's the part that surprised me when I actually sat down and mapped the feature set: once you have that, ERC-4337 is optional way more often than the diagrams admit.
I work on agent wallets at B3, which means I spend a lot of time thinking about how much rope to hand an autonomous piece of software before it does something dumb with your funds. 7702 turned out to be the cleanest primitive I've found for that. But before I get to session keys and capability scoping, let's do the boring part right: what 7702 actually is, and where the line to 4337 actually sits.
The old story on account abstraction was a migration. You had an EOA (externally owned account, the kind MetaMask generates), and if you wanted programmable logic, batched calls, spending limits, social recovery, you deployed a new smart contract wallet. New address. Users had to bridge funds over, redo their approvals, get comfortable with a UserOperation flowing through a bundler into an EntryPoint contract instead of a plain transaction. It worked, and ERC-4337 is genuinely clever engineering, but the activation energy was brutal. Most wallets stayed EOAs. Most users never touched a smart account.
EIP-7702 flips the ask. It doesn't give you a new account, it lets your existing one temporarily become something else. Same address. Same private key. Same nonce sequence you've been using since 2021. The only thing that changes is what code runs when someone calls your account. It shipped live in the Pectra upgrade on May 7, 2025, and by early 2026 the wallets that actually matter (MetaMask, Rabby, Trust, Coinbase Wallet) had integrated it. The account abstraction stack as a whole is well past hundreds of millions of UserOperations processed and tens of millions of smart accounts created, per public AA dashboards, though those numbers mix 4337 and 7702 flows and drift fast so don't quote me on the exact figure a year from now.
This is not a spec preview. It's deployed, boring, production infrastructure, and most engineers I talk to still carry the pre-7702 mental model where "smart account" is a synonym for "4337 plus a bundler." That mental model is now wrong in a specific, useful way.
Here's the mechanism. 7702 introduces a new transaction type that carries a list of authorizations. Each authorization is a small signed tuple:
authorization = (chain_id, address, nonce)
magic = 0x05
digest = keccak256( magic || rlp(chain_id, address, nonce) )
signer = ecrecover(digest, y_parity, r, s) // must equal the EOA being upgraded
You sign that tuple with the EOA's existing key. address is the delegate contract, the piece of logic you want your account to run as. nonce binds the authorization to your account's current state so it can't be replayed once your nonce moves on. chain_id is worth staring at: set it to a real chain id and the authorization only works there. Set it to 0 and it's valid on any chain, which is convenient if you want one signature to upgrade you everywhere, and a genuinely sharper piece of attack surface if that signature ever leaks or gets phished.
Once the authorization lands onchain, your account's code field stops being empty and becomes a 23-byte pointer:
code(EOA) = 0xef0100 || delegateAddress
That's the whole trick. Your address didn't change. Your storage didn't move. But every call to your account now executes the delegate's bytecode with your account's context. You're an EOA wearing a smart contract's clothes, and you can take the clothes off (or swap them) by signing a new authorization.
Say the foot-gun part plainly, because it's the one thing about 7702 that's genuinely scarier than a normal transaction signature: signing an authorization is signing away what code runs as you. A normal signature authorizes one action. A 7702 authorization authorizes an entire program to act on your behalf until you revoke it. If that delegate contract has a bug or a backdoor, or if you got phished into signing an authorization for a malicious address, the blast radius is your whole account, not one transaction. Treat 7702 signature requests with more suspicion than a normal "sign this tx," not less.
The first thing everyone does with 7702 is batching, because it's the most obviously useful thing and it needs almost nothing extra. One signature, N calls, atomic. No more "approve, wait for confirmation, then swap, and pray nothing reverts in between leaving you with a stranded approval."
Here's what that looks like with viem:
// 1) authorize: point my EOA's code at the delegate, for this chain
const auth = await walletClient.signAuthorization({
account, // my existing EOA, same address, same key
contractAddress: DELEGATE, // the smart-account logic I'm delegating to
// chainId omitted -> bound to this chain; chainId: 0 would be cross-chain (sharper edge)
});
// 2) one signature, two calls, atomic: approve + swap, no stranded approval
const hash = await walletClient.writeContract({
address: account.address, // I am now executing as the delegate
abi: delegateAbi,
functionName: "executeBatch",
args: [[
{ to: USDC, data: encodeApprove(ROUTER, amount) },
{ to: ROUTER, data: encodeSwap(USDC, WETH, amount) },
]],
authorizationList: [auth],
});That's it. No bundler, no EntryPoint, no mempool of UserOperations. A relayer (which can just be a normal RPC node you're paying gas to, or the dApp's own backend) submits a normal transaction with the authorization attached, and your account executes both calls as one unit. This is the point where most engineers' 4337 instincts should start twitching. If batching doesn't need a bundler, what actually does?
This is the part of the post I actually wanted to write. Say you want some subset of these features on your wallet:
Here's the rule, stated as a truth table:
needs_4337(F) = (permissionless_sponsorship in F) OR (multiuser_aggregation in F)
Everything else, batching, scoped session-key permissions, and gas sponsorship by a trusted relayer you have a relationship with, is satisfiable with 7702 alone: a delegate contract plus a relayer that submits the transaction and eats the gas. You don't need a mempool of UserOperations. You don't need an EntryPoint contract doing validation and execution in two phases. You don't need a bundler bidding to include your operation.
The common consumer cases, the ones that make up 95% of what people actually want from "account abstraction," are all 7702-only:
You reach for 4337 specifically when you want a permissionless market of sponsors competing to pay for your gas, or when you need to amortize many different users' operations into one onchain transaction to save on calldata and base fees. Those two things are the bundler's actual job. Everything else people were using 4337 for was really just "I wanted a smart contract wallet," and 7702 gives you that for free, on the address you already had.
This is the subtlest fork so worth its own beat. "My app pays gas for my users" and "anyone can bid to pay gas for anyone's transaction" sound similar and require completely different infrastructure.
If you're a dApp and you want to sponsor gas for your own users, 7702 covers it entirely. You run a relayer, your users sign 7702 authorizations plus their intended calls, your relayer bundles them into a normal transaction and pays the gas. You trust your own relayer because you built it. No mempool of intents, no bundler auctioning inclusion, no EntryPoint doing prepay/refund accounting. It's a backend service with a hot wallet, which is a pattern that predates account abstraction entirely.
The moment you want a permissionless market, where anyone can run a sponsor node and bid to pay gas for transactions they don't control and haven't pre-vetted, you need the machinery 4337 built specifically for that: UserOperations sitting in an alt-mempool, bundlers competing to include them, an EntryPoint contract enforcing the validation rules so a malicious sponsor can't get griefed and a malicious user can't get free compute. That's a coordination problem between mutually distrusting parties, and it's the actual reason the bundler architecture exists. If you don't have mutually distrusting parties, you don't need it.
Passkeys are the other feature people bolt onto smart accounts, and they expose a wart in the EVM that has nothing to do with 7702 versus 4337. Ethereum has a native precompile for secp256k1 signature recovery (ecrecover, roughly 3000 gas), because that's the curve every EOA uses. Passkeys, Face ID, Secure Enclave, WebAuthn, all of that runs on secp256r1, a different curve, and the EVM has no native support for it.
Verify an r1 signature in plain Solidity, doing the elliptic curve math in bytecode, and you're looking at hundreds of thousands of gas. That's not a rounding error, that's the difference between a feature and a non-starter.
RIP-7212 fixes this the same way 7702 fixed the wallet migration problem: it doesn't change the curve, it just adds a precompile.
// sketch: verifying a passkey (secp256r1 / WebAuthn) signature via RIP-7212
address constant P256VERIFY = 0x0000000000000000000000000000000000000100;
function verifyPasskey(bytes32 hash, uint256 r, uint256 s, uint256 x, uint256 y)
internal
view
returns (bool)
{
(bool ok, bytes memory result) = P256VERIFY.staticcall(
abi.encode(hash, r, s, x, y)
);
return ok && result.length > 0 && abi.decode(result, (uint256)) == 1;
}Where the precompile is live, that call costs a few thousand gas, roughly the same order as ecrecover. Where it isn't (deployment varies by chain and L2, so check before you promise anyone "passkeys are cheap"), you're back to the bytecode path and its six-figure gas bill. This is what actually makes passkey-native smart accounts economically viable, and it's an orthogonal upgrade to 7702, not a part of it, which is a distinction I see conflated constantly.
This is the part I actually care about, because it's the substrate for the agent wallet work at B3. A signature, by itself, is a yes/no. A session key, scoped properly inside a 7702 delegate, turns that yes/no into a capability: this key can call these functions, up to this much value, until this timestamp, and nothing else.
struct Permission {
bytes4[] allowedSelectors;
uint256 spendCap;
uint256 spent;
uint256 validUntil;
}
mapping(address => Permission) public permissions;
function execute(address to, bytes calldata data, uint256 value, bytes calldata sig)
external
{
address key = recoverSigner(to, data, value, sig);
Permission storage p = permissions[key];
require(block.timestamp < p.validUntil, "session expired");
require(isSelectorAllowed(p.allowedSelectors, bytes4(data[:4])), "selector not permitted");
require(p.spent + value <= p.spendCap, "spend cap exceeded");
p.spent += value;
(bool ok, ) = to.call{value: value}(data);
require(ok, "call failed");
}That's the whole shape of a bounded agent wallet. Give an autonomous process a session key that can only call swap on one router, up to $50 a day, for the next 24 hours, and it doesn't matter if the agent hallucinates, gets prompt injected, or just has a bug. The blast radius is capped by the contract, not by hoping the model behaves. Because the underlying account is a plain 7702-upgraded EOA, all your existing wallet tooling (block explorers, portfolio trackers, the wallet extension your team already uses) keeps working. You didn't create a weird new account type, you added a leash to the one you already had.
The honest framing is that 7702 didn't kill 4337, it drew a sharp line around the two things 4337 is actually for (permissionless sponsorship markets, multi-user aggregation) and handed everything else back to the wallet in your pocket. For agents specifically, that line matters more than it looks like it should: a bounded session key on a same-address EOA is the cleanest "give the bot a leash" primitive the ecosystem has produced. I'm building against exactly that at B3 right now. If you've scoped a key down to three selectors and a spend cap and let an agent loose on it, I want to hear how it went.
A few things worth saying plainly instead of burying in a footnote. 7702 authorizations are strictly more dangerous to sign than a normal transaction, you're authorizing a program to act as your account rather than authorizing one action, and phishing kits targeting 7702 signature requests are already a real category of attack. Cross-chain authorizations (chain_id = 0) widen that surface further; use them deliberately, not by default.
"4337 is optional" is a statement about capability, not a recommendation to rip it out of production systems. If you're already running a permissionless paymaster market or aggregating thousands of users' operations to amortize calldata costs, 4337 is the correct tool and 7702 doesn't replace it. The claim in this post is about the boundary, not about 4337 being obsolete.
Adoption numbers for account abstraction move fast and get reported inconsistently across dashboards that mix 4337 and 7702 activity together. Treat any specific figure, including the ones above, as a snapshot, and check a live dashboard before you cite one in a deck.
And RIP-7212 deployment is chain by chain. "Passkeys are cheap now" is true exactly where the precompile has shipped, and false everywhere else. Check before you promise it.