发送以太币 ETH
本章讲解在 Solidity
中,智能合约发送 ETH
的三种方法 transfer
、send
和 call
。
Solidity
中有三个函数可以发送 ETH
,分别是 transfer
、send
和 call
,它们既有一些相同之处,又有明显的区别,都有自己合适的使用场景。
1. transfer 函数
transfer
是 Solidity
中最安全的发送以太币 ETH
的方法。
当使用 transfer
时,将会转移指定数量的以太币到目标地址,如果发送失败,整个操作将会回滚。
在 Solidity
0.8 版本以后,为了防止重入攻击,调用 transfer
时,只携带 2300 gas
,不足以再次调用转账函数。所以,使用 transfer
发送 ETH
非常安全。
transfer
函数适合于简单的以太币转账。
transfer 函数的使用示例:
address payable receiver = 0x123...; // 接收 ETH 的地址 uint amount = 1 ether; // 要发送的以太币数量 receiver.transfer(amount);
2. send 函数
send
是另一种发送以太币的方法,它与 transfer
类似,但它返回一个布尔值来指示发送是否成功。
如果发送失败,send
不会回滚交易,而是返回布尔值 false
。因此,在使用它时需要手动处理发送失败的情况。
send
和 transfer
一样,只携带 2300 gas
,所以,使用 send
转账也是安全的。
在实际应用中,send
函数转账的使用率不高,主要原因是需要手动处理失败情况,略微麻烦。
address payable receiver = 0x123...; // 接收 ETH 的地址 uint amount = 1 ether; // 要发送的以太币数量 bool success = receiver.send(amount); if (!success) { // 处理发送失败的情况 }
3. call 函数
call
是最灵活,但也是最危险的发送以太币的方法之一。它是一种低级操作,允许有更多的参数,非常灵活,但使用时需要格外小心,容易遭到重入攻击。
call
可以接受额外的参数,并且可以指定 gas
和 ether
数量,以及调用合约中的任意函数。
call
函数同 send
一样,也有一个布尔返回值,需要手动处理发送失败的情况。
使用 call
函数进行转账,也是最常用的方式之一,主要原因就是它不受 gas
的限制,可以做更多的事情,非常灵活。
address payable receiver = 0x123...; // 接收 ETH 的地址 uint amount = 1 ether; // 要发送的以太币数量 (bool success, ) = receiver.call{value: amount}(""); if (!success) { // 处理发送失败的情况 }
4. 建议方法
我们通常建议优先使用 transfer
,因为它更安全,且在大多数情况下,可以满足基本的以太币转账需求。
只有在特殊情况下,需要更高级的操作时才考虑使用 send
或 call
,但使用时需要特别小心,以避免潜在的安全风险。
5. 示例合约
我们编写一个使用三种方法发送 ETH
的合约,目标接收地址要求属性 payable
。因为有些合约地址没有定义 receive
或者 fallback
函数,所以不可以接收以太币。
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract SendETH { // 构造函数的属性 payble,在部署时可以存入以太币 constructor() payable {} function transfer(address payable receiver, uint256 amount) external payable returns(bool) { // 调用 transfer,交易失败回滚 receiver.transfer(amount); return true; } function send(address payable receiver, uint256 amount) external payable returns(bool) { // 调用 send,交易失败返回 false bool success = receiver.send(amount); return success; } function call(address payable receiver, uint256 amount) external payable returns(bool) { // 调用 call,交易失败返回 false (bool success,) = receiver.call{value: amount}(""); return success; } }
三个函数中的接收地址 receiver
,既可以是一个 EOA
账户地址,也就是普通账户地址,也可以是一个可接收 ETH
的合约地址。
我们把上面的合约复制到 Remix
里,进行编译,部署的时候存入 10 个以太币。
然后,再找一个 Remix
里提供的外部地址作为接收者,分别调用三种方法,各发送 1 ETH
。
我们可以看到当前余额已经变为 7 ETH
。