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