11use alloy:: { primitives:: B256 , providers:: Provider } ;
22use engine_core:: { chain:: Chain , error:: AlloyRpcErrorToEngineError } ;
33use serde:: { Deserialize , Serialize } ;
4+ use tracing:: Instrument ;
45
56use crate :: {
67 FlashblocksTransactionCount , TransactionCounts ,
@@ -108,8 +109,38 @@ impl<C: Chain> EoaExecutorWorker<C> {
108109 {
109110 tracing:: warn!(
110111 error = ?e,
111- "Failed to attempt gas bump for stalled nonce"
112+ "Failed to attempt gas bump for stalled nonce, trying fallback "
112113 ) ;
114+
115+ // Fallback: try to send a no-op transaction
116+ tracing:: info!(
117+ nonce = transaction_counts. preconfirmed,
118+ "Gas bump failed, attempting no-op transaction as fallback"
119+ ) ;
120+ if let Ok ( noop_tx) = self . send_noop_transaction ( transaction_counts. preconfirmed ) . await {
121+ if let Err ( e) = self . store . process_noop_transactions ( & [ noop_tx] ) . await {
122+ tracing:: error!(
123+ error = ?e,
124+ "Failed to process fallback no-op transaction for stalled nonce"
125+ ) ;
126+ }
127+ } else {
128+ tracing:: error!( "Failed to send fallback no-op transaction for stalled nonce" ) ;
129+
130+ // Ultimate fallback: check if we should trigger auto-reset
131+ let time_since_movement = now. saturating_sub ( current_health. last_nonce_movement_at ) ;
132+ if time_since_movement > 5 * 60 * 1000 && submitted_count > 0 { // 5 minutes
133+ tracing:: warn!(
134+ nonce = transaction_counts. preconfirmed,
135+ time_since_movement = time_since_movement,
136+ "EOA appears permanently stuck, scheduling auto-reset"
137+ ) ;
138+
139+ if let Err ( e) = self . store . schedule_manual_reset ( ) . await {
140+ tracing:: error!( error = ?e, "Failed to schedule auto-reset" ) ;
141+ }
142+ }
143+ }
113144 }
114145 }
115146 }
@@ -356,7 +387,7 @@ impl<C: Chain> EoaExecutorWorker<C> {
356387 error = ?e,
357388 "Failed to build typed transaction for gas bump"
358389 ) ;
359- return Ok ( false ) ;
390+ return Err ( e ) ;
360391 }
361392 } ;
362393 let bumped_typed_tx = self . apply_gas_bump_to_typed_transaction ( typed_tx, 120 ) ; // 20% increase
@@ -375,7 +406,7 @@ impl<C: Chain> EoaExecutorWorker<C> {
375406 error = ?e,
376407 "Failed to sign transaction for gas bump"
377408 ) ;
378- return Ok ( false ) ;
409+ return Err ( e ) ;
379410 }
380411 } ;
381412
@@ -393,9 +424,15 @@ impl<C: Chain> EoaExecutorWorker<C> {
393424 )
394425 . await ?;
395426
396- // Send the bumped transaction
397- let tx_envelope = bumped_tx. into ( ) ;
398- match self . chain . provider ( ) . send_tx_envelope ( tx_envelope) . await {
427+ // Send the bumped transaction with retry logic
428+ match self . send_tx_envelope_with_retry ( bumped_tx. into ( ) , crate :: eoa:: worker:: error:: SendContext :: InitialBroadcast )
429+ . instrument ( tracing:: info_span!(
430+ "send_tx_envelope_with_retry" ,
431+ transaction_id = %newest_transaction_data. transaction_id,
432+ nonce = expected_nonce,
433+ context = "gas_bump"
434+ ) )
435+ . await {
399436 Ok ( _) => {
400437 tracing:: info!(
401438 transaction_id = ?newest_transaction_data. transaction_id,
@@ -409,10 +446,13 @@ impl<C: Chain> EoaExecutorWorker<C> {
409446 transaction_id = ?newest_transaction_data. transaction_id,
410447 nonce = expected_nonce,
411448 error = ?e,
412- "Failed to send gas bumped transaction"
449+ "Failed to send gas bumped transaction after retries "
413450 ) ;
414- // Don't fail the worker, just log the error
415- Ok ( false )
451+ // Convert RPC error to worker error and bubble up
452+ Err ( EoaExecutorWorkerError :: TransactionSendError {
453+ message : format ! ( "Failed to send gas bumped transaction: {e:?}" ) ,
454+ inner_error : e. to_engine_error ( & self . chain ) ,
455+ } )
416456 }
417457 }
418458 } else {
0 commit comments