Skip to main content

soroban_cli/commands/
mod.rs

1use std::str::FromStr;
2
3use clap::{error::ErrorKind, CommandFactory, FromArgMatches, Parser};
4
5use crate::config;
6
7pub mod cache;
8pub mod cfg;
9pub mod completion;
10pub mod container;
11pub mod contract;
12pub mod doctor;
13pub mod env;
14pub mod events;
15pub mod fee_stats;
16pub mod fees;
17pub mod global;
18pub mod keys;
19pub mod ledger;
20pub mod message;
21pub mod network;
22pub mod plugin;
23pub mod snapshot;
24pub mod token;
25pub mod tx;
26pub mod version;
27
28pub mod txn_result;
29
30pub const HEADING_RPC: &str = "RPC Options";
31pub const HEADING_ARCHIVE: &str = "Archive Options";
32pub const HEADING_GLOBAL: &str = "Global Options";
33pub const HEADING_SIGNING: &str = "Signing Options";
34pub const HEADING_TRANSACTION: &str = "Transaction Options";
35const ABOUT: &str =
36    "Work seamlessly with Stellar accounts, contracts, and assets from the command line.
37
38- Generate and manage keys and accounts
39- Build, deploy, and interact with contracts
40- Deploy asset contracts
41- Stream events
42- Start local testnets
43- Decode, encode XDR
44- More!
45
46For additional information see:
47
48- Stellar Docs: https://developers.stellar.org
49- Smart Contract Docs: https://developers.stellar.org/docs/build/smart-contracts/overview
50- CLI Docs: https://developers.stellar.org/docs/tools/developer-tools/cli/stellar-cli";
51
52// long_about is shown when someone uses `--help`; short help when using `-h`
53const LONG_ABOUT: &str = "
54
55To get started generate a new identity:
56
57    stellar keys generate alice
58
59Use keys with the `--source` flag in other commands.
60
61Commands that work with contracts are organized under the `contract` subcommand. List them:
62
63    stellar contract --help
64
65Use contracts like a CLI:
66
67    stellar contract invoke --id CCR6QKTWZQYW6YUJ7UP7XXZRLWQPFRV6SWBLQS4ZQOSAF4BOUD77OTE2 --source alice --network testnet -- --help
68
69Anything after the `--` double dash (the \"slop\") is parsed as arguments to the contract-specific CLI, generated on-the-fly from the contract schema. For the hello world example, with a function called `hello` that takes one string argument `to`, here's how you invoke it:
70
71    stellar contract invoke --id CCR6QKTWZQYW6YUJ7UP7XXZRLWQPFRV6SWBLQS4ZQOSAF4BOUD77OTE2 --source alice --network testnet -- hello --to world
72";
73
74#[derive(Parser, Debug)]
75#[command(
76    name = "stellar",
77    about = ABOUT,
78    version = version::long(),
79    long_about = ABOUT.to_string() + LONG_ABOUT,
80    disable_help_subcommand = true,
81)]
82pub struct Root {
83    #[clap(flatten)]
84    pub global_args: global::Args,
85
86    #[command(subcommand)]
87    pub cmd: Cmd,
88}
89
90impl Root {
91    pub fn new() -> Result<Self, Error> {
92        Self::try_parse().map_err(|e| match e.kind() {
93            ErrorKind::InvalidSubcommand => match plugin::default::run() {
94                Ok(()) => Error::Clap(e),
95                Err(e) => Error::PluginDefault(e),
96            },
97            _ => Error::Clap(e),
98        })
99    }
100
101    pub fn from_arg_matches<I, T>(itr: I) -> Result<Self, clap::Error>
102    where
103        I: IntoIterator<Item = T>,
104        T: Into<std::ffi::OsString> + Clone,
105    {
106        Self::from_arg_matches_mut(&mut Self::command().get_matches_from(itr))
107    }
108
109    pub async fn run(&mut self) -> Result<(), Error> {
110        match &mut self.cmd {
111            Cmd::Completion(completion) => completion.run(),
112            Cmd::Plugin(plugin) => plugin.run(&self.global_args).await?,
113            Cmd::Contract(contract) => contract.run(&self.global_args).await?,
114            Cmd::Doctor(doctor) => doctor.run(&self.global_args).await?,
115            Cmd::Config(config) => config.run()?,
116            Cmd::Events(events) => events.run().await?,
117            Cmd::Xdr(xdr) => xdr.run()?,
118            Cmd::Strkey(strkey) => strkey.run()?,
119            Cmd::Network(network) => network.run(&self.global_args).await?,
120            Cmd::Container(container) => container.run(&self.global_args).await?,
121            Cmd::Snapshot(snapshot) => snapshot.run(&self.global_args).await?,
122            Cmd::Version(version) => version.run(),
123            Cmd::Keys(id) => id.run(&self.global_args).await?,
124            Cmd::Token(token) => token.run(&self.global_args).await?,
125            Cmd::Tx(tx) => tx.run(&self.global_args).await?,
126            Cmd::Ledger(ledger) => ledger.run(&self.global_args).await?,
127            Cmd::Message(message) => message.run(&self.global_args).await?,
128            Cmd::Cache(cache) => cache.run()?,
129            Cmd::Env(env) => env.run(&self.global_args)?,
130            Cmd::Fees(env) => env.run(&self.global_args).await?,
131            Cmd::FeeStats(env) => env.run(&self.global_args).await?,
132        }
133        Ok(())
134    }
135}
136
137impl FromStr for Root {
138    type Err = clap::Error;
139
140    fn from_str(s: &str) -> Result<Self, Self::Err> {
141        Self::from_arg_matches(s.split_whitespace())
142    }
143}
144
145#[derive(Parser, Debug)]
146pub enum Cmd {
147    /// Tools for smart contract developers
148    #[command(subcommand)]
149    Contract(contract::Cmd),
150
151    /// Diagnose and troubleshoot CLI and network issues
152    Doctor(doctor::Cmd),
153
154    /// Watch the network for contract events
155    Events(events::Cmd),
156
157    /// Prints the environment variables
158    ///
159    /// Prints to stdout in a format that can be used as .env file. Environment
160    /// variables have precedence over defaults.
161    ///
162    /// By default, secret values are concealed. To display them, use `--reveal`.
163    ///
164    /// Pass a name to get the value of a single environment variable. Its value is printed without
165    /// shell quoting (control characters are neutralized), suitable for command substitution.
166    /// Concealed variables print nothing unless `--reveal` is passed.
167    ///
168    /// If there are no environment variables in use, prints the defaults.
169    Env(env::Cmd),
170
171    /// Create and manage identities including keys and addresses
172    #[command(subcommand)]
173    Keys(keys::Cmd),
174
175    /// Configure connection to networks
176    #[command(subcommand)]
177    Network(network::Cmd),
178
179    /// Start local networks in containers
180    #[command(subcommand)]
181    Container(container::Cmd),
182
183    /// Manage CLI configuration
184    #[command(subcommand)]
185    Config(cfg::Cmd),
186
187    /// Download a snapshot of a ledger from an archive.
188    #[command(subcommand)]
189    Snapshot(snapshot::Cmd),
190
191    /// Interact with SEP-41 tokens and Stellar Asset Contracts
192    #[command(subcommand)]
193    Token(token::Cmd),
194
195    /// Sign, Simulate, and Send transactions
196    #[command(subcommand)]
197    Tx(tx::Cmd),
198
199    /// Decode and encode XDR
200    Xdr(stellar_xdr::cli::Root),
201
202    /// Decode and encode strkey
203    Strkey(stellar_strkey::cli::Root),
204
205    /// Print shell completion code for the specified shell.
206    #[command(long_about = completion::LONG_ABOUT)]
207    Completion(completion::Cmd),
208
209    /// Cache for transactions and contract specs
210    #[command(subcommand)]
211    Cache(cache::Cmd),
212
213    /// Print version information
214    Version(version::Cmd),
215
216    /// The subcommand for CLI plugins
217    #[command(subcommand)]
218    Plugin(plugin::Cmd),
219
220    /// Fetch ledger information
221    #[command(subcommand)]
222    Ledger(ledger::Cmd),
223
224    /// Sign and verify arbitrary messages using SEP-53
225    #[command(subcommand)]
226    Message(message::Cmd),
227
228    /// ⚠️ Deprecated, use `fees stats` instead. Fetch network feestats
229    FeeStats(fee_stats::Cmd),
230
231    /// Fetch network feestats and configure CLI fee settings
232    #[command(subcommand)]
233    Fees(fees::Cmd),
234}
235
236#[derive(thiserror::Error, Debug)]
237pub enum Error {
238    // TODO: stop using Debug for displaying errors
239    #[error(transparent)]
240    Contract(#[from] contract::Error),
241
242    #[error(transparent)]
243    Doctor(#[from] doctor::Error),
244
245    #[error(transparent)]
246    Events(#[from] events::Error),
247
248    #[error(transparent)]
249    Keys(#[from] keys::Error),
250
251    #[error(transparent)]
252    Xdr(#[from] stellar_xdr::cli::Error),
253
254    #[error(transparent)]
255    Strkey(#[from] stellar_strkey::cli::Error),
256
257    #[error(transparent)]
258    Clap(#[from] clap::error::Error),
259
260    #[error(transparent)]
261    Plugin(#[from] plugin::Error),
262
263    #[error(transparent)]
264    PluginDefault(#[from] plugin::default::Error),
265
266    #[error(transparent)]
267    Network(#[from] network::Error),
268
269    #[error(transparent)]
270    Container(#[from] container::Error),
271
272    #[error(transparent)]
273    Config(#[from] cfg::Error),
274
275    #[error(transparent)]
276    Snapshot(#[from] snapshot::Error),
277
278    #[error(transparent)]
279    Token(#[from] token::Error),
280
281    #[error(transparent)]
282    Tx(#[from] tx::Error),
283
284    #[error(transparent)]
285    Cache(#[from] cache::Error),
286
287    #[error(transparent)]
288    Env(#[from] env::Error),
289
290    #[error(transparent)]
291    Ledger(#[from] ledger::Error),
292
293    #[error(transparent)]
294    Message(#[from] message::Error),
295
296    #[error(transparent)]
297    FeeStats(#[from] fee_stats::Error),
298
299    #[error(transparent)]
300    Fees(#[from] fees::Error),
301}