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