11//! Flashbots Task receives simulated blocks from an upstream channel and
22//! submits them to the Flashbots relay as bundles.
3- use core:: error;
4-
53use crate :: {
64 config:: { HostProvider , ZenithInstance } ,
75 quincey:: Quincey ,
86 tasks:: {
9- block:: sim:: { self , SimResult } ,
10- submit:: flashbots:: FlashbotsProvider ,
7+ block:: sim:: SimResult ,
8+ submit:: { SubmitPrep , flashbots:: FlashbotsProvider } ,
119 } ,
1210 utils,
1311} ;
1412use alloy:: {
1513 consensus:: SimpleCoder ,
16- network :: { Ethereum , EthereumWallet , TransactionBuilder } ,
14+ eips :: Encodable2718 ,
1715 primitives:: { TxHash , U256 } ,
18- providers:: { Provider , SendableTx , fillers:: TxFiller } ,
19- rlp:: { BytesMut , bytes} ,
20- rpc:: types:: {
21- TransactionRequest ,
22- mev:: { BundleItem , MevSendBundle , ProtocolVersion } ,
23- } ,
16+ rpc:: types:: mev:: { BundleItem , MevSendBundle , ProtocolVersion } ,
2417} ;
2518use init4_bin_base:: deps:: tracing:: { debug, error} ;
2619use signet_constants:: SignetSystemConstants ;
2720use signet_types:: SignRequest ;
28- use signet_zenith:: {
29- Alloy2718Coder ,
30- Zenith :: { BlockHeader , submitBlockCall} ,
31- ZenithBlock ,
32- } ;
21+ use signet_zenith:: { Alloy2718Coder , Zenith :: BlockHeader , ZenithBlock } ;
3322use tokio:: { sync:: mpsc, task:: JoinHandle } ;
3423
3524/// Errors that the `FlashbotsTask` can encounter.
@@ -57,28 +46,77 @@ pub struct FlashbotsTask {
5746 pub zenith : ZenithInstance < HostProvider > ,
5847 /// Channel for sending hashes of outbound transactions.
5948 pub outbound : mpsc:: UnboundedSender < TxHash > ,
49+ /// Determines if the BundleHelper contract should be used for block submission.
50+ /// NB: If bundle_helper is not used, instead the Zenith contract
51+ /// submitBlock function is used.
52+ bundle_helper : bool ,
6053}
6154
6255impl FlashbotsTask {
6356 /// Returns a new `FlashbotsTask` instance that receives `SimResult` types from the given
6457 /// channel and handles their preparation, submission to the Flashbots network.
65- pub fn new (
58+ pub const fn new (
6659 config : crate :: config:: BuilderConfig ,
6760 constants : SignetSystemConstants ,
6861 quincey : Quincey ,
6962 zenith : ZenithInstance < HostProvider > ,
7063 outbound : mpsc:: UnboundedSender < TxHash > ,
64+ bundle_helper : bool ,
7165 ) -> FlashbotsTask {
72- Self { config, constants, quincey, zenith, outbound }
66+ Self { config, constants, quincey, zenith, outbound, bundle_helper }
7367 }
7468
7569 /// Returns a reference to the inner `HostProvider`
7670 pub fn host_provider ( & self ) -> HostProvider {
7771 self . zenith . provider ( ) . clone ( )
7872 }
7973
80- /// Prepares a bundle transaction from the simulation result.
81- pub async fn prepare_bundle (
74+ /// Prepares a MEV bundle with the configured submit call
75+ pub async fn prepare ( & self , sim_result : & SimResult ) -> Result < MevSendBundle , FlashbotsError > {
76+ match self . bundle_helper {
77+ true => self . prepare_bundle_helper ( sim_result) . await ,
78+ false => self . prepare_zenith ( sim_result) . await ,
79+ }
80+ }
81+
82+ /// Prepares a BundleHelper call containing the rollup block and corresponding fills into a MEV bundle.
83+ async fn prepare_bundle_helper (
84+ & self ,
85+ sim_result : & SimResult ,
86+ ) -> Result < MevSendBundle , FlashbotsError > {
87+ let mut bundle_body = Vec :: new ( ) ;
88+
89+ let prep = SubmitPrep :: new (
90+ & sim_result. block ,
91+ self . host_provider ( ) ,
92+ self . quincey . clone ( ) ,
93+ self . config . clone ( ) ,
94+ self . constants . clone ( ) ,
95+ ) ;
96+
97+ let tx = prep. prep_transaction ( & sim_result. env . prev_header ) . await . map_err ( |err| {
98+ error ! ( ?err, "failed to prepare bumpable transaction" ) ;
99+ FlashbotsError :: PreparationError ( err. to_string ( ) )
100+ } ) ?;
101+
102+ let sendable = self . host_provider ( ) . fill ( tx. req ( ) . clone ( ) ) . await . map_err ( |err| {
103+ error ! ( ?err, "failed to fill transaction" ) ;
104+ FlashbotsError :: PreparationError ( err. to_string ( ) )
105+ } ) ?;
106+
107+ if let Some ( envelope) = sendable. as_envelope ( ) {
108+ bundle_body
109+ . push ( BundleItem :: Tx { tx : envelope. encoded_2718 ( ) . into ( ) , can_revert : true } ) ;
110+ debug ! ( tx_hash = ?envelope. hash( ) , "added filled transaction to bundle" ) ;
111+ }
112+
113+ let bundle = MevSendBundle :: new ( 0 , Some ( 0 ) , ProtocolVersion :: V0_1 , bundle_body) ;
114+
115+ Ok ( bundle)
116+ }
117+
118+ /// Creates a Zenith `submitBlock` transaction and packages it into a MEV bundle from a given simulation result.
119+ async fn prepare_zenith (
82120 & self ,
83121 sim_result : & SimResult ,
84122 ) -> Result < MevSendBundle , FlashbotsError > {
@@ -91,10 +129,10 @@ impl FlashbotsTask {
91129 hostBlockNumber : U256 :: from ( host_block_number) ,
92130 gasLimit : U256 :: from ( self . config . rollup_block_gas_limit ) ,
93131 rewardAddress : self . config . builder_rewards_address ,
94- blockDataHash : sim_result. block . contents_hash ( ) . clone ( ) ,
132+ blockDataHash : * sim_result. block . contents_hash ( ) ,
95133 } ;
96134
97- // Create the raw Zenith block
135+ // Create the raw Zenith block from the header
98136 let block =
99137 ZenithBlock :: < Alloy2718Coder > :: new ( header, sim_result. block . transactions ( ) . to_vec ( ) ) ;
100138
@@ -104,7 +142,7 @@ impl FlashbotsTask {
104142 FlashbotsError :: PreparationError ( err. to_string ( ) )
105143 } ) ?;
106144
107- // Sign the block
145+ // Sign the rollup block contents
108146 let signature = self
109147 . quincey
110148 . get_signature ( & SignRequest {
@@ -113,44 +151,62 @@ impl FlashbotsTask {
113151 ru_chain_id : U256 :: from ( self . constants . ru_chain_id ( ) ) ,
114152 gas_limit : U256 :: from ( self . config . rollup_block_gas_limit ) ,
115153 ru_reward_address : self . config . builder_rewards_address ,
116- contents : sim_result. block . contents_hash ( ) . clone ( ) ,
154+ contents : * sim_result. block . contents_hash ( ) ,
117155 } )
118156 . await
119157 . map_err ( |err| {
120158 error ! ( ?err, "failed to get signature for block" ) ;
121159 FlashbotsError :: PreparationError ( err. to_string ( ) )
122160 } ) ?;
123161
124- // Extract v, r, and s values from the signature
162+ // Extract the v, r, and s values from the signature
125163 let ( v, r, s) = utils:: extract_signature_components ( & signature. sig ) ;
126164
127- // Create the Zenith submit block call transaction and attach the sidecar to it
128- let submit_block_call = self
129- . zenith
130- . submitBlock ( header, v, r, s, block. encoded_txns ( ) . to_vec ( ) . into ( ) )
131- . sidecar ( sidecar) ;
132-
133- // Create the MEV bundle from the target block and bundle body
165+ // Create a MEV bundle and add the host fills to it
134166 let mut bundle_body = Vec :: new ( ) ;
135-
136- // Add host fills to the MEV bundle
137167 let host_fills = sim_result. block . host_fills ( ) ;
138168 for fill in host_fills {
139- let tx = fill. to_fill_tx ( self . constants . ru_orders ( ) ) ;
140- let filled_tx = self . zenith . provider ( ) . fill ( tx. into ( ) ) . await . map_err ( |err| {
169+ // Create a host fill transaction with the rollup orders contract and sign it
170+ let tx_req = fill. to_fill_tx ( self . constants . ru_orders ( ) ) ;
171+ let sendable = self . host_provider ( ) . fill ( tx_req) . await . map_err ( |err| {
141172 error ! ( ?err, "failed to fill transaction" ) ;
142173 FlashbotsError :: PreparationError ( err. to_string ( ) )
143174 } ) ?;
175+
176+ // Add them to the MEV bundle
177+ if let Some ( envelope) = sendable. as_envelope ( ) {
178+ bundle_body
179+ . push ( BundleItem :: Tx { tx : envelope. encoded_2718 ( ) . into ( ) , can_revert : true } ) ;
180+ debug ! ( tx_hash = ?envelope. hash( ) , "added filled transaction to MEV bundle" ) ;
181+ }
144182 }
145183
146- // TODO: Then add the submit block tx
147- // bundle_body.push(submit_block_call.into_transaction_request());
184+ // Create the Zenith submit block call transaction and attach the sidecar to it
185+ let submit_block_call = self
186+ . zenith
187+ . submitBlock ( header, v, r, s, block. encoded_txns ( ) . to_vec ( ) . into ( ) )
188+ . sidecar ( sidecar) ;
189+
190+ // Create the transaction request for the submit call
191+ let rollup_tx = submit_block_call. into_transaction_request ( ) ;
192+ let signed = self . host_provider ( ) . fill ( rollup_tx) . await . map_err ( |err| {
193+ error ! ( ?err, "failed to sign rollup transaction" ) ;
194+ FlashbotsError :: PreparationError ( err. to_string ( ) )
195+ } ) ?;
196+
197+ // Sign the rollup block tx and add it to the MEV bundle as the last transaction.
198+ if let Some ( envelope) = signed. as_envelope ( ) {
199+ bundle_body
200+ . push ( BundleItem :: Tx { tx : envelope. encoded_2718 ( ) . into ( ) , can_revert : false } ) ;
201+ debug ! ( tx_hash = ?envelope. hash( ) , "added rollup transaction to MEV bundle" ) ;
202+ }
148203
204+ // Create the MEV bundle and return it
149205 let bundle = MevSendBundle :: new (
150206 target_block,
151207 Some ( target_block) ,
152208 ProtocolVersion :: V0_1 ,
153- bundle_body, // TODO: Fill with host_fills, then add the rollup transaction
209+ bundle_body,
154210 ) ;
155211
156212 Ok ( bundle)
@@ -160,31 +216,26 @@ impl FlashbotsTask {
160216 async fn task_future ( self , mut inbound : mpsc:: UnboundedReceiver < SimResult > ) {
161217 debug ! ( "starting flashbots task" ) ;
162218
163- let zenith = self . config . connect_zenith ( self . host_provider ( ) ) ;
164-
165- let flashbots = FlashbotsProvider :: new (
166- self . config . flashbots_endpoint . clone ( ) , // TODO: Handle proper Option checks here
167- zenith,
168- & self . config ,
169- ) ;
219+ let flashbots = FlashbotsProvider :: new ( & self . config ) ;
170220
171221 loop {
222+ // Wait for a sim result to come in
172223 let Some ( sim_result) = inbound. recv ( ) . await else {
173224 debug ! ( "upstream task gone - exiting flashbots task" ) ;
174225 break ;
175226 } ;
176227 debug ! ( ?sim_result. env. block_env. number, "simulation block received" ) ;
177228
178- // Prepare the Flashbots bundle from the SimResult, skipping on error.
179- let bundle = match self . prepare_bundle ( & sim_result) . await {
180- Ok ( b ) => b ,
181- Err ( err ) => {
182- error ! ( ?err , "bundle preparation failed" ) ;
183- continue ;
184- }
229+ // Prepare a MEV bundle with the configured call type from the sim result
230+ let bundle = if let Ok ( bundle ) = self . prepare ( & sim_result) . await {
231+ debug ! ( "bundle prepared" ) ;
232+ bundle
233+ } else {
234+ debug ! ( "bundle preparation failed" ) ;
235+ continue ;
185236 } ;
186237
187- // simulate then send the bundle
238+ // simulate the bundle against Flashbots then send the bundle
188239 let sim_bundle_result = flashbots. simulate_bundle ( bundle. clone ( ) ) . await ;
189240 match sim_bundle_result {
190241 Ok ( _) => {
0 commit comments