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