接收函数 receive

本章学习 Solidity 中的接收函数 receive

视频Bilibili  |  Youtube
官网binschool.app
推特@BinSchool    DiscordBinDAO   微信:bkra50 

我们在学习智能合约的接收函数 receive 之前,必须先理解以太坊中的账户类型。

1. 账户类型

在以太坊区块链中存在两种类型的账户:外部账户合约账户,它们在以太坊上有着不同的特性和用途。

1)外部账户

外部账户,英文为 Externally Owned Account,缩写为 EOA

外部账户,也就是我们平常使用的用户账户,用于存储以太币 ETH 。这些账户可以向其它账户发送以太币,或者从其它账户接收以太币。

我们在钱包里管理的账户,通常就是外部账户。比如,在小狐狸钱包 Metamask 里添加或者生成的 Account 就是外部账户。

外部账户会有一个与之相关的以太坊地址,这个地址是一个以 "0x" 开头,长度为20字节的十六进制数,比如:0x7CA35...9C6F。

外部账户都有一个对应的私钥,只有持有私钥的人才能对交易进行签名,所以,外部账户非常适用于资金管理。

我们常说的以太坊账户,在不特别指明的情况下,一般是指外部账户。

2)合约账户

合约账户,英文为 Contract Account,缩写为 CA

我们在以太坊区块链上部署一个智能合约后,都会产生一个对应的合约地址,这个地址称为合约账户。

合约账户主要用于托管智能合约,它里面包含着智能合约的二进制代码和状态信息。

合约账户地址的格式与外部账相同:以 "0x" 开头,长度为20字节的十六进制数。

合约账户没有私钥,只能由智能合约中的代码逻辑进行控制。

它在一定条件下,也可以用来存储以太币 ETH

2. receive 函数

在以太坊区块链上部署智能合约时产生的合约账户,并不都是可以存入以太币ETH 的。

一个智能合约如果允许存入以太币,就必须实现 receive 或者 fallback 函数。如果一个智能合约中这两个函数都没有定义,那么它就不能接收以太币。

如果只是为了让合约账户能够存入以太币,按照 solidity 语言规范,推荐使用 receive 函数。因为 receive 函数简单明了,目的明确,而 fallback  函数的用途相对复杂一些,我们将在后一章节详细讲解。

定义 receive 函数的格式如下:

receive() external payable {
    // 这里可以添加自定义的处理逻辑,但也可以为空
}

receive 函数有如下几个特点:

  • 1)无需使用 function 声明。
  • 2)参数为空。
  • 3)可见性必须设置为 external
  • 4)状态可变性必须设置为 payable

当外部地址向智能合约地址发送以太币时,将触发执行 receive 函数。

我们可以在函数体内不写任何自定义的处理逻辑,它依然能够接收以太币,这也是最常见的使用方式。

如果必须在 receive 的函数体内添加处理语句的话,最好不要添加太多的业务逻辑。

因为外部调用 sendtransfer 方法进行转账的时候,为了防止重入攻击,gas 会限制在 2300。如果 receive 的函数太复杂,就很容易会耗尽 gas,从而触发交易回滚。

receive 函数里通常会执行一些简单记录日志的动作,比如触发 event

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract FuncReceive {
  // 定义接收事件
  event Received(address sender, uint amount);
 
  // 接收 ETH 时,触发 Received 事件
  receive() external payable {
    emit Received(msg.sender, msg.value);
} }

3. 部署测试

我们把合约代码复制到 Remix,进行编译,并部署到区块链上:

 

部署完成后,在 Value 中输入要存入的 ETH 数量,比如 100 wei。然后点击下方的 Transact,就会把当前账户中的 100 wei 转入到合约中,你可以看到当前合约的 Balance 变为 0.0000000000000001 ETH。 另外,查看右下方控制台的输出日志中,我们可以看到 Received 事件被触发。