1use crate::tx::{CastTxBuilder, SenderKind};
2use alloy_ens::NameOrAddress;
3use alloy_network::{Ethereum, Network};
4use alloy_primitives::U256;
5use alloy_provider::Provider;
6use alloy_rpc_types::BlockId;
7use clap::Parser;
8use eyre::Result;
9use foundry_cli::{
10 opts::{RpcOpts, TransactionOpts},
11 utils::{LoadConfig, parse_ether_value},
12};
13use foundry_common::{FoundryTransactionBuilder, provider::ProviderBuilder};
14use foundry_wallets::WalletOpts;
15use std::str::FromStr;
16use tempo_alloy::TempoNetwork;
17
18#[derive(Debug, Parser)]
20pub struct EstimateArgs {
21 #[arg(value_parser = NameOrAddress::from_str)]
23 to: Option<NameOrAddress>,
24
25 sig: Option<String>,
27
28 #[arg(allow_negative_numbers = true)]
30 args: Vec<String>,
31
32 #[arg(long, short = 'B')]
36 block: Option<BlockId>,
37
38 #[arg(long)]
42 cost: bool,
43
44 #[command(flatten)]
45 wallet: WalletOpts,
46
47 #[command(subcommand)]
48 command: Option<EstimateSubcommands>,
49
50 #[command(flatten)]
51 tx: TransactionOpts,
52
53 #[command(flatten)]
54 rpc: RpcOpts,
55}
56
57#[derive(Debug, Parser)]
58pub enum EstimateSubcommands {
59 #[command(name = "--create")]
61 Create {
62 code: String,
64
65 sig: Option<String>,
67
68 #[arg(allow_negative_numbers = true)]
70 args: Vec<String>,
71
72 #[arg(long, value_parser = parse_ether_value)]
78 value: Option<U256>,
79 },
80}
81
82impl EstimateArgs {
83 pub async fn run(self) -> Result<()> {
84 if self.tx.tempo.is_tempo() {
85 self.run_with_network::<TempoNetwork>().await
86 } else {
87 self.run_with_network::<Ethereum>().await
88 }
89 }
90
91 pub async fn run_with_network<N: Network>(self) -> Result<()>
92 where
93 N::TransactionRequest: FoundryTransactionBuilder<N>,
94 {
95 let Self { to, mut sig, mut args, mut tx, block, cost, wallet, rpc, command } = self;
96
97 let config = rpc.load_config()?;
98 let provider = ProviderBuilder::<N>::from_config(&config)?.build()?;
99 let sender = SenderKind::from_wallet_opts(wallet).await?;
100
101 let code = if let Some(EstimateSubcommands::Create {
102 code,
103 sig: create_sig,
104 args: create_args,
105 value,
106 }) = command
107 {
108 sig = create_sig;
109 args = create_args;
110 if let Some(value) = value {
111 tx.value = Some(value);
112 }
113 Some(code)
114 } else {
115 None
116 };
117
118 let (tx, _) = CastTxBuilder::new(&provider, tx, &config)
119 .await?
120 .with_to(to)
121 .await?
122 .with_code_sig_and_args(code, sig, args)
123 .await?
124 .raw()
125 .build(sender)
126 .await?;
127
128 let gas = provider.estimate_gas(tx).block(block.unwrap_or_default()).await?;
129 if cost {
130 let gas_price_wei = provider.get_gas_price().await?;
131 let cost = gas_price_wei * gas as u128;
132 let cost_eth = cost as f64 / 1e18;
133 sh_println!("{cost_eth}")?;
134 } else {
135 sh_println!("{gas}")?;
136 }
137 Ok(())
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn parse_estimate_value() {
147 let args: EstimateArgs = EstimateArgs::parse_from(["foundry-cli", "--value", "100"]);
148 assert!(args.tx.value.is_some());
149 }
150}