尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
WTF-Solidity 极简入门:使用 transfer、send 与 call 三种方法向合约发送 ETH
WTF-Solidity 极简入门使用 transfer、send 与 call 三种方法向合约发送 ETH【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity导读在 Solidity 开发中向其他合约或地址发送 ETH 是最高频的底层操作之一而transfer()、send()、call()三种内置方法在 gas 限制、失败处理与安全性上差异显著直接决定交易是否会回滚、是否容易被重入攻击。本讲WTF-Solidity 第 20 讲将基于仓库中的 SendETH.sol 与 ReceiveETH.sol 完整源码逐一拆解三种发送方式的使用语法、gas 行为与返回值处理并给出可复现的 Remix 实操流程。读完本文你将能在自己的合约中正确地选择发送 ETH 的方式写出既灵活又安全的转账逻辑。一、三种发送 ETH 方法一览Solidity 向其他合约发送 ETH 主要有三种方法transfer()用法为接收方地址.transfer(发送ETH数额)send()用法为接收方地址.send(发送ETH数额)call()用法为接收方地址.call{value: 发送ETH数额}()。其中call()是当前被鼓励使用的方案也是现代 DeFi 合约中实际应用最广的方式。仓库源码 SendETH.sol 开头就用注释精炼总结了三者的核心差异// transfer: 2300 gas, revert // send: 2300 gas, return bool // call: all gas, return (bool, data)二、先写一个接收 ETH 的合约ReceiveETH要验证“发送”是否成功必须先有一个能接收 ETH 的合约。仓库中的ReceiveETH合约见 SendETH.sol结构非常简单却覆盖了接收 ETH 的两个关键点receive()回调与余额查询。contract ReceiveETH { // 收到eth事件记录amount和gas event Log(uint amount, uint gas); // receive方法接收eth时被触发 receive() external payable{ emit Log(msg.value, gasleft()); } // 返回合约ETH余额 function getBalance() view public returns(uint) { return address(this).balance; } }receive() external payable是合约接收原生 ETH 时被自动调用的特殊回调函数任何地址向该合约直接转账时都会触发它事件Log记录了两项关键数据msg.value本次收到的 ETH 数量单位 wei和gasleft()触发时剩余的 gas用于观测转账的 gas 消耗getBalance()通过address(this).balance返回合约当前持有的 ETH 余额。部署ReceiveETH合约后调用getBalance()可以看到当前合约余额为0这是后续所有转账实验的基准状态关于receive()的触发机制当一笔交易携带msg.data为空且携带 ETH 时优先执行receive()若合约未定义receive()则回退到fallback()。这一调度流程在仓库的 Fallback.sol 中有完整的注释图解可作为本讲的延伸阅读。三、再写一个发送 ETH 的合约SendETH接下来实现发送方合约SendETH。为了让测试完整先给它加上payable构造函数和receive()使合约在部署时和部署后都能接收 ETH后续实验中的value就是预先转入该合约的 ETHcontract SendETH { // 构造函数payable使得部署的时候可以转eth进去 constructor() payable{} // receive方法接收eth时被触发 receive() external payable{} }在此基础上SendETH合约实现了三个发送函数transferETH、sendETH、callETH并预先定义了对应的错误类型见 SendETH.solerror SendFailed(); // 用send发送ETH失败error error CallFailed(); // 用call发送ETH失败error四、transfer最简单但最不灵活语法与特性用法接收方地址.transfer(发送ETH数额)transfer()的 gas 限制为2300足够完成一次普通转账但对方合约的fallback()或receive()内不能实现太复杂的逻辑否则会因 gas 不足而失败转账失败时transfer()会自动revert回滚整个交易。代码实现// 用transfer()发送ETH function transferETH(address payable _to, uint256 amount) external payable{ _to.transfer(amount); }注意两个参数_to填ReceiveETH合约地址amount为本次转账的 ETH 数额单位 wei。实验验证部署SendETH合约后对ReceiveETH合约执行transferETH当amount为10、value为0时amountvalue即转账数额超过了合约余额交易失败并revert当amount为10、value为10时amountvalue转账成功此时回到ReceiveETH合约调用getBalance()可以看到其余额变为10wei验证 ETH 已成功入账。五、send返回 bool 但不会自动回滚语法与特性用法接收方地址.send(发送ETH数额)send()的 gas 限制同样是2300复杂逻辑同样可能因 gas 不足而失败与transfer()最大的不同转账失败时不会自动revertsend()的返回值是bool代表转账成功或失败需要开发者自己检查并处理。代码实现error SendFailed(); // 用send发送ETH失败error // send()发送ETH function sendETH(address payable _to, uint256 amount) external payable{ // 处理下send的返回值如果失败revert交易并发送error bool success _to.send(amount); if(!success){ revert SendFailed(); } }因为send()失败不会回滚交易所以必须像上面这样用bool success接收返回值并显式revert否则失败的转账会被“静默吞掉”调用方完全无感知。实验验证amount为10、value为0时转账失败由于代码显式检查了返回值交易同样发生revertamount为10、value为11时amountvalue转账成功。六、call无 gas 限制的推荐方案语法与特性用法接收方地址.call{value: 发送ETH数额}()call()没有 gas 限制可以支持对方合约fallback()或receive()实现复杂逻辑转账失败时不会自动revert返回值为(bool, bytes)bool表示成功与否bytes为对方返回的 data需要额外代码处理。代码实现error CallFailed(); // 用call发送ETH失败error // call()发送ETH function callETH(address payable _to, uint256 amount) external payable{ // 处理下call的返回值如果失败revert交易并发送error (bool success,) _to.call{value: amount}(); if(!success){ revert CallFailed(); } }用元组解构(bool success,)接收返回值忽略第二位的bytes数据失败时同样显式revert CallFailed()。实验验证amount为10、value为0时转账失败因检查了返回值而revertamount为10、value为11时转账成功。下方为使用callETH成功转账的 Remix 结果交易状态为成功并返回了完整的 gas 消耗数据七、三种方法横向对比方法gas 限制失败是否自动 revert返回值是否推荐transfer()2300是无次优选择send()2300否bool几乎没人用call()无限制否(bool, bytes)最提倡结论与 SendETH.sol 的注释一致call()无 gas 限制、最为灵活是最提倡的方法transfer()有 2300 gas 限制但失败会自动 revert 交易是次优选择send()有 2300 gas 限制且失败不会自动 revert几乎没有人使用。八、源码级延伸这些方法在真实合约中如何被使用理解了三种发送方式后可以结合本仓库其他章节的源码看看它们在实际场景中的应用以加深印象。1. call 不只是转账带 calldata 调用目标函数call()的真正威力在于它不仅能发 ETH还能同时携带调用数据。仓库 Call.sol 展示了如何在一次调用中同时执行对方函数并附带 ETH(bool success, bytes memory data) _addr.call{value: msg.value}( abi.encodeWithSignature(setX(uint256), x) );这里通过abi.encodeWithSignature编码函数选择器call{value: msg.value}在调用setX的同时把msg.value一并转给对方。这正是许多合约交互的标准姿势也是本讲callETH中空 calldata的进阶形态。2. receive/fallback 在接收端的实战WETH 的存款入口receive()与fallback()并非只能发个事件。仓库 WETH.sol 中用户直接向 WETH 合约转 ETH 时fallback()/receive()会自动触发deposit()铸造等量 WETH实现“转账即存款”fallback() external payable { deposit(); } receive() external payable { deposit(); }这说明接收方的回调函数越复杂对发送方 gas 的要求就越高——这也是call()无 gas 限制反而更安全的原因之一。3. transfer 的 2300 gas 限制与提款场景在 WETH.sol 的withdraw()中可以看到transfer的应用销毁 WETH 后向用户地址转回 ETH。因为目标通常是普通 EOA 地址而非复杂合约2300 gas 足够。若目标可能是复杂合约就需要评估改用call()。4. 一个常被混淆的概念ERC20 的 transfer 不是 ETH 转账需要注意区分ERC20 代币的transfer如 Faucet.sol 中token.transfer(msg.sender, amountAllowed)操作的是代币账本mapping与本讲发送原生 ETH 的transfer()完全是两回事前者不涉及 2300 gas 限制。九、注意事项与安全提示send/call 必须处理返回值两者失败都不会自动回滚若忽略返回值会导致资金转移失败却不报错是常见的隐蔽 bug重入风险call()无 gas 限制意味着对方回调可以执行任意复杂逻辑若接收方是恶意合约可能在转账回调中发起重入攻击。当合约状态更新与外部调用顺序不当时尤其危险——重入攻击与“检查-生效-交互”模式的完整攻防演示可参考仓库 S01_ReentrancyAttack2300 gas 的历史背景transfer/send的 2300 gas 上限源于早期链上SLOAD/SSTORE成本较低的设计后续 EIP 调整存储操作成本后依赖固定 gas 上限的转账方式对接收方回调的兼容性进一步变差这也是业界转向call()的原因之一。从代码结构看本仓库后续章节如闪电贷、DEX、多签钱包等的 ETH 相关操作也普遍采用call{value: ...}模式例如 UniswapV2Flashloan.sol 中对借贷方偿还 ETH 的处理。总结本讲围绕 SendETH.sol 与ReceiveETH合约完整演示了 Solidity 发送 ETH 的三种方式transfer2300 gas、失败自动回滚、send2300 gas、返回 bool 需自行检查与call无 gas 限制、返回(bool, bytes)。在实际开发中call()因灵活性和对复杂接收逻辑的兼容性成为主流选择但使用它时必须自行检查返回值并时刻警惕重入风险而transfer()因其失败自动回滚的特性在简单提款场景中仍是次优选择。掌握这三种方法的差异与适用边界是写出健壮、安全 Solidity 合约的基础功。【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

