forge_script/
providers.rs

1use alloy_primitives::map::{hash_map::Entry, HashMap};
2use alloy_provider::{utils::Eip1559Estimation, Provider};
3use eyre::{Result, WrapErr};
4use foundry_common::provider::{get_http_provider, RetryProvider};
5use foundry_config::Chain;
6use std::{ops::Deref, sync::Arc};
7
8/// Contains a map of RPC urls to single instances of [`ProviderInfo`].
9#[derive(Default)]
10pub struct ProvidersManager {
11    pub inner: HashMap<String, ProviderInfo>,
12}
13
14impl ProvidersManager {
15    /// Get or initialize the RPC provider.
16    pub async fn get_or_init_provider(
17        &mut self,
18        rpc: &str,
19        is_legacy: bool,
20    ) -> Result<&ProviderInfo> {
21        Ok(match self.inner.entry(rpc.to_string()) {
22            Entry::Occupied(entry) => entry.into_mut(),
23            Entry::Vacant(entry) => {
24                let info = ProviderInfo::new(rpc, is_legacy).await?;
25                entry.insert(info)
26            }
27        })
28    }
29}
30
31impl Deref for ProvidersManager {
32    type Target = HashMap<String, ProviderInfo>;
33
34    fn deref(&self) -> &Self::Target {
35        &self.inner
36    }
37}
38
39/// Holds related metadata to each provider RPC.
40#[derive(Debug)]
41pub struct ProviderInfo {
42    pub provider: Arc<RetryProvider>,
43    pub chain: u64,
44    pub gas_price: GasPrice,
45}
46
47/// Represents the outcome of a gas price request
48#[derive(Debug)]
49pub enum GasPrice {
50    Legacy(Result<u128>),
51    EIP1559(Result<Eip1559Estimation>),
52}
53
54impl ProviderInfo {
55    pub async fn new(rpc: &str, mut is_legacy: bool) -> Result<Self> {
56        let provider = Arc::new(get_http_provider(rpc));
57        let chain = provider.get_chain_id().await?;
58
59        if let Some(chain) = Chain::from(chain).named() {
60            is_legacy |= chain.is_legacy();
61        };
62
63        let gas_price = if is_legacy {
64            GasPrice::Legacy(
65                provider.get_gas_price().await.wrap_err("Failed to get legacy gas price"),
66            )
67        } else {
68            GasPrice::EIP1559(
69                provider.estimate_eip1559_fees().await.wrap_err("Failed to get EIP-1559 fees"),
70            )
71        };
72
73        Ok(Self { provider, chain, gas_price })
74    }
75
76    /// Returns the gas price to use
77    pub fn gas_price(&self) -> Result<u128> {
78        let res = match &self.gas_price {
79            GasPrice::Legacy(res) => res.as_ref(),
80            GasPrice::EIP1559(res) => res.as_ref().map(|res| &res.max_fee_per_gas),
81        };
82        match res {
83            Ok(val) => Ok(*val),
84            Err(err) => Err(eyre::eyre!("{}", err)),
85        }
86    }
87}