Skip to content

Commit dfa7b91

Browse files
authored
fix(cast): improve handling of mktx --raw-unsigned with runtime validation (#11111)
* 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 * fix: fmt
1 parent a81a955 commit dfa7b91

File tree

2 files changed

+82
-5
lines changed

2 files changed

+82
-5
lines changed

crates/cast/src/cmd/mktx.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use crate::tx::{self, CastTxBuilder};
22
use alloy_ens::NameOrAddress;
33
use alloy_network::{EthereumWallet, TransactionBuilder, eip2718::Encodable2718};
4-
use alloy_primitives::hex;
4+
use alloy_primitives::{Address, hex};
55
use alloy_provider::Provider;
66
use alloy_signer::Signer;
77
use clap::Parser;
8-
use eyre::{OptionExt, Result};
8+
use eyre::Result;
99
use foundry_cli::{
1010
opts::{EthereumOpts, TransactionOpts},
1111
utils::{LoadConfig, get_provider},
@@ -49,7 +49,7 @@ pub struct MakeTxArgs {
4949
/// Generate a raw RLP-encoded unsigned transaction.
5050
///
5151
/// Relaxes the wallet requirement.
52-
#[arg(long, requires = "from")]
52+
#[arg(long)]
5353
raw_unsigned: bool,
5454

5555
/// Call `eth_signTransaction` using the `--from` argument or $ETH_FROM as sender
@@ -96,7 +96,7 @@ impl MakeTxArgs {
9696

9797
let provider = get_provider(&config)?;
9898

99-
let tx_builder = CastTxBuilder::new(&provider, tx, &config)
99+
let tx_builder = CastTxBuilder::new(&provider, tx.clone(), &config)
100100
.await?
101101
.with_to(to)
102102
.await?
@@ -106,7 +106,17 @@ impl MakeTxArgs {
106106

107107
if raw_unsigned {
108108
// Build unsigned raw tx
109-
let from = eth.wallet.from.ok_or_eyre("missing `--from` address")?;
109+
// Check if nonce is provided when --from is not specified
110+
// See: <https://github.com/foundry-rs/foundry/issues/11110>
111+
if eth.wallet.from.is_none() && tx.nonce.is_none() {
112+
eyre::bail!(
113+
"Missing required parameters for raw unsigned transaction. When --from is not provided, you must specify: --nonce"
114+
);
115+
}
116+
117+
// Use zero address as placeholder for unsigned transactions
118+
let from = eth.wallet.from.unwrap_or(Address::ZERO);
119+
110120
let raw_tx = tx_builder.build_unsigned_raw(from).await?;
111121

112122
sh_println!("{raw_tx}")?;

crates/cast/tests/cli/main.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1603,6 +1603,73 @@ casttest!(mktx_raw_unsigned, |_prj, cmd| {
16031603
]]);
16041604
});
16051605

1606+
casttest!(mktx_raw_unsigned_no_from_missing_chain, async |_prj, cmd| {
1607+
// As chain is not provided, a query is made to the provider to get the chain id, before the tx
1608+
// is built. Anvil is configured to use chain id 1 so that the produced tx will be the same
1609+
// as in the `mktx_raw_unsigned` test.
1610+
let (_, handle) = anvil::spawn(NodeConfig::test().with_chain_id(Some(1u64))).await;
1611+
cmd.args([
1612+
"mktx",
1613+
"--nonce",
1614+
"0",
1615+
"--gas-limit",
1616+
"21000",
1617+
"--gas-price",
1618+
"10000000000",
1619+
"--priority-gas-price",
1620+
"1000000000",
1621+
"0x0000000000000000000000000000000000000001",
1622+
"--raw-unsigned",
1623+
"--rpc-url",
1624+
&handle.http_endpoint(),
1625+
])
1626+
.assert_success()
1627+
.stdout_eq(str![[
1628+
r#"0x02e80180843b9aca008502540be4008252089400000000000000000000000000000000000000018080c0
1629+
1630+
"#
1631+
]]);
1632+
});
1633+
1634+
casttest!(mktx_raw_unsigned_no_from_missing_gas_pricing, async |_prj, cmd| {
1635+
let (_, handle) = anvil::spawn(NodeConfig::test()).await;
1636+
cmd.args([
1637+
"mktx",
1638+
"--nonce",
1639+
"0",
1640+
"0x0000000000000000000000000000000000000001",
1641+
"--raw-unsigned",
1642+
"--rpc-url",
1643+
&handle.http_endpoint(),
1644+
])
1645+
.assert_success()
1646+
.stdout_eq(str![[
1647+
r#"0x02e5827a69800184773594018252089400000000000000000000000000000000000000018080c0
1648+
1649+
"#
1650+
]]);
1651+
});
1652+
1653+
casttest!(mktx_raw_unsigned_no_from_missing_nonce, |_prj, cmd| {
1654+
cmd.args([
1655+
"mktx",
1656+
"--chain",
1657+
"1",
1658+
"--gas-limit",
1659+
"21000",
1660+
"--gas-price",
1661+
"20000000000",
1662+
"0x742d35Cc6634C0532925a3b8D6Ac6F67C9c2b7FD",
1663+
"--raw-unsigned",
1664+
])
1665+
.assert_failure()
1666+
.stderr_eq(str![[
1667+
r#"Error: Missing required parameters for raw unsigned transaction. When --from is not provided, you must specify: --nonce
1668+
1669+
"#
1670+
]]);
1671+
});
1672+
16061673
casttest!(mktx_ethsign, async |_prj, cmd| {
16071674
let (_api, handle) = anvil::spawn(NodeConfig::test()).await;
16081675
let rpc = handle.http_endpoint();

0 commit comments

Comments
 (0)