miracle-api 0.6.0

Miracle is a pay2e protocol for sovereign individuals living in Mirascape Horizon.
Documentation
//! Utility functions for the Miracle API/SDK
//!
//! This module provides common utilities for data conversion, validation,
//! and other helper functions used across the Miracle ecosystem.
//!
//! ## Key Features
//!
//! ### Hex String Conversion
//! - Convert hex strings to byte arrays for on-chain consumption
//! - Support for common hex prefixes (0x, \\x)
//! - Type-safe conversions with compile-time size checking
//!
//! ### Claim Instruction Utilities
//! - Convert database hex strings to `EpochClaimData` structs
//! - Bridge between off-chain database queries and on-chain instruction data
//! - Support for both hex string and native u64 inputs
//!
//! ## Usage Examples
//!
//! ```rust
//! use miracle_api::utils::{hex, claim};
//!
//! // Convert hex string to byte array
//! let bytes: [u8; 8] = hex::hex_string_to_u64_bytes("0x1234567890abcdef")?;
//!
//! // Create EpochClaimData from hex strings (from database)
//! let epoch_data = claim::epoch_claim_data_from_hex(
//!     "0x1234567890abcdef",
//!     "0xfedcba0987654321",
//!     "0x0000000000000064",
//!     "0x00000000000000c8"
//! )?;
//!
//! // Create EpochClaimData from native u64 values
//! let epoch_data = claim::epoch_claim_data_from_u64(1234567890, 9876543210, 100, 200);
//! ```
//!
//! ## Key Features
//!
//! ### Hex String Conversion
//! - Convert hex strings to byte arrays for on-chain consumption
//! - Support for common hex prefixes (0x, \\x)
//! - Type-safe conversions with compile-time size checking
//!
//! ### Claim Instruction Utilities
//! - Convert database hex strings to `EpochClaimData` structs
//! - Bridge between off-chain database queries and on-chain instruction data
//! - Support for both hex string and native u64 inputs
//!
//! ## Usage Examples
//!
//! ```rust
//! use miracle_api::utils::{hex, claim};
//!
//! // Convert hex string to byte array
//! let bytes: [u8; 8] = hex::hex_string_to_u64_bytes("0x1234567890abcdef")?;
//!
//! // Create EpochClaimData from hex strings (from database)
//! let epoch_data = claim::epoch_claim_data_from_hex(
//!     "0x1234567890abcdef",
//!     "0xfedcba0987654321",
//!     "0x0000000000000064",
//!     "0x00000000000000c8"
//! )?;
//!
//! // Create EpochClaimData from native u64 values
//! let epoch_data = claim::epoch_claim_data_from_u64(1234567890, 9876543210, 100, 200);
//! ```

/// Hex string conversion utilities
pub mod hex {
    /// Convert a hex string to a byte array of specified length
    ///
    /// ## Parameters
    /// - `hex_str`: Hex string (with or without "0x" prefix)
    /// - `expected_len`: Expected length of the resulting byte array
    ///
    /// ## Returns
    /// - `Ok([u8; N])` if conversion succeeds
    /// - `Err(&str)` with error message if conversion fails
    ///
    /// ## Examples
    /// ```rust
    /// use miracle_api::utils::hex::hex_string_to_bytes;
    ///
    /// // Convert to 8-byte array
    /// let bytes: [u8; 8] = hex_string_to_bytes("0x1234567890abcdef", 8)?;
    /// assert_eq!(bytes, [0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef]);
    ///
    /// // Convert to 32-byte array
    /// let bytes: [u8; 32] = hex_string_to_bytes("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", 32)?;
    /// ```
    pub fn hex_string_to_bytes<const N: usize>(
        hex_str: &str,
        expected_len: usize,
    ) -> Result<[u8; N], &'static str> {
        if N != expected_len {
            return Err("Array size mismatch");
        }

        // Remove common hex prefixes
        let clean_hex = hex_str
            .trim_start_matches("0x")
            .trim_start_matches("0X")
            .trim_start_matches("\\x")
            .trim_start_matches("\\X");

        // Validate hex string length
        if clean_hex.len() != N * 2 {
            return Err("Invalid hex string length");
        }

        // Validate hex characters
        if !clean_hex.chars().all(|c| c.is_ascii_hexdigit()) {
            return Err("Invalid hex characters");
        }

        // Convert to bytes
        let mut bytes = [0u8; N];
        for (i, chunk) in clean_hex.as_bytes().chunks(2).enumerate() {
            if i >= N {
                break;
            }
            let hex_byte = std::str::from_utf8(chunk).map_err(|_| "Invalid UTF-8 in hex string")?;
            bytes[i] = u8::from_str_radix(hex_byte, 16).map_err(|_| "Invalid hex byte")?;
        }

