cw_orch_cli/
common.rs

1use crate::{fetch::explorers::Explorers, types::CliLockedChain};
2pub use base64::prelude::BASE64_STANDARD as B64;
3use cw_orch::{
4    daemon::networks::SUPPORTED_NETWORKS as NETWORKS,
5    environment::{ChainInfo, ChainKind},
6};
7use ibc_chain_registry::fetchable::Fetchable;
8use inquire::{error::InquireResult, InquireError, Select};
9
10pub fn get_cw_cli_exec_path() -> String {
11    std::env::args().next().unwrap()
12}
13
14pub fn select_chain() -> color_eyre::eyre::Result<Option<CliLockedChain>> {
15    let chain_ids: Vec<_> = NETWORKS
16        .iter()
17        .map(|network| {
18            format!(
19                "{} {}({})",
20                network.network_info.chain_name.to_uppercase(),
21                network.kind.to_string().to_uppercase(),
22                network.chain_id
23            )
24        })
25        .collect();
26    let selected = Select::new("Select chain", chain_ids).raw_prompt()?;
27    let locked_chain = CliLockedChain::new(selected.index);
28    Ok(Some(locked_chain))
29}
30
31pub fn select_signer() -> color_eyre::eyre::Result<Option<String>> {
32    let entries_set_result = crate::types::keys::read_entries();
33    let signer_id = match entries_set_result {
34        // We have a file access and it has at least one signer
35        Ok(entries_set) if !entries_set.entries.is_empty() => {
36            let options = entries_set.entries.into_iter().collect();
37            Select::new("Select signer id", options)
38                .with_help_message("Use CLI mode to add signer from previous version")
39                .prompt()?
40        }
41        // We don't have access or it's empty
42        _ => inquire::Text::new("Signer id").prompt()?,
43    };
44    Ok(Some(signer_id))
45}
46
47pub fn parse_coins() -> InquireResult<cosmwasm_std::Coins> {
48    let mut coins = cosmwasm_std::Coins::default();
49    loop {
50        let coin = inquire::Text::new("Add coin to transaction")
51            .with_help_message("Leave empty to stop adding coins")
52            .with_placeholder("0ucoin")
53            .prompt()?;
54        if !coin.is_empty() {
55            match coin.parse() {
56                Ok(c) => coins
57                    .add(c)
58                    .map_err(|e| InquireError::Custom(Box::new(e)))?,
59                Err(e) => {
60                    println!("Failed to add coin: {e}")
61                }
62            }
63        } else {
64            break;
65        }
66    }
67    println!("attached coins: {coins}");
68    Ok(coins)
69}
70
71#[derive(Clone, Copy, strum::EnumIter, strum::EnumString, derive_more::Display)]
72pub enum ExpirationType {
73    AtHeight,
74    AtTime,
75    Never,
76}
77
78impl ExpirationType {
79    const VARIANTS: &'static [ExpirationType] = &[Self::AtHeight, Self::AtTime, Self::Never];
80}
81
82pub fn parse_expiration() -> InquireResult<cw_utils::Expiration> {
83    let locked = inquire::Select::new("Choose expiration type", ExpirationType::VARIANTS.to_vec())
84        .prompt()?;
85
86    let expiration = match locked {
87        ExpirationType::AtHeight => {
88            let block_height = inquire::CustomType::<u64>::new("Input block height").prompt()?;
89            cw_utils::Expiration::AtHeight(block_height)
90        }
91        ExpirationType::AtTime => {
92            let timestamp_nanos =
93                inquire::CustomType::<u64>::new("Input timestamp in nanos").prompt()?;
94            let timestamp = cosmwasm_std::Timestamp::from_nanos(timestamp_nanos);
95            cw_utils::Expiration::AtTime(timestamp)
96        }
97        ExpirationType::Never => cw_utils::Expiration::Never {},
98    };
99    Ok(expiration)
100}
101
102pub async fn show_addr_explorer(chain_info: ChainInfo, addr: &str) -> color_eyre::eyre::Result<()> {
103    if let ChainKind::Mainnet = chain_info.kind {
104        let Explorers { explorers } =
105            Explorers::fetch(chain_info.network_info.chain_name.to_owned(), None).await?;
106        for explorer in explorers {
107            if let Some(tx_page) = explorer.account_page {
108                let url = tx_page.replace("${accountAddress}", addr);
109                println!("Explorer: {url}");
110                break;
111            }
112        }
113    }
114    Ok(())
115}