跳到主要内容
区块链Mar 28, 2026

EIP-7702实战:Pectra之后构建智能账户流程

OS
Open Soft Team

Engineering Team

EIP-7702带来了什么

EIP-7702随Ethereum的Pectra升级于2025年3月激活,引入了一种新的交易类型,允许任何外部拥有账户(EOA)设置委托指示器——指向智能合约的指针,EOA在交易期间临时采用其代码。这意味着您现有的MetaMask钱包地址可以执行任意智能合约逻辑(批量调用、自定义验证、gas赞助),而无需部署新的合约钱包或更改您的地址。

这是Ethereum历史上最重大的用户体验改进。用户不再需要在EOA的简单性和智能账户的强大功能之间做选择。

EIP-7702 vs ERC-4337:何时使用哪个

两者都实现账户抽象,但在不同层级运作:

特性EIP-7702ERC-4337
层级协议(新tx类型)应用(智能合约)
账户类型升级现有EOA需要新智能合约钱包
地址保留现有EOA地址新地址(反事实)
持久性每笔交易委托永久智能合约
需要Bundler否(标准tx流程)是(独立内存池)
Gas开销~20,000 gas委托~42,000 gas UserOp验证
支持的钱包MetaMask, Coinbase Wallet, Rainbow专用(Safe, ZeroDev, Biconomy)
最适合升级现有用户新用户、复杂账户逻辑

决策框架

使用EIP-7702当:

  • 您的用户已有EOA钱包
  • 您需要批量交易或gas赞助
  • 您希望最小化集成复杂性

使用ERC-4337当:

  • 您正在构建新钱包或引导新用户
  • 您需要持久的账户逻辑
  • 您需要高级功能如账户模块、插件或守护者

代码示例

批量交易

最常见的EIP-7702用例:在单笔交易中执行多个操作。

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

/// @title BatchExecutor — EIP-7702批量调用委托目标
contract BatchExecutor {
    struct Call {
        address target;
        uint256 value;
        bytes data;
    }
    
    /// @notice 从委托EOA执行批量调用
    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))
                }
            }
        }
    }
}

使用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);

Gas赞助

/// @title SponsoredExecutor — 通过EIP-7702的gas赞助
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");
    }
}

社交恢复

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

钱包集成状态

MetaMask

MetaMask在12.4版本(2025年9月)中添加了EIP-7702支持。用户在dApp请求type-4交易时会看到“智能交易“标识。

Coinbase Wallet

Coinbase Wallet是最积极的采用者,将EIP-7702集成到“Smart Wallet“功能(2025年11月发布)中:

  • 自动批处理 — 钱包自动批处理approve+swap序列
  • Gas赞助 — Coinbase为低于$1费用的交易赞助gas
  • 一键DeFi — 常见DeFi操作的预构建委托目标

Rainbow、Rabby等

Rainbow于2026年1月添加支持。Rabby支持type-4交易但缺少批处理可视化UI。

迁移指南:从EOA到智能账户

步骤1:部署委托目标

创建并审计用户将委托的智能合约。

步骤2:更新前端

async function supportsEIP7702(provider: ethers.Provider): Promise<boolean> {
    try {
        const capabilities = await provider.send(
            "wallet_getCapabilities", []
        );
        return capabilities?.atomicBatch?.supported === true;
    } catch {
        return false;
    }
}

步骤3:实现优雅降级

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

安全注意事项

委托目标验证

委托目标合约在交易期间对EOA的资产拥有完全控制权。恶意委托目标可以转移EOA的所有ETH和代币。

缓解措施: 仅委托给经过审计和验证的合约。

重放保护

EIP-7702授权列表包含nonce以防止重放攻击。

通过委托的钓鱼

攻击者可能诱骗用户签署委托给恶意合约的授权列表。

缓解措施: 钱包必须清楚显示EOA委托给什么代码。

批量执行中的重入

bool private locked;

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

存储冲突

使用EIP-7201(命名空间存储):

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

常见问题

EIP-7702会改变我的钱包地址吗?

不会。您的EOA地址保持不变。EIP-7702在单笔交易期间临时赋予您的地址智能合约能力。

我可以在L2上使用EIP-7702吗?

是的。所有主要L2(Arbitrum, Base, Optimism, zkSync)已采用EIP-7702。

如果委托目标有bug怎么办?

损害仅限于单笔交易。但在该交易中,委托目标对EOA资产有完全访问权限。

EIP-7702与硬件钱包兼容吗?

是的。Ledger在固件2.3.0版本(2025年12月)中添加了支持。

EIP-7702节省多少gas?

对于典型的approve+swap流程,节省约**40-50%**的gas。复杂的多步DeFi操作可节省60-70%。