死磕Uniswap V4(六):账户抽象与原生ETH

本文是「死磕Uniswap V4」系列的第六篇,深入剖析V4的账户抽象设计和原生ETH支持。

系列导航

序号标题核心内容
01V4概述与架构革命Singleton、Hooks、Flash Accounting
02Hooks机制深度解析Hook接口、生命周期、实现模式
03单例架构与瞬时会计PoolManager、Currency、Accounting
04交换流程与Hook执行时序swap函数、Hook调用链、Gas分析
05费用系统与动态费率自定义费率、动态调整、费用分配
06账户抽象与原生ETHCurrency类型、settle/take、批量操作
07安全分析与最佳实践Hook安全、MEV防护、审计要点

1. Currency类型系统

1.1 统一代币抽象

V4引入Currency类型,统一处理原生ETH和ERC20代币:

/// @notice Currency类型
/// @dev 本质上是address,但提供类型安全
type Currency is address;
 
/// @notice Currency库
library CurrencyLibrary {
    /// @notice 原生ETH的特殊地址
    Currency internal constant NATIVE = Currency.wrap(address(0));
 
    /// @notice 判断是否为原生ETH
    function isNative(Currency currency) internal pure returns (bool) {
        return Currency.unwrap(currency) == address(0);
    }
 
    /// @notice 从address创建Currency
    function fromAddress(address addr) internal pure returns (Currency) {
        return Currency.wrap(addr);
    }
 
    /// @notice 获取底层地址
    function toAddress(Currency currency) internal pure returns (address) {
        return Currency.unwrap(currency);
    }
 
    /// @notice 获取Currency的转账编码
    function transferCalldata(
        Currency currency,
        address to,
        uint256 amount
    ) internal pure returns (bytes memory) {
        if (isNative(currency)) {
            return abi.encodeWithSignature("transfer(uint256)", amount);
        } else {
            return abi.encodeWithSignature(
                "transfer(address,uint256)",
                to,
                amount
            );
        }
    }
 
    /// @notice 获取Currency的transferFrom编码
    function transferFromCalldata(
        Currency currency,
        address from,
        address to,
        uint256 amount
    ) internal pure returns (bytes memory) {
        if (isNative(currency)) {
            // ETH不支持transferFrom
            return "";
        } else {
            return abi.encodeWithSignature(
                "transferFrom(address,address,uint256)",
                from,
                to,
                amount
            );
        }
    }
}

1.2 Currency与address的对应

graph LR
    subgraph Mapping["Currency映射"]
        A["address(0)"] --> B["NATIVE ETH"]
        C["address(WETH)"] --> D["WETH Token"]
        E["address(USDC)"] --> F["USDC Token"]
        G["address(token)"] --> H["Any ERC20"]
    end

    style A fill:#ffcdd2
    style B fill:#c8e6c9

1.3 Currency操作封装

/// @notice Currency操作库
library CurrencyFunctions {
    using SafeERC20 for IERC20;
 
    /// @notice 获取余额
    function balanceOf(Currency currency, address account) internal view returns (uint256) {
        if (CurrencyLibrary.isNative(currency)) {
            return account.balance;
        } else {
            return IERC20(CurrencyLibrary.toAddress(currency)).balanceOf(account);
        }
    }
 
    /// @notice 转账
    function transfer(
        Currency currency,
        address to,
        uint256 amount
    ) internal {
        if (CurrencyLibrary.isNative(currency)) {
            payable(to).transfer(amount);
        } else {
            IERC20(CurrencyLibrary.toAddress(currency)).safeTransfer(to, amount);
        }
    }
 
    /// @notice 从指定地址转账
    function transferFrom(
        Currency currency,
        address from,
        address to,
        uint256 amount
    ) internal {
        if (CurrencyLibrary.isNative(currency)) {
            // ETH需要msg.value
            require(msg.value >= amount, "Insufficient ETH");
        } else {
            IERC20(CurrencyLibrary.toAddress(currency)).safeTransferFrom(from, to, amount);
        }
    }
 
    /// @notice 授权
    function approve(
        Currency currency,
        address spender,
        uint256 amount
    ) internal {
        require(!CurrencyLibrary.isNative(currency), "Cannot approve ETH");
        IERC20(CurrencyLibrary.toAddress(currency)).safeApprove(spender, amount);
    }
 
    /// @notice 获取代币符号
    function symbol(Currency currency) internal view returns (string memory) {
        if (CurrencyLibrary.isNative(currency)) {
            return "ETH";
        } else {
            return IERC20Metadata(CurrencyLibrary.toAddress(currency)).symbol();
        }
    }
 
    /// @notice 获取代币精度
    function decimals(Currency currency) internal view returns (uint8) {
        if (CurrencyLibrary.isNative(currency)) {
            return 18;
        } else {
            return IERC20Metadata(CurrencyLibrary.toAddress(currency)).decimals();
        }
    }
}

