use crate::crypto::address::generate_zera_address;
use crate::crypto::constants::{HashType, KeyType};
use crate::crypto::signature::{Ed25519KeyPair, Ed448KeyPair};
use crate::error::{Result, ZeraError};
use crate::wallet::constants::{HdOptions, ZERA_NAME, ZERA_SYMBOL, ZERA_TYPE};
use crate::wallet::crypto_core::Slip0010HdWallet;
use crate::wallet::hd::{build_derivation_path, generate_seed};
use crate::wallet::shared::{create_base_wallet, generate_zera_public_key_identifier, Wallet};
#[derive(Debug, Clone)]
pub struct WalletOptions {
pub key_type: KeyType,
pub hash_types: Vec<HashType>,
pub mnemonic: String,
pub passphrase: String,
pub hd_options: HdOptions,
pub seed: Option<Vec<u8>>,
}
impl WalletOptions {
pub fn new(key_type: KeyType, mnemonic: impl Into<String>) -> Self {
Self {
key_type,
hash_types: Vec::new(),
mnemonic: mnemonic.into(),
passphrase: String::new(),
hd_options: HdOptions::default(),
seed: None,
}
}
}
#[derive(Debug, Clone)]
pub struct MultipleWalletOptions {
pub wallet: WalletOptions,
pub count: usize,
}
pub fn create_wallet(options: WalletOptions) -> Result<Wallet> {
if options.mnemonic.is_empty() {
return Err(ZeraError::Validation("mnemonic - must be provided".into()));
}
let seed = if let Some(seed) = options.seed.clone() {
seed
} else {
generate_seed(&options.mnemonic, &options.passphrase)?
};
let derivation_path = build_derivation_path(options.hd_options)?;
let hd_node = Slip0010HdWallet::new(&seed, &derivation_path, options.key_type)?;
let (private_key_base58, public_key_bytes, public_key_package) = match options.key_type {
KeyType::Ed25519 => {
let key_pair = Ed25519KeyPair::from_private_key(&hd_node.private_key)?;
(
key_pair.private_key_base58(),
key_pair.public_key_bytes().to_vec(),
key_pair.public_key_base58(),
)
}
KeyType::Ed448 => {
let key_pair = Ed448KeyPair::from_private_key(&hd_node.private_key)?;
(
key_pair.private_key_base58(),
key_pair.public_key_bytes().to_vec(),
key_pair.public_key_base58(),
)
}
};
let address = generate_zera_address(&public_key_bytes, options.key_type, &options.hash_types)?;
let public_key_identifier = generate_zera_public_key_identifier(
&public_key_bytes,
options.key_type,
&options.hash_types,
)?;
let base = create_base_wallet(
"hd",
&options.mnemonic,
&private_key_base58,
&address,
&public_key_identifier,
ZERA_TYPE,
ZERA_SYMBOL,
&derivation_path,
options.key_type,
options.hash_types.clone(),
);
Ok(Wallet {
wallet_type: "hd",
mnemonic: base.mnemonic,
private_key: private_key_base58,
address: base.address,
public_key: base.public_key,
public_key_package,
coin_type: ZERA_TYPE,
symbol: ZERA_SYMBOL.to_string(),
name: ZERA_NAME.to_string(),
derivation_path,
key_type: options.key_type,
hash_types: if options.hash_types.is_empty() {
None
} else {
Some(options.hash_types)
},
extended_private_key: hd_node.get_extended_private_key(),
extended_public_key: hd_node.get_extended_public_key(),
fingerprint: hd_node.get_fingerprint(),
depth: hd_node.depth,
index: hd_node.index,
})
}
pub fn derive_multiple_wallets(options: MultipleWalletOptions) -> Result<Vec<Wallet>> {
if options.count == 0 {
return Err(ZeraError::Validation(
"Count must be a positive integer".into(),
));
}
let mut wallets = Vec::with_capacity(options.count);
let base_account = options.wallet.hd_options.account_index;
for i in 0..options.count {
let mut wallet_options = options.wallet.clone();
wallet_options.hd_options.account_index = base_account + i as u32;
wallets.push(create_wallet(wallet_options)?);
}
Ok(wallets)
}