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, 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        is_legacy: bool,
29        fee_estimate: Eip1559FeeEstimatePreset,
30    ) -> Result<&ProviderInfo<N>> {
31        Ok(match self.inner.entry(rpc.to_string()) {
32            Entry::Occupied(entry) => entry.into_mut(),
33            Entry::Vacant(entry) => {
34                let info = ProviderInfo::new(rpc, is_legacy, fee_estimate).await?;
35                entry.insert(info)
36            }
37        })
38    }
39}
40
41impl<N: Network> Deref for ProvidersManager<N> {
42    type Target = HashMap<String, ProviderInfo<N>>;
43
44    fn deref(&self) -> &Self::Target {
45        &self.inner
46    }
47}
48
49/// Holds related metadata to each provider RPC.
50#[derive(Debug)]
51pub struct ProviderInfo<N: Network> {
52    pub provider: Arc<RootProvider<N>>,
53    pub chain: u64,
54    pub gas_price: GasPrice,
55}
56
57/// Represents the outcome of a gas price request
58#[derive(Debug)]
59pub enum GasPrice {
60    Legacy(Result<u128>),
61    EIP1559(Result<ResolvedEip1559Fees>),
62}
63
64impl<N: Network> ProviderInfo<N> {
65    pub async fn new(
66        rpc: &str,
67        mut is_legacy: bool,
68        fee_estimate: Eip1559FeeEstimatePreset,
69    ) -> Result<Self> {
70        let provider = Arc::new(ProviderBuilder::new(rpc).build()?);
71        let chain = provider.get_chain_id().await?;
72
73        if let Some(chain) = Chain::from(chain).named() {
74            is_legacy |= chain.is_legacy();
75        };
76
77        let gas_price = if is_legacy {
78            GasPrice::Legacy(
79                provider.get_gas_price().await.wrap_err("Failed to get legacy gas price"),
80            )
81        } else {
82            GasPrice::EIP1559(
83                estimate_eip1559_fees(&provider, fee_estimate)
84                    .await
85                    .wrap_err("Failed to get EIP-1559 fees"),
86            )
87        };
88
89        Ok(Self { provider, chain, gas_price })
90    }
91
92    /// Returns the gas price to use.
93    ///
94    /// For EIP-1559 chains this is the estimated `maxFeePerGas`.
95    pub fn gas_price(&self) -> Result<u128> {
96        match &self.gas_price {
97            GasPrice::Legacy(res) => match res {
98                Ok(val) => Ok(*val),
99                Err(err) => Err(eyre::eyre!("{}", err)),
100            },
101            GasPrice::EIP1559(res) => match res {
102                Ok(fees) => Ok(fees.max_fee_per_gas),
103                Err(err) => Err(eyre::eyre!("{}", err)),
104            },
105        }
106    }
107
108    /// Returns the resolved EIP-1559 fee breakdown, if available.
109    pub const fn eip1559_fees(&self) -> Option<&ResolvedEip1559Fees> {
110        match &self.gas_price {
111            GasPrice::EIP1559(Ok(fees)) => Some(fees),
112            _ => None,
113        }
114    }
115}