# EIP-7702 — The End of EOA vs Contract Account Distinction
EIP-7702, included in Ethereum's Pectra upgrade, introduces a revolutionary feature: EOAs (Externally Owned Accounts) can temporarily delegate their behavior to smart contracts without converting to contract accounts.
The Problem: Two Account Types
Ethereum has two account types:
EOA (Externally Owned Account)
- Controlled by private key
- Can initiate transactions
- No custom logic
- Fixed 21,000 gas for transfers
Contract Account
- Controlled by code
- Cannot initiate transactions (needs EOA)
- Custom logic (multisig, social recovery, etc.)
- Higher gas costs
This dichotomy has plagued UX for years.
What EIP-7702 Changes
EIP-7702 introduces a new transaction type that lets EOAs temporarily set their code to a smart contract's logic for the duration of a transaction.
Before EIP-7702
// User must deploy a separate contract wallet
contract SmartWallet {
address public owner;
function execute(address to, uint256 value, bytes calldata data)
external
{
require(msg.sender == owner, "Unauthorized");
(bool success,) = to.call{value: value}(data);
require(success, "Execution failed");
}
}
// User must migrate funds from EOA → SmartWallet
After EIP-7702
// User's EOA can temporarily use SmartWallet logic
// No migration, no new address, no fund transfer
How It Works
Transaction Structure
{
type: 4, // EIP-7702 transaction type
authorizationList: [
{
chainId: 1,
address: "0x...", // Smart wallet contract
nonce: 0,
v: 27,
r: "0x...",
s: "0x..."
}
],
to: "0x...",
data: "0x...",
// ... standard fields
}
The authorizationList tells the EVM: "For this transaction, execute my EOA using the code at address."
Use Cases
1. Batch Transactions
contract BatchExecutor {
function batchExecute(
address[] calldata targets,
bytes[] calldata datas
) external {
for (uint256 i = 0; i < targets.length; i++) {
(bool success,) = targets[i].call(datas[i]);
require(success, "Batch failed");
}
}
}
EOA delegates to BatchExecutor for one transaction, executes multiple calls, then reverts to normal EOA.
2. Gas Sponsorship
contract GaslessExecutor {
function executeWithPaymaster(
address target,
bytes calldata data,
address paymaster
) external {
require(msg.sender == paymaster, "Unauthorized");
(bool success,) = target.call(data);
require(success, "Execution failed");
}
}
Third parties pay gas for user transactions.
3. Social Recovery
contract RecoverableWallet {
mapping(address => bool) public guardians;
uint256 public requiredGuardians = 2;
function recover(address newOwner, bytes[] calldata signatures)
external
{
require(verifyGuardians(signatures), "Insufficient guardians");
// Transfer control to newOwner
assembly {
sstore(0, newOwner)
}
}
}
Users can recover EOAs without pre-deploying contract wallets.
4. Session Keys
contract SessionKeyWallet {
mapping(address => SessionKey) public sessions;
struct SessionKey {
uint256 expiry;
uint256 spendLimit;
uint256 spent;
}
function executeWithSession(
address sessionKey,
address target,
uint256 value,
bytes calldata data
) external {
SessionKey storage session = sessions[sessionKey];
require(block.timestamp < session.expiry, "Expired");
require(session.spent + value <= session.spendLimit, "Limit");
session.spent += value;
(bool success,) = target.call{value: value}(data);
require(success, "Failed");
}
}
Apps get temporary spending permissions without full wallet access.
EIP-7702 vs EIP-3074 vs EIP-4337
| Feature | EIP-7702 | EIP-3074 | EIP-4337 |
|---------|----------|----------|----------|
| EOA keeps address | ✅ | ✅ | ❌ (new wallet) |
| Temporary delegation | ✅ | ❌ (permanent) | N/A |
| Native protocol change | ✅ | ✅ | ❌ (opt-in) |
| Backwards compatible | ✅ | ⚠️ | ✅ |
EIP-7702 is the safest path forward because delegation is temporary and user-controlled.
Security Considerations
1. Malicious Delegation Target
// Malicious contract
contract DrainWallet {
function execute() external {
// Drains all ETH from delegating EOA
payable(attacker).transfer(address(this).balance);
}
}
Mitigation: Only delegate to audited, trusted contracts. Wallets will maintain whitelists.
2. Nonce Replay
The authorizationList includes a nonce to prevent replay attacks:
authorizationList: [{
nonce: 5, // Must match user's authorization nonce
// ...
}]
3. Chain ID Validation
authorizationList: [{
chainId: 1, // Only valid on mainnet
// ...
}]
Prevents cross-chain replay.
Implementation Example
// Simple batch executor for EIP-7702
contract SimpleExecutor {
function execute(
address[] calldata targets,
uint256[] calldata values,
bytes[] calldata datas
) external payable {
require(targets.length == values.length, "Length mismatch");
require(targets.length == datas.length, "Length mismatch");
for (uint256 i = 0; i < targets.length; i++) {
(bool success,) = targets[i].call{value: values[i]}(datas[i]);
require(success, "Call failed");
}
}
}
Users delegate to this contract for one transaction to batch multiple calls.
Timeline
- Proposed: 2023
- Accepted: Pectra upgrade (2024)
- Mainnet: Expected Q2 2026
- Wallet Support: MetaMask, Rabby, Frame integrating
Key Takeaways
EIP-7702 bridges the gap between EOAs and smart contract wallets without forcing users to migrate. It's account abstraction without the abstraction overhead.