平面波展开法计算二维声子晶体能带:Matlab实现与带隙分析

平面波展开法计算二维声子晶体能带:Matlab实现与带隙分析

简介:面向声子晶体与能带计算研究者的Matlab实现,采用平面波展开法求解二维声子晶体能带结构,尤其适合分析周期结构材料的带隙特性。压缩包共3个文件,包含1个承载核心算法的m脚本、1个提供预计算结果的mat数据文件,以及…

📅 2026/9/14 11:56:33
MATLAB数字信号处理仿真代码结构设计与滤波器实现指南

MATLAB数字信号处理仿真代码结构设计与滤波器实现指南

简介:面向数字信号处理课程学习者,这份经由MATLAB实现的课后仿真代码包,按十四章完整覆盖从采样定理、Z变换、滤波器设计到数字调制、信道编码与图像处理等核心专题。每个章节均配有可直接运行的.m脚本与.bat批处理文件,适合学生课…

📅 2026/9/14 11:56:33
GB28181协议在通信基站智慧安防中的实践与应用

GB28181协议在通信基站智慧安防中的实践与应用

1. 项目概述:通信基站智慧安防体系的GB28181实践 通信基站作为国家信息基础设施的核心节点,其安全防护一直是运营商和主管部门的重点工作。传统基站监控系统普遍存在设备品牌杂乱、协议不统一、智能化程度低等问题。我们基于国标GB28181协议和EasyCVR视频…