2. 瞬时会计深度解析

2.1 瞬时会计架构

flowchart TB
    subgraph Accounting["瞬时会计系统"]
        Delta[deltas映射<br/>account => currency => int256]
        Settle[settle函数<br/>声明应付款]
        Take[take函数<br/>声明应收款]
        Unlock[unlock函数<br/>结算所有差额]
    end

    subgraph Flow["流程"]
        A[用户操作] --> B[记录deltas]
        B --> C[更多操作]
        C --> D[累积deltas]
        D --> E[unlock时结算]
    end

    Delta --> Settle
    Delta --> Take
    Settle --> Unlock
    Take --> Unlock

    style Delta fill:#e3f2fd
    style Unlock fill:#c8e6c9

2.2 deltas数据结构

/// @notice 账户差额记录
/// @dev int256类型:
///      - 正数:应收款(Pool欠用户)
///      - 负数:应付款(用户欠Pool)
mapping(address account => mapping(Currency currency => int256)) public deltas;
 
/// @notice 差额变化结构
struct BalanceDelta {
    int256 delta0;  // currency0的变化
    int256 delta1;  // currency1的变化
}
 
/// @notice 差额操作记录(用于结算)
struct DeltaOperation {
    address account;
    Currency currency;
    int256 amount;
}
 
DeltaOperation[] private deltaOperations;  // 用于追踪所有操作

2.3 settle() 函数详解

/// @notice 用户声明将支付指定数量的代币
/// @param currency 要支付的代币类型
/// @param amount 支付数量
/// @dev 此函数记录用户的应付款,实际转账在unlock时进行
function settle(Currency currency, uint256 amount) external {
    // 1. 验证锁定状态
    require(locked != 0, "Not locked");
 
    // 2. 记录差额(负数表示应付款)
    deltas[msg.sender][currency] -= int256(amount);
 
    // 3. 对于原生ETH,立即验证msg.value
    if (CurrencyLibrary.isNative(currency)) {
        // 计算用户当前应付的ETH总额
        int256 currentDelta = deltas[msg.sender][currency];
        require(currentDelta <= 0, "Invalid delta");
 
        uint256 payableAmount = uint256(-currentDelta);
        require(msg.value >= payableAmount, "Insufficient ETH");
    }
 
    // 4. 记录操作
    deltaOperations.push(DeltaOperation({
        account: msg.sender,
        currency: currency,
        amount: -int256(amount)
    }));
 
    emit Settle(msg.sender, currency, amount);
}
 
/// @notice 批量settle
function settleAll(
    Currency[] calldata currencies,
    uint256[] calldata amounts
) external {
    require(currencies.length == amounts.length, "Length mismatch");
    require(locked != 0, "Not locked");
 
    for (uint256 i = 0; i < currencies.length; i++) {
        deltas[msg.sender][currencies[i]] -= int256(amounts[i]);
 
        if (CurrencyLibrary.isNative(currencies[i])) {
            int256 currentDelta = deltas[msg.sender][currencies[i]];
            require(uint256(-currentDelta) <= msg.value, "Insufficient ETH");
        }
 
        emit Settle(msg.sender, currencies[i], amounts[i]);
    }
}

