Skip to content
Draft
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
20 changes: 9 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ name = "zenith-builder-example"
path = "bin/builder.rs"

[dependencies]
init4-bin-base = { version = "0.18.0-rc.0", features = ["perms", "aws"] }
init4-bin-base = { version = "0.18.0-rc.7", features = ["perms", "aws", "pylon"] }

signet-constants = { version = "0.16.0-rc.8" }
signet-sim = { version = "0.16.0-rc.8" }
Expand Down Expand Up @@ -63,7 +63,7 @@ alloy-hardforks = "0.4.0"
alloy-chains = "0.2"

# comment / uncomment for local dev
# [patch.crates-io]
[patch.crates-io]
# signet-constants = { path = "../signet-sdk/crates/constants" }
# signet-types = { path = "../signet-sdk/crates/types" }
# signet-zenith = { path = "../signet-sdk/crates/zenith" }
Expand All @@ -73,4 +73,4 @@ alloy-chains = "0.2"
# signet-journal = { path = "../signet-sdk/crates/journal" }
# signet-tx-cache = { path = "../signet-sdk/crates/tx-cache" }
# signet-bundle = { path = "../signet-sdk/crates/bundle" }
# init4-bin-base = { path = "../bin-base" }
init4-bin-base = { path = "../bin-base" }
14 changes: 13 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use alloy::{
};
use eyre::Result;
use init4_bin_base::{
perms::{Authenticator, OAuthConfig, SharedToken},
perms::{Authenticator, OAuthConfig, SharedToken, pylon},
utils::{
calc::SlotCalculator,
from_env::FromEnv,
Expand All @@ -25,6 +25,9 @@ use signet_zenith::Zenith;
use std::borrow::Cow;
use tokio::join;

/// Pylon client type for blob sidecar submission.
pub type PylonClient = pylon::PylonClient;

/// Type alias for the provider used to simulate against rollup state.
pub type RuProvider = RootProvider<Ethereum>;

Expand Down Expand Up @@ -168,6 +171,10 @@ pub struct BuilderConfig {

/// The signet system constants.
pub constants: SignetSystemConstants,

/// URL for the Pylon blob server API.
#[from_env(var = "PYLON_URL", desc = "URL for the Pylon blob server API")]
pub pylon_url: url::Url,
}

impl BuilderConfig {
Expand Down Expand Up @@ -285,4 +292,9 @@ impl BuilderConfig {
((gas_limit as u128 * (self.max_host_gas_coefficient.unwrap_or(80) as u128)) / 100u128)
as u64
}

/// Connect to the Pylon blob server.
pub fn connect_pylon(&self) -> PylonClient {
PylonClient::new(self.pylon_url.clone(), self.oauth_token())
}
}
17 changes: 16 additions & 1 deletion src/tasks/block/sim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
config::{BuilderConfig, HostProvider, RuProvider},
tasks::env::SimEnv,
};
use alloy::consensus::Header;
use alloy::{consensus::Header, eips::Encodable2718, primitives::Bytes};
use init4_bin_base::{
deps::metrics::{counter, histogram},
utils::calc::SlotCalculator,
Expand Down Expand Up @@ -62,6 +62,21 @@ impl SimResult {
pub fn clone_span(&self) -> Span {
self.sim_env.clone_span()
}

/// 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 always included and placed last in the bundle.
pub fn build_bundle_body(&self, block_tx_bytes: Bytes) -> Vec<Bytes> {
self.block
.host_transactions()
.iter()
.map(|tx| tx.encoded_2718().into())
.chain(std::iter::once(block_tx_bytes))
.collect()
}
}

/// A task that builds blocks based on incoming [`SimEnv`]s and a simulation
Expand Down
56 changes: 26 additions & 30 deletions src/tasks/submit/flashbots.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
//! Flashbots Task receives simulated blocks from an upstream channel and
//! submits them to the Flashbots relay as bundles.
use crate::{
config::{BuilderConfig, FlashbotsProvider, HostProvider, ZenithInstance},
config::{BuilderConfig, FlashbotsProvider, HostProvider, PylonClient, ZenithInstance},
quincey::Quincey,
tasks::{block::sim::SimResult, submit::SubmitPrep},
};
use alloy::{
consensus::TxEnvelope,
eips::Encodable2718,
primitives::{Bytes, TxHash},
providers::ext::MevApi,
consensus::TxEnvelope, eips::Encodable2718, primitives::TxHash, providers::ext::MevApi,
rpc::types::mev::EthSendBundle,
};
use init4_bin_base::{deps::metrics::counter, utils::signer::LocalOrAws};
Expand All @@ -32,6 +29,8 @@ pub struct FlashbotsTask {
signer: LocalOrAws,
/// Channel for sending hashes of outbound transactions.
outbound: mpsc::UnboundedSender<TxHash>,
/// Pylon client for blob sidecar submission.
pylon: PylonClient,
}

impl FlashbotsTask {
Expand All @@ -48,8 +47,9 @@ impl FlashbotsTask {
)?;

let zenith = config.connect_zenith(host_provider);
let pylon = config.connect_pylon();

Ok(Self { config, quincey, zenith, flashbots, signer: builder_key, outbound })
Ok(Self { config, quincey, zenith, flashbots, signer: builder_key, outbound, pylon })
}

/// Prepares a MEV bundle from a simulation result.
Expand Down Expand Up @@ -81,7 +81,7 @@ impl FlashbotsTask {
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 txs = self.build_bundle_body(sim_result, tx_bytes);
let txs = sim_result.build_bundle_body(tx_bytes);

// Create the MEV bundle (valid only in the specific host block)
Ok(EthSendBundle {
Expand All @@ -90,7 +90,6 @@ impl FlashbotsTask {
..Default::default()
})
}

/// Prepares and signs the submission transaction for the rollup block.
///
/// Creates a `SubmitPrep` instance to build the transaction, then fills
Expand Down Expand Up @@ -130,26 +129,6 @@ impl FlashbotsTask {
}
}

/// 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<Bytes> {
sim_result
.block
.host_transactions()
.iter()
.map(|tx| tx.encoded_2718().into())
.chain(std::iter::once(tx_bytes))
.collect()
}

/// Main task loop that processes simulation results and submits bundles to Flashbots.
///
/// Receives `SimResult`s from the inbound channel, prepares MEV bundles, and submits
Expand All @@ -176,7 +155,6 @@ impl FlashbotsTask {

// Prepare a MEV bundle with the configured call type from the sim result
let result = self.prepare(&sim_result).instrument(span.clone()).await;

let bundle = match result {
Ok(bundle) => bundle,
Err(error) => {
Expand All @@ -186,16 +164,21 @@ impl FlashbotsTask {
}
};

// Due to the way the bundle is built, the block transaction is the last transaction in the bundle, and will always exist.
// We'll use this to forward the tx to pylon, which will preload the sidecar.
let block_tx = bundle.txs.last().unwrap().clone();

// Make a child span to cover submission, or use the current span
// if debug is not enabled.
let _guard = span.enter();
let submit_span = debug_span!("flashbots.submit",).or_current();

// Send the bundle to Flashbots, instrumenting the send future so
// all events inside the async send are attributed to the submit
// span.
// span. If Flashbots accepts, submit envelope to Pylon.
let flashbots = self.flashbots().to_owned();
let signer = self.signer.clone();
let pylon = self.pylon.clone();

tokio::spawn(
async move {
Expand All @@ -209,6 +192,19 @@ impl FlashbotsTask {
hash = resp.map(|r| r.bundle_hash.to_string()),
"Submitted MEV bundle to Flashbots, received OK response"
);

match pylon.post_sidecar(block_tx).await {
Ok(()) => {
counter!("signet.builder.pylon.sidecars_submitted")
.increment(1);
debug!("posted sidecar to pylon");
}
Err(err) => {
counter!("signet.builder.pylon.submission_failures")
.increment(1);
error!(%err, "pylon submission failed");
}
}
}
Err(err) => {
counter!("signet.builder.flashbots.submission_failures").increment(1);
Expand Down
1 change: 1 addition & 0 deletions src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub fn setup_test_config() -> &'static BuilderConfig {
block_query_cutoff_buffer: 3000,
max_host_gas_coefficient: Some(80),
constants: SignetSystemConstants::parmigiana(),
pylon_url: "http://localhost:8081".parse().unwrap(),
}
})
}
Expand Down
Loading