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}