soroban_cli/commands/
mod.rs

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