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}