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