cast/cmd/wallet/
list.rs

1use clap::Parser;
2use eyre::Result;
3
4use foundry_common::{fs, sh_err, sh_println};
5use foundry_config::Config;
6use foundry_wallets::multi_wallet::MultiWalletOptsBuilder;
7
8/// CLI arguments for `cast wallet list`.
9#[derive(Clone, Debug, Parser)]
10pub struct ListArgs {
11    /// List all the accounts in the keystore directory.
12    /// Default keystore directory is used if no path provided.
13    #[arg(long, default_missing_value = "", num_args(0..=1))]
14    dir: Option<String>,
15
16    /// List accounts from a Ledger hardware wallet.
17    #[arg(long, short, group = "hw-wallets")]
18    ledger: bool,
19
20    /// List accounts from a Trezor hardware wallet.
21    #[arg(long, short, group = "hw-wallets")]
22    trezor: bool,
23
24    /// List accounts from AWS KMS.
25    #[arg(long, hide = !cfg!(feature = "aws-kms"))]
26    aws: bool,
27
28    /// List all configured accounts.
29    #[arg(long, group = "hw-wallets")]
30    all: bool,
31
32    /// Max number of addresses to display from hardware wallets.
33    #[arg(long, short, default_value = "3", requires = "hw-wallets")]
34    max_senders: Option<usize>,
35}
36
37impl ListArgs {
38    pub async fn run(self) -> Result<()> {
39        // list local accounts as files in keystore dir, no need to unlock / provide password
40        if self.dir.is_some() || self.all || (!self.ledger && !self.trezor && !self.aws) {
41            let _ = self.list_local_senders();
42        }
43
44        // Create options for multi wallet - ledger, trezor and AWS
45        let list_opts = MultiWalletOptsBuilder::default()
46            .ledger(self.ledger || self.all)
47            .mnemonic_indexes(Some(vec![0]))
48            .trezor(self.trezor || self.all)
49            .aws(self.aws || self.all)
50            .interactives(0)
51            .build()
52            .expect("build multi wallet");
53
54        // macro to print senders for a list of signers
55        macro_rules! list_senders {
56            ($signers:expr, $label:literal) => {
57                match $signers.await {
58                    Ok(signers) => {
59                        for signer in signers.unwrap_or_default().iter() {
60                            signer
61                                .available_senders(self.max_senders.unwrap())
62                                .await?
63                                .iter()
64                                .for_each(|sender| {
65                                    let _ = sh_println!("{} ({})", sender, $label);
66                                })
67                        }
68                    }
69                    Err(e) => {
70                        if !self.all {
71                            sh_err!("{}", e)?;
72                        }
73                    }
74                }
75            };
76        }
77
78        list_senders!(list_opts.ledgers(), "Ledger");
79        list_senders!(list_opts.trezors(), "Trezor");
80        list_senders!(list_opts.aws_signers(), "AWS");
81
82        Ok(())
83    }
84
85    fn list_local_senders(&self) -> Result<()> {
86        let keystore_path = self.dir.clone().unwrap_or_default();
87        let keystore_dir = if keystore_path.is_empty() {
88            // Create the keystore default directory if it doesn't exist
89            let default_dir = Config::foundry_keystores_dir().unwrap();
90            fs::create_dir_all(&default_dir)?;
91            default_dir
92        } else {
93            dunce::canonicalize(keystore_path)?
94        };
95
96        // List all files within the keystore directory.
97        for entry in std::fs::read_dir(keystore_dir)? {
98            let path = entry?.path();
99            if path.is_file() {
100                if let Some(file_name) = path.file_name() {
101                    if let Some(name) = file_name.to_str() {
102                        sh_println!("{name} (Local)")?;
103                    }
104                }
105            }
106        }
107
108        Ok(())
109    }
110}