博客园 安装
掘金 安装
foundry
环境
-
win11
-
VPN开启tun模式,否则安装 foundryup会失败,具体原因不知道。安装好之后可以关掉。

-
git bash,安装命令都在git bash中执行,powershell和cmd不行
-
foundryup: 1.5.0,forge、chisel、anvil、cast Version: 1.5.1-stable
安装foundry
1
2
3
4
5
6
7
8
9
10
11
|
# 安装 foundryup,它相当于solidity的包管理器,类似npm、pip
curl -L https://foundry.paradigm.xyz | bash
# 刷新用户配置文件
source ~/.bashrc
# 安装solidity
foundryup
# 检查是否安装成功
forge --version
|
foundryup相当于已经用源码构建好的exe程序,如果不安装foundryup,另一种方法是安装 Rust 编译器和 Cargo(相当于rust的包管理器),下载 Foundry 的原始代码自己构建编译,但是更麻烦。
构建初始项目

为了方便的在vscode写foundry,可以把控制台换成git bash,这样就能运行命令了。

运行forge init hello_foundry生成的目录:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
├── README.md
├── foundry.toml
├── lib
│ └── forge-std
│ ├── LICENSE-APACHE
│ ├── LICENSE-MIT
│ ├── README.md
│ ├── foundry.toml
│ ├── package.json
│ ├── scripts
│ ├── src
│ └── test
├── script
│ └── Counter.s.sol
├── src
│ └── Counter.sol
└── test
└── Counter.t.sol
|
-
lib目录下的forge-std是依赖库目录,通常存放项目所依赖的第三方库
-
src/Counter.sol是主智能合约源代码。
-
test/Counter.t.sol 单元测试,继承forge-std/Test.sol
-
script/Counter.s.sol 部署或交互脚本,继承forge-std/Script.sol
-
经测试,.t和.s只是命名规范,实际可以不加,.t文件也可以放到src目录
克隆链上合约
1
2
3
4
5
|
#克隆链上已验证的合约
# 0x...是合约地址,WETH9是保存的文件名
forge clone 0x... WETH9
|
基础命令
1
2
3
4
5
6
7
8
|
# 安装依赖,一般克隆拉取的项目要先安装依赖才能运行,不指定<dep>默认安装所有依赖。
forge install <dep>
# 将特定依赖项更新为指定版本的最新提交,不指定<dep>则对所有依赖项执行更新。
forge update <dep>
# 删除依赖项
forge remove <deps>...
|
Soldeer包管理器
soldeer
Foundry 一直以来使用 git 子模块来处理依赖关系。随着项目变得越来越复杂,原生包管理器的需求开始出现。一种新的方法正在开发中,soldeer.xyz,这是一个用 Rust 构建的 Solidity 原生依赖管理器,并且是开源的(查看仓库)。
初始化新项目用forge soldeer init,安装依赖forge soldeer install
Hardhat 兼容
Forge 还支持基于 Hardhat 的项目,其中依赖项是 npm 包(存储在 node_modules 中)并且其合约存储在 contracts 中而不是 src 中。
要启用 Hardhat 兼容模式,请传递 –hh 标志。
anvil 本地以太坊节点
git bash执行anvil,本地启动ETH节点,会有10个测试账户,每个账户都有测试币。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
_ _
(_) | |
__ _ _ __ __ __ _ | |
/ _` | | '_ \ \ \ / / | | | |
| (_| | | | | | \ V / | | | |
\__,_| |_| |_| \_/ |_| |_|
1.5.1-stable (b0a9dd9ced 2025-12-22T11:40:33.870895500Z)
https://github.com/foundry-rs/foundry
Available Accounts
==================
(0) 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000.000000000000000000 ETH)
(1) 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (10000.000000000000000000 ETH)
(2) 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC (10000.000000000000000000 ETH)
(3) 0x90F79bf6EB2c4f870365E785982E1f101E93b906 (10000.000000000000000000 ETH)
(4) 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 (10000.000000000000000000 ETH)
(5) 0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc (10000.000000000000000000 ETH)
(6) 0x976EA74026E726554dB657fA54763abd0C3a0aa9 (10000.000000000000000000 ETH)
(7) 0x14dC79964da2C08b23698B3D3cc7Ca32193d9955 (10000.000000000000000000 ETH)
(8) 0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f (10000.000000000000000000 ETH)
(9) 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 (10000.000000000000000000 ETH)
Private Keys
==================
(0) 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
(1) 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
(2) 0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a
(3) 0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6
(4) 0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a
(5) 0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba
(6) 0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e
(7) 0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356
(8) 0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97
(9) 0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6
Wallet
==================
# Mnemonic是助记词
Mnemonic: test test test test test test test test test test test junk
Derivation path: m/44'/60'/0'/0/
Chain ID
==================
31337
Base Fee
==================
1000000000
Gas Limit
==================
30000000
Genesis Timestamp
==================
1770899343
Genesis Number
==================
0
Listening on 127.0.0.1:8545
|
1
2
|
# https://eth.merkle.io 是一个 RPC 节点地址。本地 Anvil 程序能通过RPC节点地址读取到以太坊主网上的真实数据。相当于从主网fork一份数据到本地。
$ anvil --fork-url https://eth.merkle.io
|
cast 命令行交互
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
# 查询vitalik.eth地址的余额,单位是eth,vitalik.eth是Vitalik 的 ENS 域名。cast 会自动在后台把这个域名解析成他的钱包地址 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
$ cast balance vitalik.eth --ether --rpc-url https://eth.merkle.io
32.116234270941126850
或者
$ cast balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --ether --rpc-url https://eth
.merkle.io
32.116234270941126850
# 查询主网当前区块高度
cast block-number --rpc-url https://eth.merkle.io
# 查询 WETH (Wrapped Ether) 合约的总供应量 (Total Supply)。0xC02aa...6Cc2是 WETH 合约在以太坊主网上的真实地址。
$ cast call 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 "totalSupply()" --rpc-url https://eth.merkle.io
0x00000000000000000000000000000000000000000001bc10673b6563966603a4
# 向某地址转1eth,0x7...79C8是本地的测试账户之一,0xac...ff80是本地的测试账户之一密钥
cast send 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
--value 1ether
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
|
chisel 交互式环境
Chisel 是 Foundry 工具链的组件之一,一个专为 Solidity 设计的 REPL 工具,可以在终端里快速编写、测试、调试 Solidity 表达式或小段逻辑。
REPL是一种交互式编程环境,就像在控制台键入python就可以进入交互式编程。
我的是win11,直接用chisel命令启动,代码会报错:
1
2
3
4
5
6
7
8
9
|
$ chisel
Welcome to Chisel! Type `!help` to show available commands.
➜ 1+1
Error: Compiler run failed:
Error (6845): Expected import path.
--> ReplContract.sol:5:18:
|
5 | import {Vm} from "lib/forge-std/src\Vm.sol"; # 导入时有反斜杠问题
| ^^^^^^^^^^^^^^^^^^^^
|
因此改用 chisel –no-vm命令解决:
1
2
3
4
5
6
7
8
|
$ chisel --no-vm
Welcome to Chisel! Type `!help` to show available commands.
➜ 1+1
Type: uint256
├ Hex: 0x2
├ Hex (full word): 0x0000000000000000000000000000000000000000000000000000000000000002
└ Decimal: 2
➜
|
forge
不同forge版本,命令格式会有变化,以下是forge 1.5.1-stable!
测试函数必须声明为external或public,声明为internal或 private 不会被 Forge 选中,即使它们以 test 为前缀。
forge test
1
2
3
4
5
|
# 默认扫描test目录下所有以.t.sol结尾文件的测试函数
forge test
# 测试指定文件,实际也可以执行src目录的文件来测试!
forge test --match-path test/TestNumber.sol
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// ^意思是兼容0.8~0.9的版本,如果写成pragma solidity 0.8.10;意思就是确定的0.8.10版本
pragma solidity ^0.8.10;
import {Test} from "forge-std/Test.sol";
contract ContractBTest is Test {
uint256 testNumber;
// setUp()是测试生命周期的钩子,会在每个测试用例执行前调用一次,不是构造函数!
function setUp() public {
testNumber = 42;
}
// foundry约定,要测试的函数,前缀要加上test,否则forge test不会执行这个函数!
function test_NumberIs42() public {
assertEq(testNumber, 42);
}
function test_Subtract43() public {
// testNumber是无符号整数,执行之后-1报错:arithmetic underflow or overflow (0x11)
testNumber -= 43;
}
}
|
单元和模糊测试是单个执行,通过setUp()修改的状态不会用于其他的测试。可以通过实现 beforeTestSetup 函数,在单个测试中通过依赖树模拟多个交易,如果配置的某个交易被回滚,测试将失败。
1
2
3
4
5
|
function beforeTestSetup(
bytes4 testSelector //应用于测试的选择器
) public returns (
bytes[] memory beforeTestCalldat//在测试执行之前应用的任意 calldata 数组
)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
//testC 被配置为使用由 testA 和 setB(uint256) 函数修改的状态
contract ContractTest is Test {
uint256 a;
uint256 b;
function beforeTestSetup(
bytes4 testSelector
) public pure returns (bytes[] memory beforeTestCalldat) {
if (testSelector == this.testC.selector) {
beforeTestCalldat = new bytes[](2);
beforeTestCalldat[0] = abi.encodePacked(this.testA.selector);
beforeTestCalldat[1] = abi.encodeWithSignature("setB(uint256)", 1);
}
}
function testA() public {
require(a == 0);
a += 1;
}
function setB(uint256 value) public {
b = value;
}
function testC() public {
assertEq(a, 1);
assertEq(b, 1);
}
}
|
跟踪Traces
Forge 可以给失败的测试(-vvv)或所有测试(-vvvv)生成跟踪(Traces)。每个跟踪可以有更多的子跟踪(subtraces),每个 subtraces 表示对合约的调用和返回值。
Gas 使用量(标在方括号中)显示整个函数调用 Gas。 但是,你可能会注意到,有时一条 trace 的 Gas 使用量与其所有 subtraces 的 Gas 使用量和并不完全匹配:
1
2
3
4
5
6
7
8
|
[24661] OwnerUpOnlyTest::testIncrementAsOwner()
├─ [2262] OwnerUpOnly::count()
│ └─ ← 0
├─ [20398] OwnerUpOnly::increment()
│ └─ ← ()
├─ [262] OwnerUpOnly::count()
│ └─ ← 1
└─ ← ()
|
下落不明的 Gas 是由于调用之间发生的一些额外操作,例如算术和存储读/写。