Foundry是一个智能合约开发工具链。

Foundry管理您的依赖关系、编译项目、运行测试、部署,并允许您通过命令行和Solidity脚本与链交互。

📖 Contributing

You can contribute to this book on GitHub.

Sections

Getting Started

要开始使用Foundry,请安装Foundry并设置第一个项目。

Projects

本节将向您概述如何创建和使用现有项目。

Forge Overview

本节概述将为您提供有关如何使用“伪造”来开发、测试和部署智能合约所需的所有知识。

Cast Overview

了解如何使用“cast”与智能合约交互、发送事务以及从命令行获取链数据。

Anvil Overview

Learn about anvil, Foundry's local node.

Configuration

配置Foundry的指引:

Tutorials

与Foundry建立智能合约的教程。

Appendix

References, troubleshooting, and more.


You can also check out Awesome Foundry, a curated list of awesome Foundry resources, tutorials, tools, and libraries!

## 安装

在 Linux 和 macOS 上

如果您使用 Linux 或 macOS,则有两种不同的方式来安装 Foundry。

使用 foundryup 安装最新版本

对于 Linux 和 macOS 用户来说,这是最简单的选择。

打开您的终端并输入以下命令:

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

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

foundryup

如果一切顺利,您现在可以使用三个二进制文件:forgecastanvil

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

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

💡 提示

要在安装后更新 foundryup,只需再次运行 foundryup,它将更新到最新的 Foundry 版本。 您还可以使用“foundryup -v $VERSION”恢复到特定版本的 Foundry。

从源代码构建

要从源代码构建,您需要获取 Rust 和 Cargo。 获得两者的最简单方法是使用 rustup

在 Linux 和 macOS 上,这是按如下方式完成的:

curl https://sh.rustup.rs -sSf | sh

它将下载脚本并开始安装。

在 Windows 上,从源代码构建

如果您使用 Windows,则需要从源代码构建以获取 Foundry。

rustup.rs 下载并运行 rustup-init。 它将在控制台中开始安装。

如果您遇到错误,很可能是因为您没有 VSCode安装程序,您可以在此处下载 并进行安装。

在此之后,运行以下命令从源代码构建 Foundry:

cargo install --git https://github.com/foundry-rs/foundry foundry-cli anvil --bins --locked

要从源更新,请再次运行相同的命令。

与 Docker 一起使用

Foundry 也可以完全在 Docker 容器中使用。 如果没有,可以直接从 Docker 的网站 安装 Docker。

安装后,您可以通过运行以下命令下载最新版本:

docker pull ghcr.io/foundry-rs/foundry:latest

也可以在本地构建 docker 镜像。 从 Foundry 存储库运行:

docker build -t foundry。

ℹ️ 注意

某些机器(包括带有 M1 芯片的机器)可能无法在本地构建 docker 镜像。 这是一个众所周知的问题。

Foundry 的第一步

本节概述了“forge”命令行工具。 我们演示了如何创建一个新项目、编译和测试它。

要使用 Foundry 启动一个新项目,请使用 forge init

$ forge init hello_foundry

让我们看看 forge 为我们生成了什么:

$ cd hello_foundry
$ tree . -d -L 1
.
├── lib
├── script
├── src
└── test

4 directories

我们可以使用 forge build 构建项目:

$ forge build
Compiling 10 files with 0.8.16
Solc 0.8.16 finished in 3.97s
Compiler run successful

并使用 forge test 运行测试:

$ forge test
No files changed, compilation skipped

Running 2 tests for test/Counter.t.sol:CounterTest
[PASS] testIncrement() (gas: 28312)
[PASS] testSetNumber(uint256) (runs: 256, μ: 27376, ~: 28387)
Test result: ok. 2 passed; 0 failed; finished in 24.43ms

💡 提示

您始终可以通过在末尾添加“--help”来打印任何子命令(或它们的子命令)的帮助。

创建一个新项目

要使用 Foundry 启动一个新项目,请使用 forge init

$ forge init hello_foundry

这将从默认模板创建一个新目录“hello_foundry”。 这也会初始化一个新的 git 存储库。

如果你想使用不同的模板创建一个新项目,你可以传递 --template 指令,如下所示:

$ forge init --template https://github.com/foundry-rs/forge-template hello_template

现在,让我们检查一下默认模板的样子:

$ cd hello_foundry
$ tree . -d -L 1
.
├── lib
├── script
├── src
└── test

4 directories

默认模板安装了一个依赖项:Forge 标准库。 这是用于 Foundry 项目的首选测试库。 此外,该模板还附带一个空的入门合约和一个简单的测试。

让我们构建项目:

$ forge build
Compiling 10 files with 0.8.16
Solc 0.8.16 finished in 3.97s
Compiler run successful

并运行测试:

$ forge test
No files changed, compilation skipped

Running 2 tests for test/Counter.t.sol:CounterTest
[PASS] testIncrement() (gas: 28312)
[PASS] testSetNumber(uint256) (runs: 256, μ: 27376, ~: 28387)
Test result: ok. 2 passed; 0 failed; finished in 24.43ms

您会注意到弹出了两个新目录:outcache

out 目录包含您的合约结构文件,例如 ABI,而 cacheforge 使用来仅仅重新编译必要的内容。

处理现有项目

如果您下载一个使用 Foundry 的现有项目,那真的很容易上手。

首先,从某个地方获取项目。 在此示例中,我们将从 GitHub 克隆 femplate 存储库:

$ git clone https://github.com/abigger87/femplate
$ cd femplate
$ forge install

我们运行 forge install 来安装项目中的子模块依赖项。

要构建,请使用 forge build

$ forge build
Compiling 10 files with 0.8.15
Solc 0.8.15 finished in 4.35s
Compiler run successful

要进行测试,请使用 forge test

$ forge test
No files changed, compilation skipped

Running 1 test for test/Greeter.t.sol:GreeterTest
[PASS] testSetGm() (gas: 107402)
Test result: ok. 1 passed; 0 failed; finished in 4.77ms

依赖

默认情况下,Forge 使用 git submodules 管理依赖项,这意味着它可以与任何包含智能合约的 GitHub 存储库一起使用。

添加依赖

要添加依赖项,请运行 forge install

$ forge install transmissions11/solmate
Installing solmate in "/private/var/folders/p_/xbvs4ns92wj3b9xmkc1zkw2w0000gn/T/tmp.FRH0gNvz/deps/lib/solmate" (url: Some("https://github.com/transmissions11/solmate"), tag: None)
    Installed solmate

这将拉取 solmate 库,在 git 中暂存 .gitmodules 文件并使用消息“Installed solmate”进行提交。

如果我们现在检查 lib 文件夹:

$ tree lib -L 1
lib
├── forge-std
├── solmate
└── weird-erc20

3 directories, 0 files

我们可以看到Forge安装了solmate

默认情况下,forge install 安装最新的 master 分支版本。 如果你想安装一个特定的标签或提交,你可以这样做:

$ forge install transmission11/solmate@v7

重新映射依赖项

Forge 可以重新映射依赖关系,使它们更容易导入。 Forge 将自动尝试为您推断出一些重新映射:

$ forge remappings
ds-test/=lib/forge-std/lib/ds-test/src/
forge-std/=lib/forge-std/src/
solmate/=lib/solmate/src/
weird-erc20/=lib/weird-erc20/src/

这些重新映射意味着:

  • 要从 forge-std 导入,我们会写:import "forge-std/Contract.sol";
  • 要从 ds-test 导入,我们会这样写:import "ds-test/Contract.sol";
  • 要从 solmate 导入,我们会这样写:import "solmate/Contract.sol";
  • 要从 weird-erc20 导入,我们会写:import "weird-erc20/Contract.sol";

您可以通过在项目的根目录中创建一个“remappings.txt”文件来自定义这些重新映射。

让我们创建一个名为 solmate-utils 的重映射,它指向 solmate repo中的 utils 文件夹!

solmate-utils/=lib/solmate/src/utils/

现在我们可以像这样导入 solmate repo的 src/utils 中的任何合约:

import "solmate-utils/Contract.sol";

更新依赖

您可以使用 forge update <dep> 将特定依赖项更新为您指定版本的最新提交。 例如,如果我们想从我们之前安装的 solmate 主版本中提取最新的提交,我们将运行:

$ forge update lib/solmate

或者,您可以通过运行“forge update”一次对所有依赖项执行此操作。

删除依赖

您可以使用 forge remove <deps>... 删除依赖项,其中 <deps> 是依赖项的完整路径或只是名称 . 例如,要删除 solmate,这两个命令是等价的:

$ forge remove solmate
# ... 等同于 ...
$ forge remove lib/solmate

Hardhat兼容

Forge 还支持基于 Hardhat 的项目,其中依赖项是 npm 包(存储在 node_modules 中)并且合同存储在 contracts 中而不是 src 中。

要启用 Hardhat 兼容模式,请传递 --hh 标志。

Forge 在您构建项目的方式上是灵活的。 默认情况下,结构为:

.
├── 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/ 中。

您可以分别使用 --lib-paths--contracts 标志配置 Forge 在何处查找依赖项和合同。 或者,您可以在 foundry.toml 中配置它。

结合重新映射,这为您提供了支持其他工具链(例如 Hardhat 和 Truffle)的项目结构所需的灵活性。

对于自动的 Hardhat 支持,您还可以传递 --hh 标志,它设置以下标志:--lib-paths node_modules --contracts contracts

Forge概述

Forge 是 Foundry 附带的命令行工具。 Forge 测试、构建和部署您的智能合约。

测试

Forge 可以使用 forge test 命令运行测试。 所有测试都是用 Solidity 编写的。

Forge 将在您的源目录中的任何位置查找测试。 任何具有以test开头的函数的合约都被认为是一个测试。 通常,测试将按照约定放在 src/test 中,并以 .t.sol 结尾。

下面是在新创建的项目中运行 forge test 的示例,该项目只有默认测试:

$ forge test
No files changed, compilation skipped

Running 2 tests for test/Counter.t.sol:CounterTest
[PASS] testIncrement() (gas: 28312)
[PASS] testSetNumber(uint256) (runs: 256, μ: 27376, ~: 28387)
Test result: ok. 2 passed; 0 failed; finished in 24.43ms

您还可以通过传递过滤器来运行特定测试:

$ forge test --match-contract ComplicatedContractTest --match-test testDeposit
Compiling 7 files with 0.8.10
Solc 0.8.10 finished in 4.20s
Compiler run successful

Running 2 tests for test/ComplicatedContract.t.sol:ComplicatedContractTest
[PASS] testDepositERC20() (gas: 102237)
[PASS] testDepositETH() (gas: 61458)
Test result: ok. 2 passed; 0 failed; finished in 1.05ms

这将在名称中带有 testDepositComplicatedContractTest 测试合约中运行测试。 这些标志的反向版本也存在(--no-match-contract--no-match-test)。

您可以使用 --match-path 在与 glob 模式匹配的文件名中运行测试。

$ forge test --match-path test/ContractB.t.sol
No files changed, compilation skipped

Running 1 test for test/ContractB.t.sol:ContractBTest
[PASS] testExample() (gas: 257)
Test result: ok. 1 passed; 0 failed; finished in 492.35µs

--match-path 标志的反面是 --no-match-path

日志和跟踪

forge test 的默认行为是只显示通过和失败测试的摘要。 您可以通过增加详细程度(使用-v标志)来控制此行为。 每个详细级别都会添加更多信息:

  • 2 级 (-vv):还会显示测试期间发出的日志。 这包括来自测试的断言错误,显示诸如预期与实际之类的信息。
  • 级别 3 (-vvv):还显示失败测试的堆栈跟踪。
  • 级别 4 (-vvvv):显示所有测试的堆栈跟踪,并显示失败测试的设置跟踪。
  • 级别 5 (-vvvvv):始终显示堆栈跟踪和设置跟踪。

Watch模式

当您使用forge test --watch对文件进行更改时,Forge 可以重新运行您的测试。

默认情况下,仅重新运行更改的测试文件。 如果你想重新运行更改的所有测试,你可以使用 forge test --watch --run-all

编写测试

测试是用 Solidity 编写的。 如果测试功能revert,则测试失败,否则通过。

让我们回顾一下最常见的编写测试的方式,使用 Forge Standard LibraryTest 合约,这是编写测试的首选方式 与Forge。

在本节中,我们将使用 Forge Std 的“Test”合约中的函数复习基础知识,该合约本身是 DSTest 的超集。 您将学习如何使用 Forge 标准库中的更多高级内容 soon

DSTest 提供基本的日志记录和断言功能。 要访问这些函数,请导入 forge-std/Test.sol 并从测试合约中的 Test 继承:

import "forge-std/Test.sol";

让我们检查一个基本测试:

pragma solidity 0.8.10;

import "forge-std/Test.sol";

contract ContractBTest is Test {
    uint256 testNumber;

    function setUp() public {
        testNumber = 42;
    }

    function testNumberIs42() public {
        assertEq(testNumber, 42);
    }

    function testFailSubtract43() public {
        testNumber -= 43;
    }
}

Forge 在测试中使用以下关键字:

  • setUp:在每个测试用例运行之前调用的可选函数
    function setUp() public {
        testNumber = 42;
    }
  • test:以 test 为前缀的函数作为测试用例运行
    function testNumberIs42() public {
        assertEq(testNumber, 42);
    }
  • testFail: test 前缀的倒数 - 如果函数没有revert,则测试失败
    function testFailSubtract43() public {
        testNumber -= 43;
    }

一个好的做法是将类似 testCannot 的东西与 expectRevert cheatcodes结合使用(cheatcodes在下面的 section)。 现在,不使用 testFail,您确切地知道revert了什么:

    function testCannotSubtract43() public {
        vm.expectRevert(stdError.arithmeticError);
        testNumber -= 43;
    }

测试部署到 0xb4c79daB8f259C7Aee6E5b2Aa729821864227e84。 如果您在测试中部署合同,则 0xb4c...7e84 将是它的部署者。 如果在测试中部署的合约向其部署者授予特殊权限, 例如 Ownable.solonlyOwner 修饰符,那么测试合约 0xb4c...7e84 将具有这些权限。

⚠️ 注意

测试函数必须具有externalpublic可见性。 声明为internalprivate 不会被 Forge 选中,即使它们以 test 为前缀。

共享设置

可以通过创建辅助抽象合约并在测试合约中继承它们来使用共享设置:

abstract contract HelperContract {
    address constant IMPORTANT_ADDRESS = 0x543d...;
    SomeContract someContract;
    constructor() {...}
}

contract MyContractTest is Test, HelperContract {
    function setUp() public {
        someContract = new SomeContract(0, IMPORTANT_ADDRESS);
        ...
    }
}

contract MyOtherContractTest is Test, HelperContract {
    function setUp() public {
        someContract = new SomeContract(1000, IMPORTANT_ADDRESS);
        ...
    }
}

💡 提示

使用 getCode cheatcodes部署具有不兼容 Solidity 版本的合约。

Cheatcodes

大多数时候,仅仅测试您的智能合约输出是不够的。 为了操纵区块链的状态,以及测试特定的reverts和事件Events,Foundry 附带了一组Cheatcodes。

Cheatcodes允许您更改块号、您的身份等。 它们是通过在特别指定的地址上调用特定函数来调用的:0x7109709ECfa91a80626fF3989D68f67F5b1DD12D

您可以通过 Forge 标准库的“测试”合约中提供的“vm”实例轻松访问Cheatcodes。 Forge 标准库在以下 section 中有更详细的解释。

让我们为只能由其所有者调用的智能合约编写一个测试。

pragma solidity 0.8.10;

import "forge-std/Test.sol";

error Unauthorized();

contract OwnerUpOnly {
    address public immutable owner;
    uint256 public count;

    constructor() {
        owner = msg.sender;
    }

    function increment() external {
        if (msg.sender != owner) {
            revert Unauthorized();
        }
        count++;
    }
}

contract OwnerUpOnlyTest is Test {
    OwnerUpOnly upOnly;

    function setUp() public {
        upOnly = new OwnerUpOnly();
    }

    function testIncrementAsOwner() public {
        assertEq(upOnly.count(), 0);
        upOnly.increment();
        assertEq(upOnly.count(), 1);
    }
}

如果我们现在运行forge test,我们将看到测试通过,因为OwnerUpOnlyTestOwnerUpOnly 的所有者。

$ forge test
Compiling 7 files with 0.8.10
Solc 0.8.10 finished in 4.25s
Compiler run successful

Running 1 test for test/OwnerUpOnly.t.sol:OwnerUpOnlyTest
[PASS] testIncrementAsOwner() (gas: 29162)
Test result: ok. 1 passed; 0 failed; finished in 928.64µs

让我们确保绝对不是所有者的人不能增加计数:

contract OwnerUpOnlyTest is Test {
    OwnerUpOnly upOnly;

        // ...

    function testFailIncrementAsNotOwner() public {
        vm.prank(address(0));
        upOnly.increment();
    }
}

如果我们现在运行“forge test”,我们将看到所有测试都通过了。

$ forge test
No files changed, compilation skipped

Running 2 tests for test/OwnerUpOnly.t.sol:OwnerUpOnlyTest
[PASS] testFailIncrementAsNotOwner() (gas: 8413)
[PASS] testIncrementAsOwner() (gas: 29162)
Test result: ok. 2 passed; 0 failed; finished in 1.03ms

测试通过是因为 prank Cheatcodes将我们的身份更改为下一次调用的零地址 (upOnly.increment())。 由于我们使用了 testFail 前缀,测试用例通过了,但是,使用 testFail 被认为是一种反模式(anti-pattern),因为它没有告诉我们任何关于为什么 upOnly.increment() 被revert的信息。

如果我们在启用跟踪的情况下再次运行测试,我们可以看到我们revert了正确的错误消息。

$ forge test -vvvv --match-test testFailIncrementAsNotOwner
No files changed, compilation skipped

Running 1 test for test/OwnerUpOnly.t.sol:OwnerUpOnlyTest
[PASS] testFailIncrementAsNotOwner() (gas: 8413)
Traces:
  [8413] OwnerUpOnlyTest::testFailIncrementAsNotOwner() 
    ├─ [0] VM::prank(0x0000000000000000000000000000000000000000) 
    │   └─ ← ()
    ├─ [247] 0xce71…c246::increment() 
    │   └─ ← 0x82b42900
    └─ ← 0x82b42900

Test result: ok. 1 passed; 0 failed; finished in 2.01ms

为了将来确定,让我们确保我们revert了,因为我们不是使用 expectRevert Cheatcodes的所有者:

contract OwnerUpOnlyTest is Test {
    OwnerUpOnly upOnly;

     // ...

    // Notice that we replaced `testFail` with `test`
    function testIncrementAsNotOwner() public {
        vm.expectRevert(Unauthorized.selector);
        vm.prank(address(0));
        upOnly.increment();
    }
}

如果我们最后一次运行 forge test,我们会看到测试仍然通过,但这次我们确信如果我们因为任何其他原因revert它总是会失败。

$ forge test
No files changed, compilation skipped

Running 2 tests for test/OwnerUpOnly.t.sol:OwnerUpOnlyTest
[PASS] testIncrementAsNotOwner() (gas: 8739)
[PASS] testIncrementAsOwner() (gas: 29162)
Test result: ok. 2 passed; 0 failed; finished in 1.15ms

另一个可能不那么直观的Cheatcodes是 expectEmit 函数。 在查看 expectEmit 之前,我们需要了解什么是事件Events。

事件Events是合约的可继承成员。 当您发出事件Events时,参数存储在区块链上。 indexed 属性可以添加到事件Events的最多三个参数中,以形成称为 Transfer 的数据结构。 Topics允许用户搜索区块链上的事件Events。

pragma solidity 0.8.10;

import "forge-std/Test.sol";

contract EmitContractTest is Test {
    event Transfer(address indexed from, address indexed to, uint256 amount);

    function testExpectEmit() public {
        ExpectEmit emitter = new ExpectEmit();
        // Check that topic 1, topic 2, and data are the same as the following emitted event.
        // Checking topic 3 here doesn't matter, because `Transfer` only has 2 indexed topics.
        vm.expectEmit(true, true, false, true);
        // The event we expect
        emit Transfer(address(this), address(1337), 1337);
        // The event we get
        emitter.t();
    }

    function testExpectEmitDoNotCheckData() public {
        ExpectEmit emitter = new ExpectEmit();
        // Check topic 1 and topic 2, but do not check data
        vm.expectEmit(true, true, false, false);
        // The event we expect
        emit Transfer(address(this), address(1337), 1338);
        // The event we get
        emitter.t();
    }
}

contract ExpectEmit {
    event Transfer(address indexed from, address indexed to, uint256 amount);

    function t() public {
        emit Transfer(msg.sender, address(1337), 1337);
    }
}

当我们调用 vm.expectEmit(true, true, false, true); 时,我们想要检查下一个事件Events的第一个和第二个 indexed 主题topic。

testExpectEmit() 中预期的 Transfer 事件Events意味着我们期望 fromaddress(this),而 toaddress(1337)。 这与从 emitter.t() 发出的事件Events进行比较。

换句话说,我们正在检查来自 emitter.t() 的第一个主题topic是否等于 address(this)expectEmit 中的第三个参数设置为 false,因为不需要检查 Transfer 事件中的第三个主题topic,因为只有两个。 即使我们设置为true也没关系。

expectEmit 中的第 4 个参数设置为 true,这意味着我们要检查 "non-indexed topics",也称为数据。

例如,我们希望来自 testExpectEmit 中预期事件Events的数据(即 amount)等于实际发出的事件Events中的数据。 换句话说,我们断言 emitter.t() 发出的 amount 等于 1337。 如果 expectEmit 中的第四个参数设置为 false,我们将不会检查 amount

换句话说,testExpectEmitDoNotCheckData 是一个有效的测试用例,即使数量不同,因为我们不检查数据。


📚 参考

请参阅 Cheatcodes Reference 以获得所有可用Cheatcodes的完整概述。

Forge 标准库概览

Forge Standard Library(简称 Forge Std)是一个有用的合约集合,可以让编写测试更简单、更快速、更人性化。

使用 Forge Std 是使用 Foundry 编写测试的首选方式。

它提供了开始编写测试所需的所有基本功能:

  • Vm.sol:最新的cheatcodes界面
  • console.solconsole2.sol:Hardhat 风格的日志记录功能 -Script.sol:[Solidity 脚本] 的基本实用程序(../tutorials/solidity-scripting.md)
  • Test.sol:DSTest 的超集,包含标准库、作弊代码实例 (vm) 和 Hardhat 控制台

只需导入 Test.sol 并从测试合约中的 Test 继承:

import "forge-std/Test.sol";

contract ContractTest is Test { ...

现在你可以:

// Access Hevm via the `vm` instance
vm.startPrank(alice);

// Assert and log using Dappsys Test
assertEq(dai.balanceOf(alice), 10000e18);

// Log with the Hardhat `console` (`console2`)
console.log(alice.balance);

// Use anything from the Forge Std std-libraries
deal(address(dai), alice, 10000e18);

要单独导入 Vm 界面或 console 库:

import "forge-std/Vm.sol";
import "forge-std/console.sol";

注意: console2.sol 包含 console.sol 的补丁,允许 Forge 解码控制台调用的跟踪,但它与 Hardhat 不兼容。

import "forge-std/console2.sol";

标准库

Forge Std 目前由六个标准库组成。

Std Logs

Std Logs扩展了 DSTest 库中的日志记录事件。

Std Assertions

Std Assertions扩展了 DSTest 库中的断言函数。

Std Cheats

Std Cheats 是 Forge 作弊代码的包装器,使它们更安全地使用和改进 DX。

你可以通过简单地在你的测试合约中调用它们来访问 Std Cheats,就像你调用任何其他内部函数一样:

// set up a prank as Alice with 100 ETH balance
hoax(alice, 100 ether);

Std Errors

Std Errors 提供围绕常见内部 Solidity 错误errors和恢复reverts的包装器。

Std Errors与 expectRevert cheatcodes结合使用最有用,因为您不需要自己记住内部 Solidity panic codes。 请注意,您必须通过 stdError 访问它们,因为这是一个库。

// expect an arithmetic error on the next call (e.g. underflow)
vm.expectRevert(stdError.arithmeticError);

Std Storage

Std Storage 使操作合约存储变得容易。 它可以找到并写入与特定变量关联的存储槽。

Test 合约已经提供了一个 StdStorage 实例 stdstore,您可以通过它访问任何标准存储功能。 请注意,您必须先在测试合约中添加“使用 stdStorage 来存储 StdStorage”。

// find the variable `score` in the contract `game`
// and change its value to 10
stdstore
    .target(address(game))
    .sig(game.score.selector)
    .checked_write(10);

Std Math

Std Math 是一个库,其中包含 Solidity 中未提供的有用的数学函数。

请注意,您必须通过 stdMath 访问它们,因为这是一个库。

// get the absolute value of -10
uint256 ten = stdMath.abs(-10)

📚 参考

有关 Forge 标准库的完整概述,请参阅 Forge 标准库参考

理解Traces

Forge 可以为失败的测试(-vvv)或所有测试(-vvvv)生成跟踪。

跟踪遵循相同的通用格式:

  [<Gas Usage>] <Contract>::<Function>(<Parameters>)
    ├─ [<Gas Usage>] <Contract>::<Function>(<Parameters>)
    │   └─ ← <Return Value>
    └─ ← <Return Value>

每个跟踪可以有更多的subtraces,每个subtraces表示对合约的调用和返回值。

如果您的终端支持颜色,轨迹也会有多种颜色:

  • 绿色:对于不revert的calls
  • 红色:用于revert的calls
  • 蓝色:用于调用cheat codes
  • 青色:用于发出的日志
  • 黄色:用于合约部署

gas 使用量(标在方括号中)用于整个函数调用。 但是,您可能会注意到,有时一条trace的gas使用量与其所有subtraces的gas使用量并不完全匹配:

  [24661] OwnerUpOnlyTest::testIncrementAsOwner()
    ├─ [2262] OwnerUpOnly::count()
    │   └─ ← 0
    ├─ [20398] OwnerUpOnly::increment()
    │   └─ ← ()
    ├─ [262] OwnerUpOnly::count()
    │   └─ ← 1
    └─ ← ()

下落不明的gas是由于调用之间发生的一些额外操作,例如算术和存储读/写。

Forge 将尝试解码尽可能多的签名和值,但有时这是不可能的。 在这些情况下,Traces将如下所示:

  [<Gas Usage>] <Address>::<Calldata>
    └─ ← <Return Data>

分叉测试

Forge 支持使用两种不同的方法在分叉环境中进行测试:

使用哪种方法? 分叉模式提供针对特定分叉环境运行整个测试套件,而分叉cheatcodes提供更大的灵活性和表现力,可以在测试中使用多个分叉。 您的特定用例和测试策略将有助于告知使用哪种方法。

分叉模式

要在分叉环境(例如分叉的以太坊主网)中运行所有测试,请通过 --fork-url 标志传递 RPC URL:

forge test --fork-url <your_rpc_url>

以下值已更改以反映分叉时链的值:

可以使用 --fork-block-number 指定要从中分叉的块:

forge test --fork-url <your_rpc_url> --fork-block-number 1

当您需要与现有合约进行交互时,分叉特别有用。 您可以选择以这种方式进行集成测试,就好像您在实际网络上一样。

缓存

如果同时指定了 --fork-url--fork-block-number,那么该块的数据将被缓存以供将来的测试运行。

数据缓存在 ~/.foundry/cache/rpc/<chain name>/<block number> 中。 要清除缓存,只需删除目录或运行 forge clean(删除所有构建工件和缓存目录)。

也可以通过传递 --no-storage-caching 或通过配置 no_storage_cachingfoundry.toml 完全忽略缓存 rpc_storage_caching

已改进的traces

Forge 支持使用 Etherscan 在分叉环境中识别合约。

要使用此功能,请通过 --etherscan-api-key 标志传递 Etherscan API 密钥:

forge test --fork-url <your_rpc_url> --etherscan-api-key <your_etherscan_api_key>

或者,您可以设置 ETHERSCAN_API_KEY 环境变量。

分叉cheatcodes

分叉cheatcodes允许您在 Solidity 测试代码中以编程方式进入分叉模式。 这些作弊码允许您在逐个测试的基础上使用分叉模式,并在测试中使用多个分叉,而不是通过 forge CLI 参数配置分叉模式。 每个叉子都通过其自己唯一的 uint256 标识符进行识别。

用法

重要的是要记住,所有_测试函数都是隔离的,这意味着每个测试函数都使用状态_after setUp 的_副本_执行,并在其自己的独立 EVM 中执行。

因此,在 setUp 期间创建的分支可用于测试。 下面的代码示例使用 createFork 创建两个分叉,但_不_一开始就选择一个。 每个 fork 都有一个唯一标识符 (uint256 forkId),该标识符在首次创建时分配。

通过将该 forkId 传递给 selectFork 来启用特定的分叉。

createSelectForkcreateFork 加上 selectFork 的单行代码。

一次只能有一个活动分叉,当前活动分叉的标识符可以通过 activeFork 检索。

类似于 roll,您可以使用 rollFork 设置分叉的 block.number

要了解选择分叉时会发生什么,了解分叉模式的一般工作方式很重要:

每个分叉都是一个独立的 EVM,即所有分叉都使用完全独立的存储。 唯一的例外是 msg.sender 的状态和测试合约本身,它们在分叉交换中是持久的。 换句话说,在 fork A 处于活动状态(selectFork(A))时所做的所有更改仅记录在 fork A 的存储中,如果选择了另一个 fork,则不可用。 但是,测试合约本身(变量)中记录的更改仍然可用,因为测试合约是一个 _persistent_ 帐户。

selectFork cheatcodes将 remote 部分设置为分叉的数据源,但是 local 内存在分叉交换期间保持不变。 这也意味着可以使用任何分叉随时调用 selectFork,以设置_remote_ 数据源。 但是,重要的是要记住上述“读/写”访问规则始终适用,这意味着_writes_在分叉交换中是持久的。

例子

创建和选择分叉
contract ForkTest is Test {
    // the identifiers of the forks
    uint256 mainnetFork;
    uint256 optimismFork;
    
    //Access variables from .env file via vm.envString("varname")
    //Replace ALCHEMY_KEY by your alchemy key or Etherscan key, change RPC url if need
    //inside your .env file e.g: 
    //MAINNET_RPC_URL = 'https://eth-mainnet.g.alchemy.com//v2/ALCHEMY_KEY'
    //string MAINNET_RPC_URL = vm.envString("MAINNET_RPC_URL");
    //string OPTIMISM_RPC_URL = vm.envString("OPTIMISM_RPC_URL");

    // create two _different_ forks during setup
    function setUp() public {
        mainnetFork = vm.createFork(MAINNET_RPC_URL);
        optimismFork = vm.createFork(OPTIMISM_RPC_URL);
    }

    // demonstrate fork ids are unique
    function testForkIdDiffer() public {
        assert(mainnetFork != optimismFork);
    }

    // select a specific fork
    function testCanSelectFork() public {
        // select the fork
        vm.selectFork(mainnetFork);
        assertEq(vm.activeFork(), mainnetFork);

        // from here on data is fetched from the `mainnetFork` if the EVM requests it and written to the storage of `mainnetFork`
    }

    // manage multiple forks in the same test
    function testCanSwitchForks() public {
        vm.selectFork(mainnetFork);
        assertEq(vm.activeFork(), mainnetFork);

        vm.selectFork(optimismFork);
        assertEq(vm.activeFork(), optimismFork);
    }

    // forks can be created at all times
    function testCanCreateAndSelectForkInOneStep() public {
        // creates a new fork and also selects it
        uint256 anotherFork = vm.createSelectFork(MAINNET_RPC_URL);
        assertEq(vm.activeFork(), anotherFork);
    }

    // set `block.number` of a fork
    function testCanSetForkBlockNumber() public {
        vm.selectFork(mainnetFork);
        vm.rollFork(1_337_000);

        assertEq(block.number, 1_337_000);
    }
}

分离和持久存储(storage)

如前所述,每个分叉本质上都是一个独立的 EVM,具有独立的存储(storage)空间。

选择分叉时,只有 msg.sender 和测试合约(ForkTest)的账户是持久的。 但是任何帐户都可以变成持久帐户:makePersistent

persistent 帐户是唯一的, i.e.即它存在于所有分叉上

contract ForkTest is Test {
    // the identifiers of the forks
    uint256 mainnetFork;
    uint256 optimismFork;
    
    //Access variables from .env file via vm.envString("varname")
    //Replace ALCHEMY_KEY by your alchemy key or Etherscan key, change RPC url if need
    //inside your .env file e.g: 
    //MAINNET_RPC_URL = 'https://eth-mainnet.g.alchemy.com//v2/ALCHEMY_KEY'
    //string MAINNET_RPC_URL = vm.envString("MAINNET_RPC_URL");
    //string OPTIMISM_RPC_URL = vm.envString("OPTIMISM_RPC_URL");

    // create two _different_ forks during setup
    function setUp() public {
        mainnetFork = vm.createFork(MAINNET_RPC_URL);
        optimismFork = vm.createFork(OPTIMISM_RPC_URL);
    }

    // creates a new contract while a fork is active
    function testCreateContract() public {
        vm.selectFork(mainnetFork);
        assertEq(vm.activeFork(), mainnetFork);
        
        // the new contract is written to `mainnetFork`'s storage
        SimpleStorageContract simple = new SimpleStorageContract();
        
        // and can be used as normal
        simple.set(100);
        assertEq(simple.value(), 100);
        
        // after switching to another contract we still know `address(simple)` but the contract only lives in `mainnetFork` 
        vm.selectFork(optimismFork);
        
        /* this call will therefore revert because `simple` now points to a contract that does not exist on the active fork
        * it will produce following revert message:
        * 
        * "Contract 0xCe71065D4017F316EC606Fe4422e11eB2c47c246 does not exist on active fork with id `1`
        *       But exists on non active forks: `[0]`"
        */
        simple.value();
    }
    
     // creates a new _persistent_ contract while a fork is active
     function testCreatePersistentContract() public {
        vm.selectFork(mainnetFork);
        SimpleStorageContract simple = new SimpleStorageContract();
        simple.set(100);
        assertEq(simple.value(), 100);
        
        // mark the contract as persistent so it is also available when other forks are active
        vm.makePersistent(address(simple));
        assert(vm.isPersistent(address(simple))); 
        
        vm.selectFork(optimismFork);
        assert(vm.isPersistent(address(simple))); 
        
        // This will succeed because the contract is now also available on the `optimismFork`
        assertEq(simple.value(), 100);
     }
}

contract SimpleStorageContract {
    uint256 public value;

    function set(uint256 _value) public {
        value = _value;
    }
}

有关更多详细信息和示例,请参阅 forking cheatcodes 参考。

高级测试

Forge 带有许多高级测试方法:

未来,Forge 还将支持这些:

每一章都深入探讨了测试方法解决了什么问题,以及如何将它们应用到您自己的项目中。

模糊测试(Fuzz Testing)

Forge 支持基于属性的测试。

基于属性的测试是一种测试一般行为而不是孤立场景的方法。

让我们通过编写单元测试来检查这意味着什么,找到我们正在测试的一般属性,并将其转换为基于属性的测试:

pragma solidity 0.8.10;

import "forge-std/Test.sol";

contract Safe {
    receive() external payable {}

    function withdraw() external {
        payable(msg.sender).transfer(address(this).balance);
    }
}

contract SafeTest is Test {
    Safe safe;

    // Needed so the test contract itself can receive ether
    // when withdrawing
    receive() external payable {}

    function setUp() public {
        safe = new Safe();
    }

    function testWithdraw() public {
        payable(address(safe)).transfer(1 ether);
        uint256 preBalance = address(this).balance;
        safe.withdraw();
        uint256 postBalance = address(this).balance;
        assertEq(preBalance + 1 ether, postBalance);
    }
}

运行测试,我们看到它通过了:

$ forge test
Compiling 6 files with 0.8.10
Solc 0.8.10 finished in 3.78s
Compiler run successful

Running 1 test for test/Safe.t.sol:SafeTest
[PASS] testWithdraw() (gas: 19462)
Test result: ok. 1 passed; 0 failed; finished in 873.70µs

这个单元测试_确实测试_我们可以从我们的保险箱中取出以太币。 但是,谁能说它适用于所有金额,而不仅仅是 1 个以太币?

这里的一般性质是:给定一个安全的余额,当我们提取时,我们应该得到保险箱里的东西。

Forge 将运行任何至少采用一个参数的测试作为基于属性的测试,所以让我们重写:

contract SafeTest is Test {
    // ...

    function testWithdraw(uint256 amount) public {
        payable(address(safe)).transfer(amount);
        uint256 preBalance = address(this).balance;
        safe.withdraw();
        uint256 postBalance = address(this).balance;
        assertEq(preBalance + amount, postBalance);
    }
}

如果我们现在运行测试,我们可以看到 Forge 运行基于属性的测试,但它因 amount 的高值而失败:

$ forge test
Compiling 1 files with 0.8.10
Solc 0.8.10 finished in 1.69s
Compiler run successful

Running 1 test for test/Safe.t.sol:SafeTest
[FAIL. Reason: EvmError: Revert Counterexample: calldata=0x215a2f200000000000000000000000000000000000000001000000000000000000000000, args=[79228162514264337593543950336]] testWithdraw(uint256) (runs: 47, μ: 19554, ~: 19554)
Test result: FAILED. 0 passed; 1 failed; finished in 8.75ms

给测试合约的默认以太币数量是 2**96 wei(在 DappTools 中),所以我们必须将数量类型限制为 uint96 以确保我们不会尝试发送超过 我们有:

    function testWithdraw(uint96 amount) public {

现在它通过了:

$ forge test
Compiling 1 files with 0.8.10
Solc 0.8.10 finished in 1.67s
Compiler run successful

Running 1 test for test/Safe.t.sol:SafeTest
[PASS] testWithdraw(uint96) (runs: 256, μ: 19078, ~: 19654)
Test result: ok. 1 passed; 0 failed; finished in 19.56ms

您可能希望使用 assume cheatcodes排除某些情况。 在这些情况下,模糊器fuzzer将丢弃输入并开始新的模糊测试运行:

function testWithdraw(uint96 amount) public {
    vm.assume(amount > 0.1 ether);
    // snip
}

有多种方法可以运行基于属性的测试,特别是参数测试和模糊测试。 Forge 仅支持模糊测试。

Interpreting results

您可能已经注意到,与单元测试相比,模糊测试的总结略有不同:

  • "runs" 是指模糊器fuzzer测试的场景数量。 默认情况下,模糊器fuzzer将生成 256 个场景,但是,这可以使用 FOUNDRY_FUZZ_RUNS 环境变量进行配置。
  • “μ”(希腊字母 mu)是所有模糊运行中使用的平均gas
  • “~”(波浪号)是所有模糊运行中使用的中值gas

差异化测试

Forge 可用于差分测试和差分模糊测试。 您甚至可以使用 ffi cheatcode 针对非 EVM 可执行文件进行测试。

背景

Differential testing 通过比较每个实现的输出来交叉引用同一功能的多个实现。 假设我们有一个函数规范 F(X),以及该规范的两个实现:f1(X)f2(X)。 对于存在于适当输入空间中的所有 x,我们期望 f1(x) == f2(x)。 如果 f1(x) != f2(x),我们知道至少有一个函数错误地实现了 F(X)。 这种测试平等和识别差异的过程是差异测试的核心。

差分模糊测试是差分测试的扩展。 差分模糊以编程方式生成许多x值,以发现手动选择的输入可能无法揭示的差异和边缘情况。

注意:本例中的 == 运算符可以是相等性的自定义定义。 例如,如果测试浮点实现,您可以使用具有一定公差的近似相等。

这种类型的测试在现实生活中的一些用途包括:

  • 将升级后的实施与其前身进行比较
  • 针对已知参考实现测试代码
  • 确认与第三方工具和依赖项的兼容性

以下是 Forge 如何用于差异测试的一些示例。

入门:ffi cheatcode

ffi 允许您执行任意 shell 命令并捕获输出。 这是一个模拟示例:

import "forge-std/Test.sol";

contract TestContract is Test {

    function testMyFFI () public {
        string[] memory cmds = new string[](2);
        cmds[0] = "cat";
        cmds[1] = "address.txt"; // assume contains abi-encoded address.
        bytes memory result = vm.ffi(cmds);
        address loadedAddress = abi.decode(result, (address));
        // Do something with the address
        // ...
    }
}

一个地址之前已经写入了address.txt,我们使用 FFI cheatcode读取了它。 现在可以在整个测试合约中使用此数据。

示例:差异测试默克尔树实现

默克尔树 是区块链应用程序中经常使用的密码承诺方案。 它们的流行导致了默克尔树生成器、证明器和验证器的许多不同实现。 Merkle 根和证明通常使用 JavaScript 或 Python 等语言生成,而证明验证通常发生在 Solidity 的链上。

Murky 是在 Solidity 中 Merkle 根、证明和验证的完整实现。 它的测试套件包括针对 OpenZeppelin 的 Merkle 证明库的差异测试,以及针对参考 JavaScript 实现的根生成测试。 这些测试由 Foundry 的模糊测试和ffi功能提供支持。

针对参考 TypeScript 实现的差异模糊测试

使用 ffi 作弊代码,Murky 使用 Forge 的模糊器提供的数据针对 TypeScript 实现测试自己的 Merkle 根实现:

function testMerkleRootMatchesJSImplementationFuzzed(bytes32[] memory leaves) public {
    vm.assume(leaves.length > 1);
    bytes memory packed = abi.encodePacked(leaves);
    string[] memory runJsInputs = new string[](8);

    // Build ffi command string
    runJsInputs[0] = 'npm';
    runJsInputs[1] = '--prefix';
    runJsInputs[2] = 'differential_testing/scripts/';
    runJsInputs[3] = '--silent';
    runJsInputs[4] = 'run';
    runJsInputs[5] = 'generate-root-cli';
    runJsInputs[6] = leaves.length.toString();
    runJsInputs[7] = packed.toHexString();

    // Run command and capture output
    bytes memory jsResult = vm.ffi(runJsInputs);
    bytes32 jsGeneratedRoot = abi.decode(jsResult, (bytes32));

    // Calculate root using Murky
    bytes32 murkyGeneratedRoot = m.getRoot(leaves);
    assertEq(murkyGeneratedRoot, jsGeneratedRoot);
}

注意:请参阅 Murky Repo 中的 Strings2.sol 以了解启用 (bytes memory).toHexString()

Forge 运行 npm --prefix differential_testing/scripts/ --silent run generate-root-cli {numLeaves} {hexEncodedLeaves}。 这使用参考 JavaScript 实现计算输入数据的 Merkle 根。 该脚本将根打印到标准输出,打印输出在 vm.ffi() 的返回值中被捕获为 bytes

然后测试使用 Solidity 实现计算根。

最后,测试断言两个根完全相等。 如果它们不相等,则测试失败。

针对 OpenZeppelin 的 Merkle 证明库的差异模糊测试

您可能希望对另一个 Solidity 实现使用差异测试。 在这种情况下,不需要 ffi。 相反,参考实现直接导入到测试中。

import "openzeppelin-contracts/contracts/utils/cryptography/MerkleProof.sol";
//...
function testCompatabilityOpenZeppelinProver(bytes32[] memory _data, uint256 node) public {
    vm.assume(_data.length > 1);
    vm.assume(node < _data.length);
    bytes32 root = m.getRoot(_data);
    bytes32[] memory proof = m.getProof(_data, node);
    bytes32 valueToProve = _data[node];
    bool murkyVerified = m.verifyProof(root, proof, valueToProve);
    bool ozVerified = MerkleProof.verify(proof, root, valueToProve);
    assertTrue(murkyVerified == ozVerified);
}

针对已知边缘情况的差异测试

差异测试并不总是模糊的——它们对于测试已知的边缘情况也很有用。 在 Murky 代码库的情况下,log2ceil 函数的初始实现不适用于某些长度接近 2 的幂(如 129)的数组。 作为安全检查,始终针对此长度的数组运行测试,并与 TypeScript 实现进行比较。 您可以在 此处 查看完整测试。

针对参考数据的标准化测试

FFI 还可用于将可重现的标准化数据注入测试环境。 在 Murky 库中,这被用作gas快照的基准(参见 forge snapshot)。

bytes32[100] data;
uint256[8] leaves = [4, 8, 15, 16, 23, 42, 69, 88];

function setUp() public {
    string[] memory inputs = new string[](2);
    inputs[0] = "cat";
    inputs[1] = "src/test/standard_data/StandardInput.txt";
    bytes memory result =  vm.ffi(inputs);
    data = abi.decode(result, (bytes32[100]));
    m = new Merkle();
}

function testMerkleGenerateProofStandard() public view {
    bytes32[] memory _data = _getData();
    for (uint i = 0; i < leaves.length; ++i) {
        m.getProof(_data, leaves[i]);
    }
}

src/test/standard_data/StandardInput.txt 是一个包含编码的 bytes32[100] 数组的文本文件。 它是在测试之外生成的,可以在任何语言的 Web3 SDK 中使用。 它看起来像:

0xf910ccaa307836354233316666386231414464306335333243453944383735313..423532

标准化测试合约使用 ffi 读取文件。 它将数据解码为一个数组,然后在本例中为 8 个不同的叶子生成证明。 由于数据是恒定且标准的,我们可以使用此测试有意义地测量gas和性能改进。

当然,可以将数组硬编码到测试中! 但这使得跨合约、实施等进行一致的测试变得更加困难。

示例:差异测试渐进式荷兰式拍卖

Paradigm 的 Gradual Dutch Auction 机制的参考实现包含许多差异化的模糊测试。 它是一个很好的存储库,可以进一步探索使用 ffi 的差异测试。

参考资料库

如果您有另一个可以作为参考的存储库,请贡献它!

部署

Forge 可以使用 forge create 命令将智能合约部署到指定网络。

Forge 一次只能部署一个合约。

要部署合约,您必须提供 RPC URL(env:ETH_RPC_URL)和将部署合约的帐户的私钥。

MyContract 部署到网络:

$ forge create --rpc-url <your_rpc_url> --private-key <your_private_key> src/MyContract.sol:MyContract
compiling...
success.
Deployer: 0xa735b3c25f...
Deployed to: 0x4054415432...
Transaction hash: 0x6b4e0ff93a...

Solidity 文件可能包含多个合约。 上面的 :MyContract 指定了从 src/MyContract.sol 文件部署哪个合约。

使用 --constructor-args 标志将参数传递给构造函数:

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

import {ERC20} from "solmate/tokens/ERC20.sol";

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

此外,我们可以告诉 Forge 在 Etherscan、Sourcify 或 Blockscout 上验证我们的合约(如果网络受支持),方法是传递 --verify

$ forge create --rpc-url <your_rpc_url> \
    --constructor-args "ForgeUSD" "FUSD" 18 1000000000000000000000 \
    --private-key <your_private_key> src/MyToken.sol:MyToken \
    --etherscan-api-key <your_etherscan_api_key> \
    --verify

验证预先存在的合约

建议将 --verify 标志与 forge create 一起使用,以在部署后自动验证 explorer 上的合约。 请注意,对于 Etherscan,必须设置 ETHERSCAN_API_KEY

如果您正在验证已部署的合约,请继续阅读。

您可以使用 forge verify-contract 命令在 Etherscan、Sourcify 或 Blockscout 上验证合约。

您必须提供:

  • 合约地址
  • 合约名称或合约路径 <path>:<contractname>
  • 您的 Etherscan API 密钥(env:ETHERSCAN_API_KEY)(如果在 Etherscan 上验证)。

此外,您可能需要提供:

  • ABI 编码格式的构造函数参数,如果有的话
  • 编译器版本 用于构建,带有来自提交版本前缀的 8 个十六进制数字(提交通常不是nightly构建)。 如果未指定,则会自动检测到。
  • 优化次数,如果激活了 Solidity 优化器。 如果未指定,则会自动检测到。
  • 链 ID,如果合约不在以太坊主网上

假设您想验证 MyToken(见上文)。 您将 优化次数 设置为 100 万,使用 v0.8.10 对其进行编译,并将其部署到如上所示的 Kovan 测试网(链 ID : 42). 请注意,如果未在验证时设置 --num-of-optimizations 将默认为 0,而如果未在部署时设置则默认为 200,因此请确保在离开时通过 --num-of-optimizations 200 默认编译设置。

验证方法如下:

$ forge verify-contract --chain-id 42 --num-of-optimizations 1000000 --watch --constructor-args \ 
    $(cast abi-encode "constructor(string,string,uint256,uint256)" "ForgeUSD" "FUSD" 18 1000000000000000000000) \
    --compiler-version v0.8.10+commit.fc410830 <the_contract_address> src/MyToken.sol:MyToken <your_etherscan_api_key>

Submitted contract for verification:
                Response: `OK`
                GUID: `a6yrbjp5prvakia6bqp5qdacczyfhkyi5j1r6qbds1js41ak1a`
                url: https://kovan.etherscan.io//address/0x6a54…3a4c#code

建议使用 --watch 标志 使用 verify-contract 命令轮询验证结果。

如果未提供 --watch 标志,您可以检查 使用 forge verify-check 命令的验证状态:

$ forge verify-check --chain-id 42 <GUID> <your_etherscan_api_key>
Contract successfully verified.

💡 提示

使用 Cast 的 abi-encode 对参数进行 ABI 编码。

在这个例子中,我们运行了cast abi-encode "constructor(string,string,uint8,uint256)" "ForgeUSD" "FUSD" 18 1000000000000000000000 来对参数进行 ABI 编码。


故障排除

位于 1 处的无效字符 'x'

确保私钥字符串不以“0x”开头。

EIP-1559 未激活

RPC 服务器不支持或未激活 EIP-1559。 传递 --legacy 标志以使用旧交易而不是 EIP-1559 交易。 如果您在本地环境中进行开发,则可以使用 Hardhat 而不是 Ganache。

无法解析tokens

确保传递的参数类型正确。

签名错误

确保私钥正确。

用于验证的编译器版本提交

如果您想检查您在本地运行的确切提交,请尝试:~/.svm/0.x.y/solc-0.x.y --version 其中 xy 分别是主要和次要版本号。 其输出类似于:

solc, the solidity compiler commandline interface
Version: 0.8.12+commit.f00d7308.Darwin.appleclang

注意:您不能只粘贴整个字符串“0.8.12+commit.f00d7308.Darwin.appleclang”作为编译器版本的参数。 但是您可以使用提交的 8 位十六进制数字来准确查找您应该从 编译器版本 复制和粘贴的内容。

已知的问题

验证具有不明确导入路径的合约

Forge 将源目录(srclibtest 等)作为 --include-path 参数传递给编译器。 这意味着给定以下项目树结构

|- src
|-- folder
|--- Contract.sol
|--- IContract.sol

可以使用 folder/IContract.sol 导入路径在 Contract.sol 中导入IContract

Etherscan 无法重新编译此类源代码。 考虑更改导入以使用相对导入路径。

验证没有字节码哈希的合约

目前,无法使用 bytecode_hash 在 Etherscan 上验证合约 设置为 none。 单击here 了解更多信息 元数据哈希如何用于源代码验证。

Gas 追踪

Forge 可以帮助您估算您的合约将消耗多少 gas。

目前,Forge 为这项工作提供了两种不同的工具,但它们可能会在未来合并:

  • Gas reports:Gas 报告让您大致了解 Forge 认为 合同中的个别功能会消耗 gas。
  • Gas snapshots:Gas 快照让您大致了解有多少 每次测试都会消耗Gas。

Gas 报告和 Gas 快照在某些方面有所不同:

  • Gas 报告使用跟踪来计算单个合约调用的 Gas 成本。 这以速度为代价提供了更精细的洞察力。
  • Gas快照具有更多内置工具,例如差异和将结果导出到文件。 快照不像Gas报告那样精细,但生成速度更快。

Gas 报告

Forge 可以为您的合约生成 gas 报告。 您可以通过 foundry.toml 中的 gas_reports 字段配置哪些合约输出 gas 报告。

为特定合约生成报告:

gas_reports = ["MyContract", "MyContractFactory"]

为所有合约生成报告:

gas_reports = ["*"]

要生成Gas报告,请运行“forge test --gas-report”。

您还可以将它与其他子命令结合使用,例如 forge test --match-test testBurn --gas-report,以仅生成与此测试相关的Gas报告。

示例输出:

╭───────────────────────┬─────────────────┬────────┬────────┬────────┬─────────╮
│ MockERC1155 contract  ┆                 ┆        ┆        ┆        ┆         │
╞═══════════════════════╪═════════════════╪════════╪════════╪════════╪═════════╡
│ Deployment Cost       ┆ Deployment Size ┆        ┆        ┆        ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ 1082720               ┆ 5440            ┆        ┆        ┆        ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Function Name         ┆ min             ┆ avg    ┆ median ┆ max    ┆ # calls │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ balanceOf             ┆ 596             ┆ 596    ┆ 596    ┆ 596    ┆ 44      │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ balanceOfBatch        ┆ 2363            ┆ 4005   ┆ 4005   ┆ 5647   ┆ 2       │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ batchBurn             ┆ 2126            ┆ 5560   ┆ 2584   ┆ 11970  ┆ 3       │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ batchMint             ┆ 2444            ┆ 135299 ┆ 125081 ┆ 438531 ┆ 18      │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ burn                  ┆ 814             ┆ 2117   ┆ 2117   ┆ 3421   ┆ 2       │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ isApprovedForAll      ┆ 749             ┆ 749    ┆ 749    ┆ 749    ┆ 1       │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ mint                  ┆ 26039           ┆ 31943  ┆ 27685  ┆ 118859 ┆ 22      │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ safeBatchTransferFrom ┆ 2561            ┆ 137750 ┆ 126910 ┆ 461304 ┆ 8       │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ safeTransferFrom      ┆ 1335            ┆ 34505  ┆ 28103  ┆ 139557 ┆ 9       │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ setApprovalForAll     ┆ 24485           ┆ 24485  ┆ 24485  ┆ 24485  ┆ 12      │
╰───────────────────────┴─────────────────┴────────┴────────┴────────┴─────────╯

╭───────────────────────┬─────────────────┬────────┬────────┬────────┬─────────╮
│ Example contract      ┆                 ┆        ┆        ┆        ┆         │
╞═══════════════════════╪═════════════════╪════════╪════════╪════════╪═════════╡
│ Deployment Cost       ┆ Deployment Size ┆        ┆        ┆        ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ 1082720               ┆ 5440            ┆        ┆        ┆        ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Function Name         ┆ min             ┆ avg    ┆ median ┆ max    ┆ # calls │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ foo                   ┆ 596             ┆ 596    ┆ 596    ┆ 596    ┆ 44      │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ bar                   ┆ 2363            ┆ 4005   ┆ 4005   ┆ 5647   ┆ 2       │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ baz                   ┆ 2126            ┆ 5560   ┆ 2584   ┆ 11970  ┆ 3       │
╰───────────────────────┴─────────────────┴────────┴────────┴────────┴─────────╯

您还可以通过 foundry.toml 中的 gas_reports_ignore 字段忽略合约:

gas_reports_ignore = ["Example"]

这会将输出更改为:

╭───────────────────────┬─────────────────┬────────┬────────┬────────┬─────────╮
│ MockERC1155 contract  ┆                 ┆        ┆        ┆        ┆         │
╞═══════════════════════╪═════════════════╪════════╪════════╪════════╪═════════╡
│ Deployment Cost       ┆ Deployment Size ┆        ┆        ┆        ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ 1082720               ┆ 5440            ┆        ┆        ┆        ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Function Name         ┆ min             ┆ avg    ┆ median ┆ max    ┆ # calls │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ balanceOf             ┆ 596             ┆ 596    ┆ 596    ┆ 596    ┆ 44      │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ balanceOfBatch        ┆ 2363            ┆ 4005   ┆ 4005   ┆ 5647   ┆ 2       │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ batchBurn             ┆ 2126            ┆ 5560   ┆ 2584   ┆ 11970  ┆ 3       │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ batchMint             ┆ 2444            ┆ 135299 ┆ 125081 ┆ 438531 ┆ 18      │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ burn                  ┆ 814             ┆ 2117   ┆ 2117   ┆ 3421   ┆ 2       │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ isApprovedForAll      ┆ 749             ┆ 749    ┆ 749    ┆ 749    ┆ 1       │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ mint                  ┆ 26039           ┆ 31943  ┆ 27685  ┆ 118859 ┆ 22      │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ safeBatchTransferFrom ┆ 2561            ┆ 137750 ┆ 126910 ┆ 461304 ┆ 8       │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ safeTransferFrom      ┆ 1335            ┆ 34505  ┆ 28103  ┆ 139557 ┆ 9       │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ setApprovalForAll     ┆ 24485           ┆ 24485  ┆ 24485  ┆ 24485  ┆ 12      │
╰───────────────────────┴─────────────────┴────────┴────────┴────────┴─────────╯

有关忽略列表如何工作的更多详细信息,请参阅:https://github.com/foundry-rs/foundry/pull/2528#issue-1323359692

Gas快照

Forge 可以为您的所有测试功能生成Gas快照。 这个可以 有助于大致了解您的合约将消耗多少Gas, 或者比较各种优化前后的Gas使用情况。

要生成 gas 快照,请运行 forge snapshot

默认情况下,这将生成一个名为“.gas-snapshot”的文件,其中包含你所有的 测试及其各自的Gas使用情况。

$ forge snapshot
$ cat .gas-snapshot

ERC20Test:testApprove() (gas: 31162)
ERC20Test:testBurn() (gas: 59875)
ERC20Test:testFailTransferFromInsufficientAllowance() (gas: 81034)
ERC20Test:testFailTransferFromInsufficientBalance() (gas: 81662)
ERC20Test:testFailTransferInsufficientBalance() (gas: 52882)
ERC20Test:testInfiniteApproveTransferFrom() (gas: 90167)
ERC20Test:testMetadata() (gas: 14606)
ERC20Test:testMint() (gas: 53830)
ERC20Test:testTransfer() (gas: 60473)
ERC20Test:testTransferFrom() (gas: 84152)

过滤

如果您想指定不同的输出文件,请运行forge snapshot --snap <FILE_NAME>

您还可以按Gas使用量对结果进行排序。 使用 --asc 选项对结果进行排序 升序和 --desc 以降序排列结果。

最后,您还可以为所有测试指定最小/最大Gas阈值。 要仅包含高于阈值的结果,您可以使用 --min 选项。 以同样的方式,只包括阈值以下的结果, 您可以使用 --max <VALUE> 选项。

请记住,更改将在快照文件中进行,而不是在快照中进行 显示在您的屏幕上。

您还可以将其与 forge test 的过滤器结合使用,例如 forge snapshot --match-path contracts/test/ERC721.t.sol 以生成与此测试合约相关的 gas 快照。

比较gas用量

如果您想将当前快照文件与您的 最新更改,您可以使用 --diff--check 选项。

--diff 将与快照进行比较并显示快照的更改。

它还可以选择使用文件名(--diff <FILE_NAME>),默认 是.gas-snapshot

例如:

$ forge snapshot --diff .gas-snapshot2

Running 10 tests for src/test/ERC20.t.sol:ERC20Test
[PASS] testApprove() (gas: 31162)
[PASS] testBurn() (gas: 59875)
[PASS] testFailTransferFromInsufficientAllowance() (gas: 81034)
[PASS] testFailTransferFromInsufficientBalance() (gas: 81662)
[PASS] testFailTransferInsufficientBalance() (gas: 52882)
[PASS] testInfiniteApproveTransferFrom() (gas: 90167)
[PASS] testMetadata() (gas: 14606)
[PASS] testMint() (gas: 53830)
[PASS] testTransfer() (gas: 60473)
[PASS] testTransferFrom() (gas: 84152)
Test result: ok. 10 passed; 0 failed; finished in 2.86ms
testBurn() (gas: 0 (0.000%))
testFailTransferFromInsufficientAllowance() (gas: 0 (0.000%))
testFailTransferFromInsufficientBalance() (gas: 0 (0.000%))
testFailTransferInsufficientBalance() (gas: 0 (0.000%))
testInfiniteApproveTransferFrom() (gas: 0 (0.000%))
testMetadata() (gas: 0 (0.000%))
testMint() (gas: 0 (0.000%))
testTransfer() (gas: 0 (0.000%))
testTransferFrom() (gas: 0 (0.000%))
testApprove() (gas: -8 (-0.000%))
Overall gas change: -8 (-0.000%)

--check 将快照与现有快照文件进行比较并显示所有 差异,如果有的话。 您可以通过提供不同的文件名来更改要比较的文件:--check <FILE_NAME>

$ forge snapshot --check .gas-snapshot2

Running 10 tests for src/test/ERC20.t.sol:ERC20Test
[PASS] testApprove() (gas: 31162)
[PASS] testBurn() (gas: 59875)
[PASS] testFailTransferFromInsufficientAllowance() (gas: 81034)
[PASS] testFailTransferFromInsufficientBalance() (gas: 81662)
[PASS] testFailTransferInsufficientBalance() (gas: 52882)
[PASS] testInfiniteApproveTransferFrom() (gas: 90167)
[PASS] testMetadata() (gas: 14606)
[PASS] testMint() (gas: 53830)
[PASS] testTransfer() (gas: 60473)
[PASS] testTransferFrom() (gas: 84152)
Test result: ok. 10 passed; 0 failed; finished in 2.47ms
Diff in "ERC20Test::testApprove()": consumed "(gas: 31162)" gas, expected "(gas: 31170)" gas 

调试器Debugger

Forge 附带一个交互式调试器Debugger。

调试器Debugger可在 forge debugforge test 上访问。

使用 forge test:

$ forge test --debug $FUNC

其中 $FUNC 是您要调试的函数的签名。 例如:

$ forge test --debug "testSomething()"

如果您有多个具有相同函数名称的合约,则需要使用 --match-path--match-contract 将匹配函数限制为只有一种情况。

如果匹配测试是模糊测试,调试器Debugger将打开第一个失败的模糊场景,或者最后一个成功的场景,以先到者为准。

使用 forge test

$ forge debug --debug $FILE --sig $FUNC

其中 $FILE 是您要调试的合约的路径,$FUNC 是您要调试的函数的签名。 例如:

$ forge debug --debug src/SomeContract.sol --sig "myFunc(uint256,string)" 123 "你好"

您还可以使用--sig而不是函数签名来指定原始调用数据。

如果您的源文件包含多个合约,请使用--target-contract标志指定要调试的合约。

调试器布局

An image of the debugger UI

导航

当调试器Debugger运行时,您会看到一个分为四个象限的终端:

  • Quadrant 1:调试会话中的操作码,当前操作码突出显示。 此外,还会显示当前账户地址、程序计数器和累计gas用量
  • Quadrant 2:当前栈,以及栈的大小
  • Quadrant 3:源视图
  • Quadrant 4:EVM 的当前内存

在逐步执行代码时,您会注意到堆栈和内存中的单词有时会改变颜色。

对于内存:

  • Red words 即将被当前操作码写入
  • Green words 被之前的操作码写入
  • Cyan words 正在被当前操作码读取

对于堆栈,cyan words正在被当前操作码读取或弹出。

导航

General

  • q: Quit the debugger
  • 0-9 + k:向后移动数次(或者用鼠标向上滚动)
  • 0-9 + j:向前走几步(或者用鼠标向下滚动)
  • g: 移动到事务的开头
  • G:移至事务末尾
  • c:移至上一个调用类型指令(即 CALLSTATICCALL、[DELEGATECALL][op- delegatecall] 和 CALLCODE)。
  • C: 移动到下一个调用类型指令
  • a: 移动到上一个 JUMPJUMPI 指令
  • s: 移动到下一个 JUMPDEST 指令
  • Ctrl + j:向下滚动内存视图
  • Ctrl + k:向上滚动内存视图
  • m: 将内存显示为 UTF8
  • J:向下滚动堆栈视图
  • K:向上滚动堆栈视图
  • t: 在堆栈上显示标签以查看当前操作将消耗哪些项

Cast概述

Cast 是 Foundry 用于执行以太坊 RPC 调用的命令行工具。 您可以进行智能合约调用、发送交易或检索任何类型的链数据——所有这些都来自您的命令行!

如何使用 Cast

要使用 Cast,请运行 cast 命令,然后运行子命令:

$ cast <subcommand>

例子

让我们使用 cast 来检索 DAI 代币的总供应量:

$ cast call 0x6b175474e89094c44da98b954eedeac495271d0f "totalSupply()(uint256)" --rpc-url https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf
8603853182003814300330472690

cast 还提供了许多方便的子命令,例如用于解码 calldata:

$ cast 4byte-decode 0x1F1F897F676d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e7
1) "fulfillRandomness(bytes32,uint256)"
0x676d000000000000000000000000000000000000000000000000000000000000
999

您还可以使用 cast 发送任意消息。 下面是在两个 Anvil 帐户之间发送消息的示例。

$ cast send --private-key <Your Private Key> 0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc $(cast --from-utf8 "hello world") --rpc-url http://127.0.0.1:8545/

📚 参考

有关所有可用子命令的完整概述,请参阅 cast 参考

Anvil概述

Anvil 是 Foundry 附带的本地测试网节点。 您可以使用它从前端测试您的合约或通过 RPC 进行交互。

Anvil 是 Foundry 套件的一部分,与forgecast 一起安装。 如果您还没有安装 Foundry,请参阅 Foundry installation

注意:如果您安装了旧版本的 Foundry,则需要重新安装 foundryup 才能下载 Anvil。

如何使用Anvil

要使用 Anvil,只需输入 anvil。 您应该会看到可用的帐户和私钥列表,以及节点正在侦听的地址和端口。

Anvil 是高度可配置的。 您可以运行 anvil -h 查看所有配置选项。

一些基本选项是:

#  Number of dev accounts to generate and configure. [default: 10]
anvil -a, --accounts <ACCOUNTS>

# The EVM hardfork to use. [default: latest]
anvil --hardfork <HARDFORK>

# Port number to listen on. [default: 8545]
anvil  -p, --port <PORT>

📚 参考

有关 Anvil 及其功能的深入信息,请参阅 anvil 参考

使用 foundry.toml 配置

Forge 可以使用名为“foundry.toml”的配置文件进行配置,该文件位于项目的根目录中。

配置可以由配置文件命名空间。 默认配置文件名为default,所有其他配置文件都继承自该配置文件。 您可以自由自定义default 配置文件,并根据需要添加任意数量的新配置文件。

此外,您可以在您的主目录中创建一个全局的 foundry.toml

让我们看一下包含两个配置文件的配置文件:始终启用优化器的默认配置文件,以及始终显示跟踪的 CI 配置文件:

[profile.default]
optimizer = true
optimizer_runs = 20_000

[profile.ci]
verbosity = 4

运行 forge 时,您可以使用 FOUNDRY_PROFILE 环境变量指定要使用的配置文件。

独立部分

除了配置文件部分,配置文件还可以包含独立部分([fmt][fuzz][invariant] 等)。 默认情况下,每个独立部分都属于“默认”配置文件。 即 [fmt] 等同于 [profile.default.fmt]

要为 default 以外的不同配置文件配置独立部分,请使用语法[profile.<profile name>.<standalone>]。 即[profile.ci.fuzz]


📚 参考

请参阅 foundry.toml 参考 以获得关于您可以配置的内容的完整概述。

持续集成

GitHub 操作

要使用 GitHub Actions 测试您的项目,这里有一个示例工作流程:

on: [push]

name: test

jobs:
  check:
    name: Foundry project
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          submodules: recursive

      - name: Install Foundry
        uses: foundry-rs/foundry-toolchain@v1
        with:
          version: nightly

      - name: Run tests
        run: forge test -vvv

Travis CI

要使用 Travis CI 测试您的项目,这里有一个示例工作流程:

language: rust
cache:
  cargo: true
  directories:
    - $HOME/.foundry

install:
  - curl -L https://foundry.paradigm.xyz | bash
  - export PATH=$PATH:$HOME/.foundry/bin
  - foundryup -b master

script:
  - forge test -vvv

GitLab CI

要使用 GitLab CI 测试您的项目,这里有一个示例工作流程: 注意:查看 Policy 来获取远程镜像

variables:
  GIT_SUBMODULE_STRATEGY: recursive

jobs:
  image: ghcr.io/foundry-rs/foundry
  script:
    - forge install
    - forge test -vvv

与 VSCode 集成

您可以通过安装 VSCode Solidity 扩展 获得对 Visual Studio Code 的 Solidity 支持。

为了使扩展与 Foundry 兼容,您需要将重映射(remappings)放在 remappings.txt 中。 如果它们已经在 foundry.toml 中,请复制它们并改用 remappings.txt。 如果您只是使用 Foundry 提供的自动生成的重新映射,请运行forge remappings > remappings.txt

如果您使用非标准项目布局,您可能必须将以下内容添加到您的 .vscode/settings.json 中:

{
   "solidity.packageDefaultDependenciesContractsDirectory": "src",
   “solidity.packageDefaultDependenciesDirectory”:“lib”
}

其中 src 是源代码目录,lib 是您的依赖目录。

还建议指定 Solidity 编译器版本:

“solidity.compileUsingRemoteVersion”:“v0.8.10”

要使 Foundry 与所选版本一致,请将以下内容添加到 foundry.toml 中的 default 配置文件。

solc_version = "0.8.10"

Shell自动补全

您可以为 bash, elvish, fish, powershell, 和zsh生成自动完成 shell 脚本。

zsh

首先,确保以下内容存在于您的 ~/.zshrc 文件中(如果没有,请添加):

autoload -U compinit
compinit -i

然后运行:

forge completions zsh > /usr/local/share/zsh/site-functions/_forge
cast completions zsh > /usr/local/share/zsh/site-functions/_cast
anvil completions zsh > /usr/local/share/zsh/site-functions/_anvil

对于基于 ARM 的系统:

forge completions zsh > /opt/homebrew/completions/zsh/_forge
cast completions zsh > /opt/homebrew/completions/zsh/_cast
anvil completions zsh > /opt/homebrew/completions/zsh/_anvil

fish

mkdir -p $HOME/.config/fish/completions
forge completions fish > $HOME/.config/fish/completions/forge.fish
cast completions fish > $HOME/.config/fish/completions/cast.fish
anvil completions fish > $HOME/.config/fish/completions/anvil.fish
source $HOME/.config/fish/config.fish

bash

mkdir -p $HOME/.local/share/bash-completion/completions
forge completions bash > $HOME/.local/share/bash-completion/completions/forge
cast completions bash > $HOME/.local/share/bash-completion/completions/cast
anvil completions bash > $HOME/.local/share/bash-completion/completions/anvil
exec bash

静态分析器

slither

要使用 slither 测试您的项目,这里有一个示例 slither.config.json

{
  "filter_paths": "lib",
  "solc_remaps": [
    "ds-test/=lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/"
  ]
}

请注意,您需要使用 solc-select 将 Slither 使用的 solc 更新为 Forge 使用的相同版本:

pip3 install slither-analyzer
pip3 install solc-select
solc-select install 0.8.13
solc-select use 0.8.13
slither src/Contract.sol

有关详细信息,请参阅 slither wiki

为了使用自定义配置,例如上面提到的示例 slither.config.json,使用 slither-wiki 中提到的以下命令 /用法#配置文件)。 默认情况下,slither 会查找 slither.config.json,但您可以定义路径和您选择的任何其他 json 文件:

slither --config-file <path>/file.config.json Counter.sol

示例输出(Raw):

Pragma version^0.8.13 (Counter.sol#2) necessitates a version too recent to be trusted. Consider deploying with 0.6.12/0.7.6/0.8.7
solc-0.8.13 is not recommended for deployment
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-versions-of-solidity

setNumber(uint256) should be declared external:
        - Counter.setNumber(uint256) (Counter.sol#7-9)
increment() should be declared external:
        - Counter.increment() (Counter.sol#11-13)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-external
Counter.sol analyzed (1 contracts with 78 detectors), 4 result(s) found

Slither 还有一个用于 CI/CD 的 github action

Mythril

要使用 mythril 测试您的项目,这里有一个示例 mythril.config.json

{
  "remappings": [
    "ds-test/=lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  }
}

请注意,您需要将 rust 切换为 nightly 以安装 mythril

rustup default nightly
pip3 install mythril
myth analyze src/Contract.sol --solc-json mythril.config.json

有关详细信息,请参阅 mythril 文档

您可以使用 --solc-json 标志将自定义 Solc 编译器输出传递给 Mythril。 例如:

$ myth analyze src/Counter.sol --solc-json mythril.config.json
.
.
mythril.laser.plugin.loader [INFO]: Loading laser plugin: coverage
mythril.laser.plugin.loader [INFO]: Loading laser plugin: mutation-pruner
.
.
Achieved 11.56% coverage for code: 608060405234801561001057600080fd5b5060f78061001f6000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c80633fb5c1cb1460415780638381f58a146053578063d09de08a14606d575b600080fd5b6051604c3660046083565b600055565b005b605b60005481565b60405190815260200160405180910390f35b6051600080549080607c83609b565b9190505550565b600060208284031215609457600080fd5b5035919050565b60006001820160ba57634e487b7160e01b600052601160045260246000fd5b506001019056fea2646970667358221220659fce8aadca285da9206b61f95de294d3958c409cc3011ded856f421885867464736f6c63430008100033
mythril.laser.plugin.plugins.coverage.coverage_plugin [INFO]: Achieved 90.13% coverage for code: 6080604052348015600f57600080fd5b5060043610603c5760003560e01c80633fb5c1cb1460415780638381f58a146053578063d09de08a14606d575b600080fd5b6051604c3660046083565b600055565b005b605b60005481565b60405190815260200160405180910390f35b6051600080549080607c83609b565b9190505550565b600060208284031215609457600080fd5b5035919050565b60006001820160ba57634e487b7160e01b600052601160045260246000fd5b506001019056fea2646970667358221220659fce8aadca285da9206b61f95de294d3958c409cc3011ded856f421885867464736f6c63430008100033
mythril.laser.plugin.plugins.instruction_profiler [INFO]: Total: 1.0892839431762695 s
[ADD         ]   0.9974 %,  nr      9,  total   0.0109 s,  avg   0.0012 s,  min   0.0011 s,  max   0.0013 s
.
.
[SWAP1       ]   1.8446 %,  nr     18,  total   0.0201 s,  avg   0.0011 s,  min   0.0010 s,  max   0.0013 s
[SWAP2       ]   0.8858 %,  nr      9,  total   0.0096 s,  avg   0.0011 s,  min   0.0010 s,  max   0.0011 s

mythril.analysis.security [INFO]: Starting analysis
mythril.mythril.mythril_analyzer [INFO]: Solver statistics: 
Query count: 61 
Solver time: 3.6820807456970215
The analysis was completed successfully. No issues were detected.

结果将列在输出的末尾(如果有的话)。 由于默认的 Counter.sol 没有任何逻辑,mythx报告未发现任何问题。

与 Hardhat 集成

可以让您的 Foundry 项目与 Hardhat 一起工作。 这假设您有一个正在运行的 Foundry 项目并且想要添加 Hardhat,并且你很熟悉 Hardhat。

为什么这不能开箱即用?

默认情况下,Hardhat 希望将库安装在 node_modules 中,这是所有 NodeJS 依赖项的默认文件夹。 Foundry 希望它们在 lib 中。 当然 我们可以配置 Foundry 但不容易配置到 node_modules 的目录结构。

因此,推荐的设置是使用 hardhat-preprocessor。 顾名思义,Hardhat-preprocessor 是一个 Hardhat 插件,它允许我们在合约通过 Solidity 编译器运行之前对其进行预处理。

我们使用它来修改 Solidity 文件中的导入指令,以在 Hardhat 尝试编译它们之前根据 Foundry remappings.txt 文件解析库的绝对路径。 这当然只是发生在内存中,所以你实际的 Solidity 文件永远不会改变。 现在,Hardhat 很乐意使用您随 Foundry 安装的库来遵循和编译。

给我看看示例repo!

Enjoy!

如果您想将其改编为您已有的 Foundry 项目或了解其工作原理,请阅读以下内容:

指示

在您的 Foundry 项目工作目录中:

  1. npm init - 照常设置您的项目详细信息。
  2. npm install --save-dev hardhat - 安装 Hardhat。
  3. npx hardhat - 在同一目录中设置您认为合适的 Hardhat 项目。
  4. forge remappings > remappings.txt - 每次在 Foundry 中修改库时都需要重新运行它。

现在您需要对 Hardhat 项目进行以下更改。 以下假定 TypeScript 设置:

  1. npm install --save-dev hardhat-preprocessor - hardhat-preprocessor 的详细信息
  2. 添加 import "hardhat-preprocessor"; 到您的 hardhat.config.ts 文件。
  3. 确保存在以下函数(您可以将其添加到您的 hardhat.config.ts 文件或其他地方并导入它 - 还要确保 import fs from "fs"; 存在于添加的文件中) :
function getRemappings() {
  return fs
    .readFileSync("remappings.txt", "utf8")
    .split("\n")
    .filter(Boolean) // remove empty lines
    .map((line) => line.trim().split("="));
}

感谢 @DrakeEvansV1@colinnielsen 提供此片段

  1. 将以下内容添加到导出的HardhatUserConfig对象中:
...
preprocess: {
  eachLine: (hre) => ({
    transform: (line: string) => {
      if (line.match(/^\s*import /i)) {
        for (const [from, to] of getRemappings()) {
          if (line.includes(from)) {
            line = line.replace(from, to);
            break;
          }
        }
      }
      return line;
    },
  }),
},
paths: {
  sources: "./src",
  cache: "./cache_hardhat",
},
...

现在,Hardhat 应该可以很好地与 Foundry 配合使用。 您可以运行 Foundry 测试或 Hardhat 测试/脚本并访问您的合约。

在现有的 Hardhat 项目中使用 Foundry

假设您已经有一个 Hardhat 项目,其中包含一些依赖项,例如目录 node_modules/ 中的@OpenZeppelin/contracts

您可以通过 4 个步骤在此项目中使用 Foundry 测试。

在我们开始之前,让我们看一下目录:

  • 合约在“合约”中
  • Hardhat 单元测试在 test 中,我们会将 Foundry 测试文件放在 test/foundry
  • Hardhat 将其缓存放在 cache 中,我们将把 Foundry 缓存放在 forge-cache

添加Foundry测试的4个步骤

  1. 将新创建的空 Foundry 项目中的 lib/forge-std 复制到 Hardhat 项目目录中。 注意:您还可以运行 forge init --force 在这个非空目录中初始化一个 Foundry 项目,并删除由 Foundry init 创建的不需要的目录。
  2. foundry.toml 配置复制到这个Hardhat 项目目录,并更改其中的srcouttestcache_path
[profile.default]
src = 'contracts'
out = 'out'
libs = ['node_modules', 'lib']
test = 'test/foundry'
cache_path  = 'forge-cache'

# See more config options https://book.getfoundry.sh/reference/config.html
  1. 创建一个 remappings.txt 使 Foundry 项目与 VS Code Solidity 扩展一起工作:
ds-test/=lib/forge-std/lib/ds-test/src/
forge-std/=lib/forge-std/src/

查看有关 remappings.txt 和 VS Code Solidity 扩展的更多信息:Remapping dependencies, Integrating with VSCode

  1. 创建一个子目录 test/foundry 并在其中编写 Foundry 测试。

让我们将示例测试文件 Contract.t.sol 放在这个目录中并运行 Foundry 测试

forge test

现在,Foundry测试可以在这个现有的 Hardhat 项目中进行。 由于 Hardhat 项目没有被触及,它可以像以前一样工作。

最佳实践

本指南记录了使用 Foundry 进行开发时建议的最佳实践。 一般而言,建议尽可能使用 forge fmt 处理,不处理的内容如下。

一般合约指南

  1. 始终使用命名导入语法,不要导入完整文件。 这将导入的内容限制为仅指定的项目,而不是文件中的所有内容。 导入完整文件可能会导致 solc 重复定义和滑动错误,尤其是当 repos 增长并且具有更多具有重叠名称的依赖项时。

    • Good:import {MyContract} from "src/MyContract.sol" 只导入 MyContract
    • Bad:import "src/MyContract.sol" 导入 MyContract.sol 中的所有内容。 (导入 forge-std/TestScript 在这里可能是个例外,因此您可以获得控制台库等)。
  2. 首先按 forge-std/ 对导入进行排序,然后是依赖项、test/script/,最后是 src/。 在每个中,按路径字母顺序排序(而不是按导入的显式命名项)。 (注意:一旦 foundry-rs/foundry#3396 合并,这可能会被删除)。

  3. 同样,对命名导入进行排序。 (注意:一旦 foundry-rs/foundry#3396 得到解决,这可能会被删除)。

    • Good:从“src/MyContract.sol”导入{bar,foo}
    • Bad:从“src/MyContract.sol”导入 {foo, bar}
  4. 注意导入的绝对路径和相对路径之间的权衡(绝对路径是相对于 repo 根的,例如"src/interfaces/IERC20.sol"),并为您的项目选择最佳方法:

    • 绝对路径可以更轻松地查看文件的来源并减少移动文件时的流失。
    • 相对路径使您的编辑器更有可能提供 linting 和自动完成等功能,因为编辑器/扩展可能无法理解您的重新映射。
  5. 如果从依赖项中复制库(而不是导入它),请在配置文件中使用 ignore = [] 选项以避免格式化该文件。 这使得审阅者和审计员可以更轻松地将其与原始版本进行比较。

  6. 同样,随意使用 // forgefmt: disable-* 注释指令来忽略手动格式化后看起来更好的代码行/部分。 * 支持的值是:

    • 禁用线
    • 禁用下一行
    • 禁用下一项
    • 禁用启动 -禁用结束

来自 samsczunHow Do You Even Write Secure Code Anyways 谈话的其他最佳实践:

  • 使用描述性变量名称。
  • 限制活动变量的数量。
  • 没有多余的逻辑。
  • 尽可能提前退出,减少看到代码时的心理负担。
  • 相关代码应彼此靠近放置。
  • 删除未使用的代码。

测试

一般测试指导

  1. 为了测试MyContract.sol,测试文件应该是MyContract.t.sol。 为了测试 MyScript.s.sol,测试文件应该是 MyScript.t.sol

    • 如果合约很大并且您想将其拆分为多个文件,请将它们按逻辑分组,如 MyContract.owner.t.solMyContract.deposits.t.sol 等。
  2. 永远不要在 setUp 函数中做出断言,如果您需要确保 setUp 函数执行预期的操作,请使用像 test_SetUpState() 这样的专用测试。 有关原因的更多信息,请参见 foundry-rs/foundry#1291

  3. 对于单元测试,组织测试的方式主要有两种:

    1. 将合约视为描述块:
      • contract Add 包含 add 方法的所有单元测试。
      • contract Supply 包含 supply 方法的所有测试。
      • contract Constructor 保存构造函数的所有测试。
      • 这种方法的一个好处是较小的合约应该比较大的合约编译得更快,因此随着测试套件变大,许多小型合约的这种方法应该可以节省时间。
    2. 每个被测合约都有一个测试合约,其中包含您想要的任意数量的实用程序和固定装置:
      • contract VaultTest 测试 contract Vault,但它也继承自 contract BaseTestFixturecontract TestUtilities
      • 测试函数的编写顺序应与被测合约中存在的原始函数相同。
      • 测试同一功能的所有测试功能都应连续存在于测试文件中。
  4. 集成测试应该放在同一个 test 目录中,并有明确的命名约定。 这些可能位于专用文件中,或者它们可能与现有测试文件中的相关单元测试相邻。

  5. 与测试命名保持一致,因为它有助于过滤测试(例如,对于气体报告,您可能希望过滤掉还原测试)。 组合命名约定时,请按字母顺序排列。

    • 用于标准测试的 test_Description
    • testFuzz_Description 用于模糊测试。
    • test_Revert[If|When]_Condition 用于期望恢复的测试。
    • testFork_Description 用于从网络分叉的测试。
    • testForkFuzz_Revert[If|When]_Condition 用于分叉并期望恢复的模糊测试。
  6. 当使用像 assertEq 这样的断言时,考虑利用最后一个字符串参数来更容易地识别失败。 这些可以保持简短,甚至只是数字——它们基本上可以替代显示还原的行号,例如 assertEq(x, y, "1")assertEq(x, y, "sum1")(注意:foundry-rs/foundry#2328 跟踪本地集成)。

  7. 测试事件时,最好将所有 expectEmit 参数设置为 true,即 vm.expectEmit(true, true, true, true)。 好处:

    • 这可确保您测试活动中的所有内容。
    • 如果您添加一个主题(即一个新的索引参数),它现在默认进行测试。
    • 即使你只有 1 个主题,额外的 true 论点也没有坏处。
  8. 记得写不变测试! 对于断言字符串,使用不变量的冗长英文描述:assertEq(x + y, z, "Invariant violated: the sum of x and y must always equal z")。 有关最佳实践的更多信息即将推出。

分叉测试

  1. 不要觉得你需要给分叉测试特殊待遇,并自由使用它们:

    • 闭源 web2 开发中_需要_模拟——您必须模拟 API 响应,因为该 API 的代码不是开源的,所以您不能只在本地运行它。 但对于区块链而言,情况并非如此:您正在与之交互的任何已部署代码都可以在本地执行,甚至可以免费修改。 那么,如果不需要,为什么要引入错误模拟的风险呢?
    • 避免分叉测试而更喜欢模拟的一个常见原因是分叉测试很慢。 但这并不总是正确的。 通过固定到一个块号,forge 缓存 RPC 响应,因此只有第一次运行速度较慢,而后续运行速度明显更快。 请参阅this benchmark,第一次使用远程 RPC 运行需要 7 分钟,但一旦数据被缓存结果只需要半秒。 Alchemy 和 Infura 都提供免费存档数据,因此固定到一个块应该没有问题。 (请注意,您可能需要配置 CI 以缓存运行之间的 RPC 响应)。
  2. 小心使用分叉上的模糊测试,以避免使用非确定性模糊测试破坏 RPC 请求。 如果你的分叉模糊测试的输入是一些在 RPC 调用中使用的参数来获取数据(例如查询地址的代币余额),每次运行模糊测试至少使用 1 个 RPC 请求,所以你会很快 命中率限制或使用限制。 要考虑的解决方案:

    • 用单个 multicall 替换多个 RPC 调用。
    • 指定模糊/不变 seed:这确保每个“伪造测试”调用都使用相同的模糊输入。 RPC 结果缓存在本地,因此您只会在第一次查询节点。
    • 构建您的测试,以便您模糊测试的数据由您的合约在本地计算,而不是 RPC 调用中使用的数据(根据您正在做的事情可能可行也可能不可行)。
    • 最后,您当然可以始终运行本地节点或修改您的 RPC 计划。
  3. 编写 fork 测试时,不要使用 --fork-url 标志。 相反,更喜欢以下方法,因为它提高了灵活性:

    • foundry.toml配置文件中定义 [rpc_endpoints] 并使用forking cheatcodes
    • 使用 forge-std 的“stdChains.ChainName.rpcUrl”访问测试中的 RPC URL 端点。 请参阅此处 的支持链列表和预期的配置文件别名。
    • 始终固定到一个块,以便测试具有确定性并缓存 RPC 响应。
    • 有关此分叉测试方法的更多信息,请参见 此处(这早于 StdChains,因此该方面有点过时了)。

测试工具

内部函数

要测试 internal 功能,请编写一个继承自被测合约 (CuT) 的线束合约。 从 CuT 继承的线束合约将 internal 功能公开为 external 功能。

每个被测试的 internal 函数都应该通过一个名称遵循 exposed_<function_name> 模式的外部函数公开。 例如:

// file: src/MyContract.sol
contract MyContract {
  function myInternalMethod() internal returns (uint) {
    return 42;
  }
}

// file: test/MyContract.t.sol
import {MyContract} from "src/MyContract.sol";

contract MyContractHarness is MyContract {
  // Deploy this contract then cll this method to test `myInternalMethod`.
  function exposed_myInternalMethod() external returns (uint) {
    return myInternalMethod();
  }
}

私有函数

不幸的是,目前没有好的方法来对 private方法进行单元测试,因为它们不能被任何其他合约访问。 选项包括:

  • private 函数转换为 internal
  • 将逻辑复制/粘贴到您的测试合约中,并编写一个在 CI 检查中运行的脚本,以确保两个功能相同。

Workaround函数

Harnesses 还可用于公开原始智能合约中不可用的功能或信息。 最直接的例子是当我们想要测试公共数组的长度时。 这些函数应遵循以下模式:workaround_<function_name>,例如 workaround_queueLength()

另一个用例是跟踪您不会在生产中跟踪的数据,以帮助测试不变量。 例如,您可以存储所有代币持有者的列表,以简化不变的“所有余额的总和必须等于总供应量”的验证。 这些通常被称为“幽灵变量”。 您可以在 Rikard Hjort工作 DeFi 开发的正式方法 演讲中了解更多相关信息。

最佳实践

感谢 @samsczunHow Do You Even Write Secure Code Anyways 讨论技巧 在本节和下一节中。

  • 不要针对覆盖范围进行优化,而是针对经过深思熟虑的测试进行优化。
  • 编写正面和负面的单元测试。
    • 为代码应该处理的事情编写_positive_单元测试。 验证从这些测试中更改的_all_状态。
    • 为代码不应该处理的事情编写_negative_ 单元测试。 跟进(作为相邻测试)阳性测试并进行需要通过的更改很有帮助。
    • 每个代码路径都应该有自己的单元测试。
  • 编写集成测试来测试整个功能。
  • 编写分叉测试以验证现有已部署合约的正确行为。

污点分析

测试时,您应该优先考虑攻击者可以影响的功能,即接受某种用户输入的功能。 这些被称为_sources_。

将输入数据视为_污染_,直到它被代码检查过,此时它被认为是_干净_。

sink 是发生某些重要操作的代码的一部分。 例如,在 MakerDAO 中,它将是vat.sol

您应该_确保_没有_污染_数据到达_接收器_。 这意味着所有发现自己在接收器中的数据都应该在某个时候由您检查过。 因此,您需要定义数据_应该_是什么,然后确保您的检查_确保_数据符合您的预期。

脚本

  1. 为清晰起见,坚持使用 run 作为默认函数名称。

  2. 任何不打算在脚本中直接调用的方法都应该是 internalprivate。 一般来说,唯一的公共方法应该是 run,因为当每个脚本文件只做一件事时,它更容易阅读/理解。

  3. 考虑根据脚本在协议生命周期中的运行顺序为脚本添加前缀。 例如,01_Deploy.s.sol02_TransferOwnership.s.sol。 这使事情更加自我记录。 这可能并不总是适用,具体取决于您的项目。

  4. 测试你的脚本。

    • 通过编写测试来对它们进行单元测试,这些测试断言运行脚本所做的状态更改。
    • 通过运行该脚本编写您的部署脚本和脚手架测试。 然后,针对生产部署脚本产生的状态运行所有测试。 这是获得对部署脚本的信心的好方法。
  5. 仔细审核广播了哪些交易。 未广播的事务仍在测试上下文中执行,因此缺少广播或额外广播很容易成为上一步中的错误来源。

  6. 注意抢跑。 Forge 模拟您的脚本,根据模拟结果生成交易数据,然后广播交易。 确保您的脚本对模拟和广播之间的链状态变化具有鲁棒性。 下面是一个容易受到此影响的示例脚本:

// Pseudo-code, may not compile.
contract VulnerableScript is Script {
   function run() public {
      vm.startBroadcast();

      // Transaction 1: Deploy a new Gnosis Safe with CREATE.
      // Because we're using CREATE instead of CREATE2, the address of the new
      // Safe is a function of the nonce of the gnosisSafeProxyFactory.
      address mySafe = gnosisSafeProxyFactory.createProxy(singleton, data);

      // Transaction 2: Send tokens to the new Safe.
      // We know the address of mySafe is a function of the nonce of the
      // gnosisSafeProxyFactory. If someone else deploys a Gnosis Safe between
      // the simulation and broadcast, the address of mySafe will be different,
      // and this script will send 1000 DAI to the other person's Safe. In this
      // case, we can protect ourselves from this by using CREATE2 instead of
      // CREATE, but every situation may have different solutions.
      dai.transfer(mySafe, 1000e18);

      vm.stopBroadcast();
   }
}
  1. 对于从 JSON 输入文件读取的脚本,将输入文件放在 script/input/<chainID>/<description>.json 中。 然后将 run(string memory input)(如果需要读取多个文件,则采用多个字符串输入)作为脚本的签名,并使用以下方法读取 JSON 文件。
function readInput(string memory input) internal returns (string memory) {
  string memory inputDir = string.concat(vm.projectRoot(), "/script/input/");
  string memory chainDir = string.concat(vm.toString(block.chainid), "/");
  string memory file = string.concat(input, ".json");
  return vm.readFile(string.concat(root, chainInputFolder, input));
}

注释

  1. 对于公共或外部方法和变量,使用NatSpec注释。

    • forge doc 将解析这些以自动生成文档。
    • Etherscan 将在合约 UI 中显示它们。
  2. 对于简单的 NatSpec 注释,考虑只在文档字符串中记录参数,例如 /// @notice 返回 `x` 和 `y` 的总和。,而不是使用 @param 标签。

  3. 对于复杂的 NatSpec 注释,考虑使用像 PlantUML 这样的工具来生成 ASCII 艺术图,以帮助解释代码库的复杂方面。

  4. 当使用 forge doc 生成文档时,您评论中的任何 markdown 都将正确保留,因此在有用时使用 markdown 结构评论。

    • 好:/// @notice 返回 `x` 和 `y` 的总和。
    • 错误:/// @notice 返回 x 和 y 的总和。

资源

编写更安全的代码和更好的测试:

Foundry在行动:

  • Nomad Monorepo:所有 contracts-* 包。 使用许多 Foundry 功能的好例子,包括模糊测试、ffi 和各种作弊代码。
  • Uniswap Periphery:使用继承来隔离测试装置的好例子。
  • awesome-foundry:精选的 Foundry 开发框架列表。

使用 Solmate 创建 NFT

本教程将引导您使用 Foundry 和 Solmate 创建兼容 OpenSea 的 NFT。 可以在此处 找到本教程的完整实现。

本教程仅用于说明目的,并按原样提供。 本教程未经审核或全面测试。 不应在生产环境中使用本教程中的任何代码。

创建项目并安装依赖

按照 入门部分 中列出的步骤开始设置 Foundry 项目。 我们还将为其 ERC721 实现安装 Solmate,以及一些 OpenZeppelin 实用程序库。 通过从项目的根目录运行以下命令来安装依赖项:

forge install transmissions11/solmate Openzeppelin/openzeppelin-contracts

这些依赖项将作为 git 子模块添加到您的项目中。

如果您正确地按照说明进行操作,您的项目结构应该如下所示:

Project structure

实现一个基本的 NFT

然后我们将 src/Contract.sol 中的样板合同重命名为 src/NFT.sol 并替换代码:

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.10;

import "solmate/tokens/ERC721.sol";
import "openzeppelin-contracts/utils/Strings.sol";

contract NFT is ERC721 {
    uint256 public currentTokenId;

    constructor(
        string memory _name,
        string memory _symbol
    ) ERC721(_name, _symbol) {}

    function mintTo(address recipient) public payable returns (uint256) {
        uint256 newItemId = ++currentTokenId;
        _safeMint(recipient, newItemId);
        return newItemId;
    }

    function tokenURI(uint256 id) public view virtual override returns (string memory) {
        return Strings.toString(id);
    }
}

让我们来看看 NFT 的这个非常基本的实现。 我们首先从我们的 git 子模块中导入两个合约。 我们导入了 solmate 的gas优化实施 ERC721 标准,我们的 NFT 合约将继承该标准。 我们的构造函数为我们的 NFT 获取_name_symbol参数,并将它们传递给父 ERC721 实现的构造函数。 最后,我们实现了允许任何人铸造 NFT 的 mintTo 功能。 此函数递增 currentTokenId 并使用我们父合约的 _safeMint 函数。

使用 forge 编译部署

要编译 NFT 合约,请运行forge build。 由于映射错误,您可能会遇到构建失败的情况:

Error:
Compiler run failed
error[6275]: ParserError: Source "lib/openzeppelin-contracts/contracts/contracts/utils/Strings.sol" not found: File not found. Searched the following locations: "/PATH/TO/REPO".
 --> src/NFT.sol:5:1:
  |
5 | import "openzeppelin-contracts/contracts/utils/Strings.sol";
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

这可以通过设置正确的重映射来解决。 在您的项目中创建一个文件 remappings.txt 并添加以下行

openzeppelin-contracts/=lib/openzeppelin-contracts/

(您可以在 依赖文档 中找到有关重新映射的更多信息。

默认情况下,编译器输出将位于 out目录中。 要使用 Forge 部署我们编译好的合约,我们必须为 RPC 端点和我们要用于部署的私钥设置环境变量。

通过运行以下命令设置环境变量:

export RPC_URL=<你的 RPC 端点>
export PRIVATE_KEY=<你的钱包私钥>

设置完成后,您可以通过运行以下命令,同时将相关构造函数参数添加到 NFT 合约来使用 Forge 部署您的 NFT:

forge 创建 NFT --rpc-url=$RPC_URL --private-key=$PRIVATE_KEY --constructor-args <name> <symbol>

如果部署成功,您将看到部署钱包的地址、合约地址以及打印到您的终端的交易哈希。

从你的合约中铸造 NFT

使用 Foundry 用于与智能合约交互、发送交易和获取链数据的命令行工具 Cast 可以轻松调用 NFT 合约上的函数。 让我们看看如何使用它从我们的 NFT 合约中铸造 NFT。

鉴于您已经在部署期间设置了 RPC 和私钥环境变量,请通过以下方式从您的合约中创建一个 NFT 跑步:

cast send --rpc-url=$RPC_URL <contractAddress> "mintTo(address)" <arg> --private-key=$PRIVATE_KEY

做得好! 你刚刚从你的合约中铸造了你的第一个 NFT。 您可以通过运行以下 cast call 命令来检查 currentTokenId 等于 1 的 NFT 的所有者。 您在上面提供的地址应该作为所有者返回。

cast call --rpc-url=$RPC_URL --private-key=$PRIVATE_KEY <contractAddress> "ownerOf(uint256)" 1

扩展我们的 NFT 合约功能和测试

让我们通过添加元数据来表示 NFT 的内容来扩展我们的 NFT,并设置铸币价格、最大供应量以及从铸币中提取所收集收益的可能性。 您可以使用以下代码片段替换当前的 NFT 合约:

// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.10;

import "solmate/tokens/ERC721.sol";
import "openzeppelin-contracts/contracts/utils/Strings.sol";
import "openzeppelin-contracts/contracts/access/Ownable.sol";

error MintPriceNotPaid();
error MaxSupply();
error NonExistentTokenURI();
error WithdrawTransfer();

contract NFT is ERC721, Ownable {

    using Strings for uint256;
    string public baseURI;
    uint256 public currentTokenId;
    uint256 public constant TOTAL_SUPPLY = 10_000;
    uint256 public constant MINT_PRICE = 0.08 ether;

    constructor(
        string memory _name,
        string memory _symbol,
        string memory _baseURI
    ) ERC721(_name, _symbol) {
        baseURI = _baseURI;
    }

    function mintTo(address recipient) public payable returns (uint256) {
        if (msg.value != MINT_PRICE) {
            revert MintPriceNotPaid();
        }
        uint256 newTokenId = ++currentTokenId;
        if (newTokenId > TOTAL_SUPPLY) {
            revert MaxSupply();
        }
        _safeMint(recipient, newTokenId);
        return newTokenId;
    }

    function tokenURI(uint256 tokenId)
        public
        view
        virtual
        override
        returns (string memory)
    {
        if (ownerOf(tokenId) == address(0)) {
            revert NonExistentTokenURI();
        }
        return
            bytes(baseURI).length > 0
                ? string(abi.encodePacked(baseURI, tokenId.toString()))
                : "";
    }

    function withdrawPayments(address payable payee) external onlyOwner {
        uint256 balance = address(this).balance;
        (bool transferTx, ) = payee.call{value: balance}("");
        if (!transferTx) {
            revert WithdrawTransfer();
        }
    }
}

除此之外,我们还添加了元数据,可以通过调用 NFT 合约上的“tokenURI”方法从任何前端应用程序(如 OpenSea)查询这些元数据。

注意:如果您想在部署时向构造函数提供真实 URL,并托管此 NFT 合约的元数据,请按照[此处](https://docs.opensea.io/docs/ 第 3 部分-添加元数据和付款到您的合同#intro-to-nft-metadata)。

让我们测试一些添加的功能,以确保它按预期工作。 Foundry 通过 Forge 提供了一个极快的 EVM 原生测试框架。

在您的测试文件夹中,将当前的Contract.t.sol测试文件重命名为 NFT.t.sol。 该文件将包含有关 NFT 的 mintTo 方法的所有测试。 接下来,将现有的样板代码替换为以下代码:

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.10;

import "forge-std/Test.sol";
import "../src/NFT.sol";

contract NFTTest is Test {
    using stdStorage for StdStorage;

    NFT private nft;

    function setUp() public {
        // Deploy NFT contract
        nft = new NFT("NFT_tutorial", "TUT", "baseUri");
    }

    function testFailNoMintPricePaid() public {
        nft.mintTo(address(1));
    }

    function testMintPricePaid() public {
        nft.mintTo{value: 0.08 ether}(address(1));
    }

    function testFailMaxSupplyReached() public {
        uint256 slot = stdstore
            .target(address(nft))
            .sig("currentTokenId()")
            .find();
        bytes32 loc = bytes32(slot);
        bytes32 mockedCurrentTokenId = bytes32(abi.encode(10000));
        vm.store(address(nft), loc, mockedCurrentTokenId);
        nft.mintTo{value: 0.08 ether}(address(1));
    }

    function testFailMintToZeroAddress() public {
        nft.mintTo{value: 0.08 ether}(address(0));
    }

    function testNewMintOwnerRegistered() public {
        nft.mintTo{value: 0.08 ether}(address(1));
        uint256 slotOfNewOwner = stdstore
            .target(address(nft))
            .sig(nft.ownerOf.selector)
            .with_key(1)
            .find();

        uint160 ownerOfTokenIdOne = uint160(
            uint256(
                (vm.load(address(nft), bytes32(abi.encode(slotOfNewOwner))))
            )
        );
        assertEq(address(ownerOfTokenIdOne), address(1));
    }

    function testBalanceIncremented() public {
        nft.mintTo{value: 0.08 ether}(address(1));
        uint256 slotBalance = stdstore
            .target(address(nft))
            .sig(nft.balanceOf.selector)
            .with_key(address(1))
            .find();

        uint256 balanceFirstMint = uint256(
            vm.load(address(nft), bytes32(slotBalance))
        );
        assertEq(balanceFirstMint, 1);

        nft.mintTo{value: 0.08 ether}(address(1));
        uint256 balanceSecondMint = uint256(
            vm.load(address(nft), bytes32(slotBalance))
        );
        assertEq(balanceSecondMint, 2);
    }

    function testSafeContractReceiver() public {
        Receiver receiver = new Receiver();
        nft.mintTo{value: 0.08 ether}(address(receiver));
        uint256 slotBalance = stdstore
            .target(address(nft))
            .sig(nft.balanceOf.selector)
            .with_key(address(receiver))
            .find();

        uint256 balance = uint256(vm.load(address(nft), bytes32(slotBalance)));
        assertEq(balance, 1);
    }

    function testFailUnSafeContractReceiver() public {
        vm.etch(address(1), bytes("mock code"));
        nft.mintTo{value: 0.08 ether}(address(1));
    }

    function testWithdrawalWorksAsOwner() public {
        // Mint an NFT, sending eth to the contract
        Receiver receiver = new Receiver();
        address payable payee = payable(address(0x1337));
        uint256 priorPayeeBalance = payee.balance;
        nft.mintTo{value: nft.MINT_PRICE()}(address(receiver));
        // Check that the balance of the contract is correct
        assertEq(address(nft).balance, nft.MINT_PRICE());
        uint256 nftBalance = address(nft).balance;
        // Withdraw the balance and assert it was transferred
        nft.withdrawPayments(payee);
        assertEq(payee.balance, priorPayeeBalance + nftBalance);
    }

    function testWithdrawalFailsAsNotOwner() public {
        // Mint an NFT, sending eth to the contract
        Receiver receiver = new Receiver();
        nft.mintTo{value: nft.MINT_PRICE()}(address(receiver));
        // Check that the balance of the contract is correct
        assertEq(address(nft).balance, nft.MINT_PRICE());
        // Confirm that a non-owner cannot withdraw
        vm.expectRevert("Ownable: caller is not the owner");
        vm.startPrank(address(0xd3ad));
        nft.withdrawPayments(payable(address(0xd3ad)));
        vm.stopPrank();
    }
}

contract Receiver is ERC721TokenReceiver {
    function onERC721Received(
        address operator,
        address from,
        uint256 id,
        bytes calldata data
    ) external override returns (bytes4) {
        return this.onERC721Received.selector;
    }
}

测试套件设置为带有setUp方法的合同,该方法在每个单独的测试之前运行。

如您所见,Forge 提供了许多 cheatcodes 来操纵状态以适应您的测试场景。

例如,我们的 testFailMaxSupplyReached 测试会检查在达到 NFT 的最大供应量时尝试铸造是否失败。 因此,NFT 合约的 currentTokenId 需要通过使用商店作弊码设置为最大供应量,这允许您将数据写入您的合约存储槽。 您希望写入的存储槽可以使用 forge-std 帮助程序库。 您可以使用以下命令运行测试:

forge test

如果您想练习您的 Forge 技能,请为我们的 NFT 合约的其余方法编写测试。 欢迎将它们 PR 到 nft-tutorial,您将在其中找到本教程的完整实现。

函数调用的gas报告

Foundry 提供有关您的合同的综合gas报告。 对于测试中调用的每个函数,它都会返回最小、平均、中值和最大 gas 成本。 要打印gas报告,只需运行:

forge test --gas-report

在查看合约中的各种 gas 优化时,这会派上用场。

让我们来看看我们通过用 Solmate 代替 OpenZeppelin 来实现我们的 ERC721 实现而节省的 gas。 您可以在此处 找到使用这两个库的 NFT 实现。 以下是在该存储库上运行 forge test --gas-report 时生成的gas报告。

如您所见,我们使用 Solmate 的实施在一次成功的铸造中节省了大约 500 gas(mintTo 函数调用的最大 gas 成本)。

Gas report solmate NFT

Gas report OZ NFT

就是这样,我希望这能为您提供如何开始使用 Foundry 的良好实践基础。 我们认为没有比在 solidity 中编写测试更好的方式来深入理解 solidity。 您还将体验到更少的 javascript 和 solidity 之间的上下文切换。 编码愉快!

注意:按照 this 教程学习如何使用 solidity 脚本部署此处使用的 NFT 合约。

对 Foundry 项目进行 Docker 化

本教程向您展示如何使用 Foundry 的 Docker 镜像构建、测试和部署智能合约。 它改编自 solmate nft 教程中的代码。 如果您还没有完成该教程,并且是 solidity 的新手,您可能想先从它开始。 或者,如果您对 Docker 和 Solidity 有一定的了解,您可以使用自己现有的项目并进行相应的调整。 此处 提供了 NFT 和 Docker 内容的完整源代码。

本教程仅用于说明目的,并按原样提供。 本教程未经审核或全面测试。 不应在生产环境中使用本教程中的任何代码。

安装和设置

运行本教程所需的唯一安装是 Docker,以及您选择的 IDE(可选)。 按照 Docker 安装说明

为了使以后的命令简洁明了,让我们重新标记图像: docker tag ghcr.io/foundry-rs/foundry:latest foundry:latest

在本地安装 Foundry 并不是严格要求的,但它可能有助于调试。 您可以使用 foundryup 安装它。

最后,要使用本教程的任何castforge create部分,您需要访问以太坊节点。 如果您没有自己的节点在运行(可能),您可以使用第 3 方节点服务。 我们不会在本教程中推荐特定的提供商。 开始学习节点即服务的好地方是 Ethereum 的文章 主题。

对于本教程的其余部分,假设您的以太坊节点的 RPC 端点设置如下export RPC_URL=<YOUR_RPC_URL>

Foundry docker 镜像导览

docker 镜像可以通过两种主要方式使用: 1.作为接口直接锻造和铸造 2. 作为构建您自己的容器化测试、构建和部署工具的基础镜像

我们将涵盖两者,但让我们先看看使用 docker 与 foundry 的接口。 这也是一个很好的测试,表明您的本地安装工作正常!

我们可以针对我们的 docker 镜像运行任何 cast 命令。 让我们获取最新的区块信息:

$ docker run foundry "cast block --rpc-url $RPC_URL latest"
baseFeePerGas        "0xb634241e3"
difficulty           "0x2e482bdf51572b"
extraData            "0x486976656f6e20686b"
gasLimit             "0x1c9c380"
gasUsed              "0x652993"
hash                 "0x181748772da2f968bcc91940c8523bb6218a7d57669ded06648c9a9fb6839db5"
logsBloom            "0x406010046100001198c220108002b606400029444814008210820c04012804131847150080312500300051044208430002008029880029011520380060262400001c538d00440a885a02219d49624aa110000003094500022c003600a00258009610c410323580032000849a0408a81a0a060100022505202280c61880c80020e080244400440404520d210429a0000400010089410c8408162903609c920014028a94019088681018c909980701019201808040004100000080540610a9144d050020220c10a24c01c000002005400400022420140e18100400e10254926144c43a200cc008142080854088100128844003010020c344402386a8c011819408"
miner                "0x1ad91ee08f21be3de0ba2ba6918e714da6b45836"
mixHash              "0xb920857687476c1bcb21557c5f6196762a46038924c5f82dc66300347a1cfc01"
nonce                "0x1ce6929033fbba90"
number               "0xdd3309"
parentHash           "0x39c6e1aa997d18a655c6317131589fd327ae814ef84e784f5eb1ab54b9941212"
receiptsRoot         "0x4724f3b270dcc970f141e493d8dc46aeba6fffe57688210051580ac960fe0037"
sealFields           []
sha3Uncles           "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
size                 "0x1d6bb"
stateRoot            "0x0d4b714990132cf0f21801e2931b78454b26aad706fc6dc16b64e04f0c14737a"
timestamp            "0x6246259b"
totalDifficulty      "0x9923da68627095fd2e7"
transactions         [...]
uncles               []

如果我们在一个包含一些 Solidity 源代码 的目录中,我们可以将该目录挂载到 docker 中,并根据需要使用 forge。 例如:

$ docker run -v $PWD:/app foundry "forge test --root /app --watch"
No files changed, compilation skipped

Running 8 tests for test/OpenZeppelinNft.t.sol:OpenZeppelinNftTests
[PASS] testBalanceIncremented() (gas: 217829)
[PASS] testFailMaxSupplyReached() (gas: 134524)
[PASS] testFailMintToZeroAddress() (gas: 34577)
[PASS] testFailNoMintPricePaid() (gas: 5568)
[PASS] testFailUnSafeContractReceiver() (gas: 88276)
[PASS] testMintPricePaid() (gas: 81554)
[PASS] testNewMintOwnerRegistered() (gas: 190956)
[PASS] testSafeContractReceiver() (gas: 273132)
Test result: ok. 8 passed; 0 failed; finished in 2.68ms

Running 8 tests for test/SolmateNft.sol:SolmateNftTests
[PASS] testBalanceIncremented() (gas: 217400)
[PASS] testFailMaxSupplyReached() (gas: 134524)
[PASS] testFailMintToZeroAddress() (gas: 34521)
[PASS] testFailNoMintPricePaid() (gas: 5568)
[PASS] testFailUnSafeContractReceiver() (gas: 87807)
[PASS] testMintPricePaid() (gas: 81321)
[PASS] testNewMintOwnerRegistered() (gas: 190741)
[PASS] testSafeContractReceiver() (gas: 272636)
Test result: ok. 8 passed; 0 failed; finished in 2.79ms

您可以看到我们的代码完全在容器内编译和测试。 此外,由于我们传递了 --watch 选项,容器将在检测到更改时重新编译代码。

注意:Foundry docker 镜像基于 alpine 构建,并设计为尽可能纤薄。 因此,它目前不包括像 git 这样的开发资源。 如果您计划在容器内管理整个开发生命周期,您应该在 Foundry 的镜像之上构建自定义开发镜像。

创建一个“构建和测试”图像

让我们使用 Foundry docker 镜像作为使用我们自己的 Docker 镜像的基础。 我们将使用图像来:

  1. 构建我们的 solidity 代码
  2. 运行我们的可靠性测试

一个简单的 Dockerfile 可以实现这两个目标:

# Use the latest foundry image
FROM ghcr.io/foundry-rs/foundry

# Copy our source code into the container
WORKDIR /app

# Build and test the source code
COPY . .
RUN forge build
RUN forge test

您可以构建此 docker 镜像并观察 forge 在容器内构建/运行测试:

$ docker build --no-cache --progress=plain 。

现在,如果我们的一个测试失败了怎么办? 随意修改 src/test/NFT.t.sol 使其中一个测试失败。 再次尝试构建图像。

$ docker build --no-cache --progress=plain .
<...>
#9 0.522 Failed tests:
#9 0.522 [FAIL. Reason: Ownable: caller is not the owner] testWithdrawalFailsAsNotOwner() (gas: 193917)
#9 0.522
#9 0.522 Encountered a total of 1 failing tests, 9 tests succeeded
------
error: failed to solve: executor failed running [/bin/sh -c forge test]: exit code: 1

我们的镜像未能建立,因为我们的测试失败了! 这实际上是一个很好的属性,因为这意味着如果我们有一个成功构建的 Docker 镜像(因此可以使用),我们就知道镜像中的代码通过了测试。*

*当然,docker 镜像的监管链非常重要。 Docker 层哈希对于验证非常有用。 在生产环境中,考虑[签署你的 docker 镜像](https://docs.docker.com/engine/security/trust/#:~:text=To%20sign%20a%20Docker%20Image,the%20local%20Docker %20trust%20repository)。

创建部署镜像

现在,我们将继续讨论更高级的 Dockerfile。 让我们添加一个入口点,允许我们使用构建(和测试!)的图像来部署我们的代码。 我们可以首先针对 Rinkeby 测试网。

# Use the latest foundry image
FROM ghcr.io/foundry-rs/foundry

# Copy our source code into the container
WORKDIR /app

# Build and test the source code
COPY . .
RUN forge build
RUN forge test

# Set the entrypoint to the forge deployment command
ENTRYPOINT ["forge", "create"]

让我们构建镜像,这次给它命名:

$ docker build --no-cache --progress=plain -t nft-deployer 。

以下是我们如何使用我们的 docker 镜像进行部署:

$ docker run nft-deployer --rpc-url $RPC_URL --constructor-args "ForgeNFT" "FNFT" "https://ethereum.org" --private-key $PRIVATE_KEY ./src/NFT.sol:NFT
No files changed, compilation skipped
Deployer: 0x496e09fcb240c33b8fda3b4b74d81697c03b6b3d
Deployed to: 0x23d465eaa80ad2e5cdb1a2345e4b54edd12560d3
Transaction hash: 0xf88c68c4a03a86b0e7ecb05cae8dea36f2896cd342a6af978cab11101c6224a9

我们刚刚在 docker 容器中完全构建、测试和部署了我们的合约! 本教程旨在用于测试网,但您可以针对主网运行完全相同的 Docker 镜像,并确信相同的代码正在由相同的工具部署。

为什么这有用?

Docker 是关于可移植性、可再现性和环境不变性的。 这意味着当您在环境、网络、开发人员等之间切换时,您可以减少对意外变化的关注。以下是一些基本示例,说明为什么喜欢使用 Docker 镜像进行智能合约部署:

  • 减少确保系统级依赖项在部署环境之间匹配的开销(例如,您的生产运行器是否始终具有与您的开发运行器相同版本的forge?)
  • 增加代码在部署之前已经过测试并且没有被更改的信心(例如,如果在上图中,您的代码在部署时重新编译,这是一个主要的危险信号)。
  • 缓解职责分离的痛点:拥有主网凭证的人无需确保他们拥有最新的编译器、代码库等。很容易确保某人在测试网中运行的 docker 部署映像与针对主网的相同。
  • 冒着 web2 的风险,Docker 是几乎所有公共云提供商的公认标准。 它可以轻松安排需要与区块链交互的作业、任务等。

故障排除

如上所述,默认情况下,Foundry 镜像不包含 git。 这可能会导致某些命令在没有明确原因的情况下失败。 例如:

$ docker run foundry "forge init --no-git /test"
Initializing /test...
Installing ds-test in "/test/lib/ds-test", (url: https://github.com/dapphub/ds-test, tag: None)
Error:
   0: No such file or directory (os error 2)

Location:
   cli/src/cmd/forge/install.rs:107

在这种情况下,失败仍然是由于缺少 git 安装造成的。 建议的修复方法是构建现有的 Foundry 镜像并安装您需要的任何其他开发依赖项。

测试 EIP-712 签名

介绍

EIP-712 引入了在链下签署交易的功能,其他用户稍后可以在链上执行交易。 一个常见的例子是 EIP-2612 gasless 代币批准。

传统上,设置用户或合约allowance以从所有者的余额中转移 ERC-20 代币需要所有者提交链上批准。 由于这被证明是糟糕的用户体验,DAI 引入了 ERC-20 permit(后来标准化为 EIP-2612),允许所有者签署_off-chain_批准,支出者(或其他任何人!)可以在之前在链上提交 transferFrom

本指南将涵盖使用 Foundry 在 Solidity 中测试此模式。

潜入

首先,我们将介绍基本的代币转移:

  • 所有者在链下签署批准
  • Spender 在链上调用 permittransferFrom

我们将使用 Solmate 的 ERC-20,因为随附了 EIP-712 和 EIP-2612 batteries。 如果您还没有浏览完整合约,请看一眼 - 这里是 permit 实现的:

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

我们还将使用自定义的SigUtils 合约来帮助创建、散列和签署链下批准。

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

contract SigUtils {
    bytes32 internal DOMAIN_SEPARATOR;

    constructor(bytes32 _DOMAIN_SEPARATOR) {
        DOMAIN_SEPARATOR = _DOMAIN_SEPARATOR;
    }

    // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    bytes32 public constant PERMIT_TYPEHASH =
        0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

    struct Permit {
        address owner;
        address spender;
        uint256 value;
        uint256 nonce;
        uint256 deadline;
    }

    // computes the hash of a permit
    function getStructHash(Permit memory _permit)
        internal
        pure
        returns (bytes32)
    {
        return
            keccak256(
                abi.encode(
                    PERMIT_TYPEHASH,
                    _permit.owner,
                    _permit.spender,
                    _permit.value,
                    _permit.nonce,
                    _permit.deadline
                )
            );
    }

    // computes the hash of the fully encoded EIP-712 message for the domain, which can be used to recover the signer
    function getTypedDataHash(Permit memory _permit)
        public
        view
        returns (bytes32)
    {
        return
            keccak256(
                abi.encodePacked(
                    "\x19\x01",
                    DOMAIN_SEPARATOR,
                    getStructHash(_permit)
                )
            );
    }
}

设置

  • 使用令牌的 EIP-712 域分隔符部署模拟 ERC-20 令牌和“SigUtils”助手
  • 创建私钥来模拟所有者和消费者
  • 使用 vm.addr cheatcode 推导他们的地址
  • 为所有者铸造一个测试代币
contract ERC20Test is Test {
    MockERC20 internal token;
    SigUtils internal sigUtils;

    uint256 internal ownerPrivateKey;
    uint256 internal spenderPrivateKey;

    address internal owner;
    address internal spender;

    function setUp() public {
        token = new MockERC20();
        sigUtils = new SigUtils(token.DOMAIN_SEPARATOR());

        ownerPrivateKey = 0xA11CE;
        spenderPrivateKey = 0xB0B;

        owner = vm.addr(ownerPrivateKey);
        spender = vm.addr(spenderPrivateKey);

        token.mint(owner, 1e18);
    }

389 / 5,000 翻译结果 翻译结果 测试:permit

  • 为消费者创建批准
  • 使用 sigUtils.getTypedDataHash 计算其摘要
  • 使用带有所有者私钥的 vm.sign cheatcode 对摘要进行签名
  • 存储签名的uint8 v, bytes32 r, bytes32 s
  • 调用 permit 并带有已签署的许可证和签名以执行链上批准
    function test_Permit() public {
        SigUtils.Permit memory permit = SigUtils.Permit({
            owner: owner,
            spender: spender,
            value: 1e18,
            nonce: 0,
            deadline: 1 days
        });

        bytes32 digest = sigUtils.getTypedDataHash(permit);

        (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);

        token.permit(
            permit.owner,
            permit.spender,
            permit.value,
            permit.deadline,
            v,
            r,
            s
        );

        assertEq(token.allowance(owner, spender), 1e18);
        assertEq(token.nonces(owner), 1);
    }
  • 确保因截止日期过期、签名者无效、随机数无效和签名重播而导致的调用失败
    function testRevert_ExpiredPermit() public {
        SigUtils.Permit memory permit = SigUtils.Permit({
            owner: owner,
            spender: spender,
            value: 1e18,
            nonce: token.nonces(owner),
            deadline: 1 days
        });

        bytes32 digest = sigUtils.getTypedDataHash(permit);

        (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);

        vm.warp(1 days + 1 seconds); // fast forward one second past the deadline

        vm.expectRevert("PERMIT_DEADLINE_EXPIRED");
        token.permit(
            permit.owner,
            permit.spender,
            permit.value,
            permit.deadline,
            v,
            r,
            s
        );
    }

    function testRevert_InvalidSigner() public {
        SigUtils.Permit memory permit = SigUtils.Permit({
            owner: owner,
            spender: spender,
            value: 1e18,
            nonce: token.nonces(owner),
            deadline: 1 days
        });

        bytes32 digest = sigUtils.getTypedDataHash(permit);

        (uint8 v, bytes32 r, bytes32 s) = vm.sign(spenderPrivateKey, digest); // spender signs owner's approval

        vm.expectRevert("INVALID_SIGNER");
        token.permit(
            permit.owner,
            permit.spender,
            permit.value,
            permit.deadline,
            v,
            r,
            s
        );
    }

    function testRevert_InvalidNonce() public {
        SigUtils.Permit memory permit = SigUtils.Permit({
            owner: owner,
            spender: spender,
            value: 1e18,
            nonce: 1, // owner nonce stored on-chain is 0
            deadline: 1 days
        });

        bytes32 digest = sigUtils.getTypedDataHash(permit);

        (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);

        vm.expectRevert("INVALID_SIGNER");
        token.permit(
            permit.owner,
            permit.spender,
            permit.value,
            permit.deadline,
            v,
            r,
            s
        );
    }

    function testRevert_SignatureReplay() public {
        SigUtils.Permit memory permit = SigUtils.Permit({
            owner: owner,
            spender: spender,
            value: 1e18,
            nonce: 0,
            deadline: 1 days
        });

        bytes32 digest = sigUtils.getTypedDataHash(permit);

        (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);

        token.permit(
            permit.owner,
            permit.spender,
            permit.value,
            permit.deadline,
            v,
            r,
            s
        );

        vm.expectRevert("INVALID_SIGNER");
        token.permit(
            permit.owner,
            permit.spender,
            permit.value,
            permit.deadline,
            v,
            r,
            s
        );
    }

测试:transferFrom

  • 为支出者创建、签署和执行批准
  • 使用 vm.prank cheatcode 调用 tokenTransfer 作为花费者来执行转移
    function test_TransferFromLimitedPermit() public {
        SigUtils.Permit memory permit = SigUtils.Permit({
            owner: owner,
            spender: spender,
            value: 1e18,
            nonce: 0,
            deadline: 1 days
        });

        bytes32 digest = sigUtils.getTypedDataHash(permit);

        (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);

        token.permit(
            permit.owner,
            permit.spender,
            permit.value,
            permit.deadline,
            v,
            r,
            s
        );

        vm.prank(spender);
        token.transferFrom(owner, spender, 1e18);

        assertEq(token.balanceOf(owner), 0);
        assertEq(token.balanceOf(spender), 1e18);
        assertEq(token.allowance(owner, spender), 0);
    }

    function test_TransferFromMaxPermit() public {
        SigUtils.Permit memory permit = SigUtils.Permit({
            owner: owner,
            spender: spender,
            value: type(uint256).max,
            nonce: 0,
            deadline: 1 days
        });

        bytes32 digest = sigUtils.getTypedDataHash(permit);

        (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);

        token.permit(
            permit.owner,
            permit.spender,
            permit.value,
            permit.deadline,
            v,
            r,
            s
        );

        vm.prank(spender);
        token.transferFrom(owner, spender, 1e18);

        assertEq(token.balanceOf(owner), 0);
        assertEq(token.balanceOf(spender), 1e18);
        assertEq(token.allowance(owner, spender), type(uint256).max);
    }
  • 如果allowance无效和余额无效,call会失败
    function testFail_InvalidAllowance() public {
        SigUtils.Permit memory permit = SigUtils.Permit({
            owner: owner,
            spender: spender,
            value: 5e17, // approve only 0.5 tokens
            nonce: 0,
            deadline: 1 days
        });

        bytes32 digest = sigUtils.getTypedDataHash(permit);

        (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);

        token.permit(
            permit.owner,
            permit.spender,
            permit.value,
            permit.deadline,
            v,
            r,
            s
        );

        vm.prank(spender);
        token.transferFrom(owner, spender, 1e18); // attempt to transfer 1 token
    }

    function testFail_InvalidBalance() public {
        SigUtils.Permit memory permit = SigUtils.Permit({
            owner: owner,
            spender: spender,
            value: 2e18, // approve 2 tokens
            nonce: 0,
            deadline: 1 days
        });

        bytes32 digest = sigUtils.getTypedDataHash(permit);

        (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);

        token.permit(
            permit.owner,
            permit.spender,
            permit.value,
            permit.deadline,
            v,
            r,
            s
        );

        vm.prank(spender);
        token.transferFrom(owner, spender, 2e18); // attempt to transfer 2 tokens (owner only owns 1)
    }

Bundled示例

这是 模拟合约 的一部分,它只存入 ERC-20 代币。 请注意,deposit 如何需要初步的 approvepermit tx 才能转移代币,而 depositWithPermit 设置限额_并_在单个 tx 中转移代币。

    ///                                                          ///
    ///                           DEPOSIT                        ///
    ///                                                          ///

    /// @notice Deposits ERC-20 tokens (requires pre-approval)
    /// @param _tokenContract The ERC-20 token address
    /// @param _amount The number of tokens
    function deposit(address _tokenContract, uint256 _amount) external {
        ERC20(_tokenContract).transferFrom(msg.sender, address(this), _amount);

        userDeposits[msg.sender][_tokenContract] += _amount;

        emit TokenDeposit(msg.sender, _tokenContract, _amount);
    }

    ///                                                          ///
    ///                      DEPOSIT w/ PERMIT                   ///
    ///                                                          ///

    /// @notice Deposits ERC-20 tokens with a signed approval
    /// @param _tokenContract The ERC-20 token address
    /// @param _amount The number of tokens to transfer
    /// @param _owner The user signing the approval
    /// @param _spender The user to transfer the tokens (ie this contract)
    /// @param _value The number of tokens to appprove the spender
    /// @param _deadline The timestamp the permit expires
    /// @param _v The 129th byte and chain id of the signature
    /// @param _r The first 64 bytes of the signature
    /// @param _s Bytes 64-128 of the signature
    function depositWithPermit(
        address _tokenContract,
        uint256 _amount,
        address _owner,
        address _spender,
        uint256 _value,
        uint256 _deadline,
        uint8 _v,
        bytes32 _r,
        bytes32 _s
    ) external {
        ERC20(_tokenContract).permit(
            _owner,
            _spender,
            _value,
            _deadline,
            _v,
            _r,
            _s
        );

        ERC20(_tokenContract).transferFrom(_owner, address(this), _amount);

        userDeposits[_owner][_tokenContract] += _amount;

        emit TokenDeposit(_owner, _tokenContract, _amount);
    }

设置

  • 使用令牌的 EIP-712 域分隔符部署“存款”合约、模拟 ERC-20 令牌和“SigUtils”助手
  • 创建一个私钥来模拟所有者(支出者现在是“存款”地址)
  • 使用 vm.addr cheatcode 推导出所有者地址
  • 为所有者铸造一个测试代币
contract DepositTest is Test {
    Deposit internal deposit;
    MockERC20 internal token;
    SigUtils internal sigUtils;

    uint256 internal ownerPrivateKey;
    address internal owner;

    function setUp() public {
        deposit = new Deposit();
        token = new MockERC20();
        sigUtils = new SigUtils(token.DOMAIN_SEPARATOR());

        ownerPrivateKey = 0xA11CE;
        owner = vm.addr(ownerPrivateKey);

        token.mint(owner, 1e18);
    }

测试:depositWithPermit

  • 为“存款”合约创建批准
  • 使用 sigUtils.getTypedDataHash 计算其摘要
  • 使用带有所有者私钥的 vm.sign cheatcode 对摘要进行签名
  • 存储签名的uint8 v, bytes32 r, bytes32 s
    • 注意: 可以通过 bytes signature = abi.encodePacked(r, s, v) 转换为字节
  • 使用已签署的批准和签名调用 depositWithPermit 将代币转移到合约中
    function test_DepositWithLimitedPermit() public {
        SigUtils.Permit memory permit = SigUtils.Permit({
            owner: owner,
            spender: address(deposit),
            value: 1e18,
            nonce: token.nonces(owner),
            deadline: 1 days
        });

        bytes32 digest = sigUtils.getTypedDataHash(permit);

        (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);

        deposit.depositWithPermit(
            address(token),
            1e18,
            permit.owner,
            permit.spender,
            permit.value,
            permit.deadline,
            v,
            r,
            s
        );

        assertEq(token.balanceOf(owner), 0);
        assertEq(token.balanceOf(address(deposit)), 1e18);

        assertEq(token.allowance(owner, address(deposit)), 0);
        assertEq(token.nonces(owner), 1);

        assertEq(deposit.userDeposits(owner, address(token)), 1e18);
    }

    function test_DepositWithMaxPermit() public {
        SigUtils.Permit memory permit = SigUtils.Permit({
            owner: owner,
            spender: address(deposit),
            value: type(uint256).max,
            nonce: token.nonces(owner),
            deadline: 1 days
        });

        bytes32 digest = sigUtils.getTypedDataHash(permit);

        (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest);

        deposit.depositWithPermit(
            address(token),
            1e18,
            permit.owner,
            permit.spender,
            permit.value,
            permit.deadline,
            v,
            r,
            s
        );

        assertEq(token.balanceOf(owner), 0);
        assertEq(token.balanceOf(address(deposit)), 1e18);

        assertEq(token.allowance(owner, address(deposit)), type(uint256).max);
        assertEq(token.nonces(owner), 1);

        assertEq(deposit.userDeposits(owner, address(token)), 1e18);
    }
  • 确保无效的 permittransferFrom 调用失败,如前所示

长篇大论

使用 Foundry cheatcode addrsignprank 来测试 Foundry 中的 EIP-712 签名。

所有源代码都可以在 此处 找到。

Solidity脚本

介绍

Solidity 脚本是一种使用 Solidity 以声明方式部署合约的方法,而不是使用限制更多且用户友好度较低的 forge create

Solidity 脚本就像您在使用 Hardhat 等工具时编写的脚本; Solidity 脚本的不同之处在于它们是用 Solidity 而不是 JavaScript 编写的,并且它们在快速的 Foundry EVM 后端上运行,该后端提供试运行功能。

设置

让我们尝试使用 solidity 脚本部署在 solmate 教程中制作的 NFT 合约。 首先,我们需要通过以下方式创建一个新的 Foundry 项目:

forge init solidity-scripting

由于 solmate 教程中的 NFT 合约继承了 solmate 和 OpenZeppelin 合约,我们必须通过运行以下命令将它们安装为依赖项:

# Enter the project
cd solidity-scripting

# Install Solmate and OpenZeppelin contracts as dependencies
forge install transmissions11/solmate Openzeppelin/openzeppelin-contracts

接下来,我们必须删除 src 文件夹中的 Counter.sol 文件并创建另一个名为 NFT.sol 的文件。 你可以通过运行来做到这一点:

rm src/Counter.sol test/Counter.t.sol && touch src/NFT.sol && ls src

set up commands

完成后,你应该打开你喜欢的代码编辑器并将下面的代码复制到NFT.sol 文件中。

// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.10;

import "solmate/tokens/ERC721.sol";
import "openzeppelin-contracts/utils/Strings.sol";
import "openzeppelin-contracts/access/Ownable.sol";

error MintPriceNotPaid();
error MaxSupply();
error NonExistentTokenURI();
error WithdrawTransfer();

contract NFT is ERC721, Ownable {

    using Strings for uint256;
    string public baseURI;
    uint256 public currentTokenId;
    uint256 public constant TOTAL_SUPPLY = 10_000;
    uint256 public constant MINT_PRICE = 0.08 ether;

    constructor(
        string memory _name,
        string memory _symbol,
        string memory _baseURI
    ) ERC721(_name, _symbol) {
        baseURI = _baseURI;
    }

    function mintTo(address recipient) public payable returns (uint256) {
        if (msg.value != MINT_PRICE) {
            revert MintPriceNotPaid();
        }
        uint256 newTokenId = ++currentTokenId;
        if (newTokenId > TOTAL_SUPPLY) {
            revert MaxSupply();
        }
        _safeMint(recipient, newTokenId);
        return newTokenId;
    }

    function tokenURI(uint256 tokenId)
        public
        view
        virtual
        override
        returns (string memory)
    {
        if (ownerOf(tokenId) == address(0)) {
            revert NonExistentTokenURI();
        }
        return
            bytes(baseURI).length > 0
                ? string(abi.encodePacked(baseURI, tokenId.toString()))
                : "";
    }

    function withdrawPayments(address payable payee) external onlyOwner {
        uint256 balance = address(this).balance;
        (bool transferTx, ) = payee.call{value: balance}("");
        if (!transferTx) {
            revert WithdrawTransfer();
        }
    }
}

现在,让我们尝试编译我们的合约以确保一切正常。

forge build

如果您的输出看起来像这样,则合同已成功编译。 compile successful

部署我们的合约

我们将把“NFT”合约部署到 Goerli 测试网,但为此我们需要稍微配置 Foundry,通过设置 Goerli RPC URL 之类的东西,这是一个由 Goerli Eth 资助的账户的私钥 ,以及用于验证 NFT 合约的 Etherscan 密钥。

💡 注意:您可以在 此处 获得一些 Goerli 测试网 ETH。

环境配置

完成所有这些后,创建一个 .env 文件并添加变量。 Foundry 会自动加载项目目录中的 .env 文件。

.env 文件应遵循以下格式:

GOERLI_RPC_URL=
PRIVATE_KEY=
ETHERSCAN_API_KEY=

我们现在需要编辑 foundry.toml 文件。 项目的根目录中应该已经有一个。

将以下行添加到文件末尾:

[rpc_endpoints]
goerli = "${GOERLI_RPC_URL}"

[etherscan]
goerli = { key = "${ETHERSCAN_API_KEY}" }

This creates a RPC alias for Goerli and loads the Etherscan API key.

编写脚本

接下来,我们必须创建一个文件夹并将其命名为script,并在其中创建一个名为NFT.s.sol的文件。 这是我们将创建部署脚本本身的地方。

NFT.s.sol 的内容应该是这样的:

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

import "forge-std/Script.sol";
import "../src/NFT.sol";

contract MyScript is Script {
    function run() external {
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        vm.startBroadcast(deployerPrivateKey);

        NFT nft = new NFT("NFT_tutorial", "TUT", "baseUri");

        vm.stopBroadcast();
    }
}

现在让我们通读代码并弄清楚它的实际含义和作用。

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

请记住,即使它是一个脚本,它仍然像智能合约一样工作,但从未部署过,所以就像任何其他用 Solidity 编写的智能合约一样,必须指定pragma version

import "forge-std/Script.sol";
import "../src/NFT.sol";

就像我们在编写测试时可能会导入 Forge Std 来获取测试实用程序一样,Forge Std 也提供了一些我们在这里导入的脚本实用程序。

下一行只是导入NFT合约。

contract MyScript is Script {

我们创建一个名为 MyScript 的合约,它从 Forge Std 继承了 Script

 function run() external {

默认情况下,脚本是通过调用名为run的函数(我们的入口点)来执行的。

uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

这会从我们的 .env 文件中加载私钥。 注意:.env 文件中公开私钥并将它们加载到程序中时必须小心。 这仅建议与非特权部署者一起使用或用于本地/测试设置。 对于生产设置,请查看 Foundry 支持的各种钱包选项

vm.startBroadcast(deployerPrivateKey);

这是一个特殊的Cheatcode,用于记录我们的主脚本合约进行的调用和合约创建。 我们传递 deployerPrivateKey 以指示它使用该密钥来签署交易。 稍后,我们将广播这些交易以部署我们的 NFT 合约。

 NFT nft = new NFT("NFT_tutorial", "TUT", "baseUri");

在这里,我们只是创建我们的 NFT 合约。 因为我们在这行之前调用了 vm.startBroadcast(),合约创建将被 Forge 记录下来,并且如前所述,我们可以广播交易以在链上部署合约。 默认情况下,广播事务日志将存储在“广播”目录中。 您可以通过在 foundry.toml 文件中设置 broadcast 来更改日志位置。

现在您已经了解了脚本智能合约的功能,让我们运行它。

您应该已经将我们之前提到的变量添加到 .env 中,以便下一部分工作。

在项目运行的根目录:

# To load the variables in the .env file
source .env

# To deploy and verify our contract
forge script script/NFT.s.sol:MyScript --rpc-url $GOERLI_RPC_URL --broadcast --verify -vvvv

Forge 将运行我们的脚本并为我们广播交易——这可能需要一些时间,因为 Forge 还将等待交易收据。 大约一分钟后,您应该会看到类似这样的内容:

contract verified

这确认您已成功将 NFT 合约部署到 Goerli 测试网,并已在 Etherscan 上对其进行了验证,所有这些都通过一个命令完成。

本地部署

您可以通过将端口配置为 fork-url 来部署到本地测试网 Anvil。

在这里,我们在帐户方面有两种选择。 我们可以在没有任何标志的情况下启动 anvil,并使用提供的私钥之一。 或者,我们可以传递一个助记符给 anvil 来使用。

使用 Anvil 的默认帐户

首先,启动 Anvil:

anvil

使用 Anvil 提供给您的私钥更新您的 .env 文件。

然后运行以下脚本:

forge script script/NFT.s.sol:MyScript --fork-url http://localhost:8545 --broadcast

使用自定义助记符

将以下行添加到您的 .env 文件并使用您的助记符完成它:

MNEMONIC=

预计我们之前设置的PRIVATE_KEY环境变量是这个助记词中的前10个账户之一。

使用自定义助记符启动 Anvil:

source .env

anvil --m $MNEMONIC

然后运行以下脚本:

forge script script/NFT.s.sol:MyScript --fork-url http://localhost:8545 --broadcast

💡 注意:可以在 此处 找到本教程的完整实现,要进一步阅读有关 solidity 脚本的信息,您可以查看“forge script” 参考

使用 Cast 和 Anvil 分叉主网

介绍

通过结合 AnvilCast,您可以通过与真实网络上的合约交互来进行分叉和测试。 本教程的目的是向您展示如何将 Dai 代币从持有 Dai 的人转移到 Anvil 创建的帐户。

设置

让我们从分叉主网开始。

anvil --fork-url https://mainnet.infura.io/v3/$INFURA_KEY

您将看到使用公钥和私钥创建了 10 个帐户。 我们将使用0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266(我们称此用户为 Alice)。

转账 Dai

转到 Etherscan 并搜索 Dai 代币的持有者(此处)。 让我们选择一个随机帐户。 在此示例中,我们将使用0xad0135af20fa82e106607257143d0060a7eb5cbf。 让我们将合同和帐户导出为环境变量:

export ALICE=0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
export DAI=0x6b175474e89094c44da98b954eedeac495271d0f
export LUCKY_USER=0xad0135af20fa82e106607257143d0060a7eb5cbf

我们可以使用 cast call 检查 Alice 的余额:

$ cast call $DAI \
  "balanceOf(address)(uint256)" \
  $ALICE
0

同样,我们也可以使用 cast call 查看幸运用户的余额:

$ cast call $DAI \
  "balanceOf(address)(uint256)" \
  $LUCKY_USER
71686045944718512103110072

让我们使用 cast send 将一些代币从幸运用户转移给 Alice:

# This calls Anvil and lets us impersonate our lucky user
$ cast rpc anvil_impersonateAccount $LUCKY_USER
$ cast send $DAI \
--from $LUCKY_USER \
  "transfer(address,uint256)(bool)" \
  $ALICE \
  1686045944718512103110072
blockHash               0xbf31c45f6935a0714bb4f709b5e3850ab0cc2f8bffe895fefb653d154e0aa062
blockNumber             15052891
...

让我们检查传输是否有效:

cast call $DAI \
  "balanceOf(address)(uint256)" \
  $ALICE
1686045944718512103110072

$ cast call $DAI \
  "balanceOf(address)(uint256)" \
  $LUCKY_USER
70000000000000000000000000

FAQ

This is a collection of common questions and answers. If you do not find your question listed here, hop in the Telegram support channel and let us help you!

Help! I can't see my logs

Forge does not display logs by default. If you want to see logs from Hardhat's console.log or from DSTest-style log_* events, you need to run forge test with verbosity 2 (-vv).

If you want to see other events your contracts emit, you need to run with traces enabled. To do that, set the verbosity to 3 (-vvv) to see traces for failing tests, or 4 (-vvvv) to see traces for all tests.

My tests are failing and I don't know why

To gain better insight into why your tests are failing, try using traces. To enable traces, you need to increase the verbosity on forge test to at least 3 (-vvv) but you can go as high as 5 (-vvvvv) for even more traces.

You can learn more about traces in our Understanding Traces chapter.

How do I use console.log?

To use Hardhat's console.log you must add it to your project by copying the file over from here.

Alternatively, you can use Forge Std which comes bundled with console.log. To use console.log from Forge Std, you have to import it:

import "forge-std/console.sol";

How do I run specific tests?

If you want to run only a few tests, you can use --match-test to filter test functions, --match-contract to filter test contracts, and --match-path to filter test files on forge test.

How do I use a specific Solidity compiler?

Forge will try to auto-detect what Solidity compiler works for your project.

To use a specific Solidity compiler, you can set solc in your config file, or pass --use solc:<version> to a Forge command that supports it (e.g. forge build or forge test). Paths to a solc binary are also accepted. To use a specific local solc binary, you can set solc = "<path to solc>" in your config file, or pass --use "<path to solc>". The solc version/path can also be set via the env variable FOUNDRY_SOLC=<version/path>, but the cli arg --use has priority.

For example, if you have a project that supports all 0.7.x Solidity versions, but you want to compile with solc 0.7.0, you could use forge build --use solc:0.7.0.

How do I fork from a live network?

To fork from a live network, pass --fork-url <URL> to forge test. You can also fork from a specific block using --fork-block-number <BLOCK>, which adds determinism to your test, and allows Forge to cache the chain data for that block.

For example, to fork from Ethereum mainnet at block 10,000,000 you could use: forge test --fork-url $MAINNET_RPC_URL --fork-block-number 10000000.

How do I add my own assertions?

You can add your own assertions by creating your own base test contract and having that inherit from the test framework of your choice.

For example, if you use DSTest, you could create a base test contract like this:

contract TestBase is DSTest {
    function myCustomAssertion(uint a, uint b) {
      if (a != b) {
          emit log_string("a and b did not match");
          fail();
      }
    }
}

You would then inherit from TestBase in your test contracts.

contract MyContractTest is TestBase {
    function testSomething() {
        // ...
    }
}

Similarly, if you use Forge Std, you can create a base test contract that inherits from Test.

For a good example of a base test contract that has helper methods and custom assertions, see Solmate's DSTestPlus.

How do I use Forge offline?

Forge will sometimes check for newer Solidity versions that fit your project. To use Forge offline, use the --offline flag.

I'm getting Solc errors

solc-bin doesn't offer static builds for apple silicon. Foundry relies on svm to install native builds for apple silicon.

All solc versions are installed under ~/.svm/. If you encounter solc related errors, such as SolcError: ... please to nuke ~/.svm/ and try again, this will trigger a fresh install and usually resolves the issue.

If you're on apple silion, please ensure the z3 thereom prover is installed: brew install z3

Note: native apple silicon builds are only available from 0.8.5 upwards. If you need older versions, you must enable apple silicon rosetta to run them.

Forge fails in JavaScript monorepos (pnpm)

Managers like pnpm use symlinks to manage node_modules folders.

A common layout may look like:

├── contracts
│    ├── contracts
│    ├── foundry.toml
│    ├── lib
│    ├── node_modules
│    ├── package.json
├── node_modules
│    ├── ...
├── package.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml

Where the Foundry workspace is in ./contracts, but packages in ./contracts/node_modules are symlinked to ./node_modules.

When running forge build in ./contracts/node_modules, this can lead to an error like:

error[6275]: ParserError: Source "node_modules/@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol" not found: File outside of allowed directories. The following are allowed: "<repo>/contracts", "<repo>/contracts/contracts", "<repo>/contracts/lib".
 --> node_modules/@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol:8:1:
  |
8 | import "../../../utils/cryptography/draft-EIP712.sol";

This error happens when solc was able to resolve symlinked files, but they're outside the Foundry workspace (./contracts).

Adding node_modules to allow_paths in foundry.toml grants solc access to that directory, and it will be able to read it:

# This translates to `solc --allow-paths ../node_modules`
allow_paths = ["../node_modules"]

Note that the path is relative to the Foundry workspace. See also solc allowed-paths

How to install from source?

NOTE: please ensure your rust version is up-to-date: rustup update. Current msrv = "1.62"

git clone https://github.com/foundry-rs/foundry
cd foundry
# install cast + forge
cargo install --path ./cli --profile local --bins --locked --force
# install anvil
cargo install --path ./anvil --profile local --locked --force

Or via cargo install --git https://github.com/foundry-rs/foundry --profile local --locked foundry-cli anvil.

I'm getting Permission denied (os error 13)

If you see an error like

Failed to create artifact parent folder "/.../MyProject/out/IsolationModeMagic.sol": Permission denied (os error 13)

Then there's likely a folder permission issue. Ensure user has write access in the project root's folder.

It has been reported that on linux, canonicalizing paths can result in weird paths (/_1/...). This can be resolved by nuking the entire project folder and initializing again.

References

forge Commands

General Commands

forge

NAME

forge - Build, test, fuzz, debug and deploy Solidity contracts.

SYNOPSIS

forge [options] command [args]
forge [options] --version
forge [options] --help

DESCRIPTION

This program is a set of tools to build, test, fuzz, debug and deploy Solidity smart contracts.

COMMANDS

General Commands

forge help
    Display help information about Forge.

forge completions
    Generate shell autocompletions for Forge.

Project Commands

forge init
    Create a new Forge project.

forge install
    Install one or multiple dependencies.

forge update
    Update one or multiple dependencies.

forge remove
    Remove one or multiple dependencies.

forge config
    Display the current config.

forge remappings
    Get the automatically inferred remappings for this project.

forge tree
    Display a tree visualization of the project's dependency graph.

Build Commands

forge build
    Build the project's smart contracts.

forge clean
    Remove the build artifacts and cache directories.

forge inspect
    Get specialized information about a smart contract.

Test Commands

forge test
    Run the project's tests.

forge snapshot
    Create a snapshot of each test's gas usage.

Deploy Commands

forge create
    Deploy a smart contract.

forge verify-contract
    Verify smart contracts on Etherscan.

forge verify-check
    Check verification status on Etherscan.

forge flatten
    Flatten a source file and all of its imports into one file.

Utility Commands

forge debug
    Debug a single smart contract as a script.

forge bind
    Generate Rust bindings for smart contracts.

forge cache
    Manage the Foundry cache.

forge cache clean
    Cleans cached data from ~/.foundry.

forge cache ls
    Shows cached data from ~/.foundry.

forge script
    Run a smart contract as a script, building transactions that can be sent onchain.

forge upload-selectors
    Uploads abi of given contract to https://sig.eth.samczsun.com function selector database.

OPTIONS

Special Options

-V
--version
    Print version info and exit.

Common Options

-h
--help
    Prints help information.

FILES

~/.foundry/
    Default location for Foundry's "home" directory where it stores various files.

~/.foundry/bin/
    Binaries installed using foundryup will be located here.

~/.foundry/cache/
    Forge's cache directory, where it stores cached block data and more.

~/.foundry/foundry.toml
    The global Foundry config.

~/.svm
    The location of the Forge-managed solc binaries.

EXAMPLES

  1. Create a new Forge project:

    forge init hello_foundry
    
  2. Build a project:

    forge build
    
  3. Run a project's tests:

    forge test
    

BUGS

See https://github.com/foundry-rs/foundry/issues for issues.

forge help

NAME

forge-help - Get help for a Forge command

SYNOPSIS

forge help [subcommand]

DESCRIPTION

Prints a help message for the given command.

EXAMPLES

  1. Get help for a command:

    forge help build
    
  2. Help is also available with the --help flag:

    forge build --help
    

SEE ALSO

forge

forge completions

NAME

forge-completions - Generate shell completions script

SYNOPSIS

forge completions shell

DESCRIPTION

Generates a shell completions script for the given shell.

Supported shells are:

  • bash
  • elvish
  • fish
  • powershell
  • zsh

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Generate shell completions script for zsh:
    forge completions zsh > $HOME/.oh-my-zsh/completions/_forge
    

SEE ALSO

forge

Project Commands

forge init

NAME

forge-init - Create a new Forge project.

SYNOPSIS

forge init [options] [root]

DESCRIPTION

Create a new Forge project in the directory root (by default the current working directory).

The default template creates the following project layout:

.
├── 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

However, it is possible to create a project from another using --template.

By default, forge init will also initialize a new git repository, install some submodules and create an initial commit message.

If you do not want this behavior, pass --no-git.

OPTIONS

Init Options

--force
    Create the project even if the specified root directory is not empty.

-t template
--template template
    The template to start from.

--vscode
    Create a .vscode/settings.json file with Solidity settings, and generate a remappings.txt file.

--offline
    Do not install dependencies from the network.

VCS Options

--no-commit
    Do not create an initial commit.

--no-git
    Do not create a git repository.

Display Options

-q
--quiet
    Do not print any messages.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Create a new project:

    forge init hello_foundry
    
  2. Create a new project, but do not create a git repository:

    forge init --no-git hello_foundry
    
  3. Forcibly create a new project in a non-empty directory:

    forge init --force 
    

SEE ALSO

forge

forge install

NAME

forge-install - Install one or more dependencies.

SYNOPSIS

forge install [options] [deps...]

DESCRIPTION

Install one or more dependencies.

Dependencies are installed as git submodules. If you do not want this behavior, pass --no-git.

If no arguments are provided, then existing dependencies are installed.

Dependencies can be a raw URL (https://foo.com/dep), an SSH URL (git@github.com:owner/repo), or the path to a GitHub repository (owner/repo). Additionally, a ref can be added to the dependency path to install a specific version of a dependency.

A ref can be:

  • A branch: owner/repo@master
  • A tag: owner/repo@v1.2.3
  • A commit: owner/repo@8e8128

The ref defaults to master.

You can also choose the name of the folder the dependency will be in. By default, the folder name is the name of the repository. If you want to change the name of the folder, prepend <folder>= to the dependency.

OPTIONS

Project Options

--root path
    The project's root path. By default, this is the root directory of the current git repository, or the current working directory.

VCS Options

--no-commit
    Do not create a commit.

--no-git
    Install without adding the dependency as a submodule.

Display Options

-q
--quiet
    Do not print any messages.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Install a dependency:

    forge install transmissions11/solmate
    
  2. Install a specific version of a dependency:

    forge install transmissions11/solmate@v7
    
  3. Install multiple dependencies:

    forge install transmissions11/solmate@v7 OpenZeppelin/openzeppelin-contracts
    
  4. Install a dependency without creating a submodule:

    forge install --no-git transmissions11/solmate
    
  5. Install a dependency in a specific folder:

    forge install soulmate=transmissions11/solmate
    

SEE ALSO

forge, forge update, forge remove

forge update

NAME

forge-update - Update one or more dependencies.

SYNOPSIS

forge update [options] [dep]

DESCRIPTION

Update one or more dependencies.

The argument dep is a path to the dependency you want to update. Forge will update to the latest version on the ref you specified for the dependency when you ran forge install.

If no argument is provided, then all dependencies are updated.

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Update a dependency:

    forge update lib/solmate
    
  2. Update all dependencies:

    forge update
    

SEE ALSO

forge, forge install, forge remove

forge remove

NAME

forge-remove - Remove one or multiple dependencies.

SYNOPSIS

forge remove [options] [deps...]

DESCRIPTION

Remove one or multiple dependencies.

Dependencies can be a raw URL (https://foo.com/dep), the path to a GitHub repository (owner/repo) or the path to the dependency in the project tree.

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Remove a dependency by path:

    forge remove lib/solmate
    
  2. Remove a dependency by GitHub repository name:

    forge remove dapphub/solmate
    

SEE ALSO

forge, forge install, forge update

forge config

NAME

forge-config - Display the current config.

SYNOPSIS

forge config [options]

DESCRIPTION

Display the current config.

This command can be used to create a new basic foundry.toml or to see what values are currently set, taking environment variables and the global configuration file into account.

The command supports almost all flags of the other commands in Forge to allow overriding values in the displayed configuration.

OPTIONS

Config Options

--basic
    Prints a basic configuration file.

--fix
    Attempts to fix any configuration warnings.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Create a new basic config:

    forge config > foundry.toml
    
  2. Enable FFI in foundry.toml:

    forge config --ffi > foundry.toml
    

SEE ALSO

forge

forge remappings

NAME

forge-remappings - Get the automatically inferred remappings for the project.

SYNOPSIS

forge remappings [options]

DESCRIPTION

Get the automatically inferred remappings for the project.

OPTIONS

Project Options

--root path
    The project's root path. By default, this is the root directory of the current git repository, or the current working directory.

--lib-path path
    The path to the library folder.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Create a remappings.txt file from the inferred remappings:
    forge remappings > remappings.txt
    

SEE ALSO

forge

forge tree

NAME

forge-tree - Display a tree visualization of the project's dependency graph.

SYNOPSIS

forge tree [options]

DESCRIPTION

Display a visualization of the project's dependency graph.

$ forge tree
src/OpenZeppelinNft.sol 0.8.10
├── lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol ^0.8.0
│   ├── lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol ^0.8.0
│   │   └── lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol ^0.8.0
│   ├── lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol ^0.8.0
│   ├── lib/openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Metadata.sol ^0.8.0
│   │   └── lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol ^0.8.0 (*)
│   ├── lib/openzeppelin-contracts/contracts/utils/Address.sol ^0.8.1
│   ├── lib/openzeppelin-contracts/contracts/utils/Context.sol ^0.8.0
│   ├── lib/openzeppelin-contracts/contracts/utils/Strings.sol ^0.8.0
│   └── lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol ^0.8.0
│       └── lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol ^0.8.0
├── lib/openzeppelin-contracts/contracts/utils/Strings.sol ^0.8.0
├── lib/openzeppelin-contracts/contracts/security/PullPayment.sol ^0.8.0
│   └── lib/openzeppelin-contracts/contracts/utils/escrow/Escrow.sol ^0.8.0
│       ├── lib/openzeppelin-contracts/contracts/access/Ownable.sol ^0.8.0
│       │   └── lib/openzeppelin-contracts/contracts/utils/Context.sol ^0.8.0
│       └── lib/openzeppelin-contracts/contracts/utils/Address.sol ^0.8.1
└── lib/openzeppelin-contracts/contracts/access/Ownable.sol ^0.8.0 (*)
src/SolmateNft.sol 0.8.10
├── lib/solmate/src/tokens/ERC721.sol >=0.8.0
├── lib/openzeppelin-contracts/contracts/utils/Strings.sol ^0.8.0
├── lib/openzeppelin-contracts/contracts/security/PullPayment.sol ^0.8.0 (*)
└── lib/openzeppelin-contracts/contracts/access/Ownable.sol ^0.8.0 (*)
test/OpenZeppelinNft.t.sol 0.8.10
├── lib/forge-std/src/Test.sol >=0.6.0 <0.9.0
│   ├── lib/forge-std/src/Script.sol >=0.6.0 <0.9.0
│   │   ├── lib/forge-std/src/Vm.sol >=0.6.0
│   │   ├── lib/forge-std/src/console.sol >=0.4.22 <0.9.0
│   │   └── lib/forge-std/src/console2.sol >=0.4.22 <0.9.0
│   └── lib/forge-std/lib/ds-test/src/test.sol >=0.5.0
├── src/OpenZeppelinNft.sol 0.8.10 (*)
└── lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol ^0.8.0
test/SolmateNft.sol 0.8.10
├── lib/forge-std/src/Test.sol >=0.6.0 <0.9.0 (*)
└── src/SolmateNft.sol 0.8.10 (*)

OPTIONS

Flatten Options

--charset charset
    Character set to use in output: utf8, ascii. Default: utf8

--no-dedupe
    Do not de-duplicate (repeats all shared dependencies)

Project Options

--build-info
    Generate build info files.

--build-info-path path
    Output path to directory that build info files will be written to.

--root path
    The project's root path. By default, this is the root directory of the current git repository, or the current working directory.

-c path
--contracts path
    The contracts source directory.
    Environment: DAPP_SRC

--lib-paths path
    The path to the library folder.

-r remappings
--remappings remappings
    The project's remappings.

    The parameter is a comma-separated list of remappings in the format <source>=<dest>.

--cache-path path
    The path to the compiler cache.

--config-path file
    Path to the config file.

--hh
--hardhat
    This is a convenience flag, and is the same as passing --contracts contracts --lib-paths node-modules.

Common Options

-h
--help
    Prints help information.

SEE ALSO

forge

Build Commands

forge build

NAME

forge-build - Build the project's smart contracts.

SYNOPSIS

forge build [options]

DESCRIPTION

Build the project's smart contracts.

The command will try to detect the latest version that can compile your project by looking at the version requirements of all your contracts and dependencies.

You can override this behaviour by passing --no-auto-detect. Alternatively, you can pass --use <SOLC_VERSION>.

If the command detects that the Solidity compiler version it is using to build is not installed, it will download it and install it in ~/.svm. You can disable this behavior by passing --offline.

The build is incremental, and the build cache is saved in cache/ in the project root by default. If you want to clear the cache, pass --force, and if you want to change the cache directory, pass --cache-path <PATH>.

Build Modes

There are three build modes:

  • Just compilation (default): Builds the project and saves the contract artifacts in out/ (or the path specified by --out <PATH>).
  • Size mode (--sizes): Builds the project, displays the size of non-test contracts and exits with code 1 if any of them are above the size limit.
  • Name mode (--names): Builds the project, displays the names of the contracts and exits.

The Optimizer

You can enable the optimizer by passing --optimize, and you can adjust the number of optimizer runs by passing --optimizer-runs <RUNS>.

You can also opt-in to the Solidity IR compilation pipeline by passing --via-ir. Read more about the IR pipeline in the Solidity docs.

By default, the optimizer is enabled and runs for 200 cycles.

Conditional Optimizer Usage

Many projects use the solc optimizer, either through the standard compilation pipeline or the IR pipeline. But in some cases, the optimizer can significantly slow down compilation speeds.

A config file for a project using the optimizer may look like this for regular compilation:

[profile.default]
solc-version = "0.8.17"
optimizer = true
optimizer-runs = 10_000_000

Or like this for via-ir:

[profile.default]
solc-version = "0.8.17"
via_ir = true

To reduce compilation speeds during development and testing, one approach is to have a lite profile that has the optimizer off and use this for development/testing cycle. The updated config file for regular compilation may look like this:

[profile.default]
solc-version = "0.8.17"
optimizer = true
optimizer-runs = 10_000_000

[profile.lite]
optimizer = false

Or like this for via-ir:

[profile.default]
solc-version = "0.8.17"
via_ir = true

[profile.lite.optimizer_details.yulDetails]
optimizerSteps = ''

When setup like this, forge build (or forge test / forge script) still uses the standard profile, so by default a forge script invocation will deploy your contracts with the production setting. Running FOUNDRY_PROFILE=lite forge build (and again, same for the test and script commands) will use the lite profile to reduce compilation times.

There are additional optimizer details you can configure, see the Additional Optimizer Settings section below for more info.

Artifacts

You can add extra output from the Solidity compiler to your artifacts by passing --extra-output <SELECTOR>.

The selector is a path in the Solidity compiler output, which you can read more about in the Solidity docs.

You can also write some of the compiler output to separate files by passing --extra-output-files <SELECTOR>.

Valid selectors for --extra-output-files are:

  • metadata: Written as a metadata.json file in the artifacts directory
  • ir: Written as a .ir file in the artifacts directory
  • irOptimized: Written as a .iropt file in the artifacts directory
  • ewasm: Written as a .ewasm file in the artifacts directory
  • evm.assembly: Written as a .asm file in the artifacts directory

Watch Mode

The command can be run in watch mode by passing --watch [PATH...], which will rebuild every time a watched file or directory is changed. The source directory is watched by default.

Sparse Mode (experimental)

Sparse mode only compiles files that match certain criteria.

By default, this filter applies to files that have not been changed since the last build, but for commands that take file filters (e.g. forge test), sparse mode will only recompile files that match the filter.

Sparse mode is opt-in until the feature is stabilized. To opt-in to sparse mode and try it out, set sparse_mode in your configuration file.

Additional Optimizer Settings

The optimizer can be fine-tuned with additional settings. Simply set the optimizer_details table in your configuration file. For example:

[profile.default.optimizer_details]
constantOptimizer = true
yul = true

[profile.default.optimizer_details.yulDetails]
stackAllocation = true
optimizerSteps = 'dhfoDgvulfnTUtnIf'

See the compiler input description documentation for more information on available settings (specifically settings.optimizer.details).

Revert Strings

You can control how revert strings are generated by the compiler. By default, only user supplied revert strings are included in the bytecode, but there are other options:

  • strip: Removes all revert strings (if possible, i.e. if literals are used) keeping side-effects.
  • debug: Injects strings for compiler-generated internal reverts, implemented for ABI encoders V1 and V2 for now.
  • verboseDebug: Appends further information to user-supplied revert strings (not yet implemented).

Additional Model Checker settings

Solidity's built-in model checker is an opt-in module that can be enabled via the ModelChecker object.

See Compiler Input Description settings.modelChecker and the model checker's options.

The module is available in solc release binaries for OSX and Linux. The latter requires the z3 library version [4.8.8, 4.8.14] to be installed in the system (SO version 4.8).

Similarly to the optimizer settings above, the model_checker settings must be prefixed with the profile they correspond to: [profile.default.model_checker] belongs to the [profile.default].

## foundry.toml
[profile.default.model_checker]
contracts = { '/path/to/project/src/Contract.sol' = [ 'Contract' ] }
engine = 'chc'
timeout = 10000
targets = [ 'assert' ]

The fields above are recommended when using the model checker. Setting which contract should be verified is extremely important, otherwise all available contracts will be verified which can consume a lot of time. The recommended engine is chc, but bmc and all (runs both) are also accepted. It is also important to set a proper timeout (given in milliseconds), since the default time given to the underlying solvers may not be enough. If no verification targets are given, only assertions will be checked.

The model checker will run when forge build is invoked, and will show findings as warnings if any.

OPTIONS

Build Options

--names     Print compiled contract names.

--sizes     Print compiled non-test contract sizes, exiting with code 1 if any of them are above the size limit.

Cache Options

--force
    Clear the cache and artifacts folder and recompile.

Linker Options

--libraries libraries
    Set pre-linked libraries.

    The parameter must be in the format <remapped path to lib>:<library name>:<address>, e.g. src/Contract.sol:Library:0x....

    Can also be set in your configuration file as libraries = ["<path>:<lib name>:<address>"].

Compiler Options

--optimize
    Activate the Solidity optimizer.

--optimizer-runs runs
    The number of optimizer runs.

--via-ir
    Use the Yul intermediate representation compilation pipeline.

--revert-strings
    How to treat revert and require reason strings.

--use solc_version
    Specify the solc version, or a path to a local solc, to build with.

    Valid values are in the format x.y.z, solc:x.y.z or path/to/solc.

--offline
    Do not access the network. Missing solc versions will not be installed.

--no-auto-detect
    Do not auto-detect solc.

--ignored-error-codes error_codes
    Ignore solc warnings by error code. The parameter is a comma-separated list of error codes.

--extra-output selector
    Extra output to include in the contract's artifact.

    Example keys: abi, storageLayout, evm.assembly, ewasm, ir, ir-optimized, metadata.

    For a full description, see the Solidity docs.

--extra-output-files selector
    Extra output to write to separate files.

    Example keys: abi, storageLayout, evm.assembly, ewasm, ir, ir-optimized, metadata.

    For a full description, see the Solidity docs.

--evm-version version
    The target EVM version.

Project Options

--build-info
    Generate build info files.

--build-info-path path
    Output path to directory that build info files will be written to.

--root path
    The project's root path. By default, this is the root directory of the current git repository, or the current working directory.

-c path
--contracts path
    The contracts source directory.
    Environment: DAPP_SRC

--lib-paths path
    The path to the library folder.

-r remappings
--remappings remappings
    The project's remappings.

    The parameter is a comma-separated list of remappings in the format <source>=<dest>.

--cache-path path
    The path to the compiler cache.

--config-path file
    Path to the config file.

--hh
--hardhat
    This is a convenience flag, and is the same as passing --contracts contracts --lib-paths node-modules.

-o path
--out path
    The project's artifacts directory.

--silent
    Suppress all output.

Watch Options

-w [path...]
--watch [path...]
    Watch specific file(s) or folder(s).

    By default, the project's source directory is watched.

-d delay
--delay delay
    File update debounce delay.

    During the delay, incoming change events are accumulated and only once the delay has passed, is an action taken.
    Note that this does not mean a command will be started: if --no-restart is given and a command is already running, the outcome of the action will be to do nothing.

    Defaults to 50ms. Parses as decimal seconds by default, but using an integer with the ms suffix may be more convenient.

    When using --poll mode, you'll want a larger duration, or risk overloading disk I/O.

--no-restart
    Do not restart the command while it's running.

--run-all
    Explicitly re-run the command on all files when a change is made.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Build the project:

    forge build
    
  2. Build the project with solc 0.6.0:

    forge build --use solc:0.6.0
    
  3. Build the project with additional artifact output:

    forge build --extra-output evm.assembly
    
  4. Build the project in watch mode:

    forge build --watch
    

SEE ALSO

forge, forge clean, forge inspect

forge clean

NAME

forge-clean - Remove the build artifacts and cache directories.

SYNOPSIS

forge clean [options]

DESCRIPTION

Remove the build artifacts and cache directories.

OPTIONS

Clean Options

--root path
    The project's root path. Defaults to the current working directory.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Clean artifacts and cache in a project:
    forge clean
    

SEE ALSO

forge

forge inspect

NAME

forge-inspect - Get specialized information about a smart contract

SYNOPSIS

forge inspect [options] contract_name field

DESCRIPTION

Get specialized information about a smart contract.

The field to inspect (field) can be any of:

  • abi
  • b/bytes/bytecode
  • deployedBytecode/deployed_bytecode/deployed-bytecode/deployedbytecode/deployed
  • assembly/asm
  • asmOptimized/assemblyOptimized/assemblyoptimized/assembly_optimized/asmopt/assembly-optimized/asmo/asm-optimized/asmoptimized/asm_optimized
  • methods/methodidentifiers/methodIdentifiers/method_identifiers/method-identifiers/mi
  • gasEstimates/gas/gas_estimates/gas-estimates/gasestimates
  • storageLayout/storage_layout/storage-layout/storagelayout/storage
  • devdoc/dev-doc/devDoc
  • ir
  • ir-optimized/irOptimized/iroptimized/iro/iropt
  • metadata/meta
  • userdoc/userDoc/user-doc
  • ewasm/e-wasm

OPTIONS

--pretty
    Pretty print the selected field, if supported.

Cache Options

--force
    Clear the cache and artifacts folder and recompile.

Linker Options

--libraries libraries
    Set pre-linked libraries.

    The parameter must be in the format <remapped path to lib>:<library name>:<address>, e.g. src/Contract.sol:Library:0x....

    Can also be set in your configuration file as libraries = ["<path>:<lib name>:<address>"].

Compiler Options

--optimize
    Activate the Solidity optimizer.

--optimizer-runs runs
    The number of optimizer runs.

--via-ir
    Use the Yul intermediate representation compilation pipeline.

--revert-strings
    How to treat revert and require reason strings.

--use solc_version
    Specify the solc version, or a path to a local solc, to build with.

    Valid values are in the format x.y.z, solc:x.y.z or path/to/solc.

--offline
    Do not access the network. Missing solc versions will not be installed.

--no-auto-detect
    Do not auto-detect solc.

--ignored-error-codes error_codes
    Ignore solc warnings by error code. The parameter is a comma-separated list of error codes.

--extra-output selector
    Extra output to include in the contract's artifact.

    Example keys: abi, storageLayout, evm.assembly, ewasm, ir, ir-optimized, metadata.

    For a full description, see the Solidity docs.

--extra-output-files selector
    Extra output to write to separate files.

    Example keys: abi, storageLayout, evm.assembly, ewasm, ir, ir-optimized, metadata.

    For a full description, see the Solidity docs.

--evm-version version
    The target EVM version.

Project Options

--build-info
    Generate build info files.

--build-info-path path
    Output path to directory that build info files will be written to.

--root path
    The project's root path. By default, this is the root directory of the current git repository, or the current working directory.

-c path
--contracts path
    The contracts source directory.
    Environment: DAPP_SRC

--lib-paths path
    The path to the library folder.

-r remappings
--remappings remappings
    The project's remappings.

    The parameter is a comma-separated list of remappings in the format <source>=<dest>.

--cache-path path
    The path to the compiler cache.

--config-path file
    Path to the config file.

--hh
--hardhat
    This is a convenience flag, and is the same as passing --contracts contracts --lib-paths node-modules.

-o path
--out path
    The project's artifacts directory.

--silent
    Suppress all output.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Inspect the bytecode of a contract:

    forge inspect MyContract bytecode
    
  2. Inspect the storage layout of a contract:

    forge inspect MyContract storage
    

SEE ALSO

forge, forge build

Test Commands

forge test

NAME

forge-test - Run the project's tests.

SYNOPSIS

forge test [options]

DESCRIPTION

Run the project's tests.

Forking

It is possible to run the tests in a forked environment by passing --fork-url <URL>.

When the tests are running in a forked environment, you can access all the state of the forked chain as you would if you had deployed the contracts. Cheatcodes are still available.

You can also specify a block number to fork from by passing --fork-block-number <BLOCK>. When forking from a specific block, the chain data is cached to ~/.foundry/cache. If you do not want to cache the chain data, pass --no-storage-caching.

Traces that cannot be decoded by local contracts when running in a forked environment (e.g. calls to contracts that live on mainnet, like tokens) can optionally be decoded using Etherscan. To use Etherscan for trace decoding, set ETHERSCAN_API_KEY or pass --etherscan-api-key <KEY>.

Debugging

It is possible to run a test in an interactive debugger. To start the debugger, pass --debug <TEST>.

If multiple tests match the specified pattern, you must use other test filters in order to reduce the matching number of tests to exactly 1.

If the test is a unit test, it is immediately opened in the debugger.

If the test is a fuzz test, the fuzz test is run and the debugger is opened on the first failing scenario. If there are no failing scenarios for the fuzz test, the debugger is opened on the last scenario.

More information on the debugger can be found in the debugger chapter.

Gas reports

You can generate a gas report by passing --gas-report.

More information on gas reports can be found in the gas reports chapter.

List

It is possible to list the tests without running them. You can pass --json to make it easier for outside extensions to parse structured content.

OPTIONS

Test Options

-m regex
--match regex
    Only run test functions matching the specified regex pattern.
    Deprecated: See --match-test.

--match-test regex
    Only run test functions matching the specified regex pattern.

--no-match-test regex
    Only run test functions that do not match the specified regex pattern.

--match-contract regex
    Only run tests in contracts matching the specified regex pattern.

--no-match-contract regex
    Only run tests in contracts that do not match the specified regex pattern.

--match-path glob
    Only run tests in source files matching the specified glob pattern.

--no-match-path glob
    Only run tests in source files that do not match the specified glob pattern.

--debug regex
    Run a test in the debugger.

    The argument passed to this flag is the name of the test function you want to run, and it works the same as --match-test.

    If more than one test matches your specified criteria, you must add additional filters until only one test is found (see --match-contract and --match-path).

    The matching test will be opened in the debugger regardless of the outcome of the test.

    If the matching test is a fuzz test, then it will open the debugger on the first failure case. If the fuzz test does not fail, it will open the debugger on the last fuzz case.

    For more fine-grained control of which fuzz case is run, see forge debug.

--gas-report
    Print a gas report.

--allow-failure
    Exit with code 0 even if a test fails.

--etherscan-api-key key
    Etherscan API key. If set, traces are decoded using Etherscan if --fork-url is also set.
    Environment: ETHERSCAN_API_KEY

EVM Options

-f url
--rpc-url url
--fork-url url
    Fetch state over a remote endpoint instead of starting from an empty state.

    If you want to fetch state from a specific block number, see --fork-block-number.

--fork-block-number block
    Fetch state from a specific block number over a remote endpoint. See --fork-url.

--fork-retry-backoff <BACKOFF>
     Initial retry backoff on encountering errors.

--no-storage-caching
    Explicitly disables the use of RPC caching.

    All storage slots are read entirely from the endpoint. See --fork-url.

-v
--verbosity
    Verbosity of the EVM.

    Pass multiple times to increase the verbosity (e.g. -v, -vv, -vvv).

    Verbosity levels:
    - 2: Print logs for all tests
    - 3: Print execution traces for failing tests
    - 4: Print execution traces for all tests, and setup traces for failing tests
    - 5: Print execution and setup traces for all tests

--sender address
    The address which will be executing tests

--initial-balance balance
    The initial balance of deployed contracts

--ffi
    Enables the FFI cheatcode

Executor Options

--base-fee <FEE>
--block-base-fee-per-gas <FEE>
    The base fee in a block (in wei).

--block-coinbase address
    The coinbase of the block.

--block-difficulty difficulty
    The block difficulty.

--block-gas-limit gas_limit
    The block gas limit.

--block-number block
    The block number.

--block-timestamp timestamp
    The timestamp of the block (in seconds).

--chain-id chain_id
    The chain ID.

--gas-limit gas_limit
    The block gas limit.

--gas-price gas_price
    The gas price (in wei).

--tx-origin address
    The transaction origin.

Cache Options

--force
    Clear the cache and artifacts folder and recompile.

Linker Options

--libraries libraries
    Set pre-linked libraries.

    The parameter must be in the format <remapped path to lib>:<library name>:<address>, e.g. src/Contract.sol:Library:0x....

    Can also be set in your configuration file as libraries = ["<path>:<lib name>:<address>"].

Compiler Options

--optimize
    Activate the Solidity optimizer.

--optimizer-runs runs
    The number of optimizer runs.

--via-ir
    Use the Yul intermediate representation compilation pipeline.

--revert-strings
    How to treat revert and require reason strings.

--use solc_version
    Specify the solc version, or a path to a local solc, to build with.

    Valid values are in the format x.y.z, solc:x.y.z or path/to/solc.

--offline
    Do not access the network. Missing solc versions will not be installed.

--no-auto-detect
    Do not auto-detect solc.

--ignored-error-codes error_codes
    Ignore solc warnings by error code. The parameter is a comma-separated list of error codes.

--extra-output selector
    Extra output to include in the contract's artifact.

    Example keys: abi, storageLayout, evm.assembly, ewasm, ir, ir-optimized, metadata.

    For a full description, see the Solidity docs.

--extra-output-files selector
    Extra output to write to separate files.

    Example keys: abi, storageLayout, evm.assembly, ewasm, ir, ir-optimized, metadata.

    For a full description, see the Solidity docs.

--evm-version version
    The target EVM version.

Project Options

--build-info
    Generate build info files.

--build-info-path path
    Output path to directory that build info files will be written to.

--root path
    The project's root path. By default, this is the root directory of the current git repository, or the current working directory.

-c path
--contracts path
    The contracts source directory.
    Environment: DAPP_SRC

--lib-paths path
    The path to the library folder.

-r remappings
--remappings remappings
    The project's remappings.

    The parameter is a comma-separated list of remappings in the format <source>=<dest>.

--cache-path path
    The path to the compiler cache.

--config-path file
    Path to the config file.

--hh
--hardhat
    This is a convenience flag, and is the same as passing --contracts contracts --lib-paths node-modules.

-o path
--out path
    The project's artifacts directory.

--silent
    Suppress all output.

Watch Options

-w [path...]
--watch [path...]
    Watch specific file(s) or folder(s).

    By default, the project's source directory is watched.

-d delay
--delay delay
    File update debounce delay.

    During the delay, incoming change events are accumulated and only once the delay has passed, is an action taken.
    Note that this does not mean a command will be started: if --no-restart is given and a command is already running, the outcome of the action will be to do nothing.

    Defaults to 50ms. Parses as decimal seconds by default, but using an integer with the ms suffix may be more convenient.

    When using --poll mode, you'll want a larger duration, or risk overloading disk I/O.

--no-restart
    Do not restart the command while it's running.

--run-all
    Explicitly re-run the command on all files when a change is made.

Display Options

-j
--json
     Print the deployment information as JSON.

--list
    List tests instead of running them.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Run the tests:

    forge test
    
  2. Open a test in the debugger:

    forge test --debug testSomething
    
  3. Generate a gas report:

    forge test --gas-report
    
  4. Only run tests in test/Contract.t.sol in the BigTest contract that start with testFail:

    forge test --match-path test/Contract.t.sol --match-contract BigTest \
      --match-test "testFail*"
    
  5. List tests in desired format

    forge test --list
    forge test --list --json
    forge test --list --json --match-test "testFail*" | tail -n 1 | json_pp
    

SEE ALSO

forge, forge build, forge snapshot

forge snapshot

NAME

forge-snapshot - Create a snapshot of each test's gas usage.

SYNOPSIS

forge snapshot [options]

DESCRIPTION

Create a snapshot of each test's gas usage.

The results are written to a file named .gas-snapshot. You can change the name of the file by passing --snap <PATH>.

Fuzz tests are included by default in the snapshot. They use a static seed to achieve deterministic results.

Snapshots can be compared with --diff and --check. The first flag will output a diff, and the second will output a diff and exit with code 1 if the snapshots do not match.

OPTIONS

Snapshot Options

--asc
Sort results by gas used (ascending).

--desc
    Sort results by gas used (descending).

--min min_gas
    Only include tests that used more gas that the given amount.

--max max_gas
    Only include tests that used less gas that the given amount.

--diff path
    Output a diff against a pre-existing snapshot.

    By default the comparison is done with .gas-snapshot.

--check path
    Compare against a pre-existing snapshot, exiting with code 1 if they do not match.

    Outputs a diff if the snapshots do not match.

    By default the comparison is done with .gas-snapshot.

--snap path
    Output file for the snapshot. Default: .gas-snapshot.

Test Options

-m regex
--match regex
    Only run test functions matching the specified regex pattern.
    Deprecated: See --match-test.

--match-test regex
    Only run test functions matching the specified regex pattern.

--no-match-test regex
    Only run test functions that do not match the specified regex pattern.

--match-contract regex
    Only run tests in contracts matching the specified regex pattern.

--no-match-contract regex
    Only run tests in contracts that do not match the specified regex pattern.

--match-path glob
    Only run tests in source files matching the specified glob pattern.

--no-match-path glob
    Only run tests in source files that do not match the specified glob pattern.

--debug regex
    Run a test in the debugger.

    The argument passed to this flag is the name of the test function you want to run, and it works the same as --match-test.

    If more than one test matches your specified criteria, you must add additional filters until only one test is found (see --match-contract and --match-path).

    The matching test will be opened in the debugger regardless of the outcome of the test.

    If the matching test is a fuzz test, then it will open the debugger on the first failure case. If the fuzz test does not fail, it will open the debugger on the last fuzz case.

    For more fine-grained control of which fuzz case is run, see forge debug.

--gas-report
    Print a gas report.

--allow-failure
    Exit with code 0 even if a test fails.

--etherscan-api-key key
    Etherscan API key. If set, traces are decoded using Etherscan if --fork-url is also set.
    Environment: ETHERSCAN_API_KEY

EVM Options

-f url
--rpc-url url
--fork-url url
    Fetch state over a remote endpoint instead of starting from an empty state.

    If you want to fetch state from a specific block number, see --fork-block-number.

--fork-block-number block
    Fetch state from a specific block number over a remote endpoint. See --fork-url.

--fork-retry-backoff <BACKOFF>
     Initial retry backoff on encountering errors.

--no-storage-caching
    Explicitly disables the use of RPC caching.

    All storage slots are read entirely from the endpoint. See --fork-url.

-v
--verbosity
    Verbosity of the EVM.

    Pass multiple times to increase the verbosity (e.g. -v, -vv, -vvv).

    Verbosity levels:
    - 2: Print logs for all tests
    - 3: Print execution traces for failing tests
    - 4: Print execution traces for all tests, and setup traces for failing tests
    - 5: Print execution and setup traces for all tests

--sender address
    The address which will be executing tests

--initial-balance balance
    The initial balance of deployed contracts

--ffi
    Enables the FFI cheatcode

Executor Options

--base-fee <FEE>
--block-base-fee-per-gas <FEE>
    The base fee in a block (in wei).

--block-coinbase address
    The coinbase of the block.

--block-difficulty difficulty
    The block difficulty.

--block-gas-limit gas_limit
    The block gas limit.

--block-number block
    The block number.

--block-timestamp timestamp
    The timestamp of the block (in seconds).

--chain-id chain_id
    The chain ID.

--gas-limit gas_limit
    The block gas limit.

--gas-price gas_price
    The gas price (in wei).

--tx-origin address
    The transaction origin.

Cache Options

--force
    Clear the cache and artifacts folder and recompile.

Linker Options

--libraries libraries
    Set pre-linked libraries.

    The parameter must be in the format <remapped path to lib>:<library name>:<address>, e.g. src/Contract.sol:Library:0x....

    Can also be set in your configuration file as libraries = ["<path>:<lib name>:<address>"].

Compiler Options

--optimize
    Activate the Solidity optimizer.

--optimizer-runs runs
    The number of optimizer runs.

--via-ir
    Use the Yul intermediate representation compilation pipeline.

--revert-strings
    How to treat revert and require reason strings.

--use solc_version
    Specify the solc version, or a path to a local solc, to build with.

    Valid values are in the format x.y.z, solc:x.y.z or path/to/solc.

--offline
    Do not access the network. Missing solc versions will not be installed.

--no-auto-detect
    Do not auto-detect solc.

--ignored-error-codes error_codes
    Ignore solc warnings by error code. The parameter is a comma-separated list of error codes.

--extra-output selector
    Extra output to include in the contract's artifact.

    Example keys: abi, storageLayout, evm.assembly, ewasm, ir, ir-optimized, metadata.

    For a full description, see the Solidity docs.

--extra-output-files selector
    Extra output to write to separate files.

    Example keys: abi, storageLayout, evm.assembly, ewasm, ir, ir-optimized, metadata.

    For a full description, see the Solidity docs.

--evm-version version
    The target EVM version.

Project Options

--build-info
    Generate build info files.

--build-info-path path
    Output path to directory that build info files will be written to.

--root path
    The project's root path. By default, this is the root directory of the current git repository, or the current working directory.

-c path
--contracts path
    The contracts source directory.
    Environment: DAPP_SRC

--lib-paths path
    The path to the library folder.

-r remappings
--remappings remappings
    The project's remappings.

    The parameter is a comma-separated list of remappings in the format <source>=<dest>.

--cache-path path
    The path to the compiler cache.

--config-path file
    Path to the config file.

--hh
--hardhat
    This is a convenience flag, and is the same as passing --contracts contracts --lib-paths node-modules.

-o path
--out path
    The project's artifacts directory.

--silent
    Suppress all output.

Display Options

-j
--json
     Print the deployment information as JSON.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Create a snapshot:

    forge snapshot
    
  2. Generate a diff:

    forge snapshot --diff
    
  3. Check that the snapshots match:

    forge snapshot --check
    

SEE ALSO

forge, forge test

Deploy Commands

forge create

NAME

forge-create - Deploy a smart contract.

SYNOPSIS

forge create [options] contract

DESCRIPTION

Deploy a smart contract.

The path to the contract is in the format <path>:<contract>, e.g. src/Contract.sol:Contract.

You can specify constructor arguments with --constructor-args. Alternatively, you can specify a file containing space-separated constructor arguments with --constructor-args-path.

Dynamic linking is not supported: you should predeploy your libraries and manually specify their addresses (see --libraries).

ℹ️ Note

The --constructor-args flag must be positioned last in the command, since it takes multiple values.

OPTIONS

Build Options

--constructor-args args...
    The constructor arguments.

--constructor-args-path file
    The path to a file containing the constructor arguments.

--verify
    Verify contract after creation. Runs forge verify-contract with the appropriate parameters.

--verifier name
    The verification provider. Available options: etherscan, sourcify & blockscout. Default: etherscan.

--verifier-url url
    The optional verifier url for submitting the verification request.
    Environment: VERIFIER_URL

--unlocked
    Send via eth_sendTransaction using the --from argument or $ETH_FROM as sender.

Transaction Options

--gas-limit gas_limit
    Gas limit for the transaction.

--gas-price price
    Gas price for the transaction, or max fee per gas for EIP1559 transactions.

--priority-gas-price price
    Max priority fee per gas for EIP1559 transactions.

--value value
    Ether to send in the transaction.

    Either specified as an integer (wei), or as a string with a unit, for example:
    - 1ether
    - 10gwei
    - 0.01ether

--nonce nonce
    Nonce for the transaction.

--legacy
    Send a legacy transaction instead of an EIP1559 transaction.

    This is automatically enabled for common networks without EIP1559.

WALLET OPTIONS - RAW:

-i
--interactive <NUM>
     Open an interactive prompt to enter your private key. Takes a value for the number of keys to enter.
     Defaults to 0.

--mnemonic-derivation-path <PATHS>
     The wallet derivation path. Works with both --mnemonic-path and hardware wallets.

--mnemonic-indexes <INDEXES>
     Use the private key from the given mnemonic index. Used with --mnemonic-paths.
     Defaults to 0.

--mnemonic-passphrase <PASSPHRASE>
     Use a BIP39 passphrases for the mnemonic.

--mnemonic <PATHS>
     Use the mnemonic phrases or mnemonic files at the specified paths.

--private-key <RAW_PRIVATE_KEY>
     Use the provided private key.

--private-keys <RAW_PRIVATE_KEYS>
     Use the provided private keys.

Wallet Options - Keystore

--keystore path
    Use the keystore in the given folder or file.
    Environment: ETH_KEYSTORE

--password password
    The keystore password. Used with --keystore.

Wallet Options - Hardware Wallet

-t
--trezor
    Use a Trezor hardware wallet.

-l
--ledger
    Use a Ledger hardware wallet.

Wallet Options - Remote

-f address
--from address
    Sign the transaction with the specified account on the RPC.
    Environment: ETH_FROM

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

--flashbots
    Use the Flashbots RPC URL (https://rpc.flashbots.net).

Etherscan Options

--chain chain_name
    The Etherscan chain.

--etherscan-api-key key
    Etherscan API key, or the key of an Etherscan configuration table.
    Environment: ETHERSCAN_API_KEY

Cache Options

--force
    Clear the cache and artifacts folder and recompile.

Linker Options

--libraries libraries
    Set pre-linked libraries.

    The parameter must be in the format <remapped path to lib>:<library name>:<address>, e.g. src/Contract.sol:Library:0x....

    Can also be set in your configuration file as libraries = ["<path>:<lib name>:<address>"].

Compiler Options

--optimize
    Activate the Solidity optimizer.

--optimizer-runs runs
    The number of optimizer runs.

--via-ir
    Use the Yul intermediate representation compilation pipeline.

--revert-strings
    How to treat revert and require reason strings.

--use solc_version
    Specify the solc version, or a path to a local solc, to build with.

    Valid values are in the format x.y.z, solc:x.y.z or path/to/solc.

--offline
    Do not access the network. Missing solc versions will not be installed.

--no-auto-detect
    Do not auto-detect solc.

--ignored-error-codes error_codes
    Ignore solc warnings by error code. The parameter is a comma-separated list of error codes.

--extra-output selector
    Extra output to include in the contract's artifact.

    Example keys: abi, storageLayout, evm.assembly, ewasm, ir, ir-optimized, metadata.

    For a full description, see the Solidity docs.

--extra-output-files selector
    Extra output to write to separate files.

    Example keys: abi, storageLayout, evm.assembly, ewasm, ir, ir-optimized, metadata.

    For a full description, see the Solidity docs.

--evm-version version
    The target EVM version.

Project Options

--build-info
    Generate build info files.

--build-info-path path
    Output path to directory that build info files will be written to.

--root path
    The project's root path. By default, this is the root directory of the current git repository, or the current working directory.

-c path
--contracts path
    The contracts source directory.
    Environment: DAPP_SRC

--lib-paths path
    The path to the library folder.

-r remappings
--remappings remappings
    The project's remappings.

    The parameter is a comma-separated list of remappings in the format <source>=<dest>.

--cache-path path
    The path to the compiler cache.

--config-path file
    Path to the config file.

--hh
--hardhat
    This is a convenience flag, and is the same as passing --contracts contracts --lib-paths node-modules.

-o path
--out path
    The project's artifacts directory.

--silent
    Suppress all output.

Display Options

-j
--json
     Print the deployment information as JSON.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Deploy a contract with no constructor arguments:

    forge create src/Contract.sol:ContractWithNoConstructor
    
  2. Deploy a contract with two constructor arguments:

    forge create src/Contract.sol:MyToken --constructor-args "My Token" "MT"
    

SEE ALSO

forge, forge build, forge verify-contract

forge verify-contract

NAME

forge-verify-contract - Verify smart contracts on a chosen verification provider.

SYNOPSIS

forge verify-contract [options] address contract

DESCRIPTION

Verifies a smart contract on a chosen verification provider.

You must provide:

  • The contract address
  • The contract name or the path to the contract (read below) In case of Etherscan verification, you must also provide:
  • Your Etherscan API key, either by passing it as an argument or setting ETHERSCAN_API_KEY

To find the exact compiler version, run ~/.svm/x.y.z/solc-x.y.z --version and search for the 8 hex digits in the version string here.

The path to the contract is in the format <path>:<contract>, e.g. src/Contract.sol:Contract.

By default, smart contracts are verified in a multi-file fashion. If you want to flatten the contract before verifying, pass --flatten.

This command will try to compile the source code of the flattened contract if --flatten is passed before verifying. If you do not want that, pass --force.

You can specify ABI-encoded constructor arguments with --constructor-args. Alternatively, you can specify a file containing space-separated constructor arguments with --constructor-args-path. (Note that cache must be enabled in the config for the latter to work.)

OPTIONS

Verify Contract Options

--verifier name
    The verification provider. Available options: etherscan, sourcify & blockscout. Default: etherscan.

--verifier-url url
    The optional verifier url for submitting the verification request.
    Environment: VERIFIER_URL

--compiler-version version
    The compiler version used to build the smart contract.

    To find the exact compiler version, run ~/.svm/x.y.z/solc-x.y.z --version where x and y are major and minor version numbers respectively, then search for the 8 hex digits in the version string here.

--num-of-optimizations num
--optimizer-runs num
    The number of optimization runs used to build the smart contract.

--constructor-args args
    The ABI-encoded constructor arguments. Conflicts with --constructor-args-path.

--constructor-args-path file
    The path to a file containing the constructor arguments. Conflicts with --constructor-args.

--chain-id chain
--chain chain
    The ID or name of the chain the contract is deployed to.
    Default: mainnet

--flatten
    Flag indicating whether to flatten the source code before verifying.

    If this flag is not provided, the JSON standard input will be used instead.

-f
--force
    Do not compile the flattened smart contract before verifying.

--delay delay
    Optional timeout to apply in between attempts in seconds. Defaults to 3.

--retries retries
    Number of attempts for retrying. Defaults to 15.

--watch
    Wait for verification result after submission.
    Automatically runs forge verify-check until the verification either fails or succeeds.

Project Options

--build-info
    Generate build info files.

--build-info-path path
    Output path to directory that build info files will be written to.

--root path
    The project's root path. By default, this is the root directory of the current git repository, or the current working directory.

-c path
--contracts path
    The contracts source directory.
    Environment: DAPP_SRC

--lib-paths path
    The path to the library folder.

-r remappings
--remappings remappings
    The project's remappings.

    The parameter is a comma-separated list of remappings in the format <source>=<dest>.

--cache-path path
    The path to the compiler cache.

--config-path file
    Path to the config file.

--hh
--hardhat
    This is a convenience flag, and is the same as passing --contracts contracts --lib-paths node-modules.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Verify a contract with JSON standard input on Etherscan

    forge verify-contract <address> SomeContract --watch
    
    
  2. Verify a contract on a custom Sourcify instance

    forge verify-contract --verifier sourcify \
      --verifier-url http://localhost:5000 <address> SomeContract
    
  3. Verify a flattened contract built with solc v0.8.11+commit.d7f03943:

    forge verify-contract --flatten --watch --compiler-version "v0.8.11+commit.d7f03943" \
      --constructor-args $(cast abi-encode "constructor(string,string,uint256,uint256)" "ForgeUSD" "FUSD" 18 1000000000000000000000) \
      <address> MyToken
    
  4. Verify a flattened contract by specifying constructor arguments in a file:

    forge verify-contract --flatten --watch --compiler-version "v0.8.11+commit.d7f03943" \
      --constructor-args-path constructor-args.txt <address> src/Token.sol:MyToken
    

    where constructor-args.txt contains the following content:

    ForgeUSD FUSD 18 1000000000000000000000
    

SEE ALSO

forge, forge create, forge flatten, forge verify-check

forge verify-check

NAME

forge-verify-check - Check verification status on a chosen verification provider.

SYNOPSIS

forge verify-check [options] id [etherscan_key]

The id is the verification identifier. For Etherscan & Bloxroute - it is the submission GUID, for Sourcify - it's the contract address.

DESCRIPTION

Check verification status on a chosen verification provider.

For Etherscan, you must provide an Etherscan API key, either by passing it as an argument or setting ETHERSCAN_API_KEY

OPTIONS

Verify Contract Options

--verifier name
    The verification provider. Available options: etherscan, sourcify & blockscout. Default: etherscan.

--verifier-url url
    The optional verifier url for submitting the verification request.
    Environment: VERIFIER_URL

--chain-id chain_id
    The chain ID the contract is deployed to (either a number or a chain name).
    Default: mainnet

--delay delay
    Optional timeout to apply in between attempts in seconds. Defaults to 3.

--retries retries
    Number of attempts for retrying. Defaults to 15.

Common Options

-h
--help
    Prints help information.

SEE ALSO

forge, forge create, forge verify-contract

forge flatten

NAME

forge-flatten - Flatten a source file and all of its imports into one file.

SYNOPSIS

forge flatten [options] file

DESCRIPTION

Flatten a source file and all of its imports into one file.

If --output <FILE> is not set, then the flattened contract will be output to stdout.

OPTIONS

Flatten Options

-o file
--output file
    The path to output the flattened contract. If not specified, the flattened contract will be output to stdout.

Project Options

--build-info
    Generate build info files.

--build-info-path path
    Output path to directory that build info files will be written to.

--root path
    The project's root path. By default, this is the root directory of the current git repository, or the current working directory.

-c path
--contracts path
    The contracts source directory.
    Environment: DAPP_SRC

--lib-paths path
    The path to the library folder.

-r remappings
--remappings remappings
    The project's remappings.

    The parameter is a comma-separated list of remappings in the format <source>=<dest>.

--cache-path path
    The path to the compiler cache.

--config-path file
    Path to the config file.

--hh
--hardhat
    This is a convenience flag, and is the same as passing --contracts contracts --lib-paths node-modules.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Flatten src/Contract.sol:

    forge flatten src/Contract.sol
    
  2. Flatten src/Contract.sol and write the result to src/Contract.flattened.sol:

    forge flatten --output src/Contract.flattened.sol src/Contract.sol
    

SEE ALSO

forge, forge verify-contract

Utility Commands

forge debug

NAME

forge-debug - Debug a single smart contract as a script.

SYNOPSIS

forge debug [options] path [args...]

DESCRIPTION

Debugs a single smart contract located in the source file (path) as a script.

If multiple contracts are in the specified source file, you must pass --target-contract to specify what contract you want to run.

Calls

After the script is deployed to the internal EVM a call is made to a function with the signature setUp(), if present.

By default, the script is assumed to be contained in a function with the signature run(). If you wish to run a different function, pass --sig <SIGNATURE>.

The signature can be a fragment (<function name>(<types>)), or raw calldata.

If you pass a fragment, and the function has parameters, you can add the call parameters to the end of the command (args).

Forking

It is possible to run the script in a forked environment by passing --fork-url <URL>.

When the script is running in a forked environment, you can access all the state of the forked chain as you would if you had deployed the script. Cheatcodes are still available.

You can also specify a block number to fork from by passing --fork-block-number <BLOCK>. When forking from a specific block, the chain data is cached to ~/.foundry/cache. If you do not want to cache the chain data, pass --no-storage-caching.

Debugging

It is possible to run the script in an interactive debugger. To start the debugger, pass --debug.

More information on the debugger can be found in the debugger chapter.

OPTIONS

Debug Options

--target-contract contract_name
    The name of the contract you want to run

-s signature
--sig signature
    The signature of the function you want to call in the contract, or raw calldata. Default: run()

--debug
    Open the script in the debugger.

EVM Options

-f url
--rpc-url url
--fork-url url
    Fetch state over a remote endpoint instead of starting from an empty state.

    If you want to fetch state from a specific block number, see --fork-block-number.

--fork-block-number block
    Fetch state from a specific block number over a remote endpoint. See --fork-url.

--fork-retry-backoff <BACKOFF>
     Initial retry backoff on encountering errors.

--no-storage-caching
    Explicitly disables the use of RPC caching.

    All storage slots are read entirely from the endpoint. See --fork-url.

-v
--verbosity
    Verbosity of the EVM.

    Pass multiple times to increase the verbosity (e.g. -v, -vv, -vvv).

    Verbosity levels:
    - 2: Print logs for all tests
    - 3: Print execution traces for failing tests
    - 4: Print execution traces for all tests, and setup traces for failing tests
    - 5: Print execution and setup traces for all tests

--sender address
    The address which will be executing tests

--initial-balance balance
    The initial balance of deployed contracts

--ffi
    Enables the FFI cheatcode

Executor Options

--base-fee <FEE>
--block-base-fee-per-gas <FEE>
    The base fee in a block (in wei).

--block-coinbase address
    The coinbase of the block.

--block-difficulty difficulty
    The block difficulty.

--block-gas-limit gas_limit
    The block gas limit.

--block-number block
    The block number.

--block-timestamp timestamp
    The timestamp of the block (in seconds).

--chain-id chain_id
    The chain ID.

--gas-limit gas_limit
    The block gas limit.

--gas-price gas_price
    The gas price (in wei).

--tx-origin address
    The transaction origin.

Cache Options

--force
    Clear the cache and artifacts folder and recompile.

Linker Options

--libraries libraries
    Set pre-linked libraries.

    The parameter must be in the format <remapped path to lib>:<library name>:<address>, e.g. src/Contract.sol:Library:0x....

    Can also be set in your configuration file as libraries = ["<path>:<lib name>:<address>"].

Compiler Options

--optimize
    Activate the Solidity optimizer.

--optimizer-runs runs
    The number of optimizer runs.

--via-ir
    Use the Yul intermediate representation compilation pipeline.

--revert-strings
    How to treat revert and require reason strings.

--use solc_version
    Specify the solc version, or a path to a local solc, to build with.

    Valid values are in the format x.y.z, solc:x.y.z or path/to/solc.

--offline
    Do not access the network. Missing solc versions will not be installed.

--no-auto-detect
    Do not auto-detect solc.

--ignored-error-codes error_codes
    Ignore solc warnings by error code. The parameter is a comma-separated list of error codes.

--extra-output selector
    Extra output to include in the contract's artifact.

    Example keys: abi, storageLayout, evm.assembly, ewasm, ir, ir-optimized, metadata.

    For a full description, see the Solidity docs.

--extra-output-files selector
    Extra output to write to separate files.

    Example keys: abi, storageLayout, evm.assembly, ewasm, ir, ir-optimized, metadata.

    For a full description, see the Solidity docs.

--evm-version version
    The target EVM version.

Project Options

--build-info
    Generate build info files.

--build-info-path path
    Output path to directory that build info files will be written to.

--root path
    The project's root path. By default, this is the root directory of the current git repository, or the current working directory.

-c path
--contracts path
    The contracts source directory.
    Environment: DAPP_SRC

--lib-paths path
    The path to the library folder.

-r remappings
--remappings remappings
    The project's remappings.

    The parameter is a comma-separated list of remappings in the format <source>=<dest>.

--cache-path path
    The path to the compiler cache.

--config-path file
    Path to the config file.

--hh
--hardhat
    This is a convenience flag, and is the same as passing --contracts contracts --lib-paths node-modules.

-o path
--out path
    The project's artifacts directory.

--silent
    Suppress all output.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Execute the run() function in a contract:

    forge debug src/Contract.sol
    
  2. Open a script in the debugger:

    forge debug src/Contract.sol --debug
    
  3. Execute the foo() function in a contract:

    forge debug src/Contract.sol --sig "foo()"
    
  4. Execute a contract with a function that takes parameters:

    forge debug src/Contract.sol --sig "foo(string,uint256)" "hello" 100
    
  5. Execute a contract with raw calldata:

    forge debug src/Contract.sol --sig "0x..."
    

SEE ALSO

forge, forge test

forge bind

NAME

forge-bind - Generate Rust bindings for smart contracts.

SYNOPSIS

forge bind [options]

DESCRIPTION

Generates Rust bindings for smart contracts using ethers-rs.

The bindings are generated from the project's artifacts, which by default is ./out/. If you want to generate bindings for artifacts in a different directory, pass --bindings-path <PATH>.

There are three output options:

  • Generate bindings in a crate (default)
  • Generate bindings in a module by passing --module
  • Generate bindings in a single file by passing --single-file

By default, the command will check that existing bindings are correct and exit accordingly. You can overwrite the existing bindings by passing --overwrite.

OPTIONS

Project Options

-b path
--bindings-path path
    The project's root path. By default, this is the root directory of the current git repository, or the current working directory.

--crate-name name
    The name of the Rust crate to generate, if you are generating a crate (default).
    This should be a valid crates.io crate name.

    Default: foundry-contracts

--crate-version semver
    The version of the Rust crate to generate, if you are generating a crate (default).
    This should be a standard semver version string.

    Default: 0.0.1

--module
    Generate the bindings as a module instead of a crate.

--single-file
    Generate bindings as a single file.

--overwrite
    Overwrite existing generated bindings. By default, the command will check that the bindings are correct, and then exit.
    If --overwrite is passed, it will instead delete and overwrite the bindings.

--root path
    The project's root path. By default, this is the root directory of the current git repository, or the current working directory.

--skip-cargo-toml
    Skip Cargo.toml consistency checks.
    This allows you to manage the ethers version without giving up on consistency checks.
    An example would be if you use additional features of ethers like ws, ipc, or rustls and get an ethers-providers version mismatch.

--skip-build
    Skips running forge build before generating binding.
    This allows you to skip the default forge build step that's executed first and instead generate bindings using the already existing artifacts.

--select-all
    By default all contracts ending with Test or Script are excluded. This will explicitly generate bindings for all contracts. Conflicts with --select and --skip.

--select regex+
    Create bindings only for contracts whose names match the specified filter(s). Conflicts with --skip.

--skip regex+
    Create bindings only for contracts whose names do not match the specified filter(s). Conflicts with --select.

Common Options

-h
--help
    Prints help information.

Cache Options

--force
    Clear the cache and artifacts folder and recompile.

Linker Options

--libraries libraries
    Set pre-linked libraries.

    The parameter must be in the format <remapped path to lib>:<library name>:<address>, e.g. src/Contract.sol:Library:0x....

    Can also be set in your configuration file as libraries = ["<path>:<lib name>:<address>"].

Compiler Options

--optimize
    Activate the Solidity optimizer.

--optimizer-runs runs
    The number of optimizer runs.

--via-ir
    Use the Yul intermediate representation compilation pipeline.

--revert-strings
    How to treat revert and require reason strings.

--use solc_version
    Specify the solc version, or a path to a local solc, to build with.

    Valid values are in the format x.y.z, solc:x.y.z or path/to/solc.

--offline
    Do not access the network. Missing solc versions will not be installed.

--no-auto-detect
    Do not auto-detect solc.

--ignored-error-codes error_codes
    Ignore solc warnings by error code. The parameter is a comma-separated list of error codes.

--extra-output selector
    Extra output to include in the contract's artifact.

    Example keys: abi, storageLayout, evm.assembly, ewasm, ir, ir-optimized, metadata.

    For a full description, see the Solidity docs.

--extra-output-files selector
    Extra output to write to separate files.

    Example keys: abi, storageLayout, evm.assembly, ewasm, ir, ir-optimized, metadata.

    For a full description, see the Solidity docs.

--evm-version version
    The target EVM version.

Project Options

--build-info
    Generate build info files.

--build-info-path path
    Output path to directory that build info files will be written to.

--root path
    The project's root path. By default, this is the root directory of the current git repository, or the current working directory.

-c path
--contracts path
    The contracts source directory.
    Environment: DAPP_SRC

--lib-paths path
    The path to the library folder.

-r remappings
--remappings remappings
    The project's remappings.

    The parameter is a comma-separated list of remappings in the format <source>=<dest>.

--cache-path path
    The path to the compiler cache.

--config-path file
    Path to the config file.

--hh
--hardhat
    This is a convenience flag, and is the same as passing --contracts contracts --lib-paths node-modules.

-o path
--out path
    The project's artifacts directory.

--silent
    Suppress all output.

SEE ALSO

forge

forge cache

NAME

forge-cache - Manage the Foundry cache.

SYNOPSIS

forge cache [options] command [args]
forge cache [options] --version
forge cache [options] --help

DESCRIPTION

This program is a set of tools to manage the Foundry cache.

COMMANDS

forge cache clean
    Cleans cached data from ~/.foundry.

forge cache ls
    Shows cached data from ~/.foundry.

OPTIONS

Special Options

-V
--version
    Print version info and exit.

Common Options

-h
--help
    Prints help information.

forge cache clean

NAME

forge-cache-clean - Cleans cached data from ~/.foundry.

SYNOPSIS

forge cache clean [options] [--] [chains..]

DESCRIPTION

Removes files in the ~/.foundry/cache folder which is used to cache Etherscan verification status and block data.

OPTIONS

-b
--blocks
    One or more block numbers separated by comma with no spaces

--etherscan     A boolean flag that specifies to only remove the block explorer portion of the cache

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Remove the entire cache (also, forge cache clean is an alias for this)

    forge cache clean all
    
  2. Remove the entire block explorer cache

    forge cache clean all --etherscan
    
  3. Remove cache data for a specific chain, by name

    forge cache clean rinkeby
    
  4. Remove cache data for a specific block number on a specific chain. Does not work if chain is all

    forge cache clean rinkeby -b 150000
    
  5. Remove block explorer cache data for a specific chain. Does not work if --blocks are specified.

    forge cache clean rinkeby --etherscan
    
  6. Specify multiple chains

    forge cache clean rinkeby mainnet
    
  7. Specify multiple blocks

    forge cache clean rinkeby --blocks 530000,9000000,9200000
    

SEE ALSO

forge, forge cache

forge cache ls

NAME

forge-cache-ls - Shows cached data from ~/.foundry.

SYNOPSIS

forge cache ls [chains..]

DESCRIPTION

Lists what is in the ~/.foundry/cache folder currently.

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Show the entire cache (also, forge cache ls is an alias for this)

    forge cache ls all
    
  2. Show cache data for a specific chain, by name

    forge cache ls rinkeby
    
  3. Specify multiple chains

    forge cache ls rinkeby mainnet
    

SEE ALSO

forge, forge cache

forge script

NAME

forge-script - Run a smart contract as a script, building transactions that can be sent onchain.

SYNOPSIS

forge script [options] path [args...]

DESCRIPTION

Run a smart contract as a script, building transactions that can be sent onchain.

Scripts can be used to apply state transitions on live contracts, or deploy and initialize a complex set of smart contracts using Solidity.

OPTIONS

--broadcast
    Broadcasts the transactions.

--debug
    Open the script in the debugger. Takes precedence over broadcast.

-g
--gas-estimate-multiplier multiplier
    Relative percentage by which to multiply all gas estimates. (i.e. set to 200 to double them)     Default: 130

--json
    Output results in JSON format.
    Note: The output is under development and prone to change.

--legacy
    Use legacy transactions instead of EIP1559 ones. This is auto-enabled for common networks without EIP1559.

--resume
    Resumes submitting transactions that failed or timed-out previously.

-s
--sig signature
    The signature of the function you want to call in the contract, or raw calldata.
    Default: run()

--skip-simulation
    Skips on-chain simulation.

--slow
    Makes sure a transaction is sent, only after its previous one has been confirmed and succeeded.

--target-contract contract_name
    The name of the contract you want to run.

--with-gas-price price
    Sets the gas price for broadcasted legacy transactions, or the max fee for broadcasted EIP1559 transactions.
    Note: To set the gas price in the execution environment of the script use --gas-price instead (see below).

Etherscan Options

--chain chain_name
    The Etherscan chain.

--etherscan-api-key key
    Etherscan API key, or the key of an Etherscan configuration table.
    Environment: ETHERSCAN_API_KEY

Verification Options

--verify
    If it finds a matching broadcast log, it tries to verify every contract found in the receipts.

--verifier name
    The verification provider. Available options: etherscan, sourcify & blockscout. Default: etherscan.

--verifier-url url
    The optional verifier url for submitting the verification request.
    Environment: VERIFIER_URL

--delay delay
    Optional timeout to apply in between attempts in seconds. Defaults to 3.

--retries retries
    Number of attempts for retrying. Defaults to 15.

Cache Options

--force
    Clear the cache and artifacts folder and recompile.

Linker Options

--libraries libraries
    Set pre-linked libraries.

    The parameter must be in the format <remapped path to lib>:<library name>:<address>, e.g. src/Contract.sol:Library:0x....

    Can also be set in your configuration file as libraries = ["<path>:<lib name>:<address>"].

Compiler Options

--optimize
    Activate the Solidity optimizer.

--optimizer-runs runs
    The number of optimizer runs.

--via-ir
    Use the Yul intermediate representation compilation pipeline.

--revert-strings
    How to treat revert and require reason strings.

--use solc_version
    Specify the solc version, or a path to a local solc, to build with.

    Valid values are in the format x.y.z, solc:x.y.z or path/to/solc.

--offline
    Do not access the network. Missing solc versions will not be installed.

--no-auto-detect
    Do not auto-detect solc.

--ignored-error-codes error_codes
    Ignore solc warnings by error code. The parameter is a comma-separated list of error codes.

--extra-output selector
    Extra output to include in the contract's artifact.

    Example keys: abi, storageLayout, evm.assembly, ewasm, ir, ir-optimized, metadata.

    For a full description, see the Solidity docs.

--extra-output-files selector
    Extra output to write to separate files.

    Example keys: abi, storageLayout, evm.assembly, ewasm, ir, ir-optimized, metadata.

    For a full description, see the Solidity docs.

--evm-version version
    The target EVM version.

Project Options

--build-info
    Generate build info files.

--build-info-path path
    Output path to directory that build info files will be written to.

--root path
    The project's root path. By default, this is the root directory of the current git repository, or the current working directory.

-c path
--contracts path
    The contracts source directory.
    Environment: DAPP_SRC

--lib-paths path
    The path to the library folder.

-r remappings
--remappings remappings
    The project's remappings.

    The parameter is a comma-separated list of remappings in the format <source>=<dest>.

--cache-path path
    The path to the compiler cache.

--config-path file
    Path to the config file.

--hh
--hardhat
    This is a convenience flag, and is the same as passing --contracts contracts --lib-paths node-modules.

-o path
--out path
    The project's artifacts directory.

--silent
    Suppress all output.

Build Options

--names
    Print compiled contract names.

--sizes
    Print compiled non-test contract sizes, exiting with code 1 if any of them are above the size limit.

Watch Options

-w [path...]
--watch [path...]
    Watch specific file(s) or folder(s).

    By default, the project's source directory is watched.

-d delay
--delay delay
    File update debounce delay.

    During the delay, incoming change events are accumulated and only once the delay has passed, is an action taken.
    Note that this does not mean a command will be started: if --no-restart is given and a command is already running, the outcome of the action will be to do nothing.

    Defaults to 50ms. Parses as decimal seconds by default, but using an integer with the ms suffix may be more convenient.

    When using --poll mode, you'll want a larger duration, or risk overloading disk I/O.

--no-restart
    Do not restart the command while it's running.

--run-all
    Explicitly re-run the command on all files when a change is made.

Wallet Options - Raw

-i --interactives num
    Open an interactive prompt to enter your private key. Takes a value for the number of keys to enter.
    Default: 0

--mnemonic-indexes indexes
    Use the private key from the given mnemonic index. Used with --mnemonic-path.
    Default: 0

--mnemonic-paths paths
    Use the mnemonic file at the specified path(s).

--private-key raw_private_key
    Use the provided private key.

--private-keys raw_private_keys
    Use the provided private keys.

Wallet Options - Keystore

--keystores paths
    Use the keystores in the given folders or files.
    Environment: ETH_KEYSTORE

--password passwords
    The keystore passwords. Used with --keystore.

Wallet Options - Hardware Wallet

-t
--trezor
    Use a Trezor hardware wallet.

-l
--ledger
    Use a Ledger hardware wallet.

--hd-paths paths
    The derivation paths to use with hardware wallets.

Wallet Options - Remote

-a addresses
--froms addresses
    Sign the transaction with the specified accounts on the RPC.
    Environment: ETH_FROM

EVM Options

-f url
--rpc-url url
--fork-url url
    Fetch state over a remote endpoint instead of starting from an empty state.

    If you want to fetch state from a specific block number, see --fork-block-number.

--fork-block-number block
    Fetch state from a specific block number over a remote endpoint. See --fork-url.

--fork-retry-backoff <BACKOFF>
     Initial retry backoff on encountering errors.

--no-storage-caching
    Explicitly disables the use of RPC caching.

    All storage slots are read entirely from the endpoint. See --fork-url.

-v
--verbosity
    Verbosity of the EVM.

    Pass multiple times to increase the verbosity (e.g. -v, -vv, -vvv).

    Verbosity levels:
    - 2: Print logs for all tests
    - 3: Print execution traces for failing tests
    - 4: Print execution traces for all tests, and setup traces for failing tests
    - 5: Print execution and setup traces for all tests

--sender address
    The address which will be executing tests

--initial-balance balance
    The initial balance of deployed contracts

--ffi
    Enables the FFI cheatcode

Executor Options

--base-fee <FEE>
--block-base-fee-per-gas <FEE>
    The base fee in a block (in wei).

--block-coinbase address
    The coinbase of the block.

--block-difficulty difficulty
    The block difficulty.

--block-gas-limit gas_limit
    The block gas limit.

--block-number block
    The block number.

--block-timestamp timestamp
    The timestamp of the block (in seconds).

--chain-id chain_id
    The chain ID.

--gas-limit gas_limit
    The block gas limit.

--gas-price gas_price
    The gas price (in wei).

--tx-origin address
    The transaction origin.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Run BroadcastTest as a script, broadcasting generated transactions on-chain
    forge script ./test/Broadcast.t.sol --tc BroadcastTest --sig "deploy()" \
                 -vvv --fork-url $GOERLI_RPC_URL
    

forge upload-selectors

NAME

forge-upload-selectors - Uploads abi of given contract to https://sig.eth.samczsun.com function selector database.

SYNOPSIS

forge upload-selectors [options] contract

DESCRIPTION

Uploads abi of given contract to https://sig.eth.samczsun.com function selector database.

OPTIONS

Project Options

--build-info
    Generate build info files.

--build-info-path path
    Output path to directory that build info files will be written to.

--root path
    The project's root path. By default, this is the root directory of the current git repository, or the current working directory.

-c path
--contracts path
    The contracts source directory.
    Environment: DAPP_SRC

--lib-paths path
    The path to the library folder.

-r remappings
--remappings remappings
    The project's remappings.

    The parameter is a comma-separated list of remappings in the format <source>=<dest>.

--cache-path path
    The path to the compiler cache.

--config-path file
    Path to the config file.

--hh
--hardhat
    This is a convenience flag, and is the same as passing --contracts contracts --lib-paths node-modules.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Upload ABI to selector database
    forge upload-selectors LinearVestingVault
    

cast Commands

General Commands

cast

NAME

cast - Perform Ethereum RPC calls from the comfort of your command line.

SYNOPSIS

cast [options] command [args] cast [options] --version cast [options] --help

DESCRIPTION

This program is a set of tools to interact with Ethereum and perform conversions.

COMMANDS

General Commands

cast help     Display help information about Cast.

cast completions     Generate shell autocompletions for Cast.

Chain Commands

cast chain-id     Get the Ethereum chain ID.

cast chain     Get the symbolic name of the current chain.

cast client     Get the current client version.

Transaction Commands

cast publish     Publish a raw transaction to the network.

cast receipt     Get the transaction receipt for a transaction.

cast send     Sign and publish a transaction.

cast call     Perform a call on an account without publishing a transaction.

cast rpc      Perform a raw JSON-RPC request [aliases: rp]

cast tx     Get information about a transaction.

cast run     Runs a published transaction in a local environment and prints the trace.

cast estimate     Estimate the gas cost of a transaction.

cast access-list     Create an access list for a transaction.

Block Commands

cast find-block     Get the block number closest to the provided timestamp.

cast gas-price     Get the current gas price.

cast block-number     Get the latest block number.

cast basefee     Get the basefee of a block.

cast block     Get information about a block.

cast age     Get the timestamp of a block.

Account Commands

cast balance     Get the balance of an account in wei.

cast storage     Get the raw value of a contract's storage slot.

cast proof     Generate a storage proof for a given storage slot.

cast nonce     Get the nonce for an account.

cast code     Get the bytecode of a contract.

ENS Commands

cast lookup-address     Perform an ENS reverse lookup.

cast resolve-name     Perform an ENS lookup.

cast namehash     Calculate the ENS namehash of a name.

Etherscan Commands

cast etherscan-source     Get the source code of a contract from Etherscan.

ABI Commands

cast abi-encode     ABI encode the given function arguments, excluding the selector.

cast 4byte     Get the function signatures for the given selector from https://sig.eth.samczsun.com.

cast 4byte-decode     Decode ABI-encoded calldata using https://sig.eth.samczsun.com.

cast 4byte-event     Get the event signature for a given topic 0 from https://sig.eth.samczsun.com.

cast calldata     ABI-encode a function with arguments.

cast pretty-calldata     Pretty print calldata.

cast --abi-decode     Decode ABI-encoded input or output data.

cast --calldata-decode     Decode ABI-encoded input data.

cast upload-signature     Upload the given signatures to https://sig.eth.samczsun.com.

Conversion Commands

cast --format-bytes32-string     Formats a string into bytes32 encoding.

cast --from-bin     Convert binary data into hex data.

cast --from-fix     Convert a fixed point number into an integer.

cast --from-utf8     Convert UTF8 to hex.

cast --parse-bytes32-string     Parses a string from bytes32 encoding.

cast --to-ascii     Convert hex data to an ASCII string.

cast --to-base     Convert a number of one base to another.

cast --to-bytes32     Right-pads hex data to 32 bytes.

cast --to-fix     Convert an integer into a fixed point number.

cast --to-hexdata     Normalize the input to lowercase, 0x-prefixed hex.

cast --to-int256     Convert a number to a hex-encoded int256.

cast --to-uint256     Convert a number to a hex-encoded uint256.

cast --to-unit     Convert an ETH amount into another unit (ether, gwei, wei).

cast --to-wei     Convert an ETH amount to wei.

cast shl     Perform a left shifting operation.

cast shr     Perform a right shifting operation.

Utility Commands

cast sig     Get the selector for a function.

cast keccak     Hash arbitrary data using keccak-256.

cast compute-address     Compute the contract address from a given nonce and deployer address.

cast interface     Generate a Solidity interface from a given ABI.

cast index     Compute the storage slot for an entry in a mapping.

cast --concat-hex     Concatenate hex strings.

cast --max-int     Get the maximum i256 value.

cast --min-int     Get the minimum i256 value.

cast --max-uint     Get the maximum u256 value.

cast --to-checksum-address     Convert an address to a checksummed format (EIP-55).

Wallet Commands

cast wallet     Wallet management utilities.

cast wallet new     Create a new random keypair.

cast wallet address     Convert a private key to an address.

cast wallet sign     Sign a message.

cast wallet vanity     Generate a vanity address.

cast wallet verify     Verify the signature of a message.

OPTIONS

Special Options

-V --version     Print version info and exit.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Call a function on a contract:

    cast call 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 \
      "balanceOf(address)(uint256)" 0x...
    
  2. Decode raw calldata:

    cast --calldata-decode "transfer(address,uint256)" \
      0xa9059cbb000000000000000000000000e78388b4ce79068e89bf8aa7f218ef6b9ab0e9d0000000000000000000000000000000000000000000000000008a8e4b1a3d8000
    
  3. Encode calldata:

    cast calldata "someFunc(address,uint256)" 0x... 1
    

BUGS

See https://github.com/foundry-rs/foundry/issues for issues.

cast help

NAME

cast-help - Get help for a Cast command

SYNOPSIS

cast help [subcommand]

DESCRIPTION

Prints a help message for the given command.

EXAMPLES

  1. Get help for a command:

    cast help call
    
  2. Help is also available with the --help flag:

    cast call --help
    

SEE ALSO

cast

cast completions

NAME

cast-completions - Generate shell completions script

SYNOPSIS

cast completions shell

DESCRIPTION

Generates a shell completions script for the given shell.

Supported shells are:

  • bash
  • elvish
  • fish
  • powershell
  • zsh

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Generate shell completions script for zsh:
    cast completions zsh > $HOME/.oh-my-zsh/completions/_cast
    

SEE ALSO

cast

Chain Commands

cast chain-id

NAME

cast-chain-id - Get the Ethereum chain ID.

SYNOPSIS

cast chain-id [options]

DESCRIPTION

Get the Ethereum chain ID from the RPC endpoint we are connected to.

OPTIONS

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the chain ID when talking to $RPC:

    cast chain-id --rpc-url $RPC
    
  2. Get the chain ID when $ETH_RPC_URL is set:

    cast chain-id
    

SEE ALSO

cast, cast chain

cast chain

NAME

cast-chain - Get the symbolic name of the current chain.

SYNOPSIS

cast chain [options]

DESCRIPTION

Get the symbolic chain name from the RPC endpoint we are connected to.

OPTIONS

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the chain name when talking to $RPC:

    cast chain --rpc-url $RPC
    
  2. Get the chain name when $ETH_RPC_URL is set:

    cast chain
    

SEE ALSO

cast, cast chain-id

cast client

NAME

cast-client - Get the current client version.

SYNOPSIS

cast client [options]

DESCRIPTION

Get the current client version.

OPTIONS

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the current client version:
    cast client
    

SEE ALSO

cast

Transaction Commands

cast publish

NAME

cast-publish - Publish a raw transaction to the network.

SYNOPSIS

cast publish [options] tx

DESCRIPTION

Publish a raw pre-signed transaction to the network.

OPTIONS

Publish Options

--async
--cast-async
    Do not wait for a transaction receipt.
    Environment: CAST_ASYNC

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

--flashbots
    Use the Flashbots RPC URL (https://rpc.flashbots.net).

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Publish a pre-signed transaction:

    cast publish --rpc-url $RPC $TX
    
  2. Publish a pre-signed transaction with flashbots.

    cast publish --flashbots $TX
    

SEE ALSO

cast, cast call, cast send, cast receipt

cast receipt

NAME

cast-receipt - Get the transaction receipt for a transaction.

SYNOPSIS

cast receipt [options] tx_hash [field]

DESCRIPTION

Get the transaction receipt for a transaction.

If field is specified, then only the given field of the receipt is displayed.

OPTIONS

Receipt Options

--async
--cast-async
    Do not wait for the transaction receipt if it does not exist yet.
    Environment: CAST_ASYNC

-c confirmations
--confirmations confirmations
    Wait a number of confirmations before exiting. Default: 1.

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Display Options

-j
--json
     Print the deployment information as JSON.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get a transaction receipt:

    cast receipt $TX_HASH
    
  2. Get the block number the transaction was included in:

    cast receipt $TX_HASH blockNumber
    

SEE ALSO

cast, cast call, cast send, cast publish

cast send

NAME

cast-send - Sign and publish a transaction.

SYNOPSIS

cast send [options] to [sig] [args...]

DESCRIPTION

Sign and publish a transaction.

The destination (to) can be an ENS name or an address.

The signature (sig) can be:

  • A fragment: someFunction(uint256,bytes32)
  • A selector and encoded calldata: 0xcdba2fd40000000000000000000000000000000000000000000000000000000000007a69
  • Only the function name: in this case Cast will try to fetch the function signature from Etherscan

OPTIONS

Transaction Options

--gas-limit gas_limit
    Gas limit for the transaction.

--gas-price price
    Gas price for the transaction, or max fee per gas for EIP1559 transactions.

--priority-gas-price price
    Max priority fee per gas for EIP1559 transactions.

--value value
    Ether to send in the transaction.

    Either specified as an integer (wei), or as a string with a unit, for example:
    - 1ether
    - 10gwei
    - 0.01ether

--nonce nonce
    Nonce for the transaction.

--legacy
    Send a legacy transaction instead of an EIP1559 transaction.

    This is automatically enabled for common networks without EIP1559.

--resend
    Reuse the latest nonce of the sending account.

--create code [sig args...]
    Deploy a contract by specifying raw bytecode, in place of specifying a to address.

Receipt Options

--async
--cast-async
    Do not wait for the transaction receipt if it does not exist yet.
    Environment: CAST_ASYNC

-c confirmations
--confirmations confirmations
    Wait a number of confirmations before exiting. Default: 1.

WALLET OPTIONS - RAW:

-i
--interactive <NUM>
     Open an interactive prompt to enter your private key. Takes a value for the number of keys to enter.
     Defaults to 0.

--mnemonic-derivation-path <PATHS>
     The wallet derivation path. Works with both --mnemonic-path and hardware wallets.

--mnemonic-indexes <INDEXES>
     Use the private key from the given mnemonic index. Used with --mnemonic-paths.
     Defaults to 0.

--mnemonic-passphrase <PASSPHRASE>
     Use a BIP39 passphrases for the mnemonic.

--mnemonic <PATHS>
     Use the mnemonic phrases or mnemonic files at the specified paths.

--private-key <RAW_PRIVATE_KEY>
     Use the provided private key.

--private-keys <RAW_PRIVATE_KEYS>
     Use the provided private keys.

Wallet Options - Keystore

--keystore path
    Use the keystore in the given folder or file.
    Environment: ETH_KEYSTORE

--password password
    The keystore password. Used with --keystore.

Wallet Options - Hardware Wallet

-t
--trezor
    Use a Trezor hardware wallet.

-l
--ledger
    Use a Ledger hardware wallet.

Wallet Options - Remote

-f address
--from address
    Sign the transaction with the specified account on the RPC.
    Environment: ETH_FROM

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

--flashbots
    Use the Flashbots RPC URL (https://rpc.flashbots.net).

Etherscan Options

--chain chain_name
    The Etherscan chain.

--etherscan-api-key key
    Etherscan API key, or the key of an Etherscan configuration table.
    Environment: ETHERSCAN_API_KEY

Display Options

-j
--json
     Print the deployment information as JSON.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Send some ether to Vitalik using your Ledger:

    cast send --ledger vitalik.eth --value 0.1ether
    
  2. Call deposit(address token, uint256 amount) on a contract:

    cast send --ledger 0x... "deposit(address,uint256)" 0x... 1
    
  3. Call a function that expects a struct:

    contract Test {
        struct MyStruct {
            address addr;
            uint256 amount;
        }
        function myfunction(MyStruct memory t) public pure {}
    }
    

    Structs are encoded as tuples (see struct encoding)

    cast send 0x... "myfunction((address,uint256))" "(0x...,1)"
    

SEE ALSO

cast, cast call, cast publish, cast receipt, struct encoding

cast call

NAME

cast-call - Perform a call on an account without publishing a transaction.

SYNOPSIS

cast call [options] to sig [args...]

DESCRIPTION

Perform a call on an account without publishing a transaction.

The destination (to) can be an ENS name or an address.

The signature (sig) can be:

  • A fragment: someFunction(uint256,bytes32)
  • A selector and encoded calldata: 0xcdba2fd40000000000000000000000000000000000000000000000000000000000007a69
  • Only the function name: in this case Cast will try to fetch the function signature from Etherscan

OPTIONS

Query Options

-B block
--block block
    The block height you want to query at.

    Can be a block number, or any of the tags: earliest, latest or pending.

WALLET OPTIONS - RAW:

-i
--interactive <NUM>
     Open an interactive prompt to enter your private key. Takes a value for the number of keys to enter.
     Defaults to 0.

--mnemonic-derivation-path <PATHS>
     The wallet derivation path. Works with both --mnemonic-path and hardware wallets.

--mnemonic-indexes <INDEXES>
     Use the private key from the given mnemonic index. Used with --mnemonic-paths.
     Defaults to 0.

--mnemonic-passphrase <PASSPHRASE>
     Use a BIP39 passphrases for the mnemonic.

--mnemonic <PATHS>
     Use the mnemonic phrases or mnemonic files at the specified paths.

--private-key <RAW_PRIVATE_KEY>
     Use the provided private key.

--private-keys <RAW_PRIVATE_KEYS>
     Use the provided private keys.

Wallet Options - Keystore

--keystore path
    Use the keystore in the given folder or file.
    Environment: ETH_KEYSTORE

--password password
    The keystore password. Used with --keystore.

Wallet Options - Hardware Wallet

-t
--trezor
    Use a Trezor hardware wallet.

-l
--ledger
    Use a Ledger hardware wallet.

Wallet Options - Remote

-f address
--from address
    Sign the transaction with the specified account on the RPC.
    Environment: ETH_FROM

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

--flashbots
    Use the Flashbots RPC URL (https://rpc.flashbots.net).

Etherscan Options

--chain chain_name
    The Etherscan chain.

--etherscan-api-key key
    Etherscan API key, or the key of an Etherscan configuration table.
    Environment: ETHERSCAN_API_KEY

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Call balanceOf(address) on the WETH contract:

    cast call 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 \
      "balanceOf(address)(uint256)" 0x...
    
  2. Call tokenURI(uint256)(string) on the Tubby Cats NFT contract:

    export CONTRACT=0xca7ca7bcc765f77339be2d648ba53ce9c8a262bd
    export TOKEN_ID=19938
    cast call $CONTRACT "tokenURI(uint256)(string)" $TOKEN_ID
    
  3. Call getAmountsOut(uint,address[]) on the Uniswap v2 router contract:

    cast call 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D \
     "getAmountsOut(uint,address[])" 1 "[0x6b...0f,0xc0...c2]"
    

SEE ALSO

cast, cast send, cast publish, cast receipt

cast rpc

NAME

cast-rpc - Perform a raw JSON-RPC request

SYNOPSIS

cast rpc [options] METHOD [PARAMS...]

DESCRIPTION

Perform a simple JSON-RPC POST request for the given method and with the params

OPTIONS

Query Options

-r url
--rpc-rul url
    The URL of the provider

-w
--raw
    Pass the "params" as is      If --raw is passed the first PARAM will be taken as the value of "params". If no params are given, stdin will be used. For example:      rpc eth_getBlockByNumber '["0x123", false]' --raw      => {"method": "eth_getBlockByNumber", "params": ["0x123", false] ... }

EXAMPLES

  1. Get latest eth_getBlockByNumber on localhost:

    cast rpc eth_getBlockByNumber "latest" "false"
    
  2. Get eth_getTransactionByHash on localhost:

    cast rpc eth_getTransactionByHash 0x2642e960d3150244e298d52b5b0f024782253e6d0b2c9a01dd4858f7b4665a3f
    

cast tx

NAME

cast-tx - Get information about a transaction.

SYNOPSIS

cast tx [options] tx_hash [field]

DESCRIPTION

Get information about a transaction.

If field is specified, then only the given field of the transaction is displayed.

OPTIONS

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Display Options

-j
--json
     Print the deployment information as JSON.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get information about a transaction:

    cast tx $TX_HASH
    
  2. Get the sender of a transaction:

    cast tx $TX_HASH from
    

SEE ALSO

cast, cast receipt

cast run

NAME

cast-run - Runs a published transaction in a local environment and prints the trace.

SYNOPSIS

cast run [options] --rpc-url url tx_hash

DESCRIPTION

Runs a published transaction in a local environment and prints the trace.

By default, all transactions in the block prior to the transaction you want to replay are also replayed. If you want a quicker result, you can use --quick, however, results may differ from the live execution.

You can also open the transaction in a debugger by passing --debug.

OPTIONS

Run Options

--label label
    Labels an address in the trace.
    The format is <address>:<label>. Can be passed multiple times.

-q
--quick
    Executes the transaction only with the state from the previous block.
    May result in different results than the live execution!

-v
--verbose
    Addresses are fully displayed instead of being truncated.

-d
--debug
    Open the script in the debugger.

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Replay a transaction (a simple transfer):

    cast run 0xd15e0237413d7b824b784e1bbc3926e52f4726e5e5af30418803b8b327b4f8ca
    
  2. Replay a transaction, applied on top of the state of the previous block:

    cast run --quick \
      0xd15e0237413d7b824b784e1bbc3926e52f4726e5e5af30418803b8b327b4f8ca
    
  3. Replay a transaction with address labels:

    cast run \
      --label 0xc564ee9f21ed8a2d8e7e76c085740d5e4c5fafbe:sender \
      --label 0x40950267d12e979ad42974be5ac9a7e452f9505e:recipient \
      --label 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2:weth \
      0xd15e0237413d7b824b784e1bbc3926e52f4726e5e5af30418803b8b327b4f8ca
    
  4. Replay a transaction in the debugger:

    cast run --debug \
      0xd15e0237413d7b824b784e1bbc3926e52f4726e5e5af30418803b8b327b4f8ca
    

SEE ALSO

cast

cast estimate

NAME

cast-estimate - Estimate the gas cost of a transaction.

SYNOPSIS

cast estimate [options] to sig [args...]

DESCRIPTION

Estimate the gas cost of a transaction.

The destination (to) can be an ENS name or an address.

The signature (sig) can be:

  • A fragment: someFunction(uint256,bytes32)
  • A selector and encoded calldata: 0xcdba2fd40000000000000000000000000000000000000000000000000000000000007a69
  • Only the function name: in this case Cast will try to fetch the function signature from Etherscan

OPTIONS

Transaction Options

--value value
    Ether to send in the transaction.

    Either specified as an integer (wei), or as a string with a unit, for example:
    - 1ether
    - 10gwei
    - 0.01ether

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

--flashbots
    Use the Flashbots RPC URL (https://rpc.flashbots.net).

Etherscan Options

--chain chain_name
    The Etherscan chain.

--etherscan-api-key key
    Etherscan API key, or the key of an Etherscan configuration table.
    Environment: ETHERSCAN_API_KEY

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Estimate the gas cost of calling deposit() on the WETH contract:
    cast estimate 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 \
      --value 0.1ether "deposit()"
    

SEE ALSO

cast, cast send, cast publish, cast receipt

cast access-list

NAME

cast-access-list - Create an access list for a transaction.

SYNOPSIS

cast access-list [options] to sig [args...]

DESCRIPTION

Create an access list for a transaction.

The destination (to) can be an ENS name or an address.

The signature (sig) can be:

  • A fragment: someFunction(uint256,bytes32)
  • A selector and encoded calldata: 0xcdba2fd40000000000000000000000000000000000000000000000000000000000007a69
  • Only the function name: in this case Cast will try to fetch the function signature from Etherscan

OPTIONS

Query Options

-B block
--block block
    The block height you want to query at.

    Can be a block number, or any of the tags: earliest, latest or pending.

WALLET OPTIONS - RAW:

-i
--interactive <NUM>
     Open an interactive prompt to enter your private key. Takes a value for the number of keys to enter.
     Defaults to 0.

--mnemonic-derivation-path <PATHS>
     The wallet derivation path. Works with both --mnemonic-path and hardware wallets.

--mnemonic-indexes <INDEXES>
     Use the private key from the given mnemonic index. Used with --mnemonic-paths.
     Defaults to 0.

--mnemonic-passphrase <PASSPHRASE>
     Use a BIP39 passphrases for the mnemonic.

--mnemonic <PATHS>
     Use the mnemonic phrases or mnemonic files at the specified paths.

--private-key <RAW_PRIVATE_KEY>
     Use the provided private key.

--private-keys <RAW_PRIVATE_KEYS>
     Use the provided private keys.

Wallet Options - Keystore

--keystore path
    Use the keystore in the given folder or file.
    Environment: ETH_KEYSTORE

--password password
    The keystore password. Used with --keystore.

Wallet Options - Hardware Wallet

-t
--trezor
    Use a Trezor hardware wallet.

-l
--ledger
    Use a Ledger hardware wallet.

Wallet Options - Remote

-f address
--from address
    Sign the transaction with the specified account on the RPC.
    Environment: ETH_FROM

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

--flashbots
    Use the Flashbots RPC URL (https://rpc.flashbots.net).

Etherscan Options

--chain chain_name
    The Etherscan chain.

--etherscan-api-key key
    Etherscan API key, or the key of an Etherscan configuration table.
    Environment: ETHERSCAN_API_KEY

Common Options

-h
--help
    Prints help information.

SEE ALSO

cast, cast send, cast publish, cast call

Block Commands

cast find-block

NAME

cast-find-block - Get the block number closest to the provided timestamp.

SYNOPSIS

cast find-block [options] timestamp

DESCRIPTION

Get the block number closest to the provided timestamp.

OPTIONS

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the block number closest to New Years 2021
    cast find-block 1609459200
    

SEE ALSO

cast

cast gas-price

NAME

cast-gas-price - Get the current gas price.

SYNOPSIS

cast gas-price [options]

DESCRIPTION

Get the current gas price.

OPTIONS

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the current gas price:
    cast gas-price
    

SEE ALSO

cast, cast basefee

cast block-number

NAME

cast-block-number - Get the latest block number.

SYNOPSIS

cast block-number [options]

DESCRIPTION

Get the latest block number.

OPTIONS

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the latest block number:
    cast block-number
    

SEE ALSO

cast, cast block

cast basefee

NAME

cast-basefee - Get the basefee of a block.

SYNOPSIS

cast basefee [options] block

DESCRIPTION

Get the basefee of a block.

OPTIONS

Query Options

-B block
--block block
    The block height you want to query at.

    Can be a block number, or any of the tags: earliest, latest or pending.

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the basefee of the latest block:

    cast basefee latest
    
  2. Get the basefee of the genesis block:

    cast basefee 1
    

SEE ALSO

cast, cast block, cast age

cast block

NAME

cast-block - Get information about a block.

SYNOPSIS

cast block [options] --block block

DESCRIPTION

Get information about a block.

If field is specified, then only the given field of the block is displayed.

OPTIONS

Query Options

-B block
--block block
    The block height you want to query at.

    Can be a block number, or any of the tags: earliest, latest or pending.

-f field
--field field
    If specified, only get the given field of the block.

Display Options

-j
--json
     Print the deployment information as JSON.

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the latest block:

    cast block --block latest
    
  2. Get the hash of the latest block:

    cast block --block latest --field hash
    

SEE ALSO

cast, cast basefee, cast age

cast age

NAME

cast-age - Get the timestamp of a block.

SYNOPSIS

cast age [options]

DESCRIPTION

Get the timestamp of a block.

OPTIONS

Query Options

-B block
--block block
    The block height you want to query at.

    Can be a block number, or any of the tags: earliest, latest or pending.

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the timestamp of the latest block:

    cast age latest
    
  2. Get the timestamp of the genesis block:

    cast age 1
    

SEE ALSO

cast, cast block, cast basefee

Account Commands

cast balance

NAME

cast-balance - Get the balance of an account in wei.

SYNOPSIS

cast balance [options] who

DESCRIPTION

Get the balance of an account.

The argument who can be an ENS name or an address.

OPTIONS

Query Options

-B block
--block block
    The block height you want to query at.

    Can be a block number, or any of the tags: earliest, latest or pending.

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the balance of beer.eth
    cast balance beer.eth
    

SEE ALSO

cast, cast nonce

cast storage

NAME

cast-storage - Get the raw value of a contract's storage slot.

SYNOPSIS

cast storage [options] address slot

DESCRIPTION

Get the raw value of a contract's storage slot. (Slot locations greater than 18446744073709551615 (u64::MAX) should be given as hex. Use cast index to compute mapping slots.)

The address (address) can be an ENS name or an address.

OPTIONS

Query Options

-B block
--block block
    The block height you want to query at.

    Can be a block number, or any of the tags: earliest, latest or pending.

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the value of slot 0 on the WETH contract.
    cast storage 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 0
    

SEE ALSO

cast, cast proof

cast proof

NAME

cast-proof - Generate a storage proof for a given storage slot.

SYNOPSIS

cast proof [options] address [slots...]

DESCRIPTION

Generate a storage proof for a given storage slot.

The address (address) can be an ENS name or an address.

The displayed output is a JSON object with the following keys:

  • accountProof: Proof for the account itself
  • address: The address of the account
  • balance: The balance of the account
  • codeHash: A hash of the account's code
  • nonce: The nonce of the account
  • storageHash: A hash of the account's storage
  • storageProof: An array of storage proofs, one for each requested slot
  • storageProof.key: The slot
  • storageProof.proof: The proof for the slot
  • storageProof.value: The value of the slot

OPTIONS

Query Options

-B block
--block block
    The block height you want to query at.

    Can be a block number, or any of the tags: earliest, latest or pending.

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Display Options

-j
--json
     Print the deployment information as JSON.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the proof for storage slot 0 on the WETH contract:
    cast proof 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 0
    

SEE ALSO

cast, cast storage

cast nonce

NAME

cast-nonce - Get the nonce for an account.

SYNOPSIS

cast nonce [options] who

DESCRIPTION

Get the nonce of an account.

The argument who can be an ENS name or an address.

OPTIONS

Query Options

-B block
--block block
    The block height you want to query at.

    Can be a block number, or any of the tags: earliest, latest or pending.

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the nonce of beer.eth
    cast nonce beer.eth
    

SEE ALSO

cast, cast balance

cast code

NAME

cast-code - Get the bytecode of a contract.

SYNOPSIS

cast code [options] address

DESCRIPTION

Get the bytecode of a contract.

The contract (address) can be an ENS name or an address.

OPTIONS

Query Options

-B block
--block block
    The block height you want to query at.

    Can be a block number, or any of the tags: earliest, latest or pending.

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the bytecode of the WETH contract.
    cast code 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
    

SEE ALSO

cast, cast proof

ENS Commands

cast lookup-address

NAME

cast-lookup-address - Perform an ENS reverse lookup.

SYNOPSIS

cast lookup-address [options] who

DESCRIPTION

Perform an ENS reverse lookup.

If --verify is passed, then a normal lookup is performed after the reverse lookup to verify that the address is correct.

OPTIONS

Lookup Options

-v
--verify
    Perform a normal lookup to verify that the address is correct.

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the ENS name for an address.

    cast lookup-address $ADDRESS
    
  2. Perform both a reverse and a normal lookup:

    cast lookup-address --verify $ADDRESS
    

SEE ALSO

cast, cast resolve-name

cast resolve-name

NAME

cast-resolve-name - Perform an ENS lookup.

SYNOPSIS

cast lookup-address [options] who

DESCRIPTION

Perform an ENS lookup.

If --verify is passed, then a reverse lookup is performed after the normal lookup to verify that the name is correct.

OPTIONS

Lookup Options

-v
--verify
    Perform a reverse lookup to verify that the name is correct.

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the address for an ENS name.

    cast resolve-name vitalik.eth
    
  2. Perform both a normal and a reverse lookup:

    cast resolve-name --verify vitalik.eth
    

SEE ALSO

cast, cast lookup-address

cast namehash

NAME

cast-namehash - Calculate the ENS namehash of a name.

SYNOPSIS

cast namehash [options] name

DESCRIPTION

Calculate the ENS namehash of a name.

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Calculate the namehash of an ENS name.
    cast namehash vitalik.eth
    

SEE ALSO

cast, cast lookup-address, cast resolve-name

Etherscan Commands

cast etherscan-source

NAME

cast-etherscan-source - Get the source code of a contract from Etherscan.

SYNOPSIS

cast etherscan-source [options] address

DESCRIPTION

Get the source code of a contract from Etherscan.

The destination (to) can be an ENS name or an address.

OPTIONS

Output Options

-d directory
    The output directory to expand the source tree into.     If not provided, the source will be outputted to stdout.

Etherscan Options

--chain chain_name
    The Etherscan chain.

--etherscan-api-key key
    Etherscan API key, or the key of an Etherscan configuration table.
    Environment: ETHERSCAN_API_KEY

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the source code of the WETH contract:

    cast etherscan-source 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
    
  2. Expand the source code of the WETH contract into a directory named weth

    cast etherscan-source -d weth 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
    

SEE ALSO

cast

ABI Commands

cast abi-encode

NAME

cast-abi-encode - ABI encode the given function arguments, excluding the selector.

SYNOPSIS

cast abi-encode [options] sig [args...]

DESCRIPTION

ABI encode the given function, excluding the selector.

The signature (sig) is a fragment in the form <function name>(<types...>).

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. ABI-encode the arguments for a call to someFunc(address,uint256):

    cast abi-encode "someFunc(address,uint256)" 0x... 1
    
  2. For encoding a type with components (as a tuple, or custom struct):

    cast abi-encode "someFunc((string,uint256))" "(myString,1)"
    

SEE ALSO

cast, cast calldata

cast 4byte

NAME

cast-4byte - Get the function signatures for the given selector from https://sig.eth.samczsun.com.

SYNOPSIS

cast 4byte [options] sig

DESCRIPTION

Get the function signatures for the given selector from https://sig.eth.samczsun.com.

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the function signature for the selector 0x8cc5ce99:
    cast 4byte 0x8cc5ce99
    

SEE ALSO

cast, cast 4byte-decode, cast 4byte-event

cast 4byte-decode

NAME

cast-4byte-decode - Decode ABI-encoded calldata using https://sig.eth.samczsun.com.

SYNOPSIS

cast 4byte-decode [options] calldata

DESCRIPTION

Decode ABI-encoded calldata using https://sig.eth.samczsun.com.

OPTIONS

4byte Options

--id id
    The index of the resolved signature to use.     
    https://sig.eth.samczsun.com can have multiple possible signatures for a given selector.
    The index can be an integer, or the tags "earliest" and "latest".

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Decode calldata for a transfer call:
    cast 4byte-decode 0xa9059cbb000000000000000000000000e78388b4ce79068e89bf8aa7f218ef6b9ab0e9d00000000000000000000000000000000000000000000000000174b37380cea000
    

SEE ALSO

cast, cast 4byte, cast 4byte-event

cast 4byte-event

NAME

cast-4byte-event - Get the event signature for a given topic 0 from https://sig.eth.samczsun.com.

SYNOPSIS

cast 4byte-event [options] topic_0

DESCRIPTION

Get the event signature for a given topic 0 from https://sig.eth.samczsun.com.

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the event signature for a topic 0 of 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef:
    cast 4byte-event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
    

SEE ALSO

cast, cast 4byte, cast 4byte-decode

cast calldata

NAME

cast-calldata - ABI-encode a function with arguments.

SYNOPSIS

cast calldata [options] sig [args...]

DESCRIPTION

ABI-encode a function with arguments.

The signature (sig) is a fragment in the form <function name>(<types...>).

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. ABI-encode the arguments for a call to someFunc(address,uint256):
    cast calldata "someFunc(address,uint256)" 0x... 1
    

SEE ALSO

cast, cast abi-encode

cast pretty-calldata

NAME

cast-pretty-calldata - Pretty print calldata.

SYNOPSIS

cast pretty-calldata [options] calldata

DESCRIPTION

Pretty print calldata.

Tries to decode the calldata using https://sig.eth.samczsun.com unless --offline is passed.

OPTIONS

4byte Options

-o
--offline
    Skip the https://sig.eth.samczsun.com lookup.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Decode calldata for a transfer call:
    cast pretty-calldata 0xa9059cbb000000000000000000000000e78388b4ce79068e89bf8aa7f218ef6b9ab0e9d00000000000000000000000000000000000000000000000000174b37380cea000
    

SEE ALSO

cast, cast 4byte-decode

cast --abi-decode

NAME

cast---abi-decode - Decode ABI-encoded input or output data.

SYNOPSIS

cast --abi-decode [options] sig calldata

DESCRIPTION

Decode ABI-encoded input or output data.

By default, the command will decode output data. To decode input data, pass --input or use cast --calldata-decode.

The signature (sig) is a fragment in the form <function name>(<types...>)(<types...>).

OPTIONS

Decoder Options

-i
--input
    Decode input data.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Decode output data for a balanceOf call:

    cast --abi-decode "balanceOf(address)(uint256)" \
      0x000000000000000000000000000000000000000000000000000000000000000a
    
  2. Decode input data for a transfer call:

    cast --abi-decode --input "transfer(address,uint256)" \
      0xa9059cbb000000000000000000000000e78388b4ce79068e89bf8aa7f218ef6b9ab0e9d0000000000000000000000000000000000000000000000000008a8e4b1a3d8000
    

SEE ALSO

cast, cast --calldata-decode

cast --calldata-decode

NAME

cast---calldata-decode - Decode ABI-encoded input data.

SYNOPSIS

cast --calldata-decode [options] sig calldata

DESCRIPTION

Decode ABI-encoded input data.

The signature (sig) is a fragment in the form <function name>(<types...>).

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Decode input data for a transfer call:
    cast --calldata-decode "transfer(address,uint256)" \
      0xa9059cbb000000000000000000000000e78388b4ce79068e89bf8aa7f218ef6b9ab0e9d0000000000000000000000000000000000000000000000000008a8e4b1a3d8000
    

SEE ALSO

cast, cast --abi-decode

cast upload-signature

NAME

cast-upload-signature

SYNOPSIS

cast upload-signature [signatures...]

DESCRIPTION

Upload the given signatures to https://sig.eth.samczsun.com.

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Upload signatures
    cast upload-signature 'function approve(address,uint256)' \
    'transfer(uint256)' 'event Transfer(uint256,address)'
    

Conversion Commands

cast --format-bytes32-string

NAME

cast---format-bytes32-string - Formats a string into bytes32 encoding.

SYNOPSIS

cast --format-bytes32-string [options] string

DESCRIPTION

Formats a string into bytes32 encoding.

Note that this command is for formatting a Solidity string literal into bytes32 only. If you're looking to pad a byte string, use --to-bytes32 instead.

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Turn string "hello" into bytes32 hex:
    cast --format-bytes32-string "hello"
    

SEE ALSO

cast

cast --from-bin

NAME

cast---from-bin - Convert binary data into hex data.

SYNOPSIS

cast --from-bin [options]

DESCRIPTION

Convert binary data into hex data.

The input is taken from stdin.

OPTIONS

Common Options

-h
--help
    Prints help information.

SEE ALSO

cast

cast --from-fix

NAME

cast---from-fix - Convert a fixed point number into an integer.

SYNOPSIS

cast --from-fix [options] decimals value

DESCRIPTION

Convert a fixed point number into an integer.

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Convert 10.55 to an integer:
    cast --from-fix 2 10.55
    

SEE ALSO

cast

cast --from-rlp

NAME

cast---from-rlp - Decodes RLP-encoded data.

SYNOPSIS

cast --from-rlp data

DESCRIPTION

Decodes RLP-encoded data.

The data is a hexadecimal string with optional 0x prefix.

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Decode RLP data:
    cast --from-rlp 0xc481f181f2
    
    cast --from-rlp c481f181f2
    

cast --from-utf8

NAME

cast---from-utf8 - Convert UTF8 text to hex.

SYNOPSIS

cast --from-utf8 [options] text

DESCRIPTION

Convert UTF8 text to hex.

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Convert UTF8 text to hex:
    cast --from-utf8 "hello"
    

SEE ALSO

cast

cast --parse-bytes32-string

NAME

cast---parse-bytes32-string - Parses a string from bytes32 encoding.

SYNOPSIS

cast --parse-bytes32-string [options] bytes

DESCRIPTION

Parses a Solidity string literal from its bytes32 encoding representation mostly by interpreting bytes as ASCII characters. This command undos the encoding in --format-bytes32-string.

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Parse bytes32 string encoding of "hello" back to the string representation:
    cast --parse-bytes32-string "0x68656c6c6f000000000000000000000000000000000000000000000000000000"
    

SEE ALSO

cast

cast --to-ascii

NAME

cast---to-ascii - Convert hex data to an ASCII string.

SYNOPSIS

cast --to-ascii [options] text

DESCRIPTION

Convert hex data to an ASCII string.

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Convert hex data to an ASCII string:
    cast --to-ascii "0x68656c6c6f"
    

SEE ALSO

cast

cast --to-base

NAME

cast---to-base - Convert a number of one base to another.

SYNOPSIS

cast --to-base [options] value base

DESCRIPTION

Convert a number of one base to another.

OPTIONS

Base Options

--base-in base     The base of the input number. Available options:

    10, d, dec, decimal

    16, h, hex, hexadecimal

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Convert the decimal number 64 to hexadecimal

    cast --to-base 64 hex
    
  2. Convert the hexadecimal number 100 to binary

    cast --to-base 0x100 2
    

Note: The --base-in parameter is not enforced but will be needed if the input is ambiguous.

SEE ALSO

cast

cast --to-bytes32

NAME

cast---to-bytes32 - Right-pads hex data to 32 bytes.

SYNOPSIS

cast --to-bytes32 [options] bytes

DESCRIPTION

Right-pads hex data to 32 bytes.

Note that this command is for padding a byte string only. If you're looking to format a Solidity string literal into bytes32, use --format-bytes32-string instead.

OPTIONS

Common Options

-h
--help
    Prints help information.

SEE ALSO

cast

cast --to-fix

NAME

cast---to-fix - Convert an integer into a fixed point number.

SYNOPSIS

cast --to-fix [options] decimals value

DESCRIPTION

Convert an integer into a fixed point number.

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Convert 250 to a fixed point number with 2 decimals:
    cast --to-fix 2 250
    

SEE ALSO

cast

cast --to-hexdata

NAME

cast---to-hexdata - Normalize the input to lowercase, 0x-prefixed hex.

SYNOPSIS

cast --to-hexdata [options] input

DESCRIPTION

Normalize the input to lowercase, 0x-prefixed hex.

The input data (input) can either be:

  • Mixed case hex with or without the 0x prefix.
  • 0x prefixed hex that should be concatenated, separated by :.
  • An absolute path to a file containing hex.
  • A @tag, where the tag is defined in an environment variable.

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Add 0x prefix:

    cast --to-hexdata deadbeef
    
  2. Concatenate hex values:

    cast --to-hexdata "deadbeef:0xbeef"
    
  3. Normalize hex value in MY_VAR:

    cast --to-hexdata "@MY_VAR"
    

SEE ALSO

cast

cast --to-int256

NAME

cast---to-int256 - Convert a number to a hex-encoded int256.

SYNOPSIS

cast --to-int256 [options] value

DESCRIPTION

Convert a number to a hex-encoded int256.

OPTIONS

Common Options

-h
--help
    Prints help information.

SEE ALSO

cast

cast --to-rlp

NAME

cast---to-rlp - Encodes hex data to RLP.

SYNOPSIS

cast --to-rlp array

DESCRIPTION

RLP encodes a hex string or a JSON array of hex strings.

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Encoding RLP data:
    cast --to-rlp '["0xaa","0xbb","cc"]'
    
    cast --to-rlp f0a9     
    

cast --to-uint256

NAME

cast---to-uint256 - Convert a number to a hex-encoded uint256.

SYNOPSIS

cast --to-uint256 [options] value

DESCRIPTION

Convert a number to a hex-encoded uint256.

OPTIONS

Common Options

-h
--help
    Prints help information.

SEE ALSO

cast

cast --to-unit

NAME

cast---to-unit - Convert an eth amount to another unit.

SYNOPSIS

cast --to-unit [options] value [unit]

DESCRIPTION

Convert an eth amount to another unit.

The value to convert (value) can be a quantity of eth (in wei), or a number with a unit attached to it.

Valid units are:

  • ether
  • gwei
  • wei

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Convert 1000 wei to gwei

    cast --to-unit 1000 gwei
    
  2. Convert 1 eth to gwei

    cast --to-unit 1ether gwei
    

SEE ALSO

cast

cast --to-wei

NAME

cast---to-wei - Convert an eth amount to wei.

SYNOPSIS

cast --to-wei [options] value [unit]

DESCRIPTION

Convert an eth amount to wei.

Consider using cast --to-unit.

OPTIONS

Common Options

-h
--help
    Prints help information.

SEE ALSO

cast, cast calldata

cast shl

NAME

cast-shl - Perform a left shifting operation.

SYNOPSIS

cast shl [options] value shift

DESCRIPTION

Perform a left shifting operation.

OPTIONS

Base Options

--base-in base     The base of the input number. Available options:

    10, d, dec, decimal

    16, h, hex, hexadecimal

--base-out base     The desired base of the output. Available options:

    2, b, bin, binary

    8, o, oct, octal

    10, d, dec, decimal

    16, h, hex, hexadecimal

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Perform a 3 position left bit shift of the number 61
    cast shl --base-in 10 61 3
    

Note: The --base-in parameter is not enforced but will be needed if the input is ambiguous.

SEE ALSO

cast, cast shr

cast shr

NAME

cast-shr - Perform a right shifting operation.

SYNOPSIS

cast shr [options] value shift

DESCRIPTION

Perform a left shifting operation.

OPTIONS

Base Options

--base-in base     The base of the input number. Available options:

    10, d, dec, decimal

    16, h, hex, hexadecimal

--base-out base     The desired base of the output. Available options:

    2, b, bin, binary

    8, o, oct, octal

    10, d, dec, decimal

    16, h, hex, hexadecimal

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Perform a single right bit shift of 0x12
    cast shr --base-in 16 0x12 1
    

Note: The --base-in parameter is not enforced but will be needed if the input is ambiguous.

SEE ALSO

cast, cast shl

Utility Commands

cast sig

NAME

cast-sig - Get the selector for a function.

SYNOPSIS

cast sig [options] sig

DESCRIPTION

Get the selector for a function.

The signature (sig) is a fragment in the form <function name>(<types...>).

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the selector for the function transfer(address,uint256):

    cast sig "transfer(address,uint256)"
    
  2. Get the selector for a function that expects a struct:

    contract Test {
        struct MyStruct {
            address addr;
            uint256 amount;
        }
        function myfunction(MyStruct memory t) public pure {}
    }
    

    Structs are encoded as tuples (see struct encoding).

    cast sig "myfunction((address,uint256))"
    

SEE ALSO

cast, struct encoding

cast keccak

NAME

cast-keccak - Hash arbitrary data using keccak-256.

SYNOPSIS

cast keccak [options] data

DESCRIPTION

Hash arbitrary data using keccak-256.

OPTIONS

Common Options

-h
--help
    Prints help information.

SEE ALSO

cast

cast compute-address

NAME

cast-compute-address - Compute the contract address from a given nonce and deployer address.

SYNOPSIS

cast compute-address [options] address

DESCRIPTION

Compute the contract address from a given nonce and deployer address.

OPTIONS

Compute Options

--nonce nonce
    The nonce of the account. Defaults to the latest nonce, fetched from the RPC.

RPC Options

--rpc-url url
    The RPC endpoint. Accepts a URL or an existing alias in the [rpc_endpoints] table, like mainnet.     Environment: ETH_RPC_URL

Common Options

-h
--help
    Prints help information.

SEE ALSO

cast, cast proof

cast interface

NAME

cast-interface - Generate a Solidity interface from a given ABI.

SYNOPSIS

cast interface [options] address_or_path

DESCRIPTION

Generates a Solidity interface from a given ABI.

The argument (address_or_path) can either be the path to a file containing an ABI, or an address.

If an address is provided, then the interface is generated from the ABI of the account, which is fetched from Etherscan.

ℹ️ Note

This command does not currently support ABI encoder v2.

OPTIONS

Interface Options

-n name
--name name
    The name to use for the generated interface. The default name is Interface.

-o path
    The path to the output file. If not specified, the interface will be output to stdout.

-p version
--pragma version
    The Solidity pragma version to use in the interface. Default: ^0.8.10.

Etherscan Options

--chain chain_name
    The Etherscan chain.

--etherscan-api-key key
    Etherscan API key, or the key of an Etherscan configuration table.
    Environment: ETHERSCAN_API_KEY

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Generate an interface from a file:

    cast interface ./path/to/abi.json
    
  2. Generate an interface using Etherscan:

    cast interface -o IWETH.sol 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
    
  3. Generate and name an interface from a file:

    cast interface -n LilENS ./path/to/abi.json
    

SEE ALSO

cast, cast proof

cast index

NAME

cast-index - Compute the storage slot location for an entry in a mapping.

SYNOPSIS

cast index key_type key slot

DESCRIPTION

Compute the storage slot location for an entry in a mapping.

Use cast storage to get the value.

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

// World.sol

mapping (address => uint256) public mapping1;
mapping (string => string) public mapping2;
  1. Compute the storage slot of an entry (hello) in a mapping of type mapping(string => string), located at slot 1:
    >> cast index string "hello" 1
    0x3556fc8e3c702d4479a1ab7928dd05d87508462a12f53307b5407c969223d1f8
    >> cast storage [address] 0x3556fc8e3c702d4479a1ab7928dd05d87508462a12f53307b5407c969223d1f8
    world
    

SEE ALSO

cast

cast --concat-hex

NAME

cast---concat-hex - Concatenate hex strings.

SYNOPSIS

cast --concat-hex data...

DESCRIPTION

Concatenate hex strings.

OPTIONS

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Concatenate hex strings:
    cast --concat-hex 0xa 0xb 0xc
    

SEE ALSO

cast

cast --max-int

NAME

cast---max-int - Get the maximum i256 value.

SYNOPSIS

cast --max-int

DESCRIPTION

Get the maximum i256 value.

OPTIONS

Common Options

-h
--help
    Prints help information.

SEE ALSO

cast, cast --min-int, cast --max-uint

cast --min-int

NAME

cast---min-int - Get the minimum i256 value.

SYNOPSIS

cast --min-int

DESCRIPTION

Get the minimum i256 value.

OPTIONS

Common Options

-h
--help
    Prints help information.

SEE ALSO

cast, cast --max-int

cast --max-uint

NAME

cast---max-uint - Get the maximum uint256 value.

SYNOPSIS

cast --max-uint

DESCRIPTION

Get the maximum uint256 value.

OPTIONS

Common Options

-h
--help
    Prints help information.

SEE ALSO

cast, cast --max-int

cast --to-checksum-address

NAME

cast---to-checksum-address - Convert an addresss to a checksummed format (EIP-55).

SYNOPSIS

cast --to-checksum-address address

DESCRIPTION

Convert an addresss to a checksummed format (EIP-55).

OPTIONS

Common Options

-h
--help
    Prints help information.

SEE ALSO

cast

Wallet Commands

cast wallet

NAME

cast-wallet - Wallet management utilities.

SYNOPSIS

cast wallet [options] command [args]
cast wallet [options] --version
cast wallet [options] --help

DESCRIPTION

This program is a set of tools to use, create and manage wallets.

COMMANDS

cast wallet new
    Create a new random keypair.

cast wallet address
    Convert a private key to an address.

cast wallet sign
    Sign a message.

cast wallet vanity
    Generate a vanity address.

cast wallet verify
    Verify the signature of a message.

OPTIONS

Special Options

-V
--version
    Print version info and exit.

Common Options

-h
--help
    Prints help information.

cast wallet new

NAME

cast-wallet-new - Create a new random keypair.

SYNOPSIS

cast wallet new [options] [path]

DESCRIPTION

Create a new random keypair.

If path is specified, then the new keypair will be written to a JSON keystore encrypted with a password. (path should be an existing directory.)

OPTIONS

Keystore Options

-p
--password
    Triggers a hidden password prompt for the JSON keystore.
    Deprecated: prompting for a hidden password is now the default.

--unsafe-password password
    Password for the JSON keystore in cleartext.

    This is unsafe to use and we recommend using --password instead.
    Environment: CAST_PASSWORD

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Create a new keypair without saving it to a keystore:

    cast wallet new
    
  2. Create a new keypair and save it in the keystore directory:

    cast wallet new keystore
    

SEE ALSO

cast, cast wallet

cast wallet address

NAME

cast-wallet-address - Convert a private key to an address.

SYNOPSIS

cast wallet address [options]

DESCRIPTION

Convert a private key to an address.

OPTIONS

Keystore Options

WALLET OPTIONS - RAW:

-i
--interactive <NUM>
     Open an interactive prompt to enter your private key. Takes a value for the number of keys to enter.
     Defaults to 0.

--mnemonic-derivation-path <PATHS>
     The wallet derivation path. Works with both --mnemonic-path and hardware wallets.

--mnemonic-indexes <INDEXES>
     Use the private key from the given mnemonic index. Used with --mnemonic-paths.
     Defaults to 0.

--mnemonic-passphrase <PASSPHRASE>
     Use a BIP39 passphrases for the mnemonic.

--mnemonic <PATHS>
     Use the mnemonic phrases or mnemonic files at the specified paths.

--private-key <RAW_PRIVATE_KEY>
     Use the provided private key.

--private-keys <RAW_PRIVATE_KEYS>
     Use the provided private keys.

Wallet Options - Keystore

--keystore path
    Use the keystore in the given folder or file.
    Environment: ETH_KEYSTORE

--password password
    The keystore password. Used with --keystore.

Wallet Options - Hardware Wallet

-t
--trezor
    Use a Trezor hardware wallet.

-l
--ledger
    Use a Ledger hardware wallet.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Get the address of the keypair in keystore.json:
    cast wallet address --keystore keystore.json
    

SEE ALSO

cast, cast wallet

cast wallet sign

NAME

cast-wallet-sign - Sign a message.

SYNOPSIS

cast wallet sign [options] message

DESCRIPTION

Sign a message.

OPTIONS

WALLET OPTIONS - RAW:

-i
--interactive <NUM>
     Open an interactive prompt to enter your private key. Takes a value for the number of keys to enter.
     Defaults to 0.

--mnemonic-derivation-path <PATHS>
     The wallet derivation path. Works with both --mnemonic-path and hardware wallets.

--mnemonic-indexes <INDEXES>
     Use the private key from the given mnemonic index. Used with --mnemonic-paths.
     Defaults to 0.

--mnemonic-passphrase <PASSPHRASE>
     Use a BIP39 passphrases for the mnemonic.

--mnemonic <PATHS>
     Use the mnemonic phrases or mnemonic files at the specified paths.

--private-key <RAW_PRIVATE_KEY>
     Use the provided private key.

--private-keys <RAW_PRIVATE_KEYS>
     Use the provided private keys.

Wallet Options - Keystore

--keystore path
    Use the keystore in the given folder or file.
    Environment: ETH_KEYSTORE

--password password
    The keystore password. Used with --keystore.

Wallet Options - Hardware Wallet

-t
--trezor
    Use a Trezor hardware wallet.

-l
--ledger
    Use a Ledger hardware wallet.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Sign a message using a keystore:

    cast wallet sign --keystore keystore.json --interactive "hello"
    
  2. Sign a message using a raw private key:

    cast wallet sign --private-key $PRIV_KEY "hello"
    

SEE ALSO

cast, cast wallet

cast wallet vanity

NAME

cast-wallet-vanity - Generate a vanity address.

SYNOPSIS

cast wallet vanity [options]

DESCRIPTION

Generate a vanity address.

If --nonce is specified, then the command will try to generate a vanity contract address.

OPTIONS

Keystore Options

--starts-with hex
    Prefix for the vanity address.

--ends-with hex
    Suffix for the vanity address.

--nonce nonce
    Generate a vanity contract address created by the generated keypair with the specified nonce.

Common Options

-h
--help
    Prints help information.

EXAMPLES

  1. Create a new keypair that starts with dead:

    cast wallet vanity --starts-with dead
    
  2. Create a new keypair ends with beef:

    cast wallet vanity --ends-with beef
    

SEE ALSO

cast, cast wallet

cast wallet verify

NAME

cast-wallet-verify - Verify the signature of a message.

SYNOPSIS

cast wallet verify [options] --address address message signature

DESCRIPTION

Verify the signature of a message.

OPTIONS

Signature Options

-a address
--address address
    The address of the message signer.

Common Options

-h
--help
    Prints help information.

SEE ALSO

cast, cast wallet

anvil

NAME

anvil - Create a local testnet node for deploying and testing smart contracts. It can also be used to fork other EVM compatible networks.

SYNOPSIS

anvil [options]

DESCRIPTION

Create a local testnet node for deploying and testing smart contracts. It can also be used to fork other EVM compatible networks.

This section covers an extensive list of information about Mining Modes, Supported Transport Layers, Supported RPC Methods, Anvil flags and their usages. You can run multiple flags at the same time.

Mining Modes

Mining modes refer to how frequent blocks are mined using Anvil. By default, it automatically generates a new block as soon as a transaction is submitted.

You can change this setting to interval mining if you will, which means that a new block will be generated in a given period of time selected by the user. If you want to go for this type of mining, you can do it by adding the --block-time <block-time-in-seconds> flag, like in the following example.

# Produces a new block every 10 seconds
anvil --block-time 10

There's also a third mining mode called never. In this case, it disables auto and interval mining, and mine on demand instead. You can do this by typing:

# Enables never mining mode
anvil --no-mining

Supported Transport Layers

HTTP and Websocket connections are supported. The server listens on port 8545 by default, but it can be changed by running the following command:

anvil --port <PORT>

Supported RPC Methods

Standard Methods

The standard methods are based on this reference.

  • web3_clientVersion

  • web3_sha3

  • eth_chainId

  • eth_networkId

  • eth_gasPrice

  • eth_accounts

  • eth_blockNumber

  • eth_getBalance

  • eth_getStorageAt

  • eth_getBlockByHash

  • eth_getBlockByNumber

  • eth_getTransactionCount

  • eth_getBlockTransactionCountByHash

  • eth_getBlockTransactionCountByNumber

  • eth_getUncleCountByBlockHash

  • eth_getUncleCountByBlockNumber

  • eth_getCode

  • eth_sign

  • eth_signTypedData_v4

  • eth_sendTransaction

  • eth_sendRawTransaction

  • eth_call

  • eth_createAccessList

  • eth_estimateGas

  • eth_getTransactionByHash

  • eth_getTransactionByBlockHashAndIndex

  • eth_getTransactionByBlockNumberAndIndex

  • eth_getTransactionReceipt

  • eth_getUncleByBlockHashAndIndex

  • eth_getUncleByBlockNumberAndIndex

  • eth_getLogs

  • eth_newFilter

  • eth_getFilterChanges

  • eth_newBlockFilter

  • eth_newPendingTransactionFilter

  • eth_getFilterLogs

  • eth_uninstallFilter

  • eth_getWork

  • eth_subscribe

  • eth_unsubscribe

  • eth_syncing

  • eth_submitWork

  • eth_submitHashrate

  • eth_feeHistory

  • eth_getProof

  • trace_transaction

  • trace_block

Custom Methods

The anvil_* namespace is an alias for hardhat. For more info, refer to the Hardhat documentation.

anvil_impersonateAccount
Send transactions impersonating an externally owned account or contract. While impersonating a contract, the contract functions can not be called. anvil_stopImpersonatingAccount must be used if the contract's functions are to be called again. See also EIP-3607.

anvil_stopImpersonatingAccount
Stops impersonating an account or contract if previously set with anvil_impersonateAccount

anvil_getAutomine
Returns true if automatic mining is enabled, and false if it is not

anvil_mine
Mines a series of blocks

anvil_dropTransaction
Removes transactions from the pool

anvil_reset
Reset the fork to a fresh forked state, and optionally update the fork config

anvil_setRpcUrl
Sets the backend RPC URL

anvil_setBalance
Modifies the balance of an account

anvil_setCode
Sets the code of a contract

anvil_setNonce
Sets the nonce of an address

anvil_setStorageAt
Writes a single slot of the account's storage

anvil_setCoinbase
Sets the coinbase address

anvil_setLoggingEnabled
Enable or disable logging

anvil_setMinGasPrice
Set the minimum gas price for the node

anvil_setNextBlockBaseFeePerGas
Sets the base fee of the next block

anvil_dumpState Returns a hex string representing the complete state of the chain. Can be re-imported into a fresh/restarted instance of Anvil to reattain the same state.

anvil_loadState When given a hex string previously returned by anvil_dumpState, merges the contents into the current chain state. Will overwrite any colliding accounts/storage slots.

anvil_nodeInfo Retrieves the configuration params for the currently running Anvil node.

Special Methods

The special methods come from Ganache. You can take a look at the documentation here.

evm_setAutomine
Enables or disables, based on the single boolean argument, the automatic mining of new blocks with each new transaction submitted to the network

evm_setIntervalMining
Sets the mining behavior to interval with the given interval (seconds)

evm_snapshot
Snapshot the state of the blockchain at the current block

evm_revert
Revert the state of the blockchain to a previous snapshot. Takes a single parameter, which is the snapshot id to revert to

evm_increaseTime
Jump forward in time by the given amount of time, in seconds

evm_setNextBlockTimestamp
Similar to evm_increaseTime but takes the exact timestamp that you want in the next block

anvil_setBlockTimestampInterval
Similar to evm_increaseTime but sets a block timestamp interval. The timestamp of the next block will be computed as lastBlock_timestamp + interval

evm_setBlockGasLimit
Sets the block gas limit for the following blocks

anvil_removeBlockTimestampInterval
Removes an anvil_setBlockTimestampInterval if it exists

evm_mine
Mine a single block

anvil_enableTraces
Turn on call traces for transactions that are returned to the user when they execute a transaction (instead of just txhash/receipt)

eth_sendUnsignedTransaction
Execute a transaction regardless of signature status

For the next three methods, make sure to read Geth's documentation.

txpool_status
Returns the number of transactions currently pending for inclusion in the next block(s), as well as the ones that are being scheduled for future execution only

txpool_inspect
Returns a summary of all the transactions currently pending for inclusion in the next block(s), as well as the ones that are being scheduled for future execution only

txpool_content
Returns the details of all transactions currently pending for inclusion in the next block(s), as well as the ones that are being scheduled for future execution only

OPTIONS

General Options

-a, --accounts <ACCOUNTS>
     Set the number of accounts [default: 10]

-b, --block-time <block-time>
     Block time in seconds for interval mining

--balance <BALANCE>
     Set the balance of the accounts [default: 10000]

--derivation-path <DERIVATION_PATH>
     Set the derivation path of the child key to be derived [default: m/44'/60'/0'/0/]

-h, --help
     Print help information

--hardfork <HARDFORK>
     Choose the EVM hardfork to use [default: latest]

--init <PATH>
     Initialize the genesis block with the given genesis.json file.

-m, --mnemonic <MNEMONIC>
     BIP39 mnemonic phrase used for generating accounts

--no-mining
     Disable auto and interval mining, and mine on demand instead

--order <ORDER>
     How transactions are sorted in the mempool [default: fees]

-p, --port <PORT>
     Port number to listen on [default: 8545]

--steps-tracing
     Enable steps tracing used for debug calls returning geth-style traces [aliases: tracing]

--ipc [<PATH>]
     Starts an IPC endpoint at the given PATH argument or the default path: unix: tmp/anvil.ipc, windows: \\.\pipe\anvil.ipc

--silent
     Don't print anything on startup

--timestamp <TIMESTAMP>      Set the timestamp of the genesis block

-V, --version
     Print version information

EVM Options

-f, --fork-url <URL>
     Fetch state over a remote endpoint instead of starting from an empty state

--fork-block-number <BLOCK>
     Fetch state from a specific block number over a remote endpoint (Must pass --fork-url in the same command-line)

--fork-retry-backoff <BACKOFF>
     Initial retry backoff on encountering errors.

--retries <retries>
     Number of retry requests for spurious networks (timed out requests). [default value= 5]

--timeout <timeout>
     Timeout in ms for requests sent to remote JSON-RPC server in forking mode. [default value= 45000]

--compute-units-per-second <CUPS>
     Sets the number of assumed available compute units per second for this provider [default value=330]      See also, Alchemy Ratelimits

--no-rate-limit      Disables rate limiting for this node's provider. Will always override --compute-units-per-second if present. [default value= false]      See also, Alchemy Ratelimits

--no-storage-caching>
     Explicitly disables the use of RPC caching. All storage slots are read entirely from the endpoint. This flag overrides the project's configuration file (Must pass --fork-url in the same command-line)

Executor Environment Config

--base-fee <FEE>
--block-base-fee-per-gas <FEE>
     The base fee in a block

--chain-id <CHAIN_ID>
     The chain ID

--code-size-limit <CODE_SIZE>
     EIP-170: Contract code size limit in bytes. Useful to increase this because of tests. By default, it is 0x6000 (~25kb)

--gas-limit <GAS_LIMIT>
     The block gas limit

--gas-price <GAS_PRICE>
     The gas price

Server Options

--allow-origin <allow-origin>
     Set the CORS allow_origin [default: *]

--no-cors
     Disable CORS

--host <HOST>
     The IP address the server will listen on

--config-out <OUT_FILE>
     Writes output of anvil as json to user-specified file

--prune-history
     Don't keep full chain history

EXAMPLES

  1. Set the number of accounts to 15 and their balance to 300 ETH
anvil --accounts 15 --balance 300
  1. Choose the address which will execute the tests
anvil --sender 0xC8479C45EE87E0B437c09d3b8FE8ED14ccDa825E
  1. Change how transactions are sorted in the mempool to FIFO
anvil --order fifo

Shell Completions

anvil completions shell

Generates a shell completions script for the given shell.

Supported shells are:

  • bash
  • elvish
  • fish
  • powershell
  • zsh

EXAMPLES

  1. Generate shell completions script for zsh:
    anvil completions zsh > $HOME/.oh-my-zsh/completions/_anvil
    

Usage within Docker

In order to run anvil as a service in Github Actions with the Docker container, where passing arguments to the entrypoint command is not possible, use the ANVIL_IP_ADDR environment variable to set the host's IP. ANVIL_IP_ADDR=0.0.0.0 is equivalent to providing the --host <ip> option.

Config Reference

Config Overview

Foundry's configuration system allows you to configure its tools.

Profiles

Configuration can be arbitrarily namespaced into profiles. The default profile is named default, and all other profiles inherit values from this profile. Profiles are defined in the profile map.

To add a profile named local, you would add:

[profile.local]

You can select the profile to use by setting the FOUNDRY_PROFILE environment variable.

Global configuration

You can create a foundry.toml file in your home folder to configure Foundry globally.

Environment variables

Configuration can be overriden with FOUNDRY_ and DAPP_ prefixed environment variables.

Exceptions are:

  • FOUNDRY_FFI, DAPP_FFI

Configuration format

Configuration files are written in the TOML format, with simple key-value pairs inside of sections.

This page describes each configuration key in detail. To see the default values, either refer to the specific key in this document, or see the default config.

Configuration keys

This section documents all configuration keys. All configuration keys must live under a profile, such as default.

Project

Configuration related to the project in general.

src
  • Type: string
  • Default: src
  • Environment: FOUNDRY_SRC or DAPP_SRC

The path to the contract sources relative to the root of the project.

test
  • Type: string
  • Default: test
  • Environment: FOUNDRY_TEST or DAPP_TEST

The path to the test contract sources relative to the root of the project.

out
  • Type: string
  • Default: out
  • Environment: FOUNDRY_OUT or DAPP_OUT

The path to put contract artifacts in, relative to the root of the project.

libs
  • Type: array of strings (paths)
  • Default: lib
  • Environment: FOUNDRY_LIBS or DAPP_LIBS

An array of paths that contain libraries, relative to the root of the project.

cache
  • Type: boolean
  • Default: true
  • Environment: FOUNDRY_CACHE or DAPP_CACHE

Whether or not to enable caching. If enabled, the result of compiling sources, tests, and dependencies, are cached in cache.

cache_path
  • Type: string
  • Default: cache
  • Environment: FOUNDRY_CACHE_PATH or DAPP_CACHE_PATH

The path to the cache, relative to the root of the project.

broadcast
  • Type: string
  • Default: broadcast

The path to the broadcast transaction logs, relative to the root of the project.

force
  • Type: boolean
  • Default: false
  • Environment: FOUNDRY_FORCE or DAPP_FORCE

Whether or not to perform a clean build, discarding the cache.

Solidity compiler

Configuration related to the behavior of the Solidity compiler.

Sections

General

Configuration related to the behavior of the Solidity compiler.

remappings
  • Type: array of strings (remappings)
  • Default: none
  • Environment: FOUNDRY_REMAPPINGS or DAPP_REMAPPINGS

An array of remappings in the following format: <name>=<target>.

A remapping remaps Solidity imports to different directories. For example, the following remapping

@openzeppelin/=node_modules/@openzeppelin/openzeppelin-contracts/

with an import like

import "@openzeppelin/contracts/utils/Context.sol";

becomes

import "node_modules/@openzeppelin/openzeppelin-contracts/contracts/utils/Context.sol";
auto_detect_remappings
  • Type: boolean
  • Default: true
  • Environment: FOUNDRY_AUTO_DETECT_REMAPPINGS or DAPP_AUTO_DETECT_REMAPPINGS

If enabled, Foundry will automatically try auto-detect remappings by scanning the libs folder(s).

If set to false, only the remappings in foundry.toml and remappings.txt are used.

allow_paths
  • Type: array of strings (paths)
  • Default: none
  • Environment: FOUNDRY_ALLOW_PATHS or DAPP_ALLOW_PATHS

Tells solc to allow reading source files from additional directories. This is mainly relevant for complex workspaces managed by pnmp or similar.

See also solc allowed-paths

include_paths
  • Type: array of strings (paths)
  • Default: none
  • Environment: FOUNDRY_INCLUDE_PATHS or DAPP_INCLUDE_PATHS

Make an additional source directory available to the default import callback. Use this option if you want to import contracts whose location is not fixed in relation to your main source tree, e.g. third-party libraries installed using a package manager. Can be used multiple times. Can only be used if base path has a non-empty value.

See also solc path resolution

libraries
  • Type: array of strings (libraries)
  • Default: none
  • Environment: FOUNDRY_LIBRARIES or DAPP_LIBRARIES

An array of libraries to link against in the following format: <file>:<lib>:<address>, for example: src/MyLibrary.sol:MyLibrary:0xfD88CeE74f7D78697775aBDAE53f9Da1559728E4.

solc_version
  • Type: string (semver)
  • Default: none
  • Environment: FOUNDRY_SOLC_VERSION or DAPP_SOLC_VERSION

If specified, overrides the auto-detection system (more below) and uses a single Solidity compiler version for the project.

Only strict versions are supported (i.e. 0.8.11 is valid, but ^0.8.0 is not).

auto_detect_solc
  • Type: boolean
  • Default: true
  • Environment: FOUNDRY_AUTO_DETECT_SOLC or DAPP_AUTO_DETECT_SOLC

If enabled, Foundry will automatically try to resolve appropriate Solidity compiler versions to compile your project.

This key is ignored if solc_version is set.

offline
  • Type: boolean
  • Default: false
  • Environment: FOUNDRY_OFFLINE or DAPP_OFFLINE

If enabled, Foundry will not attempt to download any missing solc versions.

If both offline and auto-detect-solc are set to true, the required version(s) of solc will be auto detected but any missing versions will not be installed.

ignored_error_codes
  • Type: array of integers
  • Default: none for source, SPDX license identifiers and contract size for tests
  • Environment: FOUNDRY_IGNORED_ERROR_CODES or DAPP_IGNORED_ERROR_CODES

An array of Solidity compiler error codes to ignore during build, such as warnings.

evm_version
  • Type: string
  • Default: london
  • Environment: FOUNDRY_EVM_VERSION or DAPP_EVM_VERSION

The EVM version to use during tests. The value must be an EVM hardfork name, such as london, byzantium, etc.

revert_strings
  • Type: string
  • Default: default
  • Environment: FOUNDRY_REVERT_STRINGS or DAPP_REVERT_STRINGS

Possible values are:

  • default does not inject compiler-generated revert strings and keeps user-supplied ones.
  • strip removes all revert strings (if possible, i.e. if literals are used) keeping side-effects.
  • debug injects strings for compiler-generated internal reverts, implemented for ABI encoders V1 and V2 for now.
  • verboseDebug even appends further information to user-supplied revert strings (not yet implemented).
extra_output_files
  • Type: array of strings
  • Default: none
  • Environment: N/A

Extra output from the Solidity compiler that should be written to files in the artifacts directory.

Valid values are:

  • metadata: Written as a metadata.json file in the artifacts directory
  • ir: Written as a .ir file in the artifacts directory
  • irOptimized: Written as a .iropt file in the artifacts directory
  • ewasm: Written as a .ewasm file in the artifacts directory
  • evm.assembly: Written as a .asm file in the artifacts directory
extra_output
  • Type: array of strings
  • Default: see below
  • Environment: N/A

Extra output to include in the contract's artifact.

The following values are always set, since they're required by Forge:

extra_output = [
  "abi",
  "evm.bytecode",
  "evm.deployedBytecode",
  "evm.methodIdentifiers",
]

For a list of valid values, see the [Solidity docs][output-desc].

bytecode_hash
  • Type: string
  • Default: ipfs
  • Environment: FOUNDRY_BYTECODE_HASH or DAPP_BYTECODE_HASH

Determines the hash method for the metadata hash that is appended to the bytecode.

Valid values are:

  • ipfs (default)
  • bzzr1
  • none
sparse_mode
  • Type: boolean
  • Default: false
  • Environment: FOUNDRY_SPARSE_MODE or DAPP_SPARSE_MODE

Enables sparse mode for builds.

Optimizer

Configuration related to the Solidity optimizer.

optimizer
  • Type: boolean
  • Default: true
  • Environment: FOUNDRY_OPTIMIZER or DAPP_OPTIMIZER

Whether or not to enable the Solidity optimizer.

optimizer_runs
  • Type: integer
  • Default: 200
  • Environment: FOUNDRY_OPTIMIZER_RUNS or DAPP_OPTIMIZER_RUNS

The amount of optimizer runs to perform.

via_ir
  • Type: boolean
  • Default: false
  • Environment: FOUNDRY_VIA_IR or DAPP_VIA_IR

If set to true, changes compilation pipeline to go through the new IR optimizer.

[optimizer_details]

The optimizer details section is used to tweak how the Solidity optimizer behaves. There are several configurable values in this section (each of them are booleans):

  • peephole
  • inliner
  • jumpdest_remover
  • order_literals
  • deduplicate
  • cse
  • constant_optimizer
  • yul

Refer to the Solidity compiler input description for the default values.

[optimizer_details.yul_details]

The Yul details subsection of the optimizer details section is used to tweak how the new IR optimizer behaves. There are two configuration values:

  • stack_allocation: Tries to improve the allocation of stack slots by freeing them up earlier.
  • optimizer_steps: Selects the optimizer steps to be applied.

Refer to the Solidity compiler input description for the default values.

ℹ️ Note
If you encounter compiler errors when using via_ir, explicitly enable the legacy optimizer and leave optimizer_steps as an empty string

Model checker

The Solidity model checker is a built-in opt-in module that is available in Solidity compilers for OSX and Linux. Learn more about the model checker in the Solidity compiler documentation

ℹ️ Note
The model checker requires z3 version 4.8.8 or 4.8.14 on Linux.

The model checker settings are configured in the [model_checker] section of the configuration.

The model checker will run when forge build is invoked, and any findings will show up as warnings.

These are the recommended settings when using the model checker:

[profile.default.model_checker]
contracts = {'/path/to/project/src/Contract.sol' = ['Contract']}
engine = 'chc'
timeout = 10000
targets = ['assert']

Setting which contract should be verified is extremely important, otherwise all available contracts will be verified which may take a long time.

The recommended engine is chc, but bmc and all (which runs both) are also accepted.

It is also important to set a proper timeout (given in milliseconds), since the default time given to the underlying solver may not be enough.

If no verification targets are given, only assertions will be checked.

[model_checker]

The following keys are available in the model checker section.

model_checker.contracts
  • Type: table
  • Default: all
  • Environment: N/A

Specifies what contracts the model checker will analyze.

The key of the table is the path to a source file, and the value is an array of contract names to check.

For example:

[profile.default.model_checker]
contracts = { "src/MyContracts.sol" = ["ContractA", "ContractB"] }
model_checker.engine
  • Type: string (see below)
  • Default: all
  • Environment: N/A

Specifies the model checker engine to run. Valid values are:

  • chc: The constrained horn clauses engine
  • bmc: The bounded model checker engine
  • all: Runs both engines

Refer to the Solidity documentation for more information on the engines.

model_checker.timeout
  • Type: number (milliseconds)
  • Default: N/A
  • Environment: N/A

Sets the timeout for the underlying model checker engines (in milliseconds).

model_checker.targets
  • Type: array of strings
  • Default: assert
  • Environment: N/A

Sets the model checker targets. Valid values are:

  • assert: Assertions
  • underflow: Arithmetic underflow
  • overflow: Arithmetic overflow
  • divByZero: Division by zero
  • constantCondition: Trivial conditions and unreachable code
  • popEmptyArray: Popping an empty array
  • outOfBounds: Out of bounds array/fixed bytes index access
  • default: All of the above (note: not the default for Forge)

Testing

Configuration related to the behavior of forge test.

Sections

General

verbosity
  • Type: integer
  • Default: 0
  • Environment: FOUNDRY_VERBOSITY or DAPP_VERBOSITY

The verbosity level to use during tests.

  • Level 2 (-vv): Logs emitted during tests are also displayed.
  • Level 3 (-vvv): Stack traces for failing tests are also displayed.
  • Level 4 (-vvvv): Stack traces for all tests are displayed, and setup traces for failing tests are displayed.
  • Level 5 (-vvvvv): Stack traces and setup traces are always displayed.
ffi
  • Type: boolean
  • Default: false
  • Environment: FOUNDRY_FFI or DAPP_FFI

Whether or not to enable the ffi cheatcode.

Warning: Enabling this cheatcode has security implications for your project, as it allows tests to execute arbitrary programs on your computer.

sender
  • Type: string (address)
  • Default: 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38
  • Environment: FOUNDRY_SENDER or DAPP_SENDER

The value of msg.sender in tests.

tx_origin
  • Type: string (address)
  • Default: 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38
  • Environment: FOUNDRY_TX_ORIGIN or DAPP_TX_ORIGIN

The value of tx.origin in tests.

initial_balance
  • Type: string (hexadecimal)
  • Default: 0xffffffffffffffffffffffff
  • Environment: FOUNDRY_INITIAL_BALANCE or DAPP_INITIAL_BALANCE

The initial balance of the test contracts in wei, written in hexadecimal.

block_number
  • Type: integer
  • Default: 1
  • Environment: FOUNDRY_BLOCK_NUMBER or DAPP_BLOCK_NUMBER

The value of block.number in tests.

chain_id
  • Type: integer
  • Default: 31337
  • Environment: FOUNDRY_CHAIN_ID or DAPP_CHAIN_ID

The value of the chainid opcode in tests.

gas_limit
  • Type: integer or string
  • Default: 9223372036854775807
  • Environment: FOUNDRY_GAS_LIMIT or DAPP_GAS_LIMIT

The gas limit for each test case.

ℹ️ Note

Due to a limitation in a dependency of Forge, you cannot raise the gas limit beyond the default without changing the value to a string.

In order to use higher gas limits use a string:

gas_limit = "18446744073709551615" # u64::MAX
gas_price
  • Type: integer
  • Default: 0
  • Environment: FOUNDRY_GAS_PRICE or DAPP_GAS_PRICE

The price of gas (in wei) in tests.

block_base_fee_per_gas
  • Type: integer
  • Default: 0
  • Environment: FOUNDRY_BLOCK_BASE_FEE_PER_GAS or DAPP_BLOCK_BASE_FEE_PER_GAS

The base fee per gas (in wei) in tests.

block_coinbase
  • Type: string (address)
  • Default: 0x0000000000000000000000000000000000000000
  • Environment: FOUNDRY_BLOCK_COINBASE or DAPP_BLOCK_COINBASE

The value of block.coinbase in tests.

block_timestamp
  • Type: integer
  • Default: 1
  • Environment: FOUNDRY_BLOCK_TIMESTAMP or DAPP_BLOCK_TIMESTAMP

The value of block.timestamp in tests.

block_difficulty
  • Type: integer
  • Default: 0
  • Environment: FOUNDRY_BLOCK_DIFFICULTY or DAPP_BLOCK_DIFFICULTY

The value of block.difficulty in tests.

gas_reports
  • Type: array of strings (contract names)
  • Default: ["*"]
  • Environment: FOUNDRY_GAS_REPORTS or DAPP_GAS_REPORTS

The contracts to print gas reports for.

no_storage_caching
  • Type: boolean
  • Default: false
  • Environment: FOUNDRY_NO_STORAGE_CACHING or DAPP_NO_STORAGE_CACHING

If set to true, then block data from RPC endpoints in tests will not be cached. Otherwise, the data is cached to $HOME/.foundry/cache/<chain id>/<block number>.

[rpc_storage_caching]

The [rpc_storage_caching] block determines what RPC endpoints are cached.

rpc_storage_caching.chains
  • Type: string or array of strings (chain names)
  • Default: all
  • Environment: N/A

Determines what chains are cached. By default, all chains are cached.

Valid values are:

  • "all"
  • A list of chain names, e.g. ["optimism", "mainnet"]
rpc_storage_caching.endpoints
  • Type: string or array of regex patterns (to match URLs)
  • Default: remote
  • Environment: N/A

Determines what RPC endpoints are cached. By default, only remote endpoints are cached.

Valid values are:

  • all
  • remote (default)
  • A list of regex patterns, e.g. ["localhost"]
eth_rpc_url
  • Type: string
  • Default: none
  • Environment: FOUNDRY_ETH_RPC_URL or DAPP_ETH_RPC_URL

The url of the rpc server that should be used for any rpc calls.

etherscan_api_key
  • Type: string
  • Default: none
  • Environment: FOUNDRY_ETHERSCAN_API_KEY or DAPP_ETHERSCAN_API_KEY

The etherscan API key for RPC calls.

test_pattern
  • Type: regex
  • Default: none
  • Environment: FOUNDRY_TEST_PATTERN or DAPP_TEST_PATTERN

Only run test methods matching regex. Equivalent to forge test --match-test <TEST_PATTERN>

test_pattern_inverse
  • Type: regex
  • Default: none
  • Environment: FOUNDRY_TEST_PATTERN_INVERSE or DAPP_TEST_PATTERN_INVERSE

Only run test methods not matching regex. Equivalent to forge test --no-match-test <TEST_PATTERN_INVERSE>

contract_pattern
  • Type: regex
  • Default: none
  • Environment: FOUNDRY_CONTRACT_PATTERN or DAPP_CONTRACT_PATTERN

Only run test methods in contracts matching regex. Equivalent to forge test --match-contract <CONTRACT_PATTERN>

contract_pattern_inverse
  • Type: regex
  • Default: none
  • Environment: FOUNDRY_CONTRACT_PATTERN_INVERSE or DAPP_CONTRACT_PATTERN_INVERSE

Only run test methods in contracts not matching regex. Equivalent to forge test --no-match-contract <CONTRACT_PATTERN_INVERSE>

path_pattern
  • Type: regex
  • Default: none
  • Environment: FOUNDRY_PATH_PATTERN or DAPP_PATH_PATTERN

Only runs test methods on files matching the path.

path_pattern_inverse
  • Type: regex
  • Default: none
  • Environment: FOUNDRY_PATH_PATTERN_INVERSE or DAPP_PATH_PATTERN_INVERSE

Only runs test methods on files not matching the path.

block_gas_limit
  • Type: integer
  • Default: none
  • Environment: FOUNDRY_BLOCK_GAS_LIMIT or DAPP_BLOCK_GAS_LIMIT

The block.gaslimit value during EVM execution.

memory_limit
  • Type: integer
  • Default: 33554432
  • Environment: FOUNDRY_MEMORY_LIMIT or DAPP_MEMORY_LIMIT

The memory limit of the EVM in bytes.

names
  • Type: boolean
  • Default: false
  • Environment: FOUNDRY_NAMES or DAPP_NAMES

Print compiled contract names.

sizes
  • Type: boolean
  • Default: false
  • Environment: FOUNDRY_SIZES or DAPP_SIZES

Print compiled contract sizes.

rpc_endpoints
  • Type: table of RPC endpoints
  • Default: none
  • Environment: none

This section lives outside of profiles and defines a table of RPC endpoints, where the key specifies the RPC endpoints's name and the value is the RPC endpoint itself.

The value can either be a valid RPC endpoint or a reference to an environment variable (wrapped with in ${}).

These RPC endpoints can be used in tests and Solidity scripts (see vm.rpc).

The following example defines an endpoint named optimism and an endpoint named mainnet that references an environment variable RPC_MAINNET:

[rpc_endpoints]
optimism = "https://optimism.alchemyapi.io/v2/..."
mainnet = "${RPC_MAINNET}"

Fuzz

Configuration values for [fuzz] section.

runs
  • Type: integer
  • Default: 256
  • Environment: FOUNDRY_FUZZ_RUNS or DAPP_FUZZ_RUNS

The amount of fuzz runs to perform for each fuzz test case. Higher values gives more confidence in results at the cost of testing speed.

max_test_rejects
  • Type: integer
  • Default: 65536
  • Environment: FOUNDRY_FUZZ_MAX_TEST_REJECTS

The maximum number of combined inputs that may be rejected before the test as a whole aborts. "Global" filters apply to the whole test case. If the test case is rejected, the whole thing is regenerated.

seed
  • Type: string (hexadecimal)
  • Default: none
  • Environment: FOUNDRY_FUZZ_SEED

Optional seed for the fuzzing RNG algorithm.

dictionary_weight
  • Type: integer (between 0 and 100)
  • Default: 40
  • Environment: FOUNDRY_FUZZ_DICTIONARY_WEIGHT

The weight of the dictionary.

include_storage
  • Type: boolean
  • Default: true
  • Environment: FOUNDRY_FUZZ_INCLUDE_STORAGE

The flag indicating whether to include values from storage.

include_push_bytes
  • Type: boolean
  • Default: true
  • Environment: FOUNDRY_FUZZ_INCLUDE_PUSH_BYTES

The flag indicating whether to include push bytes values.

Invariant

Configuration values for [invariant] section.

ℹ️ Note

Configuration for [invariant] section has the fallback logic for common config entries (runs, seed, dictionary_weight etc).

  • If the entries are not set in either section, then the defaults will be used.
  • If the entries are set in the [fuzz] section, but are not set in the [invariant] section, these values will automatically be set to the values specified in the [fuzz] section.
  • For any profile other than default:
    • If the common entries are set in the [invariant] (same as [profile.default.invariant]) section, then the values from [invariant] section will be used.
    • If the common entries are not set in the [invariant] section, but are set in the [fuzz] (same as [profile.default.fuzz]) section, then the values from the [fuzz] section will be used.
    • If it's none of the cases described above, then the defaults will be used.
runs
  • Type: integer
  • Default: 256
  • Environment: FOUNDRY_INVARIANT_RUNS

The number of runs that must execute for each invariant test group. See also fuzz.runs

depth
  • Type: integer
  • Default: 256
  • Environment: FOUNDRY_INVARIANT_DEPTH

The number of calls executed to attempt to break invariants in one run.

fail_on_revert
  • Type: boolean
  • Default: false
  • Environment: FOUNDRY_INVARIANT_FAIL_ON_REVERT

Fails the invariant fuzzing if a revert occurs.

call_override
  • Type: boolean
  • Default: false
  • Environment: FOUNDRY_INVARIANT_CALL_OVERRIDE

Allows overriding an unsafe external call when running invariant tests. eg. reentrancy checks.

dictionary_weight
  • Type: integer (between 0 and 100)
  • Default: 80
  • Environment: FOUNDRY_INVARIANT_DICTIONARY_WEIGHT

The weight of the dictionary. See also fuzz.dictionary_weight

include_storage
  • Type: boolean
  • Default: true
  • Environment: FOUNDRY_FUZZ_INCLUDE_STORAGE

The flag indicating whether to include values from storage. See also fuzz.include_storage

include_push_bytes
  • Type: boolean
  • Default: true
  • Environment: FOUNDRY_FUZZ_INCLUDE_PUSH_BYTES

The flag indicating whether to include push bytes values. See also fuzz.include_push_bytes

Formatter

Configuration related to the behavior of the Forge formatter. Each of these keys live under the [fmt] section.

line_length
  • Type: number
  • Default: 120
  • Environment: FOUNDRY_FMT_LINE_LENGTH or DAPP_FMT_LINE_LENGTH

Specifies the maximum line length where the formatter will try to wrap the line.

tab_width
  • Type: number
  • Default: 4
  • Environment: FOUNDRY_FMT_TAB_WIDTH or DAPP_FMT_TAB_WIDTH

Number of spaces per indentation level.

bracket_spacing
  • Type: bool
  • Default: false
  • Environment: FOUNDRY_FMT_BRACKET_SPACING or DAPP_FMT_BRACKET_SPACING

Whether or not to print spaces between brackets.

int_types
  • Type: string
  • Default: long
  • Environment: FOUNDRY_FMT_INT_TYPES or DAPP_FMT_INT_TYPES

Style of uint/int256 types. Valid values are:

  • long (default): Use the explicit uint256 or int256
  • short: Use the implicit uint or int
  • preserve: Use the type defined in the source code
func_attrs_with_params_multiline
  • Type: bool
  • Default: true
  • Environment: FOUNDRY_FMT_FUNC_ATTRS_WITH_PARAMS_MULTILINE or DAPP_FMT_FUNC_ATTRS_WITH_PARAMS_MULTILINE

If function parameters are multiline then always put the function attributes on separate lines.

quote_style
  • Type: string
  • Default: double
  • Environment: FOUNDRY_FMT_QUOTE_STYLE or DAPP_FMT_QUOTE_STYLE

Defines the quotation mark style. Valid values are:

  • double (default): Use double quotes where possible (")
  • single: Use single quotes where possible (')
  • preserve: Use quotation mark defined in the source code
number_underscore
  • Type: string
  • Default: preserve
  • Environment: FOUNDRY_FMT_NUMBER_UNDERSCORE or DAPP_FMT_NUMBER_UNDERSCORE

Style of underscores in number literals. Valid values are:

  • preserve (default): Use the underscores defined in the source code
  • thousands: Add an underscore every thousand, if greater than 9999. i.e. 1000 is formatted as 1000 and 10000 as 10_000
  • remove: Remove all underscores

Etherscan

Configuration related to Etherscan, such as API keys. This configuration is used in various places by Forge.

The [etherscan] section is a mapping of keys to Etherscan configuration tables. The Etherscan configuration tables hold the following keys:

  • key (string) (required): The Etherscan API key for the given network. The value of this property can also point to an environment variable.
  • chain: The chain name or ID of the chain this Etherscan configuration is for.
  • url: The Etherscan API URL.

If the key of the configuration is a chain name, then chain is not required, otherwise it is. url can be used to explicitly set the Etherscan API URL for chains not natively supported by name.

Using TOML inline table syntax, all of these are valid:

[etherscan]
mainnet = { key = "${ETHERSCAN_MAINNET_KEY}" }
mainnet2 = { key = "ABCDEFG", chain = "mainnet" }
optimism = { key = "1234567" }
unknown_chain = { key = "ABCDEFG", url = "<etherscan api url for this chain>" }

Cheatcodes Reference

Cheatcodes give you powerful assertions, the ability to alter the state of the EVM, mock data, and more.

Cheatcodes are made available through use of the cheatcode address (0x7109709ECfa91a80626fF3989D68f67F5b1DD12D).

ℹ️ Note

If you encounter errors for this address when using fuzzed addresses in your tests, you may wish to exclude it from your fuzz tests by using the following line:

vm.assume(address_ != 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);

You can also access cheatcodes easily via vm available in Forge Standard Library's Test contract.

Forge Standard Library Cheatcodes

Forge Std implements wrappers around cheatcodes, which combine multiple standard cheatcodes to improve development experience. These are not technically cheatcodes, but rather compositions of Forge's cheatcodes.

You can view the list of Forge Standard Library's cheatcode wrappers in the references section. You can reference the Forge Std source code to learn more about how the wrappers work under the hood.

Cheatcode Types

Below are some subsections for the different Forge cheatcodes.

  • Environment: Cheatcodes that alter the state of the EVM.
  • Assertions: Cheatcodes that are powerful assertions
  • Fuzzer: Cheatcodes that configure the fuzzer
  • External: Cheatcodes that interact with external state (files, commands, ...)
  • Utilities: Smaller utility cheatcodes
  • Forking: Forking mode cheatcodes
  • Snapshots: Snapshot cheatcodes
  • RPC: RPC related cheatcodes
  • File: Cheatcodes for working with files

Cheatcodes Interface

This is a Solidity interface for all of the cheatcodes present in Forge.

interface CheatCodes {
    // This allows us to getRecordedLogs()
    struct Log {
        bytes32[] topics;
        bytes data;
    }

    // Set block.timestamp
    function warp(uint256) external;

    // Set block.number
    function roll(uint256) external;

    // Set block.basefee
    function fee(uint256) external;

    // Set block.difficulty
    function difficulty(uint256) external;

    // Set block.chainid
    function chainId(uint256) external;

    // Loads a storage slot from an address
    function load(address account, bytes32 slot) external returns (bytes32);

    // Stores a value to an address' storage slot
    function store(address account, bytes32 slot, bytes32 value) external;

    // Signs data
    function sign(uint256 privateKey, bytes32 digest)
        external
        returns (uint8 v, bytes32 r, bytes32 s);

    // Computes address for a given private key
    function addr(uint256 privateKey) external returns (address);

    // Derive a private key from a provided mnemonic string,
    // or mnemonic file path, at the derivation path m/44'/60'/0'/0/{index}.
    function deriveKey(string calldata, uint32) external returns (uint256);
    // Derive a private key from a provided mnemonic string, or mnemonic file path,
    // at the derivation path {path}{index}
    function deriveKey(string calldata, string calldata, uint32) external returns (uint256);

    // Gets the nonce of an account
    function getNonce(address account) external returns (uint64);

    // Sets the nonce of an account
    // The new nonce must be higher than the current nonce of the account
    function setNonce(address account, uint64 nonce) external;

    // Performs a foreign function call via terminal
    function ffi(string[] calldata) external returns (bytes memory);

    // Set environment variables, (name, value)
    function setEnv(string calldata, string calldata) external;

    // Read environment variables, (name) => (value)
    function envBool(string calldata) external returns (bool);
    function envUint(string calldata) external returns (uint256);
    function envInt(string calldata) external returns (int256);
    function envAddress(string calldata) external returns (address);
    function envBytes32(string calldata) external returns (bytes32);
    function envString(string calldata) external returns (string memory);
    function envBytes(string calldata) external returns (bytes memory);

    // Read environment variables as arrays, (name, delim) => (value[])
    function envBool(string calldata, string calldata)
        external
        returns (bool[] memory);
    function envUint(string calldata, string calldata)
        external
        returns (uint256[] memory);
    function envInt(string calldata, string calldata)
        external
        returns (int256[] memory);
    function envAddress(string calldata, string calldata)
        external
        returns (address[] memory);
    function envBytes32(string calldata, string calldata)
        external
        returns (bytes32[] memory);
    function envString(string calldata, string calldata)
        external
        returns (string[] memory);
    function envBytes(string calldata, string calldata)
        external
        returns (bytes[] memory);

    // Read environment variables with default value, (name, value) => (value)
    function envOr(string calldata, bool) external returns (bool);
    function envOr(string calldata, uint256) external returns (uint256);
    function envOr(string calldata, int256) external returns (int256);
    function envOr(string calldata, address) external returns (address);
    function envOr(string calldata, bytes32) external returns (bytes32);
    function envOr(string calldata, string calldata) external returns (string memory);
    function envOr(string calldata, bytes calldata) external returns (bytes memory);
    
    // Read environment variables as arrays with default value, (name, value[]) => (value[])
    function envOr(string calldata, string calldata, bool[] calldata) external returns (bool[] memory);
    function envOr(string calldata, string calldata, uint256[] calldata) external returns (uint256[] memory);
    function envOr(string calldata, string calldata, int256[] calldata) external returns (int256[] memory);
    function envOr(string calldata, string calldata, address[] calldata) external returns (address[] memory);
    function envOr(string calldata, string calldata, bytes32[] calldata) external returns (bytes32[] memory);
    function envOr(string calldata, string calldata, string[] calldata) external returns (string[] memory);
    function envOr(string calldata, string calldata, bytes[] calldata) external returns (bytes[] memory);

    // Convert Solidity types to strings
    function toString(address) external returns(string memory);
    function toString(bytes calldata) external returns(string memory);
    function toString(bytes32) external returns(string memory);
    function toString(bool) external returns(string memory);
    function toString(uint256) external returns(string memory);
    function toString(int256) external returns(string memory);

    // Sets the *next* call's msg.sender to be the input address
    function prank(address) external;

    // Sets all subsequent calls' msg.sender to be the input address
    // until `stopPrank` is called
    function startPrank(address) external;

    // Sets the *next* call's msg.sender to be the input address,
    // and the tx.origin to be the second input
    function prank(address, address) external;

    // Sets all subsequent calls' msg.sender to be the input address until
    // `stopPrank` is called, and the tx.origin to be the second input
    function startPrank(address, address) external;

    // Resets subsequent calls' msg.sender to be `address(this)`
    function stopPrank() external;

    // Sets an address' balance
    function deal(address who, uint256 newBalance) external;

    // Sets an address' code
    function etch(address who, bytes calldata code) external;

    // Expects an error on next call
    function expectRevert() external;
    function expectRevert(bytes calldata) external;
    function expectRevert(bytes4) external;

    // Record all storage reads and writes
    function record() external;

    // Gets all accessed reads and write slot from a recording session,
    // for a given address
    function accesses(address)
        external
        returns (bytes32[] memory reads, bytes32[] memory writes);

    // Record all the transaction logs
    function recordLogs() external;

    // Gets all the recorded logs
    function getRecordedLogs() external returns (Log[] memory);

    // Prepare an expected log with the signature:
    //   (bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData).
    //
    // Call this function, then emit an event, then call a function.
    // Internally after the call, we check if logs were emitted in the expected order
    // with the expected topics and data (as specified by the booleans)
    //
    // The second form also checks supplied address against emitting contract.
    function expectEmit(bool, bool, bool, bool) external;
    function expectEmit(bool, bool, bool, bool, address) external;

    // Mocks a call to an address, returning specified data.
    //
    // Calldata can either be strict or a partial match, e.g. if you only
    // pass a Solidity selector to the expected calldata, then the entire Solidity
    // function will be mocked.
    function mockCall(address, bytes calldata, bytes calldata) external;

    // Clears all mocked calls
    function clearMockedCalls() external;

    // Expect a call to an address with the specified calldata.
    // Calldata can either be strict or a partial match
    function expectCall(address, bytes calldata) external;
    // Expect a call to an address with the specified
    // calldata and message value.
    // Calldata can either be strict or a partial match
    function expectCall(address, uint256, bytes calldata) external;

    // Gets the _creation_ bytecode from an artifact file. Takes in the relative path to the json file
    function getCode(string calldata) external returns (bytes memory);
    // Gets the _deployed_ bytecode from an artifact file. Takes in the relative path to the json file
    function getDeployedCode(string calldata) external returns (bytes memory);

    // Label an address in test traces
    function label(address addr, string calldata label) external;

    // When fuzzing, generate new inputs if conditional not met
    function assume(bool) external;

    // Set block.coinbase (who)
    function coinbase(address) external;

    // Using the address that calls the test contract or the address provided
    // as the sender, has the next call (at this call depth only) create a
    // transaction that can later be signed and sent onchain
    function broadcast() external;
    function broadcast(address) external;

    // Using the address that calls the test contract or the address provided
    // as the sender, has all subsequent calls (at this call depth only) create
    // transactions that can later be signed and sent onchain
    function startBroadcast() external;
    function startBroadcast(address) external;

    // Stops collecting onchain transactions
    function stopBroadcast() external;

    // Reads the entire content of file to string, (path) => (data)
    function readFile(string calldata) external returns (string memory);
    // Get the path of the current project root
    function projectRoot() external returns (string memory);
    // Reads next line of file to string, (path) => (line)
    function readLine(string calldata) external returns (string memory);
    // Writes data to file, creating a file if it does not exist, and entirely replacing its contents if it does.
    // (path, data) => ()
    function writeFile(string calldata, string calldata) external;
    // Writes line to file, creating a file if it does not exist.
    // (path, data) => ()
    function writeLine(string calldata, string calldata) external;
    // Closes file for reading, resetting the offset and allowing to read it from beginning with readLine.
    // (path) => ()
    function closeFile(string calldata) external;
    // Removes file. This cheatcode will revert in the following situations, but is not limited to just these cases:
    // - Path points to a directory.
    // - The file doesn't exist.
    // - The user lacks permissions to remove the file.
    // (path) => ()
    function removeFile(string calldata) external;
    
    // Return the value(s) that correspond to 'key'
    function parseJson(string memory json, string memory key) external returns (bytes memory);
    // Return the entire json file
    function parseJson(string memory json) external returns (bytes memory);

    // Snapshot the current state of the evm.
    // Returns the id of the snapshot that was created.
    // To revert a snapshot use `revertTo`
    function snapshot() external returns (uint256);
    // Revert the state of the evm to a previous snapshot
    // Takes the snapshot id to revert to.
    // This deletes the snapshot and all snapshots taken after the given snapshot id.
    function revertTo(uint256) external returns (bool);

    // Creates a new fork with the given endpoint and block,
    // and returns the identifier of the fork
    function createFork(string calldata, uint256) external returns (uint256);
    // Creates a new fork with the given endpoint and the _latest_ block,
    // and returns the identifier of the fork
    function createFork(string calldata) external returns (uint256);

    // Creates _and_ also selects a new fork with the given endpoint and block,
    // and returns the identifier of the fork
    function createSelectFork(string calldata, uint256)
        external
        returns (uint256);
    // Creates _and_ also selects a new fork with the given endpoint and the
    // latest block and returns the identifier of the fork
    function createSelectFork(string calldata) external returns (uint256);

    // Takes a fork identifier created by `createFork` and
    // sets the corresponding forked state as active.
    function selectFork(uint256) external;

    // Returns the currently active fork
    // Reverts if no fork is currently active
    function activeFork() external returns (uint256);

    // Updates the currently active fork to given block number
    // This is similar to `roll` but for the currently active fork
    function rollFork(uint256) external;
    // Updates the given fork to given block number
    function rollFork(uint256 forkId, uint256 blockNumber) external;

    // Fetches the given transaction from the active fork and executes it on the current state
    function transact(bytes32) external;
    // Fetches the given transaction from the given fork and executes it on the current state
    function transact(uint256, bytes32) external;

    // Marks that the account(s) should use persistent storage across
    // fork swaps in a multifork setup, meaning, changes made to the state
    // of this account will be kept when switching forks
    function makePersistent(address) external;
    function makePersistent(address, address) external;
    function makePersistent(address, address, address) external;
    function makePersistent(address[] calldata) external;
    // Revokes persistent status from the address, previously added via `makePersistent`
    function revokePersistent(address) external;
    function revokePersistent(address[] calldata) external;
    // Returns true if the account is marked as persistent
    function isPersistent(address) external returns (bool);

    /// Returns the RPC url for the given alias
    function rpcUrl(string calldata) external returns (string memory);
    /// Returns all rpc urls and their aliases `[alias, url][]`
    function rpcUrls() external returns (string[2][] memory);
}

Environment

warp

Signature

function warp(uint256) external;

Description

Sets block.timestamp.

Examples

vm.warp(1641070800);
emit log_uint(block.timestamp); // 1641070800

SEE ALSO

Forge Standard Library

skip, rewind

roll

Signature

function roll(uint256) external;

Description

Sets block.number.

Examples

vm.roll(100);
emit log_uint(block.number); // 100

SEE ALSO

fee

Signature

function fee(uint256) external;

Description

Sets block.basefee.

Examples

vm.fee(25 gwei);
emit log_uint(block.basefee); // 25000000000

difficulty

Signature

function difficulty(uint256) external;

Description

Sets block.difficulty.

Examples

vm.difficulty(25);
emit log_uint(block.difficulty); // 25

chainId

Signature

function chainId(uint256) external;

Description

Sets block.chainid.

Examples

vm.chainId(31337);
emit log_uint(block.chainid); // 31337

store

Signature

function store(address account, bytes32 slot, bytes32 value) external;

Description

Stores the value value in storage slot slot on account account.

Examples

/// contract LeetContract {
///     uint256 private leet = 1337; // slot 0
/// }

vm.store(address(leetContract), bytes32(uint256(0)), bytes32(uint256(31337)));
bytes32 leet = vm.load(address(leetContract), bytes32(uint256(0)));
emit log_uint(uint256(leet)); // 31337

SEE ALSO

Forge Standard Library

Std Storage

load

Signature

function load(address account, bytes32 slot) external returns (bytes32);

Description

Loads the value from storage slot slot on account account.

Examples

/// contract LeetContract {
///     uint256 private leet = 1337; // slot 0
/// }

bytes32 leet = cheats.load(address(leetContract), bytes32(uint256(0)));
emit log_uint(uint256(leet)); // 1337

SEE ALSO

Forge Standard Library

Std Storage

etch

Signature

function etch(address who, bytes calldata code) external;

Description

Sets the bytecode of an address who to code.

Examples

bytes memory code = address(awesomeContract).code;
address targetAddr = address(1);
vm.etch(targetAddr, code);
log_bytes(address(targetAddr).code); // 0x6080604052348015610010...

SEE ALSO

Forge Standard Library

deployCode

deal

Signature

function deal(address who, uint256 newBalance) external;

Description

Sets the balance of an address who to newBalance.

Examples

address alice = address(1);
vm.deal(alice, 1 ether);
log_uint256(alice.balance); // 1000000000000000000

SEE ALSO

Forge Standard Library

deal, hoax, startHoax

prank

Signature

function prank(address) external;
function prank(address sender, address origin) external;

Description

Sets msg.sender to the specified address for the next call. "The next call" includes static calls as well, but not calls to the cheat code address.

If the alternative signature of prank is used, then tx.origin is set as well for the next call.

Examples

/// function withdraw() public {
///     require(msg.sender == owner);

vm.prank(owner);
myContract.withdraw(); // [PASS]

SEE ALSO

Forge Standard Library

hoax

startPrank

Signature

function startPrank(address) external;
function startPrank(address sender, address origin) external;

Description

Sets msg.sender for all subsequent calls until stopPrank is called.

If the alternative signature of startPrank is used, then tx.origin is set as well for all subsequent calls.

SEE ALSO

Forge Standard Library

startHoax, changePrank

stopPrank

Signature

function stopPrank() external;

Description

Stops an active prank started by startPrank, resetting msg.sender and tx.origin to the values before startPrank was called.

record

Signature

function record() external;

Description

Tell the VM to start recording all storage reads and writes. To access the reads and writes, use accesses.

ℹ️ Note

Every write also counts as an additional read.

Examples

/// contract NumsContract {
///     uint256 public num1 = 100; // slot 0
///     uint256 public num2 = 200; // slot 1
/// }

vm.record();
numsContract.num2();
(bytes32[] memory reads, bytes32[] memory writes) = vm.accesses(
  address(numsContract)
);
emit log_uint(uint256(reads[0])); // 1

SEE ALSO

Forge Standard Library

Std Storage

accesses

Signature

function accesses(
  address
)
external
returns (
  bytes32[] memory reads,
  bytes32[] memory writes
);

Description

Gets all storage slots that have been read (reads) or written to (writes) on an address.

Note that record must be called first.

ℹ️ Note

Every write also counts as an additional read.

Examples

/// contract NumsContract {
///     uint256 public num1 = 100; // slot 0
///     uint256 public num2 = 200; // slot 1
/// }

vm.record();
numsContract.num2();
(bytes32[] memory reads, bytes32[] memory writes) = vm.accesses(
  address(numsContract)
);
emit log_uint(uint256(reads[0])); // 1

recordLogs

Signature

function recordLogs() external;

Description

Tells the VM to start recording all the emitted events. To access them, use getRecordedLogs.

Examples

/// event LogCompleted(
///   uint256 indexed topic1,
///   bytes data
/// );

vm.recordLogs();

emit LogCompleted(10, "operation completed");

Vm.Log[] memory entries = vm.getRecordedLogs();

assertEq(entries.length, 1);
assertEq(entries[0].topics[0], keccak256("LogCompleted(uint256,bytes)"));
assertEq(entries[0].topics[1], bytes32(uint256(10)));
assertEq(abi.decode(entries[0].data, (string)), "operation completed");

getRecordedLogs

Signature

struct Log {
  bytes32[] topics;
  bytes data;
}

function getRecordedLogs()
external
returns (
  Log[] memory
);

Description

Gets the emitted events recorded by recordLogs.

This function will consume the recorded logs when called.

Examples

/// event LogTopic1(
///   uint256 indexed topic1,
///   bytes data
/// );

/// event LogTopic12(
///   uint256 indexed topic1,
///   uint256 indexed topic2,
///   bytes data
/// );

/// bytes memory testData0 = "Some data";
/// bytes memory testData1 = "Other data";


// Start the recorder
vm.recordLogs();

emit LogTopic1(10, testData0);
emit LogTopic12(20, 30, testData1);

// Notice that your entries are <Interface>.Log[]
// as opposed to <instance>.Log[]
Vm.Log[] memory entries = vm.getRecordedLogs();

assertEq(entries.length, 2);

// Recall that topics[0] is the event signature
assertEq(entries[0].topics.length, 2);
assertEq(entries[0].topics[0], keccak256("LogTopic1(uint256,bytes)"));
assertEq(entries[0].topics[1], bytes32(uint256(10)));
// assertEq won't compare bytes variables. Try with strings instead.
assertEq(abi.decode(entries[0].data, (string)), string(testData0));

assertEq(entries[1].topics.length, 3);
assertEq(entries[1].topics[0], keccak256("LogTopic12(uint256,uint256,bytes)"));
assertEq(entries[1].topics[1], bytes32(uint256(20)));
assertEq(entries[1].topics[2], bytes32(uint256(30)));
assertEq(abi.decode(entries[1].data, (string)), string(testData1));

// Emit another event
emit LogTopic1(40, testData0);

// Your last read consumed the recorded logs,
// you will only get the latest emitted even after that call
entries = vm.getRecordedLogs();

assertEq(entries.length, 1);

assertEq(entries[0].topics.length, 2);
assertEq(entries[0].topics[0], keccak256("LogTopic1(uint256,bytes)"));
assertEq(entries[0].topics[1], bytes32(uint256(40)));
assertEq(abi.decode(entries[0].data, (string)), string(testData0));

setNonce

Signature

function setNonce(address account, uint64 nonce) external;

Description

Sets the nonce of the given account.

The new nonce must be higher than the current nonce of the account.

Examples

vm.setNonce(address(100), 1234);

getNonce

Signature

function getNonce(address account) external returns (uint64);

Description

Gets the nonce of the given account.

Examples

uint256 nonce = vm.getNonce(address(100));
emit log_uint(nonce); // 0

mockCall

Signature

function mockCall(address where, bytes calldata data, bytes calldata retdata) external;
function mockCall(
    address where,
    uint256 value,
    bytes calldata data,
    bytes calldata retdata
) external;

Description

Mocks all calls to an address where if the call data either strictly or loosely matches data and returns retdata.

When a call is made to where the call data is first checked to see if it matches in its entirety with data. If not, the call data is checked to see if there is a partial match, with the match starting at the first byte of the call data.

If a match is found, then retdata is returned from the call.

Using the second signature we can mock the calls with a specific msg.value. Calldata match takes precedence over msg.value in case of ambiguity.

Mocked calls are in effect until clearMockedCalls is called.

ℹ️ Note

Calls to mocked addresses may revert if there is no code on the address. This is because Solidity inserts an extcodesize check before some contract calls.

To circumvent this, use the etch cheatcode if the mocked address has no code.

ℹ️ Internal calls

This cheatcode does not currently work on internal calls. See issue #432.

Examples

Mocking an exact call:

function testMockCall() public {
    vm.mockCall(
        address(0),
        abi.encodeWithSelector(MyToken.balanceOf.selector, address(1)),
        abi.encode(10)
    );
    assertEq(IERC20(address(0)).balanceOf(address(1)), 10);
}

Mocking an entire function:

function testMockCall() public {
    vm.mockCall(
        address(0),
        abi.encodeWithSelector(MyToken.balanceOf.selector),
        abi.encode(10)
    );
    assertEq(IERC20(address(0)).balanceOf(address(1)), 10);
    assertEq(IERC20(address(0)).balanceOf(address(2)), 10);
}

Mocking a call with a given msg.value:

function testMockCall() public {
    assertEq(example.pay{value: 10}(1), 1);
    assertEq(example.pay{value: 1}(2), 2);
    vm.mockCall(
        address(example),
        10,
        abi.encodeWithSelector(example.pay.selector),
        abi.encode(99)
    );
    assertEq(example.pay{value: 10}(1), 99);
    assertEq(example.pay{value: 1}(2), 2);
}

clearMockedCalls

Signature

function clearMockedCalls() external;

Description

Clears all mocked calls.

coinbase

Signature

function coinbase(address) external;

Description

Sets block.coinbase.

Examples

emit log_address(block.coinbase); // 0x0000000000000000000000000000000000000000
vm.coinbase(0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8);
emit log_address(block.coinbase); // 0xea674fdde714fd979de3edf0f56aa9716b898ec8

broadcast

Signature

function broadcast() external;
function broadcast(address who) external;
function broadcast(uint256 privateKey) external;

Description

Using the address that calls the test contract or the address / private key provided as the sender, have the next call (at this call depth only) create a transaction that can later be signed and sent onchain.

Examples

function deploy() public {
    cheats.broadcast(ACCOUNT_A);
    Test test = new Test();

    // this won't generate tx to sign
    uint256 b = test.t(4);

    // this will
    cheats.broadcast(ACCOUNT_B);
    test.t(2);

    // this also will, using a private key from your environment variables
    cheats.broadcast(vm.envUint("PRIVATE_KEY"));
    test.t(3);
} 

startBroadcast

Signature

function startBroadcast() external;
function startBroadcast(address who) external;
function startBroadcast(uint256 privateKey) external;

Description

Using the address that calls the test contract or the address / private key provided as the sender, have all subsequent calls (at this call depth only) create transactions that can later be signed and sent onchain.

Examples

function t(uint256 a) public returns (uint256) {
    uint256 b = 0;
    emit log_string("here");
    return b;
}

function deployOther() public {
    cheats.startBroadcast(ACCOUNT_A);
    Test test = new Test();
    
    // will trigger a transaction
    test.t(1);
    
    cheats.stopBroadcast();

    // broadcast again, this time using a private key from your environment variables
    cheats.startBroadcast(vm.envUint("PRIVATE_KEY"));
    test.t(3);
    cheats.stopBroadcast();
}

stopBroadcast

Signature

function stopBroadcast() external;

Description

Stops collecting transactions for later on-chain broadcasting.

Examples

function deployNoArgs() public {
    // broadcast the next call
    cheats.broadcast();
    Test test1 = new Test();

    // broadcast all calls between this line and `stopBroadcast`
    cheats.startBroadcast();
    Test test2 = new Test();
    cheats.stopBroadcast();
}

pauseGasMetering

Signature

function pauseGasMetering() external;

Description

Pauses gas metering (i.e. gasleft() does not decrease as operations are executed).

This can be useful for getting a better sense of gas costs, by turning off gas metering for unnecessary code, as well as long-running scripts that would otherwise run out of gas.

ℹ️ Note

pauseGasMetering turns off DoS protections that come from metering gas usage.

Exposing a service that assumes a particular instance of the EVM will complete due to gas usage no longer is true, and a timeout should be enabled in that case.

resumeGasMetering

Signature

function resumeGasMetering() external;

Description

Resumes gas metering (i.e. gasleft() will decrease as operations are executed). Gas usage will resume at the same amount at which it was paused.

Assertions

expectRevert

Signature

function expectRevert() external;
function expectRevert(bytes4 msg) external;
function expectRevert(bytes calldata msg) external;

Description

If the next call does not revert with the expected message msg, then expectRevert will.

After calling expectRevert, calls to other cheatcodes before the reverting call are ignored.

This means, for example, we can call prank immediately before the reverting call.

There are 3 signatures:

  • Without parameters: Asserts that the next call reverts, regardless of the message.
  • With bytes4: Asserts that the next call reverts with the specified 4 bytes.
  • With bytes: Asserts that the next call reverts with the specified bytes.

⚠️ Gotcha: Usage with low-level calls

Normally, a call that succeeds returns a status of true (along with any return data) and a call that reverts returns false.

The Solidity compiler will insert checks that ensures that the call succeeded, and revert if it did not.

The expectRevert cheatcode works by inverting this, so the next call after this cheatcode returns true if it reverts, and false otherwise.

The implication here is that to use this cheatcode with low-level calls, you must manually assert on the call's status since Solidity is not doing it for you.

For example:

function testLowLevelCallRevert() public {
    vm.expectRevert(bytes("error message"));
    (bool status, ) = address(myContract).call(myCalldata);
    assertTrue(status, "expectRevert: call did not revert");
}

Examples

To use expectRevert with a string, convert it to bytes.

vm.expectRevert(bytes("error message"));

To use expectRevert with a custom error type without parameters, use its selector.

vm.expectRevert(MyContract.CustomError.selector);

To use expectRevert with a custom error type with parameters, ABI encode the error type.

vm.expectRevert(
    abi.encodeWithSelector(MyContract.CustomError.selector, 1, 2)
);

If you need to assert that a function reverts without a message, you can do so with expectRevert(bytes("")).

function testExpectRevertNoReason() public {
    Reverter reverter = new Reverter();
    vm.expectRevert(bytes(""));
    reverter.revertWithoutReason();
}

If you need to assert that a function reverts a four character message, e.g. AAAA, you can do so with:

function testFourLetterMessage() public {
    
    vm.expectRevert(bytes("AAAA"));
}

If used expectRevert("AAAA") the overload expectRevert(bytes4 msg) is used, resulting in a different behaviour.

You can also have multiple expectRevert() checks in a single test.

function testMultipleExpectReverts() public {
    vm.expectRevert(abi.encodePacked("INVALID_AMOUNT"));
    vault.send(user, 0);

    vm.expectRevert(abi.encodePacked("INVALID_ADDRESS"));
    vault.send(address(0), 200);
}

SEE ALSO

Forge Standard Library

Std Errors

expectEmit

Signature

function expectEmit(
    bool checkTopic1,
    bool checkTopic2,
    bool checkTopic3,
    bool checkData
) external;
function expectEmit(
    bool checkTopic1,
    bool checkTopic2,
    bool checkTopic3,
    bool checkData,
    address emitter
) external;

Description

Assert a specific log is emitted before the end of the current function.

  1. Call the cheat code, specifying whether we should check the first, second or third topic, and the log data. Topic 0 is always checked.
  2. Emit the event we are supposed to see before the end of the current function.
  3. Perform the call.

If the event is not available in the current scope (e.g. if we are using an interface, or an external smart contract), we can define the event ourselves with an identical event signature.

There are 2 signatures:

  • Without checking the emitter address: Asserts the topics match without checking the emitting address.
  • With address: Asserts the topics match and that the emitting address matches.

ℹ️ Ordering matters

If we call expectEmit and emit an event, then the next event emitted must match the one we expect.

Examples

This does not check the emitting address.

event Transfer(address indexed from, address indexed to, uint256 amount);

function testERC20EmitsTransfer() public {
    // Only `from` and `to` are indexed in ERC20's `Transfer` event,
    // so we specifically check topics 1 and 2 (topic 0 is always checked by default),
    // as well as the data (`amount`).
    vm.expectEmit(true, true, false, true);

    // We emit the event we expect to see.
    emit MyToken.Transfer(address(this), address(1), 10);

    // We perform the call.
    myToken.transfer(address(1), 10);
}

This does check the emitting address.

event Transfer(address indexed from, address indexed to, uint256 amount);

function testERC20EmitsTransfer() public {
    // We check that the token is the event emitter by passing the address as the fifth argument.
    vm.expectEmit(true, true, false, true, address(myToken));
    emit MyToken.Transfer(address(this), address(1), 10);

    // We perform the call.
    myToken.transfer(address(1), 10);
}

We can also assert that multiple events are emitted in a single call.

function testERC20EmitsBatchTransfer() public {
    // We declare multiple expected transfer events
    for (uint256 i = 0; i < users.length; i++) {
        vm.expectEmit(true, true, true, true);
        emit Transfer(address(this), users[i], 10);
    }

    // We also expect a custom `BatchTransfer(uint256 numberOfTransfers)` event.
    vm.expectEmit(false, false, false, false);
    emit BatchTransfer(users.length);

    // We perform the call.
    myToken.batchTransfer(users, 10);
}

expectCall

function expectCall(address where, bytes calldata data) external;
function expectCall(
    address where,
    uint256 value,
    bytes calldata data
) external;

Description

Expects at least one call to address where, where the call data either strictly or loosely matches data.

When a call is made to where the call data is first checked to see if it matches in its entirety with data. If not, the call data is checked to see if there is a partial match, with the match starting at the first byte of the call data.

Using the second signature we can also check if the call was made with the expected msg.value.

If the test terminates without the call being made, the test fails.

ℹ️ Internal calls

This cheatcode does not currently work on internal calls. See issue #432.

Examples

Expect that transfer is called on a token MyToken:

address alice = address(10);
vm.expectCall(
  address(token), abi.encodeCall(token.transfer, (alice, 10))
);
token.transfer(alice, 10);
// [PASS]

Expect that pay is called on a Contract with a specific msg.value and calldata:

Contract target = new Contract();
vm.expectCall(
            address(target),
            1,
            abi.encodeWithSelector(target.pay.selector, 2)
        );
target.pay{value: 1}(2);
// [PASS]

Fuzzer

assume

Signature

function assume(bool) external;

Description

If the boolean expression evaluates to false, the fuzzer will discard the current fuzz inputs and start a new fuzz run.

The assume cheatcode should mainly be used for very narrow checks. Broad checks will slow down tests as it will take a while to find valid values, and the test may fail if you hit the max number of rejects.

You can configure the rejection thresholds by setting fuzz.max_test_rejects in your foundry.toml file.

For broad checks, such as ensuring a uint256 falls within a certain range, you can bound your input with the modulo operator or Forge Standard's bound method.

More information on filtering via assume can be found here.

Examples

// Good example of using assume
function testSomething(uint256 a) public {
    vm.assume(a != 1);
    require(a != 1);
    // [PASS]
}
// In this case assume is not a great fit, so you should bound inputs manually
function testSomethingElse(uint256 a) public {
    a = bound(a, 100, 1e36);
    require(a >= 100 && a <= 1e36);
    // [PASS]
}

SEE ALSO

Forge Standard Library

bound

Forking

createFork

Signature

// Creates a new fork with the given endpoint and the _latest_ block and returns the identifier of the fork
function createFork(string calldata urlOrAlias) external returns (uint256)
// Creates _and_ also selects a new fork with the given endpoint and block and returns the identifier of the fork
function createFork(string calldata urlOrAlias, uint256 block) external returns (uint256);
// Creates a new fork with the given endpoint and at the block the given transaction was mined in, and replays all transaction mined in the block before the transaction
function createFork(string calldata urlOrAlias, bytes32 transaction) external returns (uint256);

Description

Creates a new fork from the given endpoint and returns the identifier of the fork. If a block number is passed as an argument, the fork will begin on that block, otherwise it will begin on the latest block.

If a transaction hash is provided, it will roll the fork to the block the transaction was mined in and replays all previously executed transactions.

Examples

Create a new mainnet fork with the latest block number:

uint256 forkId = vm.createFork(MAINNET_RPC_URL);
vm.selectFork(forkId);

assertEq(block.number, 15_171_037); // as of time of writing, 2022-07-19 04:55:27 UTC

Create a new mainnet fork with a given block number:

uint256 forkId = vm.createFork(MAINNET_RPC_URL, 1_337_000);
vm.selectFork(forkId);

assertEq(block.number, 1_337_000);

SEE ALSO

selectFork

Signature

function selectFork(uint256 forkId) external;

Description

Takes a fork identifier created by createFork and sets the corresponding forked state as active.

Examples

Select a previously created fork:

uint256 forkId = vm.createFork(MAINNET_RPC_URL);

vm.selectFork(forkId);

assertEq(vm.activeFork(), forkId);

SEE ALSO

createSelectFork

Signature

function createSelectFork(string calldata urlOrAlias) external returns (uint256);
function createSelectFork(string calldata urlOrAlias, uint256 block) external returns (uint256);

Description

Creates and selects a new fork from the given endpoint and returns the identifier of the fork. If a block number is passed as an argument, the fork will begin on that block, otherwise it will begin on the latest block.

Examples

Create and select a new mainnet fork with the latest block number:

uint256 forkId = vm.createSelectFork(MAINNET_RPC_URL);

assertEq(block.number, 15_171_037); // as of time of writing, 2022-07-19 04:55:27 UTC

Create and select a new mainnet fork with a given block number:

uint256 forkId = vm.createSelectFork(MAINNET_RPC_URL, 1_337_000);

assertEq(block.number, 1_337_000);

SEE ALSO

activeFork

Signature

function activeFork() external returns (uint256);

Description

Returns the identifier for the currently active fork. Reverts if no fork is currently active.

Examples

Get the currently active fork id:

uint256 mainnetForkId = vm.createFork(MAINNET_RPC_URL);
uint256 optimismForkId = vm.createFork(OPTIMISM_RPC_URL);

assert(mainnetForkId != optimismForkId);

vm.selectFork(mainnetForkId);
assertEq(vm.activeFork(), mainnetForkId);

vm.selectFork(optimismForkId);
assertEq(vm.activeFork(), optimismForkId);

SEE ALSO

rollFork

Signature

// roll the _active_ fork to the given block
function rollFork(uint256 blockNumber) external;
// roll the _active_ fork to the block in which the transaction was mined it and replays all previously executed transactions
function rollFork(bytes32 transaction) external;
// Same as `rollFork(uint256 blockNumber)` but uses the fork corresponding to the `forkId`
function rollFork(uint256 forkId, uint256 blockNumber) external;
// Same as `rollFork(bytes32 transaction)` but uses the fork corresponding to the `forkId`
function rollFork(uint256 forkId, bytes32 transaction) external;

Description

Sets block.number. If a fork identifier is passed as an argument, it will update that fork, otherwise it will update the currently active fork.

If a transaction hash is provided, it will roll the fork to the block the transaction was mined in and replays all previously executed transactions.

Examples

Set block.number for the currently active fork:

uint256 forkId = vm.createFork(MAINNET_RPC_URL);
vm.selectFork(forkId);

assertEq(block.number, 15_171_037); // as of time of writing, 2022-07-19 04:55:27 UTC

vm.rollFork(15_171_057);

assertEq(block.number, 15_171_057);

Set block.number for the fork identified by the passed forkId argument:

uint256 optimismForkId = vm.createFork(OPTIMISM_RPC_URL);

vm.rollFork(optimismForkId, 1_337_000);

vm.selectFork(optimismForkId);

assertEq(block.number, 1_337_000);

SEE ALSO

makePersistent

Signature

function makePersistent(address) external;
function makePersistent(address, address) external;
function makePersistent(address, address, address) external;
function makePersistent(address[] calldata) external;

Description

Each fork (createFork) has its own independent storage, which is also replaced when another fork is selected (selectFork). By default, only the test contract account and the caller are persistent across forks, which means that changes to the state of the test contract (variables) are preserved when different forks are selected. This way data can be shared by storing it in the contract's variables.

However, with this cheatcode, it is possible to mark the specified accounts as persistent, which means that their state is available regardless of which fork is currently active.

Examples

Mark a new contract as persistent

contract SimpleStorageContract {
    string public value;

    function set(uint256 _value) public {
        value = _value;
    }
}

function testMarkPersistent() public {
    // by default the `sender` and the contract itself are persistent
    assert(cheats.isPersistent(msg.sender));
    assert(cheats.isPersistent(address(this)));

    // select a specific fork
    cheats.selectFork(mainnetFork);
    
    // create a new contract that's stored in the `mainnetFork` storage
    SimpleStorageContract simple = new SimpleStorageContract();
    
    // `simple` is not marked as persistent
    assert(!cheats.isPersistent(address(simple)));
    
    // contract can be used
    uint256 expectedValue = 99;
    simple.set(expectedValue);
    assertEq(simple.value(), expectedValue);
    
    // mark as persistent
    cheats.makePersistent(address(simple));
    
    // select a different fork
    cheats.selectFork(optimismFork);
    
    // ensure contract is still persistent   
    assert(cheats.isPersistent(address(simple)));
    
    // value is set as expected
    assertEq(simple.value(), expectedValue);
}

SEE ALSO

revokePersistent

Signature

    function revokePersistent(address) external;
    function revokePersistent(address[] calldata) external;

Description

The counterpart of makePersistent, that makes the given contract not persistent across fork swaps

Examples

Revoke a persistent status of a contract

contract SimpleStorageContract {
    string public value;

    function set(uint256 _value) public {
        value = _value;
    }
}

function testRevokePersistent() public {
    // select a specific fork
    cheats.selectFork(mainnetFork);
    
    // create a new contract that's stored in the `mainnetFork` storage
    SimpleStorageContract simple = new SimpleStorageContract();
    
    // `simple` is not marked as persistent
    assert(!cheats.isPersistent(address(simple)));
       
    // make it persistent
    cheats.makePersistent(address(simple));
    
    // ensure it is persistent
    assert(cheats.isPersistent(address(simple)));
    
    // revoke it
    cheats.revokePersistent(address(simple));
    
    // contract no longer persistent
    assert(!cheats.isPersistent(address(simple)));
}

SEE ALSO

isPersistent

Signature

 function isPersistent(address) external returns (bool);

Description

Returns whether an account is marked as persistent (makePersistent).

Examples

Check default status of msg.sender and the current test account

// By default the `sender` and the test contract itself are persistent
assert(cheats.isPersistent(msg.sender));
assert(cheats.isPersistent(address(this)));

SEE ALSO

allowCheatcodes

Signature

function allowCheatcodes(address) external;

Description

In forking mode, explicitly grant the given address cheatcode access.

By default, the test contract, and its deployer are allowed to access cheatcodes. In addition to that, cheat code access is granted if the contract was deployed by an address that already has cheatcode access. This will prevent cheatcode access from accounts already deployed on the forked network.

ℹ️ Note

This is only useful for complexer test setup in forking mode.

transact

Signature

// Fetches the given transaction from the active fork and executes it on the current state
function transact(bytes32 txHash) external;
// Fetches the given transaction from the given fork and executes it on the current state
function transact(uint256 forkId, bytes32 txHash) external;

Description

In forking mode, fetches the Transaction from the provider and executes it on the current state

Examples

Enter forking mode and execute a transaction:

// Enter forking mode at block: https://etherscan.io/block/15596646
uint256 fork = vm.createFork(MAINNET_RPC_URL, 15596646);
vm.selectFork(fork);

// a random transfer transaction in the block: https://etherscan.io/tx/0xaba74f25a17cf0d95d1c6d0085d6c83fb8c5e773ffd2573b99a953256f989c89
bytes32 tx = 0xaba74f25a17cf0d95d1c6d0085d6c83fb8c5e773ffd2573b99a953256f989c89;

address sender = address(0xa98218cdc4f63aCe91ddDdd24F7A580FD383865b);
address recipient = address(0x0C124046Fa7202f98E4e251B50488e34416Fc306);

assertEq(sender.balance, 5764124000000000);
assertEq(recipient.balance, 3936000000000000);

// transfer amount: 0.000336 Ether
uint256 transferAmount = 3936000000000000;

// expected balance changes once the transaction is executed
uint256 expectedRecipientBalance = recipient.balance + transferAmount;
uint256 expectedSenderBalance = sender.balance - transferAmount;

// execute the transaction
vm.transact(tx);

// recipient received transfer
assertEq(recipient.balance, expectedRecipientBalance);

// sender's balance decreased by transferAmount and gas
assert(sender.balance < expectedSenderBalance);

SEE ALSO

External

ffi

Signature

function ffi(string[] calldata) external returns (bytes memory);

Description

Calls an arbitrary command if ffi is enabled.

It is generally advised to use this cheat code as a last resort, and to not enable it by default, as anyone who can change the tests of a project will be able to execute arbitrary commands on devices that run the tests.

Tips

  • By default the ffi cheatcode assumes the output of the command is a hex encoded value (e.g. a hex string of an ABI encoded value). If hex decoding fails, it will return the output as UTF8 bytes that you can cast to a string.
  • Make sure that the output does not include a \n newline character. (e.g in Rust use print! vs println!)
  • Remember that the script will be executed from the top-level directory of your project, not inside test
  • Make sure that the inputs array does not have empty elements. They will be handled as inputs by the script, instead of space
  • Use the cheatcode toString to easily convert arbitrary data to strings, so that you can pass them as command-line arguments

Examples

ABI encoded output

string[] memory inputs = new string[](3);
inputs[0] = "echo";
inputs[1] = "-n";
// ABI encoded "gm", as a hex string
inputs[2] = "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002676d000000000000000000000000000000000000000000000000000000000000";

bytes memory res = vm.ffi(inputs);
string memory output = abi.decode(res, (string));
assertEq(output, "gm");

UTF8 string output

string[] memory inputs = new string[](3);
inputs[0] = "echo";
inputs[1] = "-n";
inputs[2] = "gm";

bytes memory res = vm.ffi(inputs);
assertEq(string(res), "gm");

projectRoot

Signature

function projectRoot() external returns (string memory);

Description

Returns the root directory of the current Foundry project.

getCode

Signature

function getCode(string calldata) external returns (bytes memory);

Description

Returns the creation bytecode for a contract in the project given the path to the contract.

The calldata parameter can either be in the form ContractFile.sol (if the filename and contract name are the same), ContractFile.sol:ContractName, or the path to an artifact, relative to the root of your project.

ℹ️ Note

getCode requires read permission for the output directory, see file cheatcodes.

To grant read access set fs_permissions = [{ access = "read", path = "./out"}] in your foundry.toml.

Examples

MyContract myContract = new MyContract(arg1, arg2);

// Let's do the same thing with `getCode`
bytes memory args = abi.encode(arg1, arg2);
bytes memory bytecode = abi.encodePacked(vm.getCode("MyContract.sol:MyContract"), args);
address anotherAddress;
assembly {
    anotherAddress := create(0, add(bytecode, 0x20), mload(bytecode))
}

assertEq0(address(myContract).code, anotherAddress.code); // [PASS]

Deploy a contract to an arbitrary address by combining getCode and etch


// Deploy
bytes memory args = abi.encode(arg1, arg2);
bytes memory bytecode = abi.encodePacked(vm.getCode("MyContract.sol:MyContract"), args);
address deployed;
assembly {
deployed := create(0, add(bytecode, 0x20), mload(bytecode))
}

// Set the bytecode of an arbitrary address
vm.etch(targetAddr, deployed.code);

SEE ALSO

Forge Standard Library

deployCode getDeployedCode eth

getDeployedCode

Signature

function getDeployedCode(string calldata) external returns (bytes memory);

Description

This cheatcode works similar to getCode but only returns the deployed bytecode (aka runtime bytecode) for a contract in the project given the path to the contract.

The main use case for this cheat code is as a shortcut to deploy stateless contracts to arbitrary addresses.

The calldata parameter can either be in the form ContractFile.sol (if the filename and contract name are the same) , ContractFile.sol:ContractName, or the path to an artifact, relative to the root of your project.

ℹ️ Note

getDeployedCode requires read permission for the output directory, see file cheatcodes.

To grant read access set fs_permissions = [{ access = "read", path = "./out"}] in your foundry.toml.

Examples

Deploy a stateless contract at an arbitrary address using getDeployedCode and etch.

// A stateless contract that we want deployed at a specific address
contract Override {
    event Payload(address sender, address target, bytes data);

    function emitPayload(address target, bytes calldata message) external payable returns (uint256) {
        emit Payload(msg.sender, target, message);
        return 0;
    }
}

// get the **deployedBytecode**
bytes memory code = vm.getDeployedCode("Override.sol:Override");

// set the code of an arbitrary address
address overrideAddress = address(64);
vm.etch(overrideAddress, code);
assertEq(
    overrideAddress.code,
    code
)

SEE ALSO

Forge Standard Library

getCode etch

setEnv

Signature

function setEnv(string calldata key, string calldata value) external;

Description

Set an environment variable key=value.

ℹ️ Note

Environment variables set by a process are only accessible by itself and its child processes. Thus, calling setEnv will only modify environment variables of the currently running forge process, and won't affect the shell (forge's parent process), i.e., the they won't persist after the forge process exit.

Tips

  • The environment variable key can't be empty.
  • The environment variable key can't contain the equal sign = or the NUL character \0.
  • The environment variable value can't contain the NUL character \0.

Examples

string memory key = "hello";
string memory val = "world";
cheats.setEnv(key, val);

envOr

Signature

function envOr(string calldata key, bool defaultValue) external returns (bool value);
function envOr(string calldata key, uint256 defaultValue) external returns (uint256 value);
function envOr(string calldata key, int256 defaultValue) external returns (int256 value);
function envOr(string calldata key, address defaultValue) external returns (address value);
function envOr(string calldata key, bytes32 defaultValue) external returns (bytes32 value);
function envOr(string calldata key, string calldata defaultValue) external returns (string memory value);
function envOr(string calldata key, bytes calldata defaultValue) external returns (bytes memory value);
function envOr(string calldata key, string calldata delimiter, bool[] calldata defaultValue) external returns (bool[] memory value);
function envOr(string calldata key, string calldata delimiter, uint256[] calldata defaultValue) external returns (uint256[] memory value);
function envOr(string calldata key, string calldata delimiter, int256[] calldata defaultValue) external returns (int256[] memory value);
function envOr(string calldata key, string calldata delimiter, address[] calldata defaultValue) external returns (address[] memory value);
function envOr(string calldata key, string calldata delimiter, bytes32[] calldata defaultValue) external returns (bytes32[] memory value);
function envOr(string calldata key, string calldata delimiter, string[] calldata defaultValue) external returns (string[] memory value);
function envOr(string calldata key, string calldata delimiter, bytes[] calldata defaultValue) external returns (bytes[] memory value);

Description

A non-failing way to read an environment variable of any type: if the requested environment key does not exist, envOr() will return a default value instead of reverting (works with arrays too).

The returned type is determined by the type of defaultValue parameter passed.

Tips

  • Use envOr(key, defaultValue) to read a single value
  • Use envOr(key, delimiter, defaultValue[]) to read an array with delimiter
  • The parsing of the environment variable will be done according to the type of defaultValue (e.g. if the default value type is uint - the environment variable will be also parsed as uint)
  • Use explicit casting for literals to specify type of default variable: uint(69) will return an uint but int(69) will return an int
  • Same with: string("") and bytes("") - these will return string and bytes accordingly
  • Use dynamic arrays (bool[]) instead of fixed-size arrays (bool[4]) when providing default values (only dynamic arrays are supported)

Examples

Single Value

If the environment variable FORK is not set, you can specify it to be false by default:

bool fork = vm.envOr("FORK", false);

or

address owner;

function setUp() {
  owner = vm.envOr("OWNER", address(this));
}

Array

If the environment variable BAD_TOKENS is not set, you can specify the default to be an empty array:

address[] badTokens;

function envBadTokens() public {
  badTokens = vm.envOr("BAD_TOKENS", ",", badTokens);
}

or

function envBadTokens() public {
  address[] memory defaultBadTokens = new address[](0);
  address[] memory badTokens = vm.envOr("BAD_TOKENS", ",", defaultBadTokens);
}

envBool

Signature

function envBool(string calldata key) external returns (bool value);
function envBool(string calldata key, string calldata delimiter) external returns (bool[] memory values);

Description

Read an environment variable as bool or bool[].

Tips

  • For true, use either "true" or "True" for the environment variable value.
  • For false, use either "false" or "False" for the environment variable value.
  • For arrays, you can specify the delimiter used to seperate the values with the delimiter parameter.

Examples

Single Value

With environment variable BOOL_VALUE=true,

string memory key = "BOOL_VALUE";
bool expected = true;
bool output = cheats.envBool(key);
assert(output == expected);

Array

With environment variable BOOL_VALUES=true,false,True,False,

string memory key = "BOOL_VALUES";
string memory delimiter = ",";
bool[4] memory expected = [true, false, true, false];
bool[] memory output = cheats.envBool(key, delimiter);
assert(keccak256(abi.encodePacked((output))) == keccak256(abi.encodePacked((expected))));

envUint

Signature

function envUint(string calldata key) external returns (uint256 value);
function envUint(string calldata key, string calldata delimiter) external returns (uint256[] memory values);

Description

Read an environment variable as uint256 or uint256[].

Tips

  • If the value starts with 0x, it will be interpreted as a hex value, otherwise, it will be treated as a decimal number.
  • For arrays, you can specify the delimiter used to seperate the values with the delimiter parameter.

Examples

Single Value

With environment variable UINT_VALUE=115792089237316195423570985008687907853269984665640564039457584007913129639935,

string memory key = "UINT_VALUE";
uint256 expected = type(uint256).max;
uint256 output = cheats.envUint(key);
assert(output == expected);

Array

With environment variable UINT_VALUES=0,0x0000000000000000000000000000000000000000000000000000000000000000,

string memory key = "UINT_VALUES";
string memory delimiter = ",";
uint256[2] memory expected = [type(uint256).min, type(uint256).min];
uint256[] memory output = cheats.envUint(key, delimiter);
assert(keccak256(abi.encodePacked((output))) == keccak256(abi.encodePacked((expected))));

envInt

Signature

function envInt(string calldata key) external returns (int256 value);
function envInt(string calldata key, string calldata delimiter) external returns (int256[] memory values);

Description

Read an environment variable as int256 or int256[].

Tips

  • If the value starts with 0x, -0x or +0x, it will be interpreted as a hex value, otherwise, it will be treated as a decimal number.
  • For arrays, you can specify the delimiter used to seperate the values with the delimiter parameter.

Examples

Single Value

With environment variable INT_VALUE=-57896044618658097711785492504343953926634992332820282019728792003956564819968,

string memory key = "INT_VALUE";
int256 expected = type(int256).min;
int256 output = cheats.envInt(key);
assert(output == expected);

Array

With environment variable INT_VALUES=-0x8000000000000000000000000000000000000000000000000000000000000000,+0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,

string memory key = "INT_VALUES";
string memory delimiter = ",";
int256[2] memory expected = [type(int256).min, type(int256).max];
int256[] memory output = cheats.envInt(key, delimiter);
assert(keccak256(abi.encodePacked((output))) == keccak256(abi.encodePacked((expected))));

envAddress

Signature

function envAddress(string calldata key) external returns (address value);
function envAddress(string calldata key, string calldata delimiter) external returns (address[] memory values);

Description

Read an environment variable as address or address[].

Tips

  • For arrays, you can specify the delimiter used to seperate the values with the delimiter parameter.

Examples

Single Value

With environment variable ADDRESS_VALUE=0x7109709ECfa91a80626fF3989D68f67F5b1DD12D,

string memory key = "ADDRESS_VALUE";
address expected = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;
address output = cheats.envAddress(key);
assert(output == expected);

Array

With environment variable ADDRESS_VALUES=0x7109709ECfa91a80626fF3989D68f67F5b1DD12D,0x0000000000000000000000000000000000000000,

string memory key = "ADDRESS_VALUES";
string memory delimiter = ",";
address[2] memory expected = [
    0x7109709ECfa91a80626fF3989D68f67F5b1DD12D,
    0x0000000000000000000000000000000000000000
];
address[] memory output = cheats.envAddress(key, delimiter);
assert(keccak256(abi.encodePacked((output))) == keccak256(abi.encodePacked((expected))));

envBytes32

Signature

function envBytes32(string calldata key) external returns (bytes32 value);
function envBytes32(string calldata key, string calldata delimiter) external returns (bytes32[] memory values);

Description

Read an environment variable as bytes32 or address[].

Tips

  • For arrays, you can specify the delimiter used to seperate the values with the delimiter parameter.

Examples

Single Value

With environment variable BYTES32_VALUE=0x00,

string memory key = "BYTES32_VALUE";
bytes32 expected = bytes32(0x0000000000000000000000000000000000000000000000000000000000000000);
bytes32 output = cheats.envBytes32(key);
assert(output == expected);

Array

With environment variable BYTES32_VALUES=0x7109709ECfa91a80626fF3989D68f67F5b1DD12D,0x00,

string memory key = "BYTES32_VALUES";
string memory delimiter = ",";
bytes32[2] memory expected = [
    bytes32(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D000000000000000000000000),
    bytes32(0x0000000000000000000000000000000000000000000000000000000000000000)
];
bytes32[] memory output = cheats.envBytes32(key, delimiter);
assert(keccak256(abi.encodePacked((output))) == keccak256(abi.encodePacked((expected))));

envString

Signature

function envString(string calldata key) external returns (string value);
function envString(string calldata key, string calldata delimiter) external returns (string[] memory values);

Description

Read an environment variable as string or string[]. In case the environment variable is not defined, Forge will fail with the following error message:

[FAIL. Reason: Failed to get environment variable FOO as type string: environment variable not found]

Tips

  • You can put your environment variables in a .env file. Forge will automatically load them when running forge test.
  • For arrays, you can specify the delimiter used to separate the values with the delimiter parameter.
  • Choose a delimiter that doesn't appear in the string values, so that they can be correctly separated.

Examples

Single Value

With environment variable STRING_VALUE=hello, world!,

string memory key = "STRING_VALUE";
string expected = "hello, world!";
string output = cheats.envString(key);
assertEq(output, expected);

Array

With environment variable STRING_VALUES=hello, world!|0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;

string memory key = "STRING_VALUES";
string memory delimiter = "|";
string[2] memory expected = [
    "hello, world!",
    "0x7109709ECfa91a80626fF3989D68f67F5b1DD12D"
];
string[] memory output = cheats.envString(key, delimiter);
for (uint i = 0; i < expected.length; ++i) {
    assert(keccak256(abi.encodePacked((output[i]))) == keccak256(abi.encodePacked((expected[i]))));
}

envBytes

Signature

function envBytes(bytes calldata key) external returns (bytes value);
function envBytes(bytes calldata key, bytes calldata delimiter) external returns (bytes[] memory values);

Description

Read an environment variable as bytes or bytes[].

Tips

  • For arrays, you can specify the delimiter used to seperate the values with the delimiter parameter.

Examples

Single Value

With environment variable BYTES_VALUE=0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;

bytes memory key = "BYTES_VALUE";
bytes expected = hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D";
bytes output = cheats.envBytes(key);
assertEq(output, expected);

Array

With environment variable BYTES_VALUE=0x7109709ECfa91a80626fF3989D68f67F5b1DD12D,0x00;

bytes memory key = "BYTES_VALUES";
bytes memory delimiter = ",";
bytes[] memory expected = new bytes[](2);
expected[0] = hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D";
expected[1] = hex"00";
bytes[] memory output = cheats.envBytes(key, delimiter);
for (uint i = 0; i < expected.length; ++i) {
    assert(keccak256(abi.encodePacked((output[i]))) == keccak256(abi.encodePacked((expected[i]))));
}

parseJson

Signature

// Return the value(s) that correspond to 'key'
vm.parseJson(string memory json, string memory key)
// Return the entire json file
vm.parseJson(string memory json);

Description

These cheatcodes are used to parse JSON files in the form of strings. Usually, it's coupled with vm.readFile() which returns an entire file in the form of a string.

You can use stdJson from forge-std, as a helper library for better UX.

The cheatcode accepts either a key to search for a specific value in the JSON, or no key to return the entire JSON. It returns the value as an abi-encoded bytes array. That means that you will have to abi.decode() to the appropriate type for it to function properly, else it will revert.

JSONpath Key

parseJson uses a syntax called JSONpath to form arbitrary keys for arbitrary json files. The same syntax (or rather a dialect) is used by the tool jq.

To read more about the syntax, you can visit the README of the rust library that we use under the hood to implement the feature. That way you can be certain that you are using the correct dialect of jsonPath.

JSON Encoding Rules

Encoding Rules

  • Numbers >= 0 are encoded as uint256
  • Negative numbers are encoded as int256
  • A string that can be decoded into a type of H160 and starts with 0x is encoded as an address.In other words, if it can be decoded into an address, it's probably an address
  • A string that starts with 0x is encoded as bytes32 if it has a length of 66 or else to bytes
  • A string that is neither an address, a bytes32 or bytes, is encoded as a string
  • An array is encoded as a dynamic array of the type of it's first element
  • An object ({}) is encoded as a tuple

Decoding JSON objects into Solidity structs

JSON objects are encoded as tuples, and can be decoded via tuples or structs. That means that you can define a struct in Solidity and it will decode the entire JSON object into that struct.

For example:

The following JSON

{
    a: 43,
    b: "sigma"
}

will be decoded into:

struct Json {
    uint256 a;
    string b;
}

As the values are returned as an abi-encoded tuple, the exact name of the attributes of the struct don't need to match the names of the keys in the JSON. The above json file could also be decoded as:

struct Json {
    uint256 apple;
    string pineapple;
}

What matters is the alphabetical order. As the JSON object is an unordered data structure but the tuple is an ordered one, we had to somehow give order to the JSON. The easiest way was to order the keys by alphabetical order. That means that in order to decode the JSON object correctly, you will need to define attributes of the struct with types that correspond to the values of the alphabetical order of the keys of the JSON.

  • The struct is interpreted serially. That means that the tuple's first item will be decoded based on the first item of the struct definition (no alphabetical order).
  • The JSON will parsed alphabetically, not serially.

Thus, the first (in alphabetical order) value of the JSON, will be abi-encoded and then tried to be abi-decoded, based on the type of the first attribute of the struct.

The above JSON would not be able to be decoded with the struct below:

    struct Json {
        uint256 b;
        uint256 a;
    }

The reason is that it would try to decode the string "sigma" as a uint. To be exact, it would be decoded, but it would result to a wrong number, since it would interpret the bytes incorrectly.

Decoding JSON Objects, a tip

If your JSON object has hex numbers, they will be encoded as bytes. The way to decode them as uint for better UX, is to define two struct, one intermediary with the definition of these values as bytes and then a final struct that will be consumed by the user.

  1. Decode the JSON into the intermediary struct
  2. Convert the intermediary struct to the final one, by converting the bytes to uint. We have a helper function in forge-std to do this
  3. Give the final struct to the user for consumption

Instructions

  1. Import the library import "../StdJson.sol";
  2. Define it's usage with string: using stdJson for string;
  3. If you want to parse simple values (numbers, address, etc.) use the helper functions
  4. If you want to parse entire JSON objects:
    1. Define the struct in Solidity. Make sure to follow the alphabetical order -- it's hard to debug
    2. Use the parseRaw() helper function to return abi-encoded bytes and then decode them to your struct
    string memory root = vm.projectRoot();
    string memory path = string.concat(root, "/src/test/fixtures/broadcast.log.json");
    string memory json = vm.readFile(path);
    bytes memory transactionDetails = json.parseRaw(".transactions[0].tx");
    RawTx1559Detail memory rawTxDetail = abi.decode(transactionDetails, (RawTx1559Detail));

Forge script artifacts

We have gone ahead and created a handful of helper struct and functions to read the artifacts from broadcasting a forge script.

Currently, we only support artifacts produced by EIP1559-compatible chains and we don't support yet the parsing of the entire broadcast.json artifact. You will need to parse for individual values such as the transactions, the receipts, etc.

To read the transactions, it's as easy as doing:

    function testReadEIP1559Transactions() public {
        string memory root = vm.projectRoot();
        string memory path = string.concat(root, "/src/test/fixtures/broadcast.log.json");
        Tx1559[] memory transactions = readTx1559s(path);
    }

and then you can access their various fields in these structs:

struct Tx1559 {
    string[] arguments;
    address contractAddress;
    string contractName;
    string functionSig;
    bytes32 hash;
    Tx1559Detail txDetail;
    string opcode;
}

struct Tx1559Detail {
    AccessList[] accessList;
    bytes data;
    address from;
    uint256 gas;
    uint256 nonce;
    address to;
    uint256 txType;
    uint256 value;
}

References

Utilities

addr

Signature

function addr(uint256 privateKey) external returns (address);

Description

Computes the address for a given private key.

Examples

address alice = vm.addr(1);
emit log_address(alice); // 0x7e5f4552091a69125d5dfcb7b8c2659029395bdf

sign

Signature

function sign(uint256 privateKey, bytes32 digest) external returns (uint8 v, bytes32 r, bytes32 s);

Description

Signs a digest digest with private key privateKey, returning (v, r, s).

This is useful for testing functions that take signed data and perform an ecrecover to verify the signer.

Examples

address alice = vm.addr(1);
bytes32 hash = keccak256("Signed by Alice");
(uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash);
address signer = ecrecover(hash, v, r, s);
assertEq(alice, signer); // [PASS]

label

Signature

function label(address addr, string calldata label) external;

Description

Sets a label label for addr in test traces.

If an address is labelled, the label will show up in test traces instead of the address.

deriveKey

Signature

function deriveKey(
  string calldata mnemonic,
  uint32 index
) external returns (uint256);
function deriveKey(
  string calldata mnemonic,
  string calldata path,
  uint32 index
) external returns (uint256);

Description

Derive a private key from a given mnemonic or mnemonic file path.

The first signature derives at the derivation path m/44'/60'/0'/0/{index}. The second signature allows you to specify the derivation path as the second parameter.

Examples

Derive the private key from the test mnemonic at path m/44'/60'/0'/0/0:

string memory mnemonic = "test test test test test test test test test test test junk";
uint256 privateKey = vm.deriveKey(mnemonic, 0);

Derive the private key from the test mnemonic at path m/44'/60'/0'/1/0:

string memory mnemonic = "test test test test test test test test test test test junk";
uint256 privateKey = vm.deriveKey(mnemonic, "m/44'/60'/0'/1/", 0);

SEE ALSO

Forge Standard Library:

rememberKey

Signature

function rememberKey(uint256 privateKey) external returns (address);

Description

Stores a private key in forge's local wallet and returns the corresponding address which can later be used for broadcasting.

Examples

Derive the private key from the test mnemonic at path m/44'/60'/0'/0/0, remember it in forge's wallet and use it to start broadcasting transactions:

string memory mnemonic = "test test test test test test test test test test test junk";
uint256 privateKey = vm.deriveKey(mnemonic, 0);
address deployer = vm.rememberKey(privateKey);

vm.startBroadcast(deployer);
...
vm.stopBroadcast();

Load a private key from the PRIVATE_KEY environment variable and use it to start broadcasting transactions:

address deployer = vm.rememberKey(vm.envUint("PRIVATE_KEY"));

vm.startBroadcast(deployer);
...
vm.stopBroadcast();

SEE ALSO

Forge Standard Library:

toString

Signature

function toString(address) external returns (string memory);
function toString(bool) external returns (string memory);
function toString(uint256) external returns (string memory);
function toString(int256) external returns (string memory);
function toString(bytes32) external returns (string memory);
function toString(bytes) external returns (string memory);

Description

Convert any type to it's string version. Very useful for operations that demand strings, such as the cheatcode ffi.

Bytes are converted to a string of their hex representation with 0x at the start, signifying that they are encoded in hex.

Examples

uint256 number = 420;
string memory stringNumber = vm.toString(number);
vm.assertEq(stringNumber, "420");
bytes memory testBytes = hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D";
string memory stringBytes = cheats.toString(testBytes);
assertEq("0x7109709ecfa91a80626ff3989d68f67f5b1dd12d", stringBytes);
address testAddress =  0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;
string memory stringAddress = cheats.toString(testAddress);
assertEq("0x7109709ECfa91a80626fF3989D68f67F5b1DD12D", stringAddress);

snapshot cheatcodes

Signature

// Snapshot the current state of the evm.
// Returns the id of the snapshot that was created.
// To revert a snapshot use `revertTo`
function snapshot() external returns(uint256);
// Revert the state of the evm to a previous snapshot
// Takes the snapshot id to revert to.
// This deletes the snapshot and all snapshots taken after the given snapshot id.
function revertTo(uint256) external returns(bool);

Description

snapshot takes a snapshot of the state of the blockchain and returns the identifier of the created snapshot

revertTo reverts the state of the blockchain to the given snapshot. This deletes the given snapshot, as well as any snapshots taken after (e.g.: reverting to id 1 will delete snapshots with ids 2, 3, etc.)

Examples

struct Storage {
    uint slot0;
    uint slot1;
}

contract SnapshotTest is Test {
    Cheats constant cheats = Cheats(HEVM_ADDRESS);

    Storage store;

    function setUp() public {
        store.slot0 = 10;
        store.slot1 = 20;
    }

    function testSnapshot() public {
        uint256 snapshot = cheats.snapshot();
        store.slot0 = 300;
        store.slot1 = 400;

        assertEq(store.slot0, 300);
        assertEq(store.slot1, 400);

        // after resetting to a snapshot all changes are discarded
        cheats.revertTo(snapshot);
        assertEq(store.slot0, 10, "snapshot revert for slot 0 unsuccessful");
        assertEq(store.slot1, 20, "snapshot revert for slot 1 unsuccessful");
    }

}

Signature

    // Returns the URL for a configured alias
    function rpcUrl(string calldata alias) external returns(string memory);
    // Returns all configured (alias, URL) pairs
    function rpcUrls() external returns(string[2][] memory);

Description

Provides cheatcodes to access all RPC endpoints configured in the rpc_endpoints object of the foundry.toml

Examples

The following rpc_endpoints in foundry.toml registers two RPC aliases:

  • optimism references the URL directly
  • mainnet references the RPC_MAINNET environment value that is expected to contain the actual URL

Env variables need to be wrapped in ${}

# --snip--
[rpc_endpoints]
optimism = "https://optimism.alchemyapi.io/v2/..."
mainnet = "${RPC_MAINNET}" 
string memory url = vm.rpcUrl("optimism");
assertEq(url, "https://optimism.alchemyapi.io/v2/...");

If a ENV var is missing, rpcUrl() will revert:

vm.expectRevert("Failed to resolve env var `${RPC_MAINNET}` in `RPC_MAINNET`: environment variable not found");
string memory url = vm.rpcUrl("mainnet");

Retrieve all available alias -> URL pairs

string[2][] memory allUrls = vm.rpcUrls();
assertEq(allUrls.length, 2);

string[2] memory val = allUrls[0];
assertEq(val[0], "optimism");

string[2] memory env = allUrls[1];
assertEq(env[0], "mainnet");

SEE ALSO

Forge Config

Config Reference

File cheat codes

Signature

    // Reads the entire content of file to string, (path) => (data)
    function readFile(string calldata) external returns (string memory);
    // Reads next line of file to string, (path) => (line)
    function readLine(string calldata) external returns (string memory);
    // Writes data to file, creating a file if it does not exist, and entirely replacing its contents if it does.
    // (path, data) => ()
    function writeFile(string calldata, string calldata) external;
    // Writes line to file, creating a file if it does not exist.
    // (path, data) => ()
    function writeLine(string calldata, string calldata) external;
    // Closes file for reading, resetting the offset and allowing to read it from beginning with readLine.
    // (path) => ()
    function closeFile(string calldata) external;
    // Removes file. This cheatcode will revert in the following situations, but is not limited to just these cases:
    // - Path points to a directory.
    // - The file doesn't exist.
    // - The user lacks permissions to remove the file.
    // (path) => ()
    function removeFile(string calldata) external;

Description

These cheatcodes provided by forge-std can be used for filesystem manipulation operations.

By default, filesystem access is disallowed and requires the fs_permission setting in foundry.toml:

# Configures permissions for cheatcodes that touch the filesystem like `vm.writeFile`
# `access` restricts how the `path` can be accessed via cheatcodes
#    `read-write` | `true`   => `read` + `write` access allowed (`vm.readFile` + `vm.writeFile`)
#    `none`| `false` => no access
#    `read` => only read access (`vm.readFile`)
#    `write` => only write access (`vm.writeFile`)
# The `allowed_paths` further lists the paths that are considered, e.g. `./` represents the project root directory
# By default _no_ fs access permission is granted, and _no_ paths are allowed
# following example enables read access for the project dir _only_:
#       `fs_permissions = [{ access = "read", path = "./"}]`
fs_permissions = [] # default: all file cheat codes are disabled

Examples

Append a line to a file, this will create the file if it does not exist yet

This requires read access to the file / project root

fs_permissions = [{ access = "read", path = "./"}]
string memory path = "output.txt";

string memory line1 = "first line";
vm.writeLine(path, line1);

string memory line2 = "second line";
vm.writeLine(path, line2);

Write to and read from a file

This requires read-write access to file / project root:

fs_permissions = [{ access = "read-write", path = "./"}]
string memory path = "file.txt";
string memory data = "hello world";
vm.writeFile(path, data);

assertEq(vm.readFile(path), data);

Remove a file

This requires write access to file / project root:

fs_permissions = [{ access = "write", path = "./"}]
string memory path = "file.txt";
string memory data = "hello world";
vm.writeFile(path, data);

assertEq(vm.readFile(path), data);

Forge Standard Library Reference

Forge Standard Library (Forge Std for short) is a collection of helpful contracts that make writing tests easier, faster, and more user-friendly.

Using Forge Std is the preferred way of writing tests with Foundry.

What's included:

  • Vm.sol: Up-to-date cheatcodes interface

    import "forge-std/Vm.sol";
    
  • console.sol and console2.sol: Hardhat-style logging functionality

    import "forge-std/console.sol";
    

    Note: console2.sol contains patches to console.sol that allow Forge to decode traces for calls to the console, but it is not compatible with Hardhat.

    import "forge-std/console2.sol";
    
  • Script.sol: Basic utilities for Solidity scripting

    import "forge-std/Script.sol";
    
  • Test.sol: The complete Forge Std experience (more details below)

    import "forge-std/Test.sol";
    

Forge Std's Test

The Test contract in Test.sol provides all the essential functionality you need to get started writing tests.

Simply import Test.sol and inherit from Test in your test contract:

import "forge-std/Test.sol";

contract ContractTest is Test { ...

What's included:

  • Std Libraries

    • Std Logs: Expand upon the logging events from the DSTest library.
    • Std Assertions: Expand upon the assertion functions from the DSTest library.
    • Std Cheats: Wrappers around Forge cheatcodes for improved safety and DX.
    • Std Errors: Wrappers around common internal Solidity errors and reverts.
    • Std Storage: Utilities for storage manipulation.
    • Std Math: Useful mathematical functions.
    • Script Utils: Utility functions which can be accessed in tests and scripts.
    • Console Logging: Console logging functions.
  • A cheatcodes instance vm, from which you invoke Forge cheatcodes (see Cheatcodes Reference)

    vm.startPrank(alice);
    
  • All Hardhat console functions for logging (see Console Logging)

    console.log(alice.balance); // or `console2`
    
  • All Dappsys Test functions for asserting and logging (see Dappsys Test reference)

    assertEq(dai.balanceOf(alice), 10000e18);
    
  • Utility functions also included in Script.sol (see Script Utils)

    // Compute the address a contract will be deployed at for a given deployer address and nonce
    address futureContract = computeCreateAddress(alice, 1);
    

Std Logs

Std Logs expand upon the logging events from the DSTest library.

Events

event log_array(uint256[] val);
event log_array(int256[] val);
event log_named_array(string key, uint256[] val);
event log_named_array(string key, int256[] val);

Usage

This section provides usage examples.

log_array

event log_array(<type>[] val);

Where <type> can be int256, uint256, address.

Example
// Assuming storage
// uint256[] data = [10, 20, 30, 40, 50]; 

emit log_array(data);

log_named_array

event log_named_array(string key, <type>[] val);

Where <type> can be int256, uint256, address.

Example
// Assuming storage
// uint256[] data = [10, 20, 30, 40, 50]; 

emit log_named_array("Data", data);

Std Assertions

fail

Signature

function fail(string memory err) internal virtual;

Description

Fail a test with a message if a certain branch or execution point is hit.

Examples

function test() external {
    for(uint256 place; place < 10; ++i){
        if(game.leaderboard(place) == address(this)) return;
    }
    fail("Not in the top 10.");
}

assertFalse

Signature

function assertFalse(bool data) internal virtual;
function assertFalse(bool data, string memory err) internal virtual;

Description

Asserts the condition is false.

Examples

bool failure = contract.fun();
assertFalse(failure);

assertEq

Signature

function assertEq(bool a, bool b) internal;
function assertEq(bool a, bool b, string memory err) internal;
function assertEq(bytes memory a, bytes memory b) internal;
function assertEq(bytes memory a, bytes memory b, string memory err) internal;
function assertEq(uint256[] memory a, uint256[] memory b) internal;
function assertEq(int256[] memory a, int256[] memory b) internal;
function assertEq(uint256[] memory a, uint256[] memory b, string memory err) internal;
function assertEq(int256[] memory a, int256[] memory b, string memory err) internal;
// legacy helper for asserting two uints shorter than 256 bits: `assertEqUint(uint8(1), uint128(1));`
function assertEqUint(uint256 a, uint256 b) internal;

Description

Asserts a is equal to b.

Works with bool, bytes, and int256 and uint256 arrays.

assertApproxEqAbs

Signature

function assertApproxEqAbs(uint256 a, uint256 b, uint256 maxDelta) internal virtual;
function assertApproxEqAbs(uint256 a, uint256 b, uint256 maxDelta, string memory err) internal virtual;

Description

Asserts a is approximately equal to b with delta in absolute value.

Examples

function testFail () external {
    uint256 a = 100;
    uint256 b = 200;

    assertApproxEqAbs(a, b, 90);
}
[PASS] testFail() (gas: 23169)
Logs:
  Error: a ~= b not satisfied [uint]
    Expected: 200
      Actual: 100
   Max Delta: 90
       Delta: 100

assertApproxEqRel

Signature

function assertApproxEqRel(uint256 a, uint256 b, uint256 maxPercentDelta) internal virtual;
function assertApproxEqRel(uint256 a, uint256 b, uint256 maxPercentDelta, string memory err) internal virtual;

Description

Asserts a is approximately equal to b with delta in percentage, where 1e18 is 100%.

Examples

function testFail () external {
    uint256 a = 100;
    uint256 b = 200;
    assertApproxEqRel(a, b, 0.4e18);
}
[PASS] testFail() (gas: 23884)
Logs:
  Error: a ~= b not satisfied [uint]
      Expected: 200
        Actual: 100
   Max % Delta: 0.400000000000000000
       % Delta: 0.500000000000000000

Std Cheats

skip

Signature

function skip(uint256 time) public;

Description

Skips forward block.timestamp by the specified number of seconds.

Examples

assertEq(block.timestamp, 0);
skip(3600);
assertEq(block.timestamp, 3600);

rewind

Signature

function rewind(uint256 time) public;

Description

Rewinds block.timestamp by the specified number of seconds.

Examples

assertEq(block.timestamp, 3600);
rewind(3600);
assertEq(block.timestamp, 0);

hoax

Signature

function hoax(address who) public;
function hoax(address who, uint256 give) public;
function hoax(address who, address origin) public;
function hoax(address who, address origin, uint256 give) public;

Description

Sets up a prank from an address that has some ether.

If the balance is not specified, it will be set to 2^128 wei.

startHoax

Signature

function startHoax(address who) public;
function startHoax(address who, uint256 give) public;
function startHoax(address who, address origin) public;
function startHoax(address who, address origin, uint256 give) public;

Description

Start a perpetual prank from an address that has some ether.

If the balance is not specified, it will be set to 2^128 wei.

deal

Signature

function deal(address to, uint256 give) public;
function deal(address token, address to, uint256 give) public;
function deal(address token, address to, uint256 give, bool adjust) public;

Description

A wrapper around the deal cheatcode that also works for most ERC-20 tokens.

If the alternative signature of deal is used, adjusts the token's total supply after setting the balance.

Examples

deal(address(dai), alice, 10000e18);
assertEq(dai.balanceOf(alice), 10000e18);

deployCode

Signature

function deployCode(string memory what) public returns (address);
function deployCode(string memory what, bytes memory args) public returns (address);
function deployCode(string memory what, uint256 val) public returns (address);
function deployCode(string memory what, bytes memory args, uint256 val) public returns (address);

Description

Deploys a contract by fetching the contract bytecode from the artifacts directory.

The calldata parameter can either be in the form ContractFile.sol (if the filename and contract name are the same), ContractFile.sol:ContractName, or the path to an artifact, relative to the root of your project.

Values can also be passed by using the val parameter. This is necessary if you need to send ETH on the constructor.

Examples

address deployment = deployCode("MyContract.sol", abi.encode(arg1, arg2));

bound

Signature

function bound(uint256 x, uint256 min, uint256 max) public returns (uint256 result);

Description

A mathematical function for wrapping inputs of fuzz tests into a certain range.

You can use it instead of the assume cheatcode to get better performance in some cases. Read more here.

Examples

input = bound(input, 99, 101);

Returns 99 for input 0.
Returns 100 for input 1.
Returns 101 for input 2.
Returns 99 for input 3.
And so on.

changePrank

Signature

function changePrank(address who) internal;

Description

Stops the active prank with stopPrank and passes address to startPrank.

Useful for starting a global prank in the setUp function and deactivating it in certain tests.

makeAddr

Signature

function makeAddr(string memory name) internal returns(address addr);

Description

Creates an address derived from the provided name.

A label is created for the derived address with the provided name used as the label value.

Examples

address alice = makeAddr("alice");
emit log_address(alice); // 0x328809bc894f92807417d2dad6b7c998c1afdac6

makeAddrAndKey

Signature

function makeAddrAndKey(string memory name) internal returns(address addr, uint256 privateKey);

Description

Creates an address and private key derived from the provided name.

A label is created for the derived address with the provided name used as the label value.

Examples

(address alice, uint256 key) = makeAddrAndKey("alice");
emit log_address(alice); // 0x328809bc894f92807417d2dad6b7c998c1afdac6
emit log_uint(key); // 70564938991660933374592024341600875602376452319261984317470407481576058979585

noGasMetering

Signature

modifier noGasMetering();

Description

A function modifier that turns off gas metering for the life of the function.

Note, there is some gas associated with calling the cheatcode, so you will see some gas usage (albeit small) when using this modifier.

Examples

function addInLoop() internal returns (uint256) {
    uint256 b;
    for (uint256 i; i < 10000; i++) {
        b + i;
    }
    return b;
}

function addInLoopNoGas() internal noGasMetering returns (uint256) {
    return addInLoop();
}

function testFunc() external {
  uint256 gas_start = gasleft();
  addInLoop();
  uint256 gas_used = gas_start - gasleft();

  uint256 gas_start_no_metering = gasleft();
  addInLoopNoGas();
  uint256 gas_used_no_metering = gas_start_no_metering - gasleft();

  emit log_named_uint("Gas Metering", gas_used);
  emit log_named_uint("No Gas Metering", gas_used_no_metering);
}
[PASS] testFunc() (gas: 1887191)
Logs:
  Gas Metering: 1880082
  No Gas Metering: 3024

Std Errors

assertionError

Signature

stdError.assertionError

Description

The internal Solidity error when an assert fails.

arithmeticError

Signature

stdError.arithmeticError

Description

The internal Solidity error when an arithmetic operation fails, e.g. underflow and overflow.

Example

Assume we have a basic vault contract that can store some token (wmdToken):

contract BasicVault {

    IERC20 public immutable wmdToken;   
    mapping(address => uint) public balances;

    event Deposited(address indexed from, uint amount);
    event Withdrawal(address indexed from, uint amount);

    constructor(IERC20 wmdToken_){
        wmdToken = wmdToken_;
    }

    function deposit(uint amount) external {    
        balances[msg.sender] += amount;
        bool success = wmdToken.transferFrom(msg.sender, address(this), amount);
        require(success, "Deposit failed!"); 
        emit Deposited(msg.sender, amount);
    }

    function withdraw(uint amount) external {      
        balances[msg.sender] -= amount;
        bool success = wmdToken.transfer(msg.sender, amount);
        require(success, "Withdrawal failed!");
        emit Withdrawal(msg.sender, amount);
    }
}

We have a test function to ensure that a user is unable to withdraw tokens in excess of his deposit, like so:

    function testUserCannotWithdrawExcessOfDeposit() public {
        vm.prank(user);
        vm.expectRevert(stdError.arithmeticError);
        vault.withdraw(userTokens + 100*10**18);
    }
  1. User has tokens of amount userTokens deposited in a Vault contract.
  2. User attempts to withdraw tokens of amount in excess of his deposits.
  3. This leads to an underflow error, resulting from: balances[msg.sender] -= amount; as it would evaluate into a negative value.

To catch the error "Arithmetic over/underflow", we insert vm.expectRevert(stdError.arithmeticError) just before the function call that is expected to result in an underflow.

divisionError

Signature

stdError.divisionError

Description

The internal Solidity error when a division fails, e.g. division by zero.

enumConversionError

Signature

stdError.enumConversionError

Description

The internal Solidity error when trying to convert a number to a variant of an enum, if the number is larger than the number of variants in the enum (counting from 0).

encodeStorageError

Signature

stdError.encodeStorageError

Description

The internal Solidity error when trying to access data in storage that is corrupted. Data cannot be corrupted unless assembly had been used.

popError

Signature

stdError.popError

Description

The internal Solidity error when trying to pop an element off of an empty array.

indexOOBError

Signature

stdError.indexOOBError

Description

The internal Solidity error when trying to access an element of an array that is out of bounds.

Will not work for empty arrays in external contracts. For those, use expectRevert without any arguments.

memOverflowError

Signature

stdError.memOverflowError

Description

The internal Solidity error when trying to allocate a dynamic memory array with more than 2^64-1 items.

zeroVarError

Signature

stdError.zeroVarError

Description

The internal Solidity error when trying to call a function via a function pointer that has not been initialized.

Std Storage

Std Storage is a library that makes manipulating storage easy.

To use Std Storage, add the following line to your test contract:

using stdStorage for StdStorage;

Then, access Std Storage via the stdstore instance.

Functions

Query functions:

  • target: Set the address of the target contract
  • sig: Set the 4-byte selector of the function to static call
  • with_key: Pass an argument to the function (can be used multiple times)
  • depth: Set the position of the value in the tuple (e.g. inside a struct)

Terminator functions:

  • find: Return the slot number
  • checked_write: Set the data to be written to the storage slot(s)
  • read_<type>: Read the value from the storage slot as <type>

Example

playerToCharacter tracks info about players' characters.

// MetaRPG.sol

struct Character {
    string name;
    uint256 level;
}

mapping (address => Character) public playerToCharacter;

Let's say we want to set the level of our character to 120.

// MetaRPG.t.sol

stdstore
    .target(address(metaRpg))
    .sig("playerToCharacter(address)")
    .with_key(address(this))
    .depth(1)
    .checked_write(120);

Limitations

  • Accessing packed slots is not supported

Known issues

  • Slot(s) may not be found if the tuple contains types shorter than 32 bytes

target

Signature

function target(StdStorage storage self, address _target) internal returns (StdStorage storage);

Description

Sets the address of the contract.

Default value: address(0)

sig

Signature

function sig(StdStorage storage self, bytes4 _sig) internal returns (StdStorage storage);
function sig(StdStorage storage self, string memory _sig) internal returns (StdStorage storage);

Description

Sets the 4-byte selector of the function to static call.

Default value: hex"00000000"

Examples

uint256 slot = stdstore
    .target(addr)
    .sig(addr.fun.selector)
    .with_key(1)
    .find();
uint256 slot = stdstore
    .target(addr)
    .sig("fun(uint256)")
    .with_key(1)
    .find();

with_key

Signature

function with_key(StdStorage storage self, address who) internal returns (StdStorage storage);
function with_key(StdStorage storage self, uint256 amt) internal returns (StdStorage storage);
function with_key(StdStorage storage self, bytes32 key) internal returns (StdStorage storage);

Description

Passes an argument to the function.

Can be used multiple times to pass multiple arguments. The order matters.

Examples

uint256 slot = stdstore
    .target(addr)
    .sig("fun(uint256,address)")
    .with_key(1)
    .with_key(address(this))
    .find();

depth

Signature

function depth(StdStorage storage self, uint256 _depth) internal returns (StdStorage storage);

Description

Sets the position of the value in the tuple (e.g. inside a struct).

Default value: uint256(0)

checked_write

Signature

function checked_write(StdStorage storage self, address who) internal;
function checked_write(StdStorage storage self, uint256 amt) internal;
function checked_write(StdStorage storage self, bool write) internal;
function checked_write(StdStorage storage self, bytes32 set) internal;

Description

Sets the data to be written to the storage slot(s).

Reverts with a message if unsuccessful.

find

Signature

function find(StdStorage storage self) internal returns (uint256);

Description

Finds an arbitrary storage slot given target, sig, with_key(s), and depth.

Reverts with a message if unsuccessful.

read

Signature

function read_bytes32(StdStorage storage self) internal returns (bytes32);
function read_bool(StdStorage storage self) internal returns (bool);
function read_address(StdStorage storage self) internal returns (address);
function read_uint(StdStorage storage self) internal returns (uint256);
function read_int(StdStorage storage self) internal returns (int256);

Description

Reads the value from the storage slot as bytes32, bool, address, uint256, or int256.

Reverts with a message if unsuccessful.

Std Math

abs

Signature

function abs(int256 a) internal pure returns (uint256)

Description

Returns the absolute value of a number.

Example

uint256 ten = stdMath.abs(-10);

delta

Signature

function delta(uint256 a, uint256 b) internal pure returns (uint256)
function delta(int256 a, int256 b) internal pure returns (uint256)

Description

Returns the difference between two numbers in absolute value.

Example

uint256 four = stdMath.delta(-1, 3);

percentDelta

Signature

function delta(uint256 a, uint256 b) internal pure returns (uint256)
function delta(int256 a, int256 b) internal pure returns (uint256)

Description

Returns the difference between two numbers in percentage, where 1e18 is 100%.

Example

uint256 percent150 = stdMath.percentDelta(uint256(125), 50);
uint256 percent60 = stdMath.percentDelta(uint256(50), 125);

Script Utils

computeCreateAddress

Signature

function computeCreateAddress(address deployer, uint256 nonce) internal pure returns (address)

Description

Compute the address a contract will be deployed at for a given deployer address and nonce. Useful to precalculate the address a contract will be deployed at.

Example

address governanceAddress = computeCreateAddress(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, 1);

// this contract requires a governance contract which hasn't been deployed yet
Contract contract = new Contract(governanceAddress);
// now we deploy it
Governance governance = new Governance(contract);

// assuming `contract` has a `governance()` accessor
assertEq(governanceAddress, address(governance)); // [PASS]

deriveRememberKey

Signature

function deriveRememberKey(string memory mnemonic, uint32 index) internal returns (address who, uint256 privateKey)

Description

Derive a private key from a mnemonic and also store it in forge's local wallet. Returns the address and private key.

Example

Get a private key and address from the test mnemonic at path m/44'/60'/0'/0/0. Use them to sign some data and start broadcasting transactions:

string memory mnemonic = "test test test test test test test test test test test junk";

(address deployer, uint256 privateKey) = deriveRememberKey(mnemonic, 0);

bytes32 hash = keccak256("Signed by deployer");
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, hash);

vm.startBroadcast(deployer);
...
vm.stopBroadcast();

Get an address from the test mnemonic at path m/44'/60'/0'/0/0 to start broadcasting transcations:

string memory mnemonic = "test test test test test test test test test test test junk";

(address deployer, ) = deriveRememberKey(mnemonic, 0);

vm.startBroadcast(deployer);
...
vm.stopBroadcast();

SEE ALSO

Cheatcodes:

Console Logging

  • Similar to Hardhat's console functions.
  • You can use it in calls and transactions. It works with view functions, but not in pure ones.
  • It always works, regardless of the call or transaction failing or being successful.
  • To use it you need to import forge-std/console.sol.
  • You can call console.log with up to 4 parameters in any order of following types:
    • uint
    • string
    • bool
    • address
  • There's also the single parameter API for the types above, and additionally bytes, bytes1... up to bytes32:
    • console.logInt(int i)
    • console.logUint(uint i)
    • console.logString(string memory s)
    • console.logBool(bool b)
    • console.logAddress(address a)
    • console.logBytes(bytes memory b)
    • console.logBytes1(bytes1 b)
    • console.logBytes2(bytes2 b)
    • ...
    • console.logBytes32(bytes32 b)
  • console.log implements the same formatting options that can be found in Hardhat's console.log.
    • Example: console.log("Changing owner from %s to %s", currentOwner, newOwner)
  • console.log is implemented in standard Solidity and it is compatible Anvil and Hardhat Networks.
  • console.log calls can run in other networks, like mainnet, kovan, ropsten, etc. They do nothing in those networks, but do spend a minimal amount of gas.

console.log(format[,...args])

The console.log() method prints a formatted string using the first argument as a printf-like format string which can contain zero or more format specifiers. Each specifier is replaced with the converted value from the corresponding argument. Supported specifiers are:

  • %s: String will be used to convert all values to a human-readable string. uint256, int256 and bytes values are converted to their 0x hex encoded values.
  • %d: Number will be used to convert all values to a human-readable string. This is identical to %s.
  • %i: Works the same way as %d.
  • %o: Object. A string representation of an object with generic JavaScript-styled object formatting. For solidity types, this basically surround the string representation of the value in single-quotes.
  • %%: single percent sign ('%'). This does not consume an argument.
  • Returns: <string> The formatted string

If a specifier does not have a corresponding argument, it is not replaced:

console.log("%s:%s", "foo");
// Returns: "foo:%s"

Values that are not part of the format string are formatted using as a human-readable string representation.

If there are more arguments passed to the console.log() method than the number of specifiers, the extra arguments are concatenated to the returned string, separated by spaces:

console.log("%s:%s", "foo", "bar", "baz");
// Returns: "foo:bar baz"

If only one argument is passed to console.log(), it is returned as it is without any formatting:

console.log("%% %s");
// Returns: "%% %s"

The String format specifier (%s) should be used in most cases unless specific functionality is needed from other format specifiers.

DSTest Reference

Dappsys Test (DSTest for short) provides basic logging and assertion functionality. It is included in the Forge Standard Library.

To get access to the functions, import forge-std/Test.sol and inherit from Test in your test contract:

import "forge-std/Test.sol";

contract ContractTest is Test {
    // ... tests ...
}

Logging

This is a complete overview of all the available logging events. For detailed descriptions and example usage, see below.

event log                    (string);
event logs                   (bytes);

event log_address            (address);
event log_bytes32            (bytes32);
event log_int                (int);
event log_uint               (uint);
event log_bytes              (bytes);
event log_string             (string);

event log_named_address      (string key, address val);
event log_named_bytes32      (string key, bytes32 val);
event log_named_decimal_int  (string key, int val, uint decimals);
event log_named_decimal_uint (string key, uint val, uint decimals);
event log_named_int          (string key, int val);
event log_named_uint         (string key, uint val);
event log_named_bytes        (string key, bytes val);
event log_named_string       (string key, string val);

Logging events

This section documents all events for logging and provides usage examples.

log

event log(string);
Example
emit log("here");
// here


logs

event logs(bytes);
Example
emit logs(bytes("abcd"));
// 0x6162636400000000000000000000000000000000000000000000000000000000


log_<type>

event log_<type>(<type>);

Where <type> can be address, bytes32, int, uint, bytes, string

Example
uint256 amount = 1 ether;
emit log_uint(amount);
// 1000000000000000000


log_named_<type>

event log_named_<type>(string key, <type> val);

Where <type> can be address, bytes32, int, uint, bytes, string

Example
uint256 amount = 1 ether;
emit log_named_uint("Amount: ", amount);
// amount: 1000000000000000000


log_named_decimal_<type>

event log_named_decimal_<type>(string key, <type> val, uint decimals);

Where <type> can be int, uint

Example
uint256 amount = 1 ether;
emit log_named_decimal_uint("Amount: ", amount, 18);
// amount: 1.000000000000000000

Asserting

This is a complete overview of all the available assertion functions. For detailed descriptions and example usage, see below.

// Assert the `condition` is true
function assertTrue(bool condition) internal;
function assertTrue(bool condition, string memory err) internal;

// Assert `a` is equal to `b`
function assertEq(address a, address b) internal;
function assertEq(address a, address b, string memory err) internal;
function assertEq(bytes32 a, bytes32 b) internal;
function assertEq(bytes32 a, bytes32 b, string memory err) internal;
function assertEq(int a, int b) internal;
function assertEq(int a, int b, string memory err) internal;
function assertEq(uint a, uint b) internal;
function assertEq(uint a, uint b, string memory err) internal;
function assertEqDecimal(int a, int b, uint decimals) internal;
function assertEqDecimal(int a, int b, uint decimals, string memory err) internal;
function assertEqDecimal(uint a, uint b, uint decimals) internal;
function assertEqDecimal(uint a, uint b, uint decimals, string memory err) internal;
function assertEq(string memory a, string memory b) internal;
function assertEq(string memory a, string memory b, string memory err) internal;
function assertEq32(bytes32 a, bytes32 b) internal;
function assertEq32(bytes32 a, bytes32 b, string memory err) internal;
function assertEq0(bytes memory a, bytes memory b) internal;
function assertEq0(bytes memory a, bytes memory b, string memory err) internal;

// Assert  `a` is greater than `b`
function assertGt(uint a, uint b) internal;
function assertGt(uint a, uint b, string memory err) internal;
function assertGt(int a, int b) internal;
function assertGt(int a, int b, string memory err) internal;
function assertGtDecimal(int a, int b, uint decimals) internal;
function assertGtDecimal(int a, int b, uint decimals, string memory err) internal;
function assertGtDecimal(uint a, uint b, uint decimals) internal;
function assertGtDecimal(uint a, uint b, uint decimals, string memory err) internal;

// Assert  `a` is greater than or equal to `b`
function assertGe(uint a, uint b) internal;
function assertGe(uint a, uint b, string memory err) internal;
function assertGe(int a, int b) internal;
function assertGe(int a, int b, string memory err) internal;
function assertGeDecimal(int a, int b, uint decimals) internal;
function assertGeDecimal(int a, int b, uint decimals, string memory err) internal;
function assertGeDecimal(uint a, uint b, uint decimals) internal;
function assertGeDecimal(uint a, uint b, uint decimals, string memory err) internal;

// Assert  `a` is lesser than `b`
function assertLt(uint a, uint b) internal;
function assertLt(uint a, uint b, string memory err) internal;
function assertLt(int a, int b) internal;
function assertLt(int a, int b, string memory err) internal;
function assertLtDecimal(int a, int b, uint decimals) internal;
function assertLtDecimal(int a, int b, uint decimals, string memory err) internal;
function assertLtDecimal(uint a, uint b, uint decimals) internal;
function assertLtDecimal(uint a, uint b, uint decimals, string memory err) internal;

// Assert  `a` is lesser than or equal to `b`
function assertLe(uint a, uint b) internal;
function assertLe(uint a, uint b, string memory err) internal;
function assertLe(int a, int b) internal;
function assertLe(int a, int b, string memory err) internal;
function assertLeDecimal(int a, int b, uint decimals) internal;
function assertLeDecimal(int a, int b, uint decimals, string memory err) internal;
function assertLeDecimal(uint a, uint b, uint decimals) internal;
function assertLeDecimal(uint a, uint b, uint decimals, string memory err) internal;

Assertion functions

This section documents all functions for asserting and provides usage examples.

assertTrue

function assertTrue(bool condition) internal;

Asserts the condition is true.

Example
bool success = contract.fun();
assertTrue(success);


assertEq

function assertEq(<type> a, <type> b) internal;

Where <type> can be address, bytes32, int, uint

Asserts a is equal to b.

Example
uint256 a = 1 ether;
uint256 b = 1e18 wei;
assertEq(a, b);


assertEqDecimal

function assertEqDecimal(<type> a, <type> b, uint decimals) internal;

Where <type> can be int, uint

Asserts a is equal to b.

Example
uint256 a = 1 ether;
uint256 b = 1e18 wei;
assertEqDecimal(a, b, 18);


assertEq32

function assertEq32(bytes32 a, bytes32 b) internal;

Asserts a is equal to b.

Example
assertEq(bytes32("abcd"), 0x6162636400000000000000000000000000000000000000000000000000000000);


assertEq0

function assertEq0(bytes a, bytes b) internal;

Asserts a is equal to b.

Example
string memory name1 = "Alice";
string memory name2 = "Bob";
assertEq0(bytes(name1), bytes(name2)); // [FAIL]


assertGt

function assertGt(<type> a, <type> b) internal;

Where <type> can be int, uint

Asserts a is greater than b.

Example
uint256 a = 2 ether;
uint256 b = 1e18 wei;
assertGt(a, b);


assertGtDecimal

function assertGtDecimal(<type> a, <type> b, uint decimals) internal;

Where <type> can be int, uint

Asserts a is greater than b.

Example
uint256 a = 2 ether;
uint256 b = 1e18 wei;
assertGtDecimal(a, b, 18);


assertGe

function assertGe(<type> a, <type> b) internal;

Where <type> can be int, uint

Asserts a is greater than or equal to b.

Example
uint256 a = 1 ether;
uint256 b = 1e18 wei;
assertGe(a, b);


assertGeDecimal

function assertGeDecimal(<type> a, <type> b, uint decimals) internal;

Where <type> can be int, uint

Asserts a is greater than or equal to b.

Example
uint256 a = 1 ether;
uint256 b = 1e18 wei;
assertGeDecimal(a, b, 18);


assertLt

function assertLt(<type> a, <type> b) internal;

Where <type> can be int, uint

Asserts a is lesser than b.

Example
uint256 a = 1 ether;
uint256 b = 2e18 wei;
assertLt(a, b);


assertLtDecimal

function assertLtDecimal(<type> a, <type> b, uint decimals) internal;

Where <type> can be int, uint

Asserts a is lesser than b.

Example
uint256 a = 1 ether;
uint256 b = 2e18 wei;
assertLtDecimal(a, b, 18);


assertLe

function assertLe(<type> a, <type> b) internal;

Where <type> can be int, uint

Asserts a is lesser than or equal to b.

Example
uint256 a = 1 ether;
uint256 b = 1e18 wei;
assertLe(a, b);


assertLeDecimal

function assertLeDecimal(<type> a, <type> b, uint decimals) internal;

Where <type> can be int, uint

Asserts a is lesser than or equal to b.

Example
uint256 a = 1 ether;
uint256 b = 1e18 wei;
assertLeDecimal(a, b, 18);

ℹ️ Information

You can pass a custom error message to the above functions by providing an additional parameter string err.

Miscellaneous

Struct Encoding

Structs are user defined types that can group several variables:

struct MyStruct {
    address addr;
    uint256 amount;
}

Only the new ABI coder v2 can encode and decode arbitrarily nested arrays and structs. Since Solidity 0.8.0 it is activated by default, prior to that it needs to be activated via pragma experimental ABIEncoderV2.

Solidity structs map to the ABI type "tuple". For more information on how Solidity types map to ABI types see Mapping Solidity to ABI types in the Solidity documentation.

Structs are therefore encoded and decodes as tuples. So the struct we defined above, MyStruct, maps to the tuple (address,uint256) in terms of the ABI.

Let's see how this works in a contract:

pragma solidity =0.8.15;


contract Test {
    struct MyStruct {
        address addr;
        uint256 amount;
    }
    function f(MyStruct memory t) public pure {}
}

The ABI of the f function in this contract is:

{
	"inputs": [
		{
			"components": [
				{
					"internalType": "address",
					"name": "addr",
					"type": "address"
				},
				{
					"internalType": "uint256",
					"name": "amount",
					"type": "uint256"
				}
			],
			"internalType": "struct Test.MyStruct",
			"name": "t",
			"type": "tuple"
		}
	],
	"name": "f",
	"outputs": [],
	"stateMutability": "pure",
	"type": "function"
}

which reads: The function f takes 1 input of type tuple with two components of type address and uint256.