From 4abbb30aff60b5f59532ae0edfe50ec42fbd99af Mon Sep 17 00:00:00 2001 From: Mablr <59505383+mablr@users.noreply.github.com> Date: Sun, 27 Jul 2025 00:57:07 +0200 Subject: [PATCH 1/2] fix(mktx): improve handling of `--raw-unsigned` with runtime validation - Updated the logic for handling the `--from` argument in the `run` function. - Added validation for required `nonce` parameter when `--from` is not provided, ensuring necessary transaction details are specified. - Used `Address::ZERO` as a placeholder when there is no need to query provider - unit tests --- crates/cast/src/cmd/mktx.rs | 20 ++++++++--- crates/cast/tests/cli/main.rs | 68 +++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 5 deletions(-) diff --git a/crates/cast/src/cmd/mktx.rs b/crates/cast/src/cmd/mktx.rs index df71528ddecdd..344c7b93b1083 100644 --- a/crates/cast/src/cmd/mktx.rs +++ b/crates/cast/src/cmd/mktx.rs @@ -1,11 +1,11 @@ use crate::tx::{self, CastTxBuilder}; use alloy_ens::NameOrAddress; use alloy_network::{EthereumWallet, TransactionBuilder, eip2718::Encodable2718}; -use alloy_primitives::hex; +use alloy_primitives::{Address, hex}; use alloy_provider::Provider; use alloy_signer::Signer; use clap::Parser; -use eyre::{OptionExt, Result}; +use eyre::Result; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, utils::{LoadConfig, get_provider}, @@ -49,7 +49,7 @@ pub struct MakeTxArgs { /// Generate a raw RLP-encoded unsigned transaction. /// /// Relaxes the wallet requirement. - #[arg(long, requires = "from")] + #[arg(long)] raw_unsigned: bool, /// Call `eth_signTransaction` using the `--from` argument or $ETH_FROM as sender @@ -96,7 +96,7 @@ impl MakeTxArgs { let provider = get_provider(&config)?; - let tx_builder = CastTxBuilder::new(&provider, tx, &config) + let tx_builder = CastTxBuilder::new(&provider, tx.clone(), &config) .await? .with_to(to) .await? @@ -106,7 +106,17 @@ impl MakeTxArgs { if raw_unsigned { // Build unsigned raw tx - let from = eth.wallet.from.ok_or_eyre("missing `--from` address")?; + // Check if nonce is provided when --from is not specified + // See: + if eth.wallet.from.is_none() && tx.nonce.is_none() { + eyre::bail!( + "Missing required parameters for raw unsigned transaction. When --from is not provided, you must specify: --nonce" + ); + } + + // Use zero address as placeholder for unsigned transactions + let from = eth.wallet.from.unwrap_or(Address::ZERO); + let raw_tx = tx_builder.build_unsigned_raw(from).await?; sh_println!("{raw_tx}")?; diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 82c6bfa614d0f..a8051fbc27450 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1599,6 +1599,74 @@ casttest!(mktx_raw_unsigned, |_prj, cmd| { .stdout_eq(str![[ r#"0x02e80180843b9aca008502540be4008252089400000000000000000000000000000000000000018080c0 + + "# + ]]); +}); + +casttest!(mktx_raw_unsigned_no_from_missing_chain, async |_prj, cmd| { + // As chain is not provided, a query is made to the provider to get the chain id, before the tx + // is built. Anvil is configured to use chain id 1 so that the produced tx will be the same + // as in the `mktx_raw_unsigned` test. + let (_, handle) = anvil::spawn(NodeConfig::test().with_chain_id(Some(1u64))).await; + cmd.args([ + "mktx", + "--nonce", + "0", + "--gas-limit", + "21000", + "--gas-price", + "10000000000", + "--priority-gas-price", + "1000000000", + "0x0000000000000000000000000000000000000001", + "--raw-unsigned", + "--rpc-url", + &handle.http_endpoint(), + ]) + .assert_success() + .stdout_eq(str![[ + r#"0x02e80180843b9aca008502540be4008252089400000000000000000000000000000000000000018080c0 + +"# + ]]); +}); + +casttest!(mktx_raw_unsigned_no_from_missing_gas_pricing, async |_prj, cmd| { + let (_, handle) = anvil::spawn(NodeConfig::test()).await; + cmd.args([ + "mktx", + "--nonce", + "0", + "0x0000000000000000000000000000000000000001", + "--raw-unsigned", + "--rpc-url", + &handle.http_endpoint(), + ]) + .assert_success() + .stdout_eq(str![[ + r#"0x02e5827a69800184773594018252089400000000000000000000000000000000000000018080c0 + +"# + ]]); +}); + +casttest!(mktx_raw_unsigned_no_from_missing_nonce, |_prj, cmd| { + cmd.args([ + "mktx", + "--chain", + "1", + "--gas-limit", + "21000", + "--gas-price", + "20000000000", + "0x742d35Cc6634C0532925a3b8D6Ac6F67C9c2b7FD", + "--raw-unsigned", + ]) + .assert_failure() + .stderr_eq(str![[ + r#"Error: Missing required parameters for raw unsigned transaction. When --from is not provided, you must specify: --nonce + "# ]]); }); From 6c6ccd3308af6f07ad96dd5e192caf36a9ebeeda Mon Sep 17 00:00:00 2001 From: Mablr <59505383+mablr@users.noreply.github.com> Date: Sun, 27 Jul 2025 01:03:16 +0200 Subject: [PATCH 2/2] fix: fmt --- crates/cast/tests/cli/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index a8051fbc27450..4b0df9b59b120 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -1599,8 +1599,7 @@ casttest!(mktx_raw_unsigned, |_prj, cmd| { .stdout_eq(str![[ r#"0x02e80180843b9aca008502540be4008252089400000000000000000000000000000000000000018080c0 - - "# +"# ]]); });