diff --git a/ethers-signers/src/wallet/mnemonic.rs b/ethers-signers/src/wallet/mnemonic.rs index b50a4b16..aa2ebffd 100644 --- a/ethers-signers/src/wallet/mnemonic.rs +++ b/ethers-signers/src/wallet/mnemonic.rs @@ -1,10 +1,14 @@ //! Specific helper functions for creating/loading a mnemonic private key following BIP-39 //! specifications -use crate::{wallet::util::key_to_address, Wallet, WalletError}; +use crate::{Wallet, WalletError}; use coins_bip32::path::DerivationPath; use coins_bip39::{Mnemonic, Wordlist}; -use ethers_core::{k256::ecdsa::SigningKey, types::PathOrString, utils::to_checksum}; +use ethers_core::{ + k256::ecdsa::SigningKey, + types::PathOrString, + utils::{secret_key_to_address, to_checksum}, +}; use rand::Rng; use std::{fs::File, io::Write, marker::PhantomData, path::PathBuf, str::FromStr}; use thiserror::Error; @@ -174,7 +178,7 @@ impl MnemonicBuilder { mnemonic.derive_key(&self.derivation_path, self.password.as_deref())?; let key: &SigningKey = derived_priv_key.as_ref(); let signer = SigningKey::from_bytes(&key.to_bytes())?; - let address = key_to_address(&signer); + let address = secret_key_to_address(&signer); Ok(Wallet:: { signer, diff --git a/ethers-signers/src/wallet/mod.rs b/ethers-signers/src/wallet/mod.rs index f34f5d01..1806f619 100644 --- a/ethers-signers/src/wallet/mod.rs +++ b/ethers-signers/src/wallet/mod.rs @@ -6,8 +6,6 @@ pub use mnemonic::{MnemonicBuilder, MnemonicBuilderError}; mod private_key; pub use private_key::WalletError; -mod util; - #[cfg(feature = "yubihsm")] mod yubi; diff --git a/ethers-signers/src/wallet/private_key.rs b/ethers-signers/src/wallet/private_key.rs index b676c8f7..8e124932 100644 --- a/ethers-signers/src/wallet/private_key.rs +++ b/ethers-signers/src/wallet/private_key.rs @@ -1,13 +1,14 @@ //! Specific helper functions for loading an offline K256 Private Key stored on disk use super::Wallet; -use crate::wallet::{mnemonic::MnemonicBuilderError, util::key_to_address}; +use crate::wallet::mnemonic::MnemonicBuilderError; use coins_bip32::Bip32Error; use coins_bip39::MnemonicError; use eth_keystore::KeystoreError; use ethers_core::{ k256::ecdsa::{self, SigningKey}, rand::{CryptoRng, Rng}, + utils::secret_key_to_address, }; use std::{path::Path, str::FromStr}; use thiserror::Error; @@ -60,7 +61,7 @@ impl Wallet { { let (secret, _) = eth_keystore::new(dir, rng, password)?; let signer = SigningKey::from_bytes(secret.as_slice())?; - let address = key_to_address(&signer); + let address = secret_key_to_address(&signer); Ok(Self { signer, address, @@ -76,7 +77,7 @@ impl Wallet { { let secret = eth_keystore::decrypt_key(keypath, password)?; let signer = SigningKey::from_bytes(secret.as_slice())?; - let address = key_to_address(&signer); + let address = secret_key_to_address(&signer); Ok(Self { signer, address, @@ -87,7 +88,7 @@ impl Wallet { /// Creates a new random keypair seeded with the provided RNG pub fn new(rng: &mut R) -> Self { let signer = SigningKey::random(rng); - let address = key_to_address(&signer); + let address = secret_key_to_address(&signer); Self { signer, address, @@ -106,7 +107,7 @@ impl PartialEq for Wallet { impl From for Wallet { fn from(signer: SigningKey) -> Self { - let address = key_to_address(&signer); + let address = secret_key_to_address(&signer); Self { signer, @@ -122,7 +123,7 @@ impl From for Wallet { fn from(key: K256SecretKey) -> Self { let signer = SigningKey::from_bytes(&*key.to_bytes()) .expect("private key should always be convertible to signing key"); - let address = key_to_address(&signer); + let address = secret_key_to_address(&signer); Self { signer, diff --git a/ethers-signers/src/wallet/util.rs b/ethers-signers/src/wallet/util.rs deleted file mode 100644 index dea317c8..00000000 --- a/ethers-signers/src/wallet/util.rs +++ /dev/null @@ -1,14 +0,0 @@ -use ethers_core::{ - k256::{ecdsa::SigningKey, EncodedPoint as K256PublicKey}, - types::Address, - utils::keccak256, -}; - -pub fn key_to_address(secret_key: &SigningKey) -> Address { - // TODO: Can we do this in a better way? - let uncompressed_pub_key = K256PublicKey::from(&secret_key.verify_key()).decompress(); - let public_key = uncompressed_pub_key.unwrap().to_bytes(); - debug_assert_eq!(public_key[0], 0x04); - let hash = keccak256(&public_key[1..]); - Address::from_slice(&hash[12..]) -} diff --git a/ethers/examples/mnemonic.rs b/ethers/examples/mnemonic.rs new file mode 100644 index 00000000..0685a165 --- /dev/null +++ b/ethers/examples/mnemonic.rs @@ -0,0 +1,32 @@ +use ethers::signers::{coins_bip39::English, MnemonicBuilder}; + +fn main() -> anyhow::Result<()> { + let phrase = "work man father plunge mystery proud hollow address reunion sauce theory bonus"; + let index = 0u32; + let password = "TREZOR123"; + + // Access mnemonic phrase with password + // Child key at derivation path: m/44'/60'/0'/0/{index} + let wallet = MnemonicBuilder::::default() + .phrase(phrase) + .index(index)? + // Use this if your mnemonic is encrypted + .password(password) + .build()?; + + dbg!(&wallet); + + // Generate a random wallet (24 word phrase) at custom derivation path + let mut rng = rand::thread_rng(); + let wallet = MnemonicBuilder::::default() + .word_count(24) + .derivation_path("m/44'/60'/0'/2/1")? + // Optionally add this if you want the generated mnemonic to be written + // to a file + // .write_to(path) + .build_random(&mut rng)?; + + dbg!(&wallet); + + Ok(()) +}