diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index f44fa1093e..a73314b239 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -1228,6 +1228,7 @@ Configure connection to networks * `ls` — List networks * `use` — Set the default network that will be used on all commands. This allows you to skip `--network` or setting a environment variable, while reusing this value in all commands that require it * `health` — Fetch the health of the configured RPC +* `info` — Checks the health of the configured RPC * `settings` — Fetch the network's config settings @@ -1329,6 +1330,35 @@ Fetch the health of the configured RPC +## `stellar network info` + +Checks the health of the configured RPC + +**Usage:** `stellar network info [OPTIONS]` + +###### **Options:** + +* `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider +* `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +* `-n`, `--network ` — Name of network to use from config +* `--global` — ⚠️ Deprecated: global config is always on +* `--config-dir ` — Location of config directory. By default, it uses `$XDG_CONFIG_HOME/stellar` if set, falling back to `~/.config/stellar` otherwise. Contains configuration files, aliases, and other persistent settings +* `--output ` — Format of the output + + Default value: `text` + + Possible values: + - `text`: + Text output of network info + - `json`: + JSON result of the RPC request + - `json-formatted`: + Formatted (multiline) JSON output of the RPC request + + + + ## `stellar network settings` Fetch the network's config settings diff --git a/cmd/soroban-cli/src/commands/network/info.rs b/cmd/soroban-cli/src/commands/network/info.rs new file mode 100644 index 0000000000..4a14bd9d0b --- /dev/null +++ b/cmd/soroban-cli/src/commands/network/info.rs @@ -0,0 +1,103 @@ +use crate::commands::global; +use crate::config::network; +use crate::{config, print, rpc}; +use clap::command; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Config(#[from] config::Error), + #[error(transparent)] + Network(#[from] network::Error), + #[error(transparent)] + Serde(#[from] serde_json::Error), + #[error(transparent)] + Rpc(#[from] rpc::Error), +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, clap::ValueEnum, Default)] +pub enum OutputFormat { + /// Text output of network info + #[default] + Text, + /// JSON result of the RPC request + Json, + /// Formatted (multiline) JSON output of the RPC request + JsonFormatted, +} + +#[derive(Debug, clap::Parser, Clone)] +#[group(skip)] +pub struct Cmd { + #[command(flatten)] + pub config: config::ArgsLocatorAndNetwork, + /// Format of the output + #[arg(long, default_value = "text")] + pub output: OutputFormat, +} + +#[derive(serde::Deserialize, serde::Serialize)] +struct Info { + pub version: String, + pub commit_hash: String, + pub build_timestamp: String, + pub captive_core_version: String, + pub protocol_version: u32, + pub passphrase: String, + pub friendbot_url: Option, +} + +impl Info { + fn print_text(&self, print: &print::Print) { + print.infoln(format!("Version: {}", self.version)); + print.infoln(format!("Commit Hash: {}", self.commit_hash)); + print.infoln(format!("Build Timestamp: {}", self.build_timestamp)); + print.infoln(format!( + "Captive Core Version: {}", + self.captive_core_version + )); + print.infoln(format!("Protocol Version: {}", self.protocol_version)); + print.infoln(format!("Passphrase: {}", self.passphrase)); + if let Some(friendbot_url) = &self.friendbot_url { + print.infoln(format!("Friendbot Url: {friendbot_url}")); + } + } + + fn print_json(&self) -> Result<(), serde_json::Error> { + let json = serde_json::to_string(&self)?; + println!("{json}"); + Ok(()) + } + + fn print_json_formatted(&self) -> Result<(), serde_json::Error> { + let json = serde_json::to_string_pretty(&self)?; + println!("{json}"); + Ok(()) + } +} + +impl Cmd { + pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { + let print = print::Print::new(global_args.quiet); + let rpc_client = self.config.get_network()?.rpc_client()?; + let network_result = rpc_client.get_network().await?; + let version_result = rpc_client.get_version_info().await?; + let info = Info { + version: version_result.version, + commit_hash: version_result.commmit_hash, + build_timestamp: version_result.build_timestamp, + captive_core_version: version_result.captive_core_version, + protocol_version: network_result.protocol_version, + friendbot_url: network_result.friendbot_url, + passphrase: network_result.passphrase, + }; + + match self.output { + OutputFormat::Text => info.print_text(&print), + OutputFormat::Json => info.print_json()?, + OutputFormat::JsonFormatted => info.print_json_formatted()?, + } + + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/network/mod.rs b/cmd/soroban-cli/src/commands/network/mod.rs index 8106c47b3f..3e0d41b1fe 100644 --- a/cmd/soroban-cli/src/commands/network/mod.rs +++ b/cmd/soroban-cli/src/commands/network/mod.rs @@ -4,6 +4,7 @@ use clap::Parser; pub mod add; pub mod default; pub mod health; +pub mod info; pub mod ls; pub mod rm; pub mod settings; @@ -55,6 +56,9 @@ pub enum Cmd { /// Fetch the health of the configured RPC Health(health::Cmd), + /// Checks the health of the configured RPC + Info(info::Cmd), + /// Fetch the network's config settings Settings(settings::Cmd), } @@ -76,6 +80,9 @@ pub enum Error { #[error(transparent)] Health(#[from] health::Error), + #[error(transparent)] + Info(#[from] info::Error), + #[error(transparent)] Settings(#[from] settings::Error), @@ -115,6 +122,7 @@ impl Cmd { cmd.run(global_args).await?; } Cmd::Health(cmd) => cmd.run(global_args).await?, + Cmd::Info(cmd) => cmd.run(global_args).await?, Cmd::Settings(cmd) => cmd.run(global_args).await?, } Ok(()) diff --git a/cmd/soroban-cli/src/commands/tx/fetch/fee-bump-soroban-tx.json b/cmd/soroban-cli/src/commands/tx/fetch/fee-bump-soroban-tx.json new file mode 100644 index 0000000000..0a433bad6c --- /dev/null +++ b/cmd/soroban-cli/src/commands/tx/fetch/fee-bump-soroban-tx.json @@ -0,0 +1,57 @@ +{ + "envelope": { + "tx_fee_bump": { + "tx": { + "fee_source": "GDJLH2F7DBI6GC22J7YUTPAEFRSWKG5MN5RSE2GOOYUTO4BH66LHENRW", + "fee": "10208876", + "inner_tx": { + "tx": { + "tx": { + "source_account": "GBQUFZ3QRIP6VQ74BV6KJGBEJ7YFE4WGRCB4YCMGTXFEMYLXNI2CC2AK", + "fee": 5004538, + "ext": { + "v1": { + "ext": "v0", + "resource_fee": "5004438" + } + } + } + } + }, + "ext": "v0" + } + } + }, + // + // estimated / max fee (tx.envelope.tx_fee_bump.fee) = 10208876 + // estimated / max resource fee (tx.envelope.tx_fee_bump.tx.inner_tx.tx.tx.ext.v1.resource_fee) + // max inclusino fee = outer fee - outer resource [0] - (inner resource + inner inclusion) + "result": { + "fee_charged": "3603030", + "result": { + "tx_fee_bump_inner_success": { + "transaction_hash": "0d2bdcf1532b215a81730267d6a7cd444127b19bdb435a568543890951a95d78", + "result": { + "fee_charged": "3602930", + "ext": "v0" + } + } + }, + "ext": "v0" + }, + "result_meta": { + "v3": { + "ext": "v0", + "soroban_meta": { + "ext": { + "v1": { + "ext": "v0", + "total_non_refundable_resource_fee_charged": "285226", + "total_refundable_resource_fee_charged": "3317604", + "rent_fee_charged": "3312096" + } + } + } + } + } +} \ No newline at end of file diff --git a/cmd/soroban-cli/src/commands/tx/fetch/soroban-tx.json b/cmd/soroban-cli/src/commands/tx/fetch/soroban-tx.json new file mode 100644 index 0000000000..a17dc60e49 --- /dev/null +++ b/cmd/soroban-cli/src/commands/tx/fetch/soroban-tx.json @@ -0,0 +1,35 @@ +{ + "envelope": { + "tx": { + "tx": { + "source_account": "GDWREJ5HETNIDTQKXJZPA6LRSJMFUCO4T2DFEJYSZ2XVWRTMUG64AL4B", + "fee": 105447, + "ext": { + "v1": { + "ext": "v0", + "resource_fee": "105347" + } + } + } + } + }, + "result": { + "fee_charged": "60537", + "ext": "v0" + }, + "result_meta": { + "v3": { + "ext": "v0", + "soroban_meta": { + "ext": { + "v1": { + "ext": "v0", + "total_non_refundable_resource_fee_charged": "60358", + "total_refundable_resource_fee_charged": "79", + "rent_fee_charged": "0" + } + } + } + } + }, +} \ No newline at end of file