Skip to content
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(anvil/cast): mnemonic generation #6066

Merged
merged 6 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/anvil/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ thiserror = "1"
yansi = "0.5"
tempfile = "3"
itertools.workspace = true
rand = "0.8"

# cli
clap = { version = "4", features = ["derive", "env", "wrap_help"], optional = true }
Expand Down
35 changes: 33 additions & 2 deletions crates/anvil/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ use crate::{
use anvil_server::ServerConfig;
use clap::Parser;
use core::fmt;
use ethers::utils::WEI_IN_ETHER;
use ethers::{
signers::coins_bip39::{English, Mnemonic},
utils::WEI_IN_ETHER,
};
use foundry_config::{Chain, Config};
use futures::FutureExt;
use rand::{rngs::StdRng, SeedableRng};
use std::{
future::Future,
net::IpAddr,
Expand Down Expand Up @@ -45,9 +49,25 @@ pub struct NodeArgs {
pub timestamp: Option<u64>,

/// BIP39 mnemonic phrase used for generating accounts.
#[clap(long, short)]
/// Cannot be used if `mnemonic_random` or `mnemonic_seed` are used
#[clap(long, short, conflicts_with_all = &["mnemonic_seed", "mnemonic_random"])]
pub mnemonic: Option<String>,

/// Automatically generates a BIP39 mnemonic phrase, and derives accounts from it.
/// Cannot be used with other `mnemonic` options
/// You can specify the number of words you want in the mnemonic.
/// [default: 12]
#[clap(long, conflicts_with_all = &["mnemonic", "mnemonic_seed"], default_missing_value = "12", num_args(0..=1))]
pub mnemonic_random: Option<usize>,

/// Generates a BIP39 mnemonic phrase from a given seed
/// Cannot be used with other `mnemonic` options
///
/// CAREFUL: this is NOT SAFE and should only be used for testing.
/// Never use the private keys generated in production.
#[clap(long = "mnemonic-seed-unsafe", conflicts_with_all = &["mnemonic", "mnemonic_random"])]
pub mnemonic_seed: Option<u64>,

/// Sets the derivation path of the child key to be derived.
///
/// [default: m/44'/60'/0'/0/]
Expand Down Expand Up @@ -218,6 +238,17 @@ impl NodeArgs {
.chain_id(self.evm_opts.chain_id.unwrap_or_else(|| CHAIN_ID.into()));
if let Some(ref mnemonic) = self.mnemonic {
gen = gen.phrase(mnemonic);
} else if let Some(count) = self.mnemonic_random {
let mut rng = rand::thread_rng();
let mnemonic = match Mnemonic::<English>::new_with_count(&mut rng, count) {
Ok(mnemonic) => mnemonic.to_phrase(),
Err(_) => DEFAULT_MNEMONIC.to_string(),
};
gen = gen.phrase(mnemonic);
} else if let Some(seed) = self.mnemonic_seed {
let mut seed = StdRng::seed_from_u64(seed);
let mnemonic = Mnemonic::<English>::new(&mut seed).to_phrase();
gen = gen.phrase(mnemonic);
}
if let Some(ref derivation) = self.derivation_path {
gen = gen.derivation_path(derivation);
Expand Down
39 changes: 38 additions & 1 deletion crates/cast/bin/cmd/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ use alloy_primitives::Address;
use clap::Parser;
use ethers::{
core::rand::thread_rng,
signers::{LocalWallet, Signer},
signers::{
coins_bip39::{English, Mnemonic},
LocalWallet, MnemonicBuilder, Signer,
},
types::{transaction::eip712::TypedData, Signature},
};
use ethers_core::utils::to_checksum;
use eyre::{Context, Result};
use foundry_cli::opts::{RawWallet, Wallet};
use foundry_common::fs;
Expand Down Expand Up @@ -38,6 +42,18 @@ pub enum WalletSubcommands {
unsafe_password: Option<String>,
},

/// Generates a random BIP39 mnemonic phrase
#[clap(visible_alias = "nm")]
NewMnemonic {
/// Number of words for the mnemonic
#[clap(long, short, default_value = "12")]
words: usize,

/// Number of accounts to display
#[clap(long, short, default_value = "1")]
accounts: u8,
},

/// Generate a vanity address.
#[clap(visible_alias = "va")]
Vanity(VanityArgs),
Expand Down Expand Up @@ -149,6 +165,27 @@ impl WalletSubcommands {
println!("Private key: 0x{}", hex::encode(wallet.signer().to_bytes()));
}
}
WalletSubcommands::NewMnemonic { words, accounts } => {
let mut rng = thread_rng();
let phrase = Mnemonic::<English>::new_with_count(&mut rng, words)?.to_phrase();

let builder = MnemonicBuilder::<English>::default().phrase(phrase.as_str());
let derivation_path = "m/44'/60'/0'/0/";
let wallets = (0..accounts)
.map(|i| builder.clone().derivation_path(&format!("{derivation_path}{i}")))
.collect::<Result<Vec<_>, _>>()?;
let wallets =
wallets.into_iter().map(|b| b.build()).collect::<Result<Vec<_>, _>>()?;

println!("{}", Paint::green("Successfully generated a new mnemonic."));
println!("Phrase:\n{phrase}");
println!("\nAccounts:");
for (i, wallet) in wallets.iter().enumerate() {
println!("- Account {i}:");
println!("Address: {}", to_checksum(&wallet.address(), None));
println!("Private key: 0x{}\n", hex::encode(wallet.signer().to_bytes()));
}
}
WalletSubcommands::Vanity(cmd) => {
cmd.run()?;
}
Expand Down