EIP-7702 en la practica: construir flujos de cuenta inteligente despues de Pectra
Engineering Team
Lo que permite EIP-7702
EIP-7702, activado con la actualizacion Pectra de Ethereum en marzo de 2025, introduce un nuevo tipo de transaccion que permite a cualquier cuenta de propiedad externa (EOA) establecer un designador de delegacion — un puntero a un contrato inteligente cuyo codigo el EOA adopta temporalmente durante la duracion de una transaccion. Esto significa que su direccion de billetera MetaMask existente puede ejecutar logica arbitraria de contrato inteligente sin desplegar una nueva billetera de contrato ni cambiar su direccion.
Esta es la mejora de UX mas significativa en la historia de Ethereum.
EIP-7702 vs ERC-4337: cuando usar cual
| Caracteristica | EIP-7702 | ERC-4337 |
|---|---|---|
| Nivel | Protocolo (nuevo tipo tx) | Aplicacion (contrato inteligente) |
| Tipo de cuenta | Actualiza EOAs existentes | Requiere nueva billetera de contrato |
| Direccion | Mantiene la direccion EOA existente | Nueva direccion (contrafactual) |
| Persistencia | Delegacion por transaccion | Contrato inteligente permanente |
| Bundler requerido | No (flujo tx estandar) | Si (mempool separado) |
| Overhead de gas | ~20.000 gas para delegacion | ~42.000 gas para validacion UserOp |
| Billeteras soportadas | MetaMask, Coinbase Wallet, Rainbow | Especializadas (Safe, ZeroDev, Biconomy) |
| Ideal para | Actualizar usuarios existentes | Nuevos usuarios, logica compleja |
Marco de decision
Usar EIP-7702 cuando:
- Sus usuarios ya tienen billeteras EOA
- Necesita transacciones por lotes o patrocinio de gas
- Quiere complejidad de integracion minima
Usar ERC-4337 cuando:
- Esta construyendo una nueva billetera o incorporando nuevos usuarios
- Necesita logica de cuenta persistente
- Necesita funciones avanzadas como modulos, plugins o guardianes
Ejemplos de codigo
Transacciones por lotes
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
/// @title BatchExecutor — Objetivo de delegacion EIP-7702 para llamadas por lotes
contract BatchExecutor {
struct Call {
address target;
uint256 value;
bytes data;
}
function executeBatch(Call[] calldata calls) external payable {
for (uint256 i = 0; i < calls.length; i++) {
(bool success, bytes memory result) = calls[i].target.call{
value: calls[i].value
}(calls[i].data);
if (!success) {
assembly {
revert(add(result, 32), mload(result))
}
}
}
}
}
Uso del lado del cliente con ethers.js v6:
import { ethers } from "ethers";
const batchTx = {
type: 4,
to: userAddress,
authorizationList: [{
chainId: 1,
address: BATCH_EXECUTOR_ADDRESS,
nonce: await provider.getTransactionCount(userAddress),
yParity: 0, r: "0x...", s: "0x..."
}],
data: batchExecutorInterface.encodeFunctionData("executeBatch", [[
{
target: USDC_ADDRESS,
value: 0,
data: usdcInterface.encodeFunctionData("approve", [
UNISWAP_ROUTER, ethers.parseUnits("1000", 6)
])
},
{
target: UNISWAP_ROUTER,
value: 0,
data: routerInterface.encodeFunctionData("exactInputSingle", [{
tokenIn: USDC_ADDRESS,
tokenOut: WETH_ADDRESS,
fee: 3000,
recipient: userAddress,
amountIn: ethers.parseUnits("1000", 6),
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
}])
}
]])
};
const tx = await signer.sendTransaction(batchTx);
Patrocinio de gas
/// @title SponsoredExecutor — Patrocinio de gas via EIP-7702
contract SponsoredExecutor {
mapping(address => uint256) public nonces;
function sponsoredExecute(
address user,
address target,
bytes calldata data,
uint256 userNonce,
uint256 deadline,
bytes calldata signature
) external {
require(block.timestamp <= deadline, "Expired");
require(nonces[user] == userNonce, "Invalid nonce");
nonces[user]++;
bytes32 digest = _hashTypedData(user, target, data, userNonce, deadline);
address signer = ECDSA.recover(digest, signature);
require(signer == user, "Invalid signature");
(bool success, bytes memory result) = target.call(data);
require(success, "Call failed");
}
}
Recuperacion social
/// @title RecoveryModule — Recuperacion social via EIP-7702
contract RecoveryModule {
struct RecoveryConfig {
address[] guardians;
uint256 threshold;
uint256 delay;
}
mapping(address => RecoveryConfig) public configs;
mapping(bytes32 => uint256) public recoveryTimestamps;
mapping(bytes32 => uint256) public approvalCounts;
mapping(bytes32 => mapping(address => bool)) public hasApproved;
function setupRecovery(
address[] calldata guardians,
uint256 threshold,
uint256 delay
) external {
require(guardians.length >= threshold, "Invalid threshold");
require(threshold >= 2, "Min 2 guardians");
configs[msg.sender] = RecoveryConfig(guardians, threshold, delay);
}
function approveRecovery(
address account,
address newOwner
) external {
RecoveryConfig memory config = configs[account];
require(_isGuardian(config, msg.sender), "Not a guardian");
bytes32 recoveryId = keccak256(abi.encode(account, newOwner));
require(!hasApproved[recoveryId][msg.sender], "Already approved");
hasApproved[recoveryId][msg.sender] = true;
approvalCounts[recoveryId]++;
if (approvalCounts[recoveryId] >= config.threshold) {
recoveryTimestamps[recoveryId] = block.timestamp + config.delay;
}
}
function executeRecovery(
address account,
address newOwner
) external {
bytes32 recoveryId = keccak256(abi.encode(account, newOwner));
uint256 timestamp = recoveryTimestamps[recoveryId];
require(timestamp > 0 && block.timestamp >= timestamp, "Not ready");
}
}
Estado de integracion de billeteras
MetaMask
MetaMask anadio soporte EIP-7702 en la version 12.4 (septiembre 2025).
Coinbase Wallet
Coinbase Wallet es el adoptante mas agresivo, integrando EIP-7702 en su funcion “Smart Wallet” (lanzada en noviembre 2025):
- Auto-batching — la billetera agrupa automaticamente secuencias approve+swap
- Patrocinio de gas — Coinbase patrocina gas para transacciones menores de $1
- DeFi con un clic — objetivos de delegacion preconstruidos para operaciones DeFi comunes
Rainbow, Rabby y otros
Rainbow anadio soporte en enero 2026. Rabby soporta transacciones type-4 pero sin UI de visualizacion por lotes.
Guia de migracion
Paso 1: Desplegar objetivos de delegacion
Paso 2: Actualizar su frontend
async function supportsEIP7702(provider: ethers.Provider): Promise<boolean> {
try {
const capabilities = await provider.send(
"wallet_getCapabilities", []
);
return capabilities?.atomicBatch?.supported === true;
} catch {
return false;
}
}
Paso 3: Implementar degradacion elegante
async function executeSwap(tokenIn, tokenOut, amount) {
if (await supportsEIP7702(provider)) {
return executeBatchSwap(tokenIn, tokenOut, amount);
} else {
await approve(tokenIn, ROUTER, amount);
return swap(tokenIn, tokenOut, amount);
}
}
Consideraciones de seguridad
Verificacion del objetivo de delegacion
El contrato objetivo de delegacion tiene control total sobre los activos del EOA durante la transaccion.
Mitigacion: Solo delegar a contratos auditados y verificados.
Proteccion contra replay
Las listas de autorizacion EIP-7702 incluyen un nonce para prevenir ataques de replay.
Phishing via delegacion
Mitigacion: Las billeteras deben mostrar claramente a que codigo delega el EOA.
Reentrancia en ejecucion por lotes
bool private locked;
modifier noReentrant() {
require(!locked, "Reentrant");
locked = true;
_;
locked = false;
}
Colisiones de almacenamiento
Usar EIP-7201 (almacenamiento con espacio de nombres):
bytes32 constant STORAGE_SLOT = keccak256(
abi.encode(uint256(keccak256("batch.executor.storage")) - 1)
) & ~bytes32(uint256(0xff));
Preguntas frecuentes
EIP-7702 cambia mi direccion de billetera?
No. Su direccion EOA permanece igual.
Puedo usar EIP-7702 en L2?
Si. Todos los L2 principales (Arbitrum, Base, Optimism, zkSync) han adoptado EIP-7702.
Que pasa si el objetivo de delegacion tiene un bug?
El dano se limita a una sola transaccion. Sin embargo, dentro de esa transaccion, el objetivo tiene acceso completo a los activos del EOA.
Es EIP-7702 compatible con billeteras de hardware?
Si. Ledger anadio soporte en firmware 2.3.0 (diciembre 2025).
Cuanto gas ahorra EIP-7702?
Para un flujo tipico de approve+swap, EIP-7702 ahorra aproximadamente 40-50% de gas. Para operaciones DeFi complejas de multiples pasos, el ahorro puede alcanzar el 60-70%.