hermes_cli/commands/keys/
balance.rs

1use core::fmt::Write;
2
3use hermes_cli_components::traits::build::CanLoadBuilder;
4use hermes_cli_framework::command::CommandRunner;
5use hermes_cli_framework::output::{json, Output};
6use ibc_relayer::chain::handle::ChainHandle;
7use ibc_relayer::config::ChainConfig;
8use ibc_relayer_types::core::ics24_host::identifier::ChainId;
9use oneline_eyre::eyre::eyre;
10
11use crate::contexts::app::HermesApp;
12
13/// The data structure that represents the arguments when invoking the `keys balance` CLI command.
14///
15/// The command has one argument and one optional flag:
16///
17/// `keys balance --chain <chain_id> --key-name <KEY_NAME>`
18///
19/// If no key name is given, it will be taken from the configuration file.
20/// If successful the balance and denominator of the account, associated with the key name
21/// on the given chain, will be displayed.
22#[derive(Debug, clap::Parser)]
23pub struct KeysBalanceCmd {
24    #[clap(
25        long = "chain",
26        required = true,
27        value_name = "CHAIN_ID",
28        help_heading = "REQUIRED",
29        help = "Identifier of the chain"
30    )]
31    chain_id: ChainId,
32
33    #[clap(
34        long = "key-name",
35        value_name = "KEY_NAME",
36        help = "(optional) name of the key (defaults to the `key_name` defined in the config)"
37    )]
38    key_name: Option<String>,
39
40    #[clap(
41        long = "denom",
42        value_name = "DENOM",
43        help = "(optional) query the balance for the given denom (defaults to the `denom` defined in the config for the gas price)"
44    )]
45    denom: Option<String>,
46
47    #[clap(
48        long = "all",
49        help = "(optional) query the balance for all denom. This flag overwrites the `--denom` flag (defaults to false)"
50    )]
51    all: bool,
52}
53
54impl CommandRunner<HermesApp> for KeysBalanceCmd {
55    async fn run(&self, app: &HermesApp) -> hermes_cli_framework::Result<Output> {
56        let builder = app.load_builder().await?;
57
58        let chain = builder.build_chain(&self.chain_id).await?;
59        let key_name = self.key_name.clone();
60
61        if self.all {
62            match get_balances(chain.handle.clone(), key_name) {
63                Ok(success_msg) => success_msg.exit(),
64                Err(e) => Output::error(format!("`keys balance` command failed: {}", e)).exit(),
65            }
66        } else {
67            match get_balance(chain.handle.clone(), key_name, self.denom.clone()) {
68                Ok(success_msg) => success_msg.exit(),
69                Err(e) => Output::error(format!("`keys balance` command failed: {}", e)).exit(),
70            }
71        }
72    }
73}
74
75fn get_balance(
76    chain: impl ChainHandle,
77    key_name: Option<String>,
78    denom: Option<String>,
79) -> eyre::Result<Output> {
80    match chain.query_balance(key_name.clone(), denom) {
81        Ok(balance) if json() => Ok(Output::success(balance)),
82        Ok(balance) => {
83            // Retrieve the key name string to output
84            let key_name = key_name.unwrap_or_else(|| {
85                let chain_config = chain.config().expect(
86                    "`keys balance` command failed due to an error retrieving chain config",
87                );
88
89                let ChainConfig::CosmosSdk(cosmos_config) = chain_config;
90
91                cosmos_config.key_name
92            });
93
94            Ok(Output::success_msg(format!(
95                "balance for key `{key_name}`: {} {}",
96                balance.amount, balance.denom
97            )))
98        }
99        Err(e) => Err(e.1.wrap_err("`keys balance` command failed")),
100    }
101}
102
103fn get_balances(chain: impl ChainHandle, key_name: Option<String>) -> eyre::Result<Output> {
104    match chain.query_all_balances(key_name.clone()) {
105        Ok(balances) if json() => Ok(Output::success(balances)),
106        Ok(balances) => {
107            // Retrieve the key name string to output.
108            let key_name = key_name.unwrap_or_else(|| {
109                let chain_config = chain.config().expect(
110                    "`keys balance` command failed due to an error retrieving chain config",
111                );
112
113                let ChainConfig::CosmosSdk(cosmos_config) = chain_config;
114
115                cosmos_config.key_name
116            });
117
118            let mut pretty_output = format!("Balances for key `{key_name}`:");
119
120            for balance in balances {
121                write!(pretty_output, "\n\t{} {}", balance.amount, balance.denom)
122                    .map_err(|_| eyre!("failed to write balance output"))?;
123            }
124
125            Ok(Output::success_msg(pretty_output))
126        }
127        Err(e) => Err(e
128            .1
129            .wrap_err("`keys balance` command failed due to a problem querying the balance")),
130    }
131}