cast/cmd/
estimate.rs

1use crate::tx::{CastTxBuilder, SenderKind};
2use alloy_ens::NameOrAddress;
3use alloy_primitives::U256;
4use alloy_provider::Provider;
5use alloy_rpc_types::BlockId;
6use clap::Parser;
7use eyre::Result;
8use foundry_cli::{
9    opts::{EthereumOpts, TransactionOpts},
10    utils::{self, parse_ether_value, LoadConfig},
11};
12use std::str::FromStr;
13
14/// CLI arguments for `cast estimate`.
15#[derive(Debug, Parser)]
16pub struct EstimateArgs {
17    /// The destination of the transaction.
18    #[arg(value_parser = NameOrAddress::from_str)]
19    to: Option<NameOrAddress>,
20
21    /// The signature of the function to call.
22    sig: Option<String>,
23
24    /// The arguments of the function to call.
25    args: Vec<String>,
26
27    /// The block height to query at.
28    ///
29    /// Can also be the tags earliest, finalized, safe, latest, or pending.
30    #[arg(long, short = 'B')]
31    block: Option<BlockId>,
32
33    /// Calculate the cost of a transaction using the network gas price.
34    ///
35    /// If not specified the amount of gas will be estimated.
36    #[arg(long)]
37    cost: bool,
38
39    #[command(subcommand)]
40    command: Option<EstimateSubcommands>,
41
42    #[command(flatten)]
43    tx: TransactionOpts,
44
45    #[command(flatten)]
46    eth: EthereumOpts,
47}
48
49#[derive(Debug, Parser)]
50pub enum EstimateSubcommands {
51    /// Estimate gas cost to deploy a smart contract
52    #[command(name = "--create")]
53    Create {
54        /// The bytecode of contract
55        code: String,
56
57        /// The signature of the constructor
58        sig: Option<String>,
59
60        /// Constructor arguments
61        args: Vec<String>,
62
63        /// Ether to send in the transaction
64        ///
65        /// Either specified in wei, or as a string with a unit type:
66        ///
67        /// Examples: 1ether, 10gwei, 0.01ether
68        #[arg(long, value_parser = parse_ether_value)]
69        value: Option<U256>,
70    },
71}
72
73impl EstimateArgs {
74    pub async fn run(self) -> Result<()> {
75        let Self { to, mut sig, mut args, mut tx, block, cost, eth, command } = self;
76
77        let config = eth.load_config()?;
78        let provider = utils::get_provider(&config)?;
79        let sender = SenderKind::from_wallet_opts(eth.wallet).await?;
80
81        let code = if let Some(EstimateSubcommands::Create {
82            code,
83            sig: create_sig,
84            args: create_args,
85            value,
86        }) = command
87        {
88            sig = create_sig;
89            args = create_args;
90            if let Some(value) = value {
91                tx.value = Some(value);
92            }
93            Some(code)
94        } else {
95            None
96        };
97
98        let (tx, _) = CastTxBuilder::new(&provider, tx, &config)
99            .await?
100            .with_to(to)
101            .await?
102            .with_code_sig_and_args(code, sig, args)
103            .await?
104            .build_raw(sender)
105            .await?;
106
107        let gas = provider.estimate_gas(tx).block(block.unwrap_or_default()).await?;
108        if cost {
109            let gas_price_wei = provider.get_gas_price().await?;
110            let cost = gas_price_wei * gas as u128;
111            let cost_eth = cost as f64 / 1e18;
112            sh_println!("{cost_eth}")?;
113        } else {
114            sh_println!("{gas}")?;
115        }
116        Ok(())
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn parse_estimate_value() {
126        let args: EstimateArgs = EstimateArgs::parse_from(["foundry-cli", "--value", "100"]);
127        assert!(args.tx.value.is_some());
128    }
129}