From 49edfb6fbd6e60032ba87120102a8e62e1459a1b Mon Sep 17 00:00:00 2001 From: File Large Date: Mon, 10 Nov 2025 21:18:56 +0100 Subject: [PATCH 1/2] feat: fail bundles with no exclusive profit after simulation --- crates/rbuilder/src/building/order_commit.rs | 2 ++ crates/rbuilder/src/building/sim.rs | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/crates/rbuilder/src/building/order_commit.rs b/crates/rbuilder/src/building/order_commit.rs index c45ab67a5..3d45b2d17 100644 --- a/crates/rbuilder/src/building/order_commit.rs +++ b/crates/rbuilder/src/building/order_commit.rs @@ -345,6 +345,8 @@ pub enum OrderErr { Transaction(#[from] TransactionErr), #[error("Bundle error: {0}")] Bundle(#[from] BundleErr), + #[error("No exclusive profit")] + NoExclusiveProfit, #[error("Negative profit: {0}")] NegativeProfit(U256), } diff --git a/crates/rbuilder/src/building/sim.rs b/crates/rbuilder/src/building/sim.rs index 2e70fd412..f03b6233a 100644 --- a/crates/rbuilder/src/building/sim.rs +++ b/crates/rbuilder/src/building/sim.rs @@ -466,6 +466,14 @@ pub fn simulate_order_using_fork( match result { Ok(res) => { let sim_value = create_sim_value(&order, &res, mempool_tx_detector); + if sim_value + .non_mempool_profit_info() + .coinbase_profit() + .is_zero() + { + return Ok(OrderSimResult::Failed(OrderErr::NoExclusiveProfit)); + } + let new_nonces = res.nonces_updated.into_iter().collect::>(); Ok(OrderSimResult::Success( Arc::new(SimulatedOrder { From 8d3643c5898d51528f9bcfb0b1e44d56a14bca87 Mon Sep 17 00:00:00 2001 From: Daniel Xifra Date: Tue, 11 Nov 2025 12:11:20 -0300 Subject: [PATCH 2/2] applied same logic then reusing txs in building --- .../src/building/builders/ordering_builder.rs | 25 +++++++++++-------- crates/rbuilder/src/building/order_commit.rs | 17 ++++++++++++- crates/rbuilder/src/building/sim.rs | 13 +++------- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/crates/rbuilder/src/building/builders/ordering_builder.rs b/crates/rbuilder/src/building/builders/ordering_builder.rs index da5d5bbd0..9661db65a 100644 --- a/crates/rbuilder/src/building/builders/ordering_builder.rs +++ b/crates/rbuilder/src/building/builders/ordering_builder.rs @@ -12,9 +12,9 @@ use crate::{ block_building_helper::BlockBuildingHelper, BuiltBlockId, LiveBuilderInput, OrderIntakeConsumer, }, - BlockBuildingContext, ExecutionError, NullPartialBlockExecutionTracer, OrderPriority, - PartialBlockExecutionTracer, PrioritizedOrderStore, SimulatedOrderSink, Sorting, - ThreadBlockBuildingContext, + order_is_worth_executing, BlockBuildingContext, ExecutionError, + NullPartialBlockExecutionTracer, OrderPriority, PartialBlockExecutionTracer, + PrioritizedOrderStore, SimulatedOrderSink, Sorting, ThreadBlockBuildingContext, }, live_builder::building::built_block_cache::BuiltBlockCache, provider::StateProviderFactory, @@ -423,14 +423,17 @@ impl OrderingBuilderContext { } Err(err) => { if let ExecutionError::LowerInsertedValue { inplace, .. } = &err { - // try to reinsert order into the map - let order_attempts = self.order_attempts.entry(sim_order.id()).or_insert(0); - if *order_attempts < self.config.failed_order_retries { - let mut new_order = (*sim_order).clone(); - new_order.sim_value = inplace.clone(); - block_orders.insert_order(Arc::new(new_order)); - *order_attempts += 1; - reinserted = true; + if order_is_worth_executing(inplace).is_ok() { + // try to reinsert order into the map + let order_attempts = + self.order_attempts.entry(sim_order.id()).or_insert(0); + if *order_attempts < self.config.failed_order_retries { + let mut new_order = (*sim_order).clone(); + new_order.sim_value = inplace.clone(); + block_orders.insert_order(Arc::new(new_order)); + *order_attempts += 1; + reinserted = true; + } } } if !reinserted { diff --git a/crates/rbuilder/src/building/order_commit.rs b/crates/rbuilder/src/building/order_commit.rs index 3d45b2d17..52fcd34bb 100644 --- a/crates/rbuilder/src/building/order_commit.rs +++ b/crates/rbuilder/src/building/order_commit.rs @@ -18,7 +18,7 @@ use itertools::Itertools; use rbuilder_primitives::{ evm_inspector::{RBuilderEVMInspector, UsedStateTrace}, BlockSpace, Bundle, Order, OrderId, RefundConfig, ShareBundle, ShareBundleBody, - ShareBundleInner, TransactionSignedEcRecoveredWithBlobs, + ShareBundleInner, SimValue, TransactionSignedEcRecoveredWithBlobs, }; use reth::{ consensus_common::validation::MAX_RLP_BLOCK_SIZE, revm::database::StateProviderDatabase, @@ -345,12 +345,27 @@ pub enum OrderErr { Transaction(#[from] TransactionErr), #[error("Bundle error: {0}")] Bundle(#[from] BundleErr), + /// This is not really an error from order execution. We should probably move it away from here. + /// It's used after simulation to reject orders with no exclusive profit (only profit from mempool txs) #[error("No exclusive profit")] NoExclusiveProfit, #[error("Negative profit: {0}")] NegativeProfit(U256), } +/// Sometimes we want to reject orders that pass simulation but we think are not going to be good for the block. +pub fn order_is_worth_executing(sim_value: &SimValue) -> Result<(), OrderErr> { + if sim_value + .non_mempool_profit_info() + .coinbase_profit() + .is_zero() + { + Err(OrderErr::NoExclusiveProfit) + } else { + Ok(()) + } +} + /// Tracer for PartialBlockFork execution. /// Passing the NullPartialBlockForkExecutionTracer should have 0 overhead (compiler should optimize it out). pub trait PartialBlockForkExecutionTracer { diff --git a/crates/rbuilder/src/building/sim.rs b/crates/rbuilder/src/building/sim.rs index f03b6233a..b0eccaf7f 100644 --- a/crates/rbuilder/src/building/sim.rs +++ b/crates/rbuilder/src/building/sim.rs @@ -5,8 +5,8 @@ use super::{ }; use crate::{ building::{ - BlockBuildingContext, BlockBuildingSpaceState, BlockState, CriticalCommitOrderError, - NullPartialBlockForkExecutionTracer, + order_is_worth_executing, BlockBuildingContext, BlockBuildingSpaceState, BlockState, + CriticalCommitOrderError, NullPartialBlockForkExecutionTracer, }, live_builder::order_input::mempool_txs_detector::MempoolTxsDetector, provider::StateProviderFactory, @@ -466,14 +466,9 @@ pub fn simulate_order_using_fork( match result { Ok(res) => { let sim_value = create_sim_value(&order, &res, mempool_tx_detector); - if sim_value - .non_mempool_profit_info() - .coinbase_profit() - .is_zero() - { - return Ok(OrderSimResult::Failed(OrderErr::NoExclusiveProfit)); + if let Err(err) = order_is_worth_executing(&sim_value) { + return Ok(OrderSimResult::Failed(err)); } - let new_nonces = res.nonces_updated.into_iter().collect::>(); Ok(OrderSimResult::Success( Arc::new(SimulatedOrder {