Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solidity中的回退函数详解 #63

Open
MagicalBridge opened this issue Jun 22, 2024 · 0 comments
Open

Solidity中的回退函数详解 #63

MagicalBridge opened this issue Jun 22, 2024 · 0 comments

Comments

@MagicalBridge
Copy link
Owner

基本概念

在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函数可以使用内联汇编解析传入的数据,并根据数据执行相应的逻辑,如调用特定的内部函数。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant