foundry Documentation

Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.

10,551 stars Rust Rust 1 file ยท ~8,708 tokens #alloy#blockchain#bytecode#compiler#compilers#dapp#eth#ethereum
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy foundry on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name foundry --restart always foundry-rs/foundry:latest

File: README.md


Blazing fast, portable and modular toolkit for Ethereum application development, written in Rust.

  • Forge โ€” Build, test, fuzz, debug and deploy Solidity contracts.
  • Cast โ€” Swiss Army knife for interacting with EVM smart contracts, sending transactions and getting chain data.
  • Anvil โ€” Fast local Ethereum development node.
  • Chisel โ€” Fast, utilitarian and verbose Solidity REPL.

Installation

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

See the installation guide for more details.

To verify a downloaded release archive or container image, see Verifying Releases.

Getting Started

Initialize a new project, build and test:

sh
forge init counter && cd counter
forge build
forge test

Interact with a live network:

sh
cast block-number --rpc-url https://eth.merkle.io
cast balance vitalik.eth --ether --rpc-url https://eth.merkle.io

Fork mainnet locally:

sh
anvil --fork-url https://eth.merkle.io

Read the Foundry Docs to learn more.

Contributing

Contributions are welcome and highly appreciated. To get started, check out the contributing guidelines.

Join our [Telegram][tg-url] to chat about the development of Foundry.

Support

Having trouble? Check the Foundry Docs, join the [support Telegram][tg-support-url], or open an issue.

License

Licensed under either of Apache License, Version 2.0 or [MIT license](LICENSE-MIT) at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in these crates by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

File: crates/forge/assets/README.md

Foundry

Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.

Foundry consists of:

  • Forge: Ethereum testing framework (like Truffle, Hardhat and DappTools).
  • Cast: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data.
  • Anvil: Local Ethereum node, akin to Ganache, Hardhat Network.
  • Chisel: Fast, utilitarian, and verbose solidity REPL.

Documentation

https://book.getfoundry.sh/

Usage

Build

shell
$ forge build

Test

shell
$ forge test

Format

shell
$ forge fmt

Gas Snapshots

shell
$ forge snapshot

Anvil

shell
$ anvil

Deploy

shell
$ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key>

Cast

shell
$ cast <subcommand>

Help

shell
$ forge --help
$ anvil --help
$ cast --help

File: crates/fmt/README.md

Formatter (`fmt`)

Solidity formatter that respects (some parts of)
the Style Guide and
is tested on the Prettier Solidity Plugin cases.

Architecture

The formatter is built on top of Solar, and the architecture is based on a Wadler-style pretty-printing engine. The formatting process consists of two main steps:

  1. Parsing: The Solidity source code is parsed using solar into an Abstract Syntax Tree (AST). The AST is a tree representation of the code's syntactic structure.
  2. Printing: The AST is traversed by a visitor, which generates a stream of abstract tokens that are then processed by a pretty-printing engine to produce the final formatted code.

The Pretty Printer (`pp`)

The core of the formatter is a pretty-printing engine inspired by Philip Wadler's algorithm, and adapted from the implementations in rustc_ast_pretty and prettyplease. Its goal is to produce an optimal and readable layout by making intelligent decisions about line breaks.

The process works like this:

  1. AST to Abstract Tokens: The formatter's State object walks the solar AST. Instead of directly writing strings, it translates the AST nodes into a stream of abstract formatting "commands" called Tokens. This decouples the code's structure from the final text output. The primary tokens are:

    • String: An atomic, unbreakable piece of text, like a keyword (function), an identifier (myVar), or a literal (42).
    • Break: A potential line break. This is the core of the engine's flexibility. The Printer later decides whether to render a Break as a single space or as a newline with appropriate indentation.
    • Begin/End: These tokens define a logical group of tokens that should be formatted as a single unit. This allows the printer to decide how to format the entire group at once.
  2. Grouping and Breaking Strategy: The Begin and End tokens create formatting "boxes" that guide the breaking strategy. There are two main types of boxes:

    • Consistent Box (cbox): If any Break inside this box becomes a newline, then all Breaks inside it must also become newlines. This is ideal for lists like function parameters or struct fields, ensuring they are either all on one line or neatly arranged with one item per line.
    • Inconsistent Box (ibox): Breaks within this box are independent. The printer can wrap a long line at any Break point without forcing other breaks in the same box to become newlines. This is useful for formatting long expressions or comments.
  3. The Printer Engine: The Printer consumes this stream of tokens and makes the final decisions:

    • It maintains a buffer of tokens and tracks the remaining space on the current line based on the configured line_length.
    • When it encounters a Begin token for a group, it calculates whether the entire group could fit on the current line if all its Breaks were spaces.
    • If it fits, all Breaks in that group are rendered as spaces.
    • If it doesn't fit, Breaks are rendered as newlines, and the indentation level is adjusted accordingly based on the box's rules (consistent or inconsistent).

