1use crate::tx::{self, CastTxBuilder};
2use alloy_ens::NameOrAddress;
3use alloy_network::{eip2718::Encodable2718, EthereumWallet, TransactionBuilder};
4use alloy_primitives::hex;
5use alloy_provider::Provider;
6use alloy_signer::Signer;
7use clap::Parser;
8use eyre::{OptionExt, Result};
9use foundry_cli::{
10 opts::{EthereumOpts, TransactionOpts},
11 utils::{get_provider, LoadConfig},
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 args: Vec<String>,
29
30 #[command(subcommand)]
31 command: Option<MakeTxSubcommands>,
32
33 #[command(flatten)]
34 tx: TransactionOpts,
35
36 #[arg(
38 long,
39 value_name = "BLOB_DATA_PATH",
40 conflicts_with = "legacy",
41 requires = "blob",
42 help_heading = "Transaction options"
43 )]
44 path: Option<PathBuf>,
45
46 #[command(flatten)]
47 eth: EthereumOpts,
48
49 #[arg(long, requires = "from")]
53 raw_unsigned: bool,
54
55 #[arg(long, requires = "from", conflicts_with = "raw_unsigned")]
57 ethsign: bool,
58}
59
60#[derive(Debug, Parser)]
61pub enum MakeTxSubcommands {
62 #[command(name = "--create")]
64 Create {
65 code: String,
67
68 sig: Option<String>,
70
71 args: Vec<String>,
73 },
74}
75
76impl MakeTxArgs {
77 pub async fn run(self) -> Result<()> {
78 let Self { to, mut sig, mut args, command, tx, path, eth, raw_unsigned, ethsign } = self;
79
80 let blob_data = if let Some(path) = path { Some(std::fs::read(path)?) } else { None };
81
82 let code = if let Some(MakeTxSubcommands::Create {
83 code,
84 sig: constructor_sig,
85 args: constructor_args,
86 }) = command
87 {
88 sig = constructor_sig;
89 args = constructor_args;
90 Some(code)
91 } else {
92 None
93 };
94
95 let config = eth.load_config()?;
96
97 let provider = get_provider(&config)?;
98
99 let tx_builder = CastTxBuilder::new(&provider, tx, &config)
100 .await?
101 .with_to(to)
102 .await?
103 .with_code_sig_and_args(code, sig, args)
104 .await?
105 .with_blob_data(blob_data)?;
106
107 if raw_unsigned {
108 let from = eth.wallet.from.ok_or_eyre("missing `--from` address")?;
110 let raw_tx = tx_builder.build_unsigned_raw(from).await?;
111
112 sh_println!("{raw_tx}")?;
113 return Ok(());
114 }
115
116 if ethsign {
117 let (tx, _) = tx_builder.build(config.sender).await?;
120 let signed_tx = provider.sign_transaction(tx).await?;
121
122 sh_println!("{signed_tx}")?;
123 return Ok(());
124 }
125
126 let signer = eth.wallet.signer().await?;
129 let from = signer.address();
130
131 tx::validate_from_address(eth.wallet.from, from)?;
132
133 let (tx, _) = tx_builder.build(&signer).await?;
134
135 let tx = tx.build(&EthereumWallet::new(signer)).await?;
136
137 let signed_tx = hex::encode(tx.encoded_2718());
138 sh_println!("0x{signed_tx}")?;
139
140 Ok(())
141 }
142}