Skip to main content

foundry_common/
wallet.rs

1use alloy_primitives::U256;
2use alloy_signer_local::{
3    MnemonicBuilder, PrivateKeySigner,
4    coins_bip39::{
5        ChineseSimplified, ChineseTraditional, Czech, English, French, Italian, Japanese, Korean,
6        Portuguese, Spanish, Wordlist,
7    },
8};
9
10/// Appends `index` to `path`, inserting a `/` separator when needed.
11pub fn derive_key_path(path: &str, index: u32) -> String {
12    let mut out = path.to_string();
13    if !out.ends_with('/') {
14        out.push('/');
15    }
16    out.push_str(&index.to_string());
17    out
18}
19
20/// Derives a private key from a BIP-39 mnemonic using the given BIP-32 path and index.
21pub fn derive_private_key<W: Wordlist>(
22    mnemonic: &str,
23    path: &str,
24    index: u32,
25) -> Result<U256, String> {
26    let wallet = MnemonicBuilder::<W>::default()
27        .phrase(mnemonic)
28        .derivation_path(derive_key_path(path, index))
29        .map_err(|e| e.to_string())?
30        .build()
31        .map_err(|e| e.to_string())?;
32    Ok(U256::from_be_bytes(wallet.credential().to_bytes().into()))
33}
34
35/// Derives a private key from a BIP-39 mnemonic, selecting the wordlist by name.
36///
37/// Recognised language names: `chinese_simplified`, `chinese_traditional`, `czech`, `english`,
38/// `french`, `italian`, `japanese`, `korean`, `portuguese`, `spanish`.
39pub fn derive_private_key_with_language(
40    mnemonic: &str,
41    path: &str,
42    index: u32,
43    language: &str,
44) -> Result<U256, String> {
45    match language {
46        "chinese_simplified" => derive_private_key::<ChineseSimplified>(mnemonic, path, index),
47        "chinese_traditional" => derive_private_key::<ChineseTraditional>(mnemonic, path, index),
48        "czech" => derive_private_key::<Czech>(mnemonic, path, index),
49        "english" => derive_private_key::<English>(mnemonic, path, index),
50        "french" => derive_private_key::<French>(mnemonic, path, index),
51        "italian" => derive_private_key::<Italian>(mnemonic, path, index),
52        "japanese" => derive_private_key::<Japanese>(mnemonic, path, index),
53        "korean" => derive_private_key::<Korean>(mnemonic, path, index),
54        "portuguese" => derive_private_key::<Portuguese>(mnemonic, path, index),
55        "spanish" => derive_private_key::<Spanish>(mnemonic, path, index),
56        _ => Err(format!("unsupported mnemonic language: {language:?}")),
57    }
58}
59
60/// Constructs a [`PrivateKeySigner`] from a raw private key value.
61///
62/// Returns `Err` when `private_key` is zero or its bytes are not a valid secp256k1 scalar.
63pub fn private_key_from_u256(private_key: U256) -> Result<PrivateKeySigner, String> {
64    if private_key.is_zero() {
65        return Err("private key cannot be zero".to_string());
66    }
67    PrivateKeySigner::from_slice(&private_key.to_be_bytes::<32>()).map_err(|e| e.to_string())
68}