发送以太币 ETH

本章讲解在 Solidity 中,智能合约发送 ETH 的三种方法 transfersendcall

官网binschool.app
推特@BinSchool    DiscordBinDAO   微信:bkra50 

Solidity 中有三个函数可以发送 ETH,分别是 transfersendcall,它们既有一些相同之处,又有明显的区别,都有自己合适的使用场景。

1. transfer 函数

transferSolidity 中最安全的发送以太币 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。因此,在使用它时需要手动处理发送失败的情况。

sendtransfer 一样,只携带 2300 gas,所以,使用 send 转账也是安全的。

在实际应用中,send 函数转账的使用率不高,主要原因是需要手动处理失败情况,略微麻烦。

address payable receiver = 0x123...; // 接收 ETH 的地址
uint amount = 1 ether; // 要发送的以太币数量
bool success = receiver.send(amount);
if (!success) {
    // 处理发送失败的情况
}

3. call 函数

call 是最灵活,但也是最危险的发送以太币的方法之一。它是一种低级操作,允许有更多的参数,非常灵活,但使用时需要格外小心,容易遭到重入攻击。

call 可以接受额外的参数,并且可以指定 gasether 数量,以及调用合约中的任意函数。

call 函数同 send 一样,也有一个布尔返回值,需要手动处理发送失败的情况。

使用 call 函数进行转账,也是最常用的方式之一,主要原因就是它不受 gas 的限制,可以做更多的事情,非常灵活。

address payable receiver = 0x123...; // 接收 ETH 的地址
uint amount = 1 ether; // 要发送的以太币数量

(bool success, ) = receiver.call{value: amount}("");
if (!success) {
    // 处理发送失败的情况
}

4. 建议方法

我们通常建议优先使用 transfer,因为它更安全,且在大多数情况下,可以满足基本的以太币转账需求。

只有在特殊情况下,需要更高级的操作时才考虑使用 sendcall,但使用时需要特别小心,以避免潜在的安全风险。

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