Crucially, this entire process is deterministic. Because the formatter completely rebuilds the code from the AST, it discards all original whitespace, line breaks, and other stylistic variations. This means that for a given AST and configuration, the output will always be identical. No matter how inconsistently the input code is formatted, the result is a single, canonical representation, ensuring predictability and consistency across any codebase.

Debug Mode: To visualize the debug output, and understand how the pretty-printer makes its decisions about boxes and breaks, see the Debug section in Testing.

Comments

Comment handling is a critical aspect of the formatter, designed to preserve developer intent while restructuring the code.

  1. Categorization: Comments are parsed and categorized by their position and style: Isolated (on its own line), Mixed (on a line with code), and Trailing (at the end of a line).

  2. Blank Line Handling: Blank lines in the source code are treated as a special BlankLine comment type, allowing the formatter to preserve vertical spacing that separates logical blocks of code. However, to maintain a clean and consistent vertical rhythm, any sequence of multiple blank lines is collapsed into a single blank line. This prevents excessive empty space in the formatted output.

  3. Integration with Printing: During the AST traversal, the formatter queries for comments that appear before the current code element. These comments, including blank lines, are then strategically inserted into the Printer's token stream. The formatter inserts Break tokens around comments to ensure they are correctly spaced from the surrounding code, and emits one or two hardbreaks for blank lines to maintain the original vertical rhythm.

This approach allows the formatter to respect both the syntactic structure of the code and the developer's textual annotations and spacing, producing a clean, readable, and intentional layout.

Example

Source Code

solidity
pragma   solidity ^0.8.10 ;
contract  HelloWorld {
    string   public message;
    constructor(  string memory initMessage) { message = initMessage;}
}



event    Greet( string  indexed  name) ;

Abstract Syntax Tree (AST) (simplified)

text
SourceUnit
 โ”œโ”€ PragmaDirective("solidity", "^0.8.10")
 โ”œโ”€ ItemContract("HelloWorld")
 โ”‚   โ”œโ”€ VariableDefinition { name: "message", ty: "string", visibility: "public" }
 โ”‚   โ””โ”€ ItemFunction {
 โ”‚         kind: Constructor,
 โ”‚         header: FunctionHeader {
 โ”‚            parameters: [
 โ”‚               VariableDefinition { name: "initMessage", ty: "string", data_location: "memory" }
 โ”‚            ]
 โ”‚         },
 โ”‚         body: Block {
 โ”‚            stmts: [
 โ”‚               Stmt { kind: Expr(Assign {lhs: Ident("message"), rhs: Ident("initMessage")}) }
 โ”‚            ]
 โ”‚         }
 โ”‚      }
 โ””โ”€ ItemEvent { name: "Greet", parameters: [
       VariableDefinition { name: "name", ty: "string", indexed: true }
    ] }

Formatted Source Code
The code is reconstructed from the AST using the pretty-printer.

solidity
pragma solidity ^0.8.10;

contract HelloWorld {
    string public message;

    constructor(string memory initMessage) {
        message = initMessage;
    }
}

event Greet(string indexed name);

Configuration

The formatter supports multiple configuration options defined in foundry.toml.

Option Default Description
line_length 120 Maximum line length where the formatter will try to wrap the line.
tab_width 4 Number of spaces per indentation level. Ignored if style is tab.
style space The style of indentation. Options: space, tab.
bracket_spacing false Print spaces between brackets.
int_types long Style for uint256/int256 types. Options: long, short, preserve.
multiline_func_header attributes_first The style of multiline function headers. Options: attributes_first, params_always, params_first_multi, all, all_params.
prefer_compact all Style that determines if a broken list, should keep its elements together on their own line, before breaking individually. Options: none, calls, events, errors, events_errors, all.
quote_style double The style of quotation marks. Options: double, single, preserve.
number_underscore preserve The style of underscores in number literals. Options: preserve, remove, thousands.
hex_underscore remove The style of underscores in hex literals. Options: preserve, remove, bytes.
single_line_statement_blocks preserve The style of single-line blocks in statements. Options: preserve, single, multi.
override_spacing false Print a space in the override attribute.
wrap_comments false Wrap comments when line_length is reached.
docs_style preserve Enforces the style of doc (natspec) comments. Options: preserve, line, block.
ignore [] Globs to ignore.
contract_new_lines false Add a new line at the start and end of contract declarations.
sort_imports false Sort import statements alphabetically in groups. A group is a set of imports separated by a newline.
namespace_import_style prefer_plain Style for namespace imports. Options: prefer_plain (import "foo" as foo;), prefer_glob (import * as foo from "foo";), preserve.
pow_no_space false Suppress spaces around the power operator (**).
single_line_imports false Keep single imports on a single line, even if they exceed the line length limit.

