接收ETH
Solidity 支持两种特殊的回调函数,分别是receive()和 fallback()
(1)接收ETH使用recieve
(2)处理合约中不存在的函数调用,或者用于代理合约使用fallback
⚠️:Solidity 版本0.6.x之前,语法只有fallback()函数,既在接收用户发送ETH的时候,也在被调用函数签名没有匹配到的时候调用。
0.6.x之后,Solidity讲fallback拆分成receive()和 fallback()
接收 ETH的回调函数receive()
receive() 函数是在合约收到ETH转账时被调用的函数。一个合约最多有一个receive函数。声明方式不需要function关键字:
receive() external payable {...}
不能有任何参数
不能返回任何值
必须声明关键字external 和 payable
⚠️:receive 最好不要执行太多的逻辑,因为如果其他合约send和transfer方法发送ETH的话gas费用被限制在2300,太复杂会触发Out of Gas报错;如果call 发送ETH,就可以自己自定义gas 执行更复杂的逻辑
有些恶意合约,会在receive函数潜入恶意消耗gas的内容或者执行故意失败的代码,导致一些包含退款和转账逻辑的合约不能正常工作。因此包含这些逻辑的时候,一定注意这些情况。
event receivedCalled(address Sender,uint Value);
receive() external payable {
emit receivedCalled(msg.sender, msg.value);
}
回退函数fallback()
在调用合约不存在的函数时被触发。可用于接收ETH,也可以用于代理合约。声明也不用function关键字。必须声明external,一搬同时声明payable:
fallback() external payable {...}
event fallbackCalled(address Sender,uint Value, bytes Data);
fallback() external payable {
emit fallbackCalled(msg.sender, msg.value, msg.data);
}
两种回调函数区别
image.png
发送ETH
Solidity 有三种方法向其他合约发送ETH:transfer()、send()、和call()。其中call()是当前推荐的
接收ETH 的合约
部署一个接收ETH的合约ReceiveETH。
Log事件,用于在日志中记录收到的ETH数量和gas的剩余量。
包含receive()函数,收到其他合约转入出发,并释放log事件
查询余额的getBalance()函数
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract ReceiveETH {
event Log(uint amount, uint gas);
receive() external payable {
emit Log(msg.value, gasleft());
}
function getBalance() view public returns(uint) {
return address(this).balance;
}
}
发送ETH 的合约
发送ETH的合约SendETH,在其中实现payable的构造函数和receive()回调函数,我们能偶在部署适合部署后想合约转账
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract SendETH {
constructor() payable {}
receive() external payable {}
function transferETH(address payable _to,uint amount) external payable {
_to.transfer(amount);
}
error SendFailed();
function sendETH(address payable _to,uint amount)external payable {
bool success = _to.send(amount);
if(!success) {
revert SendFailed();
}
}
error CallFailed();
function callETH(address payable _to,uint amount)external payable {
(bool success,) = _to.call{value: amount}("");
if(!success) {
revert CallFailed();
}
}
}
- 使用 transfer()发送ETH
transfer用法:_Address.transfer(amount);
_Address 是接收方地址,amount是发送ETH的数量
注意⚠️:
(1)transfer()的gas限制是2300,用于转账时足够的,但对于合约的receive()函数或者fallback()函数不能实现太复杂的逻辑
(2)transfer()如果转账失败,会自动revert(会滚交易)。
测试:填入前面ReceiveETH的合约地址和金额,调用transferETH()发送ETH。测试amount 给10
在调用时,我们Account中value 默认0 ,不够交易所以交易失败了revert
修改value为10,再次调用交易成功。
- 使用send()发送ETH
send()函数的用法:_bool = _Address.send(amount)
_Address 是接收方地址,amount是发送ETH的数量
注意⚠️:
(1)send()的gas限制是2300,用于转账时足够的,但对于合约的receive()函数或者fallback()函数不能实现太复杂的逻辑
(2)如果转账失败,不会自动会滚
(3)send()函数会返回一个布尔值,代表转账成功或者失败。可使用这个值进行额外的处理。
测试:步骤如上 - 使用call() 发送ETH
call的函数用法:
(_bool,_data) = _Address.call{value: amount}(_bytes);
_Address 是接收方地址,amount是发送ETH的数量
{value:amount}是Solidity的特殊用法,位于函数和参数之间,用于置顶一些交易信息。call()函数带了一个bytes类型参数用于发送额外的二进制数据。
注意⚠️:
(1)call()没有gas限制,可以支持对方合约的fallback()或者receive()函数实现复杂逻辑
(2)call()如果失败,不会自动会滚
(3)call()有两个返回值,第一个返回值_bool是布尔类型,代表转账成功或者失败,可以使用这个返回值进行额外处理
第二个返回值_data为对方的消息数据
测试:步骤如上
