发送以太币 ETH

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

推特@Hita_DAO    DiscordHitaDAO

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