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