cast/cmd/
mktx.rs

1use crate::tx::{self, CastTxBuilder};
2use alloy_network::{eip2718::Encodable2718, EthereumWallet, TransactionBuilder};
3use alloy_primitives::hex;
4use alloy_signer::Signer;
5use clap::Parser;
6use eyre::{OptionExt, Result};
7use foundry_cli::{
8    opts::{EthereumOpts, TransactionOpts},
9    utils::{get_provider, LoadConfig},
10};
11use foundry_common::ens::NameOrAddress;
12use std::{path::PathBuf, str::FromStr};
13
14/// CLI arguments for `cast mktx`.
15#[derive(Debug, Parser)]
16pub struct MakeTxArgs {
17    /// The destination of the transaction.
18    ///
19    /// If not provided, you must use `cast mktx --create`.
20    #[arg(value_parser = NameOrAddress::from_str)]
21    to: Option<NameOrAddress>,
22
23    /// The signature of the function to call.
24    sig: Option<String>,
25
26    /// The arguments of the function to call.
27    args: Vec<String>,
28
29    #[command(subcommand)]
30    command: Option<MakeTxSubcommands>,
31
32    #[command(flatten)]
33    tx: TransactionOpts,
34
35    /// The path of blob data to be sent.
36    #[arg(
37        long,
38        value_name = "BLOB_DATA_PATH",
39        conflicts_with = "legacy",
40        requires = "blob",
41        help_heading = "Transaction options"
42    )]
43    path: Option<PathBuf>,
44
45    #[command(flatten)]
46    eth: EthereumOpts,
47
48    /// Generate a raw RLP-encoded unsigned transaction.
49    ///
50    /// Relaxes the wallet requirement.
51    #[arg(long, requires = "from")]
52    raw_unsigned: bool,
53}
54
55#[derive(Debug, Parser)]
56pub enum MakeTxSubcommands {
57    /// Use to deploy raw contract bytecode.
58    #[command(name = "--create")]
59    Create {
60        /// The initialization bytecode of the contract to deploy.
61        code: String,
62
63        /// The signature of the constructor.
64        sig: Option<String>,
65
66        /// The constructor arguments.
67        args: Vec<String>,
68    },
69}
70
71impl MakeTxArgs {
72    pub async fn run(self) -> Result<()> {
73        let Self { to, mut sig, mut args, command, tx, path, eth, raw_unsigned } = self;
74
75        let blob_data = if let Some(path) = path { Some(std::fs::read(path)?) } else { None };
76
77        let code = if let Some(MakeTxSubcommands::Create {
78            code,
79            sig: constructor_sig,
80            args: constructor_args,
81        }) = command
82        {
83            sig = constructor_sig;
84            args = constructor_args;
85            Some(code)
86        } else {
87            None
88        };
89
90        let config = eth.load_config()?;
91
92        let provider = get_provider(&config)?;
93
94        let tx_builder = CastTxBuilder::new(provider, tx, &config)
95            .await?
96            .with_to(to)
97            .await?
98            .with_code_sig_and_args(code, sig, args)
99            .await?
100            .with_blob_data(blob_data)?;
101
102        if raw_unsigned {
103            // Build unsigned raw tx
104            let from = eth.wallet.from.ok_or_eyre("missing `--from` address")?;
105            let raw_tx = tx_builder.build_unsigned_raw(from).await?;
106
107            sh_println!("{raw_tx}")?;
108            return Ok(());
109        }
110
111        // Retrieve the signer, and bail if it can't be constructed.
112        let signer = eth.wallet.signer().await?;
113        let from = signer.address();
114
115        tx::validate_from_address(eth.wallet.from, from)?;
116
117        let (tx, _) = tx_builder.build(&signer).await?;
118
119        let tx = tx.build(&EthereumWallet::new(signer)).await?;
120
121        let signed_tx = hex::encode(tx.encoded_2718());
122        sh_println!("0x{signed_tx}")?;
123
124        Ok(())
125    }
126}