Skip to main content

forge_script/
providers.rs

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