1use super::{auth::confirm_and_build, print_result_line};
2use crate::tx::{CastTxBuilder, read_only_sender};
3use alloy_ens::NameOrAddress;
4use alloy_network::{Ethereum, Network};
5use alloy_primitives::U256;
6use alloy_provider::Provider;
7use alloy_rpc_types::BlockId;
8use clap::Parser;
9use eyre::Result;
10use foundry_cli::{
11 opts::{RpcOpts, TransactionOpts},
12 utils::{LoadConfig, parse_ether_value},
13};
14use foundry_common::{FoundryTransactionBuilder, provider::ProviderBuilder};
15use foundry_config::Config;
16use foundry_wallets::{BrowserWalletOpts, WalletOpts};
17use std::str::FromStr;
18use tempo_alloy::TempoNetwork;
19
20#[cfg(feature = "base")]
21use base_common_network::Base;
22
23#[derive(Debug, Parser)]
25pub struct EstimateArgs {
26 #[arg(value_parser = NameOrAddress::from_str)]
28 to: Option<NameOrAddress>,
29
30 sig: Option<String>,
32
33 #[arg(allow_negative_numbers = true)]
35 args: Vec<String>,
36
37 #[arg(long, short = 'B')]
41 block: Option<BlockId>,
42
43 #[arg(long)]
47 cost: bool,
48
49 #[command(flatten)]
50 wallet: WalletOpts,
51
52 #[command(flatten)]
53 browser: BrowserWalletOpts,
54
55 #[command(subcommand)]
56 command: Option<EstimateSubcommands>,
57
58 #[command(flatten)]
59 tx: TransactionOpts,
60
61 #[arg(long)]
63 force: bool,
64
65 #[command(flatten)]
66 rpc: RpcOpts,
67}
68
69#[derive(Debug, Parser)]
70pub enum EstimateSubcommands {
71 #[command(name = "--create")]
73 Create {
74 code: String,
76
77 sig: Option<String>,
79
80 #[arg(allow_negative_numbers = true)]
82 args: Vec<String>,
83
84 #[arg(long, value_parser = parse_ether_value)]
90 value: Option<U256>,
91 },
92}
93
94impl EstimateArgs {
95 pub async fn run(self) -> Result<()> {
96 let config = self.rpc.load_config()?;
97 let requires_tempo = self.tx.tempo.is_tempo() || self.tx.tempo.session_id()?.is_some();
98 let network = super::resolve_transaction_network(&config, requires_tempo).await?;
99 if network.is_tempo() {
100 return self.run_with_network::<TempoNetwork>(config).await;
101 }
102 #[cfg(feature = "base")]
103 if network.is_base() {
104 super::validate_base_transaction_options(&self.tx)?;
105 return self.run_with_network::<Base>(config).await;
106 }
107 self.run_with_network::<Ethereum>(config).await
108 }
109
110 async fn run_with_network<N: Network>(self, config: Config) -> Result<()>
111 where
112 N::TransactionRequest: FoundryTransactionBuilder<N>,
113 {
114 let Self {
115 to,
116 mut sig,
117 mut args,
118 mut tx,
119 block,
120 cost,
121 wallet,
122 browser,
123 force,
124 rpc: _,
125 command,
126 } = self;
127
128 let provider = ProviderBuilder::<N>::from_config(&config)?.build()?;
129 let chain_id = match config.chain {
130 Some(chain) => chain.id(),
131 None => provider.get_chain_id().await?,
132 };
133 let (sender, is_browser) =
134 read_only_sender::<N>(&browser, wallet, &tx.tempo, chain_id).await?;
135
136 let code = if let Some(EstimateSubcommands::Create {
137 code,
138 sig: create_sig,
139 args: create_args,
140 value,
141 }) = command
142 {
143 sig = create_sig;
144 args = create_args;
145 if let Some(value) = value {
146 tx.value = Some(value);
147 }
148 Some(code)
149 } else {
150 None
151 };
152
153 let builder = CastTxBuilder::new(&provider, tx, &config)
154 .await?
155 .with_to(to)
156 .await?
157 .with_code_sig_and_args(code, sig, args)
158 .await?
159 .raw();
160 let Some(tx) = confirm_and_build(builder, sender, force, None, true).await? else {
161 return Ok(());
162 };
163
164 let tx = if is_browser { tx.browser_wallet_gas_estimation_request() } else { tx };
165 let gas = provider.estimate_gas(tx).block(block.unwrap_or_default()).await?;
166 if cost {
167 let cost = provider.get_gas_price().await? * gas as u128;
168 print_result_line(cost as f64 / 1e18)
169 } else {
170 print_result_line(gas)
171 }
172 }
173}