diff --git a/zk_toolbox/crates/common/src/ethereum.rs b/zk_toolbox/crates/common/src/ethereum.rs index 33caaad9789e..2100746fecff 100644 --- a/zk_toolbox/crates/common/src/ethereum.rs +++ b/zk_toolbox/crates/common/src/ethereum.rs @@ -6,18 +6,17 @@ use ethers::{ middleware::MiddlewareBuilder, prelude::{Http, LocalWallet, Provider, Signer, SignerMiddleware}, providers::Middleware, - types::{Address, TransactionRequest, H256}, + types::{Address, TransactionRequest}, }; use types::TokenInfo; use crate::{logger, wallets::Wallet}; pub fn create_ethers_client( - private_key: H256, + mut wallet: LocalWallet, l1_rpc: String, chain_id: Option, ) -> anyhow::Result, ethers::prelude::Wallet>> { - let mut wallet = LocalWallet::from_bytes(private_key.as_bytes())?; if let Some(chain_id) = chain_id { wallet = wallet.with_chain_id(chain_id); } diff --git a/zk_toolbox/crates/common/src/forge.rs b/zk_toolbox/crates/common/src/forge.rs index 7fd5399cc66b..e573e492aa4f 100644 --- a/zk_toolbox/crates/common/src/forge.rs +++ b/zk_toolbox/crates/common/src/forge.rs @@ -143,10 +143,12 @@ impl ForgeScript { } // Do not start the script if balance is not enough - pub fn private_key(&self) -> Option { + pub fn private_key(&self) -> Option { self.args.args.iter().find_map(|a| { if let ForgeScriptArg::PrivateKey { private_key } = a { - Some(H256::from_str(private_key).unwrap()) + let key = H256::from_str(private_key).unwrap(); + let key = LocalWallet::from_bytes(key.as_bytes()).unwrap(); + Some(key) } else { None } @@ -164,11 +166,7 @@ impl ForgeScript { } pub fn address(&self) -> Option
{ - self.private_key().and_then(|a| { - LocalWallet::from_bytes(a.as_bytes()) - .ok() - .map(|a| Address::from_slice(a.address().as_bytes())) - }) + self.private_key().map(|k| k.address()) } pub async fn get_the_balance(&self) -> anyhow::Result> { diff --git a/zk_toolbox/crates/common/src/wallets.rs b/zk_toolbox/crates/common/src/wallets.rs index ed5e11b3261a..43a9864474cc 100644 --- a/zk_toolbox/crates/common/src/wallets.rs +++ b/zk_toolbox/crates/common/src/wallets.rs @@ -1,31 +1,70 @@ use ethers::{ - core::rand::Rng, + core::rand::{CryptoRng, Rng}, signers::{coins_bip39::English, LocalWallet, MnemonicBuilder, Signer}, types::{Address, H256}, }; use serde::{Deserialize, Serialize}; +use types::parse_h256; -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Wallet { +#[derive(Serialize, Deserialize)] +struct WalletSerde { pub address: Address, pub private_key: Option, } -impl Wallet { - pub fn random(rng: &mut impl Rng) -> Self { - let private_key = H256::random_using(rng); - let local_wallet = LocalWallet::from_bytes(private_key.as_bytes()).unwrap(); +#[derive(Debug, Clone)] +pub struct Wallet { + pub address: Address, + pub private_key: Option, +} - Self { - address: Address::from_slice(local_wallet.address().as_bytes()), - private_key: Some(private_key), +impl<'de> Deserialize<'de> for Wallet { + fn deserialize>(d: D) -> Result { + let x = WalletSerde::deserialize(d)?; + Ok(match x.private_key { + None => Self { + address: x.address, + private_key: None, + }, + Some(k) => { + let k = LocalWallet::from_bytes(k.as_bytes()).map_err(serde::de::Error::custom)?; + if k.address() != x.address { + return Err(serde::de::Error::custom(format!( + "address does not match private key: got address {:#x}, want {:#x}", + x.address, + k.address(), + ))); + } + Self::new(k) + } + }) + } +} + +impl Serialize for Wallet { + fn serialize(&self, s: S) -> Result { + WalletSerde { + address: self.address, + private_key: self.private_key_h256(), } + .serialize(s) + } +} + +impl Wallet { + pub fn private_key_h256(&self) -> Option { + self.private_key + .as_ref() + .map(|k| parse_h256(k.signer().to_bytes().as_slice()).unwrap()) + } + + pub fn random(rng: &mut (impl Rng + CryptoRng)) -> Self { + Self::new(LocalWallet::new(rng)) } - pub fn new_with_key(private_key: H256) -> Self { - let local_wallet = LocalWallet::from_bytes(private_key.as_bytes()).unwrap(); + pub fn new(private_key: LocalWallet) -> Self { Self { - address: Address::from_slice(local_wallet.address().as_bytes()), + address: private_key.address(), private_key: Some(private_key), } } @@ -35,14 +74,13 @@ impl Wallet { .phrase(mnemonic) .derivation_path(&format!("{}/{}", base_path, index))? .build()?; - let private_key = H256::from_slice(&wallet.signer().to_bytes()); - Ok(Self::new_with_key(private_key)) + Ok(Self::new(wallet)) } pub fn empty() -> Self { Self { address: Address::zero(), - private_key: Some(H256::zero()), + private_key: None, } } } diff --git a/zk_toolbox/crates/config/src/chain.rs b/zk_toolbox/crates/config/src/chain.rs index affc8ccc770c..d6b6e2b866b7 100644 --- a/zk_toolbox/crates/config/src/chain.rs +++ b/zk_toolbox/crates/config/src/chain.rs @@ -87,8 +87,8 @@ impl ChainConfig { pub fn get_wallets_config(&self) -> anyhow::Result { let path = self.configs.join(WALLETS_FILE); - if let Ok(wallets) = WalletsConfig::read(self.get_shell(), &path) { - return Ok(wallets); + if self.get_shell().path_exists(&path) { + return WalletsConfig::read(self.get_shell(), &path); } if self.wallet_creation == WalletCreation::Localhost { let wallets = create_localhost_wallets(self.get_shell(), &self.link_to_code, self.id)?; diff --git a/zk_toolbox/crates/config/src/ecosystem.rs b/zk_toolbox/crates/config/src/ecosystem.rs index a5fcd8b72199..7ac81cd53941 100644 --- a/zk_toolbox/crates/config/src/ecosystem.rs +++ b/zk_toolbox/crates/config/src/ecosystem.rs @@ -196,8 +196,8 @@ impl EcosystemConfig { pub fn get_wallets(&self) -> anyhow::Result { let path = self.config.join(WALLETS_FILE); - if let Ok(wallets) = WalletsConfig::read(self.get_shell(), &path) { - return Ok(wallets); + if self.get_shell().path_exists(&path) { + return WalletsConfig::read(self.get_shell(), &path); } if self.wallet_creation == WalletCreation::Localhost { // Use 0 id for ecosystem wallets diff --git a/zk_toolbox/crates/config/src/wallets.rs b/zk_toolbox/crates/config/src/wallets.rs index 9c87453954ec..c650781bff53 100644 --- a/zk_toolbox/crates/config/src/wallets.rs +++ b/zk_toolbox/crates/config/src/wallets.rs @@ -1,6 +1,5 @@ use common::wallets::Wallet; -use ethers::types::H256; -use rand::Rng; +use rand::{CryptoRng, Rng}; use serde::{Deserialize, Serialize}; use crate::{ @@ -20,7 +19,7 @@ pub struct WalletsConfig { impl WalletsConfig { /// Generate random wallets - pub fn random(rng: &mut impl Rng) -> Self { + pub fn random(rng: &mut (impl CryptoRng + Rng)) -> Self { Self { deployer: Some(Wallet::random(rng)), operator: Wallet::random(rng), @@ -42,13 +41,6 @@ impl WalletsConfig { token_multiplier_setter: Some(Wallet::empty()), } } - pub fn deployer_private_key(&self) -> Option { - self.deployer.as_ref().and_then(|wallet| wallet.private_key) - } - - pub fn governor_private_key(&self) -> Option { - self.governor.private_key - } } impl FileConfigWithDefaultName for WalletsConfig { diff --git a/zk_toolbox/crates/types/src/lib.rs b/zk_toolbox/crates/types/src/lib.rs index 8b6470571051..075e39345bcf 100644 --- a/zk_toolbox/crates/types/src/lib.rs +++ b/zk_toolbox/crates/types/src/lib.rs @@ -10,5 +10,5 @@ pub use prover_mode::*; pub use token_info::*; pub use wallet_creation::*; pub use zksync_basic_types::{ - commitment::L1BatchCommitmentMode, protocol_version::ProtocolSemanticVersion, + commitment::L1BatchCommitmentMode, parse_h256, protocol_version::ProtocolSemanticVersion, }; diff --git a/zk_toolbox/crates/zk_inception/src/accept_ownership.rs b/zk_toolbox/crates/zk_inception/src/accept_ownership.rs index d2bab9283740..474e76e599a8 100644 --- a/zk_toolbox/crates/zk_inception/src/accept_ownership.rs +++ b/zk_toolbox/crates/zk_inception/src/accept_ownership.rs @@ -1,13 +1,10 @@ use common::{ forge::{Forge, ForgeScript, ForgeScriptArgs}, spinner::Spinner, + wallets::Wallet, }; use config::{forge_interface::script_params::ACCEPT_GOVERNANCE_SCRIPT_PARAMS, EcosystemConfig}; -use ethers::{ - abi::parse_abi, - contract::BaseContract, - types::{Address, H256}, -}; +use ethers::{abi::parse_abi, contract::BaseContract, types::Address}; use lazy_static::lazy_static; use xshell::Shell; @@ -31,7 +28,7 @@ pub async fn accept_admin( shell: &Shell, ecosystem_config: &EcosystemConfig, admin: Address, - governor: Option, + governor: &Wallet, target_address: Address, forge_args: &ForgeScriptArgs, l1_rpc_url: String, @@ -62,7 +59,7 @@ pub async fn accept_owner( shell: &Shell, ecosystem_config: &EcosystemConfig, governor_contract: Address, - governor: Option, + governor: &Wallet, target_address: Address, forge_args: &ForgeScriptArgs, l1_rpc_url: String, @@ -89,10 +86,10 @@ pub async fn accept_owner( async fn accept_ownership( shell: &Shell, - governor: Option, + governor: &Wallet, mut forge: ForgeScript, ) -> anyhow::Result<()> { - forge = fill_forge_private_key(forge, governor)?; + forge = fill_forge_private_key(forge, Some(governor))?; check_the_balance(&forge).await?; let spinner = Spinner::new(MSG_ACCEPTING_GOVERNANCE_SPINNER); forge.run(shell)?; diff --git a/zk_toolbox/crates/zk_inception/src/commands/chain/accept_chain_ownership.rs b/zk_toolbox/crates/zk_inception/src/commands/chain/accept_chain_ownership.rs index 37d69fcf5bcc..cf3e2981b3c7 100644 --- a/zk_toolbox/crates/zk_inception/src/commands/chain/accept_chain_ownership.rs +++ b/zk_toolbox/crates/zk_inception/src/commands/chain/accept_chain_ownership.rs @@ -30,7 +30,7 @@ pub async fn run(args: ForgeScriptArgs, shell: &Shell) -> anyhow::Result<()> { shell, &ecosystem_config, contracts.l1.chain_admin_addr, - chain_config.get_wallets_config()?.governor_private_key(), + &chain_config.get_wallets_config()?.governor, contracts.l1.diamond_proxy_addr, &args, l1_rpc_url.clone(), diff --git a/zk_toolbox/crates/zk_inception/src/commands/chain/deploy_l2_contracts.rs b/zk_toolbox/crates/zk_inception/src/commands/chain/deploy_l2_contracts.rs index 8f0e04b5338a..5a4f1f86f350 100644 --- a/zk_toolbox/crates/zk_inception/src/commands/chain/deploy_l2_contracts.rs +++ b/zk_toolbox/crates/zk_inception/src/commands/chain/deploy_l2_contracts.rs @@ -277,10 +277,7 @@ async fn call_forge( forge = forge.with_signature(signature); } - forge = fill_forge_private_key( - forge, - ecosystem_config.get_wallets()?.governor_private_key(), - )?; + forge = fill_forge_private_key(forge, Some(&ecosystem_config.get_wallets()?.governor))?; check_the_balance(&forge).await?; forge.run(shell)?; diff --git a/zk_toolbox/crates/zk_inception/src/commands/chain/deploy_paymaster.rs b/zk_toolbox/crates/zk_inception/src/commands/chain/deploy_paymaster.rs index 0da56f0c962d..4a93fcc089f8 100644 --- a/zk_toolbox/crates/zk_inception/src/commands/chain/deploy_paymaster.rs +++ b/zk_toolbox/crates/zk_inception/src/commands/chain/deploy_paymaster.rs @@ -56,10 +56,7 @@ pub async fn deploy_paymaster( if let Some(address) = sender { forge = forge.with_sender(address); } else { - forge = fill_forge_private_key( - forge, - chain_config.get_wallets_config()?.governor_private_key(), - )?; + forge = fill_forge_private_key(forge, Some(&chain_config.get_wallets_config()?.governor))?; } if broadcast { diff --git a/zk_toolbox/crates/zk_inception/src/commands/chain/init/mod.rs b/zk_toolbox/crates/zk_inception/src/commands/chain/init/mod.rs index ac80a5b98f72..d92c56d2eb10 100644 --- a/zk_toolbox/crates/zk_inception/src/commands/chain/init/mod.rs +++ b/zk_toolbox/crates/zk_inception/src/commands/chain/init/mod.rs @@ -107,7 +107,7 @@ pub async fn init( shell, ecosystem_config, contracts_config.l1.chain_admin_addr, - chain_config.get_wallets_config()?.governor_private_key(), + &chain_config.get_wallets_config()?.governor, contracts_config.l1.diamond_proxy_addr, &init_args.forge_args.clone(), init_args.l1_rpc_url.clone(), @@ -121,7 +121,7 @@ pub async fn init( set_token_multiplier_setter( shell, ecosystem_config, - chain_config.get_wallets_config()?.governor_private_key(), + &chain_config.get_wallets_config()?.governor, contracts_config.l1.chain_admin_addr, chain_config .get_wallets_config() diff --git a/zk_toolbox/crates/zk_inception/src/commands/chain/register_chain.rs b/zk_toolbox/crates/zk_inception/src/commands/chain/register_chain.rs index 9f2ff41f897e..65ee05a1ea5f 100644 --- a/zk_toolbox/crates/zk_inception/src/commands/chain/register_chain.rs +++ b/zk_toolbox/crates/zk_inception/src/commands/chain/register_chain.rs @@ -81,7 +81,7 @@ pub async fn register_chain( if let Some(address) = sender { forge = forge.with_sender(address); } else { - forge = fill_forge_private_key(forge, config.get_wallets()?.governor_private_key())?; + forge = fill_forge_private_key(forge, Some(&config.get_wallets()?.governor))?; check_the_balance(&forge).await?; } diff --git a/zk_toolbox/crates/zk_inception/src/commands/chain/set_token_multiplier_setter.rs b/zk_toolbox/crates/zk_inception/src/commands/chain/set_token_multiplier_setter.rs index 475725cd14ef..4a6cd31b2c0a 100644 --- a/zk_toolbox/crates/zk_inception/src/commands/chain/set_token_multiplier_setter.rs +++ b/zk_toolbox/crates/zk_inception/src/commands/chain/set_token_multiplier_setter.rs @@ -3,12 +3,13 @@ use common::{ forge::{Forge, ForgeScript, ForgeScriptArgs}, logger, spinner::Spinner, + wallets::Wallet, }; use config::{forge_interface::script_params::ACCEPT_GOVERNANCE_SCRIPT_PARAMS, EcosystemConfig}; use ethers::{abi::parse_abi, contract::BaseContract, utils::hex}; use lazy_static::lazy_static; use xshell::Shell; -use zksync_basic_types::{Address, H256}; +use zksync_basic_types::Address; use crate::{ messages::{ @@ -52,7 +53,7 @@ pub async fn run(args: ForgeScriptArgs, shell: &Shell) -> anyhow::Result<()> { set_token_multiplier_setter( shell, &ecosystem_config, - chain_config.get_wallets_config()?.governor_private_key(), + &chain_config.get_wallets_config()?.governor, contracts_config.l1.chain_admin_addr, token_multiplier_setter_address, &args.clone(), @@ -72,7 +73,7 @@ pub async fn run(args: ForgeScriptArgs, shell: &Shell) -> anyhow::Result<()> { pub async fn set_token_multiplier_setter( shell: &Shell, ecosystem_config: &EcosystemConfig, - governor: Option, + governor: &Wallet, chain_admin_address: Address, target_address: Address, forge_args: &ForgeScriptArgs, @@ -105,10 +106,10 @@ pub async fn set_token_multiplier_setter( async fn update_token_multiplier_setter( shell: &Shell, - governor: Option, + governor: &Wallet, mut forge: ForgeScript, ) -> anyhow::Result<()> { - forge = fill_forge_private_key(forge, governor)?; + forge = fill_forge_private_key(forge, Some(governor))?; check_the_balance(&forge).await?; forge.run(shell)?; Ok(()) diff --git a/zk_toolbox/crates/zk_inception/src/commands/chain/setup_legacy_bridge.rs b/zk_toolbox/crates/zk_inception/src/commands/chain/setup_legacy_bridge.rs index 925014fe4e61..f61c640ffb6b 100644 --- a/zk_toolbox/crates/zk_inception/src/commands/chain/setup_legacy_bridge.rs +++ b/zk_toolbox/crates/zk_inception/src/commands/chain/setup_legacy_bridge.rs @@ -59,10 +59,7 @@ pub async fn setup_legacy_bridge( ) .with_broadcast(); - forge = fill_forge_private_key( - forge, - ecosystem_config.get_wallets()?.governor_private_key(), - )?; + forge = fill_forge_private_key(forge, Some(&ecosystem_config.get_wallets()?.governor))?; let spinner = Spinner::new(MSG_DEPLOYING_PAYMASTER); check_the_balance(&forge).await?; diff --git a/zk_toolbox/crates/zk_inception/src/commands/consensus/mod.rs b/zk_toolbox/crates/zk_inception/src/commands/consensus/mod.rs index f30e37af4bcd..a21ba2d62cf9 100644 --- a/zk_toolbox/crates/zk_inception/src/commands/consensus/mod.rs +++ b/zk_toolbox/crates/zk_inception/src/commands/consensus/mod.rs @@ -11,7 +11,7 @@ use ethers::{ contract::{FunctionCall, Multicall}, middleware::{Middleware, NonceManagerMiddleware, SignerMiddleware}, providers::{Http, JsonRpcClient, PendingTransaction, Provider, RawCall as _}, - signers::{LocalWallet, Signer as _}, + signers::Signer as _, types::{Address, BlockId, H256}, }; use xshell::Shell; @@ -182,9 +182,7 @@ impl Setup { .governor .private_key .context(messages::MSG_GOVERNOR_PRIVATE_KEY_NOT_SET)?; - let governor = LocalWallet::from_bytes(governor.as_bytes()) - .context("LocalWallet::from_bytes()")? - .with_chain_id(self.genesis.l2_chain_id.as_u64()); + let governor = governor.with_chain_id(self.genesis.l2_chain_id.as_u64()); let provider = self.provider().context("provider()")?; let signer = SignerMiddleware::new(provider, governor.clone()); // Allows us to send next transaction without waiting for the previous to complete. diff --git a/zk_toolbox/crates/zk_inception/src/commands/ecosystem/common.rs b/zk_toolbox/crates/zk_inception/src/commands/ecosystem/common.rs index 950d39876b09..42b8f79b97eb 100644 --- a/zk_toolbox/crates/zk_inception/src/commands/ecosystem/common.rs +++ b/zk_toolbox/crates/zk_inception/src/commands/ecosystem/common.rs @@ -54,7 +54,7 @@ pub async fn deploy_l1( if let Some(address) = sender { forge = forge.with_sender(address); } else { - forge = fill_forge_private_key(forge, wallets_config.deployer_private_key())?; + forge = fill_forge_private_key(forge, wallets_config.deployer.as_ref())?; } if broadcast { diff --git a/zk_toolbox/crates/zk_inception/src/commands/ecosystem/init.rs b/zk_toolbox/crates/zk_inception/src/commands/ecosystem/init.rs index 6b64b740aedd..bf5a4605c09c 100644 --- a/zk_toolbox/crates/zk_inception/src/commands/ecosystem/init.rs +++ b/zk_toolbox/crates/zk_inception/src/commands/ecosystem/init.rs @@ -151,10 +151,7 @@ async fn deploy_erc20( .with_rpc_url(l1_rpc_url) .with_broadcast(); - forge = fill_forge_private_key( - forge, - ecosystem_config.get_wallets()?.deployer_private_key(), - )?; + forge = fill_forge_private_key(forge, ecosystem_config.get_wallets()?.deployer.as_ref())?; let spinner = Spinner::new(MSG_DEPLOYING_ERC20_SPINNER); check_the_balance(&forge).await?; @@ -262,7 +259,7 @@ async fn deploy_ecosystem_inner( shell, config, contracts_config.l1.governance_addr, - config.get_wallets()?.governor_private_key(), + &config.get_wallets()?.governor, contracts_config.ecosystem_contracts.bridgehub_proxy_addr, &forge_args, l1_rpc_url.clone(), @@ -273,7 +270,7 @@ async fn deploy_ecosystem_inner( shell, config, contracts_config.l1.chain_admin_addr, - config.get_wallets()?.governor_private_key(), + &config.get_wallets()?.governor, contracts_config.ecosystem_contracts.bridgehub_proxy_addr, &forge_args, l1_rpc_url.clone(), @@ -284,7 +281,7 @@ async fn deploy_ecosystem_inner( shell, config, contracts_config.l1.governance_addr, - config.get_wallets()?.governor_private_key(), + &config.get_wallets()?.governor, contracts_config.bridges.shared.l1_address, &forge_args, l1_rpc_url.clone(), @@ -295,7 +292,7 @@ async fn deploy_ecosystem_inner( shell, config, contracts_config.l1.chain_admin_addr, - config.get_wallets()?.governor_private_key(), + &config.get_wallets()?.governor, contracts_config.bridges.shared.l1_address, &forge_args, l1_rpc_url.clone(), @@ -306,7 +303,7 @@ async fn deploy_ecosystem_inner( shell, config, contracts_config.l1.governance_addr, - config.get_wallets()?.governor_private_key(), + &config.get_wallets()?.governor, contracts_config .ecosystem_contracts .state_transition_proxy_addr, @@ -319,7 +316,7 @@ async fn deploy_ecosystem_inner( shell, config, contracts_config.l1.chain_admin_addr, - config.get_wallets()?.governor_private_key(), + &config.get_wallets()?.governor, contracts_config .ecosystem_contracts .state_transition_proxy_addr, diff --git a/zk_toolbox/crates/zk_inception/src/utils/forge.rs b/zk_toolbox/crates/zk_inception/src/utils/forge.rs index cabc8ff7566b..355cf7b5f930 100644 --- a/zk_toolbox/crates/zk_inception/src/utils/forge.rs +++ b/zk_toolbox/crates/zk_inception/src/utils/forge.rs @@ -1,6 +1,6 @@ -use anyhow::anyhow; -use common::forge::ForgeScript; -use ethers::types::{H256, U256}; +use anyhow::Context as _; +use common::{forge::ForgeScript, wallets::Wallet}; +use ethers::types::U256; use crate::{ consts::MINIMUM_BALANCE_FOR_WALLET, @@ -9,10 +9,14 @@ use crate::{ pub fn fill_forge_private_key( mut forge: ForgeScript, - private_key: Option, + wallet: Option<&Wallet>, ) -> anyhow::Result { if !forge.wallet_args_passed() { - forge = forge.with_private_key(private_key.ok_or(anyhow!(MSG_DEPLOYER_PK_NOT_SET_ERR))?); + forge = forge.with_private_key( + wallet + .and_then(|w| w.private_key_h256()) + .context(MSG_DEPLOYER_PK_NOT_SET_ERR)?, + ); } Ok(forge) } diff --git a/zk_toolbox/crates/zk_supervisor/src/commands/test/utils.rs b/zk_toolbox/crates/zk_supervisor/src/commands/test/utils.rs index 8656ff44d319..d980490c3d58 100644 --- a/zk_toolbox/crates/zk_supervisor/src/commands/test/utils.rs +++ b/zk_toolbox/crates/zk_supervisor/src/commands/test/utils.rs @@ -43,10 +43,11 @@ impl TestWallets { } pub fn get_test_pk(&self, chain_config: &ChainConfig) -> anyhow::Result { - self.get_test_wallet(chain_config)? - .private_key - .ok_or(anyhow::Error::msg("Private key not found")) - .map(|pk| pk.encode_hex::()) + Ok(self + .get_test_wallet(chain_config)? + .private_key_h256() + .context("Private key not found")? + .encode_hex()) } pub async fn init_test_wallet(