Foundry 项目结构

本章介绍一个新的 Foundry 项目的基本结构。

推特@Hita_DAO    DiscordHitaDAO

我们使用 forge init 命令构建的项目,在默认情况下,项目中包含 4 个目录 libscriptsrctest,以及 foundry.toml 文件。

项目的结构如下所示:

.
├── 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:项目配置文件,用于配置 forge 命令的行为。
  • 目录 src:用于存放待发布的智能合约文件。我们最终要发布的智能合约都要放到这个目录下。
  • 目录 test:用于存放测试使用的智能合约文件。我们使用这些合约执行测试,以验证 src 目录下合约功能的正确性。
  • 目录 lib:用于存放项目依赖的库。如果项目需要依赖于外部智能合约或库(比如 OpenZeppelin 的合约),它们将会被安装在这个目录下。
  • 目录 script:包含用于与智能合约交互的脚本。这些脚本可以用来部署合约、执行交易或者调用合约的函数。

这个结构旨在帮助开发者组织和管理智能合约项目,使得代码更加模块化,便于管理和协作。

这些目录都是按照默认配置生成的,但是我们可以通过 forge 命令参数改变这些目录。

比如:使用 --lib-paths 可以指定依赖库所在的目录;使用 --contracts 可以指定合约存放的目录。

另外,我们还可以在 foundry.toml 中自行配置这些目录。

lib 目录

lib 目录中,默认安装了 forge-std 依赖库,这也是 Foundry 项目必不可少的一个基础依赖库。

forge-stdFoundry 框架中的一个标准库,旨在为智能合约开发者提供一系列的实用功能和工具,以便更高效地编写、测试和部署智能合约。

forge-std 包含了许多预定义的合约和库函数,这些功能涵盖了合约测试、模拟交易、状态修改等多个方面,极大地简化了合约开发过程中的常见任务。

src 目录

待发布合约的默认目录是 src。这个目录中可以放置任意多个合约,它们最终都要部署到主网。我们的测试工作也都是针对这些合约进行的。

在一个新创建的项目中,默认包含了一个样例合约 Counter.sol,用于初学者学习使用。在实际项目中,我们可以直接删除。

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

contract Counter {
    uint256 public number;

    function setNumber(uint256 newNumber) public {
        number = newNumber;
    }

    function increment() public {
        number++;
    }
}

这个合约实现了一个简单的计数器功能。其中函数 setNumber 用于设置计数器的初值。函数 increment 用于将计数器加 1 。

test 目录

用于存放测试使用的智能合约文件。我们使用这些合约执行测试,以验证 src 目录下合约功能的正确性。

在一个新创建的项目中,默认包含了一个样例合约 Counter.t.sol,用于测试 src 目录中 Counter.sol 合约。

它的代码如下:

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

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

contract CounterTest is Test {
    Counter public counter;

    function setUp() public {
        counter = new Counter();
        counter.setNumber(0);
    }

    function test_Increment() public {
        counter.increment();
        assertEq(counter.number(), 1);
    }

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

合约中任何具有以 test 开头的函数的合约都被视为测试用例,比如: 函数 test_Increment 和 test_SetNumber,它们都是测试用例。

我们在执行 forge test 命令时,就会自动执行 test_Increment 和 test_SetNumber 这两个函数。