“与其感慨路难行,不如马上出发。”
接收以太币

无论是合约还是外部账户,目前都无法阻止有人向它们发送以太币。合约可以对普通的转账做出反应并拒绝,但有一些方法可以在不创建消息调用的情况下转移以太币。一种方法是将合约地址设置为接收挖矿奖励的地址,另一种方法是使用 selfdestruct(x) 函数。这个函数会销毁合约并将其中的以太币发送到 x 地址。

本文只讨论在合约中处理普通转账(函数调用的交易)的情况。

概述

一个合约要想接收普通转账的以太币,必须实现 receive 或者 payable 类型的 fallback,否则在转账给合约时,合约会拒绝接收并抛出异常,以太币也会被返回。

在 Solidity 0.6.x 版本之前,只存在 fallback 函数,用来接收以太币,并在外部调用函数没有匹配时被调用。但这违背了单一职责原则,容易导致代码混乱并造成错误。后来引入的 receive 函数专门用来处理接收以太币的情况。可能是为了向后兼容,也可能是考虑转账交易本身就无法匹配合约中的任何函数,fallback 函数依然保留了处理以太币的功能,但需要为 fallback 函数添加 payable 修饰符。

receive 函数

一个合约最多可以有一个 receive 函数,这个函数不能有参数,不能返回任何东西,必须具有 externalpayable 修饰符。只有纯以太坊转账交易(也不能带有 calldata)时,该函数会被调用。

receive() external payable {
    emit Received(msg.sender, msg.value);
}

fallback 函数

一个合约最多可以有一个 fallback 函数,这个函数必须具有 external 修饰符,如需接收以太币,则要添加 payable 修饰符。

// 不附带参数的声明
fallback () external [payable]

// 附带参数的声明
fallback (bytes calldata input) external [payable] returns (bytes memory output)

触发条件判断

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