特殊函数 #

一、构造函数 constructor #

1.1 基本概念 #

构造函数在合约部署时自动执行一次,用于初始化合约状态。

solidity
contract ConstructorDemo {
    address public owner;
    uint256 public initialValue;
    
    // 构造函数
    constructor(uint256 _value) {
        owner = msg.sender;
        initialValue = _value;
    }
}

1.2 构造函数特点 #

solidity
contract ConstructorFeatures {
    address public owner;
    string public name;
    
    // 1. 只执行一次
    constructor(string memory _name) {
        owner = msg.sender;
        name = _name;
    }
    
    // 2. 不能有返回值
    // constructor() returns (uint256) {}  // 错误
    
    // 3. 可以有参数
    // 4. 可以重载(但不推荐)
}

1.3 构造函数可见性 #

solidity
contract ConstructorVisibility {
    address public owner;
    
    // 构造函数默认是internal(如果省略可见性)
    // 但推荐显式声明为public或省略
    constructor() {
        owner = msg.sender;
    }
}

1.4 构造函数示例 #

solidity
contract Token {
    string public name;
    string public symbol;
    uint8 public decimals;
    uint256 public totalSupply;
    address public owner;
    
    mapping(address => uint256) public balanceOf;
    
    // 带参数的构造函数
    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals,
        uint256 _totalSupply
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;
        totalSupply = _totalSupply;
        owner = msg.sender;
        
        balanceOf[msg.sender] = _totalSupply;
    }
}

// 部署时传入参数
// new Token("MyToken", "MTK", 18, 1000000 * 10**18)

1.5 继承中的构造函数 #

solidity
contract Parent {
    uint256 public value;
    
    constructor(uint256 _value) {
        value = _value;
    }
}

contract Child is Parent {
    string public name;
    
    // 子合约需要调用父合约构造函数
    constructor(uint256 _value, string memory _name) Parent(_value) {
        name = _name;
    }
}

contract AnotherChild is Parent {
    constructor() Parent(100) {
        // 调用父合约构造函数,传入固定值
    }
}

二、接收函数 receive #

2.1 基本概念 #

receive函数在合约接收纯ETH转账时被调用。

solidity
contract ReceiveDemo {
    event Received(address indexed from, uint256 amount);
    
    // receive函数
    receive() external payable {
        emit Received(msg.sender, msg.value);
    }
}

2.2 receive特点 #

solidity
contract ReceiveFeatures {
    // 1. 必须是external payable
    // 2. 不能有参数
    // 3. 不能有返回值
    // 4. 一个合约只能有一个receive函数
    
    receive() external payable {
        // 处理ETH接收
    }
}

2.3 receive使用示例 #

solidity
contract Vault {
    mapping(address => uint256) public balances;
    
    event Deposit(address indexed from, uint256 amount);
    
    // 通过receive接收ETH
    receive() external payable {
        balances[msg.sender] += msg.value;
        emit Deposit(msg.sender, msg.value);
    }
    
    // 查询余额
    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }
    
    // 提取
    function withdraw(uint256 amount) public {
        require(balances[msg.sender] >= amount, "Insufficient balance");
        balances[msg.sender] -= amount;
        payable(msg.sender).transfer(amount);
    }
}

2.4 receive触发条件 #

solidity
contract ReceiveTrigger {
    event Received(address indexed from, uint256 amount);
    
    receive() external payable {
        emit Received(msg.sender, msg.value);
    }
    
    // receive在以下情况被调用:
    // 1. 向合约发送ETH,不调用任何函数
    // 2. 使用transfer()或send()发送ETH
    // 3. 使用call{value: ...}("")发送空数据
}

contract TriggerExample {
    ReceiveTrigger public target;
    
    constructor(address _target) {
        target = ReceiveTrigger(_target);
    }
    
    // 触发receive
    function triggerReceive() public payable {
        payable(address(target)).transfer(msg.value);
    }
    
    // 不触发receive(调用函数)
    function notTriggerReceive() public payable {
        // 调用特定函数,不触发receive
    }
}

三、回退函数 fallback #

3.1 基本概念 #

fallback函数在调用不存在的函数或发送ETH但receive不存在时被调用。

