Web3 简介

Welcome to Web3! Web3代表互联网下一个时代以区块链为核心实现去中心化用户掌控数据和资产通过智能合约自动化交互摆脱中心化平台控制 security 👉 安全审计 笔记合集 Web3安全聚焦预防黑客攻击和漏洞利用 采用多层防御策略包括智能合约审计访问控制实时监控 使用Slither Mythril检测重入攻击整数溢出权限问题 最佳实践强调私钥管理多签名钱包紧急暂停机制 2025年主要威胁包括闪贷治理攻击跨链桥漏洞钓鱼攻击 强调安全即代码从设计阶段整合形式验证AI驱动监控 开源库OpenZeppelin提供安全合约模板减少常见错误 机构级项目需持续审计漏洞赏金计划构建信任 wallet 钱包是Web3入口管理私钥和资产交互 分为热钱包和冷钱包 热钱包如MetaMask浏览器扩展方便dApp连接但易受网络攻击 冷钱包包括Ledger硬件钱包和纸钱包离线存储更安全适合大额持有 非托管钱包用户完全控制密钥 托管钱包如Coinbase简化但依赖平台 ERC-4337账户抽象提升用户体验支持社交恢复气体赞助 多链钱包如Rainbow支持Layer2降低费用 安全提示必须使用硬件签名绝不泄露种子短语定期备份 EIP 👉 EIP 笔记合集 EIP是以太坊改进提案标准化平台变更 涵盖核心协议客户端API合约规范 任何人可提交草案经社区讨论编辑审查进入最终状态 核心EIP需全网共识可能引发硬分叉 如EIP-1559引入基础费市场 ERC是EIP子集专注应用层标准 EIP过程确保透明包容 从草稿到最后调用再到最终实施 EIP-4844引入blob数据大幅降低Layer2成本 未来EIP聚焦可扩展性隐私如Verkle树 codeLanuage 👉 合约语言 笔记合集 智能合约语言最终编译为EVM字节码 Solidity主流类JavaScript支持继承修饰符库生态最丰富 Vyper Python风格故意去除继承无限循环提升可审计性安全性 Yul中间语言用于精细气体优化 Rust主要用于Solana非EVM链 Ethereum生态仍以Solidity和Vyper为主 学习Solidity推荐从Remix IDE开始 Vyper强调人类可读代码减少攻击面 选择语言取决于项目复杂度和安全优先级 EVM 👉 EVM 笔记合集 EVM是以太坊虚拟机去中心化计算引擎 执行智能合约字节码采用栈基架构深度1024 每操作码消耗气体防止滥用 Turing完备支持任意逻辑但有气体上限避免无限循环 EVM负责状态转换更新账户余额存储 所有节点一致执行保证确定性隔离 兼容链如Polygon Arbitrum共享EVM实现无缝移植 未来升级EOF引入容器化代码提升效率 EVM是整个以太坊生态的计算核心 framework 👉 Web3 开发框架合集 Web3开发框架大幅简化合约构建测试部署 Hardhat JavaScript基础插件化支持控制台日志自定义任务深度集成Ethers.js Truffle老牌套件自带Ganache本地链Mocha测试适合新手 Foundry Rust编写超快编译铸造Anvil分叉主网用Solidity写测试支持模糊测试 当前主流选择 Hardhat灵活度最高 Truffle最适合教学 Foundry性能极致适合高级开发者 搭配VS Code Solidity扩展开发体验最佳 ...

November 20, 2025 · 1 min · 98 words · Guangyang Zhong

EVM 笔记合集

