1use crate::tx::{self, CastTxBuilder};
2use alloy_ens::NameOrAddress;
3use alloy_network::{EthereumWallet, TransactionBuilder, eip2718::Encodable2718};
4use alloy_primitives::{Address, hex};
5use alloy_provider::Provider;
6use alloy_signer::Signer;
7use clap::Parser;
8use eyre::Result;
9use foundry_cli::{
10 opts::{EthereumOpts, TransactionOpts},
11 utils::{LoadConfig, get_provider},
12};
13use std::{path::PathBuf, str::FromStr};
14
15#[derive(Debug, Parser)]
17pub struct MakeTxArgs {
18 #[arg(value_parser = NameOrAddress::from_str)]
22 to: Option<NameOrAddress>,
23
24 sig: Option<String>,
26
27 #[arg(allow_negative_numbers = true)]
29 args: Vec<String>,
30
31 #[command(subcommand)]
32 command: Option<MakeTxSubcommands>,
33
34 #[command(flatten)]
35 tx: TransactionOpts,
36
37 #[arg(
39 long,
40 value_name = "BLOB_DATA_PATH",
41 conflicts_with = "legacy",
42 requires = "blob",
43 help_heading = "Transaction options"
44 )]
45 path: Option<PathBuf>,
46
47 #[command(flatten)]
48 eth: EthereumOpts,
49
50 #[arg(long)]
54 raw_unsigned: bool,
55
56 #[arg(long, requires = "from", conflicts_with = "raw_unsigned")]
58 ethsign: bool,
59}
60
61#[derive(Debug, Parser)]
62pub enum MakeTxSubcommands {
63 #[command(name = "--create")]
65 Create {
66 code: String,
68
69 sig: Option<String>,
71
72 #[arg(allow_negative_numbers = true)]
74 args: Vec<String>,
75 },
76}
77
78impl MakeTxArgs {
79 pub async fn run(self) -> Result<()> {
80 let Self { to, mut sig, mut args, command, tx, path, eth, raw_unsigned, ethsign } = self;
81
82 let blob_data = if let Some(path) = path { Some(std::fs::read(path)?) } else { None };
83
84 let code = if let Some(MakeTxSubcommands::Create {
85 code,
86 sig: constructor_sig,
87 args: constructor_args,
88 }) = command
89 {
90 sig = constructor_sig;
91 args = constructor_args;
92 Some(code)
93 } else {
94 None
95 };
96
97 let config = eth.load_config()?;
98
99 let provider = get_provider(&config)?;
100
101 let tx_builder = CastTxBuilder::new(&provider, tx.clone(), &config)
102 .await?
103 .with_to(to)
104 .await?
105 .with_code_sig_and_args(code, sig, args)
106 .await?
107 .with_blob_data(blob_data)?;
108
109 if raw_unsigned {
110 if eth.wallet.from.is_none() && tx.nonce.is_none() {
114 eyre::bail!(
115 "Missing required parameters for raw unsigned transaction. When --from is not provided, you must specify: --nonce"
116 );
117 }
118
119 let from = eth.wallet.from.unwrap_or(Address::ZERO);
121
122 let raw_tx = tx_builder.build_unsigned_raw(from).await?;
123
124 sh_println!("{raw_tx}")?;
125 return Ok(());
126 }
127
128 if ethsign {
129 let (tx, _) = tx_builder.build(config.sender).await?;
132 let signed_tx = provider.sign_transaction(tx).await?;
133
134 sh_println!("{signed_tx}")?;
135 return Ok(());
136 }
137
138 let signer = eth.wallet.signer().await?;
141 let from = signer.address();
142
143 tx::validate_from_address(eth.wallet.from, from)?;
144
145 let (tx, _) = tx_builder.build(&signer).await?;
146
147 let tx = tx.build(&EthereumWallet::new(signer)).await?;
148
149 let signed_tx = hex::encode(tx.encoded_2718());
150 sh_println!("0x{signed_tx}")?;
151
152 Ok(())
153 }
154}