miracle_api/
utils.rs

1//! Utility functions for the Miracle API/SDK
2//!
3//! This module provides common utilities for data conversion, validation,
4//! and other helper functions used across the Miracle ecosystem.
5//!
6//! ## Key Features
7//!
8//! ### Hex String Conversion
9//! - Convert hex strings to byte arrays for on-chain consumption
10//! - Support for common hex prefixes (0x, \\x)
11//! - Type-safe conversions with compile-time size checking
12//!
13//! ### Claim Instruction Utilities
14//! - Convert database hex strings to `EpochClaimData` structs
15//! - Bridge between off-chain database queries and on-chain instruction data
16//! - Support for both hex string and native u64 inputs
17//!
18//! ## Usage Examples
19//!
20//! ```rust
21//! use miracle_api::utils::{hex, claim};
22//!
23//! // Convert hex string to byte array
24//! let bytes: [u8; 8] = hex::hex_string_to_u64_bytes("0x1234567890abcdef")?;
25//!
26//! // Create EpochClaimData from hex strings (from database)
27//! let epoch_data = claim::epoch_claim_data_from_hex(
28//!     "0x1234567890abcdef",
29//!     "0xfedcba0987654321",
30//!     "0x0000000000000064",
31//!     "0x00000000000000c8"
32//! )?;
33//!
34//! // Create EpochClaimData from native u64 values
35//! let epoch_data = claim::epoch_claim_data_from_u64(1234567890, 9876543210, 100, 200);
36//! ```
37//!
38//! ## Key Features
39//!
40//! ### Hex String Conversion
41//! - Convert hex strings to byte arrays for on-chain consumption
42//! - Support for common hex prefixes (0x, \\x)
43//! - Type-safe conversions with compile-time size checking
44//!
45//! ### Claim Instruction Utilities
46//! - Convert database hex strings to `EpochClaimData` structs
47//! - Bridge between off-chain database queries and on-chain instruction data
48//! - Support for both hex string and native u64 inputs
49//!
50//! ## Usage Examples
51//!
52//! ```rust
53//! use miracle_api::utils::{hex, claim};
54//!
55//! // Convert hex string to byte array
56//! let bytes: [u8; 8] = hex::hex_string_to_u64_bytes("0x1234567890abcdef")?;
57//!
58//! // Create EpochClaimData from hex strings (from database)
59//! let epoch_data = claim::epoch_claim_data_from_hex(
60//!     "0x1234567890abcdef",
61//!     "0xfedcba0987654321",
62//!     "0x0000000000000064",
63//!     "0x00000000000000c8"
64//! )?;
65//!
66//! // Create EpochClaimData from native u64 values
67//! let epoch_data = claim::epoch_claim_data_from_u64(1234567890, 9876543210, 100, 200);
68//! ```
69
70/// Hex string conversion utilities
71pub mod hex {
72    /// Convert a hex string to a byte array of specified length
73    ///
74    /// ## Parameters
75    /// - `hex_str`: Hex string (with or without "0x" prefix)
76    /// - `expected_len`: Expected length of the resulting byte array
77    ///
78    /// ## Returns
79    /// - `Ok([u8; N])` if conversion succeeds
80    /// - `Err(&str)` with error message if conversion fails
81    ///
82    /// ## Examples
83    /// ```rust
84    /// use miracle_api::utils::hex::hex_string_to_bytes;
85    ///
86    /// // Convert to 8-byte array
87    /// let bytes: [u8; 8] = hex_string_to_bytes("0x1234567890abcdef", 8)?;
88    /// assert_eq!(bytes, [0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef]);
89    ///
90    /// // Convert to 32-byte array
91    /// let bytes: [u8; 32] = hex_string_to_bytes("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", 32)?;
92    /// ```
93    pub fn hex_string_to_bytes<const N: usize>(
94        hex_str: &str,
95        expected_len: usize,
96    ) -> Result<[u8; N], &'static str> {
97        if N != expected_len {
98            return Err("Array size mismatch");
99        }
100
101        // Remove common hex prefixes
102        let clean_hex = hex_str
103            .trim_start_matches("0x")
104            .trim_start_matches("0X")
105            .trim_start_matches("\\x")
106            .trim_start_matches("\\X");
107
108        // Validate hex string length
109        if clean_hex.len() != N * 2 {
110            return Err("Invalid hex string length");
111        }
112
113        // Validate hex characters
114        if !clean_hex.chars().all(|c| c.is_ascii_hexdigit()) {
115            return Err("Invalid hex characters");
116        }
117
118        // Convert to bytes
119        let mut bytes = [0u8; N];
120        for (i, chunk) in clean_hex.as_bytes().chunks(2).enumerate() {
121            if i >= N {
122                break;
123            }
124            let hex_byte = std::str::from_utf8(chunk).map_err(|_| "Invalid UTF-8 in hex string")?;
125            bytes[i] = u8::from_str_radix(hex_byte, 16).map_err(|_| "Invalid hex byte")?;
126        }
127
128        Ok(bytes)
129    }
130
131    /// Convert a hex string to an 8-byte array (for u64 values)
132    ///
133    /// ## Parameters
134    /// - `hex_str`: Hex string (with or without "0x" prefix)
135    ///
136    /// ## Returns
137    /// - `Ok([u8; 8])` if conversion succeeds
138    /// - `Err(&str)` with error message if conversion fails
139    ///
140    /// ## Examples
141    /// ```rust
142    /// use miracle_api::utils::hex::hex_string_to_u64_bytes;
143    ///
144    /// let bytes = hex_string_to_u64_bytes("0x1234567890abcdef")?;
145    /// assert_eq!(bytes, [0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef]);
146    /// ```
147    pub fn hex_string_to_u64_bytes(hex_str: &str) -> Result<[u8; 8], &'static str> {
148        hex_string_to_bytes::<8>(hex_str, 8)
149    }
150
151    /// Convert a hex string to a 32-byte array (for hash values)
152    ///
153    /// ## Parameters
154    /// - `hex_str`: Hex string (with or without "0x" prefix)
155    ///
156    /// ## Returns
157    /// - `Ok([u8; 32])` if conversion succeeds
158    /// - `Err(&str)` with error message if conversion fails
159    ///
160    /// ## Examples
161    /// ```rust
162    /// use miracle_api::utils::hex::hex_string_to_hash_bytes;
163    ///
164    /// let hash = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
165    /// let bytes = hex_string_to_hash_bytes(hash)?;
166    /// assert_eq!(bytes.len(), 32);
167    /// ```
168    pub fn hex_string_to_hash_bytes(hex_str: &str) -> Result<[u8; 32], &'static str> {
169        hex_string_to_bytes::<32>(hex_str, 32)
170    }
171
172    /// Convert bytes to a hex string with "0x" prefix
173    ///
174    /// ## Parameters
175    /// - `bytes`: Byte array or slice to convert
176    ///
177    /// ## Returns
178    /// - Hex string with "0x" prefix
179    ///
180    /// ## Examples
181    /// ```rust
182    /// use miracle_api::utils::hex::bytes_to_hex_string;
183    ///
184    /// let bytes = [0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef];
185    /// let hex = bytes_to_hex_string(&bytes);
186    /// assert_eq!(hex, "0x1234567890abcdef");
187    /// ```
188    pub fn bytes_to_hex_string(bytes: &[u8]) -> String {
189        format!("0x{}", hex::encode(bytes))
190    }
191}
192
193/// Data conversion utilities for claim instructions
194pub mod claim {
195    use super::*;
196    use crate::dmt::EpochClaimData;
197
198    /// Create EpochClaimData from hex strings (from database queries)
199    ///
200    /// This function bridges the gap between database hex string output
201    /// and on-chain byte array requirements.
202    ///
203    /// ## Parameters
204    /// - `customer_reward_pool`: Hex string for customer reward pool
205    /// - `merchant_reward_pool`: Hex string for merchant reward pool
206    /// - `total_customer_activity`: Hex string for total customer activity
207    /// - `total_merchant_activity`: Hex string for total merchant activity
208    ///
209    /// ## Returns
210    /// - `Ok(EpochClaimData)` if conversion succeeds
211    /// - `Err(&str)` with error message if conversion fails
212    ///
213    /// ## Examples
214    /// ```rust
215    /// use miracle_api::utils::claim::epoch_claim_data_from_hex;
216    ///
217    /// let epoch_data = epoch_claim_data_from_hex(
218    ///     "0x1234567890abcdef",
219    ///     "0xfedcba0987654321",
220    ///     "0x0000000000000064",
221    ///     "0x00000000000000c8"
222    /// )?;
223    /// ```
224    pub fn epoch_claim_data_from_hex(
225        customer_reward_pool: &str,
226        merchant_reward_pool: &str,
227        total_customer_activity: &str,
228        total_merchant_activity: &str,
229    ) -> Result<EpochClaimData, &'static str> {
230        let customer_reward_pool_bytes = hex::hex_string_to_u64_bytes(customer_reward_pool)?;
231        let merchant_reward_pool_bytes = hex::hex_string_to_u64_bytes(merchant_reward_pool)?;
232        let total_customer_activity_bytes = hex::hex_string_to_u64_bytes(total_customer_activity)?;
233        let total_merchant_activity_bytes = hex::hex_string_to_u64_bytes(total_merchant_activity)?;
234
235        Ok(EpochClaimData {
236            customer_reward_pool: customer_reward_pool_bytes,
237            merchant_reward_pool: merchant_reward_pool_bytes,
238            total_customer_activity: total_customer_activity_bytes,
239            total_merchant_activity: total_merchant_activity_bytes,
240        })
241    }
242
243    /// Create EpochClaimData from u64 values
244    ///
245    /// This function creates EpochClaimData from native u64 values,
246    /// converting them to the byte arrays required by the on-chain program.
247    ///
248    /// ## Parameters
249    /// - `customer_reward_pool`: u64 customer reward pool value
250    /// - `merchant_reward_pool`: u64 merchant reward pool value
251    /// - `total_customer_activity`: u64 total customer activity value
252    /// - `total_merchant_activity`: u64 total merchant activity value
253    ///
254    /// ## Returns
255    /// - `EpochClaimData` with proper byte arrays
256    ///
257    /// ## Examples
258    /// ```rust
259    /// use miracle_api::utils::claim::epoch_claim_data_from_u64;
260    ///
261    /// let epoch_data = epoch_claim_data_from_u64(
262    ///     1234567890,
263    ///     9876543210,
264    ///     100,
265    ///     200
266    /// );
267    /// ```
268    pub fn epoch_claim_data_from_u64(
269        customer_reward_pool: u64,
270        merchant_reward_pool: u64,
271        total_customer_activity: u64,
272        total_merchant_activity: u64,
273    ) -> EpochClaimData {
274        EpochClaimData {
275            customer_reward_pool: customer_reward_pool.to_le_bytes(),
276            merchant_reward_pool: merchant_reward_pool.to_le_bytes(),
277            total_customer_activity: total_customer_activity.to_le_bytes(),
278            total_merchant_activity: total_merchant_activity.to_le_bytes(),
279        }
280    }
281}