Foundry是一个用Rust语言编写部署环境,能够帮助开发者管理依赖项、编译项目、运行测试、部署合约以及从命令行与区块链交互。由于最近开发公链使用了和foundry一样的evm crate,就安装了一下看看,后边可能需要对foundry进行二次开发;这次就简单的测试一下;

Foundry由四个工具组成:

  • Forge- 编译、测试和部署合约
  • Cast- 用于与合约交互的命令行界面
  • Anvil- 用于开发目的的本地测试节点,可分叉预先存在的网络
  • Chisel- 用于快速测试Solidity片段的Solidity REPL(交互式编程环境)

安装

这里我只写一下,我实践过方法;

在 Linux 和 macOS 上

如果您使用 Linux 或 macOS,使用 foundryup 安装最新版本,对于 Linux 和 macOS 用户来说,这是最简单的选择。打开您的终端并输入以下命令:


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


这将下载foundryup。 然后通过运行安装 Foundry:


foundryup


如果一切顺利,您现在可以使用三个二进制文件:forge、cast 、anvil和chisel。

如果您使用 macOS 并显示以下错误,您需要键入 brew install libusb 来安装库

dyld[32719]:Library not loaded:/usr/local/opt/libusb/lib/libusb-1.0.0.dylib

从源码构建

要从源代码构建,您需要获取 Rust和 Cargo。所以需要先按照rust的开发环境;读者自行安装。

安装好环境执行,就比较简单了,方法1或者2,任选一种;

方法1:

cargo install --git https://github.com/foundry-rs/foundry --profile local forge cast chisel anvil

方法2:

# clone the repository
git clone https://github.com/foundry-rs/foundry.git
cd foundry
# install Forge
cargo install --path ./crates/forge --profile local --force
# install Cast
cargo install --path ./crates/cast --profile local --force
# install Anvil
cargo install --path ./crates/anvil --profile local --force
# install Chisel
cargo install --path ./crates/chisel --profile local --force

Linux/macOS使用foundryup,很方便;如果你是在windows,只能从源码构建了;

创建Foundry项目

创建

使用如下命令,创建一个默认的项目

forge init foundry

创建默认项目后,您将看到以下文件:

.
├── foundry.toml
├── lib
│   └── forge-std
│       ├── LICENSE-APACHE
│       ├── LICENSE-MIT
│       ├── README.md
│       ├── foundry.toml
│       ├── lib
│       └── src
├── script
│   └── Counter.s.sol
├── src
│   └── Counter.sol
└── test
    └── Counter.t.sol

7 directories, 8 files
  • 您可以使用 foundry.toml 配置 Foundry 的行为。
  • 重新映射在 remappings.txt 中指定。
  • 合约的默认目录是 src/
  • 测试的默认目录是test/,其中任何具有以test开头的函数的合约都被视为测试。
  • 依赖项作为 git 子模块存储在 lib/ 中。
修改

src文件夹可能已经包含Counter.sol(一个最小的Solidity合约),我可以自行删除此合约。然后添加,一个ERC-20合约。在合约目录中,可以创建一个Token.sol文件,打开文件并添加以下合约:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";

contract Token is ERC20{
    constructor(
        string memory name,
        string memory symbol,
        uint256 initialSupply) ERC20(name, symbol){
        _mint(msg.sender, initialSupply);
    }
}

由于我们使用了OpenZeppelin合约,需要在编译合约之前,安装OpenZeppelin合约作为依赖项。我们使用forge install xxx安装依赖项;(默认情况下,Forge 使用 git submodules管理依赖项,这意味着它可以与任何包含智能合约的 GitHub 代码库一起使用。)所以xxx可以是一个 GitHub 仓库的路径(owner/repo),详细可以参考forge install 

forge install OpenZeppelin/openzeppelin-contracts
编译

安装完所有的依赖项后,您可以开始编译合约:

forge build

编译完成后,将创建两个文件夹:out和cache。您合约的ABI和字节码将包含在out文件夹中。这两个文件夹已被默认Foundry项目初始化中包含的.gitignore忽略。

测试项目

让我们回顾一下最常见的编写测试的方式,使用 Forge 标准库(forge-std) 的 Test 合约,这是使用 Forge 编写测试的首选方式。它提供基本的日志记录和断言功能。 要访问这些函数,请导入 forge-std/Test.sol 并继承自测试合约 Test :

import "forge-std/Test.sol";

测试之前,我们先看下一test文件夹底下的Counter.t.sol;注意名字中的.t。

import {Test, console2} from "forge-std/Test.sol";
import {Counter} from "../src/Counter.sol";

contract CounterTest is Test {
    Counter public counter;
//setUp:在每个测试用例运行之前调用的可选函数,用于部署环境
    function setUp() public {
        counter = new Counter();
        counter.setNumber(0);
    }
//test:以 test 为前缀的函数作为测试用例运行
    function test_Increment() public {
        counter.increment();
        assertEq(counter.number(), 1);
    }
//testFail: test 前缀的测试的反面 - 如果函数没有 revert,则测试失败
    function testFail_Increment() public {
        counter.increment();
        assertEq(counter.number(), 99);
    }

    function testFuzz_SetNumber(uint256 x) public {
        counter.setNumber(x);
        assertEq(counter.number(), x);
    }
}

我们修改创建一个Token.t.sol,用来测试我们的Token.sol;

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test, console2} from "forge-std/Test.sol";
import {Token} from "../src/Token.sol";

contract TokenTest is Test {
    Token public token;
//使用0x9创建一个新的erc20合约
    function setUp() public {
        vm.prank(address(9));
        token = new Token("XQToken","XQ", 10000);
    }
//测试0x9的地址上,token数量是不是等于总量
    function test_BalanceOf() public {
        uint256 bo = token.balanceOf(address(9));
        assertEq(bo, 10000);
    }
}

然后就可以使用,forge test进行测试啦;

更多细节可以查看Foundry