1use std::{
2 collections::HashMap,
3 hash::{Hash, Hasher},
4};
5
6use num_bigint::BigUint;
7use serde::{Deserialize, Serialize};
8
9use super::{Address, Balance};
10use crate::{dto::ResponseToken, models::Chain, traits::TokenOwnerFinding, Bytes};
11
12pub type TransferCost = u64;
14
15pub type TransferTax = u64;
17
18#[derive(Debug, Clone, Deserialize, Serialize, Eq)]
19pub struct Token {
20 pub address: Bytes,
21 pub symbol: String,
22 pub decimals: u32,
23 pub tax: TransferTax,
24 pub gas: Vec<Option<TransferCost>>,
25 pub chain: Chain,
26 pub quality: u32,
34}
35
36impl Token {
37 pub fn new(
38 address: &Bytes,
39 symbol: &str,
40 decimals: u32,
41 tax: u64,
42 gas: &[Option<u64>],
43 chain: Chain,
44 quality: u32,
45 ) -> Self {
46 Self {
47 address: address.clone(),
48 symbol: symbol.to_string(),
49 decimals,
50 tax,
51 gas: gas.to_owned(),
52 chain,
53 quality,
54 }
55 }
56
57 pub fn one(&self) -> BigUint {
63 BigUint::from((1.0 * 10f64.powi(self.decimals as i32)) as u128)
64 }
65}
66
67impl PartialOrd for Token {
68 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
69 self.address.partial_cmp(&other.address)
70 }
71}
72
73impl PartialEq for Token {
74 fn eq(&self, other: &Self) -> bool {
75 self.address == other.address
76 }
77}
78
79impl Hash for Token {
80 fn hash<H: Hasher>(&self, state: &mut H) {
81 self.address.hash(state);
82 }
83}
84
85impl TryFrom<ResponseToken> for Token {
86 type Error = ();
87
88 fn try_from(value: ResponseToken) -> Result<Self, Self::Error> {
89 Ok(Self {
90 address: value.address,
91 decimals: value.decimals,
92 symbol: value.symbol.to_string(),
93 gas: value.gas,
94 chain: Chain::from(value.chain),
95 tax: value.tax,
96 quality: value.quality,
97 })
98 }
99}
100
101#[derive(Debug, Clone, Eq, PartialEq)]
111pub enum TokenQuality {
112 Good,
113 Bad { reason: String },
114}
115
116impl TokenQuality {
117 pub fn is_good(&self) -> bool {
118 matches!(self, Self::Good { .. })
119 }
120
121 pub fn bad(reason: impl ToString) -> Self {
122 Self::Bad { reason: reason.to_string() }
123 }
124}
125
126#[derive(Debug)]
139pub struct TokenOwnerStore {
140 values: HashMap<Address, (Address, Balance)>,
141}
142
143impl TokenOwnerStore {
144 pub fn new(values: HashMap<Address, (Address, Balance)>) -> Self {
145 TokenOwnerStore { values }
146 }
147}
148
149#[async_trait::async_trait]
150impl TokenOwnerFinding for TokenOwnerStore {
151 async fn find_owner(
152 &self,
153 token: Address,
154 _min_balance: Balance,
155 ) -> Result<Option<(Address, Balance)>, String> {
156 Ok(self.values.get(&token).cloned())
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use std::str::FromStr;
163
164 use super::*;
165
166 #[test]
167 fn test_constructor() {
168 let token = Token::new(
169 &Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(),
170 "USDC",
171 6,
172 1000,
173 &[Some(1000u64)],
174 Chain::Ethereum,
175 100,
176 );
177
178 assert_eq!(token.symbol, "USDC");
179 assert_eq!(token.decimals, 6);
180 assert_eq!(
181 format!("{token_address:#x}", token_address = token.address),
182 "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
183 );
184 }
185
186 #[test]
187 fn test_cmp() {
188 let usdc = Token::new(
189 &Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(),
190 "USDC",
191 6,
192 1000,
193 &[Some(1000u64)],
194 Chain::Ethereum,
195 100,
196 );
197 let usdc2 = Token::new(
198 &Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(),
199 "USDC2",
200 6,
201 1000,
202 &[Some(1000u64)],
203 Chain::Ethereum,
204 100,
205 );
206 let weth = Token::new(
207 &Bytes::from_str("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2").unwrap(),
208 "WETH",
209 18,
210 1000,
211 &[Some(1000u64)],
212 Chain::Ethereum,
213 100,
214 );
215
216 assert!(usdc < weth);
217 assert_eq!(usdc, usdc2);
218 }
219
220 #[test]
221 fn test_one() {
222 let usdc = Token::new(
223 &Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(),
224 "USDC",
225 6,
226 1000,
227 &[Some(1000u64)],
228 Chain::Ethereum,
229 100,
230 );
231
232 assert_eq!(usdc.one(), BigUint::from(1000000u64));
233 }
234}