use std::collections::HashMap;
use alloy::primitives::Address;
use types::{ApiResponse, Candle, Granularity, Protocols};
use crate::{network::Network, serde_override::SerdeResponseParse};
pub mod types;
pub struct OneInch {
api_key: String,
}
impl OneInch {
pub fn new(api_key: String) -> Self {
OneInch { api_key }
}
pub async fn prices(&self, network: Network) -> crate::Result<HashMap<Address, String>> {
if !matches!(
network,
Network::EthereumMainnet
| Network::OptimismMainnet
| Network::ArbitrumMainnet
| Network::AvalancheMainnet
| Network::BscMainnet
| Network::FantomOperaMainnet
| Network::GnosisMainnet
| Network::PolygonMainnet
| Network::ZkSyncMainnet
| Network::BaseMainnet
| Network::LineaMainnet
) {
return Err(crate::Error::ChainIdNotSupported(
"1inch::prices",
network.chain_id(),
));
}
let url = format!("https://api.1inch.dev/price/v1.1/{}", network.chain_id());
let client = reqwest::Client::new();
let response = client
.get(url)
.bearer_auth(self.api_key.clone())
.query(&[("currency", "USD")])
.send()
.await?;
response.serde_parse_custom().await
}
pub async fn balances(
&self,
address: Address,
network: Network,
) -> crate::Result<HashMap<Address, String>> {
let url = format!(
"https://api.1inch.dev/balance/v1.2/{}/balances/{}",
network.chain_id(),
address
);
let client = reqwest::Client::new();
let result = client
.get(url)
.bearer_auth(self.api_key.clone())
.send()
.await?
.serde_parse_custom::<HashMap<Address, String>>()
.await?;
Ok(result
.into_iter()
.filter(|(_, balance)| *balance != "0")
.collect::<HashMap<Address, String>>())
}
pub async fn portfolio(
&self,
network: Network,
addresses: Vec<Address>,
use_cache: bool,
) -> crate::Result<ApiResponse<Protocols>> {
let url = "https://api.1inch.dev/portfolio/portfolio/v4/overview/protocols/current_value";
let client = reqwest::Client::new();
let response = client
.get(url)
.bearer_auth(self.api_key.clone())
.query(&[(
"addresses",
addresses
.iter()
.map(|a| a.to_string())
.collect::<Vec<_>>()
.join(","),
)])
.query(&[("chain_id", network.chain_id())])
.query(&[("use_cache", use_cache)])
.send()
.await?;
response.serde_parse_custom().await
}
pub async fn ohlc(
&self,
token0: Address,
token1: Address,
network: Network,
granularity: Granularity,
order_asc: Option<bool>,
limit: Option<usize>,
) -> crate::Result<Vec<Candle>> {
let url = "https://api.1inch.dev/portfolio/integrations/prices/v1/time_range/cross_prices";
let client = reqwest::Client::new();
let mut query_params = vec![
("token0_address", token0.to_string()),
("token1_address", token1.to_string()),
("chain_id", network.chain_id().to_string()),
("granularity", granularity.into()),
];
if let Some(order) = order_asc {
query_params.push(("order_asc", order.to_string()));
}
if let Some(lim) = limit {
query_params.push(("limit", lim.to_string()));
}
client
.get(url)
.bearer_auth(self.api_key.clone())
.query(&query_params)
.send()
.await?
.serde_parse_custom()
.await
}
}
#[cfg(test)]
mod tests {
use alloy::primitives::address;
use rstest::rstest;
use super::*;
use crate::network::Network;
#[rstest]
#[case(Network::EthereumMainnet)]
#[case(Network::OptimismMainnet)]
#[case(Network::ArbitrumMainnet)]
#[tokio::test]
async fn test_prices(#[case] network: Network) {
let api_key = std::env::var("ONEINCH_API_KEY").expect("ONEINCH_API_KEY key not set in env");
let oneinch = OneInch::new(api_key);
let result = oneinch.prices(network).await.unwrap();
println!("1inch prices: {:?}", result);
}
#[rstest]
#[case(Network::EthereumMainnet)]
#[case(Network::OptimismMainnet)]
#[case(Network::ArbitrumMainnet)]
#[tokio::test]
async fn test_balances(#[case] network: Network) {
let api_key = std::env::var("ONEINCH_API_KEY").expect("ONEINCH_API_KEY key not set in env");
let oneinch = OneInch::new(api_key);
let balances = oneinch
.balances(
address!("0xf51C53b2C184Aa43A7d24aA4f0Cf13D0e8b56c51"),
network,
)
.await
.unwrap();
println!("1inch balances: {:?}", balances);
}
#[rstest]
#[case(Network::EthereumMainnet)]
#[case(Network::OptimismMainnet)]
#[case(Network::ArbitrumMainnet)]
#[tokio::test]
async fn test_portfolio(#[case] network: Network) {
let api_key = std::env::var("ONEINCH_API_KEY").expect("ONEINCH_API_KEY key not set in env");
let oneinch = OneInch::new(api_key);
let addresses = vec![address!("0xf51C53b2C184Aa43A7d24aA4f0Cf13D0e8b56c51")];
let result = oneinch.portfolio(network, addresses, false).await.unwrap();
println!("1inch portfolio: {:?}", result);
}
#[tokio::test]
async fn test_ohlc() {
let api_key = std::env::var("ONEINCH_API_KEY").expect("ONEINCH_API_KEY key not set in env");
let oneinch = OneInch::new(api_key);
let result = oneinch
.ohlc(
address!("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
address!("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),
Network::EthereumMainnet,
Granularity::Hour,
Some(true),
Some(100),
)
.await
.unwrap();
println!("1inch OHLC: {:?}", result);
}
}