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