Skip to content

Commit 123ad0a

Browse files
authored
feat(forge): replace TypedTransaction with TransactionWithMetadata on ScriptSequence (#1891)
* replace TypedTransaction with TransactionWithMetadata * move ScriptSequence into its own module * remove create2_contracts from ScriptSequence * use function signature instead for tx metadata * feed tx hash to tx metadata when adding to pending * remove unused branch * update progress bar after adding to pending * use decoder to decode transaction metadata * add test for new log format * dont skip vec serialization on sequence * make arguments an Option * return err when you can * use flat_map on add_libraries * remove duplicate unwrap of path * nits 🛠️ 🛠️
1 parent 0dabf63 commit 123ad0a

File tree

13 files changed

+599
-384
lines changed

13 files changed

+599
-384
lines changed

cli/src/cmd/forge/script/broadcast.rs

Lines changed: 39 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
use super::{
2+
sequence::{ScriptSequence, TransactionWithMetadata},
3+
*,
4+
};
15
use crate::{
2-
cmd::{
3-
forge::script::receipts::wait_for_receipts, has_different_gas_calc, ScriptSequence,
4-
VerifyBundle,
5-
},
6+
cmd::{forge::script::receipts::wait_for_receipts, has_different_gas_calc},
67
init_progress,
78
opts::WalletType,
89
update_progress,
@@ -11,15 +12,14 @@ use crate::{
1112
use ethers::{
1213
prelude::{Http, Provider, RetryClient, Signer, SignerMiddleware, TxHash},
1314
providers::Middleware,
14-
types::{transaction::eip2718::TypedTransaction, Chain},
15+
types::transaction::eip2718::TypedTransaction,
1516
utils::format_units,
1617
};
18+
use foundry_config::Chain;
1719
use futures::StreamExt;
1820
use indicatif::{ProgressBar, ProgressStyle};
1921
use std::{cmp::min, fmt, sync::Arc};
2022

21-
use super::*;
22-
2323
impl ScriptArgs {
2424
/// Sends the transactions which haven't been broadcasted yet.
2525
pub async fn send_transactions(
@@ -32,8 +32,8 @@ impl ScriptArgs {
3232

3333
if already_broadcasted < deployment_sequence.transactions.len() {
3434
let required_addresses = deployment_sequence
35-
.transactions
36-
.iter()
35+
.typed_transactions()
36+
.into_iter()
3737
.skip(already_broadcasted)
3838
.map(|tx| *tx.from().expect("No sender for onchain transaction!"))
3939
.collect();
@@ -50,7 +50,7 @@ impl ScriptArgs {
5050

5151
// Make a one-time gas price estimation
5252
let (gas_price, eip1559_fees) = {
53-
match deployment_sequence.transactions.front().unwrap() {
53+
match deployment_sequence.transactions.front().unwrap().typed_tx() {
5454
TypedTransaction::Legacy(_) | TypedTransaction::Eip2930(_) => {
5555
(provider.get_gas_price().await.ok(), None)
5656
}
@@ -63,8 +63,8 @@ impl ScriptArgs {
6363
// Iterate through transactions, matching the `from` field with the associated
6464
// wallet. Then send the transaction. Panics if we find a unknown `from`
6565
let sequence = deployment_sequence
66-
.transactions
67-
.iter()
66+
.typed_transactions()
67+
.into_iter()
6868
.skip(already_broadcasted)
6969
.map(|tx| {
7070
let from = *tx.from().expect("No sender for onchain transaction!");
@@ -115,15 +115,14 @@ impl ScriptArgs {
115115
let tx_hash = self.send_transaction(tx, signer, sequential_broadcast, fork_url);
116116

117117
if sequential_broadcast {
118+
let tx_hash = tx_hash.await?;
119+
deployment_sequence.add_pending(index, tx_hash);
120+
118121
update_progress!(pb, (index + already_broadcasted));
119122
index += 1;
120123

121-
wait_for_receipts(
122-
vec![tx_hash.await?],
123-
deployment_sequence,
124-
provider.clone(),
125-
)
126-
.await?;
124+
wait_for_receipts(vec![tx_hash], deployment_sequence, provider.clone())
125+
.await?;
127126
} else {
128127
pending_transactions.push(tx_hash);
129128
}
@@ -135,11 +134,12 @@ impl ScriptArgs {
135134
let mut tx_hashes = vec![];
136135

137136
while let Some(tx_hash) = buffer.next().await {
138-
update_progress!(pb, (index + already_broadcasted));
139-
index += 1;
140137
let tx_hash = tx_hash?;
141-
deployment_sequence.add_pending(tx_hash);
138+
deployment_sequence.add_pending(index, tx_hash);
142139
tx_hashes.push(tx_hash);
140+
141+
update_progress!(pb, (index + already_broadcasted));
142+
index += 1;
143143
}
144144

145145
// Checkpoint save
@@ -207,8 +207,10 @@ impl ScriptArgs {
207207
) -> eyre::Result<()> {
208208
if let Some(txs) = transactions {
209209
if script_config.evm_opts.fork_url.is_some() {
210-
let (gas_filled_txs, create2_contracts) =
211-
self.execute_transactions(txs, script_config, decoder).await.map_err(|_| {
210+
let gas_filled_txs = self
211+
.execute_transactions(txs, script_config, decoder, &verify.known_contracts)
212+
.await
213+
.map_err(|_| {
212214
eyre::eyre!(
213215
"One or more transactions failed when simulating the
214216
on-chain version. Check the trace by re-running with `-vvv`"
@@ -230,10 +232,6 @@ impl ScriptArgs {
230232

231233
deployment_sequence.add_libraries(libraries);
232234

233-
create2_contracts
234-
.into_iter()
235-
.for_each(|addr| deployment_sequence.add_create2(addr));
236-
237235
if self.broadcast {
238236
self.send_transactions(&mut deployment_sequence, &fork_url).await?;
239237
if self.verify {
@@ -255,35 +253,35 @@ impl ScriptArgs {
255253
/// and/or gas calculations).
256254
async fn handle_chain_requirements(
257255
&self,
258-
txes: VecDeque<TypedTransaction>,
256+
txes: VecDeque<TransactionWithMetadata>,
259257
provider: Arc<Provider<RetryClient<Http>>>,
260258
chain: u64,
261-
) -> eyre::Result<VecDeque<TypedTransaction>> {
262-
let is_legacy =
263-
self.legacy || Chain::try_from(chain).map(|x| Chain::is_legacy(&x)).unwrap_or_default();
259+
) -> eyre::Result<VecDeque<TransactionWithMetadata>> {
260+
let mut is_legacy = self.legacy;
261+
if let Chain::Named(chain) = Chain::from(chain) {
262+
is_legacy |= chain.is_legacy();
263+
};
264264

265-
let mut new_txes: VecDeque<TypedTransaction> = VecDeque::new();
265+
let mut new_txes = VecDeque::new();
266266
let mut total_gas = U256::zero();
267-
for tx in txes.into_iter() {
268-
let mut tx = if is_legacy {
269-
TypedTransaction::Legacy(tx.into())
270-
} else {
271-
TypedTransaction::Eip1559(tx.into())
272-
};
267+
for mut tx in txes.into_iter() {
268+
tx.change_type(is_legacy);
269+
270+
let typed_tx = tx.typed_tx_mut();
273271

274272
if has_different_gas_calc(chain) {
275-
tx.set_gas(provider.estimate_gas(&tx).await?);
273+
typed_tx.set_gas(provider.estimate_gas(typed_tx).await?);
276274
}
277275

278-
total_gas += *tx.gas().expect("");
276+
total_gas += *typed_tx.gas().expect("gas is set");
279277

280278
new_txes.push_back(tx);
281279
}
282280

283281
// We don't store it in the transactions, since we want the most updated value. Right before
284282
// broadcasting.
285283
let per_gas = {
286-
match new_txes.front().unwrap() {
284+
match new_txes.front().unwrap().typed_tx() {
287285
TypedTransaction::Legacy(_) | TypedTransaction::Eip2930(_) => {
288286
provider.get_gas_price().await?
289287
}

cli/src/cmd/forge/script/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
cmd::{get_cached_entry_by_name, unwrap_contracts, VerifyBundle},
2+
cmd::{get_cached_entry_by_name, unwrap_contracts},
33
compile,
44
opts::forge::ContractInfo,
55
};

cli/src/cmd/forge/script/cmd.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
use crate::{
2-
cmd::{unwrap_contracts, ScriptSequence},
3-
utils::get_http_provider,
4-
};
1+
use crate::{cmd::unwrap_contracts, utils::get_http_provider};
52

63
use ethers::{
74
prelude::{artifacts::CompactContractBytecode, ArtifactId, Middleware, Signer},
@@ -10,7 +7,7 @@ use ethers::{
107
use forge::executor::opts::EvmOpts;
118
use foundry_config::{figment::Figment, Config};
129

13-
use super::*;
10+
use super::{sequence::ScriptSequence, *};
1411

1512
impl ScriptArgs {
1613
pub async fn run_script(mut self) -> eyre::Result<()> {

cli/src/cmd/forge/script/executor.rs

Lines changed: 48 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use crate::{cmd::needs_setup, utils};
22

3-
use cast::executor::inspector::DEFAULT_CREATE2_DEPLOYER;
43
use ethers::{
5-
prelude::NameOrAddress,
64
solc::artifacts::CompactContractBytecode,
75
types::{transaction::eip2718::TypedTransaction, Address, U256},
86
};
@@ -13,7 +11,7 @@ use forge::{
1311

1412
use std::collections::VecDeque;
1513

16-
use crate::cmd::forge::script::*;
14+
use crate::cmd::forge::script::{sequence::TransactionWithMetadata, *};
1715

1816
impl ScriptArgs {
1917
/// Locally deploys and executes the contract method that will collect all broadcastable
@@ -73,69 +71,72 @@ impl ScriptArgs {
7371
transactions: VecDeque<TypedTransaction>,
7472
script_config: &ScriptConfig,
7573
decoder: &mut CallTraceDecoder,
76-
) -> eyre::Result<(VecDeque<TypedTransaction>, Vec<Address>)> {
74+
contracts: &BTreeMap<ArtifactId, (Abi, Vec<u8>)>,
75+
) -> eyre::Result<VecDeque<TransactionWithMetadata>> {
7776
let mut runner = self.prepare_runner(script_config, script_config.evm_opts.sender).await;
78-
7977
let mut failed = false;
8078
let mut sum_gas = 0;
81-
let mut final_txs = transactions.clone();
82-
let mut create2_contracts = vec![];
8379

8480
if script_config.evm_opts.verbosity > 3 {
8581
println!("==========================");
8682
println!("Simulated On-chain Traces:\n");
8783
}
8884

89-
transactions
85+
let address_to_abi: BTreeMap<Address, (String, &Abi)> = decoder
86+
.contracts
87+
.iter()
88+
.filter_map(|(addr, contract_name)| {
89+
if let Some((_, (abi, _))) =
90+
contracts.iter().find(|(artifact, _)| artifact.name == *contract_name)
91+
{
92+
return Some((*addr, (contract_name.clone(), abi)))
93+
}
94+
None
95+
})
96+
.collect();
97+
98+
let final_txs: VecDeque<TransactionWithMetadata> = transactions
9099
.into_iter()
91100
.map(|tx| match tx {
92-
TypedTransaction::Legacy(tx) => (tx.from, tx.to, tx.data, tx.value),
93-
_ => unreachable!(),
94-
})
95-
.map(|(from, to, data, value)| {
96-
runner
97-
.simulate(
98-
from.expect("Transaction doesn't have a `from` address at execution time"),
99-
to,
100-
data,
101-
value,
102-
)
103-
.expect("Internal EVM error")
104-
})
105-
.enumerate()
106-
.for_each(|(i, mut result)| {
107-
match &mut final_txs[i] {
108-
TypedTransaction::Legacy(tx) => {
109-
// We store the CREATE2 address, since it's hard to get it otherwise
110-
if let Some(NameOrAddress::Address(to)) = tx.to {
111-
if to == DEFAULT_CREATE2_DEPLOYER {
112-
create2_contracts.push(Address::from_slice(&result.returned));
113-
}
114-
}
115-
// We inflate the gas used by the transaction by x1.3 since the estimation
116-
// might be off
117-
tx.gas = Some(U256::from(result.gas * 13 / 10))
101+
TypedTransaction::Legacy(mut tx) => {
102+
let mut result = runner
103+
.simulate(
104+
tx.from.expect(
105+
"Transaction doesn't have a `from` address at execution time",
106+
),
107+
tx.to.clone(),
108+
tx.data.clone(),
109+
tx.value,
110+
)
111+
.expect("Internal EVM error");
112+
113+
// We inflate the gas used by the transaction by x1.3 since the estimation
114+
// might be off
115+
tx.gas = Some(U256::from(result.gas * 13 / 10));
116+
117+
sum_gas += result.gas;
118+
if !result.success {
119+
failed = true;
118120
}
119-
_ => unreachable!(),
120-
}
121-
122-
sum_gas += result.gas;
123-
if !result.success {
124-
failed = true;
125-
}
126121

127-
if script_config.evm_opts.verbosity > 3 {
128-
for (_kind, trace) in &mut result.traces {
129-
decoder.decode(trace);
130-
println!("{}", trace);
122+
if script_config.evm_opts.verbosity > 3 {
123+
for (_kind, trace) in &mut result.traces {
124+
decoder.decode(trace);
125+
println!("{}", trace);
126+
}
131127
}
128+
129+
TransactionWithMetadata::new(tx.into(), &result, &address_to_abi, decoder)
130+
.unwrap()
132131
}
133-
});
132+
_ => unreachable!(),
133+
})
134+
.collect();
134135

135136
if failed {
136137
Err(eyre::Report::msg("Simulated execution failed"))
137138
} else {
138-
Ok((final_txs, create2_contracts))
139+
Ok(final_txs)
139140
}
140141
}
141142

0 commit comments

Comments
 (0)