Skip to main content

forge_script/
providers.rs

1use alloy_network::Network;
2use alloy_primitives::map::{HashMap, hash_map::Entry};
3use alloy_provider::{Provider, RootProvider};
4use eyre::{Result, WrapErr};
5use foundry_common::provider::{
6    ProviderBuilder,
7    fee::{ResolvedEip1559Fees, estimate_eip1559_fees},
8};
9use foundry_config::{Chain, Config, Eip1559FeeEstimatePreset};
10use std::{ops::Deref, sync::Arc};
11
12/// Contains a map of RPC urls to single instances of [`ProviderInfo`].
13pub struct ProvidersManager<N: Network> {
14    pub inner: HashMap<String, ProviderInfo<N>>,
15}
16
17impl<N: Network> Default for ProvidersManager<N> {
18    fn default() -> Self {
19        Self { inner: Default::default() }
20    }
21}
22
23impl<N: Network> ProvidersManager<N> {
24    /// Get or initialize the RPC provider.
25    pub async fn get_or_init_provider(
26        &mut self,
27        rpc: &str,
28        chain: Option<u64>,
29        is_legacy: bool,
30        fee_estimate: Eip1559FeeEstimatePreset,
31        config: &Config,
32    ) -> Result<&ProviderInfo<N>> {
33        Ok(match self.inner.entry(rpc.to_string()) {
34            Entry::Occupied(entry) => entry.into_mut(),
35            Entry::Vacant(entry) => {
36                let info = ProviderInfo::new(rpc, chain, is_legacy, fee_estimate, config).await?;
37                entry.insert(info)
38            }
39        })
40    }
41}
42
43impl<N: Network> Deref for ProvidersManager<N> {
44    type Target = HashMap<String, ProviderInfo<N>>;
45
46    fn deref(&self) -> &Self::Target {
47        &self.inner
48    }
49}
50
51/// Holds related metadata to each provider RPC.
52#[derive(Debug)]
53pub struct ProviderInfo<N: Network> {
54    pub provider: Arc<RootProvider<N>>,
55    pub chain: u64,
56    pub gas_price: GasPrice,
57}
58
59/// Represents the outcome of a gas price request
60#[derive(Debug)]
61pub enum GasPrice {
62    Legacy(Result<u128>),
63    EIP1559(Result<ResolvedEip1559Fees>),
64}
65
66impl<N: Network> ProviderInfo<N> {
67    pub async fn new(
68        rpc: &str,
69        chain: Option<u64>,
70        mut is_legacy: bool,
71        fee_estimate: Eip1559FeeEstimatePreset,
72        config: &Config,
73    ) -> Result<Self> {
74        let provider = Arc::new(ProviderBuilder::from_config_with_url(config, rpc)?.build()?);
75        let chain = match chain {
76            Some(chain) => chain,
77            None => provider.get_chain_id().await?,
78        };
79
80        if let Some(chain) = Chain::from(chain).named() {
81            is_legacy |= chain.is_legacy();
82        };
83
84        let gas_price = if is_legacy {
85            GasPrice::Legacy(
86                provider.get_gas_price().await.wrap_err("Failed to get legacy gas price"),
87            )
88        } else {
89            GasPrice::EIP1559(
90                estimate_eip1559_fees(&provider, fee_estimate)
91                    .await
92                    .wrap_err("Failed to get EIP-1559 fees"),
93            )
94        };
95
96        Ok(Self { provider, chain, gas_price })
97    }
98
99    /// Returns the gas price to use.
100    ///
101    /// For EIP-1559 chains this is the estimated `maxFeePerGas`.
102    pub fn gas_price(&self) -> Result<u128> {
103        match &self.gas_price {
104            GasPrice::Legacy(res) => match res {
105                Ok(val) => Ok(*val),
106                Err(err) => Err(eyre::eyre!("{}", err)),
107            },
108            GasPrice::EIP1559(res) => match res {
109                Ok(fees) => Ok(fees.max_fee_per_gas),
110                Err(err) => Err(eyre::eyre!("{}", err)),
111            },
112        }
113    }
114
115    /// Returns the resolved EIP-1559 fee breakdown, if available.
116    pub const fn eip1559_fees(&self) -> Option<&ResolvedEip1559Fees> {
117        match &self.gas_price {
118            GasPrice::EIP1559(Ok(fees)) => Some(fees),
119            _ => None,
120        }
121    }
122}