一个合约中最多有一个receive函数,与fallback带payable形式一样,用来接收以太,函数声明:
receive() external payable {
......
}
不需要function关键字,无参无返回值,且必须是external 与 payable 修饰。receive函数可以是virtual的,可以被重载也可以有修改器modifier。
之前一篇文章测试了fallback回退函数,可参考Solitidy - fallback 回退函数 - 2种触发执行方式_瘦身小蚂蚁的博客-优快云博客
fallback有个2个执行条件:
- 如果在一个合约的调用中,没有其他函数与给定的函数标识符匹配时(或没有提供调用数据),fallback函数会被执行;
- 当合约收到以太时,fallback函数会被执行。
receive只有1个执行条件:
当合约收到以时,receive函数会被执行
现在来演示一下receive函数是如何接收以太的:
测试合约代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
// 包含receive函数的合约,合约账户能够接收到其它合约转的以太
contract TestReceive {
string message;
//构造函数,初始化状态变量message,同时可向合约账户存款
constructor() payable {
message = "hello";
}
//回退函数,能够为此合约账户接收以太
receive() external payable {
}
//存款,若部署时忘记存款,可直接调用此函数向合约账户存款
function deposit() external payable {
}
//发送以太
function sendEther(address _addr) external {
bool result = payable(_addr).send(2);
require(result, "send fail");
}
//查看合约账户余额
function getContractBalance() external view returns (uint256) {
return address(this).balance;
}
}
// 不包含receive函数的合约
contract TestWithoutReceive {
//构造函数,初始化时可向合约账户存款
constructor() payable{
}
//存款,若部署时忘记存款,可直接调用此函数向合约账户存款
function deposit() external payable {
}
//发送以太
function sendEther(address _addr) external returns (bool) {
bool result = payable(_addr).send(2);
return result;
}
//查看合约账户余额
function getContractBalance() external view returns (uint256) {
return address(this).balance;
}
}
部署
部署时可直接向合约转 20Wei,若部署时忘记转,则可使用合约中的 deposit 存款函数向合约转 20Wei,如下图所示:
测试步骤与结果
(1)使用未含receive合约函数向有receive合约发送以太(转账成功)
- 调用未含receive合约(TestWithoutReceive)中的发送以太函数sendEther,参数为含receive合约(TestReceive)地址,转2Wei;
- 查看TestWithoutReceive合约账户余额,发现少了2Wei,当前为18Wei了,证明转账成功;
- 查看TestReceive合约账户余额,发现多了2Wei,当前为22Wei,证明接收成功,合约账户能够接收以太,是因为合约中含有receive函数。
(2)使用含receive合约函数向未含receive合约发送以太(转账失败)
使用含receive合约函数向未含receive合约发送以太,发现转账失败,报错了(如下图所示),即没有receive函数的合约不能接收以太。官网文档解释如下:
一个没有定义 receive函数,也没有fallback函数带payable修饰符的合约,直接接收以太(没有函数调用,即使用 send 或 transfer)会抛出一个异常, 并返还以太(在 Solidity v0.4.0 之前行为会有所不同)。所以如果你想让你的合约接收以太,必须实现 receive函数,或是fallback函数带payalbe。