Ir al contenido principal
BlockchainMar 28, 2026

EIP-7702 en la practica: construir flujos de cuenta inteligente despues de Pectra

OS
Open Soft Team

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

CaracteristicaEIP-7702ERC-4337
NivelProtocolo (nuevo tipo tx)Aplicacion (contrato inteligente)
Tipo de cuentaActualiza EOAs existentesRequiere nueva billetera de contrato
DireccionMantiene la direccion EOA existenteNueva direccion (contrafactual)
PersistenciaDelegacion por transaccionContrato inteligente permanente
Bundler requeridoNo (flujo tx estandar)Si (mempool separado)
Overhead de gas~20.000 gas para delegacion~42.000 gas para validacion UserOp
Billeteras soportadasMetaMask, Coinbase Wallet, RainbowEspecializadas (Safe, ZeroDev, Biconomy)
Ideal paraActualizar usuarios existentesNuevos 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%.