简介 以太坊虚拟机(Ethereum Virtual Machine,EVM)是一个去中心化的、跨节点一致的执行环境,负责在所有以太坊节点上安全地执行智能合约代码。节点运行 EVM 来处理交易、执行合约逻辑,并通过消耗 Gas(燃料) 来度量并限制计算资源,从而保障网络的效率与安全 前提知识 要快速理解 EVM,建议具备以下基础: 计算机科学常用术语:字节(byte)、内存(memory)、堆栈(stack)等 密码学与区块链基础:哈希函数(如 Keccak-256)、默克尔树(Merkle Tree / Patricia Trie)等概念 智能合约开发的基本流程(编译后生成字节码、部署到链上、通过交易触发执行)会帮助理解细节 从账本到状态机:概念上的迁移 与比特币常被比作“分布式账本”不同,以太坊更适合被看作一个分布式状态机。除了记录账号与余额之外,以太坊还维护一个可随交易变化的机器状态,并能在状态变更时执行任意机器代码(即智能合约)。这些状态变更的规则由 EVM 明确定义 数学上可以把以太坊的状态转换描述为一个确定性的函数: Y(S, T) = S' 给定旧的有效状态 S 与一组新的有效交易 T,状态转换函数 Y(S, T) 产出新的有效状态 S' 状态(State) 以太坊的全局状态由一种称为 改进版 Merkle–Patricia Trie 的巨大数据结构保存。该结构将所有账户(包括外部拥有账户和合约账户)及其关联的数据通过哈希组织起来,最终回溯到区块链上的单一根哈希(state root)。这种设计保证了状态可证明性与高效性(例如轻客户端验证) 交易(Transactions) 交易是由账户发起并经密码学签名的指令,分为两类: 消息调用交易(Message Call):调用现有合约或向外部账户发送 ETH 合约创建交易(Contract Creation):将已编译的智能合约字节码部署为一个新的合约账户。部署后,合约字节码存储在该合约账户中;每当有交易/消息调用该合约时,EVM 会读取并执行这些字节码 EVM 的工作方式 堆栈机(Stack machine) EVM 是一个基于堆栈的虚拟机: 堆栈深度上限为 1024 项 每个堆栈项为 256 位(与以太坊使用的 256 位加密原语保持一致,例如 Keccak-256) 绝大多数操作以堆栈为中心进行:入栈、出栈、二元运算(ADD、SUB、MUL、DIV、AND、OR、XOR)等 内存(Memory) 执行期间,EVM 维护一个瞬态的 内存(memory),它是一个可字节寻址的动态字节数组: ...

January 11, 2025 · 1 min · 162 words · Guangyang Zhong

EIP 笔记合集

简介 EIP(Ethereum Improvement Proposal)是以太坊改进提案 任何人都能提交的标准化文档 驱动以太坊从创世区块到今天的唯一正式机制 ERC只是EIP的应用层子集 所有硬分叉升级Layer2变革账户抽象都源于EIP 好的,下面是不含任何表情、按 EIP 状态与类型分类整理的中文笔记版,适合课堂笔记、复习或写作使用。 EIP 状态 EIP(Ethereum Improvement Proposal,以太坊改进提案)在提出、讨论和落地过程中,会经历以下生命周期状态 Idea(想法阶段) 处于预草案阶段,仅是初步构想 不会被记录在官方 EIP 仓库中 通常在论坛、Issue 或社区中进行非正式讨论 Draft(草案阶段) 正式进入 EIP 生命周期的第一个阶段 按 EIP 模板规范化后,由 EIP Editor 合并进仓库 处于持续开发和修改中 Review(评审阶段) 作者认为提案已较为成熟 主动请求社区或同行进行技术评审 重点关注规范完整性、可行性与兼容性 Last Call(最终审查阶段) 进入最终审查窗口,通常为 14 天 由 EIP Editor 指定并设置 last-call-deadline 若发现需要进行规范性修改,将退回 Review 状态 Final(最终状态) 成为正式标准 进入终态,不再进行实质性修改 仅允许修正勘误或补充非规范性说明 Stagnant(停滞状态) Draft 或 Review 状态下,连续 6 个月无实质进展 会被标记为停滞 作者或 Editor 可重新激活并移回 Draft Withdrawn(撤回) 作者主动撤回提案 该状态具有终结性 EIP 编号不可再次使用,重新提出需新编号 Living(持续更新) ...