2.4 take() 函数详解

/// @notice 用户声明将接收指定数量的代币
/// @param currency 要接收的代币类型
/// @param recipient 接收地址
/// @param amount 接收数量
/// @dev 此函数记录用户的应收款,实际转账在unlock时进行
function take(
    Currency currency,
    address recipient,
    uint256 amount
) external {
    // 1. 验证锁定状态
    require(locked != 0, "Not locked");
 
    // 2. 记录差额(正数表示应收款)
    deltas[recipient][currency] += int256(amount);
 
    // 3. 记录操作
    deltaOperations.push(DeltaOperation({
        account: recipient,
        currency: currency,
        amount: int256(amount)
    }));
 
    emit Take(msg.sender, recipient, currency, amount);
}
 
/// @notice 批量take
function takeAll(
    Currency[] calldata currencies,
    address recipient,
    uint256[] calldata amounts
) external {
    require(currencies.length == amounts.length, "Length mismatch");
    require(locked != 0, "Not locked");
 
    for (uint256 i = 0; i < currencies.length; i++) {
        deltas[recipient][currencies[i]] += int256(amounts[i]);
        emit Take(msg.sender, recipient, currencies[i], amounts[i]);
    }
}

2.5 unlock结算详解

/// @notice 内部结算函数
/// @dev 在unlock时调用,结算所有账户的差额
function _accountingBalance() internal {
    // 1. 遍历所有有差额的账户
    for (uint256 i = 0; i < accountsWithDeltas.length; i++) {
        address account = accountsWithDeltas[i];
 
        // 2. 遍历该账户的所有差额代币
        for (uint256 j = 0; j < currenciesWithDeltas.length; j++) {
            Currency currency = currenciesWithDeltas[j];
            int256 delta = deltas[account][currency];
 
            if (delta == 0) continue;
 
            if (delta > 0) {
                // 正数:Pool需要支付给用户
                _payToAccount(account, currency, uint256(delta));
            } else {
                // 负数:用户需要支付给Pool
                _collectFromAccount(account, currency, uint256(-delta));
            }
 
            // 清零差额
            deltas[account][currency] = 0;
        }
    }
 
    // 3. 清空列表
    delete accountsWithDeltas;
    delete currenciesWithDeltas;
}
 
/// @notice 支付给账户
function _payToAccount(
    address account,
    Currency currency,
    uint256 amount
) ) private {
    if (CurrencyLibrary.isNative(currency)) {
        // 原生ETH:直接发送
        payable(account).transfer(amount);
    } else {
        // ERC20:转账
        IERC20(CurrencyLibrary.toAddress(currency)).transfer(account, amount);
    }
 
    emit Payment(account, currency, amount);
}
 
/// @notice 从账户收取
function _collectFromAccount(
    address account,
    Currency currency,
    uint256 amount
) private {
    if (CurrencyLibrary.isNative(currency)) {
        // ETH在settle时已经验证msg.value
        // 这里不需要额外操作
        // 但需要退还多余的ETH
        int256 remainingDelta = deltas[account][currency];
        if (remainingDelta < 0) {
            // 用户支付了足够的ETH
            // 不需要退款(msg.value在交易结束时自动退还)
        }
    } else {
        // ERC20:transferFrom
        IERC20(CurrencyLibrary.toAddress(currency)).transferFrom(
            account,
            address(this),
            amount
        );
    }
 
    emit Collection(account, currency, amount);
}

3. 原生ETH支持

3.1 ETH vs WETH对比

graph TB
    subgraph WETHMode["V3: WETH模式"]
        W1[用户持有ETH]
        W2[调用WETH.deposit]
        W3[获得WETH]
        W4[授权WETH给Uniswap]
        W5[执行swap]
        W6[收到WETH]
        W7[调用WETH.withdraw]
        W8[获得ETH]
    end

    subgraph NativeMode["V4: 原生ETH模式"]
        N1[用户持有ETH]
        N2[直接swap<br/>currency = NATIVE]
        N3[收到ETH]
    end

    style W2 fill:#ffcdd2
    style W4 fill:#ffcdd2
    style W6 fill:#ffcdd2
    style W7 fill:#ffcdd2
    style N2 fill:#c8e6c9

