Solidity 102

1. 函数重载
2. 库合约
3. Import
4. 接收ETH
5. 发送ETH
6. 调用其他合约
7. Call
8. Delegatecall
9. 在合约中创建新合约
10. Create2
11. 删除合约
12. ABI编码解码
13. Hash
14. 选择器
15. Try Catch
19. 接收ETH
接收ETH

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

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

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

我们这一讲主要讲接收ETH的情况。

接收ETH函数 receive

receive()函数是在合约收到ETH转账时被调用的函数。一个合约最多有一个receive()函数,声明方式与一般函数不一样,不需要function关键字:receive() external payable { ... }。receive()函数不能有任何的参数,不能返回任何值,必须包含external和payable。

当合约接收ETH的时候,receive()会被触发。receive()最好不要执行太多的逻辑因为如果别人用send和transfer方法发送ETH的话,gas会限制在2300,receive()太复杂可能会触发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

fallback()函数会在调用合约不存在的函数时被触发。可用于接收ETH,也可以用于代理合约proxy contract。fallback()声明时不需要function关键字,必须由external修饰,一般也会用payable修饰,用于接收ETH:fallback() external payable { ... }。

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

event fallbackCalled(address Sender, uint Value, bytes Data);

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

receive和fallback的区别

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

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

简单来说,合约接收ETH时,msg.data为空且存在receive()时,会触发receive();msg.data不为空或不存在receive()时,会触发fallback(),此时fallback()必须为payable。

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

Remix 演示

  1. 首先在 Remix 上部署合约 "Fallback.sol"。

  2. "VALUE" 栏中填入要发送给合约的金额(单位是 Wei),然后点击 "Transact"。

    19-1.jpg

  3. 可以看到交易成功,并且触发了 "receivedCalled" 事件。

    19-2.jpg

  4. "VALUE" 栏中填入要发送给合约的金额(单位是 Wei),"CALLDATA" 栏中填入随意编写的msg.data,然后点击 "Transact"。

    19-3.jpg

  5. 可以看到交易成功,并且触发了 "fallbackCalled" 事件。

    19-4.jpg

总结

这一讲,我介绍了Solidity中的两种特殊函数,receive()和fallback(),他们主要在两种情况下被使用,处理接收ETH和代理合约proxy contract。

上一章下一章