十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

WTF-Solidity 实战:手写一个 ERC20 代币水龙头(Token Faucet)合约并完成领币全流程

WTF-Solidity 实战:手写一个 ERC20 代币水龙头(Token Faucet)合约并完成领币全流程 WTF-Solidity 实战手写一个 ERC20 代币水龙头Token Faucet合约并完成领币全流程【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity本篇技术指南以 WTF-Solidity 仓库第 32 讲Languages/pt-br/32_Faucet/readme.md为核心讲解代币水龙头Token Faucet的由来以及如何用 Solidity 实现一个简版 ERC20 水龙头合约——用户可免费领取固定数量代币且每个地址仅限一次。读完本文你将掌握水龙头合约的状态变量、事件与函数设计思路能够基于 Faucet.sol 的源码逐行理解领币逻辑并能在 Remix 中独立完成部署代币 → 部署水龙头 → 充值 → 领币 → 查余额的完整实操闭环。1. 代币水龙头是什么从比特币水龙头说起当人渴了会去水龙头接水当用户想要免费代币时就会去代币水龙头领取。所谓代币水龙头Token Faucet就是让用户免费领取代币的网站或应用通常用于新项目的冷启动、测试网代币分发或社区拉新。最早的代币水龙头是比特币BTC水龙头。按照教程文档的记载现在一枚 BTC 的价格已达数万美元量级但在 2010 年BTC 的价格不足 $0.1持有人极少。为了扩大影响力比特币社区的 Gavin Andresen 开发了 BTC 水龙头让人们可以免费领取 BTC。撸羊毛人人都喜欢大量用户因此涌入其中一部分最终成为了 BTC 的忠实信徒。BTC 水龙头累计发放了超过 19,700 枚 BTC按文档记录当时价值约 6 亿美元。这个历史案例揭示了一个朴素的产品逻辑免费代币是吸引新用户的低成本获客工具。ERC20 水龙头正是这一思路在智能合约层面的标准实现。2. ERC20 水龙头合约的整体设计本讲实现的是一个简版ERC20水龙头逻辑非常直白预先将一些 ERC20 代币转入水龙头合约地址用户调用requestTokens()函数领取100单位代币每个地址只能领取一次。在仓库中该合约有两个位置可供对照葡萄牙语版 Languages/pt-br/32_Faucet/Faucet.sol从源码结构看这是一个填空练习模板——Faucet合约的函数体被保留为注释供学习者自行补全实现中文完整版 32_Faucet/Faucet.sol包含可编译运行的完整实现本文的代码拆解均以此为准。值得注意的是32_Faucet/Faucet.sol 这一个文件里同时定义了ERC20完整代币合约和Faucet水龙头合约两个合约。水龙头依赖上一讲 31_ERC20 中介绍的 ERC20 标准并通过接口合约 32_Faucet/IERC20.sol 与代币交互而不是耦合某个具体实现。水龙头合约本身只包含3 个状态变量 1 个事件 2 个函数构造函数与requestTokens()。3. 状态变量与事件水龙头的库存与台账3.1 三个状态变量水龙头合约定义了 3 个状态变量分别控制每次发多少发什么币谁领过uint256 public amountAllowed 100; // 每次领取 100 单位代币 address public tokenContract; // token 合约地址 mapping(address bool) public requestedAddress; // 记录领取过代币的地址变量类型作用说明amountAlloweduint256每次可领取的代币数量默认100注意单位不是枚而是代币的最小原子单位wei。由于代币存在小数位数decimals划分实际到手的代币枚数可能远小于 100tokenContractaddress发放的 ERC20 代币合约地址部署时由构造函数写入之后不可更改该简版无 setterrequestedAddressmapping(address bool)已领取过代币的地址台账实现每个地址只能领一次的防重领机制这里特别强调amountAllowed的单位问题示例中的 ERC20 合约将decimals设为18见 32_Faucet/Faucet.sol 中uint8 public decimals 18;因此100单位意味着100 wei即10^-16枚代币数量极小。这也是教程在演示中铸造/转账10000单位的原因——水龙头领取数量在实际产品中通常需要按代币精度重新折算。3.2 SendToken 事件合约定义了一个SendToken事件在每次成功领币时释放记录领取者地址与领取数量方便链上索引与前端监听// SendToken 事件 event SendToken(address indexed Receiver, uint256 indexed Amount);两个参数都标记了indexed意味着它们可以作为事件的检索过滤条件Topic例如前端可通过SendToken事件实时展示谁领了多少代币。4. 核心函数实现构造函数与 requestTokens4.1 构造函数绑定要发放的代币水龙头需要知道发什么代币因此在部署时通过构造函数传入 ERC20 代币合约地址// 部署时设定 ERC20 代币合约 constructor(address _tokenContract) { tokenContract _tokenContract; // 写入 token 合约地址 }构造函数将_tokenContract写入状态变量tokenContract。后续requestTokens()中的所有代币操作查余额、转账都以该地址为准。4.2 requestTokens()领币主函数requestTokens()是水龙头的核心函数任何用户都可以调用它来领取代币// 用户领取代币函数 function requestTokens() external { require(!requestedAddress[msg.sender], Cant Request Multiple Times!); // 每个地址只能领一次 IERC20 token IERC20(tokenContract); // 创建 IERC20 合约对象 require(token.balanceOf(address(this)) amountAllowed, Faucet Empty!); // 水龙头空了 token.transfer(msg.sender, amountAllowed); // 发送 token requestedAddress[msg.sender] true; // 记录领取地址 emit SendToken(msg.sender, amountAllowed); // 释放 SendToken 事件 }逐行拆解该函数依次完成 5 个动作防重领校验require(!requestedAddress[msg.sender], ...)检查调用者是否已领过已领过则直接回滚交易错误信息为 Cant Request Multiple Times!。接口对象化IERC20 token IERC20(tokenContract);把状态变量里的地址包装成IERC20接口对象之后即可调用balanceOf、transfer等标准方法。接口定义见 32_Faucet/IERC20.sol它声明了totalSupply、balanceOf、transfer、allowance、approve、transferFrom六个函数以及Transfer、Approval两个事件。库存校验require(token.balanceOf(address(this)) amountAllowed, Faucet Empty!)检查水龙头合约自身的代币余额是否足够不足则回滚错误信息为 Faucet Empty!。这一步在转账前完成避免转入资金不足导致后续失败。转账与记账token.transfer(msg.sender, amountAllowed)把代币发给调用者随后requestedAddress[msg.sender] true将该地址标记为已领取封堵重复领取。事件通知emit SendToken(msg.sender, amountAllowed)释放事件供链下索引。4.3 与同文件 ERC20 实现的配合水龙头能够运行前提是代币合约真正实现了IERC20接口。在 32_Faucet/Faucet.sol 中同文件的ERC20合约提供了transfer(address recipient, uint amount)直接操作balanceOf映射完成转账并释放Transfer事件mint(uint amount)向调用者铸造代币更新totalSupply并释放Transfer(address(0), msg.sender, amount)burn(uint amount)销毁调用者持有的代币balanceOf/allowance等查询函数。水龙头通过接口而非具体合约名调用这些方法意味着任何正确实现IERC20的 ERC20 代币包括 OpenZeppelin 标准实现都能直接接入该水龙头无需修改水龙头代码——这是面向接口编程在合约协作中的典型体现。5. 安全边界与设计要点从源码结构可以总结出这个简版水龙头的几个设计要点与边界防重领requestedAddress映射 require前置校验从状态层面杜绝一个地址反复撸。余额预检先查balanceOf(address(this))再转账避免对空水龙头的无效调用。0.8.x 安全特性合约使用pragma solidity ^0.8.34Solidity 0.8 起默认启用算术溢出检查示例中transfer内部的balanceOf[msg.sender] - amount若出现余额不足会直接回滚不会产生负余额。无管理员权限该简版水龙头没有 owner、没有暂停开关、没有限时机制任何地址均可直接调用requestTokens()领取。从代码结构看若用于生产环境可在此基础上扩展管理员配置、领取频率限制、总量上限、人机验证等能力这些属于推断的扩展方向并非本合约已有功能。事件可检索SendToken的两个参数均为indexed链下可据此高效过滤领币记录。6. Remix 实操演示五分钟跑通领币全流程以下步骤基于 Remix IDE 在线环境完整演示从部署代币到领取成功的全流程对应教程文档 Languages/pt-br/32_Faucet/readme.md 的演示章节Step 1部署 ERC20 代币合约编译 32_Faucet/Faucet.sol在部署面板选择ERC20合约构造函数参数填写名称与符号WTFname/symbol均填 WTF点击 Deploy。部署成功后在ERC20合约实例上调用mint给自己铸造10000单位代币。Step 2部署 Faucet 水龙头合约在部署面板切换为Faucet合约构造函数参数_tokenContract填入上一步ERC20合约的地址点击 Deploy。Step 3为水龙头充值回到ERC20合约实例调用transfer(address recipient, uint256 amount)将recipient填为Faucet合约地址、amount填10000把代币转入水龙头。至此水龙头库存就位。Step 4切换账户调用 requestTokens() 领币在 Remix 右上角切换一个新账户调用Faucet合约的requestTokens()函数。交易成功后可在终端/日志面板看到SendToken事件被释放Amount为100Step 5用 balanceOf 验证领取结果回到ERC20合约实例调用balanceOf(address)查询刚才领币账户的余额输出显示为100领取成功若再用同一账户第二次调用requestTokens()交易会因 Cant Request Multiple Times! 被回滚——这正是防重领机制的直观验证。7. 总结本讲围绕 WTF-Solidity 第 32 讲完成了三件事首先回顾了代币水龙头的起源比特币水龙头及其历史意义然后逐字段拆解了 ERC20 水龙头合约的完整实现——3 个状态变量、1 个SendToken事件、构造函数与requestTokens()领币函数最后通过 Remix 五步实操验证了部署代币 → 部署水龙头 → 充值 → 领币 → 查余额的完整闭环。这套模式的核心价值在于以最小代码量实现免费代币 防重领 事件可追踪的分发能力是空投、测试网代币分发、冷启动拉新等场景的经典基础组件。下一篇可以在此基础上进一步探索更复杂的分发方案如 Merkle 树白名单空投等参见仓库后续章节。【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表