solidity
contract FallbackDemo {
    event FallbackCalled(address indexed from, uint256 amount, bytes data);
    
    // fallback函数
    fallback() external payable {
        emit FallbackCalled(msg.sender, msg.value, msg.data);
    }
}

3.2 fallback特点 #

solidity
contract FallbackFeatures {
    // 1. 必须是external
    // 2. 可以是payable(接收ETH)
    // 3. 不能有参数
    // 4. 不能有返回值
    // 5. 一个合约只能有一个fallback函数
    
    fallback() external payable {
        // 处理未知调用
    }
}

3.3 fallback使用示例 #

solidity
contract Proxy {
    address public implementation;
    
    event ProxyCall(address indexed sender, bytes data);
    
    constructor(address _implementation) {
        implementation = _implementation;
    }
    
    // 代理合约使用fallback转发调用
    fallback() external payable {
        address impl = implementation;
        assembly {
            calldatacopy(0, 0, calldatasize())
            let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
            returndatacopy(0, 0, returndatasize())
            switch result
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
        }
    }
    
    receive() external payable {
        // 接收ETH
    }
}

3.4 receive与fallback的区别 #

solidity
contract ReceiveVsFallback {
    event ReceiveCalled(address indexed from, uint256 amount);
    event FallbackCalled(address indexed from, uint256 amount, bytes data);
    
    // receive:只处理纯ETH转账
    receive() external payable {
        emit ReceiveCalled(msg.sender, msg.value);
    }
    
    // fallback:处理其他情况
    fallback() external payable {
        emit FallbackCalled(msg.sender, msg.value, msg.data);
    }
    
    // 调用规则:
    // 1. 发送ETH + 空数据 -> receive
    // 2. 发送ETH + 非空数据 -> fallback
    // 3. 调用不存在的函数 -> fallback
    // 4. 如果没有receive,发送ETH -> fallback
}

3.5 调用流程图 #

text
调用合约
    │
    ├── 调用存在的函数
    │       └── 执行该函数
    │
    └── 调用不存在的函数或发送ETH
            │
            ├── 发送ETH + 空数据
            │       │
            │       ├── 有receive函数 -> 执行receive
            │       │
            │       └── 无receive函数 -> 执行fallback
            │
            └── 发送ETH + 非空数据 或 不发送ETH
                    │
                    └── 执行fallback

四、综合示例 #

4.1 支付合约 #

solidity
contract PaymentContract {
    address public owner;
    mapping(address => uint256) public balances;
    
    event Deposit(address indexed from, uint256 amount);
    event Withdrawal(address indexed to, uint256 amount);
    event FallbackTriggered(address indexed from, uint256 amount, bytes data);
    
    constructor() {
        owner = msg.sender;
    }
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }
    
    // 显式存款函数
    function deposit() public payable {
        require(msg.value > 0, "Must send ETH");
        balances[msg.sender] += msg.value;
        emit Deposit(msg.sender, msg.value);
    }
    
    // receive:接收纯ETH转账
    receive() external payable {
        balances[msg.sender] += msg.value;
        emit Deposit(msg.sender, msg.value);
    }
    
    // fallback:处理其他情况
    fallback() external payable {
        if (msg.value > 0) {
            balances[msg.sender] += msg.value;
            emit Deposit(msg.sender, msg.value);
        }
        emit FallbackTriggered(msg.sender, msg.value, msg.data);
    }
    
    // 提款
    function withdraw(uint256 amount) public {
        require(balances[msg.sender] >= amount, "Insufficient balance");
        balances[msg.sender] -= amount;
        payable(msg.sender).transfer(amount);
        emit Withdrawal(msg.sender, amount);
    }
    
    // 查询合约余额
    function getContractBalance() public view returns (uint256) {
        return address(this).balance;
    }
    
    // 紧急提取(仅owner)
    function emergencyWithdraw() public onlyOwner {
        uint256 balance = address(this).balance;
        payable(owner).transfer(balance);
    }
}

4.2 代理合约 #

