以上三种转账方式都属于地址的成员属性(members of address)。
参见地址成员类型
Transfer
如果当前合约的余额不够大或者 Ether转账被接收账户拒绝,转账功能将失败。接收方智能合约应定义回退函数,否则转账调用将引发错误。transfer函数在失败时恢复。另外它被硬编码以防止重入攻击(这句话不是很能理解)。
示例1:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract AddressFunction {
function transfer(address payable _to) public payable {
_to.transfer(msg.value);
}
}
测试
Send
Send是和Transfer具有同等功能的低级api。如果执行失败,当前合约不会因为异常而停止,但会返回false。
示例2:
function send(address payable _to) public payable {
bool isSend = _to.send(msg.value);
require(isSend, "Send fail");
}
初看这段代码总会感觉怪怪的,一般思维调send方法不是发送方吗(solidity设计的某些api反人类),其实这里的_to指向的是接收方(包括transfer使用)。
这里为什么要加上payable关键字,它可以修饰地址和函数,使之具有接受和转账以太的功能。
贴一个官方文档的说明;
测试
send与transfer都有2300gas的限制,msg.value可以为零。官方推荐在收款人取款的模式下,使用transfer更好,因为send需要不断对发送地址余额进行判断。
尝试转移大于发送地址余额的value,此时交易将一直处于pending状态....
transact to AddressFunction.send pending ...
Call
这是将 ETH 发送到智能合约的推荐方式。空参数触发接收地址的回退功能(fallback function)。
示例3:
function calls(address payable _to) public payable {
(bool isSuccess, /* memory data */ ) = _to.call{value: msg.value}("");
require(isSuccess, "Failure! Ether not send.");
}
测试
使用call,还可以触发合约中定义的其他功能,并发送固定数量的gas来执行该功能。交易状态作为布尔值发送,返回值在数据变量(bytes memory data)中发送。
更具体使用的格式如下:
(bool sent, bytes memory data) = _to.call{gas :10000, value: msg.value}("func_signature(uint256 args)");
2019年,solidity官方已经弃用了send和transfer,推荐call方法进行转账操作,但还是要小心使用官方给出了警告:
参考: https://medium.com/coinmonks/solidity-transfer-vs-send-vs-call-function-64c92cfc878a
Types — Solidity 0.8.17 documentation