Solidity 金融和支付 3| 发送以太币 (Send ETH)

在 Solidity 中,向其他地址发送 ETH 主要有三种方式:transfersendcall

1. transfer(不推荐用于新项目)

  • 语法: payableAddress.transfer(amount);
  • 特点:
    • 自动转发 2300 gas,足够记录日志,但不足以指向复杂的逻辑或状态更改。
    • 如果转账失败,会 自动回滚 交易(revert)。
    • 从 Solidity 0.6 开始,目标地址必须是 address payable 类型。
  • 适用场景: 简单转账,接收方为普通钱包或简单合约,无需担心重入攻击(因为 gas 限制)。
  • 缺点: gas 固定为 2300,未来以太坊硬分叉可能该变 gas 成本(如 lstanbul 升级后某些操作码 gas 增加),导致 transfer 可能失败。因此,官方逐渐建议弃用。

2. send (不推荐)

  • 语法: bool success = payableAddress.send(amount);
  • 特点:
    • 同样只提供 2300 gas。
    • 转账失败时 不会自动回滚 ,而是返回 false。开发者需要手动检查返回值并处理失败情况。
  • 适用场景: 极少使用,因为需要手动处理失败,且 gas 限制问题与 transfer 相同。
  • 注意: 必须检查返回值,否则即使失败,交易也会继续执行,可能导致状态不一致。

3. call (目前推荐的方式)

  • 语法: (bool success,) = payableAddress.call{value: amount}("");
  • 特点:
    • 转发所有可用 gas(默认),也可以手动指定 gas,如 {value:amount,gas: gasAmount}
    • 失败时返回 false 不会自动回滚 ,需要开发者检查 success 并决定是否 revert
    • 可以调用任意函数(通过编码数据),所以更灵活。
    • 目标地址无需时 payable,因为 call 会将地址视为 address,但若需要接收 ETH,接收方仍需有receive()fallback() 函数。
  • 安全性: 由于转发所有 gas,容易引发 重入攻击 。因此,使用 call 发送 ETH 时, 必须 遵循"检查-生效-交互" 模式,并考虑使用重入锁(如OpenZeppelin 的 ReentrancyGuard)。

示例代码:使用 call 发送 ETH

solidity 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SendEther {
    // 事件记录转账
    event Sent(address indexed to, uint256 amount);

    // 向指定地址发送 ETH
    function sendViaCall(address payable _to) external payable {
        // 使用 call 发送 msg.value(调用函数时附带的 ETH)
        (bool success, ) = _to.call{value: msg.value}("");
        require(success, "ETH transfer failed");
        emit Sent(_to, msg.value);
    }

    // 接收 ETH 的函数(必须存在,否则合约无法接收直接转账)
    receive() external payable {
        // 可以记录收到 ETH 的事件
    }
}

解释

  • sendViaCall 是一个 payable 函数,调用时可以附带 ETH(msg.value)。
  • 使用 _to.call{value: msg.value}("") 将 ETH 转发给 _to 地址。
  • 检查返回值,失败时 revert
  • 合约必须定义 receive()fallback() 才能接收直接转账(如通过 sendtransfercall 发送的普通转账)。

接收 ETH 的合约要求

如果目标地址是一个合约,它必须实现以下函数之一才能成功接收 ETH(否则转账会失败):

  • receive() external payable: 当调用数据为空时执行(即纯 ETH 转账)。
  • fallback() external payable: 当没有匹配的函数且调用数据非空时执行,也可以接收 ETH。

示例接收合约:

solidity 复制代码
contract Receiver {
    event Received(address indexed from, uint256 amount);

    receive() external payable {
        emit Received(msg.sender, msg.value);
    }
}

安全性总结

  • 重入攻击 :使用 call 时,如果接收方是恶意合约,它可以在 receive 中回调发送方函数,导致未预期的状态更改。解决方案:
    • 先更新余额等状态,再发送 ETH("检查-生效-交互"模式)。
    • 使用重入锁修饰器。
  • Gas 限制transfersend 的 2300 gas 可能不足,未来分叉可能更不可靠。call 默认转发所有 gas,更灵活,但需防范重入。
  • 返回值检查sendcall 必须检查返回值,transfer 不需要。

建议 :在 Solidity 0.8.x 及以上版本,优先使用 call 并配合重入保护,或使用 OpenZeppelin 的 Address.sendValue 库函数(内部也是用 call,并处理了返回值)。

相关推荐
普通网友8 小时前
数据加密与零知识证明在区块链中的应用解析
区块链·零知识证明
御坂100578 小时前
区块链智能合约AI化:链下计算+TensorRT验证
区块链· 智能合约· tensorrt
BlockChain8888 小时前
区块链入门【一】:揭开“信任机器”的神秘面纱
区块链·ai编程
QQ5110082858 小时前
基于区块链的个人医疗咨询挂号信息系统vue
前端·vue.js·区块链
BlockChain8888 小时前
区块链的组件:从数据结构到去中心化共识
数据结构·去中心化·区块链
Jerry.张蒙18 小时前
大语言模型(LLM)的核心逻辑理解
大数据·人工智能·学习·语言模型·自然语言处理·区块链
Joy T19 小时前
【Web3】NFT 元数据去中心化存储与智能合约集成实战
开发语言·web3·去中心化·区块链·php·智能合约·hardhat
wayz111 天前
3.1 标准化流程:数据清洗→因子计算→分层回测→IC/IR分析
金融·量化交易
Agent产品评测局2 天前
保险行业自动化工具选型,核保理赔全流程优化:2026年大模型Agent重塑数智金融新基座
大数据·人工智能·ai·金融·自动化
想你依然心痛2 天前
HarmonyOS 5.0金融安全APP开发实战:基于可信执行环境与分布式风控的移动支付系统
安全·金融·harmonyos