WTF-Solidity 怎么用?从三行 HelloWeb3 到合约安全的 74 讲完整学习路径
【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程,供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity
clone 下来第一眼,是 74 个编号目录:01 到 57,外加 S01 到 S17,每个还带自己的 readme 和截图。不知道先打开哪个的人,多半已经关掉了。WTF-Solidity 这套 Solidity 极简教程的解法不是让你读 README 目录,而是把 74 讲压成五段递进路线——照着走,每段结束都有明确的"完成标志"。
📸 项目快照
| 项目 | 说明 |
|---|---|
| 项目名 | WTF-Solidity |
| 一句话定位 | 面向编程新手的 Solidity 极简入门教程,每周更新 1~3 讲 |
| 技术栈 | Solidity 0.8.34 + Foundry,Remix 可在线跑 |
| 课程总量 | 主线 57 讲 + 安全篇 S01~S17 共 17 讲,另有 EVM 操作码等专题 |
| 多语言支持 | 中 / 英 / 西 / 葡 / 日 |
| 社区入口 | Discord、微信群与官网 wtf.academy |
最值得说的是:合约安全没有散落在语法课里,而是单独成 S01~S17 共 17 讲——重入、溢出、蜜罐、抢跑各占一篇,这在整个教程里不多见。另一个工程细节是 src/ 把每个章节目录软链成统一编译根,配 foundry.toml 之后全仓库可本地验证,学完就能用 forge 自检。
🗺️ 学习地图:五段递进(74 讲怎么读)
语法地基 · 第 1~15 讲
这一段解决"一个合约由什么构成":值类型、函数、变量作用域、数组结构体、mapping、修饰器、事件、继承、接口、异常,逐块搭起写合约的地基。卡在这里,后面每讲都在补洞。两个最值得反复看的点:第 1 讲的 HelloWeb3.sol 全文只有三行,SPDX、pragma、合约体一行一个,是全书的压缩索引;第 5 讲 DataStorage.sol 用可运行代码对比 storage/memory/calldata 的 gas 差异,这是新手最容易踩、面试最爱问的点。
完成标志:不查教程,独立写出一个带存储变量、事件和自定义 error 的合约。
进阶 · 第 16~30 讲:底层调用与 EVM 交互
这一段解决"合约怎么和链、和其他合约说话":重载、library、import、receive/fallback、三种发 ETH 方式、call/delegatecall、create/create2、ABI 编码、hash、selector、try-catch。看不懂 call 与 delegatecall 的区别,后面代理合约会整段跳过。第 23 讲 Delegatecall.sol 用同一个合约、两条路径调出两个不同返回值,把"在谁的存储里执行"讲得极其直观;第 30 讲 TryCatch.sol 收尾外部调用异常,是进阶段的出口关卡。
完成标志:能写出一个收 ETH、调外部合约并妥善处理失败分支的合约。
应用 · 第 31~45 讲:写得出业务合约
这一段是"能不能写出有用合约"的分水岭:ERC20、水龙头、Merkle 空投、ERC721、荷兰拍卖、签名、NFT 交换、ERC1155、WETH、分账、vesting、代币锁、时间锁。第 34 讲 ERC721.sol 配 IERC165.sol、IERC721Receiver.sol 一整套接口族,safeTransferFrom 是逐行读的价值所在;第 37 讲 Signature.sol 把数字签名落地,直接铺垫安全篇的 S6 重放攻击。
完成标志:从零写出一个 ERC20 和一份可验证的白名单空投合约。
Degen · 第 46~51 讲:代理与可升级
代理、可升级、透明代理、UUPS、多签、ERC4626 金库。第 46 讲 ProxyContract.sol 手写内联汇编 delegatecall,是理解后续三讲的钥匙。
完成标志:解释得清透明代理与 UUPS 在"谁能调管理函数"上的区别。
合约安全 · S01~S17 讲
重入、选择器碰撞、中心化、权限、溢出、签名重放、坏随机数、绕过合约检查、DoS、蜜罐、抢跑、tx.origin、未检查调用、时间戳、预言机、NFT 重入、跨服重入。每讲都串联前面的知识点:重入对应第 19/20 讲的转账与 receive,蜜罐对应 fallback 与权限控制。S17 在仓库里只附文章,没有独立 .sol,以 readme.md 为主。
完成标志:拿到任意合约,能在 10 分钟内指出最明显的一个隐患。
动手 5 分钟:跑通第一讲
- 克隆仓库:
git clone https://gitcode.com/GitHub_Trending/wt/WTF-Solidity cd WTF-Solidity- 打开 01_HelloWeb3/HelloWeb3.sol,三行合约:SPDX 注释、
pragma solidity ^0.8.34、一个 public 字符串。 - 把源码粘进 Remix 的默认 JavaScript 虚拟机环境,点 Compile 编译。
- 选一个测试账户点 Deploy,部署到本地 EVM。
- 点击合约里的
_string,看到Hello Web3!即跑通。
线上 IDE 免安装、打开就能部署;本地跑则需 Foundry 环境,仓库根目录 foundry.toml 已配好solc = "0.8.34"与依赖路径映射,无需再动。
📂 仓库目录结构解读
WTF-Solidity/ ├── 01_HelloWeb3/ # 课程章节 01~57:readme.md + *.sol + img/ ├── S01_ReentrancyAttack/ # 安全章节 S01~S17 ├── src/ # 各章节软链,统一编译根 ├── lib/ │ ├── forge-std/ │ └── openzeppelin-contracts/ ├── Languages/ # en / es / pt-br / ja ├── Topics/ # ERC721、Tools、翻译等专题 ├── img/ ├── foundry.toml └── scripts/run-forge-tests.sh- src/:每个条目是软链,如
src/01_HelloWeb3 -> ../01_HelloWeb3,全仓库一次配置即可编译。 - lib/:forge-std 测试库与 OpenZeppelin 源码,既是依赖也可当参考读物。
- Languages/:en 覆盖最全,正文标注 preliminary,需社区继续审校。
- Topics/:含 TOOL01_Remix、TOOL07_Foundry 等开发工具文档。
- foundry.toml:
src = "src"、solc = "0.8.34",remappings 把forge-std/与@openzeppelin/contracts/指向 lib 子目录。 - scripts/run-forge-tests.sh:跑根目录 RootTest,再逐章节回归;第 18 讲与第 39 讲因依赖外部 URL 和 Chainlink 被排除。
值得逐行读的 3 个讲次
重入攻击:先转款后扣余额的代价
S1 把"外部调用放在状态更新之前"的代价演示到极限:1 ETH 本金如何清空整家银行,以及两种标准修法(checks-effects-interactions 与重入锁)。
// S01_ReentrancyAttack/ReentrancyAttack.sol function withdraw() external { uint256 balance = balanceOf[msg.sender]; require(balance > 0, "Insufficient balance"); // 转账先发生,触发恶意 receive 重入 (bool success, ) = msg.sender.call{value: balance}(""); require(success, "Failed to send Ether"); balanceOf[msg.sender] = 0; // 更新在转账之后,太晚了 }call发出即可能激活接收方receive(),攻击合约借此循环提款。GoodBank把balanceOf[msg.sender] = 0;挪到转账之前,重入在 require 处失败。ProtectedBank的nonReentrant锁:_status置 1 执行、结束归 0,第二次进入直接 revert。
延伸思考:如果转账用的transfer只有 2300 gas,这套攻击还成立吗?
代理合约:一段内联汇编看懂 delegatecall
第 46 讲不依赖任何库,手写代理转发,是读懂 47~49 讲的前提,也是"数据与逻辑分离"最原始的形态。
// 46_ProxyContract/ProxyContract.sol function _delegate() internal { assembly { let _implementation := sload(0) // 读存储槽 0 calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas(), _implementation, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) if iszero(result) { revert(0, returndataize()) } } }- 逻辑代码在 Proxy 的存储上下文里执行,状态槽位按 Proxy 布局解释。
- 源码注释明确提示:
Logic与Proxy的状态变量顺序必须一致,否则槽位冲突。 returndatacopy把逻辑合约返回值原样回传给调用方,代理对调用者透明。
延伸思考:x只写在 Logic 里,为什么经 Proxy 调increment()拿到的却是 1 而不是 100?
ERC721:safeTransferFrom 的自保机制
ERC721 用 ERC165 做能力自证,用 receiver 回调防止 NFT 被永久锁进不会"接货"的合约,是标准接口设计的范本。
// 34_ERC721/ERC721.sol function _checkOnERC721Received( address from, address to, uint256 tokenId ) internal { if (to.isContract()) { bytes4 ret = IERC721Receiver(to).onERC721Received( msg.sender, from, tokenId, "" ); require(ret == IERC721Receiver.onERC721Received.selector, "ERC721: caller is not an NFT receiver"); } }- 接收方是合约就必须返回约定 selector,否则整笔转账 revert。
supportsInterface用type(IERC721).interfaceId自证支持的接口集合。- 教程用它免费铸造 WTFApe.sol,把标准转成可运行应用。
延伸思考:receiver 回调本身又是外部调用,这里为什么不会重入?
社区与延伸
多语言现状:英文最完整但标注 preliminary,西语覆盖前 30 讲,葡语体量最大(139 份源码、115 份教程),日语 33 讲。
仓库内延伸阅读:
- Topics/Tools/:Remix、Foundry 等工具教程
- Topics/Translation/:MetaMask 16 条安全建议、EVM 操作码资料
- lib/openzeppelin-contracts/:工业级合约参考实现
- Languages/en/README.md:英文版课程目录
下次再看到那 74 个目录,不用从 README 读起——从 01_HelloWeb3 打开三行合约,顺着编号走到 S17,每段结束都有完成标志给你对答案。
【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程,供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考