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+ }
0 commit comments