3.2 原生ETH交换示例

/// @notice 原生ETH交换示例
/// @param amountInEth 输入的ETH数量
/// @param amountOutMin 最小输出数量
function swapEthForToken(
    uint256 amountInEth,
    uint256 amountOutMin,
    PoolKey calldata key
) external payable returns (uint256 amountOut) {
    // 验证currency0是原生ETH
    require(
        CurrencyLibrary.isNative(key.currency0),
        "Currency0 must be ETH"
    );
 
    // 构造交换参数
    SwapParams memory params = SwapParams({
        zeroForOne: true,  // ETH → Token
        amountSpecified: int256(amountInEth),
        sqrtPriceLimitX96: TickMath.MIN_SQRT_RATIO + 1,
        hookData: ""
    });
 
    // 设置限制
    BalanceDelta memory limits = BalanceDelta({
        delta0: -int256(amountInEth),  // 最多支付amountInEth的ETH
        delta1: int256(amountOutMin)   // 最少收到amountOutMin的Token
    });
 
    // 执行交换
    BalanceDelta memory delta = poolManager.swap{value: amountInEth}(key, params, limits);
 
    // 记录ETH的支付(通过msg.value)
    // 注意:原生ETH的settle是通过msg.value验证的
 
    amountOut = uint256(delta.delta1);
 
    emit EthSwap(msg.sender, amountInEth, amountOut);
}
 
/// @notice Token换ETH示例
function swapTokenForEth(
    uint256 amountIn,
    uint256 amountOutEthMin,
    PoolKey calldata key
) external returns (uint256 amountOutEth) {
    // 验证currency1是原生ETH
    require(
        CurrencyLibrary.isNative(key.currency1),
        "Currency1 must be ETH"
    );
 
    // 构造交换参数
    SwapParams memory params = SwapParams({
        zeroForOne: false,  // Token → ETH
        amountSpecified: int256(amountIn),
        sqrtPriceLimitX96: TickMath.MAX_SQRT_RATIO - 1,
        hookData: ""
    });
 
    // 设置限制
    BalanceDelta memory limits = BalanceDelta({
        delta0: int256(amountOutEthMin),  // 最少收到amountOutEthMin的ETH
        delta1: -int256(amountIn)          // 最多支付amountIn的Token
    });
 
    // 执行交换
    BalanceDelta memory delta = poolManager.swap(key, params, limits);
 
    // settle Token
    poolManager.settle(key.currency0, amountIn);
 
    // take ETH
    poolManager.take(CurrencyLibrary.NATIVE, msg.sender, uint256(delta.delta0));
 
    amountOutEth = uint256(delta.delta0);
 
    emit TokenSwap(msg.sender, amountIn, amountOutEth);
}

3.3 Gas节省分析

操作V3 (WETH)V4 (Native)节省
WETH包装~42,0000100%
WETH解包~42,0000100%
授权检查~5,0000100%
approve调用~46,0000100%
单笔ETH交易~135,000~50,000~63%

4. 批量操作

4.1 批量交换实现

