1use alloy::primitives::{U256, map::HashMap};
2use figment::{
3 Figment,
4 providers::{Format, Toml},
5};
6use serde::Deserialize;
7
8use crate::{
9 Result,
10 error::EthPricesError,
11 provider::RpcProvider,
12 quoter::{
13 AnyQuoter,
14 erc4626::{ERC4626Config, ERC4626Quoter},
15 fixed::FixedQuoter,
16 uniswap_v2::{UniswapV2Config, UniswapV2Quoter},
17 uniswap_v3::{UniswapV3Quoter, factory::UniswapV3Config},
18 },
19};
20
21#[derive(Debug, Deserialize, PartialEq)]
22pub struct Config {
23 pub chains: HashMap<String, ChainConfig>,
24}
25
26#[derive(Debug, Deserialize, PartialEq)]
27pub struct ChainConfig {
28 pub chain_id: u64,
29 pub rpc_url: String,
30 pub tokens: Vec<TokenConfig>,
31 pub quoters: QuotersConfig,
32}
33
34#[derive(Debug, Deserialize, PartialEq, Clone)]
35pub struct QuotersConfig {
36 pub fixed: Vec<FixedQuoter>,
37 pub uniswap_v2: Option<UniswapV2Config>,
38 pub uniswap_v3: Option<UniswapV3Config>,
39 pub erc4626: Vec<ERC4626Config>,
40}
41
42impl QuotersConfig {
43 pub async fn all(self, provider: &RpcProvider) -> Result<Vec<AnyQuoter>> {
44 let mut quoters: Vec<AnyQuoter> = Vec::new();
45 for tracker in self.fixed {
46 if tracker.fixed_rate <= U256::from(0) {
47 return Err(EthPricesError::InvalidConfiguration(format!(
48 "Fixed rate for {} to {} must be > 0.0",
49 tracker.token_in, tracker.token_out
50 )));
51 }
52 quoters.push(tracker.into());
53 }
54
55 if let Some(uniswap_v2_config) = &self.uniswap_v2 {
56 for uni_quoters in uniswap_v2_config.pairs.iter() {
57 let quoter = UniswapV2Quoter::from_selector(provider, uni_quoters.clone()).await?;
58 quoters.push(quoter.into());
59 }
60 }
61
62 if let Some(uniswap_v3_config) = &self.uniswap_v3 {
63 for uni_quoters in uniswap_v3_config.pools.iter() {
64 let quoter = UniswapV3Quoter::from_selector(provider, uni_quoters.clone()).await?;
65 quoters.push(quoter.into());
66 }
67 }
68
69 for erc4626_config in &self.erc4626 {
70 let quoter = ERC4626Quoter::new(erc4626_config.vault_address, provider).await?;
71 quoters.push(quoter.into());
72 }
73
74 Ok(quoters)
75 }
76}
77
78#[derive(Debug, Deserialize, PartialEq)]
79pub struct TokenConfig {
80 pub address: String,
81 pub slug: Option<String>,
82 pub decimals: u8,
83}
84
85impl Config {
86 pub async fn load(path: &str) -> Result<Self> {
87 let figment = Figment::new().merge(Toml::file(path));
88 figment
89 .extract::<Config>()
90 .map_err(|e| EthPricesError::ConfigError(e.to_string()))
91 }
92}