August 16, 2024 · 2 min · 280 words · Guangyang Zhong

安全审计 笔记合集

简介 Web3,作为一个以去中心化为核心理念的新一代互联网范式,其基石建立在区块链技术——一个不可篡改、透明公开的分布式账本之上。这个账本通过精密的共识机制确保网络参与者能够在无需相互信任的情况下达成一致,这无疑是革命性的 然而,这种“不可篡改”的特性是一把双刃剑。一旦智能合约部署上链,其代码逻辑便永久固化,任何潜在的错误、漏洞或设计缺陷都将被永恒记录,并可能被恶意利用,造成无法挽回的资产损失。在传统互联网中,一个漏洞可以通过“打补丁”快速修复;在区块链世界,修复往往意味着复杂的迁移、昂贵的社区沟通,乃至彻底的失败。因此,“安全前置” 的理念在这里被提升到了前所未有的高度 安全审计(Security Audit)正是在这种严苛环境下应运而生的核心质量保障手段。它不再是一种可选的成本,而是智能合约开发生命周期中不可或缺的、防御性的关键环节。一个严谨、深入的审计过程,是项目对自身代码负责、对用户资产负责的最重要体现 👉 0907 审计模板仓库 👉 0907 审计报告仓库 高层概述 ( 什么是安全审计/智能合约审计? ) 当人们提到"审计"时,通常指的是安全审查(Security Review)。这是对智能合约代码进行系统性、专业性的安全检查过程,旨在识别潜在的安全漏洞、逻辑错误和设计缺陷。 审计的局限性 There is no silver bullet ( 感兴趣的同学可以去搜一下该词的意思 ) to auditing:需要明确的是,审计并非万能的解决方案。即使经过最严格的审计,智能合约仍可能存在: 未被发现的复杂逻辑漏洞 经济模型的长尾风险 与外部协议集成时的未知交互风险 编译器或区块链底层本身的潜在问题 一次审计只能提供特定时间点的安全快照,无法保证永久的绝对安全。 安全审计的三个阶段 第一阶段:初步审查(Initial Review) 1. 范围界定(Scoping) 确定审计的具体范围:哪些合约、哪些功能 明确审计目标、时间线和交付物 了解协议的业务逻辑和设计意图 2. 侦察(Reconnaissance) 初步代码阅读和理解架构 识别关键组件和依赖关系 梳理权限模型和资金流向 3. 漏洞识别(Vulnerability Identification) 系统性检查各类安全漏洞 结合自动化工具和人工审查 从攻击者角度思考可能的攻击向量 4. 报告(Reporting) 详细记录发现的每个问题 按严重程度分类(危急、高危、中危、低危) 提供具体的修复建议和代码示例 第二阶段:协议修复与验证 1. 修复问题(Protocol Fixes) 开发团队根据审计报告修复漏洞 可能需要重新设计部分逻辑 确保修复方案不引入新的问题 2. 重新测试与添加测试(Retests and Adds Tests) 审计团队验证修复的有效性 为修复的部分添加专门的测试用例 确保修复后的代码仍能满足业务需求 第三阶段:缓解措施审查(Mitigation Review) 1. 侦察(Reconnaissance) 审查所有修复的代码变更 理解修复方案的安全影响 2. 漏洞识别(Vulnerability Identification) 确认原始漏洞已被彻底修复 检查修复是否引入了新的攻击面 3. 报告(Reporting) 提供最终的审计结论 确认所有高风险问题已解决 给出部署前的最终建议 智能合约开发生命周期 1. 规划与设计(Plan & Design) 需求分析和功能规划 架构设计和模式选择 安全考虑融入设计阶段(Security by Design) 经济模型和激励机制的数学验证 2. 开发与测试(Develop & Test) 编写合约代码和单元测试 集成测试和端到端测试 模糊测试(Fuzzing)和差异化测试 测试网部署和模拟攻击 3. 智能合约审计与部署后规划 这不仅仅是一个步骤,而是一个包含多个子阶段的综合过程(类似于传统软件开发生命周期SDLC,但具有区块链特有的安全考量): ...

