dexther_lib 0.0.261

Dexther tools for the platform.
Documentation
use crate::types::{PairCreated, PairInfo, TokenInfo};
use ethers::utils::hex;
use eyre::{eyre, ErrReport, Result};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::time::Duration;
use tokio::time::sleep;

const MAX_RETRIES: u32 = 15;
const BASE_DELAY_MS: u64 = 100;
const MAX_DELAY_MS: u64 = 2000;

impl Error for JsonRpcError {}

impl fmt::Display for JsonRpcError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "JSON-RPC error: {} (code {})", self.message, self.code)
    }
}

#[derive(Serialize, Deserialize, Debug)]
struct JsonRpcRequest {
    jsonrpc: String,
    method: String,
    params: Vec<serde_json::Value>,
    id: u64,
}

#[derive(Serialize, Deserialize, Debug)]
struct JsonRpcResponse<T> {
    id: u64,
    jsonrpc: String,
    result: Option<T>,
    #[serde(skip_serializing_if = "Option::is_none")]
    error: Option<JsonRpcError>,
}

#[derive(Serialize, Deserialize, Debug)]
struct JsonRpcError {
    code: i32,
    message: String,
}

pub async fn get_tokens_for_pairs_with_retry(
    url: &str,
    pairs: Vec<(PairCreated, [String; 2])>,
) -> Result<Vec<(PairInfo, TokenInfo, TokenInfo)>> {
    let mut retries = 0;
    let mut delay = Duration::from_millis(BASE_DELAY_MS);

    loop {
        match get_tokens_for_pairs(url, pairs.clone()).await {
            Ok(result) => return Ok(result),
            Err(err) => {
                println!("Error parent: {:?}", err);
                println!("Retrying... (attempt {}/{})", retries + 1, MAX_RETRIES);
                if retries >= MAX_RETRIES {
                    return Err(eyre!("Error: {:?}", err));
                }
                // Rate limit exceeded, retry with backoff
                retries += 1;
                sleep(delay).await;
                delay = Duration::from_millis(std::cmp::min(
                    (delay.as_millis() * 2).try_into().unwrap_or(MAX_DELAY_MS),
                    MAX_DELAY_MS,
                ));
                continue;
            }
        }
    }
}

pub async fn get_tokens_for_pairs(
    url: &str,
    pairs: Vec<(PairCreated, [String; 2])>,
) -> Result<Vec<(PairInfo, TokenInfo, TokenInfo)>> {
    let client = Client::new();

    let methods = [
        ("eth_call", "0x313ce567"), // decimals()
        ("eth_call", "0x8da5cb5b"), // owner()
        ("eth_call", "0x18160ddd"), // totalSupply()
        ("eth_call", "0x06fdde03"), // name()
        ("eth_call", "0x95d89b41"), // symbol()
    ];

    println!("Getting tokens for {} pairs", pairs.len());

    let mut requests = Vec::new();
    let mut id = 1u64;

    for (pair_address, token_addresses) in pairs.clone() {
        for address in token_addresses {
            for &(method, data) in &methods {
                requests.push(JsonRpcRequest {
                    jsonrpc: "2.0".to_string(),
                    method: method.to_string(),
                    params: vec![
                        serde_json::json!({
                            "to": address,
                            "data": data
                        }),
                        "latest".into(),
                    ],
                    id: id,
                });
                id += 1;
            }
        }

        // Add requests for the pair address
        for &(method, data) in &methods[3..5] {
            // Only name() and symbol() methods
            println!("Pair address: {:?}", pair_address.pair);
            requests.push(JsonRpcRequest {
                jsonrpc: "2.0".to_string(),
                method: method.to_string(),
                params: vec![
                    serde_json::json!({
                        "to": pair_address.pair,
                        "data": data
                    }),
                    "latest".into(),
                ],
                id: id,
            });
            id += 1;
        }
    }

    let response = client.post(url).json(&requests).send().await?;

    let raw_response = response.text().await?;
    println!("Raw JSON response: {}", raw_response);
    let results: Vec<JsonRpcResponse<Value>> = serde_json::from_str(&raw_response)?;
    let mut parsed_results: Vec<(PairInfo, TokenInfo, TokenInfo)> = Vec::new();

    let mut result_map: HashMap<u64, &JsonRpcResponse<Value>> = HashMap::new();

    for result in results.iter() {
        result_map.insert(result.id, result);
    }

    // Parse results for each pair
    for pair_idx_usize in 0..pairs.len() {
        let pair_idx = pair_idx_usize as u64;

        println!("Result map: {:?}", result_map);
        println!("Result map id 2: {:?}", result_map.get(&2).unwrap());
        println!(
            "Result map id 2 unwrap: {:?}",
            parse_result_number(result_map.get(&2).unwrap())
        );


        let pair = PairInfo {
            address: pairs[pair_idx_usize].0.pair.to_lowercase().to_string(),
            name: match parse_result_text(result_map.get(&((pair_idx * 12) + 11)).unwrap()){
                Ok(name) => name,
                Err(err) => return Err(err),
            },
            symbol: match parse_result_text(result_map.get(&((pair_idx * 12) + 12)).unwrap()){
                Ok(symbol) => symbol,
                Err(err) => return Err(err),
            },
            v3: pairs[pair_idx_usize].0.v3,
            factory: pairs[pair_idx_usize].0.factory.to_lowercase().to_string(),
            tickSpacing: pairs[pair_idx_usize].0.tickSpacing,
            fee: pairs[pair_idx_usize].0.fee,
        };

        let token0 = TokenInfo {
            decimals: match parse_result_number(result_map.get(&((pair_idx * 12) + 1)).unwrap()) {
                Ok(decimals) => decimals,
                Err(err) => return Err(err),
            },
            owner: match parse_address_hex(result_map.get(&((pair_idx * 12) + 2)).unwrap()).as_str()
            {
                "0" => "None".to_string(),
                other => other.to_string(),
            },
            address: pairs[pair_idx_usize].1[0].to_lowercase().to_string(),
            total_supply: parse_result_number(result_map.get(&((pair_idx * 12) + 3)).unwrap())
                .unwrap(),
            name: match parse_result_text(result_map.get(&((pair_idx * 12) + 4)).unwrap()){
                Ok(name) => name,
                Err(err) => return Err(err),
            },
            symbol: match parse_result_text(result_map.get(&((pair_idx * 12) + 5)).unwrap()){
                Ok(symbol) => symbol,
                Err(err) => return Err(err),
            },
        };

        let token1 = TokenInfo {
            decimals: match parse_result_number(result_map.get(&((pair_idx * 12) + 6)).unwrap()) {
                Ok(decimals) => decimals,
                Err(err) => return Err(err),
            },
            owner: match parse_address_hex(result_map.get(&((pair_idx * 12) + 7)).unwrap()).as_str()
            {
                "0" => "None".to_string(),
                other => other.to_string(),
            },
            address: pairs[pair_idx_usize].1[1].to_lowercase().to_string(),
            total_supply: match parse_result_number(result_map.get(&((pair_idx * 12) + 8)).unwrap())
            {
                Ok(total_supply) => total_supply,
                Err(err) => return Err(err),
            },
            name: match parse_result_text(result_map.get(&((pair_idx * 12) + 9)).unwrap()){
                Ok(name) => name,
                Err(err) => return Err(err),
            },
            symbol: match parse_result_text(result_map.get(&((pair_idx * 12) + 10)).unwrap()){
                Ok(symbol) => symbol,
                Err(err) => return Err(err),
            },
        };

        parsed_results.push((pair, token0, token1));
    }

    Ok(parsed_results)
}

