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, 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}