August 16, 2025 · 3 min · 431 words · Guangyang Zhong

Web3 开发框架合集

简介 foundry 👉 foundry 使用指南 hardhat

August 18, 2025 · 1 min · 6 words · Guangyang Zhong

合约语言 笔记合集

简介 本文件为 Code Language 的简要说明,面向 Web3 开发者与学习者,概述 Solana 与 Ethereum 两大生态中常见的合约语言及其基本用途与特点。内容以技术介绍为主,强调实际开发中的可理解性与参考价值,适合作为项目仓库或学习笔记中的快速查阅文档 Ethereum 以太坊生态的合约开发语言丰富,从高级语言到中间/低级语言都有对应工具链。下面按常见度与用途列出 Solidity 定位:以太坊主流高级语言(类似 JS/Java 风格),最广泛使用的智能合约语言 特点: 面向合约、支持继承、接口、事件、ABI 自动化 丰富生态(Truffle/Hardhat/Foundry 等工具链) 优点: 开发门槛低、资料与示例丰富、主网项目多 示例: // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Counter { uint256 public count; function inc() public { count += 1; } } Vyper 定位:以 Python 风格设计的以太坊合约语言,追求简洁与安全性(避免复杂特性) 特点: 语法更接近 Python,但只用于合约开发(不是通用 Python) 有意减少语言特性以降低易错面(例如限制继承、复杂抽象) 优点: 更可预测、审计面更小,适合对安全性要求高的合约。 示例(概念): count: public(uint256) @external def inc(): self.count += 1 Yul 👉 Yul 使用指南 定位:EVM 的中间表示(IR),Solidity 等编译器可输出 Yul,用于更精细的优化与跨 EVM 后端(例如 EVM + ewasm)。 特点: 更低级但仍比纯汇编更结构化,适合做跨编译器优化与手写热路径 用途: ...

January 16, 2024 · 2 min · 239 words · Guangyang Zhong

DeFi 笔记合集

简介 去中心化金融旨在通过区块链与智能合约,构建一个开放、透明、可组合的金融服务生态系统。本合集从核心协议原理出发,系统梳理各赛道的运行机制、风险模型与演进趋势。 Lending DeFi借贷协议允许用户无需中介即可存入资产获取收益,或超额抵押资产借出其他资产。其核心机制包括资金池、利率模型、清算机制以及治理代币经济学 Flash Loan 👉 Flash Loan 闪电贷 Liquid Staking 流动性质押协议允许用户在参与区块链共识(如质押ETH)的同时,获得代表质押头寸的流动性衍生代币(如stETH),从而解决质押资产流动性锁定的问题 DEX 👉 DEX 经典概念 去中心化交易所(DEX)通过自动做市商机制,允许用户直接在链上交易加密资产。核心在于流动性池、恒定乘积等定价公式、手续费激励以及无常损失管理 RWA 现实世界资产旨在将传统金融资产(如国债、信贷、不动产)通过代币化引入区块链,为DeFi生态系统提供具有实际价值支撑的收益资产,并面临合规、托管与法律映射等独特挑战

August 16, 2025 · 1 min · 20 words · Guangyang Zhong

DEX 经典概念

