master

Master Thesis code
git clone git://popovic.xyz/master.git
Log | Files | Refs | README | LICENSE

tokens.rs (6020B)


      1 #![allow(dead_code)]
      2 #![allow(unused_variables)]
      3 #![allow(unused_imports)]
      4 #![allow(unused_mut)]
      5 
      6 use std::{
      7     collections::BTreeMap,
      8     fs::OpenOptions,
      9     path::Path,
     10     str::FromStr
     11 };
     12 use alloy::{
     13     primitives::Address,
     14     providers::RootProvider,
     15     transports::{http::{Client, Http}, BoxTransport},
     16 };
     17 use indicatif::{ProgressBar, ProgressStyle};
     18 use anyhow::{anyhow, Result};
     19 use csv::StringRecord;
     20 use log::info;
     21 
     22 use crate::{interfaces::IERC20, pools::Pool};
     23 
     24 
     25 #[derive(Debug, Clone)]
     26 pub struct Token {
     27     pub id: i64,
     28     pub address: Address,
     29     pub name: String,
     30     pub symbol: String,
     31     pub decimals: u8
     32 }
     33 
     34 impl From<StringRecord> for Token {
     35     fn from(record: StringRecord) -> Self {
     36         Self {
     37             id: record.get(0).unwrap().parse().unwrap(),
     38             address: Address::from_str(record.get(1).unwrap()).unwrap(),
     39             name: String::from(record.get(2).unwrap()),
     40             symbol: String::from(record.get(3).unwrap()),
     41             decimals: record.get(4).unwrap().parse().unwrap(),
     42         }
     43     }
     44 }
     45 
     46 impl Token {
     47     pub fn cache_row(&self) -> (i64, String, String, String, u8) {
     48         (
     49             self.id,
     50             format!("{:?}", self.address),
     51             self.name.clone(),
     52             self.symbol.clone(),
     53             self.decimals,
     54         )
     55     }
     56 }
     57 
     58 pub async fn load_tokens(
     59     provider: RootProvider<BoxTransport>,
     60     path: &Path,
     61     pools: &BTreeMap<Address, Pool>,
     62     parallel: u64,
     63     last_pool_id: i64,
     64 ) -> Result<BTreeMap<Address, Token>> {
     65 
     66     info!("Loading tokens...");
     67 
     68     let mut tokens = BTreeMap::new();
     69 
     70     let file = OpenOptions::new()
     71         .write(true)
     72         .append(true)
     73         .create(true)
     74         .open(path)
     75         .unwrap();
     76 
     77     let mut writer = csv::Writer::from_writer(file);
     78 
     79     let mut token_id = 0;
     80     if path.exists() {
     81         let mut reader = csv::Reader::from_path(path)?;
     82         for row in reader.records() {
     83             let row = row.unwrap();
     84             let token = Token::from(row);
     85             tokens.insert(token.address, token);
     86             token_id += 1;
     87         }
     88     } else {
     89         writer.write_record(&[
     90             "id",
     91             "address",
     92             "name",
     93             "symbol",
     94             "decimals",
     95         ])?;
     96     }
     97 
     98     let pb = ProgressBar::new(pools.len() as u64);
     99     pb.set_style(
    100         ProgressStyle::with_template(
    101             "[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}",
    102         )
    103         .unwrap()
    104         .progress_chars("##-"),
    105     );
    106 
    107     let new_token_id = token_id;
    108 
    109     let mut count = 0;
    110     let mut requests = Vec::new();
    111 
    112     for (_, pool) in pools.into_iter() {
    113         let pool_id = pool.id;
    114         if pool_id <= last_pool_id {
    115             continue;
    116         }
    117         let token0 = pool.token0;
    118         let token1 = pool.token1;
    119         for token in [token0, token1] {
    120             if !tokens.contains_key(&token) {
    121                 requests.push(
    122                     tokio::task::spawn(
    123                         get_token_data(
    124                             provider.clone(),
    125                             token,
    126                         )
    127                     )
    128                 );
    129                 count += 1 ;
    130             }
    131             if count == parallel {
    132                 let results = futures::future::join_all(requests).await;
    133                 for result in results {
    134                     match result {
    135                         Ok(r) => match r {
    136                             Ok(t) => {
    137                                 tokens.insert(
    138                                     t.address,
    139                                     Token {
    140                                         id: token_id,
    141                                         address: t.address,
    142                                         name: t.name,
    143                                         symbol: t.symbol,
    144                                         decimals: t.decimals
    145                                     }
    146                                 );
    147                                 token_id += 1
    148                             }
    149                             Err(e) => { info!("Something wrong 0 {:?}", e) }
    150                         }
    151                         Err(e) => { info!("Something wrong 1 {:?}", e) }
    152                     }
    153                 }
    154                 requests = Vec::new();
    155                 count = 0;
    156                 pb.inc(parallel);
    157             }
    158         }
    159     }
    160 
    161     let mut added = 0;
    162     for token in tokens.values().collect::<Vec<&Token>>().iter() {
    163         if token.id >= new_token_id {
    164             writer.serialize(token.cache_row())?;
    165             added += 1
    166         }
    167     }
    168     writer.flush()?;
    169 
    170     Ok(tokens)
    171 }
    172 
    173 async fn get_token_data(
    174     provider: RootProvider<BoxTransport>,
    175     token: Address,
    176 ) -> Result<Token> {
    177 
    178     let interface = IERC20::new(token, provider);
    179 
    180     let decimals = match interface.decimals().call().await {
    181         Ok(r) => r.decimals,
    182         Err(e) => { return Err(anyhow!("Decimals of token failed {:?}", e )) }
    183     };
    184 
    185     let name = match interface.name().call().await {
    186         Ok(r) => r.name,
    187         Err(e) => {
    188             info!("Name of token {:?} failed {:?}", token, e);
    189             String::from("PlaceHolderName")
    190         }
    191     };
    192     let symbol = match interface.symbol().call().await{
    193         Ok(r) => r.symbol,
    194         Err(e) => {
    195             info!("Symbol of token failed {:?}", e );
    196             String::from("PlaceHolderSymbol")
    197         }
    198     };
    199 
    200     Ok(Token {
    201         id: -1,
    202         address: token,
    203         name,
    204         symbol,
    205         decimals,
    206     })
    207 }
    208 
    209 pub fn load_tokens_from_file(
    210     path: &Path,
    211 ) -> Result<BTreeMap<Address, Token>> {
    212     let mut tokens = BTreeMap::new();
    213 
    214     if path.exists() {
    215         let mut reader = csv::Reader::from_path(path)?;
    216         for row in reader.records() {
    217             let row = row.unwrap();
    218             let token = Token::from(row);
    219             tokens.insert(token.address, token);
    220         }
    221     } else {
    222         return Err(anyhow!("File path does not exist"));
    223     }
    224 
    225     Ok(tokens)
    226 }