Skip to content

Commit c5ac3ca

Browse files
alanhwucodyborn
andauthored
feat: use Flashbots bundles for Unichain (#73)
* feat: use Flashbots bundles for Unichain * fix: clean parsing * feat: make receipt polling interval configurable * simplify order_hash form Co-authored-by: Cody Born <[email protected]> * fix: correctly disable revert-protection for Unichain bundles * feat: allow buffer for bundle execution after target block * refactor: abstract bundle logic and transaction helpers * refactor: turn 0x into a constant * feat: use struct for bundle params --------- Co-authored-by: Cody Born <[email protected]>
1 parent 7a3b8c2 commit c5ac3ca

File tree

4 files changed

+340
-113
lines changed

4 files changed

+340
-113
lines changed

src/executors/bundle_client.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use std::sync::Arc;
2+
use alloy::{
3+
eips::eip2718::Encodable2718,
4+
network::{AnyNetwork, EthereumWallet, TransactionBuilder},
5+
primitives::keccak256,
6+
providers::{DynProvider, Provider},
7+
rpc::types::TransactionRequest,
8+
serde::WithOtherFields,
9+
hex,
10+
};
11+
use anyhow::Result;
12+
use serde::{Serialize, Deserialize};
13+
use serde_json::Value;
14+
use tracing::info;
15+
16+
const HEX_PREFIX: &str = "0x";
17+
18+
#[derive(Debug, Serialize, Deserialize)]
19+
#[serde(rename_all = "camelCase")]
20+
pub struct BundleParams {
21+
pub txs: Vec<String>,
22+
pub min_block_number: String,
23+
pub max_block_number: String,
24+
pub reverting_tx_hashes: Vec<String>,
25+
}
26+
27+
pub struct BundleClient {
28+
sender_client: Arc<DynProvider<AnyNetwork>>,
29+
}
30+
31+
impl BundleClient {
32+
pub fn new(sender_client: Arc<DynProvider<AnyNetwork>>) -> Self {
33+
Self { sender_client }
34+
}
35+
36+
/// Send a single transaction as a bundle
37+
pub async fn send_bundle(
38+
&self,
39+
wallet: &EthereumWallet,
40+
tx_request: WithOtherFields<TransactionRequest>,
41+
target_block: u64,
42+
bundle_window: u64,
43+
order_hash: &str,
44+
) -> Result<(alloy::network::AnyTxEnvelope, Option<String>)> {
45+
// Sign the transaction
46+
let tx_envelope = tx_request.build(wallet).await?;
47+
let raw_tx = tx_envelope.encoded_2718();
48+
let signed_tx = format!("{}{}", HEX_PREFIX, hex::encode(&raw_tx));
49+
50+
let tx_hash = keccak256(&raw_tx);
51+
let tx_hash_hex = format!("{}{}", HEX_PREFIX, hex::encode(tx_hash));
52+
53+
// Build bundle params (single transaction bundle that's allowed to revert)
54+
let params = BundleParams {
55+
txs: vec![signed_tx],
56+
min_block_number: format!("{}{:x}", HEX_PREFIX, target_block),
57+
max_block_number: format!("{}{:x}", HEX_PREFIX, target_block + bundle_window),
58+
reverting_tx_hashes: vec![tx_hash_hex],
59+
};
60+
61+
info!("{} - Sending bundle for blocks {}-{}", order_hash, target_block, target_block + bundle_window);
62+
info!("{} - Bundle params: {:?}", order_hash, params);
63+
64+
// Send bundle
65+
let bundle_result = self.sender_client
66+
.raw_request::<Vec<Value>, Value>(
67+
std::borrow::Cow::Borrowed("eth_sendBundle"),
68+
vec![serde_json::to_value(&params)?]
69+
)
70+
.await?;
71+
72+
// Extract bundle hash if available
73+
let bundle_hash = bundle_result.get("bundleHash")
74+
.and_then(|h| h.as_str())
75+
.map(|s| s.to_string());
76+
77+
if let Some(hash) = &bundle_hash {
78+
info!("{} - Bundle submitted with hash: {}", order_hash, hash);
79+
}
80+
81+
Ok((tx_envelope, bundle_hash))
82+
}
83+
}

src/executors/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ pub mod dutch_executor;
22
pub mod priority_executor;
33
pub mod queued_executor;
44
pub mod reactor_error_code;
5+
pub mod bundle_client;
6+
pub mod transaction_utils;

0 commit comments

Comments
 (0)