|
1 | 1 | //! A generic Flashbots bundle API wrapper. |
2 | | -use alloy::network::{Ethereum, Network}; |
3 | | -use alloy::primitives::Address; |
4 | | -use alloy::providers::{ |
5 | | - Provider, SendableTx, |
6 | | - fillers::{FillerControlFlow, TxFiller}, |
| 2 | +use crate::config::{BuilderConfig, HostProvider}; |
| 3 | +use alloy::{ |
| 4 | + primitives::BlockNumber, |
| 5 | + providers::Provider, |
| 6 | + rpc::types::mev::{EthBundleHash, MevSendBundle}, |
7 | 7 | }; |
8 | | -use alloy::rpc::types::eth::TransactionRequest; |
9 | | -use alloy::rpc::types::mev::{EthBundleHash, MevSendBundle, ProtocolVersion}; |
10 | | -use alloy_transport::TransportResult; |
11 | 8 | use eyre::Context as _; |
12 | | -use std::ops::Deref; |
13 | | - |
14 | | -use crate::tasks::block::sim::SimResult; |
15 | | -use signet_types::SignedFill; |
| 9 | +use init4_bin_base::deps::tracing::debug; |
| 10 | +use serde_json::json; |
| 11 | +use signet_zenith::Zenith::ZenithInstance; |
16 | 12 |
|
17 | 13 | /// A wrapper over a `Provider` that adds Flashbots MEV bundle helpers. |
18 | | -#[derive(Debug, Clone)] |
19 | | -pub struct FlashbotsProvider<P> { |
20 | | - inner: P, |
| 14 | +#[derive(Debug)] |
| 15 | +pub struct FlashbotsProvider { |
21 | 16 | /// The base URL for the Flashbots API. |
22 | 17 | pub relay_url: url::Url, |
| 18 | + /// Zenith instance for constructing Signet blocks. |
| 19 | + pub zenith: ZenithInstance<HostProvider>, |
| 20 | + /// Builder configuration for the task. |
| 21 | + pub config: BuilderConfig, |
23 | 22 | } |
24 | 23 |
|
25 | | -impl<P: Provider<Ethereum>> FlashbotsProvider<P> { |
| 24 | +impl FlashbotsProvider { |
26 | 25 | /// Wraps a provider with the URL and returns a new `FlashbotsProvider`. |
27 | | - pub fn new(inner: P, relay_url: url::Url) -> Self { |
28 | | - Self { inner, relay_url } |
29 | | - } |
30 | | - |
31 | | - /// Consume self and return the inner provider. |
32 | | - pub fn into_inner(self) -> P { |
33 | | - self.inner |
34 | | - } |
35 | | - |
36 | | - /// Borrow the inner provider. |
37 | | - pub const fn inner(&self) -> &P { |
38 | | - &self.inner |
39 | | - } |
40 | | -} |
41 | | - |
42 | | -impl<P> Deref for FlashbotsProvider<P> { |
43 | | - type Target = P; |
44 | | - fn deref(&self) -> &Self::Target { |
45 | | - &self.inner |
46 | | - } |
47 | | -} |
48 | | - |
49 | | -impl<P> FlashbotsProvider<P> |
50 | | -where |
51 | | - P: Provider<Ethereum> + Clone + Send + Sync + 'static, |
52 | | -{ |
53 | | - /// Convert a SignedFill to a TransactionRequest calling the Orders contract. |
54 | | - /// |
55 | | - /// This prepares the calldata for RollupOrders::fillPermit2(outputs, permit2) and sets |
56 | | - /// `to` to the given Orders contract address. The returned request is unsigned. |
57 | | - pub fn fill_to_tx_request(fill: &SignedFill, orders_contract: Address) -> TransactionRequest { |
58 | | - fill.to_fill_tx(orders_contract) |
59 | | - } |
60 | | - |
61 | | - /// Construct a new empty bundle template for the given block number. |
62 | | - pub fn empty_bundle(&self, target_block: u64) -> MevSendBundle { |
63 | | - MevSendBundle::new(target_block, Some(target_block), ProtocolVersion::V0_1, vec![]) |
64 | | - } |
65 | | - |
66 | | - /// Prepares a bundle transaction from the simulation result. |
67 | | - pub fn prepare_bundle(&self, sim_result: &SimResult, target_block: u64) -> MevSendBundle { |
68 | | - let bundle_body = Vec::new(); |
69 | | - |
70 | | - // Populate the bundle body with the simulation result. |
71 | | - |
72 | | - // TODO: Push host fills into the Flashbots bundle body. |
73 | | - let _host_fills = sim_result.block.host_fills(); |
74 | | - // _host_fills.iter().map(|f| f.to_fill_tx(todo!())); |
75 | | - |
76 | | - // TODO: Add the rollup block blob transaction to the Flashbots bundle body. |
77 | | - // let blob_tx = ...; |
78 | | - let _ = &sim_result; // keep param used until wired |
79 | | - |
80 | | - // Create the bundle from the target block and bundle body |
81 | | - MevSendBundle::new(target_block, Some(target_block), ProtocolVersion::V0_1, bundle_body) |
| 26 | + pub fn new( |
| 27 | + relay_url: url::Url, |
| 28 | + zenith: ZenithInstance<HostProvider>, |
| 29 | + config: &BuilderConfig, |
| 30 | + ) -> Self { |
| 31 | + Self { relay_url, zenith, config: config.clone() } |
82 | 32 | } |
83 | 33 |
|
84 | 34 | /// Submit the prepared Flashbots bundle to the relay via `mev_sendBundle`. |
85 | 35 | pub async fn send_bundle(&self, bundle: MevSendBundle) -> eyre::Result<EthBundleHash> { |
86 | | - // NOTE: The Flashbots relay expects a single parameter which is the bundle object. |
| 36 | + // NB: The Flashbots relay expects a single parameter which is the bundle object. |
87 | 37 | // Alloy's `raw_request` accepts any serializable params; wrapping in a 1-tuple is fine. |
88 | 38 | let hash: EthBundleHash = self |
89 | | - .inner |
| 39 | + .zenith |
| 40 | + .provider() |
90 | 41 | .raw_request("mev_sendBundle".into(), (bundle,)) |
91 | 42 | .await |
92 | | - .wrap_err("flashbots mev_sendBundle RPC failed")?; |
| 43 | + .wrap_err("mev_sendBundle RPC failed")?; |
| 44 | + debug!(?hash, "mev_sendBundle response"); |
93 | 45 | Ok(hash) |
94 | 46 | } |
95 | 47 |
|
96 | 48 | /// Simulate a bundle via `mev_simBundle`. |
97 | | - pub async fn simulate_bundle(&self, bundle: &MevSendBundle) -> eyre::Result<()> { |
98 | | - // We ignore the response (likely a JSON object with sim traces) for now and just ensure success. |
99 | | - let _resp: serde_json::Value = self |
100 | | - .inner |
| 49 | + pub async fn simulate_bundle(&self, bundle: MevSendBundle) -> eyre::Result<()> { |
| 50 | + let resp: serde_json::Value = self |
| 51 | + .zenith |
| 52 | + .provider() |
101 | 53 | .raw_request("mev_simBundle".into(), (bundle.clone(),)) |
102 | 54 | .await |
103 | | - .wrap_err("flashbots mev_simBundle RPC failed")?; |
| 55 | + .wrap_err("mev_simBundle RPC failed")?; |
| 56 | + debug!(?resp, "mev_simBundle response"); |
104 | 57 | Ok(()) |
105 | 58 | } |
106 | 59 |
|
107 | | - /// Check the status of a previously submitted bundle. |
108 | | - pub async fn bundle_status(&self, _hash: EthBundleHash) -> eyre::Result<()> { |
109 | | - eyre::bail!("FlashbotsProvider::bundle_status unimplemented") |
110 | | - } |
111 | | -} |
112 | | - |
113 | | -impl<N, P> TxFiller<N> for FlashbotsProvider<P> |
114 | | -where |
115 | | - N: Network, |
116 | | - P: TxFiller<N> + Provider<N> + Clone + Send + Sync + core::fmt::Debug + 'static, |
117 | | -{ |
118 | | - type Fillable = <P as TxFiller<N>>::Fillable; |
119 | | - |
120 | | - fn status(&self, tx: &N::TransactionRequest) -> FillerControlFlow { |
121 | | - TxFiller::<N>::status(&self.inner, tx) |
122 | | - } |
123 | | - |
124 | | - fn fill_sync(&self, tx: &mut SendableTx<N>) { |
125 | | - TxFiller::<N>::fill_sync(&self.inner, tx) |
126 | | - } |
127 | | - |
128 | | - fn prepare<Prov: Provider<N>>( |
129 | | - &self, |
130 | | - provider: &Prov, |
131 | | - tx: &N::TransactionRequest, |
132 | | - ) -> impl core::future::Future<Output = TransportResult<Self::Fillable>> + Send { |
133 | | - TxFiller::<N>::prepare(&self.inner, provider, tx) |
134 | | - } |
135 | | - |
136 | | - fn fill( |
| 60 | + /// Check that status of a bundle |
| 61 | + pub async fn bundle_status( |
137 | 62 | &self, |
138 | | - fillable: Self::Fillable, |
139 | | - tx: SendableTx<N>, |
140 | | - ) -> impl core::future::Future<Output = TransportResult<SendableTx<N>>> + Send { |
141 | | - TxFiller::<N>::fill(&self.inner, fillable, tx) |
| 63 | + _hash: EthBundleHash, |
| 64 | + block_number: BlockNumber, |
| 65 | + ) -> eyre::Result<()> { |
| 66 | + let params = json!({ "bundleHash": _hash, "blockNumber": block_number }); |
| 67 | + let resp: serde_json::Value = self |
| 68 | + .zenith |
| 69 | + .provider() |
| 70 | + .raw_request("flashbots_getBundleStatsV2".into(), (params,)) |
| 71 | + .await |
| 72 | + .wrap_err("flashbots_getBundleStatsV2 RPC failed")?; |
| 73 | + debug!(?resp, "flashbots_getBundleStatsV2 response"); |
| 74 | + Ok(()) |
142 | 75 | } |
143 | 76 | } |
0 commit comments