Skip to main content

haqq_grpc/
lib.rs

1// include!("gen/mod.rs");
2
3#[allow(clippy::all)]
4mod gen;
5pub use gen::*;
6
7pub mod prelude {
8    use core::fmt;
9    use std::str::FromStr;
10
11    use crate::cosmos::base::v1beta1::DecCoin;
12
13    use super::*;
14    use alloy_primitives::U256;
15
16    use cosmos::base::v1beta1::Coin;
17    use tonic::metadata::{Ascii, MetadataValue};
18
19    pub const HEIGHT_METADATA_KEY: &str = "x-cosmos-block-height";
20
21    pub trait CoinExt<'a> {
22        fn get_denom(&'a self, denom: impl Into<String>) -> Option<&'a str>;
23    }
24
25    // impl<'a, I> CoinOptionExt<'a> for I
26    // where
27    //     I: AsRef<[Coin]>,
28    // {
29    //     fn get_denom(&'a self, denom: impl Into<String>) -> Option<&'a Coin> {
30    //         let denom = denom.into();
31    //         self.as_ref().iter().find(move |c| c.denom == denom)
32    //     }
33    // }
34
35    impl<'a> CoinExt<'a> for Option<Coin> {
36        fn get_denom(&'a self, denom: impl Into<String>) -> Option<&'a str> {
37            self.as_ref()
38                .filter(|c| c.denom == denom.into())
39                .map(|c| c.amount.as_str())
40        }
41    }
42
43    impl<'a> CoinExt<'a> for Vec<Coin> {
44        fn get_denom(&'a self, denom: impl Into<String>) -> Option<&'a str> {
45            let denom = denom.into();
46            for coin in self.iter() {
47                if coin.denom == denom {
48                    return Some(coin.amount.as_str());
49                }
50            }
51            None
52        }
53    }
54
55    impl<'a> CoinExt<'a> for Vec<DecCoin> {
56        fn get_denom(&'a self, denom: impl Into<String>) -> Option<&'a str> {
57            let denom = denom.into();
58            for coin in self.iter() {
59                if coin.denom == denom {
60                    return Some(coin.amount.as_str());
61                }
62            }
63            None
64        }
65    }
66
67    pub trait AmountExt {
68        fn to_u256(&self) -> Result<U256, alloy_primitives::ruint::ParseError>;
69    }
70
71    impl AmountExt for String {
72        fn to_u256(&self) -> Result<U256, alloy_primitives::ruint::ParseError> {
73            U256::from_str(self)
74        }
75    }
76
77    impl<'a> AmountExt for &'a str {
78        fn to_u256(&self) -> Result<U256, alloy_primitives::ruint::ParseError> {
79            U256::from_str(self)
80        }
81    }
82
83    impl AmountExt for Coin {
84        fn to_u256(&self) -> Result<U256, alloy_primitives::ruint::ParseError> {
85            self.amount.to_u256()
86        }
87    }
88
89    #[derive(Clone, Debug, PartialEq)]
90    #[allow(non_camel_case_types)]
91    pub enum Denom {
92        aISLM,
93        ISLM,
94        Other(String),
95    }
96
97    impl Denom {
98        pub fn conversion_pair(&self) -> Option<(Self, U256, U256)> {
99            match self {
100                Self::aISLM => Some((
101                    Self::ISLM,
102                    U256::from(1),
103                    U256::pow(U256::from(10), U256::from(18)),
104                )),
105                Self::ISLM => Some((
106                    Self::aISLM,
107                    U256::pow(U256::from(10), U256::from(18)),
108                    U256::from(1),
109                )),
110                Self::Other(_) => None,
111            }
112        }
113    }
114
115    impl fmt::Display for Denom {
116        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117            match self {
118                Self::aISLM => write!(f, "aISLM"),
119                Self::ISLM => write!(f, "ISLM"),
120                Denom::Other(d) => write!(f, "{}", d),
121            }
122        }
123    }
124
125    pub trait CosmosMessage {
126        fn with_height(self, height: MetadataValue<Ascii>) -> tonic::Request<Self>
127        where
128            Self: Sized,
129        {
130            let mut req = tonic::Request::new(self);
131            req.metadata_mut().insert(HEIGHT_METADATA_KEY, height);
132            req
133        }
134
135        fn with_height_num(self, height: u64) -> tonic::Request<Self>
136        where
137            Self: Sized,
138        {
139            let height = MetadataValue::from_str(&height.to_string()).unwrap();
140            self.with_height(height)
141        }
142    }
143
144    impl<T> CosmosMessage for T {}
145
146    pub trait CosmosResponse<T> {
147        fn get_height(&self) -> Option<&MetadataValue<Ascii>>;
148    }
149
150    impl<T> CosmosResponse<T> for tonic::Response<T> {
151        fn get_height(&self) -> Option<&MetadataValue<Ascii>> {
152            self.metadata().get(HEIGHT_METADATA_KEY)
153        }
154    }
155}
156
157#[cfg(test)]
158mod test {
159    use super::cosmos::base::v1beta1::Coin;
160    use super::prelude::*;
161    use alloy_primitives::U256;
162    use std::str::FromStr;
163
164    #[test]
165    fn denom() {
166        let oneislm_in_aislm =
167            U256::from_str(&format!("1{}", (0..18).map(|_| "0").collect::<String>())).unwrap();
168        let oneislm_in_islm = U256::from_str("1").unwrap();
169
170        let (denom, mul, div) = Denom::aISLM.conversion_pair().unwrap();
171        assert_eq!(denom, Denom::ISLM);
172        assert_eq!(oneislm_in_islm, oneislm_in_aislm * mul / div);
173
174        let (denom, mul, div) = Denom::ISLM.conversion_pair().unwrap();
175        assert_eq!(denom, Denom::aISLM);
176        assert_eq!(oneislm_in_aislm, oneislm_in_islm * mul / div);
177    }
178
179    #[test]
180    fn coin_option_ext() {
181        let coin = Coin {
182            denom: "aISLM".to_string(),
183            amount: "0".to_string(),
184        };
185
186        assert!(vec![coin.clone()].get_denom("aISLM").is_some());
187        assert_eq!(Some(coin).get_denom("aISLM"), Some("0"));
188    }
189
190    #[test]
191    fn coin_ext() {
192        let mut coin = Coin {
193            denom: "ISLM".to_string(),
194            amount: "1.2345".to_string(),
195        };
196
197        assert!(coin.to_u256().is_err());
198
199        coin.amount = "12345".to_string();
200        assert_eq!(coin.to_u256().unwrap(), U256::from_str("12345").unwrap());
201
202        assert_eq!(
203            "12345".to_string().to_u256().unwrap(),
204            U256::from_str("12345").unwrap()
205        );
206        assert_eq!("12345".to_u256().unwrap(), U256::from_str("12345").unwrap());
207    }
208}