简介 AMM(Automated Market Maker,自动做市商)是一类无需订单簿、无需对手方撮合就能完成资产交换的机制。它通过一条数学定价曲线(例如最经典的恒定乘积公式 x * y = k)来确定池中两种资产的价格与兑换比例 交易者与 AMM 交互时,并不是与某个具体对手方进行买卖,而是与池子本身交易: 当交易者往池子中加入一种资产时,池子的储备量发生变化 定价曲线根据新储备自动给出另一种资产能取出的数量 由此实现“自动化报价”和“自动化执行” 1. 基本概念与不变式 AMM 的核心思想:用一个自动化的数学不变式替代传统的订单簿,以池内储备(reserves)决定交易价格与数量。 最经典的不变式(恒定乘积)为: x * y = k 其中 x、y 为池内两种资产的数量(reserve of X, reserve of Y),k 为常数(在无手续费的理想情况下保持不变)。 交易通过改变 x 或 y 的值来完成,新的 x 和 y 必须满足不变式(考虑手续费时,换入量先扣手续费再作用于不变式) 2. 交易定价与交换公式(恒定乘积,含手续费) 设池初始为 x(Token X)与 y(Token Y),不变式 k = x * y。有交易者向池中放入 Δx(交易量),手续费比例为 f(例如 0.003 = 0.3%)。实际用于保持不变式的增量为: Δx_eff = Δx * (1 - f) 新的储备为: x' = x + Δx_eff y' = k / x' (保持 x' * y' = k) 交易者能从池中取出的 Y 数量为: ...

August 20, 2025 · 5 min · 953 words · Guangyang Zhong

Flash Loan 闪电贷

