-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(cast wallet list
) issue #6958: Include HW wallets in cast wallet ls
#7123
Changes from 2 commits
e8a8bf5
981b966
c4ce461
38fc356
4ecb726
6474ca3
a9cd6ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,147 @@ | ||||||
use clap::Parser; | ||||||
use eyre::Result; | ||||||
|
||||||
use foundry_common::{fs, types::ToAlloy}; | ||||||
use foundry_config::Config; | ||||||
use foundry_wallets::{multi_wallet::MultiWalletOptsBuilder, WalletSigner}; | ||||||
|
||||||
/// CLI arguments for `cast wallet list`. | ||||||
#[derive(Clone, Debug, Parser)] | ||||||
pub struct ListArgs { | ||||||
/// List all the accounts in the keystore directory. | ||||||
/// Default keystore directory is used if no path provided. | ||||||
#[clap(long, default_missing_value = "", num_args(0..=1), help_heading = "List local accounts")] | ||||||
dir: Option<String>, | ||||||
|
||||||
/// List accounts from a Ledger hardware wallet. | ||||||
#[clap(long, short, help_heading = "List Ledger hardware wallet accounts")] | ||||||
ledger: bool, | ||||||
|
||||||
/// List accounts from a Trezor hardware wallet. | ||||||
#[clap(long, short, help_heading = "List Trezor hardware wallet accounts")] | ||||||
trezor: bool, | ||||||
|
||||||
/// List accounts from AWS KMS. | ||||||
#[clap(long, help_heading = "List AWS KMS account")] | ||||||
aws: bool, | ||||||
|
||||||
/// List all configured accounts. | ||||||
#[clap(long, help_heading = "List all accounts")] | ||||||
all: bool, | ||||||
} | ||||||
|
||||||
impl ListArgs { | ||||||
pub async fn run(self) -> Result<()> { | ||||||
// list local accounts as files in keystore dir, no need to unlock / provide password | ||||||
if self.dir.is_some() || self.all || (!self.ledger && !self.trezor && !self.aws) { | ||||||
self.list_local_senders().await?; | ||||||
} | ||||||
|
||||||
// Create options for multi wallet - ledger, trezor and AWS | ||||||
let list_opts = MultiWalletOptsBuilder::default() | ||||||
.ledger(self.ledger || self.all) | ||||||
.mnemonic_indexes(Some(vec![0])) | ||||||
.trezor(self.trezor || self.all) | ||||||
.aws(self.aws || self.all) | ||||||
.interactives(0) | ||||||
.build() | ||||||
.expect("build multi wallet"); | ||||||
|
||||||
// max number of senders to be shown for ledger and trezor signers | ||||||
let max_senders = 3; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should this be an argument? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yep, made this a wallet list argument, grouped with hardware wallets and all option 38fc356 |
||||||
|
||||||
// List ledger accounts | ||||||
match list_opts.ledgers().await { | ||||||
Ok(signers) => { | ||||||
self.list_senders(signers.unwrap_or_default(), max_senders, "Ledger").await? | ||||||
} | ||||||
Err(e) => { | ||||||
if !self.all { | ||||||
println!("{}", e) | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
// List Trezor accounts | ||||||
match list_opts.trezors().await { | ||||||
Ok(signers) => { | ||||||
self.list_senders(signers.unwrap_or_default(), max_senders, "Trezor").await? | ||||||
} | ||||||
Err(e) => { | ||||||
if !self.all { | ||||||
println!("{}", e) | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
// List AWS accounts | ||||||
match list_opts.aws_signers().await { | ||||||
Ok(signers) => { | ||||||
self.list_senders(signers.unwrap_or_default(), max_senders, "AWS").await? | ||||||
} | ||||||
Err(e) => { | ||||||
if !self.all { | ||||||
println!("{}", e) | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would be nice if we could reduce duplication here via macro/fn There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yep, good point, changed in c4ce461 |
||||||
Ok(()) | ||||||
} | ||||||
|
||||||
async fn list_local_senders(&self) -> Result<()> { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. doesn't need async There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updated in 38fc356 |
||||||
let keystore_path = self.dir.clone().unwrap_or_default(); | ||||||
let keystore_dir = if keystore_path.is_empty() { | ||||||
// Create the keystore default directory if it doesn't exist | ||||||
let default_dir = Config::foundry_keystores_dir().unwrap(); | ||||||
fs::create_dir_all(&default_dir)?; | ||||||
default_dir | ||||||
} else { | ||||||
dunce::canonicalize(keystore_path)? | ||||||
}; | ||||||
|
||||||
match std::fs::read_dir(keystore_dir) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove match, use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removed in 38fc356 |
||||||
Ok(entries) => { | ||||||
entries.flatten().for_each(|entry| { | ||||||
let path = entry.path(); | ||||||
if path.is_file() && path.extension().is_none() { | ||||||
if let Some(file_name) = path.file_name() { | ||||||
if let Some(name) = file_name.to_str() { | ||||||
println!("{} (Local)", name); | ||||||
} | ||||||
} | ||||||
} | ||||||
}); | ||||||
} | ||||||
Err(e) => { | ||||||
if !self.all { | ||||||
println!("{}", e) | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
Ok(()) | ||||||
} | ||||||
|
||||||
async fn list_senders( | ||||||
&self, | ||||||
signers: Vec<WalletSigner>, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
max_senders: usize, | ||||||
label: &str, | ||||||
) -> eyre::Result<()> { | ||||||
for signer in signers.iter() { | ||||||
match signer.available_senders(max_senders).await { | ||||||
Ok(senders) => { | ||||||
senders.iter().for_each(|sender| println!("{} ({})", sender.to_alloy(), label)); | ||||||
} | ||||||
Err(e) => { | ||||||
if !self.all { | ||||||
println!("{}", e) | ||||||
} | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
Ok(()) | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ foundry-common.workspace = true | |
|
||
async-trait = "0.1" | ||
clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } | ||
derive_builder = "0.20.0" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm personally okay with this any objections @DaniPopes ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's fine |
||
eyre.workspace = true | ||
hex = { workspace = true, features = ["serde"] } | ||
itertools.workspace = true | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove
help_heading
s, these don't need sections for each optionThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed in 38fc356