Solitidy - fallback 回退函数 - 2种触发执行方式

本文详细介绍了以太坊智能合约中的fallback函数,包括其作为无名、无参数、无返回值的外部函数特性,以及两种执行情况:调用不存在的函数和接收以太币。通过示例合约代码,展示了如何在合约中实现和测试fallback函数,以及在转账过程中如何使用。同时,文中提到在fallback函数中尝试修改状态变量会导致转账失败的情况。
摘要由CSDN通过智能技术生成

fallback简介

详情参考: 合约 — Solidity develop 文档

fallback 函数是合约中的一个未命名函数,没有参数且没有返回值,可见性必须是 external,且可以是 virtual的(即可以被重载),也可以有修改器 modifier。

fallback执行条件:

  1. 如果在一个合约的调用中,没有其他函数与给定的函数标识符匹配时(或没有提供调用数据),fallback函数会被执行;
  2. 当合约收到以太时,fallback函数会被执行。 

以下针对2种执行方式进行示例展示,源码也可以参见:smartcontract/Fallback at main · tracyzhang1998/smartcontract · GitHub

执行条件1 

如果在一个合约的调用中,没有其他函数与给定的函数标识符匹配时(或没有提供调用数据),fallback函数会被执行

测试合约代码

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

//基合约实现
contract TestFallback {
    string message;

    //构造函数,初始化状态变量message
    constructor() {
        message = "hello";
    }

    fallback() external {
        message = "fallback"; 
    }

    //调用此合约中不存在的函数
    function testFallback() external returns (bytes memory) {

        // 调用不存在的函数getMsgNew()
        bytes memory method = abi.encodeWithSignature("getMsgNew()");
        (bool success, bytes memory returnData) = address(this).call(method);

        require(success, "get fail");
        return returnData;
    }

    //调用此合约中已存在函数,但是没有传递参数
    function testFallbackWithNoParam() external returns (bytes memory) {

        // 调用已存在的函数setMsg(),未传递参数
        bytes memory method = abi.encodeWithSignature("setMsg()");
        (bool success, bytes memory returnData) = address(this).call(method);

        require(success, "set fail");
        return returnData;
    }

    function getMsg() external view returns (string memory) {
        return message;
    }

    function setMsg(string memory _message) external {
        message = _message;
    }
}

测试步骤与结果

(1)调用一个不存在的函数

0、部署合约,调用getMsg函数查看状态变量初始值为"hello"

1、调用函数testFallback,调用一个不存在的函数

2、调用getMsg函数查看状态变量已修改fallback函数中设置的"fallback"了

(2)调用一个已存在的函数但没传递参数 

1、调用setMsg函数设置状态变量初始值为"hello"

2、调用函数testFallbackWithNoParam,调用一个已存在的函数但没传递参数

3、调用getMsg函数查看状态变量已修改fallback函数中设置的"fallback"了

执行条件2

当合约收到以太时,fallback函数会被执行,为了接收以太,fallback 函数必须标记为 payable。 如果不存在这样的函数(如下代码片段),则合约不能通过常规交易接收以太。

fallback() external payable {
}

测试合约代码

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

// 包含fallback函数的合约,合约账户能够接收到其它合约转的以太
contract TestFallback {
    string message;

    //构造函数,初始化状态变量message,同时可向合约账户存款
    constructor() payable {
        message = "hello";
    }

    //回退函数,能够为此合约账户接收以太
    fallback() external payable {
    }

    //存款,若部署时忘记存款,可直接调用此函数向合约账户存款
    function deposit() external payable {
    }

    //发送以太
    function sendEther(address _addr) external {
        bool result = payable(_addr).send(2);
        require(result, "send fail");
    }
    
    //查看合约账户余额
    function getContractBalance() external view returns (uint256) {
        return address(this).balance;
    }
}


// 不包含fallback函数的合约
contract TestWithoutFallback {

    //构造函数,初始化时可向合约账户存款
    constructor() payable{
    }

    //存款,若部署时忘记存款,可直接调用此函数向合约账户存款
    function deposit() external payable {
    }

    //发送以太
    function sendEther(address _addr) external returns (bool) {
        bool result = payable(_addr).send(2);
        return result;
    }

    //查看合约账户余额
    function getContractBalance() external view returns (uint256) {
        return address(this).balance;
    }
}

部署

部署时可直接向合约转 20Wei,若部署时忘记转,则可使用合约中的 deposit 存款函数向合约转 20Wei,如下图所示:

测试步骤与结果

(1)使用未含fallback合约函数向有fallback合约发送以太(转账成功)

  1. 调用未含fallback合约(TestWithoutFallback)中的发送以太函数sendEther,参数为含fallback合约(TestFallback)地址,转2Wei;
  2. 查看TestWithoutFallback合约账户余额,发现少了2Wei,当前为18Wei了,证明转账成功;
  3. 查看TestFallback合约账户余额,发现多了2Wei,当前为22Wei,证明接收成功,合约账户能够接收以太,是因为合约中含有fallback函数(且为payable)。

(2)使用含fallback合约函数向未含fallback合约发送以太(转账失败)

使用含fallback合约函数向未含fallback合约发送以太,发现转账失败,报错了(如下图所示),即没有fallback函数的合约不能接收以太。官网文档解释如下:

一个没有fallback函数带payable修饰符的合约,也没有定义 receive函数(receive函数可参见 Solidity - receive 接收以太函数 - 1种触发方式_瘦身小蚂蚁的博客-CSDN博客),直接接收以太(没有函数调用,即使用 send 或 transfer)会抛出一个异常, 并返还以太(在 Solidity v0.4.0 之前行为会有所不同)。所以如果你想让你的合约接收以太,必须实现 fallback 函数。

遇到的问题

在测试时,想通过查看状态变量message看是否发生调用fallback,在fallback函数中对message状态变量修改值,如下所示:

    fallback() external payable {

        message = "fallback";

    }

测试使用未含fallback合约函数向有fabllback合约发送以太,如上面测试结果应该转账成功,但是此时转账会失败,如下图所示:

最终去除了fallback函数中设置message状态变量,函数体未包含任何内容,转账成功,与第(1)步测试结果相同。

    fallback() external payable {

    }

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值