/// @notice 批量交换路由
/// @param route 交换路由:包含多个池子和交换参数
/// @param amountIn 输入数量
/// @param amountOutMin 最小输出数量
function batchSwap(
    Route calldata route,
    uint256 amountIn,
    uint256 amountOutMin
) external payable returns (uint256 amountOut) {
    // 锁定
    lock();
 
    uint256 currentAmount = amountIn;
 
    // 执行路由中的每一步
    for (uint256 i = 0; i < route.keys.length; i++) {
        PoolKey calldata key = route.keys[i];
        SwapParams calldata params = route.params[i];
 
        // 更新输入数量
        params.amountSpecified = int256(currentAmount);
 
        // 设置默认限制
        BalanceDelta memory limits = BalanceDelta({
            delta0: type(int256).min,
            delta1: type(int256).min
        });
 
        // 执行交换
        BalanceDelta memory delta = poolManager.swap{value: msg.value}(key, params, limits);
 
        // 更新下一笔的输入数量
        currentAmount = params.zeroForOne
            ? uint256(delta.delta1)
            : uint256(delta.delta0);
    }
 
    // 验证最终输出
    require(currentAmount >= amountOutMin, "Insufficient output");
 
    // settle第一个代币
    poolManager.settle(route.keys[0].currency0, amountIn);
 
    // take最后一个代币
    Currency lastCurrency = route.keys[route.keys.length - 1].currency1;
    poolManager.take(lastCurrency, msg.sender, currentAmount);
 
    // 解锁并结算
    unlock();
 
    amountOut = currentAmount;
 
    emit BatchSwap(msg.sender, route, amountIn, amountOut);
}
 
/// @notice 路由结构
struct Route {
    PoolKey[] keys;      // 池子数组
    SwapParams[] params; // 参数数组
}

4.2 批量流动性管理

/// @notice 批量修改流动性头寸
function batchModifyPositions(
    PositionParams[] calldata positions
) external payable {
    lock();
 
    for (uint256 i = 0; i < positions.length; i++) {
        PositionParams calldata pos = positions[i];
 
        ModifyPositionParams memory params = ModifyPositionParams({
            tickLower: pos.tickLower,
            tickUpper: pos.tickUpper,
            liquidityDelta: pos.liquidityDelta
        });
 
        poolManager.modifyPosition(pos.key, params);
 
        // 如果是添加流动性,settle代币
        if (pos.liquidityDelta > 0) {
            (uint256 amount0, uint256 amount1) = _getLiquidityAmounts(pos);
            poolManager.settle(pos.key.currency0, amount0);
            poolManager.settle(pos.key.currency1, amount1);
        }
    }
 
    unlock();
}
 
/// @notice 头寸参数
struct PositionParams {
    PoolKey key;
    int24 tickLower;
    int24 tickUpper;
    int128 liquidityDelta;
}

4.3 批量操作Gas对比

graph LR
    subgraph Single["单独操作"]
        S1[操作1] --> S2[操作2] --> S3[操作3]
        S1Gas[~75k]
        S2Gas[~75k]
        S3Gas[~75k]
        Total1[~225k]
    end

    subgraph Batch["批量操作"]
        B1[lock一次]
        B2[执行所有操作]
        B3[unlock一次]
        B1Gas[~5k]
        B2Gas[~150k]
        B3Gas[~10k]
        Total2[~165k]
    end

    Total1 -.->|节省 27%| Total2

    style B2 fill:#c8e6c9

5. 元交易支持

5.1 元交易设计

通过Hooks可以实现元交易(用户不需要支付gas):

