11use crate :: traits:: BundleDatastore ;
2- use alloy_primitives:: TxHash ;
2+ use alloy_consensus:: Transaction ;
3+ use alloy_consensus:: private:: alloy_eips:: Decodable2718 ;
4+ use alloy_consensus:: transaction:: SignerRecoverable ;
35use alloy_primitives:: hex:: { FromHex , ToHexExt } ;
6+ use alloy_primitives:: { Address , TxHash } ;
47use alloy_rpc_types_mev:: EthSendBundle ;
58use anyhow:: Result ;
9+ use op_alloy_consensus:: OpTxEnvelope ;
610use sqlx:: PgPool ;
711use tracing:: info;
812use uuid:: Uuid ;
913
14+ /// Extended bundle data that includes the original bundle plus extracted metadata
15+ #[ derive( Debug , Clone ) ]
16+ pub struct BundleWithMetadata {
17+ pub bundle : EthSendBundle ,
18+ pub txn_hashes : Vec < TxHash > ,
19+ pub senders : Vec < Address > ,
20+ pub min_base_fee : i64 ,
21+ }
22+
1023/// PostgreSQL implementation of the BundleDatastore trait
1124pub struct PostgresDatastore {
1225 pool : PgPool ,
@@ -28,11 +41,46 @@ impl PostgresDatastore {
2841 }
2942}
3043
44+ impl PostgresDatastore {
45+ fn extract_bundle_metadata (
46+ & self ,
47+ bundle : & EthSendBundle ,
48+ ) -> Result < ( Vec < String > , i64 , Vec < String > ) > {
49+ let mut senders = Vec :: new ( ) ;
50+ let mut txn_hashes = Vec :: new ( ) ;
51+
52+ let mut min_base_fee = i64:: MAX ;
53+
54+ for tx_bytes in & bundle. txs {
55+ let envelope = OpTxEnvelope :: decode_2718_exact ( tx_bytes) ?;
56+ txn_hashes. push ( envelope. hash ( ) . encode_hex_with_prefix ( ) ) ;
57+
58+ let sender = match envelope. recover_signer ( ) {
59+ Ok ( signer) => signer,
60+ Err ( err) => return Err ( err. into ( ) ) ,
61+ } ;
62+
63+ senders. push ( sender. encode_hex_with_prefix ( ) ) ;
64+ min_base_fee = min_base_fee. min ( envelope. max_fee_per_gas ( ) as i64 ) ; // todo type and todo not right
65+ }
66+
67+ let minimum_base_fee = if min_base_fee == i64:: MAX {
68+ 0
69+ } else {
70+ min_base_fee
71+ } ;
72+
73+ Ok ( ( senders, minimum_base_fee, txn_hashes) )
74+ }
75+ }
76+
3177#[ async_trait:: async_trait]
3278impl BundleDatastore for PostgresDatastore {
3379 async fn insert_bundle ( & self , bundle : EthSendBundle ) -> Result < Uuid > {
3480 let id = Uuid :: new_v4 ( ) ;
3581
82+ let ( senders, minimum_base_fee, txn_hashes) = self . extract_bundle_metadata ( & bundle) ?;
83+
3684 let txs: Vec < String > = bundle
3785 . txs
3886 . iter ( )
@@ -52,13 +100,17 @@ impl BundleDatastore for PostgresDatastore {
52100 sqlx:: query!(
53101 r#"
54102 INSERT INTO bundles (
55- id, txs, reverting_tx_hashes, dropping_tx_hashes,
103+ id, senders, minimum_base_fee, txn_hashes,
104+ txs, reverting_tx_hashes, dropping_tx_hashes,
56105 block_number, min_timestamp, max_timestamp,
57106 created_at, updated_at
58107 )
59- VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW())
108+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NOW(), NOW())
60109 "# ,
61110 id,
111+ & senders,
112+ minimum_base_fee,
113+ & txn_hashes,
62114 & txs,
63115 & reverting_tx_hashes,
64116 & dropping_tx_hashes,
@@ -72,11 +124,11 @@ impl BundleDatastore for PostgresDatastore {
72124 Ok ( id)
73125 }
74126
75- async fn get_bundle ( & self , id : Uuid ) -> Result < Option < EthSendBundle > > {
127+ async fn get_bundle ( & self , id : Uuid ) -> Result < Option < BundleWithMetadata > > {
76128 let result = sqlx:: query!(
77129 r#"
78- SELECT txs, reverting_tx_hashes, dropping_tx_hashes ,
79- block_number, min_timestamp, max_timestamp
130+ SELECT senders, minimum_base_fee, txn_hashes, txs, reverting_tx_hashes ,
131+ dropping_tx_hashes, block_number, min_timestamp, max_timestamp
80132 FROM bundles
81133 WHERE id = $1
82134 "# ,
@@ -104,7 +156,7 @@ impl BundleDatastore for PostgresDatastore {
104156 . map ( TxHash :: from_hex)
105157 . collect ( ) ;
106158
107- Ok ( Some ( EthSendBundle {
159+ let bundle = EthSendBundle {
108160 txs : txs?,
109161 block_number : row. block_number . unwrap_or ( 0 ) as u64 ,
110162 min_timestamp : row. min_timestamp . map ( |t| t as u64 ) ,
@@ -116,6 +168,27 @@ impl BundleDatastore for PostgresDatastore {
116168 refund_recipient : None ,
117169 refund_tx_hashes : Vec :: new ( ) ,
118170 extra_fields : Default :: default ( ) ,
171+ } ;
172+
173+ let txn_hashes: Result < Vec < TxHash > , _ > = row
174+ . txn_hashes
175+ . unwrap_or_default ( )
176+ . into_iter ( )
177+ . map ( TxHash :: from_hex)
178+ . collect ( ) ;
179+
180+ let senders: Result < Vec < Address > , _ > = row
181+ . senders
182+ . unwrap_or_default ( )
183+ . into_iter ( )
184+ . map ( Address :: from_hex)
185+ . collect ( ) ;
186+
187+ Ok ( Some ( BundleWithMetadata {
188+ bundle,
189+ txn_hashes : txn_hashes?,
190+ senders : senders?,
191+ min_base_fee : row. minimum_base_fee . unwrap_or ( 0 ) ,
119192 } ) )
120193 }
121194 None => Ok ( None ) ,
0 commit comments