Aller au contenu principal
BlockchainMar 28, 2026

EIP-7702 en pratique : construire des flux de comptes intelligents apres Pectra

OS
Open Soft Team

Engineering Team

Ce que permet EIP-7702

EIP-7702, active avec la mise a jour Pectra d’Ethereum en mars 2025, introduit un nouveau type de transaction qui permet a tout compte externe (EOA) de definir un designateur de delegation — un pointeur vers un contrat intelligent dont le code est temporairement adopte par l’EOA pendant la duree d’une transaction. Cela signifie que votre adresse de portefeuille MetaMask existante peut executer une logique de contrat intelligent arbitraire sans deployer un nouveau portefeuille de contrat ni changer votre adresse.

C’est l’amelioration UX la plus significative de l’histoire d’Ethereum.

EIP-7702 vs ERC-4337 : quand utiliser lequel

CaracteristiqueEIP-7702ERC-4337
NiveauProtocole (nouveau type tx)Application (contrat intelligent)
Type de compteMet a jour les EOA existantsNecessite un nouveau portefeuille de contrat
AdresseConserve l’adresse EOA existanteNouvelle adresse (contrefactuelle)
PersistanceDelegation par transactionContrat intelligent permanent
Bundler requisNon (flux tx standard)Oui (mempool separe)
Surcharge gas~20 000 gas pour delegation~42 000 gas pour validation UserOp
Portefeuilles supportesMetaMask, Coinbase Wallet, RainbowSpecialises (Safe, ZeroDev, Biconomy)
Ideal pourMettre a jour les utilisateurs existantsNouveaux utilisateurs, logique complexe

Cadre de decision

Utilisez EIP-7702 quand :

  • Vos utilisateurs ont deja des portefeuilles EOA
  • Vous avez besoin de transactions par lots ou de parrainage de gas
  • Vous voulez une complexite d’integration minimale

Utilisez ERC-4337 quand :

  • Vous construisez un nouveau portefeuille ou integrez de nouveaux utilisateurs
  • Vous avez besoin d’une logique de compte persistante
  • Vous avez besoin de fonctionnalites avancees

Exemples de code

Transactions par lots

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

/// @title BatchExecutor — Cible de delegation EIP-7702 pour les appels par lots
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))
                }
            }
        }
    }
}

Utilisation cote client avec 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);

Parrainage de gas

/// @title SponsoredExecutor — Parrainage 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");
    }
}

Recuperation sociale

/// @title RecoveryModule — Recuperation sociale 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");
    }
}

Statut d’integration des portefeuilles

MetaMask

MetaMask a ajoute le support EIP-7702 dans la version 12.4 (septembre 2025).

Coinbase Wallet

Coinbase Wallet est l’adopteur le plus agressif, integrant EIP-7702 dans sa fonctionnalite “Smart Wallet” (lancee en novembre 2025) :

  • Auto-batching — le portefeuille regroupe automatiquement les sequences approve+swap
  • Parrainage de gas — Coinbase parraine le gas pour les transactions de moins de 1 $
  • DeFi en un clic — cibles de delegation pre-construites pour les operations DeFi courantes

Rainbow, Rabby et autres

Rainbow a ajoute le support en janvier 2026. Rabby supporte les transactions type-4 mais sans l’interface de visualisation par lots.

Guide de migration

Etape 1 : Deployer les cibles de delegation

Etape 2 : Mettre a jour votre 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;
    }
}

Etape 3 : Implementer la degradation gracieuse

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);
    }
}

Considerations de securite

Verification de la cible de delegation

Le contrat cible de delegation a un controle total sur les actifs de l’EOA pendant la transaction.

Attenuation : Ne deleguez qu’a des contrats audites et verifies.

Protection contre le rejeu

Les listes d’autorisation EIP-7702 incluent un nonce pour prevenir les attaques par rejeu.

Phishing via delegation

Attenuation : Les portefeuilles doivent afficher clairement a quel code l’EOA delegue.

Reentrance dans l’execution par lots

bool private locked;

modifier noReentrant() {
    require(!locked, "Reentrant");
    locked = true;
    _;
    locked = false;
}

Collisions de stockage

Utilisez EIP-7201 (stockage namespace) :

bytes32 constant STORAGE_SLOT = keccak256(
    abi.encode(uint256(keccak256("batch.executor.storage")) - 1)
) & ~bytes32(uint256(0xff));

Questions frequentes

EIP-7702 change-t-il mon adresse de portefeuille ?

Non. Votre adresse EOA reste la meme. EIP-7702 donne temporairement des capacites de contrat intelligent pour la duree d’une seule transaction.

Puis-je utiliser EIP-7702 sur les L2 ?

Oui. Tous les L2 majeurs (Arbitrum, Base, Optimism, zkSync) ont adopte EIP-7702.

Que se passe-t-il si la cible de delegation a un bug ?

Les dommages sont limites a la seule transaction. Cependant, au sein de cette transaction, la cible a un acces complet aux actifs de l’EOA.

EIP-7702 est-il compatible avec les portefeuilles materiels ?

Oui. Ledger a ajoute le support dans le firmware 2.3.0 (decembre 2025).

Combien de gas EIP-7702 economise-t-il ?

Pour un flux approve+swap typique, EIP-7702 economise environ 40-50 % de gas. Pour les operations DeFi multi-etapes complexes, les economies peuvent atteindre 60-70 %.