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 节点等信息
相关推荐
Nightmare0043 分钟前
决策树学习
学习·算法·决策树
DKPT1 小时前
Java设计模式之行为型模式(命令模式)
java·笔记·学习·设计模式·命令模式
屁股割了还要学7 小时前
快速过一遍Python基础语法
开发语言·python·学习·青少年编程
kikikidult7 小时前
Ubuntu20.04运行openmvg和openmvs实现三维重建(未成功,仅供参考)
人工智能·笔记·ubuntu·计算机视觉
凌辰揽月8 小时前
AJAX 学习
java·前端·javascript·学习·ajax·okhttp
一缕猫毛8 小时前
【学习笔记】Linux命令
笔记·学习
永日456708 小时前
学习日记-spring-day45-7.10
java·学习·spring
迅~8 小时前
如何快速学习GO语言
学习
HXR_plume10 小时前
【计算机网络】王道考研笔记整理(2)物理层
笔记·计算机网络·考研