ERC-7579模块化账户标准:智能合约钱包的"乐高"插拔方案

前言

ERC-7579 是目前 Web3 领域最前沿的模块化账户抽象(Modular Account Abstraction)标准。

如果说 ERC-4337 解决了"如何通过 Bundler 发送交易",那么 ERC-7579 解决的就是"智能合约钱包内部如何像乐高一样插拔功能"。它由 Rhinestone、Biconomy 和 ZeroDev 等头部团队共同推动,旨在消除不同钱包厂商(如 Safe, Biconomy, Kernel)之间的模块不兼容问题。

一、 什么是 ERC-7579?

定义: 模块化智能合约账户(MSCA)的轻量级互操作性标准。
核心痛点: 解决不同钱包插件不兼容的问题。
定位: 它是 ERC-4337 的增强插件集。如果 4337 是地基,7579 就是插座标准,让各种功能插件可以"即插即用"。

二、 它能做什么?(三大核心价值)

  1. 跨钱包通用: 开发一个"自动理财"模块,可在 Safe、Biconomy 等所有兼容钱包中运行。
  2. 标准化操作: 统一了插件的安装、卸载和调用逻辑,降低安全风险。
  3. 极度轻量化: 只规定最基础的接口,给钱包开发者留出最大的创新空间。

三、 落地场景

  1. 全自动链上策略 (Executor 场景) : 你不需要给 AI Agent 提供钱包私钥。你只需安装一个 Executor 模块,授权它"只能在 ETH 跌破 2000 时调用 Uniswap 卖出"。这实现了所有权与操作权的分离
  2. 企业级风控 (Hook 场景) : 公司账户可以安装一个 SpendingLimitHook。无论管理员如何签名,只要单笔金额超过 10 ETH,Hook 就会直接在合约层 Revert。
  3. 无缝跨链账户 (Validator 场景) : 利用统一的模块标准,你在 Base 链上安装的"指纹识别验证器",可以轻松同步并部署到 Arbitrum 或 Optimism 的 7579 钱包中。

四、 ERC-7579 核心架构

ERC-7579 定义了四种标准的模块类型,每种类型负责钱包的一个特定维度:

模块类型 角色 (ID) 落地场景示例 核心功能
Validator 验证器 (1) Passkey 验证、多签、社会恢复 决定"谁"有权发起这笔交易。
Executor 执行器 (2) 自动定投 (DCA)、网格交易、止损 允许第三方或脚本在特定条件下代表钱包执行操作。
Fallback 回退处理器 (3) ERC-1271 签名支持、NFT 接收钩子 处理钱包原生不支持的函数调用。
Hook 钩子 (4) 每日支出限额、黑名单拦截 在交易执行前后进行检查(类似 Web2 的中间件)。

五、MVP版ERC-7579定时转账:智能合约开发、测试、部署一体化

5.1智能合约

  • 5.1.1:Mock7579Account
js 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract Mock7579Account {
    function execute(address target, uint256 value, bytes calldata callData) external payable {
        // 简化:生产环境需校验 msg.sender 是否为已安装模块
        (bool success, ) = target.call{value: value}(callData);
        require(success, "Mock7579Account: Execution failed");
    }

    receive() external payable {}
}
  • 5.1.2:ScheduledOrdersExecutor
js 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

// 简化版接口,实际生产环境建议通过 npm 安装 @rhinestone/module-bases
interface IERC7579Executor {
    function onInstall(bytes calldata data) external;
    function onUninstall(bytes calldata data) external;
    function isModuleType(uint256 typeID) external pure returns (bool);
}

interface IMockAccount {
    function execute(address target, uint256 value, bytes calldata callData) external payable;
}

