kabu_types_entities/
token.rs1use 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}