Solidity - Fallback Function探究

目录

事件起因

问题描述

问题分析

问题探究


事件起因

项目实训内容中涉及fallback函数,被我的专业老师(兼高级工程师=_=)出难题了,当时他只留下了一句话,“这就是你们和我的差距!”(藐视<-<),随后我便通宵达旦,好好研究了一番......

问题描述

  1. 为什么需要fallback函数,它究竟起什么作用?
  2. 合约中只有fallback函数,如何进行调用测试?
  3. receive函数必须写吗?什么情况才会调用?(0.6.x引入)

问题分析

带着这些让人头皮发麻的questions,我的脑细胞们已披甲持戈,准备浴血奋战了。

据官方解释,简单说fallback是一个不接受任何参数且不返回任何内容的函数。

它的执行场景有以下两个方面:

  • 调用一个不存在的函数时触发。
  • 发送Ether到一个合约地址中,但是receive()不存在或msg.data不为空。

另外,当通过transfer或send方法调用时,fallback函数有2300 gas的限制。


问题探究

这里为了体现与时俱进的创作理念,示例合约选取了最新0.8.x编译版本进行测试,合约编辑器使用的是remix-IDE desktop app版

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

contract fallbackTest {   
    fallback() payable external {}   
}

在fallbackTest合约中呢,只写了个fallback函数,编译部署成功后截图如下:

Solidity - Fallback Function探究

 那可以看到部署后,我们并没有相应的方法按钮去调用,怎么办呐。。。别急,这里提供了CALLDATA(low level interaction)一种别样的调用方式。那么问题来了,在 CALLDATA 下面的框框里应该填什么?(有兴趣的同学可以研究一下,这里我们只是测试fallback函数,里面啥都不用填)同时为了体现payable修饰符的作用,我们在部署界面找到value输入框,例如下图:

Solidity - Fallback Function探究

伴随着调用fallback函数,附加了发送1个以太(默认单位 Wei),然后只需点击CALLDATA框右边的 Transact 按钮就好了。那么这笔钱发送给谁了呢?其实是默认存入当前的合约账户里了。

Solidity - Fallback Function探究


问题进阶探究

下面的内容比较烧脑,无妨,我先抛个砖。

  Which function is called, fallback() or receive()?   

        send Ether
               |
    msg.data is empty?
              / \
            yes  no
            /     \
receive() exists?  fallback()
         /   \
        yes   no
        /      \
    receive()   fallback()

参考合约示例:

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

contract ReceiveEther {
    // Function to receive Ether. msg.data must be empty
    receive() external payable {}

    // Fallback function is called when msg.data is not empty
    fallback() external payable {}

    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}

contract SendEther {
    function sendViaTransfer(address payable _to) public payable {
        _to.transfer(msg.value);
    }

    function sendViaSend(address payable _to) public payable {
        bool sent = _to.send(msg.value);
        require(sent, "Failed to send Ether");
    }

    function sendViaCall(address payable _to) public payable {
        (bool sent, bytes memory data) = _to.call{value: msg.value}("");
        require(sent, "Failed to send Ether");
    }
}

好奇心十分强大的同学可以对此合约进行测试,从中体会fallback与receive函数的调用时机。因为本人困得一个字都码不来了~嗯,如果学习过程中,存在疑问或解释不当之处,还望各位道友加以批评指正,3Q!!!:)

上一篇:vs2019 jsoncpp 编译


下一篇:scoket通信(基础)