/// @notice 元交易支持Hook
contract MetaTransactionHook is IHooks {
    IPoolManager public immutable poolManager;
 
    mapping(address => uint256) public nonces;
    mapping(address => uint256) public gasCredits;
 
    /// @notice 元交易签名
    struct MetaTransaction {
        address signer;           // 签名者
        PoolKey key;              // 池子
        SwapParams params;        // 交换参数
        uint256 nonce;            // nonce
        uint256 gasPrice;         // gas价格
        uint256 gasLimit;         // gas限制
        bytes signature;          // 签名
    }
 
    /// @notice 执行元交易
    function executeMetaTransaction(
        MetaTransaction calldata metaTx
    ) external {
        // 1. 验证签名
        bytes32 messageHash = keccak256(abi.encode(
            metaTx.signer,
            metaTx.key,
            metaTx.params,
            metaTx.nonce,
            metaTx.gasPrice,
            metaTx.gasLimit,
            block.chainid
        ));
 
        bytes32 ethSignedMessageHash = keccak256(
            abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash)
        );
 
        (address recovered, ) = recoverSigner(
            ethSignedMessageHash,
            metaTx.signature
        );
 
        require(recovered == metaTx.signer, "Invalid signature");
        require(nonces[metaTx.signer] == metaTx.nonce, "Invalid nonce");
 
        // 2. 验证gas积分
        uint256 gasCost = metaTx.gasPrice * metaTx.gasLimit;
        require(gasCredits[metaTx.signer] >= gasCost, "Insufficient gas credit");
 
        // 3. 执行交换
        BalanceDelta memory limits = BalanceDelta({
            delta0: type(int256).min,
            delta1: type(int256).min
        });
 
        BalanceDelta memory delta = poolManager.swap(
            metaTx.key,
            metaTx.params,
            limits
        );
 
        // 4. 扣除gas积分
        gasCredits[metaTx.signer] -= gasCost;
 
        // 5. 更新nonce
        nonces[metaTx.signer]++;
 
        emit MetaTransactionExecuted(metaTx.signer, delta);
    }
 
    /// @notice 添加gas积分
    function addGasCredits(address user, uint256 amount) external {
        gasCredits[user] += amount;
        emit GasCreditsAdded(user, amount);
    }
 
    function recoverSigner(
        bytes32 hash,
        bytes memory signature
    ) private pure returns (address, ECDSA.RecoverError) {
        return ECDSA.recover(hash, signature);
    }
 
    event MetaTransactionExecuted(address indexed signer, BalanceDelta delta);
    event GasCreditsAdded(address indexed user, uint256 amount);
}

5.2 Relayer模式

/// @notice Relayer转发交易
contract Relayer {
    IPoolManager public immutable poolManager;
 
    mapping(address => uint256) public nonces;
 
    struct ForwardedRequest {
        address from;             // 原始发起者
        PoolKey key;
        SwapParams params;
        uint256 nonce;
        uint256 deadline;
        bytes signature;
    }
 
    /// @notice 转发交易
    function execute(
        ForwardedRequest calldata req,
        bytes calldata extraData
    ) external payable {
        // 1. 验证deadline
        require(block.timestamp <= req.deadline, "Expired");
 
        // 2. 验证签名
        bytes32 messageHash = keccak256(abi.encode(
            req.from,
            req.key,
            req.params,
            req.nonce,
            req.deadline,
            extraData
        ));
 
        _verifySignature(messageHash, req.signature, req.from);
 
        // 3. 验证nonce
        require(nonces[req.from] == req.nonce, "Invalid nonce");
 
        // 4. 执行交换(使用relayer的msg.value)
        BalanceDelta memory limits = BalanceDelta({
            delta0: type(int256).min,
            delta1: type(int256).min
        });
 
        poolManager.swap{value: msg.value}(req.key, req.params, limits);
 
        // 5. 更新nonce
        nonces[req.from]++;
 
        // 6. 如果用户需要支付代币,从用户收取
        // (通过Hook实现)
 
        emit Forwarded(req.from, msg.sender, req.nonce);
    }
 
    function _verifySignature(
        bytes32 hash,
        bytes memory signature,
        address expectedSigner
    ) private pure {
        bytes32 ethSignedHash = keccak256(
            abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)
        );
 
        address signer = ECDSA.recover(ethSignedHash, signature);
        require(signer == expectedSigner, "Invalid signature");
    }
 
    event Forwarded(address indexed from, address indexed relayer, uint256 nonce);
}

6. 账户抽象应用

6.1 账户抽象钱包集成