contract ScheduledOrdersExecutor is IERC7579Executor, ReentrancyGuard {
    using EnumerableSet for EnumerableSet.AddressSet;

    struct Order {
        uint256 interval;
        uint256 lastExecution;
        address destination;
        uint256 amount;
        bool active;
    }

    mapping(address => Order) public orders;
    EnumerableSet.AddressSet private _accounts;

    error InvalidExecution();
    error AlreadyInitialized();
    error NotInitialized();

    function onInstall(bytes calldata data) external override {
        if (orders[msg.sender].active) revert AlreadyInitialized();
        
        (uint256 interval, address destination, uint256 amount) = abi.decode(data, (uint256, address, uint256));
        
        orders[msg.sender] = Order({
            interval: interval,
            lastExecution: block.timestamp,
            destination: destination,
            amount: amount,
            active: true
        });
        _accounts.add(msg.sender);
    }

    function onUninstall(bytes calldata) external override {
        delete orders[msg.sender];
        _accounts.remove(msg.sender);
    }

    // 由外部 Keeper 触发,代为执行账户的转账逻辑
    function executeOrder(address account) external nonReentrant {
        Order storage order = orders[account];
        if (!order.active) revert NotInitialized();
        if (block.timestamp < order.lastExecution + order.interval) revert InvalidExecution();

        order.lastExecution = block.timestamp;

        // 回调账户的执行接口
        IMockAccount(account).execute(order.destination, order.amount, "");
    }

    function isModuleType(uint256 typeID) external pure override returns (bool) {
        return typeID == 2; // Executor 类型
    }

    function isInitialized(address account) external view returns (bool) {
        return orders[account].active;
    }
}

5.2 测试脚本

  • ERC-7579 定期转账模块测试
    • 场景1:模块安装与配置初始化
    • 场景2:未到执行时间尝试执行应失败
    • 场景3:到达间隔时间后成功触发转账
    • 场景4:卸载模块后无法再次执行
js 复制代码
import assert from "node:assert/strict";
import { describe, it, beforeEach } from "node:test";
import { parseEther, encodeAbiParameters, parseAbiParameters, getAddress } from 'viem';
import { network } from "hardhat";

describe("ERC-7579 定期转账模块测试", function () {
    let executor: any, account: any;
    let publicClient: any, owner: any, receiver: any;

    beforeEach(async function () {
        // @ts-ignore
        const { viem } = await (network as any).connect();
        publicClient = await viem.getPublicClient();
        [owner, receiver] = await viem.getWalletClients();

        executor = await viem.deployContract("ScheduledOrdersExecutor");
        account = await viem.deployContract("Mock7579Account");

        // 注入 ETH
        await owner.sendTransaction({
            to: account.address,
            value: parseEther("10")
        });

        // 【关键修复】:伪装合约账户,使其能够发送交易
        await publicClient.request({
            method: "hardhat_impersonateAccount",
            params: [account.address],
        });
    });

    it("场景1:模块安装与配置初始化", async function () {
        const interval = 3600n;
        const amount = parseEther("1");
        const installData = encodeAbiParameters(
            parseAbiParameters('uint256, address, uint256'),
            [interval, receiver.account.address, amount]
        );

        // 使用 impersonation 发送交易
        await executor.write.onInstall([installData], { 
            account: account.address // 现在 Hardhat 允许以这个地址发送了
        });

        const order = await executor.read.orders([account.address]);
        // order 返回的是一个数组/结构体: [interval, lastExecution, destination, amount, active]
        assert.equal(order[0], interval);
        assert.equal(getAddress(order[2]), getAddress(receiver.account.address));
        assert.equal(order[4], true);
    });

    it("场景2:未到执行时间尝试执行应失败", async function () {
        const installData = encodeAbiParameters(
            parseAbiParameters('uint256, address, uint256'),
            [3600n, receiver.account.address, parseEther("1")]
        );
        await executor.write.onInstall([installData], { account: account.address });

        // 预期失败:InvalidExecution()
        await assert.rejects(
            executor.write.executeOrder([account.address]),
            (err: any) => err.message.includes("InvalidExecution")
        );
    });

    it("场景3:到达间隔时间后成功触发转账", async function () {
        const interval = 3600n;
        const amount = parseEther("1");
        const installData = encodeAbiParameters(
            parseAbiParameters('uint256, address, uint256'),
            [interval, receiver.account.address, amount]
        );
        await executor.write.onInstall([installData], { account: account.address });

        const beforeBalance = await publicClient.getBalance({ address: receiver.account.address });

        // 快进时间
        await publicClient.request({ method: "evm_increaseTime", params: [3601] });
        await publicClient.request({ method: "evm_mine" });

        // 执行订单
        await executor.write.executeOrder([account.address]);

        const afterBalance = await publicClient.getBalance({ address: receiver.account.address });
        assert.equal(afterBalance - beforeBalance, amount);
    });

    it("场景4:卸载模块后无法再次执行", async function () {
        const installData = encodeAbiParameters(
            parseAbiParameters('uint256, address, uint256'),
            [3600n, receiver.account.address, parseEther("1")]
        );
        await executor.write.onInstall([installData], { account: account.address });

        // 卸载
        await executor.write.onUninstall(["0x"], { account: account.address });

        // 预期失败:NotInitialized()
        await assert.rejects(
            executor.write.executeOrder([account.address]),
            (err: any) => err.message.includes("NotInitialized")
        );
    });
});

