Solidity中的回退函数fallback和receive详解

  • Louis
  • 更新于 2024-08-29 19:09
  • 阅读 1471

在Solidity中,回退函数(fallbackfunctions)是在合约接收到以太币或调用不存在的函数时触发的特殊函数。自Solidity0.6.0版本起,回退函数分为两种:fallback函数和receive函数。

基本概念

在Solidity中,回退函数(fallback functions)是在合约接收到以太币或调用不存在的函数时触发的特殊函数。自Solidity 0.6.0版本起,回退函数分为两种:fallback函数和receive函数。

回退函数类型

receive函数:

  • 用于处理接收纯以太币转账(即直接向合约地址发送以太币,而不调用任何函数)。

  • 必须是externalpayable的,且不能有参数和返回值。

fallback函数:

  • 当调用不存在的函数或向合约发送以太币但没有receive函数时触发。

  • 可以是payable或非payable,但若希望处理以太币转账,必须是payable的。

  • 通常用于记录调用日志、反应错误的调用或处理某些特殊逻辑。

代码示例

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

contract FallbackExample {
    // 记录合约的所有者
    address public owner;

    // 事件:当收到以太币时触发
    event EtherReceived(address indexed sender, uint256 amount);

    // 事件:当调用了不存在的函数时触发
    event FallbackCalled(address indexed sender, uint256 amount, bytes data);

    // 构造函数:设置合约所有者
    constructor() {
        owner = msg.sender;
    }

    // receive函数:接收纯以太币转账时调用
    receive() external payable {
        emit EtherReceived(msg.sender, msg.value);
    }

    // fallback函数:调用不存在的函数或向合约发送以太币但没有receive函数时调用
    fallback() external payable {
        emit FallbackCalled(msg.sender, msg.value, msg.data);
        // 处理传入的数据
        if (msg.data.length > 0) {
            // 简单示例:解析前四个字节(函数选择器)
            bytes4 selector;
            assembly {
                selector := mload(add(msg.data, 32))
            }
            // 根据函数选择器执行不同的逻辑
            if (selector == bytes4(keccak256("someFunction()"))) {
                // 调用内部函数someFunction
                someFunction();
            } else {
                // 其他处理
            }
        }
    }

    // 提取资金函数:仅合约所有者可以调用
    function withdraw(uint256 _amount) external onlyOwner {
        require(address(this).balance >= _amount, "Insufficient contract balance");
        payable(owner).transfer(_amount);
    }

    // 获取合约当前余额
    function getContractBalance() external view returns (uint256) {
        return address(this).balance;
    }

    // 修饰符:限制只有合约所有者可以调用
    modifier onlyOwner() {
        require(msg.sender == owner, "Only the contract owner can call this function");
        _;
    }
}

详细解释和注释

  1. 合约声明:

    • contract FallbackExample {}: 定义一个合约。
  2. 合约所有者:

    • address public owner;: 记录合约的所有者地址。
  3. 事件:

    • event EtherReceived(address indexed sender, uint256 amount);: 当收到以太币时触发。
    • event FallbackCalled(address indexed sender, uint256 amount, bytes data);: 当调用不存在的函数或向合约发送以太币但没有receive函数时触发。
  4. 构造函数:

    • constructor() { owner = msg.sender; }: 部署合约时执行,设置合约所有者。
  5. receive函数:

    • receive() external payable { emit EtherReceived(msg.sender, msg.value); }: 接收纯以太币转账时调用,记录发送者和金额。
  6. fallback函数:

    • fallback() external payable { emit FallbackCalled(msg.sender, msg.value, msg.data); ... }: 当调用不存在的函数或向合约发送以太币但没有receive函数时调用,记录发送者、金额和调用数据。

    • if (msg.data.length > 0) { ... }: 检查传入数据是否存在。

    • assembly { selector := mload(add(msg.data, 32)) }: 使用内联汇编从msg.data中加载前四个字节作为函数选择器。

    • if (selector == bytes4(keccak256("someFunction()"))) { ... }: 检查函数选择器是否匹配特定函数,并调用相应逻辑。

  7. 提现函数:

    • function withdraw(uint256 _amount) external onlyOwner { ... }: 提取合约中的以太币,只有合约所有者可以调用。
  8. 获取合约余额函数:

    • function getContractBalance() external view returns (uint256) { return address(this).balance; }: 返回合约当前的以太币余额。
  9. 修饰符:

    • modifier onlyOwner() { require(msg.sender == owner, "Only the contract owner can call this function"); _; }: 限制只有合约所有者可以调用某些函数。

总结

  • receive函数:用于处理接收纯以太币转账。
  • fallback函数:用于处理调用不存在的函数或向合约发送以太币但没有receive函数时的情况。可以根据传入的msg.data执行不同的逻辑。
  • 数据处理fallback函数可以使用内联汇编解析传入的数据,并根据数据执行相应的逻辑,如调用特定的内部函数。
点赞 0
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Louis
Louis
web3 developer,技术交流或者有工作机会可加VX: magicalLouis