/// @notice 支持账户抽象钱包的Hook
contract AccountAbstractionHook is IHooks {
    IPoolManager public immutable poolManager;
 
    /// @notice 支持的ERC-4337钱包聚合器
    mapping(address => bool) public supportedAggregators;
 
    /// @notice 检查是否为AA钱包
    function isAccountAbstractedWallet(address account) public view returns (bool) {
        // 检查是否为支持的AA钱包
        // 例如:Smart Wallet、Argent、Safe等
        return _isSmartWallet(account);
    }
 
    function beforeSwap(
        address sender,
        PoolKey calldata key,
        IPoolManager.SwapParams calldata params,
        bytes calldata hookData
    ) external returns (bytes4, int256, int256) {
        // 如果是AA钱包,允许特殊处理
        if (isAccountAbstractedWallet(sender)) {
            // 1. 检查paymaster是否支持
            if (_hasPaymaster(sender)) {
                // 2. 跳过余额验证
                // 3. 允许延迟支付
                return (IHooks.beforeSwap.selector, 0, 0);
            }
        }
 
        return (IHooks.beforeSwap.selector, 0, 0);
    }
 
    function _isSmartWallet(address account) private view returns (bool) {
        // 实现智能钱包检测逻辑
        // 可以通过检查代码大小或调用特定方法来识别
        return account.code.length > 0;
    }
 
    function _hasPaymaster(address account) private view returns (bool) {
        // 检查钱包是否有paymaster支持
        // 可以通过调用钱包的方法来确认
        return true; // 简化示例
    }
}

6.2 社交恢复集成

/// @notice 社交恢复钱包支持
contract SocialRecoverySupport is IHooks {
    IPoolManager public immutable poolManager;
 
    /// @notice 社交恢复映射
    mapping(address => address[]) public guardians;
    mapping(address => mapping(address => bool)) public isGuardian;
 
    /// @notice 添加守护者
    function addGuardian(address wallet, address guardian) external {
        require(!isGuardian[wallet][guardian], "Already guardian");
        guardians[wallet].push(guardian);
        isGuardian[wallet][guardian] = true;
 
        emit GuardianAdded(wallet, guardian);
    }
 
    /// @notice 社交恢复
    function recoverWallet(
        address oldWallet,
        address newWallet,
        uint8 requiredSignatures,
        bytes calldata signature
    ) external {
        // 验证守护者签名
        bytes32 messageHash = keccak256(abi.encodePacked(
            "RECOVER",
            oldWallet,
            newWallet,
            block.number
        ));
 
        uint256 validSignatures = 0;
 
        for (uint256 i = 0; i < guardians[oldWallet].length; i++) {
            address guardian = guardians[oldWallet][i];
            if (_isValidSignature(messageHash, signature, guardian)) {
                validSignatures++;
            }
        }
 
        require(validSignatures >= requiredSignatures, "Insufficient signatures");
 
        // 转移所有权
        _transferOwnership(oldWallet, newWallet);
 
        emit WalletRecovered(oldWallet, newWallet);
    }
 
    function _isValidSignature(
        bytes32 hash,
        bytes calldata signature,
        address signer
    ) private pure returns (bool) {
        // 实现签名验证
        return true;
    }
 
    function _transferOwnership(address from, address to) private {
        // 实现所有权转移
    }
 
    event GuardianAdded(address indexed wallet, address guardian);
    event WalletRecovered(address indexed oldWallet, address indexed newWallet);
}

7. 本章小结

7.1 账户抽象总结

mindmap
  root((账户抽象))
    Currency类型
      原生ETH address(0)
      ERC20代币
      统一接口
    瞬时会计
      deltas记录
      settle声明应付款
      take声明应收款
      unlock统一结算
    原生ETH支持
      无需WETH
      msg.value验证
      直接转账
    批量操作
      单次锁定
      多次操作
      统一结算
    元交易
      签名验证
      nonce防重放
      gas积分

7.2 关键函数回顾

函数用途参数
settle()声明应付款currency, amount
take()声明应收款currency, recipient, amount
unlock()结算所有差额-
isNative()判断是否为ETHcurrency
balanceOf()获取余额currency, account

7.3 Gas优化要点

优化节省说明
原生ETH~42k × 2无需WETH包装/解包
批量操作~30-40%共享lock/unlock
瞬时会计~10-15k减少外部调用
元交易21k × 0用户无需支付gas

下一篇预告

在最后一篇文章中,我们将深入探讨安全分析与最佳实践,包括:

  • Hook安全最佳实践
  • 常见漏洞和攻击向量
  • MEV防护策略
  • 审计要点
  • 开发工具和测试

参考资料