原文首发于 Decert.me : https://decert.me/tutorial/solidity/tools/foundry/ 本文用于学习并进行了增加补充,最好的学习手册是官方文档,写得非常详细
curl -L https://foundry.paradigm.xyz | bash
foundryup
注意:只需要全局下载和安装一次,以后在任何目录下都不需要再安装了,如果需要更新就再次运行foundryup
安装安装后,有三个命令行工具 forge
, cast
, anvil
组成
forge: 用来执行初始化项目、管理依赖、测试、构建、部署智能合约 ;
cast: 执行以太坊 RPC 调用的命令行工具, 进行智能合约调用、发送交易或检索任何类型的链数据
anvil: 创建一个本地测试网节点, 也可以用来分叉其他与 EVM 兼容的网络。
注意:这三个是使用过程中的主要命令,学会了这三个命令就能实现构建,测试和调用
cd到你要创建项目的目录下,执行命令 forge init learn-foundry
项目创建好出现以下几个文件:
src
:智能合约目录
script
:部署脚本文件
lib
: 依赖库目录
test
:智能合约测试用例文件夹
foundry.toml
:配置文件,配置连接的网络URL 及编译选项。
.gitmodules
:文件记录了目录与子库的关系
在src
目录下有Counter.sol合约:
demo
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Counter {
uint256 public counter;
function setNumber(uint256 newNumber) public {
counter = newNumber;
}
function increment() public {
counter++;
}
}
在foundry.toml
中使用solc
配置编译器版本:
[profile.default]
src = 'src'
out = 'out'
libs = ['lib']
solc = "0.8.18"
使用命令forge build
编译合约,这个命令是将所有*.sol的文件进行编译
在测试目录下test
添加自己的测试用例,添加文件 Counter.t.sol
,foundry 测试用例使用 .t.sol
后缀,约定具有以test
开头的函数的合约都被认为是一个测试, 以下是测试代码:
import "forge-std/Test.sol";
import "../src/Counter.sol";
contract CounterTest is Test {
Counter public counter;
function setUp() public {
counter = new Counter();
counter.setNumber(0);
}
function testIncrement() public {
counter.increment();
assertEq(counter.number(), 1);
}
function testSetNumber(uint256 x) public {
counter.setNumber(x);
assertEq(counter.number(), x);
}
}
引入 Forge 标准库 的 Test
合约,并让测试合约继承 Test
合约, 这是使用 Forge 编写测试的首选方式。第 9 行 setUp()
函数用来进行一些初始化,它是每个测试用例运行之前调用的可选函数,第 14、19 行是以 test
为前缀的函数的两个测试用例,测试用例中使用 assertEq
断言判断相等。testSetNumber
带有一个参数 x
, 它使用了基于属性的模糊测试, forge 模糊器默认会随机指定256 个值运行测试。模糊测试只给定属性,不指定具体的参数,有forge模糊起随机的在这个属性范围内随机选择一个值进行测试。
使用命令forge test
进行测试,这个命令是将所有*.t.sol的文件进行测试
注意testSetNumber进行了模糊测试,(runs: 256, μ: 28064, ~: 28453)
,含义是:
"runs" 是指模糊器 fuzzer 测试的场景数量。 默认情况下,模糊器 fuzzer 将生成 256 个场景,但是,其可以使用 FOUNDRY_FUZZ_RUNS
环境变量进行配置。
“μ”(希腊字母 mu)是所有模糊运行中使用的平均 Gas
“~”(波浪号)是所有模糊运行中使用的中值 Gas
在foundry.toml配置文件:
就可以修改环境变量FOUNDRY_FUZZ_RUNS
为56:
测试相关的参数可以在这里查看
Forge 提供 create 命令部署合约, 如:
forge create src/Counter.sol:Counter --rpc-url <RPC_URL> --private-key <privateKey>
create 命令需要输入的参数较多,使用部署脚本是更推荐的做法是使用 solidity-scripting 部署。
创建.env文件,
GOERLI_RPC_URL=
PRIVATE_KEY=
ETHERSCAN_API_KEY=
在script里面编写脚本Counter.s.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/Script.sol";
import "../src/Counter.sol";
contract CounterScript is Script {
function setUp() public {}
function run() public {
uint256 deployer = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployer);
Counter counter = new Counter();
console2.log("Counter deployed on %s", address(counter));
vm.stopBroadcast();
}
}
默认情况下,脚本是通过调用名为 run
的函数(我们的入口点)来执行的。
执行命令:
# To load the variables in the .env file
source .env
# To deploy and verify our contract
forge script script/Counter.s.sol:CounterScript --rpc-url $GOERLI_RPC_URL --broadcast --verify -vvvv
命令创建一个本地开发网节点(好像是对 hardhat node的封装 ),用于部署和测试智能合约。它也可以用来分叉其他与 EVM 兼容的网络
设置节点端口
anvil --port <PORT>
使用自定义助记词
anvil --mnemonic=<MNEMONIC>
从节点URL(需要是存档节点)fork 区块链状态,可以指定某个区块时的状态。
anvil --fork-url=$RPC --fork-block-number=<BLOCK>
命令可以用来和区块链交互,因此可以直接使用 cast
在命令行中调用合约。
cast call 0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0 "counter()" --rpc-url local
cast send 0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0 "setNumber(uint256)" 1 --rpc-url local --private-key ***
使用 forge install
可以安装第三方的库,不同于 npm,forge 会把整个第三方的库的 Git 仓库作为子模块放在lib目录下。 使用命令如下
forge install [OPTIONS] <github username>/<github project>@<tag>
forge install OpenZeppelin/openzeppelin-contracts
如果是从git上clone已有的项目,可以直接forge install
安装之后,.gitmodules
会添加新记录:
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
branch = v4.8.2
lib 下也会多一个openzeppelin文件夹:
> tree lib -L 1
lib
├── forge-std
└── openzeppelin-contracts
引入第三方库:
import "openzeppelin-contracts/contracts/access/Ownable.sol";
使用 npm 安装库
如果你使用NPM来安装库,也同样可以支持,在项目根目录下初始化项目,并安装库:
npm init -y
npm install @openzeppelin/contracts
安装完成之后,把node_modules文件夹 配置在 foundry.toml 的 libs中:
[profile.default]
src = 'src'
out = 'out'
libs = ['lib','node_modules']
标准库封装了很多好好的方法可以直接使用,分为 4 个部分:
Vm.sol
:提供作弊码(Cheatcodes)
console.sol
和 console2.sol
:Hardhat 风格的日志记录功能, console2.sol
包含 console.sol
的补丁,允许Forge 解码对控制台的调用追踪,但它与 Hardhat 不兼容。
Script.sol
:Solidity 脚本 的基本实用程序
Test.sol
:DSTest 的超集,包含标准库、作弊码实例 (vm
) 和 Foundry 控制台