        Ok(bytes)
    }

    /// Convert a hex string to an 8-byte array (for u64 values)
    ///
    /// ## Parameters
    /// - `hex_str`: Hex string (with or without "0x" prefix)
    ///
    /// ## Returns
    /// - `Ok([u8; 8])` if conversion succeeds
    /// - `Err(&str)` with error message if conversion fails
    ///
    /// ## Examples
    /// ```rust
    /// use miracle_api::utils::hex::hex_string_to_u64_bytes;
    ///
    /// let bytes = hex_string_to_u64_bytes("0x1234567890abcdef")?;
    /// assert_eq!(bytes, [0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef]);
    /// ```
    pub fn hex_string_to_u64_bytes(hex_str: &str) -> Result<[u8; 8], &'static str> {
        hex_string_to_bytes::<8>(hex_str, 8)
    }

    /// Convert a hex string to a 32-byte array (for hash values)
    ///
    /// ## Parameters
    /// - `hex_str`: Hex string (with or without "0x" prefix)
    ///
    /// ## Returns
    /// - `Ok([u8; 32])` if conversion succeeds
    /// - `Err(&str)` with error message if conversion fails
    ///
    /// ## Examples
    /// ```rust
    /// use miracle_api::utils::hex::hex_string_to_hash_bytes;
    ///
    /// let hash = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
    /// let bytes = hex_string_to_hash_bytes(hash)?;
    /// assert_eq!(bytes.len(), 32);
    /// ```
    pub fn hex_string_to_hash_bytes(hex_str: &str) -> Result<[u8; 32], &'static str> {
        hex_string_to_bytes::<32>(hex_str, 32)
    }

    /// Convert bytes to a hex string with "0x" prefix
    ///
    /// ## Parameters
    /// - `bytes`: Byte array or slice to convert
    ///
    /// ## Returns
    /// - Hex string with "0x" prefix
    ///
    /// ## Examples
    /// ```rust
    /// use miracle_api::utils::hex::bytes_to_hex_string;
    ///
    /// let bytes = [0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef];
    /// let hex = bytes_to_hex_string(&bytes);
    /// assert_eq!(hex, "0x1234567890abcdef");
    /// ```
    pub fn bytes_to_hex_string(bytes: &[u8]) -> String {
        format!("0x{}", hex::encode(bytes))
    }
}

/// Data conversion utilities for claim instructions
pub mod claim {
    use super::*;
    use crate::dmt::EpochClaimData;

    /// Create EpochClaimData from hex strings (from database queries)
    ///
    /// This function bridges the gap between database hex string output
    /// and on-chain byte array requirements.
    ///
    /// ## Parameters
    /// - `customer_reward_pool`: Hex string for customer reward pool
    /// - `merchant_reward_pool`: Hex string for merchant reward pool
    /// - `total_customer_activity`: Hex string for total customer activity
    /// - `total_merchant_activity`: Hex string for total merchant activity
    ///
    /// ## Returns
    /// - `Ok(EpochClaimData)` if conversion succeeds
    /// - `Err(&str)` with error message if conversion fails
    ///
    /// ## Examples
    /// ```rust
    /// use miracle_api::utils::claim::epoch_claim_data_from_hex;
    ///
    /// let epoch_data = epoch_claim_data_from_hex(
    ///     "0x1234567890abcdef",
    ///     "0xfedcba0987654321",
    ///     "0x0000000000000064",
    ///     "0x00000000000000c8"
    /// )?;
    /// ```
    pub fn epoch_claim_data_from_hex(
        customer_reward_pool: &str,
        merchant_reward_pool: &str,
        total_customer_activity: &str,
        total_merchant_activity: &str,
    ) -> Result<EpochClaimData, &'static str> {
        let customer_reward_pool_bytes = hex::hex_string_to_u64_bytes(customer_reward_pool)?;
        let merchant_reward_pool_bytes = hex::hex_string_to_u64_bytes(merchant_reward_pool)?;
        let total_customer_activity_bytes = hex::hex_string_to_u64_bytes(total_customer_activity)?;
        let total_merchant_activity_bytes = hex::hex_string_to_u64_bytes(total_merchant_activity)?;

        Ok(EpochClaimData {
            customer_reward_pool: customer_reward_pool_bytes,
            merchant_reward_pool: merchant_reward_pool_bytes,
            total_customer_activity: total_customer_activity_bytes,
            total_merchant_activity: total_merchant_activity_bytes,
        })
    }

    /// Create EpochClaimData from u64 values
    ///
    /// This function creates EpochClaimData from native u64 values,
    /// converting them to the byte arrays required by the on-chain program.
    ///
    /// ## Parameters
    /// - `customer_reward_pool`: u64 customer reward pool value
    /// - `merchant_reward_pool`: u64 merchant reward pool value
    /// - `total_customer_activity`: u64 total customer activity value
    /// - `total_merchant_activity`: u64 total merchant activity value
    ///
    /// ## Returns
    /// - `EpochClaimData` with proper byte arrays
    ///
    /// ## Examples
    /// ```rust
    /// use miracle_api::utils::claim::epoch_claim_data_from_u64;
    ///
    /// let epoch_data = epoch_claim_data_from_u64(
    ///     1234567890,
    ///     9876543210,
    ///     100,
    ///     200
    /// );
    /// ```
    pub fn epoch_claim_data_from_u64(
        customer_reward_pool: u64,
        merchant_reward_pool: u64,
        total_customer_activity: u64,
        total_merchant_activity: u64,
    ) -> EpochClaimData {
        EpochClaimData {
            customer_reward_pool: customer_reward_pool.to_le_bytes(),
            merchant_reward_pool: merchant_reward_pool.to_le_bytes(),
            total_customer_activity: total_customer_activity.to_le_bytes(),
            total_merchant_activity: total_merchant_activity.to_le_bytes(),
        }
    }
}