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 let value = felt.as_int() as u32;
75
76 let base_encoded = value & 0x7FFF; let quote_encoded = (value >> 15) & 0x7FFF; let base = decode_currency(base_encoded).unwrap();
82 let quote = decode_currency(quote_encoded).unwrap();
83
84 let base_currency = Currency::new(&base).unwrap();
86 let quote_currency = Currency::new("e).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 for _ in 0..3 {
98 if remaining == 0 {
100 break;
101 }
102
103 let char_value = remaining & 0x1F; if char_value >= 26 {
105 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}