1use crate::tx::{self, CastTxBuilder};
2use alloy_consensus::{SignableTransaction, Signed};
3use alloy_eips::Encodable2718;
4use alloy_ens::NameOrAddress;
5use alloy_network::{Ethereum, EthereumWallet, Network, TransactionBuilder};
6use alloy_primitives::{Address, hex};
7use alloy_provider::Provider;
8use alloy_signer::{Signature, Signer};
9use clap::Parser;
10use eyre::Result;
11use foundry_cli::{
12 opts::{EthereumOpts, TransactionOpts},
13 utils::LoadConfig,
14};
15use foundry_common::{FoundryTransactionBuilder, provider::ProviderBuilder};
16use std::{path::PathBuf, str::FromStr};
17use tempo_alloy::TempoNetwork;
18
19#[derive(Debug, Parser)]
21pub struct MakeTxArgs {
22 #[arg(value_parser = NameOrAddress::from_str)]
26 to: Option<NameOrAddress>,
27
28 sig: Option<String>,
30
31 #[arg(allow_negative_numbers = true)]
33 args: Vec<String>,
34
35 #[command(subcommand)]
36 command: Option<MakeTxSubcommands>,
37
38 #[command(flatten)]
39 tx: TransactionOpts,
40
41 #[arg(
43 long,
44 value_name = "BLOB_DATA_PATH",
45 conflicts_with = "legacy",
46 requires = "blob",
47 help_heading = "Transaction options"
48 )]
49 path: Option<PathBuf>,
50
51 #[command(flatten)]
52 eth: EthereumOpts,
53
54 #[arg(long)]
58 raw_unsigned: bool,
59
60 #[arg(long, requires = "from", conflicts_with = "raw_unsigned")]
62 ethsign: bool,
63}
64
65#[derive(Debug, Parser)]
66pub enum MakeTxSubcommands {
67 #[command(name = "--create")]
69 Create {
70 code: String,
72
73 sig: Option<String>,
75
76 #[arg(allow_negative_numbers = true)]
78 args: Vec<String>,
79 },
80}
81
82impl MakeTxArgs {
83 pub async fn run(self) -> Result<()> {
84 if self.tx.tempo.is_tempo() {
85 self.run_generic::<TempoNetwork>().await
86 } else {
87 self.run_generic::<Ethereum>().await
88 }
89 }
90
91 pub async fn run_generic<N: Network>(self) -> Result<()>
92 where
93 N::TxEnvelope: From<Signed<N::UnsignedTx>>,
94 N::UnsignedTx: SignableTransaction<Signature>,
95 N::TransactionRequest: FoundryTransactionBuilder<N>,
96 {
97 let Self { to, mut sig, mut args, command, tx, path, eth, raw_unsigned, ethsign } = self;
98
99 let print_sponsor_hash = tx.tempo.print_sponsor_hash;
100
101 let blob_data = if let Some(path) = path { Some(std::fs::read(path)?) } else { None };
102
103 let code = if let Some(MakeTxSubcommands::Create {
104 code,
105 sig: constructor_sig,
106 args: constructor_args,
107 }) = command
108 {
109 sig = constructor_sig;
110 args = constructor_args;
111 Some(code)
112 } else {
113 None
114 };
115
116 let config = eth.load_config()?;
117
118 let provider = ProviderBuilder::<N>::from_config(&config)?.build()?;
119
120 let tx_builder = CastTxBuilder::new(&provider, tx.clone(), &config)
121 .await?
122 .with_to(to)
123 .await?
124 .with_code_sig_and_args(code, sig, args)
125 .await?
126 .with_blob_data(blob_data)?;
127
128 if print_sponsor_hash {
130 let signer = eth.wallet.signer().await?;
133 let from = signer.address();
134 let (tx, _) = tx_builder.build(from).await?;
135 let hash = tx.compute_sponsor_hash(from).ok_or_else(|| {
136 eyre::eyre!("This network does not support sponsored transactions")
137 })?;
138 sh_println!("{hash:?}")?;
139 return Ok(());
140 }
141
142 if raw_unsigned {
143 if eth.wallet.from.is_none() && tx.nonce.is_none() {
147 eyre::bail!(
148 "Missing required parameters for raw unsigned transaction. When --from is not provided, you must specify: --nonce"
149 );
150 }
151
152 let from = eth.wallet.from.unwrap_or(Address::ZERO);
154
155 let (tx, _) = tx_builder.build(from).await?;
156 let raw_tx = hex::encode_prefixed(tx.build_unsigned()?.encoded_for_signing());
157
158 sh_println!("{raw_tx}")?;
159 return Ok(());
160 }
161
162 if ethsign {
163 let (tx, _) = tx_builder.build(config.sender).await?;
166 let signed_tx = provider.sign_transaction(tx).await?;
167
168 sh_println!("{signed_tx}")?;
169 return Ok(());
170 }
171
172 let signer = eth.wallet.signer().await?;
175 let from = signer.address();
176
177 tx::validate_from_address(eth.wallet.from, from)?;
178
179 let (tx, _) = tx_builder.build(&signer).await?;
180
181 let tx = tx.build(&EthereumWallet::new(signer)).await?;
182
183 let signed_tx = hex::encode(tx.encoded_2718());
184 sh_println!("0x{signed_tx}")?;
185
186 Ok(())
187 }
188}