简介 闪电贷(Flash Loan) 是一种在单笔区块链交易内借入资产、使用资产并在同一交易内归还借款(含手续费)的借贷模式。核心特点是 “原子性”:借款、使用和还款必须在同一笔交易内完成,否则交易会回滚,借款不会生效。 这种机制使得借款人在不提供抵押的情况下临时动用大量资产,用于套利、清算、头寸迁移等操作;同时也被用于发动复杂的攻击(如操纵价格、闪电清算等)。 原理与执行流程 典型单笔闪电贷交易包含三步(原子性): 借款(Borrow) 在交易开始阶段通过闪电贷提供方(Lending Pool/Router)请求借入若干资产。 使用(Use) 在同一交易中,用借入资金执行任意链上操作,例如在不同 DEX 之间套利、进行清算、调整抵押头寸、执行跨协议操作等。 还款(Repay) 在交易结束前,将借入金额加上借贷方要求的手续费一并偿还给闪电贷提供方。如果无法偿还(余额不足或逻辑失败),整笔交易 revert,等同于“未发生”。 关键点:闪电贷的安全性与“回滚语义”绑定;若中间步骤出错,借贷不会被实际放出和使用。 常见闪电贷提供方 Aave(flashLoan / flashLoanSimple 接口) DyDx(Solo or v3) Uniswap(flash swap:允许先输出资产,再在同一交易内履约) Balancer / Curve / 其它支持 flash 的池子 (实现细节与接口在不同协议间不同) 典型用途(Use cases) 跨 DEX 套利:利用不同交易所价格差套利,套利收益用于偿还贷款并留盈利 清算(Liquidation):借资产替别人清算欠债(避免自己需提前持有资产) 头寸迁移 / 债务重组:一笔交易内把抵押品替换、债务迁移到另一个协议或仓位 杠杆构建 / 扩展:临时放大仓位用于策略 原子化多步操作:把多个必须同时完成的步骤整合为一笔交易,避免中间状态风险 代码示例 // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.20; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { AssetToken } from "./AssetToken.sol"; import { IERC20, IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { Oracle } from "./Oracle.sol"; interface IFlashLoanReceiver { function executeOperation(address token, uint256 amount, uint256 fee, address initiator, bytes calldata params) external; } contract ThunderLoan is Ownable, Oracle { using SafeERC20 for IERC20; /*////////////////////////////////////////////////////////////// STATE VARIABLES //////////////////////////////////////////////////////////////*/ mapping(IERC20 => AssetToken) public s_tokenToAssetToken; mapping(IERC20 => bool) private s_currentlyFlashLoaning; uint256 private constant FEE_PRECISION = 1e18; uint256 private s_flashLoanFee = 3e15; // 0.3% /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Deposit(address indexed account, IERC20 indexed token, uint256 amount); event Withdraw(address indexed account, IERC20 indexed token, uint256 amount); event FlashLoan(address indexed receiver, IERC20 indexed token, uint256 amount, uint256 fee); event TokenAllowed(IERC20 indexed token, bool allowed); /*////////////////////////////////////////////////////////////// MODIFIERS //////////////////////////////////////////////////////////////*/ modifier notZero(uint256 amount) { require(amount > 0, "Amount cannot be zero"); _; } modifier allowedToken(IERC20 token) { require(address(s_tokenToAssetToken[token]) != address(0), "Token not allowed"); _; } /*////////////////////////////////////////////////////////////// EXTERNAL FUNCTIONS //////////////////////////////////////////////////////////////*/ constructor(address oracle) Oracle(oracle) Ownable(msg.sender) {} function deposit(IERC20 token, uint256 amount) external notZero(amount) allowedToken(token) { AssetToken assetToken = s_tokenToAssetToken[token]; // 计算应铸造的资产代币数量 uint256 mintAmount = (amount * FEE_PRECISION) / assetToken.getExchangeRate(); // 铸造资产代币给用户 assetToken.mint(msg.sender, mintAmount); // 更新汇率(包含手续费) assetToken.updateExchangeRate(calculateFee(token, amount)); // 将底层代币转移到资产代币合约 token.safeTransferFrom(msg.sender, address(assetToken), amount); emit Deposit(msg.sender, token, amount); } function withdraw(IERC20 token, uint256 assetAmount) external notZero(assetAmount) allowedToken(token) { AssetToken assetToken = s_tokenToAssetToken[token]; // 计算可取回的底层代币数量 uint256 underlyingAmount = (assetAmount * assetToken.getExchangeRate()) / FEE_PRECISION; // 销毁用户的资产代币 assetToken.burn(msg.sender, assetAmount); // 将底层代币转给用户 assetToken.transferUnderlyingTo(msg.sender, underlyingAmount); emit Withdraw(msg.sender, token, underlyingAmount); } function flashLoan( address receiver, IERC20 token, uint256 amount, bytes calldata params ) external notZero(amount) allowedToken(token) { AssetToken assetToken = s_tokenToAssetToken[token]; // 检查合约余额是否足够 uint256 poolBalance = token.balanceOf(address(assetToken)); require(amount <= poolBalance, "Insufficient pool balance"); // 检查接收者是合约 require(receiver.code.length > 0, "Receiver must be contract"); // 计算手续费 uint256 fee = calculateFee(token, amount); // 更新汇率 assetToken.updateExchangeRate(fee); // 标记为正在闪电贷 s_currentlyFlashLoaning[token] = true; // 将资金转给接收者 assetToken.transferUnderlyingTo(receiver, amount); // 调用接收者的回调函数 IFlashLoanReceiver(receiver).executeOperation( address(token), amount, fee, msg.sender, params ); // 验证还款 uint256 newBalance = token.balanceOf(address(assetToken)); require(newBalance >= poolBalance + fee, "Flash loan not repaid"); // 重置闪电贷状态 s_currentlyFlashLoaning[token] = false; emit FlashLoan(receiver, token, amount, fee); } function repay(IERC20 token, uint256 amount) external { require(s_currentlyFlashLoaning[token], "Not in flash loan"); token.safeTransferFrom(msg.sender, address(s_tokenToAssetToken[token]), amount); } /*////////////////////////////////////////////////////////////// ADMIN FUNCTIONS //////////////////////////////////////////////////////////////*/ function setAllowedToken(IERC20 token, bool allowed) external onlyOwner { if (allowed) { require(address(s_tokenToAssetToken[token]) == address(0), "Already allowed"); string memory name = string.concat("ThunderLoan ", IERC20Metadata(address(token)).name()); string memory symbol = string.concat("tl", IERC20Metadata(address(token)).symbol()); AssetToken assetToken = new AssetToken(address(this), token, name, symbol); s_tokenToAssetToken[token] = assetToken; } else { delete s_tokenToAssetToken[token]; } emit TokenAllowed(token, allowed); } function setFlashLoanFee(uint256 newFee) external onlyOwner { require(newFee <= FEE_PRECISION, "Fee too high"); s_flashLoanFee = newFee; } /*////////////////////////////////////////////////////////////// VIEW FUNCTIONS //////////////////////////////////////////////////////////////*/ function calculateFee(IERC20 token, uint256 amount) public view returns (uint256) { uint256 tokenValue = (amount * getPriceInWeth(address(token))) / FEE_PRECISION; return (tokenValue * s_flashLoanFee) / FEE_PRECISION; } function isAllowedToken(IERC20 token) public view returns (bool) { return address(s_tokenToAssetToken[token]) != address(0); } function getFlashLoanFee() external view returns (uint256) { return s_flashLoanFee; } } 经济可行性与可行性检查 在执行闪电贷策略前需判断经济可行性,常见校验条目: ...