📅 2026/9/14 11:56:33
MORE NEWS

更多资讯

📰

Wazuh Engine 输出阶段(Outputs)完全指南:first_of 路由、File 落盘与 Indexer 索引配置详解

Wazuh Engine 输出阶段(Outputs)完全指南:first_of 路由、File 落盘与 Indexer 索引配置详解 【免费下载链接】wazuh Wazuh - The Open Source Security Platform. Unified XDR and SIEM protection for endpoints and cloud workloads. 项…

📰

aisuite 分层架构实战指南:统一 Chat Completions 客户端、Agent API 与 OpenWorker 智能体框架

aisuite 分层架构实战指南:统一 Chat Completions 客户端、Agent API 与 OpenWorker 智能体框架 【免费下载链接】aisuite Simple, unified interface to multiple Generative AI providers 项目地址: https://gitcode.com/GitHub_Trending/ai/aisuite aisu…

📰

为什么SMT贴片单价不能只看数字?PCBA全成本核算与代工厂评估指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Vitest 数据库事务逐测试隔离:用 aroundEach 和 Scoped Fixture 实现零清理集成测试

Vitest 数据库事务逐测试隔离:用 aroundEach 和 Scoped Fixture 实现零清理集成测试 【免费下载链接】vitest Next generation testing framework powered by Vite. 项目地址: https://gitcode.com/GitHub_Trending/vi/vitest 集成测试要访问真实数据库&…

📰

办公智能体套件落地指南:从智能体编排到企业知识库与安全审计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Gemini MCP Server 离线工作模式实战:Ollama 本地模型断网 AI 开发全解

Gemini MCP Server 离线工作模式实战:Ollama 本地模型断网 AI 开发全解 【免费下载链接】pal-mcp-server The power of Claude Code / GeminiCLI / CodexCLI [Gemini / OpenAI / OpenRouter / Azure / Grok / Ollama / Custom Model / All Of The Above] working a…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