kabu_types_market/
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 { ONE_ETHER } else { U256::from(10).pow(U256::from(self.decimals)) }
105    }
106
107    #[inline]
108    pub fn get_address(&self) -> Address {
109        self.address
110    }
111
112    #[inline]
113    pub fn is_weth(&self) -> bool {
114        self.weth
115    }
116
117    #[inline]
118    pub fn is_basic(&self) -> bool {
119        self.basic
120    }
121
122    #[inline]
123    pub fn is_middle(&self) -> bool {
124        self.middle
125    }
126
127    pub fn set_basic(&mut self) -> &mut Self {
128        self.basic = true;
129        self
130    }
131
132    pub fn set_middle(&mut self) -> &mut Self {
133        self.middle = true;
134        self
135    }
136
137    pub fn to_float(&self, value: U256) -> f64 {
138        if self.decimals == 0 {
139            0f64
140        } else {
141            let divider = self.get_exp();
142            let ret = value.div_rem(divider);
143
144            let div = u64::try_from(ret.0);
145            let rem = u64::try_from(ret.1);
146
147            if div.is_err() || rem.is_err() {
148                0f64
149            } else {
150                div.unwrap_or_default() as f64 + ((rem.unwrap_or_default() as f64) / (10u64.pow(self.decimals as u32) as f64))
151            }
152        }
153    }
154
155    pub fn to_float_sign(&self, value: I256) -> f64 {
156        let r: U256 = if value.is_positive() { value.into_raw() } else { value.neg().into_raw() };
157        let f = self.to_float(r);
158        if value.is_positive() { f } else { -f }
159    }
160
161    pub fn from_float(&self, value: f64) -> U256 {
162        let multiplier = U256::from(value as i64);
163        let modulus = U256::from(((value - value.round()) * (10 ^ self.decimals as i64) as f64) as u64);
164        multiplier.mul(U256::from(10).pow(U256::from(self.decimals))).add(modulus)
165    }
166
167    pub fn get_eth_price(&self) -> Option<U256> {
168        if self.is_weth() {
169            Some(ONE_ETHER)
170        } else {
171            match self.eth_price.read() {
172                Ok(x) => *x,
173                _ => None,
174            }
175        }
176    }
177
178    pub fn set_eth_price(&self, price: Option<U256>) {
179        if let Ok(mut x) = self.eth_price.write() {
180            *x = price;
181        }
182    }
183
184    pub fn calc_eth_value(&self, value: U256) -> Option<U256> {
185        self.get_eth_price().map(|x| value.mul(ONE_ETHER).div(x))
186    }
187
188    pub fn calc_token_value_from_eth(&self, eth_value: U256) -> Option<U256> {
189        let x = self.get_eth_price();
190        x.map(|x| eth_value.mul(x).div(ONE_ETHER))
191    }
192}
193
194#[cfg(test)]
195mod test {
196    use super::*;
197    use kabu_defi_address_book::TokenAddressEth;
198
199    #[test]
200    fn test_to_float() {
201        let weth_token = Token::new_with_data(TokenAddressEth::WETH, Some("WETH".to_string()), None, Some(18), true, false);
202        let _usdc_token = Token::new_with_data(TokenAddressEth::USDC, Some("USDC".to_string()), None, Some(6), false, false);
203        let _usdt_token = Token::new_with_data(TokenAddressEth::USDT, Some("USDT".to_string()), None, Some(6), false, false);
204        let _dai_token = Token::new_with_data(TokenAddressEth::DAI, Some("DAI".to_string()), None, Some(18), false, false);
205        let _wbtc_token = Token::new_with_data(TokenAddressEth::WBTC, Some("WBTC".to_string()), None, Some(8), false, false);
206
207        let one_ether = U256::from(10).pow(U256::from(15));
208
209        println!("{}", weth_token.to_float(one_ether));
210    }
211}