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