August 20, 2025 · 3 min · 549 words · Guangyang Zhong

foundry Forge 使用指南

简介 Forge 是 Foundry 的核心组件,用于编写、运行和调试 Solidity 智能合约测试,支持单元测试、模糊测试(fuzz testing)和不变量测试(invariant testing)。它允许开发者在 Solidity 中直接编写测试,而无需切换到其他语言。Forge 通过 forge test 命令运行测试,并提供丰富的作弊码(cheatcodes)来模拟各种场景 测试类型 Stateless Test 无状态测试(Stateless Test)是指每个测试函数独立运行,状态在每次调用后重置。这包括标准的单元测试和无状态模糊测试。在无状态测试中,Forge 会为每个测试函数创建一个干净的环境,确保测试之间互不干扰。这适合测试单一函数的行为或特定输入输出 Stateful Test 有状态测试(Stateful Test)允许测试在多个函数调用序列中保持状态,通常用于不变量测试(Invariant Testing)。在这种测试中,Forge 会生成随机调用序列,并在每个调用后检查不变量是否成立。这有助于发现复杂协议中的逻辑错误,尤其是在多步交互场景下 forge 基础测试 文件目录与命名 Foundry 项目采用标准的目录结构,便于管理和测试: src/:存放核心智能合约代码,例如 Contract.sol test/:存放测试合约,通常以 .t.sol 结尾,例如 Contract.t.sol。测试文件放在这里以便 Forge 自动识别 script/:存放部署脚本,例如 Deploy.s.sol lib/:存放依赖库,如 forge-std 或 OpenZeppelin foundry.toml:配置文件,用于设置测试参数、依赖等 命名规范: 测试合约文件名以 .t.sol 结尾 源合约文件名以 .sol 结尾 避免使用特殊字符,确保文件名描述性强 合约代码框架 Forge 测试合约必须继承自 forge-std/Test.sol 中的 Test 合约,这提供了断言、日志和作弊码功能。基本框架如下: // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "forge-std/Test.sol"; contract MyContractTest is Test { // 待测试合约实例 MyContract public myContract; // setUp 函数:在每个测试前运行,初始化状态 function setUp() public { myContract = new MyContract(); } // 测试函数:以 test 开头 function test_Example() public { // 测试逻辑 assertTrue(true); } } setUp():可选,用于初始化合约或状态。 测试函数:必须以 test 开头,public 或 external 断言:使用 assertEq(a, b)、assertTrue(condition) 等 作弊码:如 vm.prank(address) 模拟调用者 forge 进阶测试 Fuzz Testing 模糊测试(Fuzz Testing)是一种 property based 测试,Forge 会生成随机输入来验证合约的通用行为。测试函数需至少有一个参数(如 uint256 amount),Forge 会自动将其视为 fuzz 测试,默认运行 256 次 ...

August 20, 2025 · 2 min · 372 words · Guangyang Zhong