kabu_types_entities/
token.rs

1use std::cmp::Ordering;
2use std::hash::{Hash, Hasher};
3use std::ops::{Add, Div, Mul, Neg};
4use std::string::ToString;
5use std::sync::Arc;
6use std::sync::RwLock;
7
8use alloy_primitives::utils::Unit;
9use alloy_primitives::{Address, I256, U256};
10
11const ONE_ETHER: U256 = Unit::ETHER.wei_const();
12
13#[derive(Clone, Debug, Default)]
14pub struct Token {
15    address: Address,
16    weth: bool,
17    basic: bool,
18    middle: bool,
19    decimals: u8,
20    name: Option<String>,
21    symbol: Option<String>,
22    eth_price: Arc<RwLock<Option<U256>>>,
23}
24
25pub type TokenWrapper = Arc<Token>;
26
27impl Hash for Token {
28    #[inline]
29    fn hash<H: Hasher>(&self, state: &mut H) {
30        self.address.hash(state)
31    }
32}
33
34impl PartialEq for Token {
35    #[inline]
36    fn eq(&self, other: &Self) -> bool {
37        self.address == other.get_address()
38    }
39}
40
41impl Eq for Token {}
42
43impl Ord for Token {
44    #[inline]
45    fn cmp(&self, other: &Self) -> Ordering {
46        self.address.cmp(&other.get_address())
47    }
48}
49
50impl PartialOrd for Token {
51    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
52        Some(self.cmp(other))
53    }
54}
55
56impl Token {
57    #[inline]
58    pub fn new<T: Into<Address>>(address: T) -> Token {
59        Self::new_with_data(address, None, None, Some(18), false, false)
60    }
61
62    #[inline]
63    pub fn zero() -> Token {
64        Self::new_with_data(Address::default(), None, None, Some(18), false, false)
65    }
66
67    pub fn new_with_data<T: Into<Address>>(
68        address: T,
69        symbol: Option<String>,
70        name: Option<String>,
71        decimals: Option<u8>,
72        basic: bool,
73        middle: bool,
74    ) -> Token {
75        Token {
76            address: address.into(),
77            symbol,
78            name,
79            decimals: decimals.unwrap_or(18),
80            basic,
81            middle,
82            weth: false,
83            eth_price: Arc::new(RwLock::new(None)),
84        }
85    }
86
87    #[inline]
88    pub fn get_symbol(&self) -> String {
89        self.symbol.clone().unwrap_or(self.address.to_string())
90    }
91
92    #[inline]
93    pub fn get_name(&self) -> String {
94        self.name.clone().unwrap_or(self.address.to_string())
95    }
96
97    #[inline]
98    pub fn get_decimals(&self) -> u8 {
99        self.decimals
100    }
101
102    #[inline]
103    pub fn get_exp(&self) -> U256 {
104        if self.decimals == 18 {
105            ONE_ETHER
106        } else {
107            U256::from(10).pow(U256::from(self.decimals))
108        }
109    }
110
111    #[inline]
112    pub fn get_address(&self) -> Address {
113        self.address
114    }
115
116    #[inline]
117    pub fn is_weth(&self) -> bool {
118        self.weth
119    }
120
121    #[inline]
122    pub fn is_basic(&self) -> bool {
123        self.basic
124    }
125
126    #[inline]
127    pub fn is_middle(&self) -> bool {
128        self.middle
129    }
130
131    pub fn set_basic(&mut self) -> &mut Self {
132        self.basic = true;
133        self
134    }
135
136    pub fn set_middle(&mut self) -> &mut Self {
137        self.middle = true;
138        self
139    }
140
141    pub fn to_float(&self, value: U256) -> f64 {
142        if self.decimals == 0 {
143            0f64
144        } else {
145            let divider = self.get_exp();
146            let ret = value.div_rem(divider);
147
148            let div = u64::try_from(ret.0);
149            let rem = u64::try_from(ret.1);
150
151            if div.is_err() || rem.is_err() {
152                0f64
153            } else {
154                div.unwrap_or_default() as f64 + ((rem.unwrap_or_default() as f64) / (10u64.pow(self.decimals as u32) as f64))
155            }
156        }
157    }
158
159    pub fn to_float_sign(&self, value: I256) -> f64 {
160        let r: U256 = if value.is_positive() { value.into_raw() } else { value.neg().into_raw() };
161        let f = self.to_float(r);
162        if value.is_positive() {
163            f
164        } else {
165            -f
166        }
167    }
168
169    pub fn from_float(&self, value: f64) -> U256 {
170        let multiplier = U256::from(value as i64);
171        let modulus = U256::from(((value - value.round()) * (10 ^ self.decimals as i64) as f64) as u64);
172        multiplier.mul(U256::from(10).pow(U256::from(self.decimals))).add(modulus)
173    }
174
175    pub fn get_eth_price(&self) -> Option<U256> {
176        if self.is_weth() {
177            Some(ONE_ETHER)
178        } else {
179            match self.eth_price.read() {
180                Ok(x) => *x,
181                _ => None,
182            }
183        }
184    }
185
186    pub fn set_eth_price(&self, price: Option<U256>) {
187        if let Ok(mut x) = self.eth_price.write() {
188            *x = price;
189        }
190    }
191
192    pub fn calc_eth_value(&self, value: U256) -> Option<U256> {
193        self.get_eth_price().map(|x| value.mul(ONE_ETHER).div(x))
194    }
195
196    pub fn calc_token_value_from_eth(&self, eth_value: U256) -> Option<U256> {
197        let x = self.get_eth_price();
198        x.map(|x| eth_value.mul(x).div(ONE_ETHER))
199    }
200}
201
202#[cfg(test)]
203mod test {
204    use super::*;
205    use kabu_defi_address_book::TokenAddressEth;
206
207    #[test]
208    fn test_to_float() {
209        let weth_token = Token::new_with_data(TokenAddressEth::WETH, Some("WETH".to_string()), None, Some(18), true, false);
210        let usdc_token = Token::new_with_data(TokenAddressEth::USDC, Some("USDC".to_string()), None, Some(6), false, false);
211        let usdt_token = Token::new_with_data(TokenAddressEth::USDT, Some("USDT".to_string()), None, Some(6), false, false);
212        let dai_token = Token::new_with_data(TokenAddressEth::DAI, Some("DAI".to_string()), None, Some(18), false, false);
213        let wbtc_token = Token::new_with_data(TokenAddressEth::WBTC, Some("WBTC".to_string()), None, Some(8), false, false);
214
215        let one_ether = U256::from(10).pow(U256::from(15));
216
217        println!("{}", weth_token.to_float(one_ether));
218    }
219}