Skip to main content

pm_types/
pair.rs

1use std::str::FromStr;
2
3use crate::currency::Currency;
4use miden_client::{Felt, Word, ZERO};
5
6#[derive(Debug, Clone)]
7pub struct Pair {
8    pub base: Currency,
9    pub quote: Currency,
10}
11
12impl Pair {
13    pub fn new(base: Currency, quote: Currency) -> Self {
14        Self { base, quote }
15    }
16
17    pub fn encode(&self) -> Option<u32> {
18        let base_encoded = self.base.encode()?;
19        let quote_encoded = self.quote.encode()?;
20        Some(base_encoded | (quote_encoded << 15))
21    }
22
23    pub fn to_word(&self) -> Word {
24        [ZERO, ZERO, ZERO, self.try_into().unwrap()]
25    }
26}
27
28impl TryFrom<Pair> for Felt {
29    type Error = anyhow::Error;
30
31    fn try_from(value: Pair) -> anyhow::Result<Self> {
32        let encoded = value
33            .encode()
34            .ok_or_else(|| anyhow::anyhow!("Invalid asset pair format"))?;
35
36        let value = u64::from(encoded);
37        Ok(Felt::new(value))
38    }
39}
40
41impl TryFrom<&Pair> for Felt {
42    type Error = anyhow::Error;
43
44    fn try_from(value: &Pair) -> anyhow::Result<Self> {
45        let encoded = value
46            .encode()
47            .ok_or_else(|| anyhow::anyhow!("Invalid asset pair format"))?;
48
49        let value = u64::from(encoded);
50        Ok(Felt::new(value))
51    }
52}
53
54impl FromStr for Pair {
55    type Err = anyhow::Error;
56
57    fn from_str(s: &str) -> Result<Self, Self::Err> {
58        let parts: Vec<&str> = s.split('/').collect();
59
60        if parts.len() != 2 {
61            return Err(anyhow::anyhow!("Invalid pair format. Expected BASE/QUOTE"));
62        }
63
64        let base = Currency::from_str(parts[0])?;
65        let quote = Currency::from_str(parts[1])?;
66
67        Ok(Pair::new(base, quote))
68    }
69}
70
71impl From<Felt> for Pair {
72    fn from(felt: Felt) -> Self {
73        // Convert Felt to u32
74        let value = felt.as_int() as u32;
75
76        // Extract base and quote portions
77        let base_encoded = value & 0x7FFF; // Lower 15 bits
78        let quote_encoded = (value >> 15) & 0x7FFF; // Upper 15 bits
79
80        // Decode each currency string
81        let base = decode_currency(base_encoded).unwrap();
82        let quote = decode_currency(quote_encoded).unwrap();
83
84        // Create currencies
85        let base_currency = Currency::new(&base).unwrap();
86        let quote_currency = Currency::new(&quote).unwrap();
87
88        Pair::new(base_currency, quote_currency)
89    }
90}
91
92fn decode_currency(encoded: u32) -> Option<String> {
93    let mut result = String::new();
94    let mut remaining = encoded;
95
96    // Each character uses 5 bits (A-Z = 0-25)
97    for _ in 0..3 {
98        // Assuming max 3 characters per currency
99        if remaining == 0 {
100            break;
101        }
102
103        let char_value = remaining & 0x1F; // Get lowest 5 bits
104        if char_value >= 26 {
105            // Invalid character value
106            return None;
107        }
108
109        let decoded_char = char::from_u32('A' as u32 + char_value)?;
110        result.push(decoded_char);
111
112        remaining >>= 5;
113    }
114
115    if result.is_empty() {
116        None
117    } else {
118        Some(result)
119    }
120}
121
122impl std::fmt::Display for Pair {
123    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124        write!(f, "{}/{}", self.base.0, self.quote.0)
125    }
126}