From b80db5562e42ef902154d6df1fcb43ca90fff295 Mon Sep 17 00:00:00 2001 From: dylan Date: Mon, 10 Nov 2025 18:02:39 -0700 Subject: [PATCH 1/5] feat: updates the bundle prep to track the rollup tx hash --- src/tasks/submit/flashbots.rs | 128 ++++++++++++++++++++++++---------- 1 file changed, 92 insertions(+), 36 deletions(-) diff --git a/src/tasks/submit/flashbots.rs b/src/tasks/submit/flashbots.rs index 83edd792..2a4c52bb 100644 --- a/src/tasks/submit/flashbots.rs +++ b/src/tasks/submit/flashbots.rs @@ -16,8 +16,8 @@ use init4_bin_base::{deps::metrics::counter, utils::signer::LocalOrAws}; use tokio::{sync::mpsc, task::JoinHandle}; use tracing::{Instrument, debug, debug_span}; -/// Handles construction, simulation, and submission of rollup blocks to the -/// Flashbots network. +/// Handles preparation and submission of simulated rollup blocks to the +/// Flashbots relay as MEV bundles. #[derive(Debug)] pub struct FlashbotsTask { /// Builder configuration for the task. @@ -31,12 +31,14 @@ pub struct FlashbotsTask { /// The key used to sign requests to the Flashbots relay. signer: LocalOrAws, /// Channel for sending hashes of outbound transactions. - _outbound: mpsc::UnboundedSender, + outbound: mpsc::UnboundedSender, } impl FlashbotsTask { - /// Returns a new `FlashbotsTask` instance that receives `SimResult` types from the given - /// channel and handles their preparation, submission to the Flashbots network. + /// Creates a new `FlashbotsTask` instance with initialized providers and connections. + /// + /// Sets up Quincey for block signing, host provider for transaction submission, + /// Flashbots provider for bundle submission, and Zenith instance for contract interactions. pub async fn new( config: BuilderConfig, outbound: mpsc::UnboundedSender, @@ -50,28 +52,56 @@ impl FlashbotsTask { let zenith = config.connect_zenith(host_provider); - Ok(Self { config, quincey, zenith, flashbots, signer: builder_key, _outbound: outbound }) + Ok(Self { config, quincey, zenith, flashbots, signer: builder_key, outbound }) } - /// Returns a reference to the inner `HostProvider` - pub fn host_provider(&self) -> HostProvider { - self.zenith.provider().clone() - } - - /// Returns a reference to the inner `FlashbotsProvider` - pub const fn flashbots(&self) -> &FlashbotsProvider { - &self.flashbots - } - - /// Prepares a MEV bundle with the configured submit call + /// Prepares a MEV bundle from a simulation result. + /// + /// This function serves as an entry point for bundle preparation and is left + /// for forward compatibility when adding different bundle preparation methods. pub async fn prepare(&self, sim_result: &SimResult) -> eyre::Result { // This function is left for forwards compatibility when we want to add // different bundle preparation methods in the future. self.prepare_bundle_helper(sim_result).await } - /// Prepares a BundleHelper call containing the rollup block and corresponding fills into a MEV bundle. + /// Prepares a MEV bundle containing the host transactions and the rollup block. + /// + /// This method orchestrates the bundle preparation by: + /// 1. Preparing and signing the submission transaction + /// 2. Tracking the transaction hash for monitoring + /// 3. Encoding the transaction for bundle inclusion + /// 4. Constructing the complete bundle body async fn prepare_bundle_helper(&self, sim_result: &SimResult) -> eyre::Result { + // Prepare and sign the transaction + let block_tx = self.prepare_signed_transaction(sim_result).await?; + + // Track the outbound transaction + self.track_outbound_tx(&block_tx); + + // Encode the transaction + let tx_bytes = block_tx.encoded_2718().into(); + + // Build the bundle body with the block_tx bytes as the last transaction in the bundle. + let bundle_body = self.build_bundle_body(sim_result, tx_bytes); + + // Create the MEV bundle (valid only in the specific host block) + Ok(MevSendBundle::new( + sim_result.host_block_number(), + Some(sim_result.host_block_number()), + ProtocolVersion::V0_1, + bundle_body, + )) + } + + /// Prepares and signs the submission transaction for the rollup block. + /// + /// Creates a `SubmitPrep` instance to build the transaction, then fills + /// and signs it using the host provider. + async fn prepare_signed_transaction( + &self, + sim_result: &SimResult, + ) -> eyre::Result { let prep = SubmitPrep::new( &sim_result.block, self.host_provider(), @@ -80,34 +110,48 @@ impl FlashbotsTask { ); let tx = prep.prep_transaction(sim_result.prev_host()).await?; - let sendable = self.host_provider().fill(tx.into_request()).await?; - let tx_bytes = sendable - .as_envelope() - .ok_or_eyre("failed to get envelope from filled tx")? - .encoded_2718() - .into(); + sendable.as_envelope().ok_or_eyre("failed to get envelope from filled tx").cloned() + } + + /// Tracks the outbound transaction hash and increments submission metrics. + /// + /// Sends the transaction hash to the outbound channel for monitoring. + /// Logs a debug message if the channel is closed. + fn track_outbound_tx(&self, envelope: &alloy::consensus::TxEnvelope) { + counter!("signet.builder.flashbots.").increment(1); + let hash = *envelope.tx_hash(); + if self.outbound.send(hash).is_err() { + debug!("outbound channel closed, could not track tx hash"); + } + } - let bundle_body = sim_result + /// Constructs the MEV bundle body from host transactions and the submission transaction. + /// + /// Combines all host transactions from the rollup block with the prepared rollup block + /// submission transaction, wrapping each as a non-revertible bundle item. + /// + /// The rollup block transaction is placed last in the bundle. + fn build_bundle_body( + &self, + sim_result: &SimResult, + tx_bytes: alloy::primitives::Bytes, + ) -> Vec { + sim_result .block .host_transactions() .iter() .cloned() .chain(std::iter::once(tx_bytes)) .map(|tx| BundleItem::Tx { tx, can_revert: false }) - .collect::>(); - - // Only valid in the specific host block - Ok(MevSendBundle::new( - sim_result.host_block_number(), - Some(sim_result.host_block_number()), - ProtocolVersion::V0_1, - bundle_body, - )) + .collect() } - /// Task future that runs the Flashbots submission loop. + /// Main task loop that processes simulation results and submits bundles to Flashbots. + /// + /// Receives `SimResult`s from the inbound channel, prepares MEV bundles, and submits + /// them to the Flashbots relay. Skips empty blocks and continues processing on errors. async fn task_future(self, mut inbound: mpsc::UnboundedReceiver) { debug!("starting flashbots task"); @@ -172,7 +216,19 @@ impl FlashbotsTask { } } - /// Spawns the Flashbots task that handles incoming `SimResult`s. + /// Returns a clone of the host provider for transaction operations. + fn host_provider(&self) -> HostProvider { + self.zenith.provider().clone() + } + + /// Returns a reference to the Flashbots provider. + const fn flashbots(&self) -> &FlashbotsProvider { + &self.flashbots + } + + /// Spawns the Flashbots task in a new Tokio task. + /// + /// Returns a channel sender for submitting `SimResult`s and a join handle for the task. pub fn spawn(self) -> (mpsc::UnboundedSender, JoinHandle<()>) { let (sender, inbound) = mpsc::unbounded_channel::(); let handle = tokio::spawn(self.task_future(inbound)); From 9e7fe9b3ef383a90a785182c38161d8d9b59f69c Mon Sep 17 00:00:00 2001 From: dylan Date: Wed, 12 Nov 2025 20:33:50 -0700 Subject: [PATCH 2/5] fmt --- src/tasks/submit/flashbots.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/submit/flashbots.rs b/src/tasks/submit/flashbots.rs index 2a4c52bb..9b2f5b61 100644 --- a/src/tasks/submit/flashbots.rs +++ b/src/tasks/submit/flashbots.rs @@ -131,7 +131,7 @@ impl FlashbotsTask { /// /// Combines all host transactions from the rollup block with the prepared rollup block /// submission transaction, wrapping each as a non-revertible bundle item. - /// + /// /// The rollup block transaction is placed last in the bundle. fn build_bundle_body( &self, From 25ed5fbdbbb7b986c616796417310f00340f25b0 Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 13 Nov 2025 15:06:29 -0700 Subject: [PATCH 3/5] change name to `prepare_bundle` --- src/tasks/submit/flashbots.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tasks/submit/flashbots.rs b/src/tasks/submit/flashbots.rs index 9b2f5b61..20250c24 100644 --- a/src/tasks/submit/flashbots.rs +++ b/src/tasks/submit/flashbots.rs @@ -62,7 +62,7 @@ impl FlashbotsTask { pub async fn prepare(&self, sim_result: &SimResult) -> eyre::Result { // This function is left for forwards compatibility when we want to add // different bundle preparation methods in the future. - self.prepare_bundle_helper(sim_result).await + self.prepare_bundle(sim_result).await } /// Prepares a MEV bundle containing the host transactions and the rollup block. @@ -72,7 +72,7 @@ impl FlashbotsTask { /// 2. Tracking the transaction hash for monitoring /// 3. Encoding the transaction for bundle inclusion /// 4. Constructing the complete bundle body - async fn prepare_bundle_helper(&self, sim_result: &SimResult) -> eyre::Result { + async fn prepare_bundle(&self, sim_result: &SimResult) -> eyre::Result { // Prepare and sign the transaction let block_tx = self.prepare_signed_transaction(sim_result).await?; @@ -131,7 +131,7 @@ impl FlashbotsTask { /// /// Combines all host transactions from the rollup block with the prepared rollup block /// submission transaction, wrapping each as a non-revertible bundle item. - /// + /// /// The rollup block transaction is placed last in the bundle. fn build_bundle_body( &self, From f2c898e013197f90eff77dfa8dd0e9d64778052a Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 13 Nov 2025 16:22:50 -0700 Subject: [PATCH 4/5] docs: updates README to reflect latest changes - removes mentions of BuilderHelper contract and config - removes deprecated variables around builder helper and chain configs - adds documentation for latest flashbots related configs --- README.md | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 0bb5ecd7..ce9053ea 100644 --- a/README.md +++ b/README.md @@ -73,9 +73,7 @@ When the deadline is reached, the simulator is stopped, and all open simulation ### ✨ Submit Task -If Flashbots endpoint has been configured the Flashbots submit task will prepare a Flashbots bundle out of that Signet block, and then submits that bundle to the Flashbots endpoint. - -If a Flashbots endpoint has _not_ been configured, the Builder uses the [builder helper contract] and to craft a rollup block transaction and submits that to the default mempool. This mode of operation is only for testing on private networks and should not be used in production, since it can leak sensitive transaction data from the Signet block. +The Flashbots submit task prepares a Flashbots bundle out of the Signet block and its host transactions and then submits that bundle to the Flashbots endpoint. It sends the hash of the rollup block transaction for to the Metrics task for further tracking. If the block received from simulation is empty, the submit task will ignore it. @@ -88,32 +86,29 @@ Finally, if it's non-empty, the submit task attempts to get a signature for the The Builder is configured via environment variables. The following values are supported for configuration. Key | Required | Description ------------------------------ | -------- | ---------------------------------------------------------------------------------------- -`HOST_CHAIN_ID` | Yes | Host-chain ID (e.g. `3151908`) -`RU_CHAIN_ID` | Yes | Rollup-chain ID (e.g. `14174`) +----------------------------- | -------- | ------------------------------------------------------------------------------ +`RUST_LOG` | No | The log level of the builder +`CHAIN_NAME` | No | The chain name ("pecorino", or the corresponding name) `HOST_RPC_URL` | Yes | RPC endpoint for the host chain `ROLLUP_RPC_URL` | Yes | RPC endpoint for the rollup chain -`TX_POOL_URL` | Yes | Transaction pool URL (must end with `/`) -`TX_BROADCAST_URLS` | No | Additional endpoints for blob txs (comma-separated, slash required) -`FLASHBOTS_ENDPOINT` | No | Flashbots API to submit blocks to. Defaults to the BuilderHelper submit task if not set. -`ZENITH_ADDRESS` | Yes | Zenith contract address -`BUILDER_HELPER_ADDRESS` | Yes | Builder helper contract address `QUINCEY_URL` | Yes | Remote sequencer signing endpoint -`BUILDER_PORT` | Yes | HTTP port for the Builder (default: `8080`) -`SEQUENCER_KEY` | Yes | AWS KMS key ID _or_ local private key for sequencer signing +`SEQUENCER_KEY` | No | AWS Key ID _OR_ local private key for the Sequencer; set IFF using local Sequencer signing instead of remote (via `QUINCEY_URL`) Quincey signing +`TX_POOL_URL` | Yes | Transaction pool URL (must end with `/`) +`FLASHBOTS_ENDPOINT` | No | Flashbots API to submit blocks to +`ROLLUP_BLOCK_GAS_LIMIT` | No | Override for rollup block gas limit +`MAX_HOST_GAS_COEFFICIENT` | No | Optional maximum host gas coefficient, as a percentage, to use when building blocks `BUILDER_KEY` | Yes | AWS KMS key ID _or_ local private key for builder signing +`AWS_ACCESS_KEY_ID` | No | AWS secret access key ID (required if not using `BUILDER_KEY`) +`AWS_SECRET_ACCESS_KEY` | No | AWS secret access key (required if not using `BUILDER_KEY`) +`AWS_DEFAULT_REGION` | No | AWS region for the KMS key in question (required if not using `BUILDER_KEY`) +`BUILDER_PORT` | Yes | HTTP port for the Builder (default: `8080`) `BUILDER_REWARDS_ADDRESS` | Yes | Address receiving builder rewards -`ROLLUP_BLOCK_GAS_LIMIT` | No | Override for block gas limit -`CONCURRENCY_LIMIT` | No | Max concurrent tasks the simulator uses +`CONCURRENCY_LIMIT` | No | Optional max number of concurrent tasks the simulator uses. Defaults to a system call to determine optimal parallelism `OAUTH_CLIENT_ID` | Yes | Oauth client ID for the builder `OAUTH_CLIENT_SECRET` | Yes | Oauth client secret for the builder `OAUTH_AUTHENTICATE_URL` | Yes | Oauth authenticate URL for the builder for performing OAuth logins `OAUTH_TOKEN_URL` | Yes | Oauth token URL for the builder to get an Oauth2 access token `AUTH_TOKEN_REFRESH_INTERVAL` | Yes | The OAuth token refresh interval in seconds. -`CHAIN_NAME` | No | The chain name ("pecorino", or the corresponding name) -`SLOT_OFFSET` | No | Slot timing offset in seconds. Required if `CHAIN_NAME` is not present -`SLOT_DURATION` | No | Slot duration in seconds. Required if `CHAIN_NAME` is not present -`START_TIMESTAMP` | No | UNIX timestamp for slot 0\. Required if `CHAIN_NAME` is not present -------------------------------------------------------------------------------- @@ -188,5 +183,3 @@ The previous header's basefee is tracked through the build loop and used for gas ## 🪪 License This project is licensed under the [MIT License](https://opensource.org/licenses/MIT). - -[builder helper contract]: https://github.com/init4tech/helper-contracts/blob/main/src/BuilderHelper.sol From e40cdd7b8a416ee0ce8e6ca596ac6eaa05954548 Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 13 Nov 2025 16:25:53 -0700 Subject: [PATCH 5/5] fmt --- src/tasks/submit/flashbots.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/submit/flashbots.rs b/src/tasks/submit/flashbots.rs index 20250c24..7ec68a2a 100644 --- a/src/tasks/submit/flashbots.rs +++ b/src/tasks/submit/flashbots.rs @@ -131,7 +131,7 @@ impl FlashbotsTask { /// /// Combines all host transactions from the rollup block with the prepared rollup block /// submission transaction, wrapping each as a non-revertible bundle item. - /// + /// /// The rollup block transaction is placed last in the bundle. fn build_bundle_body( &self,