无论是合约还是外部账户,目前都无法阻止有人向它们发送以太币。合约可以对普通的转账做出反应并拒绝,但有一些方法可以在不创建消息调用的情况下转移以太币。一种方法是将合约地址设置为接收挖矿奖励的地址,另一种方法是使用 selfdestruct(x)
函数。这个函数会销毁合约并将其中的以太币发送到 x
地址。
本文只讨论在合约中处理普通转账(函数调用的交易)的情况。
一个合约要想接收普通转账的以太币,必须实现 receive
或者 payable 类型的 fallback
,否则在转账给合约时,合约会拒绝接收并抛出异常,以太币也会被返回。
在 Solidity 0.6.x 版本之前,只存在 fallback 函数,用来接收以太币,并在外部调用函数没有匹配时被调用。但这违背了单一职责原则,容易导致代码混乱并造成错误。后来引入的 receive 函数专门用来处理接收以太币的情况。可能是为了向后兼容,也可能是考虑转账交易本身就无法匹配合约中的任何函数,fallback 函数依然保留了处理以太币的功能,但需要为 fallback 函数添加 payable 修饰符。
一个合约最多可以有一个 receive 函数,这个函数不能有参数,不能返回任何东西,必须具有 external 和 payable 修饰符。只有纯以太坊转账交易(也不能带有 calldata)时,该函数会被调用。
receive() external payable {
emit Received(msg.sender, msg.value);
}
一个合约最多可以有一个 fallback 函数,这个函数必须具有 external 修饰符,如需接收以太币,则要添加 payable 修饰符。
// 不附带参数的声明
fallback () external [payable]
// 附带参数的声明
fallback (bytes calldata input) external [payable] returns (bytes memory output)
接收ETH
|
msg.data是空?
/ \
是 否
/ \
receive()存在? fallback()
/ \
是 否
/ \
receive() fallback()