|
1 | 1 | //! Implementations of [`Crypto`](spec::Group::Crypto) Cheatcodes. |
2 | 2 |
|
3 | | -use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; |
| 3 | +use crate::{Cheatcode, Cheatcodes, Result, ScriptWallets, Vm::*}; |
4 | 4 | use alloy_primitives::{keccak256, Address, B256, U256}; |
5 | 5 | use alloy_signer::{Signer, SignerSync}; |
6 | 6 | use alloy_signer_local::{ |
7 | 7 | coins_bip39::{ |
8 | 8 | ChineseSimplified, ChineseTraditional, Czech, English, French, Italian, Japanese, Korean, |
9 | 9 | Portuguese, Spanish, Wordlist, |
10 | 10 | }, |
11 | | - MnemonicBuilder, PrivateKeySigner, |
| 11 | + LocalSigner, MnemonicBuilder, PrivateKeySigner, |
12 | 12 | }; |
13 | 13 | use alloy_sol_types::SolValue; |
| 14 | +use foundry_wallets::multi_wallet::MultiWallet; |
14 | 15 | use k256::{ |
15 | 16 | ecdsa::SigningKey, |
16 | 17 | elliptic_curve::{bigint::ArrayEncoding, sec1::ToEncodedPoint}, |
17 | 18 | }; |
18 | 19 | use p256::ecdsa::{signature::hazmat::PrehashSigner, Signature, SigningKey as P256SigningKey}; |
| 20 | +use std::sync::Arc; |
19 | 21 |
|
20 | 22 | /// The BIP32 default derivation path prefix. |
21 | 23 | const DEFAULT_DERIVATION_PATH_PREFIX: &str = "m/44'/60'/0'/0/"; |
@@ -89,14 +91,56 @@ impl Cheatcode for rememberKeyCall { |
89 | 91 | fn apply(&self, state: &mut Cheatcodes) -> Result { |
90 | 92 | let Self { privateKey } = self; |
91 | 93 | let wallet = parse_wallet(privateKey)?; |
92 | | - let address = wallet.address(); |
93 | | - if let Some(script_wallets) = state.script_wallets() { |
94 | | - script_wallets.add_local_signer(wallet); |
95 | | - } |
| 94 | + let address = inject_wallet(state, wallet); |
96 | 95 | Ok(address.abi_encode()) |
97 | 96 | } |
98 | 97 | } |
99 | 98 |
|
| 99 | +impl Cheatcode for rememberKeys_0Call { |
| 100 | + fn apply(&self, state: &mut Cheatcodes) -> Result { |
| 101 | + let Self { mnemonic, derivationPath, count } = self; |
| 102 | + tracing::info!("Remembering {} keys", count); |
| 103 | + let wallets = derive_wallets::<English>(mnemonic, derivationPath, *count)?; |
| 104 | + |
| 105 | + tracing::info!("Adding {} keys to script wallets", count); |
| 106 | + |
| 107 | + let mut addresses = Vec::<Address>::with_capacity(wallets.len()); |
| 108 | + for wallet in wallets { |
| 109 | + let addr = inject_wallet(state, wallet); |
| 110 | + addresses.push(addr); |
| 111 | + } |
| 112 | + |
| 113 | + Ok(addresses.abi_encode()) |
| 114 | + } |
| 115 | +} |
| 116 | + |
| 117 | +impl Cheatcode for rememberKeys_1Call { |
| 118 | + fn apply(&self, state: &mut Cheatcodes) -> Result { |
| 119 | + let Self { mnemonic, derivationPath, language, count } = self; |
| 120 | + let wallets = derive_wallets_str(mnemonic, derivationPath, language, *count)?; |
| 121 | + let mut addresses = Vec::<Address>::with_capacity(wallets.len()); |
| 122 | + for wallet in wallets { |
| 123 | + let addr = inject_wallet(state, wallet); |
| 124 | + addresses.push(addr); |
| 125 | + } |
| 126 | + |
| 127 | + Ok(addresses.abi_encode()) |
| 128 | + } |
| 129 | +} |
| 130 | + |
| 131 | +fn inject_wallet(state: &mut Cheatcodes, wallet: LocalSigner<SigningKey>) -> Address { |
| 132 | + let address = wallet.address(); |
| 133 | + if let Some(script_wallets) = state.script_wallets() { |
| 134 | + script_wallets.add_local_signer(wallet); |
| 135 | + } else { |
| 136 | + // This is needed in case of testing scripts, wherein script wallets are not set on setup. |
| 137 | + let script_wallets = ScriptWallets::new(MultiWallet::default(), None); |
| 138 | + script_wallets.add_local_signer(wallet); |
| 139 | + Arc::make_mut(&mut state.config).script_wallets = Some(script_wallets); |
| 140 | + } |
| 141 | + address |
| 142 | +} |
| 143 | + |
100 | 144 | impl Cheatcode for sign_1Call { |
101 | 145 | fn apply(&self, _state: &mut Cheatcodes) -> Result { |
102 | 146 | let Self { privateKey, digest } = self; |
@@ -228,7 +272,7 @@ fn sign_with_wallet( |
228 | 272 | } else if signers.len() == 1 { |
229 | 273 | *signers.keys().next().unwrap() |
230 | 274 | } else { |
231 | | - bail!("could not determine signer"); |
| 275 | + bail!("could not determine signer, there are multiple signers available use vm.sign(signer, digest) to specify one"); |
232 | 276 | }; |
233 | 277 |
|
234 | 278 | let wallet = signers |
@@ -309,6 +353,50 @@ fn derive_key<W: Wordlist>(mnemonic: &str, path: &str, index: u32) -> Result { |
309 | 353 | Ok(private_key.abi_encode()) |
310 | 354 | } |
311 | 355 |
|
| 356 | +fn derive_wallets_str( |
| 357 | + mnemonic: &str, |
| 358 | + path: &str, |
| 359 | + language: &str, |
| 360 | + count: u32, |
| 361 | +) -> Result<Vec<LocalSigner<SigningKey>>> { |
| 362 | + match language { |
| 363 | + "chinese_simplified" => derive_wallets::<ChineseSimplified>(mnemonic, path, count), |
| 364 | + "chinese_traditional" => derive_wallets::<ChineseTraditional>(mnemonic, path, count), |
| 365 | + "czech" => derive_wallets::<Czech>(mnemonic, path, count), |
| 366 | + "english" => derive_wallets::<English>(mnemonic, path, count), |
| 367 | + "french" => derive_wallets::<French>(mnemonic, path, count), |
| 368 | + "italian" => derive_wallets::<Italian>(mnemonic, path, count), |
| 369 | + "japanese" => derive_wallets::<Japanese>(mnemonic, path, count), |
| 370 | + "korean" => derive_wallets::<Korean>(mnemonic, path, count), |
| 371 | + "portuguese" => derive_wallets::<Portuguese>(mnemonic, path, count), |
| 372 | + "spanish" => derive_wallets::<Spanish>(mnemonic, path, count), |
| 373 | + _ => Err(fmt_err!("unsupported mnemonic language: {language:?}")), |
| 374 | + } |
| 375 | +} |
| 376 | + |
| 377 | +fn derive_wallets<W: Wordlist>( |
| 378 | + mnemonic: &str, |
| 379 | + path: &str, |
| 380 | + count: u32, |
| 381 | +) -> Result<Vec<LocalSigner<SigningKey>>> { |
| 382 | + let mut out = path.to_string(); |
| 383 | + |
| 384 | + if !out.ends_with('/') { |
| 385 | + out.push('/'); |
| 386 | + } |
| 387 | + |
| 388 | + let mut wallets = Vec::with_capacity(count as usize); |
| 389 | + for idx in 0..count { |
| 390 | + let wallet = MnemonicBuilder::<W>::default() |
| 391 | + .phrase(mnemonic) |
| 392 | + .derivation_path(format!("{out}{idx}"))? |
| 393 | + .build()?; |
| 394 | + wallets.push(wallet); |
| 395 | + } |
| 396 | + |
| 397 | + Ok(wallets) |
| 398 | +} |
| 399 | + |
312 | 400 | #[cfg(test)] |
313 | 401 | mod tests { |
314 | 402 | use super::*; |
|
0 commit comments