fn parse_result_text(result: &JsonRpcResponse<Value>) -> Result<String> {
    if let Some(serde_json::Value::String(hex_data)) = &result.result {
        let data = &hex_data[2..]; // Skip "0x"
        if let Ok(bytes) = hex::decode(data) {
            if let Ok(text) = std::str::from_utf8(&bytes) {
                let text = text.trim_matches('\0').trim();
                let trimmed =
                    text.trim_matches(|c: char| c.is_whitespace() || c == '\0' || c.is_control());
                return Ok(trimmed.to_string());
            } else {
                println!("Failed to decode UTF-8 text for result {}", result.id);
                Ok("None".to_string())
            }
        } else {
            println!("Failed to decode hex for result {}", result.id);
            Ok("None".to_string())
        }
    } else if let Some(err) = &result.error {
        println!(
            "Error for ID {}: {} (Code {})",
            result.id, err.message, err.code
        );
        if err.code == -32005 {
            Err(eyre!("Rate limit exceeded"))
        } else if err.code == -32000{
            Ok("None".to_string())
        } else {
            Err(eyre!(
                "Error for ID {}: {} (Code {})",
                result.id,
                err.message,
                err.code
            ))        }
    } else {
        Err(eyre!("No result or error for ID {}", result.id))
    }
}

fn parse_address_hex(result: &JsonRpcResponse<Value>) -> String {
    if let Some(serde_json::Value::String(hex_data)) = &result.result {
        if hex_data.len() > 26 {
            // Ensure there's enough length for stripping
            let stripped_data = &hex_data[26..]; // Strip the leading 24 zeros and "0x"
            let address = format!("0x{}", stripped_data);
            return address.to_string();
        } else {
            println!("Invalid hex_data length: {}", hex_data.len());
        }
    } else if let Some(err) = &result.error {
        println!(
            "Error for ID {}: {} (Code {})",
            result.id, err.message, err.code
        );
    } else {
        println!("No result or error for ID {}", result.id);
    }
    "None".to_string()
}

fn parse_result_number(result: &JsonRpcResponse<Value>) -> Result<String> {
    if let Some(serde_json::Value::String(hex_data)) = &result.result {
        let data = &hex_data[2..]; // Skip "0x"
        println!("INSIDE DATA: {:?}", data);
        let number = u128::from_str_radix(data, 16).unwrap_or_default();
        println!("INSIDE DATA: {:?}", data);

        println!("Result {}: {}", result.id, number);
        return Ok(number.to_string());
    }  else if let Some(err) = &result.error {
        println!(
            "Error for ID {}: {} (Code {})",
            result.id, err.message, err.code
        );
        if err.code == -32005 {
            Err(eyre!("Rate limit exceeded"))
        }else if err.code == -32000{
            Ok("None".to_string())
        } else {
            Err(eyre!(
                "Error for ID {}: {} (Code {})",
                result.id,
                err.message,
                err.code
            ))
        }
    } else {
        Err(eyre!("No result or error for ID {}", result.id))
    }
}