solidity
contract ProxyContract {
    address public implementation;
    address public admin;
    
    event Upgraded(address indexed implementation);
    event AdminChanged(address indexed oldAdmin, address indexed newAdmin);
    
    constructor(address _implementation) {
        implementation = _implementation;
        admin = msg.sender;
    }
    
    modifier onlyAdmin() {
        require(msg.sender == admin, "Not admin");
        _;
    }
    
    // 升级实现合约
    function upgrade(address _newImplementation) public onlyAdmin {
        implementation = _newImplementation;
        emit Upgraded(_newImplementation);
    }
    
    // 更换管理员
    function changeAdmin(address _newAdmin) public onlyAdmin {
        address oldAdmin = admin;
        admin = _newAdmin;
        emit AdminChanged(oldAdmin, _newAdmin);
    }
    
    // fallback:转发所有调用到实现合约
    fallback() external payable {
        _delegate(implementation);
    }
    
    // receive:接收ETH
    receive() external payable {
        // 可以选择转发或直接接收
    }
    
    function _delegate(address _impl) internal {
        assembly {
            calldatacopy(0, 0, calldatasize())
            let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)
            returndatacopy(0, 0, returndatasize())
            switch result
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
        }
    }
}

4.3 多功能钱包 #

solidity
contract MultiFunctionWallet {
    address public owner;
    mapping(bytes4 => address) public methods;
    
    event Deposit(address indexed from, uint256 amount);
    event MethodRegistered(bytes4 indexed selector, address indexed implementation);
    event MethodUnregistered(bytes4 indexed selector);
    
    constructor() {
        owner = msg.sender;
    }
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }
    
    // 注册方法
    function registerMethod(bytes4 selector, address implementation) public onlyOwner {
        methods[selector] = implementation;
        emit MethodRegistered(selector, implementation);
    }
    
    // 注销方法
    function unregisterMethod(bytes4 selector) public onlyOwner {
        delete methods[selector];
        emit MethodUnregistered(selector);
    }
    
    // receive:接收ETH
    receive() external payable {
        emit Deposit(msg.sender, msg.value);
    }
    
    // fallback:动态方法分发
    fallback() external payable {
        address impl = methods[msg.sig];
        require(impl != address(0), "Method not found");
        
        assembly {
            calldatacopy(0, 0, calldatasize())
            let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
            returndatacopy(0, 0, returndatasize())
            switch result
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
        }
    }
}

五、最佳实践 #

5.1 构造函数最佳实践 #

solidity
contract ConstructorBestPractices {
    address public owner;
    uint256 public value;
    
    // 推荐:参数使用下划线前缀
    constructor(uint256 _value) {
        owner = msg.sender;
        value = _value;
        
        // 推荐:在构造函数中进行验证
        require(_value > 0, "Value must be positive");
    }
    
    // 推荐:使用modifier简化权限检查
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }
}

5.2 receive/fallback最佳实践 #

solidity
contract ReceiveFallbackBestPractices {
    event Deposit(address indexed from, uint256 amount);
    event FallbackCalled(address indexed from, bytes data);
    
    // 推荐:receive只处理纯ETH转账
    receive() external payable {
        emit Deposit(msg.sender, msg.value);
    }
    
    // 推荐:fallback处理其他情况
    fallback() external payable {
        // 记录调用信息
        emit FallbackCalled(msg.sender, msg.data);
        
        // 如果发送了ETH,记录
        if (msg.value > 0) {
            emit Deposit(msg.sender, msg.value);
        }
    }
}

5.3 安全考虑 #

solidity
contract SecurityConsiderations {
    address public owner;
    
    constructor() {
        owner = msg.sender;
    }
    
    // 注意:receive和fallback可以接收ETH
    // 确保合约逻辑正确处理ETH
    
    receive() external payable {
        // 可以添加限制
        require(msg.value <= 10 ether, "Amount too large");
    }
    
    // 注意:fallback可能被意外触发
    fallback() external payable {
        // 记录所有调用
        // emit FallbackCalled(msg.sender, msg.value, msg.data);
    }
}

六、总结 #

特殊函数要点:

函数 说明 触发条件
constructor 初始化合约 部署时执行一次
receive 接收ETH 纯ETH转账
fallback 回退处理 调用不存在函数或发送ETH+数据

使用建议:

  • constructor用于初始化状态
  • receive处理纯ETH转账
  • fallback处理未知调用
  • 注意安全考虑

接下来,让我们学习合约基础!

最后更新:2026-03-27