1use clap::Parser;
2use eyre::Result;
3use std::env;
4
5use foundry_common::{fs, sh_err, sh_println};
6use foundry_config::Config;
7use foundry_wallets::multi_wallet::MultiWalletOptsBuilder;
8
9#[derive(Clone, Debug, Parser)]
11pub struct ListArgs {
12 #[arg(long, default_missing_value = "", num_args(0..=1))]
15 dir: Option<String>,
16
17 #[arg(long, short, group = "hw-wallets")]
19 ledger: bool,
20
21 #[arg(long, short, group = "hw-wallets")]
23 trezor: bool,
24
25 #[arg(long, hide = !cfg!(feature = "aws-kms"))]
30 aws: bool,
31
32 #[arg(long, hide = !cfg!(feature = "gcp-kms"))]
39 gcp: bool,
40
41 #[arg(long, group = "hw-wallets")]
43 all: bool,
44
45 #[arg(long, short, default_value = "3", requires = "hw-wallets")]
47 max_senders: Option<usize>,
48}
49
50impl ListArgs {
51 pub async fn run(self) -> Result<()> {
52 if self.dir.is_some()
54 || self.all
55 || (!self.ledger && !self.trezor && !self.aws && !self.gcp)
56 {
57 let _ = self.list_local_senders();
58 }
59
60 let list_opts = MultiWalletOptsBuilder::default()
62 .ledger(self.ledger || self.all)
63 .mnemonic_indexes(Some(vec![0]))
64 .trezor(self.trezor || self.all)
65 .aws(self.aws || self.all)
66 .gcp(self.gcp || (self.all && gcp_env_vars_set()))
67 .interactives(0)
68 .build()
69 .expect("build multi wallet");
70
71 macro_rules! list_senders {
73 ($signers:expr, $label:literal) => {
74 match $signers.await {
75 Ok(signers) => {
76 for signer in signers.unwrap_or_default().iter() {
77 signer
78 .available_senders(self.max_senders.unwrap())
79 .await?
80 .iter()
81 .for_each(|sender| {
82 let _ = sh_println!("{} ({})", sender, $label);
83 })
84 }
85 }
86 Err(e) => {
87 if !self.all {
88 sh_err!("{}", e)?;
89 }
90 }
91 }
92 };
93 }
94
95 list_senders!(list_opts.ledgers(), "Ledger");
96 list_senders!(list_opts.trezors(), "Trezor");
97 list_senders!(list_opts.aws_signers(), "AWS");
98 list_senders!(list_opts.gcp_signers(), "GCP");
99
100 Ok(())
101 }
102
103 fn list_local_senders(&self) -> Result<()> {
104 let keystore_path = self.dir.clone().unwrap_or_default();
105 let keystore_dir = if keystore_path.is_empty() {
106 let default_dir = Config::foundry_keystores_dir().unwrap();
108 fs::create_dir_all(&default_dir)?;
109 default_dir
110 } else {
111 dunce::canonicalize(keystore_path)?
112 };
113
114 for entry in std::fs::read_dir(keystore_dir)? {
116 let path = entry?.path();
117 if path.is_file()
118 && let Some(file_name) = path.file_name()
119 && let Some(name) = file_name.to_str()
120 {
121 sh_println!("{name} (Local)")?;
122 }
123 }
124
125 Ok(())
126 }
127}
128
129fn gcp_env_vars_set() -> bool {
130 let required_vars =
131 ["GCP_PROJECT_ID", "GCP_LOCATION", "GCP_KEY_RING", "GCP_KEY_NAME", "GCP_KEY_VERSION"];
132
133 required_vars.iter().all(|&var| env::var(var).is_ok())
134}