Foundry 学习笔记 005

🧠 Foundry 学习笔记:完整知识体系整理


一、Foundry 核心组件简介

工具名 说明
forge 合约开发、测试、编译的主工具
cast 与链交互工具:查询数据、发送交易、部署合约
anvil 本地模拟链,用于测试、开发和 fork 主网数据

二、基础开发结构

复制代码
forge init token-presale-foundry

生成项目目录结构:

复制代码
token-presale-foundry/
├── src/                # 合约代码
├── test/               # 测试代码
├── script/             # 部署脚本
├── foundry.toml        # 配置文件
├── .env                # 环境变量

三、合约开发与部署流程

1. 编写合约(src/TokenPresale.sol)

使用 PriceConverter 库,实现预售逻辑,包含:

  • fund():捐赠 ETH -> USD 换算

  • withdrawETH():项目方提现

  • claimTokens():线性解锁后用户领取 token

  • modifiers 修饰器:权限、时间、状态判断

  • fallback/receive 函数自动处理 ETH 转账

2. 编译合约

复制代码
forge build

四、合约测试模块详解

1. 单元测试(unit)

复制代码
contract TokenPresaleTest_Unit is Test {
  address owner = makeAddr("owner");
  ...
}
  • 使用 makeAddr("user1") 创建虚拟地址

  • vm.prank(user):模拟下一个 tx 由 user 发出

  • vm.startPrank(user) 与 vm.stopPrank() 配套使用模拟多条 tx

  • vm.expectRevert(Error.selector) 断言 revert 错误

2. 测试

fund

函数最小捐赠限制

复制代码
function testFund_RevertWhenTooSmall() public {
    uint256 tooSmallAmount = 0.009 ether;
    vm.prank(user1);
    vm.expectRevert(TooSmall.selector);
    tokenPresale.fund{value: tooSmallAmount}();
}

知识点:

  • Solidity 中 .selector 是函数/错误签名的前 4 字节

  • 不能用 TooSmall() 因为 .expectRevert 期望的是 bytes4

3. 使用 warp 模拟时间推移

复制代码
vm.warp(tokenPresale.unlockStartTime() + tokenPresale.unlockDuration() + 1);
  • vm.warp() 模拟区块时间

  • 解锁线性领取功能需要模拟时间推进


五、Fork 测试(Forked)

使用主网预言机数据测试 ETH/USD 换算逻辑:

复制代码
forge test --match-path test/forked/TokenPresaleTest_Forked.t.sol --fork-url $SEPOLIA_RPC_URL -vvvv

知识点:

  • Fork 测试不会消耗 Metamask 测试币

  • 使用 getLatestETHPriceInUSD() 查询主网预言机价格

  • 容忍误差断言写法:

    assertApproxEqAbs(actual, expected, tolerance, "msg");


六、Staging 测试(模拟完整流程)

文件:test/staging/TokenPresaleTest_Staging.t.sol

流程覆盖:

  1. 多用户捐赠 → 达成目标

  2. owner 提现 → 启用领取

  3. 模拟时间推进:领取 30%、60%、100%

  4. 紧急暂停:验证暂停状态下逻辑和领取是否被影响


七、部署脚本与 Makefile 自动化

1. 脚本文件

复制代码
contract DeployTokenPresale is Script {
    function run() external {
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        vm.startBroadcast(deployerPrivateKey);
        TokenPresale tokenPresale = new TokenPresale();
        console.log("TokenPresale deployed at:", address(tokenPresale));
        vm.stopBroadcast();
    }
}

2. 本地

.env

示例

复制代码
SEPOLIA_PRIVATE_KEY=你的私钥
SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/xxx
ETHERSCAN_API_KEY=xxx
MYWALLET_ADDRESS=0xf3...266
SEPOLIA_MYWALLET_ADDRESS=0x44...6c5
RPC_URL=http://127.0.0.1:8545

3. Makefile 自动化部署命令

复制代码
deploy-anvil:
	forge script script/DeployTokenPresale.s.sol:DeployTokenPresale \
		--rpc-url $(RPC_URL) \
		--broadcast \
		--wallet mywallet \
		--sig "run()" \
		-vvvv

注意:

  • .env 不应上传至 GitHub,需加 .gitignore

八、cast send 直接部署(CLI 方式)

复制代码
cast send \
  --from 0xf39f...92266 \
  --rpc-url http://127.0.0.1:8545 \
  --create out/TokenPresale.sol/TokenPresale.json \
  -- \
  -vvvv

错误排查:

  • --unlocked 错误使用

  • --legacy 需要写在 -- 后

  • mywallet 不能直接作为 --from,需先用 cast wallet 解锁并查地址


九、Foundry 调试日志解释

调用结构如下:

复制代码
TokenPresale::fund{value: 8091...}()
├─ EACAggregatorProxy::latestRoundData()
│  └─ AccessControlledOffchainAggregator::latestRoundData()
│     └─ 返回价格数据:24535,表示 $2453.5

关键术语解释:

  • staticcall\]:只读调用(不改变链状态)

  • 1 ether = 1e18 wei,ETH 捐赠在换算 USD 时自动转换精度


十、总结

类别 工具 用法
合约编写 Solidity 编写逻辑:fund、claim、withdraw
本地模拟 Anvil anvil 启动本地链
测试工具 forge test 支持 unit / forked / staging
脚本部署 forge script 搭配 .env 和 Makefile 实现自动化部署
链上交互 cast send / call / wallet 查询状态、部署合约、签名交易
环境变量管理 .env + Makefile 配置私钥、钱包地址、RPC 节点等信息
相关推荐
干饭小白19 分钟前
ffmpeg使用流程
笔记
余_弦25 分钟前
区块链钱包开发(十)—— 构建主控制器metamask-controller.js
区块链
啊我不会诶1 小时前
BD202402跑步 线性求逆元 素数筛 数学
学习·算法·补题
chennalC#c.h.JA Ptho1 小时前
iPad os
经验分享·笔记·架构·电脑
明月清了个风1 小时前
工作笔记-----IAP的相关内容
arm开发·笔记·iap·嵌入式软件·程序升级
Virgil1392 小时前
【DL学习笔记】各种卷积操作总结(深度可分离、空洞、转置、可变形)
笔记·深度学习·学习
慕y2742 小时前
Java学习第一百零一部分——网关(Gateway)
java·网络·学习
wrynhyxa2 小时前
回归的wry
经验分享·笔记
特种加菲猫3 小时前
从exec到Shell:深度解析Linux进程等待,程序替换与自主Shell实现
linux·笔记
小眼睛FPGA4 小时前
【盘古100Pro+开发板实验例程】FPGA学习 | 腐蚀膨胀 | 图像实验指导手册
科技·学习·ai·fpga开发·fpga