Skip to content

Commit 70792cd

Browse files
committed
refactor: similar code in cast send and cast mktx
1 parent 4c4f5f9 commit 70792cd

File tree

5 files changed

+95
-91
lines changed

5 files changed

+95
-91
lines changed

crates/cast/bin/cmd/mktx.rs

+5-41
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use cast::TxBuilder;
1+
use crate::tx;
22
use clap::Parser;
33
use ethers_core::types::NameOrAddress;
44
use ethers_middleware::MiddlewareBuilder;
@@ -75,10 +75,7 @@ impl MakeTxArgs {
7575
None
7676
};
7777

78-
// ensure mandatory fields are provided
79-
if code.is_none() && to.is_none() {
80-
eyre::bail!("Must specify a recipient address or contract code to deploy");
81-
}
78+
tx::validate_to_address(&code, &to)?;
8279

8380
let config = Config::from(&eth);
8481
let provider = utils::get_provider(&config)?;
@@ -89,49 +86,16 @@ impl MakeTxArgs {
8986
let signer = eth.wallet.signer(chain.id()).await?;
9087
let from = signer.address();
9188

92-
// prevent misconfigured hwlib from sending a transaction that defies
93-
// user-specified --from
94-
if let Some(specified_from) = eth.wallet.from {
95-
if specified_from != from.to_alloy() {
96-
eyre::bail!(
97-
"\
98-
The specified sender via CLI/env vars does not match the sender configured via
99-
the hardware wallet's HD Path.
100-
Please use the `--hd-path <PATH>` parameter to specify the BIP32 Path which
101-
corresponds to the sender, or let foundry automatically detect it by not specifying any sender address."
102-
)
103-
}
104-
}
89+
tx::validate_from_address(eth.wallet.from, from.to_alloy())?;
10590

10691
if resend {
10792
tx.nonce = Some(provider.get_transaction_count(from, None).await?.to_alloy());
10893
}
10994

11095
let provider = provider.with_signer(signer);
11196

112-
let params = sig.as_deref().map(|sig| (sig, args));
113-
let mut builder = TxBuilder::new(&provider, from, to, chain, tx.legacy).await?;
114-
builder
115-
.etherscan_api_key(api_key)
116-
.gas(tx.gas_limit)
117-
.gas_price(tx.gas_price)
118-
.priority_gas_price(tx.priority_gas_price)
119-
.value(tx.value)
120-
.nonce(tx.nonce);
121-
122-
if let Some(code) = code {
123-
let mut data = hex::decode(code)?;
124-
125-
if let Some((sig, args)) = params {
126-
let (mut sigdata, _) = builder.create_args(sig, args).await?;
127-
data.append(&mut sigdata);
128-
}
129-
130-
builder.set_data(data);
131-
} else {
132-
builder.args(params).await?;
133-
}
134-
let (mut tx, _) = builder.build();
97+
let (mut tx, _) =
98+
tx::build_tx(&provider, from, to, code, sig, args, tx, chain, api_key).await?;
13599

136100
// Fill nonce, gas limit, gas price, and max priority fee per gas if needed
137101
provider.fill_transaction(&mut tx, None).await?;

crates/cast/bin/cmd/send.rs

+14-48
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use cast::{Cast, TxBuilder};
1+
use crate::tx;
2+
use cast::Cast;
23
use clap::Parser;
34
use ethers_core::types::NameOrAddress;
45
use ethers_middleware::MiddlewareBuilder;
@@ -82,7 +83,7 @@ impl SendTxArgs {
8283
let SendTxArgs {
8384
eth,
8485
to,
85-
sig,
86+
mut sig,
8687
cast_async,
8788
mut args,
8889
mut tx,
@@ -93,24 +94,20 @@ impl SendTxArgs {
9394
unlocked,
9495
} = self;
9596

96-
let mut sig = sig.unwrap_or_default();
9797
let code = if let Some(SendTxSubcommands::Create {
9898
code,
9999
sig: constructor_sig,
100100
args: constructor_args,
101101
}) = command
102102
{
103-
sig = constructor_sig.unwrap_or_default();
103+
sig = constructor_sig;
104104
args = constructor_args;
105105
Some(code)
106106
} else {
107107
None
108108
};
109109

110-
// ensure mandatory fields are provided
111-
if code.is_none() && to.is_none() {
112-
eyre::bail!("Must specify a recipient address or contract code to deploy");
113-
}
110+
tx::validate_to_address(&code, &to)?;
114111

115112
let config = Config::from(&eth);
116113
let provider = utils::get_provider(&config)?;
@@ -155,7 +152,8 @@ impl SendTxArgs {
155152
config.sender.to_ethers(),
156153
to,
157154
code,
158-
(sig, args),
155+
sig,
156+
args,
159157
tx,
160158
chain,
161159
api_key,
@@ -173,19 +171,7 @@ impl SendTxArgs {
173171
let signer = eth.wallet.signer(chain.id()).await?;
174172
let from = signer.address();
175173

176-
// prevent misconfigured hwlib from sending a transaction that defies
177-
// user-specified --from
178-
if let Some(specified_from) = eth.wallet.from {
179-
if specified_from != from.to_alloy() {
180-
eyre::bail!(
181-
"\
182-
The specified sender via CLI/env vars does not match the sender configured via
183-
the hardware wallet's HD Path.
184-
Please use the `--hd-path <PATH>` parameter to specify the BIP32 Path which
185-
corresponds to the sender, or let foundry automatically detect it by not specifying any sender address."
186-
)
187-
}
188-
}
174+
tx::validate_from_address(eth.wallet.from, from.to_alloy())?;
189175

190176
if resend {
191177
tx.nonce = Some(provider.get_transaction_count(from, None).await?.to_alloy());
@@ -198,7 +184,8 @@ corresponds to the sender, or let foundry automatically detect it by not specify
198184
from,
199185
to,
200186
code,
201-
(sig, args),
187+
sig,
188+
args,
202189
tx,
203190
chain,
204191
api_key,
@@ -217,7 +204,8 @@ async fn cast_send<M: Middleware, F: Into<NameOrAddress>, T: Into<NameOrAddress>
217204
from: F,
218205
to: Option<T>,
219206
code: Option<String>,
220-
args: (String, Vec<String>),
207+
sig: Option<String>,
208+
args: Vec<String>,
221209
tx: TransactionOpts,
222210
chain: Chain,
223211
etherscan_api_key: Option<String>,
@@ -228,30 +216,8 @@ async fn cast_send<M: Middleware, F: Into<NameOrAddress>, T: Into<NameOrAddress>
228216
where
229217
M::Error: 'static,
230218
{
231-
let (sig, params) = args;
232-
let params = if !sig.is_empty() { Some((&sig[..], params)) } else { None };
233-
let mut builder = TxBuilder::new(&provider, from, to, chain, tx.legacy).await?;
234-
builder
235-
.etherscan_api_key(etherscan_api_key)
236-
.gas(tx.gas_limit)
237-
.gas_price(tx.gas_price)
238-
.priority_gas_price(tx.priority_gas_price)
239-
.value(tx.value)
240-
.nonce(tx.nonce);
241-
242-
if let Some(code) = code {
243-
let mut data = hex::decode(code)?;
244-
245-
if let Some((sig, args)) = params {
246-
let (mut sigdata, _) = builder.create_args(sig, args).await?;
247-
data.append(&mut sigdata);
248-
}
249-
250-
builder.set_data(data);
251-
} else {
252-
builder.args(params).await?;
253-
};
254-
let builder_output = builder.build();
219+
let builder_output =
220+
tx::build_tx(&provider, from, to, code, sig, args, tx, chain, etherscan_api_key).await?;
255221

256222
let cast = Cast::new(provider);
257223

crates/cast/bin/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use std::time::Instant;
2626

2727
pub mod cmd;
2828
pub mod opts;
29+
pub mod tx;
2930

3031
use opts::{Cast as Opts, CastSubcommand, ToBaseArgs};
3132

crates/cast/bin/tx.rs

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use alloy_primitives::Address;
2+
use cast::{TxBuilder, TxBuilderOutput};
3+
use ethers_core::types::NameOrAddress;
4+
use ethers_providers::Middleware;
5+
use eyre::Result;
6+
use foundry_cli::opts::TransactionOpts;
7+
use foundry_config::Chain;
8+
9+
/// Prevents a misconfigured hwlib from sending a transaction that defies user-specified --from
10+
pub fn validate_from_address(
11+
specified_from: Option<Address>,
12+
signer_address: Address,
13+
) -> Result<()> {
14+
if let Some(specified_from) = specified_from {
15+
if specified_from != signer_address {
16+
eyre::bail!(
17+
"\
18+
The specified sender via CLI/env vars does not match the sender configured via
19+
the hardware wallet's HD Path.
20+
Please use the `--hd-path <PATH>` parameter to specify the BIP32 Path which
21+
corresponds to the sender, or let foundry automatically detect it by not specifying any sender address."
22+
)
23+
}
24+
}
25+
Ok(())
26+
}
27+
28+
/// Ensures the transaction is either a contract deployment or a recipient address is specified
29+
pub fn validate_to_address(code: &Option<String>, to: &Option<NameOrAddress>) -> Result<()> {
30+
if code.is_none() && to.is_none() {
31+
eyre::bail!("Must specify a recipient address or contract code to deploy");
32+
}
33+
Ok(())
34+
}
35+
36+
#[allow(clippy::too_many_arguments)]
37+
pub async fn build_tx<M: Middleware, F: Into<NameOrAddress>, T: Into<NameOrAddress>>(
38+
provider: &M,
39+
from: F,
40+
to: Option<T>,
41+
code: Option<String>,
42+
sig: Option<String>,
43+
args: Vec<String>,
44+
tx: TransactionOpts,
45+
chain: impl Into<Chain>,
46+
etherscan_api_key: Option<String>,
47+
) -> Result<TxBuilderOutput> {
48+
let mut builder = TxBuilder::new(provider, from, to, chain, tx.legacy).await?;
49+
builder
50+
.etherscan_api_key(etherscan_api_key)
51+
.gas(tx.gas_limit)
52+
.gas_price(tx.gas_price)
53+
.priority_gas_price(tx.priority_gas_price)
54+
.value(tx.value)
55+
.nonce(tx.nonce);
56+
57+
let params = sig.as_deref().map(|sig| (sig, args));
58+
if let Some(code) = code {
59+
let mut data = hex::decode(code)?;
60+
61+
if let Some((sig, args)) = params {
62+
let (mut sigdata, _) = builder.create_args(sig, args).await?;
63+
data.append(&mut sigdata);
64+
}
65+
66+
builder.set_data(data);
67+
} else {
68+
builder.args(params).await?;
69+
}
70+
71+
let builder_output = builder.build();
72+
Ok(builder_output)
73+
}

crates/cast/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,15 @@ use std::{
3131
sync::atomic::{AtomicBool, Ordering},
3232
};
3333
use tokio::signal::ctrl_c;
34-
use tx::{TxBuilderOutput, TxBuilderPeekOutput};
34+
use tx::TxBuilderPeekOutput;
3535

3636
pub use foundry_evm::*;
3737
pub use rusoto_core::{
3838
credential::ChainProvider as AwsChainProvider, region::Region as AwsRegion,
3939
request::HttpClient as AwsHttpClient, Client as AwsClient,
4040
};
4141
pub use rusoto_kms::KmsClient;
42-
pub use tx::TxBuilder;
42+
pub use tx::{TxBuilder, TxBuilderOutput};
4343

4444
pub mod base;
4545
pub mod errors;

0 commit comments

Comments
 (0)