contract-wallet Link to heading

  • 外部账户(EOA)与 合约账户在 EVM 层面是等效的,都是有:nonce(交

易序号)、balance(余额)、storageRoot(状态)、codeHash(代码)

  • 如果该合约可以持有资金、调用任意合约方法,就是一个智能合约钱包账

  • 智能合约钱包账户:支持多签、multicall、密钥替换、找回

  • AA(ERC4337 / ERC7702): 抽象掉 EOA 与 智能合约钱包账户的区别

example Link to heading

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

/**
 * @title ContractWallet - 智能合约钱包
 * @dev 一个简单的智能合约钱包实现,支持接收资金和执行任意交易
 */
contract ContractWallet {
    
    // ============ 事件定义 ============
    
    /**
     * @dev 存款事件 - 当合约接收到资金时触发
     * @param sender 发送者地址
     * @param amount 存款金额
     * @param balance 存款后合约的总余额
     */
    event Deposit(address indexed sender, uint amount, uint balance);
    
    /**
     * @dev 执行交易事件 - 当合约执行外部交易时触发
     * @param to 目标合约/地址
     * @param value 发送的 ETH 数量
     * @param data 调用数据(函数调用的编码数据)
     */
    event ExecuteTransaction(
        address indexed to,
        uint value,
        bytes data
    );

    // ============ 状态变量 ============
    
    /**
     * @dev 钱包所有者地址
     * 只有所有者可以执行交易
     */
    address public owner;

    // ============ 修饰符 ============
    
    /**
     * @dev 只有所有者可以调用的修饰符
     * 用于保护关键函数,确保只有钱包所有者能执行操作
     */
    modifier onlyOwner() {
        require(owner == msg.sender, "not owner");
        _; // 继续执行被修饰的函数
    }

    // ============ 构造函数 ============
    
    /**
     * @dev 构造函数 - 部署时设置钱包所有者
     * @param _owner 钱包所有者地址
     */
    constructor(address _owner) {
        owner = _owner;
    }

    // ============ 接收函数 ============
    
    /**
     * @dev receive 函数 - 接收 ETH 转账
     * 当有人直接向合约地址转账时自动调用
     * external: 只能从外部调用
     * payable: 可以接收 ETH
     */
    receive() external payable {
        // 触发存款事件,记录发送者、金额和合约余额
        emit Deposit(msg.sender, msg.value, address(this).balance);
    }

    // ============ 核心功能函数 ============
    
    /**
     * @dev 执行任意交易 - 智能合约钱包的核心功能
     * @param _to 目标地址(可以是 EOA 地址或合约地址)
     * @param _value 要发送的 ETH 数量(单位:wei)
     * @param _data 调用数据(ABI 编码的函数调用数据)
     * 
     * 使用场景:
     * 1. 转账 ETH:_to=接收地址, _value=金额, _data=""
     * 2. 调用合约:_to=合约地址, _value=0, _data=函数调用编码
     * 3. 部署合约:_to=0x0, _value=0, _data=合约字节码
     */
    function executeTransaction(
        address _to,
        uint _value,
        bytes memory _data
    ) public onlyOwner {
        
        // 使用 call 执行低级调用
        // {value: _value} 指定发送的 ETH 数量
        // _data 是要执行的数据(函数调用或合约创建)
        (bool success, ) = _to.call{value: _value}(_data);
        
        // 检查调用是否成功,失败则回滚交易
        require(success, "tx failed");

        // 触发执行交易事件
        emit ExecuteTransaction(_to, _value, _data);
    }
}