19. solidity 接收ETH receive() 和 fallback()

19. 接收ETH receive() 和 fallback()

Solidity支持两种特殊的回调函数,receive()fallback(),他们主要在两种情况下被使用:

  1. 接收ETH
  2. 处理合约中不存在的函数调用(代理合约proxy contract)

注意⚠️:在solidity 0.6.x版本之前,语法上只有 fallback() 函数,用来接收用户发送的ETH时调用以及在被调用函数签名没有匹配到时,来调用。 0.6版本之后,solidity才将 fallback() 函数拆分成 receive()fallback() 两个函数。

这里主要讲接收ETH的情况。

接收ETH函数:receive() external payable { ... }

receive()只用于处理接收ETH。一个合约最多有一个receive()函数,声明方式与一般函数不一样,不需要function关键字:receive() external payable { ... }receive()函数不能有任何的参数,不能返回任何值,必须包含externalpayable

当合约接收ETH的时候,receive()会被触发。receive()最好不要执行太多的逻辑因为如果别人用sendtransfer方法发送ETH的话,gas会限制在2300receive()太复杂可能会触发Out of Gas报错;如果用call就可以自定义gas执行更复杂的逻辑(这三种发送ETH的方法我们之后会讲到)。

我们可以在receive()里发送一个event,例如:

    // 定义事件
    event Received(address Sender, uint Value);
    // 接收ETH时释放Received事件
    receive() external payable {
        emit Received(msg.sender, msg.value);
    }

有些恶意合约,会在receive() 函数(老版本的话,就是 fallback() 函数)嵌入恶意消耗gas的内容或者使得执行故意失败的代码,导致一些包含退款和转账逻辑的合约不能正常工作,因此写包含退款等逻辑的合约时候,一定要注意这种情况。

回退函数:fallback() external payable { ... }

fallback()函数会在调用合约不存在的函数时被触发。

可用于接收ETH,也可以用于代理合约proxy contractfallback()声明时不需要function关键字,必须external修饰,一般也会用payable修饰,用于接收ETH:fallback() external payable { ... }

我们定义一个fallback()函数,被触发时候会释放fallbackCalled事件,并输出msg.sendermsg.valuemsg.data:

    // fallback
    fallback() external payable{
        emit fallbackCalled(msg.sender, msg.value, msg.data);
    }

receive和fallback的区别

receivefallback都能够用于接收ETH,他们触发的规则如下:

触发fallback() 还是 receive()?
           接收ETH
              |
         msg.data是空?
            /  \
          是    否
          /      \
receive()存在?   fallback()
        / \
       是  否
      /     \
receive()   fallback()

简单来说,合约接收ETH时,只有msg.data为空且存在receive()时,才会触发receive()

其余情况都是触发fallback(),此时fallback()必须为payable

receive()payable fallback()均不存在的时候,向合约直接发送ETH将会报错(你仍可以通过带有payable的函数向合约发送ETH)。

Remix 演示

  1. 首先在 Remix 上部署合约 “Fallback.sol”。
  2. “VALUE” 栏中填入要发送给合约的金额(单位是 Wei),然后点击 “Transact”。 img
  3. 可以看到交易成功,并且触发了 “receivedCalled” 事件。 img
  4. “VALUE” 栏中填入要发送给合约的金额(单位是 Wei),“CALLDATA” 栏中填入随意编写的msg.data,然后点击 “Transact”。 img
  5. 可以看到交易成功,并且触发了 “fallbackCalled” 事件。 img

习题

  1. fallback(or receive)函数不能在合约内部调用

  2. vitalik想部署一个能接收ETH和msg.data的合约,那么他部署的合约中必须含有fallback函数

  3. 假设存在如下合约,现在vitalik向该合约发起一笔低级交互,value=100Wei,msg.data=0xaa,如下图所示:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LVIaPjuZ-1680009419173)(null)]

    则会报错:

    error:'Fallback' function is not defined,value和msg.data均发送失败
    
  4. 假设存在如下合约,现在vitalik想调用该合约中不存在的函数,他在calldata中输入函数选择器,并将value设置为1ETH,如下图所示:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lAFsmYrn-1680009419189)(null)]

    则会报错:

    error:'Fallback' function is not defined,value和msg.data均发送失败
    

猜你喜欢

转载自blog.csdn.net/qq_42465670/article/details/129825403