Skip to main content

cast/cmd/
estimate.rs

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