zera-sdk 0.1.0

Rust SDK for ZERA transactions, validator APIs, and bridge workflows
Documentation
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)
}