加密日记【004】foundry
April 7th, 2023

原文首发于 Decert.me : https://decert.me/tutorial/solidity/tools/foundry/ 本文用于学习并进行了增加补充,最好的学习手册是官方文档,写得非常详细

1. 下载

curl -L https://foundry.paradigm.xyz | bash

2.安装

foundryup

注意:只需要全局下载和安装一次,以后在任何目录下都不需要再安装了,如果需要更新就再次运行foundryup

3. 使用

安装安装后,有三个命令行工具 forgecastanvil 组成

  • forge: 用来执行初始化项目、管理依赖、测试、构建、部署智能合约 ;

  • cast: 执行以太坊 RPC 调用的命令行工具, 进行智能合约调用、发送交易或检索任何类型的链数据

  • anvil: 创建一个本地测试网节点, 也可以用来分叉其他与 EVM 兼容的网络。

注意:这三个是使用过程中的主要命令,学会了这三个命令就能实现构建,测试和调用

4.初始化项目

cd到你要创建项目的目录下,执行命令 forge init learn-foundry

项目创建好出现以下几个文件:

  • src:智能合约目录

  • script :部署脚本文件

  • lib: 依赖库目录

  • test:智能合约测试用例文件夹

  • foundry.toml:配置文件,配置连接的网络URL 及编译选项。

  • .gitmodules :文件记录了目录与子库的关系

5.创建合约

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的文件进行编译

6.编写自动化测试

在测试目录下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:

测试相关的参数可以在这里查看

7.部署合约

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

Anvil

命令创建一个本地开发网节点(好像是对 hardhat node的封装 ),用于部署和测试智能合约。它也可以用来分叉其他与 EVM 兼容的网络

设置节点端口

anvil --port <PORT>

使用自定义助记词

anvil --mnemonic=<MNEMONIC> 

从节点URL(需要是存档节点)fork 区块链状态,可以指定某个区块时的状态。

anvil --fork-url=$RPC --fork-block-number=<BLOCK>

Cast

命令可以用来和区块链交互,因此可以直接使用 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.solconsole2.sol:Hardhat 风格的日志记录功能, console2.sol 包含 console.sol 的补丁,允许Forge 解码对控制台的调用追踪,但它与 Hardhat 不兼容。

  • Script.solSolidity 脚本 的基本实用程序

  • Test.sol:DSTest 的超集,包含标准库、作弊码实例 (vm) 和 Foundry 控制台

Subscribe to 0x3c
Receive the latest updates directly to your inbox.
Nft graphic
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.
More from 0x3c

Skeleton

Skeleton

Skeleton