Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down
63 changes: 56 additions & 7 deletions src/tasks/submit/flashbots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -202,25 +206,70 @@ impl FlashbotsTask {
let response =
flashbots.send_bundle(bundle).with_auth(signer.clone()).into_future().await;

match response {
Ok(resp) => {
// Check if we met the submission deadline
let met_deadline = Instant::now() <= deadline;

match (response, met_deadline) {
(Ok(resp), true) => {
counter!("signet.builder.flashbots.bundles_submitted").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"
);
}
(Ok(resp), false) => {
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"
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) => {
(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);
counter!("signet.builder.flashbots.deadline_missed").increment(1);
error!(%err, "MEV bundle submission failed AFTER deadline - error returned");
}
}
}
.instrument(submit_span.clone()),
);
}
}

/// 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()
Expand Down
1 change: 1 addition & 0 deletions src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
}
Expand Down