data3 0.2.0

SDK for accessing ethereum data hosted by indexers.
Documentation
use std::collections::{HashMap, HashSet};

use alloy::primitives::{Address, U256};

use crate::{alchemy::Alchemy, network::Network, oneinch::OneInch};

pub struct Fallback {
    pub alchemy: Alchemy,
    pub oneinch: OneInch,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TokenBalance {
    token: Address,
    balance: U256,
}

impl TokenBalance {
    pub fn into_array(hm: HashMap<Address, String>) -> Vec<Self> {
        hm.into_iter()
            .map(|(token, balance)| {
                let balance = U256::from_str_radix(&balance, 10).unwrap_or_default();
                Self { token, balance }
            })
            .collect()
    }
}

impl Fallback {
    pub fn new(alchemy_api_key: String, oneinch_api_key: String) -> Self {
        Self {
            alchemy: Alchemy::new(alchemy_api_key),
            oneinch: OneInch::new(oneinch_api_key),
        }
    }

    pub async fn balances(
        &self,
        address: Address,
        network: Network,
    ) -> crate::Result<HashSet<TokenBalance>> {
        let result_a = TokenBalance::into_array(self.oneinch.balances(address, network).await?);

        let result_b = self
            .alchemy
            .get_token_balances_by_wallet(address, vec![network])
            .await?
            .into_iter()
            .filter_map(|entry| {
                entry.token_address.map(|token_address| TokenBalance {
                    token: token_address,
                    balance: entry.token_balance,
                })
            })
            .collect::<Vec<TokenBalance>>();

        let set_a: HashSet<_> = result_a.clone().into_iter().collect();
        let set_b: HashSet<_> = result_b.clone().into_iter().collect();

        Ok(set_a.intersection(&set_b).cloned().collect())
    }
}

#[cfg(test)]
mod tests {
    use alloy::primitives::address;

    use super::*;
    use crate::network::Network;

    #[tokio::test]
    async fn test_fallback_balances() {
        let fallback = Fallback::new(
            std::env::var("ALCHEMY_API_KEY").expect("ALCHEMY_API_KEY key not set in env"),
            std::env::var("ONEINCH_API_KEY").expect("ONEINCH_API_KEY key not set in env"),
        );

        let result = fallback
            .balances(
                address!("0xf51C53b2C184Aa43A7d24aA4f0Cf13D0e8b56c51"),
                Network::EthereumMainnet,
            )
            .await
            .unwrap();

        println!("Fallback balances: {:?}", result);

        // assert!(false);
    }
}