kabu_types_market/
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 { 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}