Skip to main content

cast/cmd/
estimate.rs

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/// CLI arguments for `cast estimate`.
19#[derive(Debug, Parser)]
20pub struct EstimateArgs {
21    /// The destination of the transaction.
22    #[arg(value_parser = NameOrAddress::from_str)]
23    to: Option<NameOrAddress>,
24
25    /// The signature of the function to call.
26    sig: Option<String>,
27
28    /// The arguments of the function to call.
29    #[arg(allow_negative_numbers = true)]
30    args: Vec<String>,
31
32    /// The block height to query at.
33    ///
34    /// Can also be the tags earliest, finalized, safe, latest, or pending.
35    #[arg(long, short = 'B')]
36    block: Option<BlockId>,
37
38    /// Calculate the cost of a transaction using the network gas price.
39    ///
40    /// If not specified the amount of gas will be estimated.
41    #[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    /// Estimate gas cost to deploy a smart contract
60    #[command(name = "--create")]
61    Create {
62        /// The bytecode of contract
63        code: String,
64
65        /// The signature of the constructor
66        sig: Option<String>,
67
68        /// Constructor arguments
69        #[arg(allow_negative_numbers = true)]
70        args: Vec<String>,
71
72        /// Ether to send in the transaction
73        ///
74        /// Either specified in wei, or as a string with a unit type:
75        ///
76        /// Examples: 1ether, 10gwei, 0.01ether
77        #[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}