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::wallet_multi::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, hide = !cfg!(feature = "turnkey"))]
43 turnkey: bool,
44
45 #[arg(long, group = "hw-wallets")]
47 all: bool,
48
49 #[arg(long, short, default_value = "3", requires = "hw-wallets")]
51 max_senders: Option<usize>,
52}
53
54impl ListArgs {
55 pub async fn run(self) -> Result<()> {
56 if self.dir.is_some()
58 || self.all
59 || (!self.ledger && !self.trezor && !self.aws && !self.gcp)
60 {
61 match self.list_local_senders() {
62 Ok(()) => {}
63 Err(e) => {
64 if !self.all {
65 sh_err!("{}", e)?;
66 }
67 }
68 }
69 }
70
71 let list_opts = MultiWalletOptsBuilder::default()
73 .ledger(self.ledger || self.all)
74 .mnemonic_indexes(Some(vec![0]))
75 .trezor(self.trezor || self.all)
76 .aws(self.aws || self.all)
77 .gcp(self.gcp || (self.all && gcp_env_vars_set()))
78 .turnkey(self.turnkey || self.all)
79 .interactives(0)
80 .interactive(false)
81 .browser(false)
82 .browser_port(Default::default())
83 .browser_disable_open(Default::default())
84 .browser_development(Default::default())
85 .build()
86 .expect("build multi wallet");
87
88 macro_rules! list_senders {
90 ($signers:expr, $label:literal) => {
91 match $signers.await {
92 Ok(signers) => {
93 for signer in signers.unwrap_or_default().iter() {
94 signer
95 .available_senders(self.max_senders.unwrap())
96 .await?
97 .iter()
98 .for_each(|sender| {
99 let _ = sh_println!("{} ({})", sender, $label);
100 })
101 }
102 }
103 Err(e) => {
104 if !self.all {
105 sh_err!("{}", e)?;
106 }
107 }
108 }
109 };
110 }
111
112 list_senders!(list_opts.ledgers(), "Ledger");
113 list_senders!(list_opts.trezors(), "Trezor");
114 list_senders!(list_opts.aws_signers(), "AWS");
115 list_senders!(list_opts.gcp_signers(), "GCP");
116
117 Ok(())
118 }
119
120 fn list_local_senders(&self) -> Result<()> {
121 let keystore_path = self.dir.as_deref().unwrap_or_default();
122 let keystore_dir = if keystore_path.is_empty() {
123 let default_dir = Config::foundry_keystores_dir().unwrap();
125 fs::create_dir_all(&default_dir)?;
126 default_dir
127 } else {
128 dunce::canonicalize(keystore_path)?
129 };
130
131 for entry in std::fs::read_dir(keystore_dir)? {
133 let path = entry?.path();
134 if path.is_file()
135 && let Some(file_name) = path.file_name()
136 && let Some(name) = file_name.to_str()
137 {
138 if let Some(address) = name.split("--").last() {
140 sh_println!("0x{} (Local)", address)?;
141 }
142 }
143 }
144
145 Ok(())
146 }
147}
148
149fn gcp_env_vars_set() -> bool {
150 let required_vars =
151 ["GCP_PROJECT_ID", "GCP_LOCATION", "GCP_KEY_RING", "GCP_KEY_NAME", "GCP_KEY_VERSION"];
152
153 required_vars.iter().all(|&var| env::var(var).is_ok())
154}