Skip to main content

forge/
opts.rs

1use crate::cmd::{
2    bind::BindArgs, bind_json, build::BuildArgs, cache::CacheArgs, clone::CloneArgs,
3    compiler::CompilerArgs, config, coverage, create::CreateArgs, doc::DocArgs, eip712, exploit,
4    flatten, fmt::FmtArgs, fuzz::FuzzArgs, geiger, init::InitArgs, inspect, install::InstallArgs,
5    lint::LintArgs, lsp::LspArgs, properties::PropertiesArgs, reinit::ReinitArgs,
6    remappings::RemappingArgs, remove::RemoveArgs, selectors::SelectorsSubcommands, snapshot,
7    soldeer, test, tree, update,
8};
9use clap::{Parser, Subcommand, ValueHint};
10use forge_script::ScriptArgs;
11use forge_verify::{VerifyArgs, VerifyBytecodeArgs, VerifyCheckArgs};
12use foundry_cli::opts::GlobalArgs;
13use foundry_common::version::{LONG_VERSION, SHORT_VERSION};
14use std::path::PathBuf;
15
16/// Build, test, fuzz, debug and deploy Solidity contracts.
17#[derive(Parser)]
18#[command(
19    name = "forge",
20    version = SHORT_VERSION,
21    long_version = LONG_VERSION,
22    after_help = "Find more information in the book: https://getfoundry.sh/forge/overview",
23    next_display_order = None,
24)]
25pub struct Forge {
26    /// Include the global arguments.
27    #[command(flatten)]
28    pub global: GlobalArgs,
29
30    #[command(subcommand)]
31    pub cmd: ForgeSubcommand,
32}
33
34#[derive(Subcommand)]
35pub enum ForgeSubcommand {
36    /// Run the project's tests
37    ///
38    /// Examples:
39    /// - forge test
40    /// - forge test --match-test test_Increment -vvvv (show traces for a matching test)
41    /// - forge test --match-contract CounterTest --fuzz-runs 1000
42    #[command(verbatim_doc_comment, visible_alias = "t")]
43    Test(test::TestArgs),
44
45    /// Run and manage Forge fuzzing corpora.
46    Fuzz(FuzzArgs),
47
48    /// Run agents that prove bugs with failing tests and report the bugs that they agree on
49    ///
50    /// Examples:
51    /// - forge properties --target src/A.sol --agent-command ./agent.sh --seed 1 --seed 2
52    /// - forge properties --context docs/spec.md --agent-command ./agent.sh --seed 1 --seed 2
53    #[command(verbatim_doc_comment)]
54    Properties(PropertiesArgs),
55
56    /// Run a smart contract as a script, building transactions that can be sent onchain
57    ///
58    /// Examples:
59    /// - forge script script/Counter.s.sol (simulate the script locally)
60    /// - forge script script/Counter.s.sol --rpc-url $RPC_URL --broadcast --account dev
61    /// - forge script script/Counter.s.sol --rpc-url $RPC_URL --resume
62    #[command(verbatim_doc_comment)]
63    Script(ScriptArgs),
64
65    /// Generate coverage reports
66    ///
67    /// Examples:
68    /// - forge coverage
69    /// - forge coverage --report lcov --report-file lcov.info
70    /// - forge coverage --match-contract CounterTest
71    #[command(verbatim_doc_comment)]
72    Coverage(coverage::CoverageArgs),
73
74    /// Generate Rust bindings for smart contracts.
75    #[command(alias = "bi")]
76    Bind(BindArgs),
77
78    /// Build the project's smart contracts
79    ///
80    /// Examples:
81    /// - forge build
82    /// - forge build --sizes (print a contract size report)
83    /// - forge build --watch (rebuild on file changes)
84    #[command(verbatim_doc_comment, visible_aliases = ["b", "compile"])]
85    Build {
86        /// Require foundry.lock to match direct Git dependency submodules.
87        #[arg(long)]
88        locked: bool,
89        #[command(flatten)]
90        args: BuildArgs,
91    },
92
93    /// Clone a contract from Etherscan
94    ///
95    /// Examples:
96    /// - forge clone $WETH weth --etherscan-api-key $KEY (clone WETH into ./weth)
97    /// - forge clone --chain sepolia --etherscan-api-key $KEY $ADDRESS my-contract
98    #[command(verbatim_doc_comment)]
99    Clone(CloneArgs),
100
101    /// Update one or multiple dependencies.
102    ///
103    /// If no arguments are provided, then all dependencies are updated.
104    #[command(visible_alias = "u")]
105    Update(update::UpdateArgs),
106
107    /// Install one or multiple dependencies
108    ///
109    /// If no arguments are provided, then existing dependencies will be installed.
110    ///
111    /// Examples:
112    /// - forge install (install all dependencies of the project)
113    /// - forge install openzeppelin/openzeppelin-contracts
114    /// - forge install openzeppelin/openzeppelin-contracts@v5.0.2 (pin a version)
115    #[command(verbatim_doc_comment, visible_aliases = ["i", "add"])]
116    Install(InstallArgs),
117
118    /// Reinitialize the project's Git submodules, discarding local changes.
119    Reinit(ReinitArgs),
120
121    /// Remove one or multiple dependencies.
122    #[command(visible_alias = "rm")]
123    Remove(RemoveArgs),
124
125    /// Get the automatically inferred remappings for the project.
126    #[command(visible_alias = "re")]
127    Remappings(RemappingArgs),
128
129    /// Verify smart contracts on Etherscan and Sourcify
130    ///
131    /// Examples:
132    /// - forge verify-contract --chain sepolia $ADDRESS src/Counter.sol:Counter --watch
133    /// - forge verify-contract $ADDRESS src/Counter.sol:Counter --verifier sourcify
134    #[command(verbatim_doc_comment, visible_alias = "v")]
135    VerifyContract(VerifyArgs),
136
137    /// Check verification status on the selected verifier.
138    #[command(visible_alias = "vc")]
139    VerifyCheck(VerifyCheckArgs),
140
141    /// Verify the deployed bytecode against its source on Etherscan.
142    #[command(visible_alias = "vb")]
143    VerifyBytecode(VerifyBytecodeArgs),
144
145    /// Deploy a smart contract
146    ///
147    /// Examples:
148    /// - forge create Counter --rpc-url $RPC_URL --account dev --broadcast
149    /// - forge create Token --private-key $PK --broadcast --constructor-args Token TKN
150    #[command(verbatim_doc_comment, visible_alias = "c")]
151    Create(CreateArgs),
152
153    /// Create a new Forge project.
154    Init(InitArgs),
155
156    /// Generate shell completions script.
157    #[command(visible_alias = "com")]
158    Completions {
159        #[arg(value_enum)]
160        shell: foundry_cli::clap::Shell,
161    },
162
163    /// Remove the build artifacts and cache directories.
164    #[command(visible_alias = "cl")]
165    Clean {
166        /// The project's root path.
167        ///
168        /// By default root of the Git repository, if in one,
169        /// or the current working directory.
170        #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")]
171        root: Option<PathBuf>,
172    },
173
174    /// Manage the Foundry cache.
175    Cache(CacheArgs),
176
177    /// Create a gas snapshot of each test's gas usage
178    ///
179    /// Examples:
180    /// - forge snapshot
181    /// - forge snapshot --diff (compare against the existing .gas-snapshot file)
182    /// - forge snapshot --check (fail if gas usage does not match .gas-snapshot)
183    #[command(verbatim_doc_comment, visible_alias = "s")]
184    Snapshot(snapshot::GasSnapshotArgs),
185
186    /// Display the current config.
187    #[command(visible_alias = "co")]
188    Config(config::ConfigArgs),
189
190    /// Flatten a source file and all of its imports into one file.
191    #[command(visible_alias = "f")]
192    Flatten(flatten::FlattenArgs),
193
194    /// Format Solidity source files
195    ///
196    /// Examples:
197    /// - forge fmt
198    /// - forge fmt --check (report formatting issues without writing changes)
199    /// - forge fmt src/Counter.sol
200    #[command(verbatim_doc_comment)]
201    Fmt(FmtArgs),
202
203    /// Lint Solidity source files
204    #[command(visible_alias = "l")]
205    Lint(LintArgs),
206
207    /// Open Solidity in VS Code or start the language server.
208    Lsp(LspArgs),
209
210    /// Get specialized information about a smart contract
211    ///
212    /// Examples:
213    /// - forge inspect Counter abi
214    /// - forge inspect Counter bytecode
215    /// - forge inspect src/Counter.sol:Counter storageLayout
216    #[command(verbatim_doc_comment, visible_alias = "in")]
217    Inspect(inspect::InspectArgs),
218
219    /// Display a tree visualization of the project's dependency graph.
220    #[command(visible_alias = "tr")]
221    Tree(tree::TreeArgs),
222
223    /// DEPRECATED: Detects usage of unsafe cheat codes in a project and its dependencies.
224    ///
225    /// This is an alias for `forge lint --only-lint unsafe-cheatcode`.
226    Geiger(geiger::GeigerArgs),
227
228    /// Generate documentation for the project.
229    Doc(DocArgs),
230
231    /// Ask a model on the Tempo Gateway to find a bug in a contract and prove it with a test.
232    ///
233    /// Examples:
234    /// - forge exploit --target src/Vault.sol
235    /// - forge exploit --target src/Vault.sol --context docs/spec.md --attempts 5
236    #[command(verbatim_doc_comment)]
237    Exploit(exploit::ExploitArgs),
238
239    /// Function selector utilities.
240    #[command(visible_alias = "se")]
241    Selectors {
242        #[command(subcommand)]
243        command: SelectorsSubcommands,
244    },
245
246    /// Compiler utilities.
247    Compiler(CompilerArgs),
248
249    /// Soldeer dependency manager.
250    Soldeer(soldeer::SoldeerArgs),
251
252    /// Generate EIP-712 struct encodings for structs from a given file.
253    Eip712(eip712::Eip712Args),
254
255    /// Generate bindings for serialization/deserialization of project structs via JSON cheatcodes.
256    BindJson(bind_json::BindJsonArgs),
257}
258
259#[cfg(test)]
260mod tests {
261    use super::*;
262    use clap::CommandFactory;
263
264    #[test]
265    fn verify_cli() {
266        Forge::command().debug_assert();
267    }
268
269    #[test]
270    fn parse_lsp_args() {
271        let args = Forge::try_parse_from(["forge", "lsp", "--stdio"]).unwrap();
272        let ForgeSubcommand::Lsp(args) = args.cmd else {
273            panic!("expected lsp subcommand");
274        };
275        assert!(args.stdio);
276    }
277
278    #[test]
279    fn parse_lsp_editor_args() {
280        let args = Forge::try_parse_from([
281            "forge",
282            "lsp",
283            "--vscode",
284            "--code-path",
285            "code-insiders",
286            "project",
287        ])
288        .unwrap();
289        let ForgeSubcommand::Lsp(args) = args.cmd else {
290            panic!("expected lsp subcommand");
291        };
292        assert!(!args.stdio);
293        assert!(args.vscode);
294        assert_eq!(args.path, Some(PathBuf::from("project")));
295        assert_eq!(args.code_path, Some(PathBuf::from("code-insiders")));
296    }
297
298    #[test]
299    fn lsp_stdio_conflicts_with_editor_args() {
300        for options in [&["--vscode"][..], &["--code-path", "code"], &["project"]] {
301            let error = Forge::try_parse_from(
302                ["forge", "lsp", "--stdio"].into_iter().chain(options.iter().copied()),
303            )
304            .err()
305            .expect("stdio must reject editor launch options");
306            assert_eq!(error.kind(), clap::error::ErrorKind::ArgumentConflict);
307        }
308    }
309}