5.3 部署脚本

js 复制代码
// scripts/deploy.js
import { network, artifacts } from "hardhat";
import { parseUnits } from "viem";
async function main() {
  // 连接网络
  const { viem } = await network.connect({ network: network.name });//指定网络进行链接
  
  // 获取客户端
  const [deployer, investor] = await viem.getWalletClients();
  const publicClient = await viem.getPublicClient();
 
  const deployerAddress = deployer.account.address;
   console.log("部署者的地址:", deployerAddress);
  
  // 部署ScheduledOrdersExecutor合约
  const ScheduledOrdersExecutorArtifact = await artifacts.readArtifact("ScheduledOrdersExecutor");
  // 1. 部署合约并获取交易哈希
  const ScheduledOrdersExecutorHash = await deployer.deployContract({
    abi: ScheduledOrdersExecutorArtifact.abi,
    bytecode: ScheduledOrdersExecutorArtifact.bytecode,
    args: [],
  });
  const ScheduledOrdersExecutorReceipt = await publicClient.waitForTransactionReceipt({ 
     hash: ScheduledOrdersExecutorHash 
   });
   console.log("ScheduledOrdersExecutor合约地址:", ScheduledOrdersExecutorReceipt.contractAddress);
   
   // 部署Mock7579Account合约
   const Mock7579AccountArtifact = await artifacts.readArtifact("Mock7579Account");
   // 1. 部署合约并获取交易哈希
   const Mock7579AccountHash = await deployer.deployContract({
     abi: Mock7579AccountArtifact.abi,
     bytecode: Mock7579AccountArtifact.bytecode,
     args: [],
   });
   const Mock7579AccountReceipt = await publicClient.waitForTransactionReceipt({ 
      hash: Mock7579AccountHash 
    });
    console.log("Mock7579Account合约地址:", Mock7579AccountReceipt.contractAddress);
}

main().catch(console.error);

总结

ERC-7579 的意义在于打破了钱包生态的孤岛。它通过标准化的模块化方案,降低了开发者的开发门槛,同时也给用户带来了极大的自由度。

如果说 ERC-4337 让钱包变成了"账户",那么 ERC-7579 就让账户变成了"操作系统"。 它打破了厂商壁垒,让用户可以像在手机上安装 App 一样,自由定制自己的链上账户功能。

相关推荐
重明链迹实验室4 小时前
重明链迹丨每周区块链安全要闻(0427-0503)
安全·web3·区块链
Jiamiren9 小时前
WEEX Labs亮相香港Web3嘉年华,深化全球科技生态交流
科技·web3
OneBlock Community1 天前
Web3 的下一波机会在哪?黑客松获奖团队复盘
web3
TinTin Land1 天前
孙宇晨线上亮相 HTX Genesis 开幕峰会,黑客松赛制正式发布!
web3
TinTin Land1 天前
超600人参与|OpenBOX@HK: OpenClaw OPC Connect 开发者大会精彩落幕!
web3
潇楠Web3哨兵2 天前
《「潇楠WEB3哨兵」Agent 全栈架构:从记忆系统到技能扩展,桌面端 AI 投研助手的完整技术实现》
web3·agent
长安链开源社区3 天前
长安链开发大赛 在期待什么样的作品?
web3·区块链
BlockChain8883 天前
以太坊开发入门:从 0 到 1 搭建第一个 DApp
区块链·智能合约
Web3VentureView3 天前
SYNBO走进以太坊中国高校行复旦大学专场:链接Web3下一代开发者
人工智能·web3·区块链·加密货币·synbo