1
1
use crate :: {
2
- cmd:: { forge:: script:: receipts:: wait_for_receipts, ScriptSequence , VerifyBundle } ,
2
+ cmd:: {
3
+ forge:: script:: receipts:: wait_for_receipts, has_different_gas_calc, ScriptSequence ,
4
+ VerifyBundle ,
5
+ } ,
6
+ init_progress,
3
7
opts:: WalletType ,
8
+ update_progress,
4
9
utils:: get_http_provider,
5
10
} ;
6
11
use ethers:: {
7
- prelude:: { SignerMiddleware , TxHash } ,
12
+ prelude:: { Signer , SignerMiddleware , TxHash } ,
8
13
providers:: Middleware ,
9
- types:: { transaction:: eip2718:: TypedTransaction , Chain , TransactionReceipt } ,
14
+ types:: { transaction:: eip2718:: TypedTransaction , Chain } ,
10
15
} ;
11
- use std:: fmt;
16
+ use futures:: StreamExt ;
17
+ use indicatif:: { ProgressBar , ProgressStyle } ;
18
+ use std:: { cmp:: min, fmt} ;
12
19
13
20
use super :: * ;
14
21
15
22
impl ScriptArgs {
23
+ /// Sends the transactions which haven't been broadcasted yet.
16
24
pub async fn send_transactions (
17
25
& self ,
18
26
deployment_sequence : & mut ScriptSequence ,
19
27
fork_url : & str ,
20
28
) -> eyre:: Result < ( ) > {
21
- let provider = get_http_provider ( fork_url) ;
29
+ let provider = get_http_provider ( fork_url, true ) ;
30
+ let already_broadcasted = deployment_sequence. receipts . len ( ) ;
22
31
23
- if deployment_sequence . receipts . len ( ) < deployment_sequence. transactions . len ( ) {
32
+ if already_broadcasted < deployment_sequence. transactions . len ( ) {
24
33
let required_addresses = deployment_sequence
25
34
. transactions
26
35
. iter ( )
27
- . skip ( deployment_sequence . receipts . len ( ) )
36
+ . skip ( already_broadcasted )
28
37
. map ( |tx| * tx. from ( ) . expect ( "No sender for onchain transaction!" ) )
29
38
. collect ( ) ;
30
39
31
- let local_wallets = self . wallets . find_all ( & provider, required_addresses) . await ?;
40
+ let local_wallets = self . wallets . find_all ( provider. clone ( ) , required_addresses) . await ?;
32
41
if local_wallets. is_empty ( ) {
33
42
eyre:: bail!( "Error accessing local wallet when trying to send onchain transaction, did you set a private key, mnemonic or keystore?" )
34
43
}
@@ -38,31 +47,111 @@ impl ScriptArgs {
38
47
// otherwise.
39
48
let sequential_broadcast = local_wallets. len ( ) != 1 || self . slow ;
40
49
41
- let transactions = deployment_sequence. transactions . clone ( ) ;
50
+ // Make a one-time gas price estimation
51
+ let ( gas_price, eip1559_fees) = {
52
+ match deployment_sequence. transactions . front ( ) . unwrap ( ) {
53
+ TypedTransaction :: Legacy ( _) | TypedTransaction :: Eip2930 ( _) => {
54
+ ( provider. get_gas_price ( ) . await . ok ( ) , None )
55
+ }
56
+ TypedTransaction :: Eip1559 ( _) => {
57
+ ( None , provider. estimate_eip1559_fees ( None ) . await . ok ( ) )
58
+ }
59
+ }
60
+ } ;
42
61
43
62
// Iterate through transactions, matching the `from` field with the associated
44
63
// wallet. Then send the transaction. Panics if we find a unknown `from`
45
- let sequence = transactions. into_iter ( ) . map ( |tx| {
46
- let from = * tx. from ( ) . expect ( "No sender for onchain transaction!" ) ;
47
- let signer = local_wallets. get ( & from) . expect ( "`find_all` returned incomplete." ) ;
48
- ( tx, signer)
49
- } ) ;
64
+ let sequence = deployment_sequence
65
+ . transactions
66
+ . iter ( )
67
+ . skip ( already_broadcasted)
68
+ . map ( |tx| {
69
+ let from = * tx. from ( ) . expect ( "No sender for onchain transaction!" ) ;
70
+ let signer = local_wallets. get ( & from) . expect ( "`find_all` returned incomplete." ) ;
50
71
51
- let mut future_receipts = vec ! [ ] ;
72
+ let mut tx = tx . clone ( ) ;
52
73
53
- println ! ( "##\n Sending transactions." ) ;
54
- for ( tx, signer) in sequence {
55
- let receipt = self . send_transaction ( tx, signer, sequential_broadcast, fork_url) ;
74
+ tx. set_chain_id ( signer. chain_id ( ) ) ;
56
75
57
- if sequential_broadcast {
58
- wait_for_receipts ( vec ! [ receipt] , deployment_sequence) . await ?;
59
- } else {
60
- future_receipts. push ( receipt) ;
76
+ if let Some ( gas_price) = self . with_gas_price {
77
+ tx. set_gas_price ( gas_price) ;
78
+ } else {
79
+ // fill gas price
80
+ match tx {
81
+ TypedTransaction :: Eip2930 ( _) | TypedTransaction :: Legacy ( _) => {
82
+ tx. set_gas_price ( gas_price. expect ( "Could not get gas_price." ) ) ;
83
+ }
84
+ TypedTransaction :: Eip1559 ( ref mut inner) => {
85
+ let eip1559_fees =
86
+ eip1559_fees. expect ( "Could not get eip1559 fee estimation." ) ;
87
+ inner. max_fee_per_gas = Some ( eip1559_fees. 0 ) ;
88
+ inner. max_priority_fee_per_gas = Some ( eip1559_fees. 1 ) ;
89
+ }
90
+ }
91
+ }
92
+
93
+ ( tx, signer)
94
+ } )
95
+ . collect :: < Vec < _ > > ( ) ;
96
+
97
+ let pb = init_progress ! ( deployment_sequence. transactions, "txes" ) ;
98
+
99
+ // We send transactions and wait for receipts in batches of 100, since some networks
100
+ // cannot handle more than that.
101
+ let batch_size = 100 ;
102
+ let mut index = 0 ;
103
+
104
+ for ( batch_number, batch) in sequence. chunks ( batch_size) . map ( |f| f. to_vec ( ) ) . enumerate ( )
105
+ {
106
+ let mut pending_transactions = vec ! [ ] ;
107
+
108
+ println ! (
109
+ "##\n Sending transactions [{} - {}]." ,
110
+ batch_number * batch_size,
111
+ batch_number * batch_size + min( batch_size, batch. len( ) ) - 1
112
+ ) ;
113
+ for ( tx, signer) in batch. into_iter ( ) {
114
+ let tx_hash = self . send_transaction ( tx, signer, sequential_broadcast, fork_url) ;
115
+
116
+ if sequential_broadcast {
117
+ update_progress ! ( pb, ( index + already_broadcasted) ) ;
118
+ index += 1 ;
119
+
120
+ wait_for_receipts (
121
+ vec ! [ tx_hash. await ?] ,
122
+ deployment_sequence,
123
+ provider. clone ( ) ,
124
+ )
125
+ . await ?;
126
+ } else {
127
+ pending_transactions. push ( tx_hash) ;
128
+ }
61
129
}
62
- }
63
130
64
- if !sequential_broadcast {
65
- wait_for_receipts ( future_receipts, deployment_sequence) . await ?;
131
+ if !pending_transactions. is_empty ( ) {
132
+ let mut buffer = futures:: stream:: iter ( pending_transactions) . buffered ( 7 ) ;
133
+
134
+ let mut tx_hashes = vec ! [ ] ;
135
+
136
+ while let Some ( tx_hash) = buffer. next ( ) . await {
137
+ update_progress ! ( pb, ( index + already_broadcasted) ) ;
138
+ index += 1 ;
139
+ let tx_hash = tx_hash?;
140
+ deployment_sequence. add_pending ( tx_hash) ;
141
+ tx_hashes. push ( tx_hash) ;
142
+ }
143
+
144
+ // Checkpoint save
145
+ deployment_sequence. save ( ) ?;
146
+
147
+ if !sequential_broadcast {
148
+ println ! ( "##\n Waiting for receipts." ) ;
149
+ wait_for_receipts ( tx_hashes, deployment_sequence, provider. clone ( ) ) . await ?;
150
+ }
151
+ }
152
+
153
+ // Checkpoint save
154
+ deployment_sequence. save ( ) ?;
66
155
}
67
156
}
68
157
@@ -80,7 +169,7 @@ impl ScriptArgs {
80
169
signer : & WalletType ,
81
170
sequential_broadcast : bool ,
82
171
fork_url : & str ,
83
- ) -> Result < ( TransactionReceipt , U256 ) , BroadcastError > {
172
+ ) -> Result < TxHash , BroadcastError > {
84
173
let from = tx. from ( ) . expect ( "no sender" ) ;
85
174
86
175
if sequential_broadcast {
@@ -118,26 +207,27 @@ impl ScriptArgs {
118
207
if let Some ( txs) = transactions {
119
208
if script_config. evm_opts . fork_url . is_some ( ) {
120
209
let ( gas_filled_txs, create2_contracts) =
121
- self . execute_transactions ( txs, script_config, decoder)
122
- . await
123
- . map_err ( |_| eyre:: eyre!( "One or more transactions failed when simulating the on-chain version. Check the trace by re-running with `-vvv`" ) ) ?;
210
+ self . execute_transactions ( txs, script_config, decoder) . await . map_err ( |_| {
211
+ eyre:: eyre!(
212
+ "One or more transactions failed when simulating the
213
+ on-chain version. Check the trace by re-running with `-vvv`"
214
+ )
215
+ } ) ?;
216
+
124
217
let fork_url = self . evm_opts . fork_url . as_ref ( ) . unwrap ( ) . clone ( ) ;
125
218
126
- let provider = get_http_provider ( & fork_url) ;
127
- let chain = provider. get_chainid ( ) . await ?. as_u64 ( ) ;
219
+ let chain = get_http_provider ( & fork_url, false ) . get_chainid ( ) . await ?. as_u64 ( ) ;
128
220
let is_legacy = self . legacy ||
129
221
Chain :: try_from ( chain) . map ( |x| Chain :: is_legacy ( & x) ) . unwrap_or_default ( ) ;
130
222
131
223
let txes = gas_filled_txs
132
224
. into_iter ( )
133
225
. map ( |tx| {
134
- let mut tx = if is_legacy {
226
+ if is_legacy {
135
227
TypedTransaction :: Legacy ( tx. into ( ) )
136
228
} else {
137
229
TypedTransaction :: Eip1559 ( tx. into ( ) )
138
- } ;
139
- tx. set_chain_id ( chain) ;
140
- tx
230
+ }
141
231
} )
142
232
. collect ( ) ;
143
233
@@ -189,31 +279,44 @@ impl fmt::Display for BroadcastError {
189
279
/// transaction hash that can be used on a later run with `--resume`.
190
280
async fn broadcast < T , U > (
191
281
signer : & SignerMiddleware < T , U > ,
192
- legacy_or_1559 : TypedTransaction ,
193
- ) -> Result < ( TransactionReceipt , U256 ) , BroadcastError >
282
+ mut legacy_or_1559 : TypedTransaction ,
283
+ ) -> Result < TxHash , BroadcastError >
194
284
where
195
- SignerMiddleware < T , U > : Middleware ,
285
+ T : Middleware ,
286
+ U : Signer ,
196
287
{
197
288
tracing:: debug!( "sending transaction: {:?}" , legacy_or_1559) ;
198
- let nonce = * legacy_or_1559. nonce ( ) . unwrap ( ) ;
199
- let pending = signer
200
- . send_transaction ( legacy_or_1559, None )
289
+
290
+ if has_different_gas_calc ( signer. signer ( ) . chain_id ( ) ) {
291
+ match legacy_or_1559 {
292
+ TypedTransaction :: Legacy ( ref mut inner) => inner. gas = None ,
293
+ TypedTransaction :: Eip1559 ( ref mut inner) => inner. gas = None ,
294
+ TypedTransaction :: Eip2930 ( ref mut inner) => inner. tx . gas = None ,
295
+ } ;
296
+
297
+ legacy_or_1559. set_gas (
298
+ signer
299
+ . estimate_gas ( & legacy_or_1559)
300
+ . await
301
+ . map_err ( |err| BroadcastError :: Simple ( err. to_string ( ) ) ) ?,
302
+ ) ;
303
+ }
304
+
305
+ // Signing manually so we skip `fill_transaction` and its `eth_createAccessList` request.
306
+ let signature = signer
307
+ . sign_transaction (
308
+ & legacy_or_1559,
309
+ * legacy_or_1559. from ( ) . expect ( "Tx should have a `from`." ) ,
310
+ )
201
311
. await
202
312
. map_err ( |err| BroadcastError :: Simple ( err. to_string ( ) ) ) ?;
203
313
204
- let tx_hash = pending. tx_hash ( ) ;
314
+ // Submit the raw transaction
315
+ let pending = signer
316
+ . provider ( )
317
+ . send_raw_transaction ( legacy_or_1559. rlp_signed ( & signature) )
318
+ . await
319
+ . map_err ( |err| BroadcastError :: Simple ( err. to_string ( ) ) ) ?;
205
320
206
- let receipt = match pending. await {
207
- Ok ( receipt) => match receipt {
208
- Some ( receipt) => receipt,
209
- None => {
210
- return Err ( BroadcastError :: ErrorWithTxHash (
211
- format ! ( "Didn't receive a receipt for {}" , tx_hash) ,
212
- tx_hash,
213
- ) )
214
- }
215
- } ,
216
- Err ( err) => return Err ( BroadcastError :: ErrorWithTxHash ( err. to_string ( ) , tx_hash) ) ,
217
- } ;
218
- Ok ( ( receipt, nonce) )
321
+ Ok ( pending. tx_hash ( ) )
219
322
}
0 commit comments