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);
}
}