From fbac75dc759d56dab96ea16d720031957f45fb16 Mon Sep 17 00:00:00 2001 From: init4samwise Date: Thu, 12 Feb 2026 07:16:05 +0000 Subject: [PATCH 1/3] feat: add deadline tracking to block submission Adds a configurable deadline for bundle submission to Flashbots. Submissions that complete before the deadline log success at info level. Submissions that complete after the deadline log warnings to alert that the submission may be too late. New configuration: - SUBMIT_DEADLINE_BUFFER: ms before slot end by which submission must complete (default: 500ms) New metrics: - signet.builder.flashbots.deadline_met: successful submissions within deadline - signet.builder.flashbots.deadline_missed: submissions that missed the deadline Closes ENG-1640 --- src/config.rs | 9 ++++++ src/tasks/submit/flashbots.rs | 61 +++++++++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/src/config.rs b/src/config.rs index 1296ccc..1f93de4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -163,6 +163,15 @@ pub struct BuilderConfig { )] pub block_query_cutoff_buffer: u64, + /// Number of milliseconds before the end of the slot by which bundle submission to Flashbots must complete. + /// If submission completes after this deadline, a warning is logged. + #[from_env( + var = "SUBMIT_DEADLINE_BUFFER", + desc = "Number of milliseconds before the end of the slot by which bundle submission must complete. Submissions that miss this deadline will be logged as warnings.", + default = 500 + )] + pub submit_deadline_buffer: u64, + /// The slot calculator for the builder. pub slot_calculator: SlotCalculator, diff --git a/src/tasks/submit/flashbots.rs b/src/tasks/submit/flashbots.rs index 6b3d998..a72442a 100644 --- a/src/tasks/submit/flashbots.rs +++ b/src/tasks/submit/flashbots.rs @@ -13,8 +13,9 @@ use alloy::{ rpc::types::mev::EthSendBundle, }; use init4_bin_base::{deps::metrics::counter, utils::signer::LocalOrAws}; +use std::time::{Duration, Instant}; use tokio::{sync::mpsc, task::JoinHandle}; -use tracing::{Instrument, debug, debug_span, error, instrument}; +use tracing::{Instrument, debug, debug_span, error, info, instrument, warn}; /// Handles preparation and submission of simulated rollup blocks to the /// Flashbots relay as MEV bundles. @@ -166,6 +167,9 @@ impl FlashbotsTask { let span = sim_result.clone_span(); + // Calculate the submission deadline for this block + let deadline = self.calculate_submit_deadline(); + // Don't submit empty blocks if sim_result.block.is_empty() { counter!("signet.builder.flashbots.empty_block").increment(1); @@ -202,17 +206,34 @@ impl FlashbotsTask { let response = flashbots.send_bundle(bundle).with_auth(signer.clone()).into_future().await; + // Check if we met the submission deadline + let met_deadline = Instant::now() <= deadline; + match response { Ok(resp) => { counter!("signet.builder.flashbots.bundles_submitted").increment(1); - debug!( - hash = resp.map(|r| r.bundle_hash.to_string()), - "Submitted MEV bundle to Flashbots, received OK response" - ); + if met_deadline { + info!( + hash = resp.as_ref().map(|r| r.bundle_hash.to_string()), + "Submitted MEV bundle to Flashbots within deadline" + ); + counter!("signet.builder.flashbots.deadline_met").increment(1); + } else { + warn!( + hash = resp.as_ref().map(|r| r.bundle_hash.to_string()), + "Submitted MEV bundle to Flashbots AFTER deadline - submission may be too late" + ); + counter!("signet.builder.flashbots.deadline_missed").increment(1); + } } Err(err) => { counter!("signet.builder.flashbots.submission_failures").increment(1); - error!(%err, "MEV bundle submission failed - error returned"); + if met_deadline { + error!(%err, "MEV bundle submission failed - error returned"); + } else { + error!(%err, "MEV bundle submission failed AFTER deadline - error returned"); + counter!("signet.builder.flashbots.deadline_missed").increment(1); + } } } } @@ -221,6 +242,34 @@ impl FlashbotsTask { } } + /// Calculates the deadline for bundle submission. + /// + /// The deadline is calculated as the time remaining in the current slot, + /// minus the configured submit deadline buffer. Submissions completing + /// after this deadline will be logged as warnings. + /// + /// # Returns + /// + /// An `Instant` representing the submission deadline. + fn calculate_submit_deadline(&self) -> Instant { + let slot_calculator = &self.config.slot_calculator; + + // Get the current number of milliseconds into the slot. + let timepoint_ms = + slot_calculator.current_point_within_slot_ms().expect("host chain has started"); + + let slot_duration = slot_calculator.slot_duration() * 1000; // convert to milliseconds + let submit_buffer = self.config.submit_deadline_buffer; + + // To find the remaining slot time, subtract the timepoint from the slot duration. + // Then subtract the submit deadline buffer to give us margin before slot ends. + let remaining = slot_duration.saturating_sub(timepoint_ms).saturating_sub(submit_buffer); + + // The deadline is calculated by adding the remaining time to the current instant. + let deadline = Instant::now() + Duration::from_millis(remaining); + deadline.max(Instant::now()) + } + /// Returns a clone of the host provider for transaction operations. fn host_provider(&self) -> HostProvider { self.zenith.provider().clone() From 14a63ac50c95923ce04ba6e481c3e98fdd8d16be Mon Sep 17 00:00:00 2001 From: init4samwise Date: Thu, 12 Feb 2026 17:26:18 +0000 Subject: [PATCH 2/3] refactor: unnest match arms in flashbots submission Address PR review feedback: use tuple matching on (response, met_deadline) instead of nested if/else blocks inside match arms. This flattens the code structure while preserving all functionality. --- src/tasks/submit/flashbots.rs | 44 +++++++++++++++++------------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/tasks/submit/flashbots.rs b/src/tasks/submit/flashbots.rs index a72442a..ec04dca 100644 --- a/src/tasks/submit/flashbots.rs +++ b/src/tasks/submit/flashbots.rs @@ -209,31 +209,31 @@ impl FlashbotsTask { // Check if we met the submission deadline let met_deadline = Instant::now() <= deadline; - match response { - Ok(resp) => { + match (response, met_deadline) { + (Ok(resp), true) => { counter!("signet.builder.flashbots.bundles_submitted").increment(1); - if met_deadline { - info!( - hash = resp.as_ref().map(|r| r.bundle_hash.to_string()), - "Submitted MEV bundle to Flashbots within deadline" - ); - counter!("signet.builder.flashbots.deadline_met").increment(1); - } else { - warn!( - hash = resp.as_ref().map(|r| r.bundle_hash.to_string()), - "Submitted MEV bundle to Flashbots AFTER deadline - submission may be too late" - ); - counter!("signet.builder.flashbots.deadline_missed").increment(1); - } + counter!("signet.builder.flashbots.deadline_met").increment(1); + info!( + hash = resp.as_ref().map(|r| r.bundle_hash.to_string()), + "Submitted MEV bundle to Flashbots within deadline" + ); } - Err(err) => { + (Ok(resp), false) => { + counter!("signet.builder.flashbots.bundles_submitted").increment(1); + counter!("signet.builder.flashbots.deadline_missed").increment(1); + warn!( + hash = resp.as_ref().map(|r| r.bundle_hash.to_string()), + "Submitted MEV bundle to Flashbots AFTER deadline - submission may be too late" + ); + } + (Err(err), true) => { + counter!("signet.builder.flashbots.submission_failures").increment(1); + error!(%err, "MEV bundle submission failed - error returned"); + } + (Err(err), false) => { counter!("signet.builder.flashbots.submission_failures").increment(1); - if met_deadline { - error!(%err, "MEV bundle submission failed - error returned"); - } else { - error!(%err, "MEV bundle submission failed AFTER deadline - error returned"); - counter!("signet.builder.flashbots.deadline_missed").increment(1); - } + counter!("signet.builder.flashbots.deadline_missed").increment(1); + error!(%err, "MEV bundle submission failed AFTER deadline - error returned"); } } } From 6b6a1db20bad9cff06d0e7d60c4e076dba585647 Mon Sep 17 00:00:00 2001 From: init4samwise Date: Fri, 13 Feb 2026 10:27:32 +0000 Subject: [PATCH 3/3] fix: add missing submit_deadline_buffer to test config Adds the missing submit_deadline_buffer field to the test BuilderConfig in test_utils.rs, using the same default value (500ms) as defined in the main config. --- src/test_utils.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test_utils.rs b/src/test_utils.rs index cb9d3e7..51ce082 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -54,6 +54,7 @@ pub fn setup_test_config() -> &'static BuilderConfig { 0, 1, ), block_query_cutoff_buffer: 3000, + submit_deadline_buffer: 500, max_host_gas_coefficient: Some(80), constants: SignetSystemConstants::parmigiana(), }