Check FormatterConfig for a more detailed explanation.

Inline Configuration

The formatter can be instructed to skip specific sections of code using inline comments. While the tool supports fine-grained control, it is generally more robust and efficient to disable formatting for entire AST items or statements.

This approach is preferred because it allows the formatter to treat the entire disabled item as a single, opaque unit. It can simply copy the original source text for that item's span instead of partially formatting a line, switching to copy mode, and then resuming formatting. This leads to more predictable output and avoids potential edge cases with complex, partially-disabled statements.

Disable Line

These directives are best used when they apply to a complete, self-contained AST statement, as shown below. In this case, uint x = 100; is a full statement, making it a good candidate for a line-based disable.

To disable the next line:

solidity
// forgefmt: disable-next-line
uint x = 100;

To disable the current line:

solidity
uint x = 100; // forgefmt: disable-line

Disable Block

This is the recommended approach for complex, multi-line constructs where you want to preserve specific formatting. In the example below, the entire function definition is disabled. This is preferable to trying to disable individual lines within the signature, because lines like uint256 b /* a comment that goes inside the comma */, do not correspond to a complete AST item or statement on their own. Disabling the whole item is cleaner and more aligned with the code's structure.

solidity
// forgefmt: disable-start
function fnWithManyArguments(
    uint a,
    uint256 b /* a comment that goes inside the comma */,
    uint256      c
) external returns (bool) {
// forgefmt: disable-end

Contributing

Check out the foundry contribution guide.

Guidelines for contributing to forge fmt:

Opening an issue

  1. Create a short, concise title describing the issue.
    • Bad: Forge fmt does not work
    • Good: bug(forge-fmt): misplaces postfix comment on if-statement
  2. Fill in the issue template fields, including Foundry version, platform, and component info.
  3. Provide code snippets showing the current and expected behaviors.
  4. If it's a feature request, explain why the feature is needed.
  5. Add the C-forge and Cmd-forge-fmt labels.

Fixing a Bug or Developing a Feature

  1. Specify the issue being addressed in the PR description.
  2. Add a note on your solution in the PR description.
  3. Ensure the PR includes comprehensive acceptance tests under fmt/testdata/, covering:
    • The specific case being fixed/added.
    • Behavior with different kinds of comments (isolated, mixed, trailing).
    • If it's a new config value, tests covering all available options.

Testing

Tests are located in the fmt/testdata folder. Each test consists of an original.sol file and one or more expected output files, named *.fmt.sol.

The default configuration can be overridden from within an expected output file by adding a comment in the format // config: {config_key} = {config_value}. For example:

solidity
// config: line_length = 160

The testing process for each test suite is as follows:

  1. Read original.sol and the corresponding *.fmt.sol expected output.
  2. Parse any // config: comments from the expected file to create a test-specific configuration.
  3. Format original.sol and assert that the output matches the content of *.fmt.sol.
  4. To ensure idempotency, format the content of *.fmt.sol again and assert that the output does not change.

Debug

The formatter includes a debug mode that provides visual insight into the pretty-printer's decision-making process. This is invaluable for troubleshooting complex formatting issues and understanding how the boxes and breaks described in The Pretty Printer section work.

To enable it, run the formatter with the FMT_DEBUG environment variable set:

sh
FMT_DEBUG=1 cargo test -p forge-fmt --test formatter Repros

When enabled, the output will be annotated with special characters representing the printer's internal state:

  • Boxes:

    • ยซ and ยป: Mark the start and end of a consistent box (cbox).
    • โ€น and โ€บ: Mark the start and end of an inconsistent box (ibox).
  • Breaks:

    • ยท: Represents a Break token, which could be a space or a newline.

For example, running debug mode on the HelloWorld contract from earlier would produce an output like this:

text
pragma solidity ^0.8.10;ยท
ยท
ยซโ€นยซcontract HelloWorld ยป{โ€บยท
โ€นโ€น    stringยท publicยท messageโ€บโ€บ;ยท
ยท
ยซ    constructorยซ(ยซโ€นโ€นstring memory initMessageโ€บโ€บยป)ยป {ยปยท
ยซโ€น        message = ยทinitMessageโ€บยท;ยท
ยป    }ยท
ยป}ยท
ยท
event Greet(ยซยซโ€นโ€นstring indexed nameโ€บโ€บยปยป);ยท

This annotated output allows you to see exactly how the printer is grouping tokens and where it considers inserting a space or a newline. This makes it much easier to diagnose why a certain layout is being produced.


File: crates/doc/README.md

forge-doc

Solidity documentation generator powered by solar.

Overview

forge-doc walks the solar HIR (High-level Intermediate Representation) and emits
vocs-flavoured MDX pages suitable for publishing as a documentation site.

Key components

  • DocBuilder: entry point, configures source roots, output directory, and optional
    Git/deployment metadata, then calls build() to generate the docs.
  • render: AST โ†’ MDX conversion with HIR lookups. Handles contracts, functions, structs,
    enums, errors, events, and UDVTs, including explicit @inheritdoc and implicit NatSpec
    inheritance, plus inline {Link} rewriting. Depends on HIR for inheritance and cross-reference
    resolution.
  • hir_ext: HIR-aware helpers including name-to-page mapping, inheritance links, effective
    NatSpec resolution, and inline link replacement.
  • utils: small utility functions, git_source_url and read_deployments.
  • vocs: vocs site scaffolding, generates vocs.config.ts, vocs.sidebar.ts
    (always regenerated, imported by vocs.config.ts), package.json, .gitignore, and
    src/pages/index.mdx.

File: crates/config/README.md

Configuration

Foundry's configuration system allows you to configure its tools the way you want while also providing with a
sensible set of defaults.

Profiles

Configurations can be arbitrarily namespaced with profiles. Foundry's default config is also named default, but you can
arbitrarily name and configure profiles as you like and set the FOUNDRY_PROFILE environment variable to the selected
profile's name. This results in foundry's tools (forge) preferring the values in the profile with the named that's set
in FOUNDRY_PROFILE. But all custom profiles inherit from the default profile.

foundry.toml

Foundry's tools search for a foundry.toml or the filename in a FOUNDRY_CONFIG environment variable starting at the
current working directory. If it is not found, the parent directory, its parent directory, and so on are searched until
the file is found or the root is reached. But the typical location for the global foundry.toml would
be ~/.foundry/foundry.toml, which is also checked. If the path set in FOUNDRY_CONFIG is absolute, no such search
takes place and the absolute path is used directly.

In foundry.toml you can define multiple profiles, therefore the file is assumed to be nested, so each top-level key
declares a profile and its values configure the profile.

The following is an example of what such a file might look like. This can also be obtained with forge config

toml
## defaults for _all_ profiles
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
solc = "0.8.10" # to use a specific local solc install set the path as `solc = "/solc"`
eth-rpc-url = "https://mainnet.infura.io"

## set only when the `hardhat` profile is selected
[profile.hardhat]
src = "contracts"
out = "artifacts"
libs = ["node_modules"]

## set only when the `spells` profile is selected
[profile.spells]
## --snip-- more settings

Default profile

When determining the profile to use, Config considers the following sources in ascending priority order to read from
and merge, at the per-key level:

  1. [Config::default()], which provides default values for all parameters.
  2. foundry.toml or TOML file path in FOUNDRY_CONFIG environment variable.
  3. FOUNDRY_ or DAPP_ prefixed environment variables.

The selected profile is the value of the FOUNDRY_PROFILE environment variable, or if it is not set, "default".

Configuration Reference

For a full list of all configuration options, see the Foundry Book:

Environment Variables

Foundry's tools read all environment variable names prefixed with FOUNDRY_ using the string after the _ as the name
of a configuration value as the value of the parameter as the value itself. But the
corresponding dapptools config vars are also
supported, this means that FOUNDRY_SRC and DAPP_SRC are equivalent.

Some exceptions to the above are explicitly ignored due to security concerns.

Environment variables take precedence over values in foundry.toml. Values are parsed as a loose form of TOML syntax.


File: crates/cli/README.md

Foundry CLIs

The CLIs are written using clap's derive feature.

Installation

See Installation.

Usage

Read the ๐Ÿ“– Foundry Book

Debugging

Debug logs are printed with
tracing. You can configure the
verbosity level via the
RUST_LOG
environment variable, on a per package level,
e.g.:RUST_LOG=forge,foundry_evm forge test


File: crates/cheatcodes/README.md

foundry-cheatcodes

Foundry cheatcodes definitions and implementations.

Structure

  • assets/: JSON interface and specification
  • spec/: Defines common traits and structs
  • src/: Rust implementations of the cheatcodes

Overview

All cheatcodes are defined in a single sol! macro call in spec/src/vm.rs.

This, combined with the use of an internal Cheatcode derive macro,
allows us to generate both the Rust definitions and the JSON specification of the cheatcodes.

Cheatcodes are manually implemented through the Cheatcode trait, which is called in the
Cheatcodes inspector implementation.

See the cheatcodes dev documentation for more details.

JSON interface

The JSON interface is guaranteed to be stable, and can be used by third-party tools to interact with
the Foundry cheatcodes externally.

For example, here are some tools that make use of the JSON interface:

If you are making use of the JSON interface, please don't hesitate to open a PR to add your project to this list!

Adding a new cheatcode

Please see the cheatcodes dev documentation on how to add new cheatcodes.


File: docs/dev/architecture.md

Architecture

This document describes the high-level architecture of Foundry.

`evm/`

Foundry's EVM tooling. This is built around revm and has additional
implementation of:

  • cheatcodes: a set of solidity calls dedicated to testing which can manipulate the environment in which the execution is run

`config/`

Includes all of Foundry's settings and how to get them

`cli/`

The core forge and cast cli implementation. Includes all subcommands.


File: docs/dev/cheatcodes.md

Cheatcodes

Foundry's EVM support is mainly dedicated to testing and exploration, it features a set of cheatcodes which can
manipulate the environment in which the execution is run.

Most of the time, simply testing your smart contracts outputs isn't enough. To manipulate the state of the EVM, as well
as test for specific reverts and events, Foundry is shipped with a set of cheatcodes.

[`revm::Inspector`](https://docs.rs/revm/latest/revm/trait.Inspector.html)

To understand how cheatcodes are implemented, we first need to look at revm::Inspector,
a trait that provides a set of callbacks to be notified at certain stages of EVM execution.

For example, Inspector::call
is called when the EVM is about to execute a call and is provided with the call's inputs and the
current state of the EVM.

[Foundry inspectors](../../crates/evm/evm/src/inspectors/)

The evm crate has a variety of inspectors for different use cases, such as

  • coverage
  • tracing
  • debugger
  • logging

[Cheatcode inspector](../../crates/cheatcodes/src/inspector.rs)

The concept of cheatcodes and cheatcode inspector is very simple.

Cheatcodes are calls to a specific address, the cheatcode handler address, defined as
address(uint160(uint256(keccak256("hevm cheat code")))) (0x7109709ECfa91a80626fF3989D68f67F5b1DD12D).

In Solidity, this can be initialized as Vm constant vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);,
but generally this is inherited from forge-std/Test.sol.

Since cheatcodes are bound to a constant address, the cheatcode inspector listens for that address:

rust
impl Inspector for Cheatcodes {
    fn call(&mut self, ...) -> ... {
        if call.contract == CHEATCODE_ADDRESS {
            // intercepted cheatcode call
            // --snip--
        }
    }
}

When a call to a cheatcode is intercepted we try to decode the calldata into a known cheatcode.

Rust bindings for the cheatcode interface are generated via the Alloy sol! macro.

If a call was successfully decoded into the VmCalls enum that the sol! macro generates, the
last step is a large match over the decoded function call structs, which serves as the
implementation handler for the cheatcode. This is also automatically generated, in part, by the
sol! macro, through the use of a custom internal derive procedural macro.

Cheatcodes implementation

All the cheatcodes are defined in a large sol! macro call in cheatcodes/spec/src/vm.rs:

rust
sol! {
#[derive(Cheatcode)]
interface Vm {
    //  ======== Types ========

    /// Error thrown by a cheatcode.
    error CheatcodeError(string message);

    // ...

    // ======== EVM ========

    /// Gets the address for a given private key.
    #[cheatcode(group = Evm, safety = Safe)]
    function addr(uint256 privateKey) external pure returns (address keyAddr);

    /// Gets the nonce of an account.
    #[cheatcode(group = Evm, safety = Safe)]
    function getNonce(address account) external view returns (uint64 nonce);

    // ...
}
}

This, combined with the use of an internal Cheatcode derive macro,
allows us to generate both the Rust definitions and the JSON specification of the cheatcodes.

Cheatcodes are manually implemented through the Cheatcode trait, which is
called in the Cheatcodes inspector implementation.

[`sol!`]

Generates the raw Rust bindings for the cheatcodes, as well as lets us specify custom attributes
individually for each item, such as functions and structs, or for entire interfaces.

The way bindings are generated and extra information can be found in the sol! documentation.

We leverage this macro to apply the Cheatcode derive macro on the Vm interface.

[`Cheatcode`](../../crates/macros/src/cheatcodes.rs) derive macro

This is derived once on the Vm interface declaration, which recursively applies it to all of the
interface's items, as well as the sol!-generated items, such as the VmCalls enum.

This macro performs extra checks on functions and structs at compile time to make sure they are
documented and have named parameters, and generates a macro which is later used to implement the
match { ... } function that is to be used to dispatch the cheatcode implementations after a call is
decoded.

The latter is what fails compilation when adding a new cheatcode, and is fixed by implementing the
Cheatcode trait to the newly-generated function call struct(s).

The Cheatcode derive macro also parses the #[cheatcode(...)] attributes on functions, which are
used to specify additional properties of the JSON interface.

These are all the attributes that can be specified on cheatcode functions:

  • #[cheatcode(group = )]: The group that the cheatcode belongs to. Required.
  • #[cheatcode(status = )]: The current status of the cheatcode. E.g. whether it is stable or experimental, etc. Defaults to Stable.
  • #[cheatcode(safety = )]: Whether the cheatcode is safe to use inside of scripts. E.g. it does not change state in an unexpected way. Defaults to the group's safety if unspecified. If the group is ambiguous, then it must be specified manually.

Multiple attributes can be specified by separating them with commas, e.g. #[cheatcode(group = Evm, status = Experimental)].

`Cheatcode` trait

This trait defines the interface that all cheatcode implementations must implement.
There are three methods that can be implemented:

Method Purpose When to use
apply Pure cheatcodes that don't need EVM data access For simple state manipulations
apply_stateful Cheatcodes that need EVM data access For operations requiring current EVM state
apply_full Cheatcodes that need EVM executor access For operations requiring recursive EVM calls

Only one of these methods can be implemented.

This trait is implemented manually for each cheatcode in the foundry-cheatcodes
crate on the sol!-generated function call structs.

[JSON interface](../../crates/cheatcodes/assets/cheatcodes.json)

The JSON interface and schema
are automatically generated from the sol! macro call by running cargo cheats.

The initial execution of this command, following the addition of a new cheat code, will result in an
update to the JSON files, which is expected to fail. This failure is necessary for the CI system to
detect that changes have occurred. Subsequent executions should pass, confirming the successful
update of the files.

Adding a new cheatcode

  1. Add its Solidity definition(s) in cheatcodes/spec/src/vm.rs. Ensure that all structs and functions are documented, and that all function parameters are named. This will initially fail to compile because of the automatically generated match { ... } expression. This is expected, and will be fixed in the next step
  2. Implement the cheatcode in cheatcodes in its category's respective module. Follow the existing implementations as a guide.
  3. If a struct, enum, error, or event was added to Vm, update spec::Cheatcodes::new
  4. Update the JSON interface by running cargo cheats twice. This is expected to fail the first time that this is run after adding a new cheatcode; see JSON interface
  5. Write an integration test for the cheatcode in [testdata/default/cheats/]

Mapping storage hooks

The experimental, unsafe registerMappingSstoreHook cheatcode registers a post-store callback for
one concrete mapping root in a target's effective storage context. Its callback arguments are
(account, computedSlot, rootSlot, keys, oldValue, newValue). keys contains raw bytes32 storage
words in root-to-leaf order.

Mapping hooks require complete 64-byte Keccak provenance observed after the latest mapping-hook
registration for the target. Resolution follows the complete chain to its terminal root rather
than stopping at a registered intermediate hash. Scalar, offset, incomplete, unknown, and
previously precomputed slots do not invoke them. Raw and mapping SSTORE hooks cannot be combined
for the same target. Callbacks must authenticate msg.sender == address(vm) to prevent external
spoofing.