@@ -4,7 +4,7 @@ use crate::{
44 utils:: extract_signature_components,
55} ;
66use alloy:: {
7- consensus:: { SimpleCoder , Transaction , constants :: GWEI_TO_WEI } ,
7+ consensus:: SimpleCoder ,
88 eips:: BlockNumberOrTag ,
99 network:: { TransactionBuilder , TransactionBuilder4844 } ,
1010 primitives:: { FixedBytes , TxHash , U256 } ,
@@ -13,7 +13,7 @@ use alloy::{
1313 sol_types:: { SolCall , SolError } ,
1414 transports:: TransportError ,
1515} ;
16- use eyre:: { bail, eyre } ;
16+ use eyre:: bail;
1717use init4_bin_base:: deps:: {
1818 metrics:: { counter, histogram} ,
1919 tracing:: { self , Instrument , debug, debug_span, error, info, instrument, warn} ,
@@ -101,21 +101,11 @@ impl SubmitTask {
101101 let data = submitCall { fills, header, v, r, s } . abi_encode ( ) ;
102102
103103 let sidecar = block. encode_blob :: < SimpleCoder > ( ) . build ( ) ?;
104- Ok ( TransactionRequest :: default ( )
105- . with_blob_sidecar ( sidecar)
106- . with_input ( data)
107- . with_max_priority_fee_per_gas ( ( GWEI_TO_WEI * 16 ) as u128 ) )
108- }
109104
110- /// Returns the next host block height.
111- async fn next_host_block_height ( & self ) -> eyre:: Result < u64 > {
112- let result = self . provider ( ) . get_block_number ( ) . await ?;
113- let next = result. checked_add ( 1 ) . ok_or_else ( || eyre ! ( "next host block height overflow" ) ) ?;
114- debug ! ( next, "next host block height" ) ;
115- Ok ( next)
105+ Ok ( TransactionRequest :: default ( ) . with_blob_sidecar ( sidecar) . with_input ( data) )
116106 }
117107
118- /// Prepares and then sends the EIP-4844 transaction with a sidecar encoded with a rollup block to the network.
108+ /// Prepares and sends the EIP-4844 transaction with a sidecar encoded with a rollup block to the network.
119109 async fn submit_transaction (
120110 & self ,
121111 retry_count : usize ,
@@ -140,10 +130,7 @@ impl SubmitTask {
140130
141131 // Simulate the transaction with a call to the host provider
142132 if let Some ( maybe_error) = self . sim_with_call ( & tx) . await {
143- warn ! (
144- error = ?maybe_error,
145- "error in transaction simulation"
146- ) ;
133+ warn ! ( error = ?maybe_error, "error in transaction simulation" ) ;
147134 if let Err ( e) = maybe_error {
148135 return Err ( e) ;
149136 }
@@ -160,19 +147,13 @@ impl SubmitTask {
160147 if let Err ( TransportError :: ErrorResp ( e) ) =
161148 self . provider ( ) . call ( tx. clone ( ) ) . block ( BlockNumberOrTag :: Pending . into ( ) ) . await
162149 {
163- error ! (
164- code = e. code,
165- message = %e. message,
166- data = ?e. data,
167- "error in transaction submission"
168- ) ;
169-
150+ // NB: These errors are all handled the same way but are logged for debugging purposes
170151 if e. as_revert_data ( )
171152 . map ( |data| data. starts_with ( & IncorrectHostBlock :: SELECTOR ) )
172153 . unwrap_or_default ( )
173154 {
174155 debug ! ( %e, "incorrect host block" ) ;
175- return Some ( Ok ( ControlFlow :: Retry ) ) ;
156+ return Some ( Ok ( ControlFlow :: Skip ) ) ;
176157 }
177158
178159 if e. as_revert_data ( )
@@ -191,6 +172,12 @@ impl SubmitTask {
191172 return Some ( Ok ( ControlFlow :: Skip ) ) ;
192173 }
193174
175+ error ! (
176+ code = e. code,
177+ message = %e. message,
178+ data = ?e. data,
179+ "unknown error in host transaction simulation call"
180+ ) ;
194181 return Some ( Ok ( ControlFlow :: Skip ) ) ;
195182 }
196183
@@ -207,22 +194,14 @@ impl SubmitTask {
207194 ) -> Result < TransactionRequest , eyre:: Error > {
208195 // TODO: ENG-1082 Implement fills
209196 let fills = vec ! [ ] ;
210-
211197 // Extract the signature components from the response
212198 let ( v, r, s) = extract_signature_components ( & resp. sig ) ;
213199
214- // Bump gas with each retry to replace the previous
215- // transaction while maintaining the same nonce
216- // TODO: Clean this up if this works
217- let gas_coefficient: u64 = ( 15 * ( retry_count + 1 ) ) . try_into ( ) . unwrap ( ) ;
218- let gas_limit: u64 = 1_500_000 + ( gas_coefficient * 1_000_000 ) ;
219- let max_priority_fee_per_gas: u128 = ( retry_count as u128 ) ;
220- debug ! (
221- retry_count,
222- gas_coefficient, gas_limit, max_priority_fee_per_gas, "calculated gas limit"
223- ) ;
200+ // Calculate gas limits based on retry attempts
201+ let ( max_fee_per_gas, max_priority_fee_per_gas, max_fee_per_blob_gas) =
202+ calculate_gas_limits ( retry_count) ;
224203
225- // manually retrieve nonce
204+ // manually retrieve nonce // TODO: Maybe this should be done in Env task and passed through elsewhere
226205 let nonce =
227206 self . provider ( ) . get_transaction_count ( self . provider ( ) . default_signer_address ( ) ) . await ?;
228207 debug ! ( nonce, "assigned nonce" ) ;
@@ -240,13 +219,12 @@ impl SubmitTask {
240219 // Create a blob transaction with the blob header and signature values and return it
241220 let tx = self
242221 . build_blob_tx ( fills, header, v, r, s, block) ?
243- . with_from ( self . provider ( ) . default_signer_address ( ) )
244222 . with_to ( self . config . builder_helper_address )
245- . with_gas_limit ( gas_limit )
223+ . with_max_fee_per_gas ( max_fee_per_gas )
246224 . with_max_priority_fee_per_gas ( max_priority_fee_per_gas)
225+ . with_max_fee_per_blob_gas ( max_fee_per_blob_gas)
247226 . with_nonce ( nonce) ;
248227
249- debug ! ( ?tx, "prepared transaction request" ) ;
250228 Ok ( tx)
251229 }
252230
@@ -257,23 +235,16 @@ impl SubmitTask {
257235 resp : & SignResponse ,
258236 tx : TransactionRequest ,
259237 ) -> Result < ControlFlow , eyre:: Error > {
260- debug ! (
261- host_block_number = %resp. req. host_block_number,
262- gas_limit = %resp. req. gas_limit,
263- nonce = ?tx. nonce,
264- "sending transaction to network"
265- ) ;
266-
267238 // assign the nonce and fill the rest of the values
268239 let SendableTx :: Envelope ( tx) = self . provider ( ) . fill ( tx) . await ? else {
269240 bail ! ( "failed to fill transaction" )
270241 } ;
271- debug ! ( tx_hash = %tx . tx_hash ( ) , nonce = ?tx. nonce ( ) , gas_limit = ?tx . gas_limit ( ) , blob_gas_used = ?tx . blob_gas_used ( ) , "filled blob transaction" ) ;
242+ debug ! ( tx_hash = ?tx. hash ( ) , host_block_number = %resp . req . host_block_number , "sending transaction to network " ) ;
272243
273244 // send the tx via the primary host_provider
274245 let fut = spawn_provider_send ! ( self . provider( ) , & tx) ;
275246
276- // spawn send_tx futures for all additional broadcast host_providers
247+ // spawn send_tx futures on retry attempts for all additional broadcast host_providers
277248 for host_provider in self . config . connect_additional_broadcast ( ) {
278249 spawn_provider_send ! ( & host_provider, & tx) ;
279250 }
@@ -283,16 +254,19 @@ impl SubmitTask {
283254 error ! ( "receipts task gone" ) ;
284255 }
285256
286- // question mark unwraps join error, which would be an internal panic
287- // then if let checks for rpc error
288257 if let Err ( e) = fut. await ? {
289- error ! ( error = %e, "Primary tx broadcast failed. Skipping transaction." ) ;
258+ // Detect and handle transaction underprice errors
259+ if matches ! ( e, TransportError :: ErrorResp ( ref err) if err. code == -32000 && err. message. contains( "replacement transaction underpriced" ) )
260+ {
261+ debug ! ( ?tx, "underpriced transaction error - retrying tx with gas bump" ) ;
262+ return Ok ( ControlFlow :: Retry ) ;
263+ }
264+
265+ // Unknown error, log and skip
266+ error ! ( error = %e, "Primary tx broadcast failed" ) ;
290267 return Ok ( ControlFlow :: Skip ) ;
291268 }
292269
293- // Okay so the code gets all the way to this log
294- // but we don't see the tx hash in the logs or in the explorer,
295- // not even as a failed TX, just not at all.
296270 info ! (
297271 tx_hash = %tx. tx_hash( ) ,
298272 ru_chain_id = %resp. req. ru_chain_id,
@@ -341,54 +315,38 @@ impl SubmitTask {
341315
342316 // Retry loop
343317 let result = loop {
318+ // Log the retry attempt
344319 let span = debug_span ! ( "SubmitTask::retrying_handle_inbound" , retries) ;
345320
346- let inbound_result = match self
347- . handle_inbound ( retries, block)
348- . instrument ( span. clone ( ) )
349- . await
350- {
351- Ok ( control_flow) => {
352- debug ! ( ?control_flow, retries, "successfully handled inbound block" ) ;
353- control_flow
354- }
355- Err ( err) => {
356- // Log the retry attempt
357- retries += 1 ;
358-
359- // Delay until next slot if we get a 403 error
360- if err. to_string ( ) . contains ( "403 Forbidden" ) {
361- let ( slot_number, _, _) = self . calculate_slot_window ( ) ?;
362- debug ! ( slot_number, ?block, "403 detected - not assigned to slot" ) ;
363- return Ok ( ControlFlow :: Skip ) ;
364- } else {
365- error ! ( error = %err, "error handling inbound block" ) ;
321+ let inbound_result =
322+ match self . handle_inbound ( retries, block) . instrument ( span. clone ( ) ) . await {
323+ Ok ( control_flow) => control_flow,
324+ Err ( err) => {
325+ // Delay until next slot if we get a 403 error
326+ if err. to_string ( ) . contains ( "403 Forbidden" ) {
327+ let ( slot_number, _, _) = self . calculate_slot_window ( ) ?;
328+ debug ! ( slot_number, "403 detected - skipping slot" ) ;
329+ return Ok ( ControlFlow :: Skip ) ;
330+ } else {
331+ error ! ( error = %err, "error handling inbound block" ) ;
332+ }
333+
334+ ControlFlow :: Retry
366335 }
367-
368- ControlFlow :: Retry
369- }
370- } ;
336+ } ;
371337
372338 let guard = span. entered ( ) ;
373339
374340 match inbound_result {
375341 ControlFlow :: Retry => {
342+ retries += 1 ;
376343 if retries > retry_limit {
377344 counter ! ( "builder.building_too_many_retries" ) . increment ( 1 ) ;
378345 debug ! ( "retries exceeded - skipping block" ) ;
379346 return Ok ( ControlFlow :: Skip ) ;
380347 }
381348 drop ( guard) ;
382-
383- // Detect a slot change and break out of the loop in that case too
384- let ( this_slot, start, end) = self . calculate_slot_window ( ) ?;
385- if this_slot != current_slot {
386- debug ! ( "slot changed - skipping block" ) ;
387- break inbound_result;
388- }
389-
390- // Otherwise retry the block
391- debug ! ( retries, this_slot, start, end, "retrying block" ) ;
349+ debug ! ( retries, start, end, "retrying block" ) ;
392350 continue ;
393351 }
394352 ControlFlow :: Skip => {
@@ -424,6 +382,12 @@ impl SubmitTask {
424382 let now = std:: time:: SystemTime :: now ( ) ;
425383 now. duration_since ( UNIX_EPOCH ) . unwrap ( ) . as_secs ( )
426384 }
385+
386+ /// Returns the next host block height.
387+ async fn next_host_block_height ( & self ) -> eyre:: Result < u64 > {
388+ let block_num = self . provider ( ) . get_block_number ( ) . await ?;
389+ Ok ( block_num + 1 )
390+ }
427391
428392 /// Task future for the submit task
429393 /// NB: This task assumes that the simulator will only send it blocks for
@@ -471,3 +435,27 @@ impl SubmitTask {
471435 ( sender, handle)
472436 }
473437}
438+
439+ fn calculate_gas_limits ( retry_count : usize ) -> ( u128 , u128 , u128 ) {
440+ let base_fee_per_gas: u128 = 100_000_000_000 ;
441+ let base_priority_fee_per_gas: u128 = 2_000_000_000 ;
442+ let base_fee_per_blob_gas: u128 = 1_000_000_000 ;
443+
444+ let bump_multiplier = 1150u128 . pow ( retry_count as u32 ) ; // 15% bump
445+ let blob_bump_multiplier = 2000u128 . pow ( retry_count as u32 ) ; // 100% bump (double each time) for blob gas
446+ let bump_divisor = 1000u128 . pow ( retry_count as u32 ) ;
447+
448+ let max_fee_per_gas = base_fee_per_gas * bump_multiplier / bump_divisor;
449+ let max_priority_fee_per_gas = base_priority_fee_per_gas * bump_multiplier / bump_divisor;
450+ let max_fee_per_blob_gas = base_fee_per_blob_gas * blob_bump_multiplier / bump_divisor;
451+
452+ debug ! (
453+ retry_count,
454+ max_fee_per_gas,
455+ max_priority_fee_per_gas,
456+ max_fee_per_blob_gas,
457+ "calculated bumped gas parameters"
458+ ) ;
459+
460+ ( max_fee_per_gas, max_priority_fee_per_gas, max_fee_per_blob_gas)
461+ }
0 commit comments