1use crate::tx::{self, CastTxBuilder};
2use alloy_eips::Encodable2718;
3use alloy_ens::NameOrAddress;
4use alloy_network::{EthereumWallet, NetworkWallet, TransactionBuilder};
5use alloy_primitives::{Address, hex};
6use alloy_provider::Provider;
7use alloy_signer::Signer;
8use clap::Parser;
9use eyre::Result;
10use foundry_cli::{
11 opts::{EthereumOpts, TransactionOpts},
12 utils::{LoadConfig, get_provider},
13};
14use std::{path::PathBuf, str::FromStr};
15
16#[derive(Debug, Parser)]
18pub struct MakeTxArgs {
19 #[arg(value_parser = NameOrAddress::from_str)]
23 to: Option<NameOrAddress>,
24
25 sig: Option<String>,
27
28 #[arg(allow_negative_numbers = true)]
30 args: Vec<String>,
31
32 #[command(subcommand)]
33 command: Option<MakeTxSubcommands>,
34
35 #[command(flatten)]
36 tx: TransactionOpts,
37
38 #[arg(
40 long,
41 value_name = "BLOB_DATA_PATH",
42 conflicts_with = "legacy",
43 requires = "blob",
44 help_heading = "Transaction options"
45 )]
46 path: Option<PathBuf>,
47
48 #[command(flatten)]
49 eth: EthereumOpts,
50
51 #[arg(long)]
55 raw_unsigned: bool,
56
57 #[arg(long, requires = "from", conflicts_with = "raw_unsigned")]
59 ethsign: bool,
60}
61
62#[derive(Debug, Parser)]
63pub enum MakeTxSubcommands {
64 #[command(name = "--create")]
66 Create {
67 code: String,
69
70 sig: Option<String>,
72
73 #[arg(allow_negative_numbers = true)]
75 args: Vec<String>,
76 },
77}
78
79impl MakeTxArgs {
80 pub async fn run(self) -> Result<()> {
81 let Self { to, mut sig, mut args, command, tx, path, eth, raw_unsigned, ethsign } = self;
82
83 let blob_data = if let Some(path) = path { Some(std::fs::read(path)?) } else { None };
84
85 let code = if let Some(MakeTxSubcommands::Create {
86 code,
87 sig: constructor_sig,
88 args: constructor_args,
89 }) = command
90 {
91 sig = constructor_sig;
92 args = constructor_args;
93 Some(code)
94 } else {
95 None
96 };
97
98 let config = eth.load_config()?;
99
100 let provider = get_provider(&config)?;
101
102 let tx_builder = CastTxBuilder::new(&provider, tx.clone(), &config)
103 .await?
104 .with_to(to)
105 .await?
106 .with_code_sig_and_args(code, sig, args)
107 .await?
108 .with_blob_data(blob_data)?;
109
110 if raw_unsigned {
111 if eth.wallet.from.is_none() && tx.nonce.is_none() {
115 eyre::bail!(
116 "Missing required parameters for raw unsigned transaction. When --from is not provided, you must specify: --nonce"
117 );
118 }
119
120 let from = eth.wallet.from.unwrap_or(Address::ZERO);
122
123 let raw_tx = tx_builder.build_unsigned_raw(from).await?;
124
125 sh_println!("{raw_tx}")?;
126 return Ok(());
127 }
128
129 let is_tempo = tx_builder.is_tempo();
130
131 if ethsign {
132 let (tx, _) = tx_builder.build(config.sender).await?;
135 let signed_tx = provider.sign_transaction(tx.into_inner()).await?;
136
137 sh_println!("{signed_tx}")?;
138 return Ok(());
139 }
140
141 let signer = eth.wallet.signer().await?;
144 let from = signer.address();
145
146 tx::validate_from_address(eth.wallet.from, from)?;
147
148 if is_tempo {
153 let (ftx, _) = tx_builder.build(&signer).await?;
154
155 let signed_tx = signer.sign_request(ftx).await?;
157
158 let mut raw_tx = Vec::with_capacity(signed_tx.encode_2718_len());
160 signed_tx.encode_2718(&mut raw_tx);
161
162 let signed_tx_hex = hex::encode(&raw_tx);
163 sh_println!("0x{signed_tx_hex}")?;
164
165 return Ok(());
166 }
167
168 let (tx, _) = tx_builder.build(&signer).await?;
169
170 let tx = tx.into_inner().build(&EthereumWallet::new(signer)).await?;
171
172 let signed_tx = hex::encode(tx.encoded_2718());
173 sh_println!("0x{signed_tx}")?;
174
175 Ok(())
176 }
177}