1#[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> 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}