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