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