use crate::crypto::constants::{HashType, KeyType};
use crate::error::{Result, ZeraError};
#[derive(Debug, Clone)]
pub struct BaseWallet {
pub wallet_type: String,
pub mnemonic: String,
pub private_key: String,
pub address: String,
pub public_key: String,
pub coin_type: u32,
pub symbol: String,
pub derivation_path: String,
pub key_type: KeyType,
pub hash_types: Option<Vec<HashType>>,
}
#[derive(Debug, Clone)]
pub struct Wallet {
pub wallet_type: &'static str,
pub mnemonic: String,
pub private_key: String,
pub address: String,
pub public_key: String,
pub public_key_package: String,
pub coin_type: u32,
pub symbol: String,
pub name: String,
pub derivation_path: String,
pub key_type: KeyType,
pub hash_types: Option<Vec<HashType>>,
pub extended_private_key: String,
pub extended_public_key: String,
pub fingerprint: String,
pub depth: u8,
pub index: u32,
}
impl Wallet {
pub fn secure_clear(&mut self) {
self.mnemonic.clear();
self.private_key.clear();
}
}
#[derive(Debug, Clone)]
pub struct SanitizedWallet {
pub wallet_type: String,
pub address: String,
pub public_key: Option<String>,
pub coin_type: u32,
pub symbol: String,
pub derivation_path: String,
pub key_type: KeyType,
pub hash_types: Option<Vec<HashType>>,
pub mnemonic: &'static str,
pub private_key: &'static str,
}
pub fn generate_zera_public_key_identifier(
public_key: &[u8],
key_type: KeyType,
hash_types: &[HashType],
) -> Result<String> {
if public_key.is_empty() {
return Err(ZeraError::InvalidInput(
"Public key must be non-empty".into(),
));
}
let key_prefix = key_type.prefix();
let public_key_b58 = bs58::encode(public_key).into_string();
if hash_types.is_empty() {
return Ok(format!("{key_prefix}_{public_key_b58}"));
}
let mut sorted = hash_types.to_vec();
sorted.sort();
let hash_prefixes = sorted
.iter()
.map(|h| h.prefix())
.collect::<Vec<_>>()
.join("_");
Ok(format!("{key_prefix}_{hash_prefixes}_{public_key_b58}"))
}
#[allow(clippy::too_many_arguments)]
pub fn create_base_wallet(
wallet_type: &str,
mnemonic: &str,
private_key: &str,
address: &str,
public_key: &str,
coin_type: u32,
symbol: &str,
derivation_path: &str,
key_type: KeyType,
hash_types: Vec<HashType>,
) -> BaseWallet {
BaseWallet {
wallet_type: wallet_type.to_string(),
mnemonic: mnemonic.to_string(),
private_key: private_key.to_string(),
address: address.to_string(),
public_key: public_key.to_string(),
coin_type,
symbol: symbol.to_string(),
derivation_path: derivation_path.to_string(),
key_type,
hash_types: if hash_types.is_empty() {
None
} else {
Some(hash_types)
},
}
}
pub fn validate_wallet_object(wallet: &BaseWallet) -> bool {
!wallet.wallet_type.is_empty()
&& !wallet.mnemonic.is_empty()
&& !wallet.private_key.is_empty()
&& !wallet.address.is_empty()
&& !wallet.public_key.is_empty()
&& !wallet.symbol.is_empty()
&& !wallet.derivation_path.is_empty()
}
pub fn sanitize_wallet_for_logging(wallet: &BaseWallet) -> SanitizedWallet {
let public_key = if wallet.public_key.is_empty() {
None
} else {
Some(format!(
"{}...",
&wallet.public_key[..wallet.public_key.len().min(10)]
))
};
SanitizedWallet {
wallet_type: wallet.wallet_type.clone(),
address: wallet.address.clone(),
public_key,
coin_type: wallet.coin_type,
symbol: wallet.symbol.clone(),
derivation_path: wallet.derivation_path.clone(),
key_type: wallet.key_type,
hash_types: wallet.hash_types.clone(),
mnemonic: "[REDACTED]",
private_key: "[REDACTED]",
}
}
pub fn create_wallet_summary(wallet: &BaseWallet) -> String {
if !validate_wallet_object(wallet) {
return "Invalid wallet object".to_string();
}
let hash_types = wallet
.hash_types
.as_ref()
.filter(|v| !v.is_empty())
.map(|vals| {
vals.iter()
.map(|h| h.as_str())
.collect::<Vec<_>>()
.join(", ")
})
.unwrap_or_else(|| "none".to_string());
format!(
"Wallet Summary:\n Type: {}\n Address: {}\n Key Type: {}\n Hash Types: {}\n Derivation Path: {}\n Coin Type: {}\n Symbol: {}",
wallet.wallet_type,
wallet.address,
wallet.key_type.as_str(),
hash_types,
wallet.derivation_path,
wallet.coin_type,
wallet.symbol
)
}