@@ -137,10 +137,11 @@ pub struct ResourceEstimate {
137137 /// Recommended fee based on a percentile of transactions above the threshold.
138138 /// Provides a safety margin over the bare minimum.
139139 pub recommended_priority_fee : U256 ,
140- /// Total resource usage of transactions at or below the threshold.
140+ /// Total resource usage of transactions at or above the threshold fee .
141141 pub cumulative_usage : u128 ,
142- /// Number of transactions that would be displaced at the threshold fee.
143- pub supporting_transactions : usize ,
142+ /// Number of transactions at or above `threshold_priority_fee`. These higher-paying
143+ /// transactions remain included alongside the bundle; lower-paying ones are displaced.
144+ pub threshold_tx_count : usize ,
144145 /// Total transactions considered in the estimate.
145146 pub total_transactions : usize ,
146147}
@@ -282,6 +283,7 @@ impl PriorityFeeEstimator {
282283 // Materialise sorted transactions per flashblock so we can drop the lock before
283284 // running the estimation logic.
284285 let mut flashblock_transactions = Vec :: new ( ) ;
286+ let mut total_tx_count = 0usize ;
285287 for flashblock in block_metrics. flashblocks ( ) {
286288 let sorted: Vec < MeteredTransaction > = flashblock
287289 . transactions_sorted_by_priority_fee ( )
@@ -291,6 +293,7 @@ impl PriorityFeeEstimator {
291293 if sorted. is_empty ( ) {
292294 continue ;
293295 }
296+ total_tx_count += sorted. len ( ) ;
294297 flashblock_transactions. push ( ( flashblock. flashblock_index , sorted) ) ;
295298 }
296299 drop ( cache_guard) ;
@@ -299,16 +302,20 @@ impl PriorityFeeEstimator {
299302 return Ok ( None ) ;
300303 }
301304
302- // Flatten into a single list for use-it-or-lose-it resources.
303- let mut aggregate_transactions: Vec < MeteredTransaction > = flashblock_transactions
304- . iter ( )
305- . flat_map ( |( _, txs) | txs. iter ( ) . cloned ( ) )
306- . collect ( ) ;
307- aggregate_transactions. sort_by ( |a, b| a. priority_fee_per_gas . cmp ( & b. priority_fee_per_gas ) ) ;
305+ // Build the aggregate list for use-it-or-lose-it resources by collecting references
306+ // to avoid cloning transactions twice.
307+ let mut aggregate_refs: Vec < & MeteredTransaction > = Vec :: with_capacity ( total_tx_count) ;
308+ for ( _, txs) in & flashblock_transactions {
309+ aggregate_refs. extend ( txs. iter ( ) ) ;
310+ }
311+ aggregate_refs. sort_by ( |a, b| a. priority_fee_per_gas . cmp ( & b. priority_fee_per_gas ) ) ;
308312
309313 let mut flashblock_estimates = Vec :: new ( ) ;
310314
311- for ( flashblock_index, txs) in flashblock_transactions {
315+ for ( flashblock_index, txs) in & flashblock_transactions {
316+ // Build a reference slice for this flashblock's transactions.
317+ let txs_refs: Vec < & MeteredTransaction > = txs. iter ( ) . collect ( ) ;
318+
312319 let mut estimates = ResourceEstimates :: default ( ) ;
313320 for resource in ResourceKind :: all ( ) {
314321 let Some ( demand_value) = demand. demand_for ( resource) else {
@@ -318,10 +325,10 @@ impl PriorityFeeEstimator {
318325 continue ;
319326 } ;
320327
321- let transactions = if resource. use_it_or_lose_it ( ) {
322- & aggregate_transactions
328+ let transactions: & [ & MeteredTransaction ] = if resource. use_it_or_lose_it ( ) {
329+ & aggregate_refs
323330 } else {
324- & txs
331+ & txs_refs
325332 } ;
326333 let estimate = compute_estimate (
327334 resource,
@@ -337,7 +344,7 @@ impl PriorityFeeEstimator {
337344 }
338345
339346 flashblock_estimates. push ( FlashblockResourceEstimates {
340- flashblock_index,
347+ flashblock_index : * flashblock_index ,
341348 estimates,
342349 } ) ;
343350 }
@@ -410,7 +417,7 @@ impl PriorityFeeEstimator {
410417 threshold_priority_fee : median_fee,
411418 recommended_priority_fee : median_fee,
412419 cumulative_usage : 0 ,
413- supporting_transactions : 0 ,
420+ threshold_tx_count : 0 ,
414421 total_transactions : 0 ,
415422 } ,
416423 ) ;
@@ -464,7 +471,7 @@ impl PriorityFeeEstimator {
464471/// Returns `Err` if the bundle's demand exceeds the resource limit.
465472fn compute_estimate (
466473 resource : ResourceKind ,
467- transactions : & [ MeteredTransaction ] ,
474+ transactions : & [ & MeteredTransaction ] ,
468475 demand : u128 ,
469476 limit : u128 ,
470477 usage_fn : fn ( & MeteredTransaction ) -> u128 ,
@@ -486,13 +493,13 @@ fn compute_estimate(
486493 threshold_priority_fee : default_fee,
487494 recommended_priority_fee : default_fee,
488495 cumulative_usage : 0 ,
489- supporting_transactions : 0 ,
496+ threshold_tx_count : 0 ,
490497 total_transactions : 0 ,
491498 } ) ;
492499 }
493500
494501 // Sort transactions by priority fee descending (highest first).
495- let mut sorted: Vec < _ > = transactions. iter ( ) . collect ( ) ;
502+ let mut sorted: Vec < _ > = transactions. to_vec ( ) ;
496503 sorted. sort_by ( |a, b| b. priority_fee_per_gas . cmp ( & a. priority_fee_per_gas ) ) ;
497504
498505 // Walk from highest-paying transactions, subtracting usage from remaining capacity.
@@ -523,41 +530,45 @@ fn compute_estimate(
523530 threshold_priority_fee : default_fee,
524531 recommended_priority_fee : default_fee,
525532 cumulative_usage : included_usage,
526- supporting_transactions : sorted. len ( ) ,
533+ threshold_tx_count : sorted. len ( ) ,
527534 total_transactions : sorted. len ( ) ,
528535 } ) ;
529536 }
530537
531- let ( threshold_idx , threshold_fee) = match last_included_idx {
538+ let ( supporting_count , threshold_fee, recommended_fee ) = match last_included_idx {
532539 Some ( idx) => {
533540 // At least one transaction fits alongside the bundle.
534541 // The threshold is the fee of the last included transaction.
535- ( idx, sorted[ idx] . priority_fee_per_gas )
542+ let threshold_fee = sorted[ idx] . priority_fee_per_gas ;
543+
544+ // For recommended fee, look at included transactions (those above threshold)
545+ // and pick one at the specified percentile for a safety margin.
546+ let included = & sorted[ ..=idx] ;
547+ let percentile = percentile. clamp ( 0.0 , 1.0 ) ;
548+ let recommended_fee = if included. len ( ) <= 1 {
549+ threshold_fee
550+ } else {
551+ // Pick from the higher end of included transactions for safety.
552+ let pos = ( ( included. len ( ) - 1 ) as f64 * ( 1.0 - percentile) ) . round ( ) as usize ;
553+ included[ pos. min ( included. len ( ) - 1 ) ] . priority_fee_per_gas
554+ } ;
555+
556+ ( idx + 1 , threshold_fee, recommended_fee)
536557 }
537558 None => {
538559 // No transactions fit - even the first transaction would crowd out
539560 // the bundle. The bundle must beat the highest fee to be included.
540- ( 0 , sorted[ 0 ] . priority_fee_per_gas )
561+ // Report 0 supporting transactions since none were actually included.
562+ let threshold_fee = sorted[ 0 ] . priority_fee_per_gas ;
563+ ( 0 , threshold_fee, threshold_fee)
541564 }
542565 } ;
543566
544- // For recommended fee, look at included transactions (those above threshold)
545- // and pick one at the specified percentile for a safety margin.
546- let included = & sorted[ ..=threshold_idx] ;
547- let percentile = percentile. clamp ( 0.0 , 1.0 ) ;
548- let recommended_fee = if included. len ( ) <= 1 {
549- threshold_fee
550- } else {
551- // Pick from the higher end of included transactions for safety.
552- let pos = ( ( included. len ( ) - 1 ) as f64 * ( 1.0 - percentile) ) . round ( ) as usize ;
553- included[ pos. min ( included. len ( ) - 1 ) ] . priority_fee_per_gas
554- } ;
555-
556567 Ok ( ResourceEstimate {
557568 threshold_priority_fee : threshold_fee,
558569 recommended_priority_fee : recommended_fee,
559570 cumulative_usage : included_usage,
560- supporting_transactions : threshold_idx + 1 ,
571+ threshold_tx_count : supporting_count ,
561572 total_transactions : sorted. len ( ) ,
562573 } )
563574}
@@ -645,9 +656,10 @@ mod tests {
645656 // - Include tx priority=5: remaining = 20-10 = 10 < 15 ✗ (stop)
646657 // Threshold = 10 (the last included tx's fee)
647658 let txs = vec ! [ tx( 10 , 10 ) , tx( 5 , 10 ) , tx( 2 , 10 ) ] ;
659+ let txs_refs: Vec < & MeteredTransaction > = txs. iter ( ) . collect ( ) ;
648660 let quote = compute_estimate (
649661 ResourceKind :: GasUsed ,
650- & txs ,
662+ & txs_refs ,
651663 15 ,
652664 30 , // limit
653665 usage_extractor ( ResourceKind :: GasUsed ) ,
@@ -657,7 +669,7 @@ mod tests {
657669 . expect ( "no error" ) ;
658670 assert_eq ! ( quote. threshold_priority_fee, U256 :: from( 10 ) ) ;
659671 assert_eq ! ( quote. cumulative_usage, 10 ) ; // Only the first tx was included
660- assert_eq ! ( quote. supporting_transactions , 1 ) ;
672+ assert_eq ! ( quote. threshold_tx_count , 1 ) ;
661673 assert_eq ! ( quote. total_transactions, 3 ) ;
662674 }
663675
@@ -666,9 +678,10 @@ mod tests {
666678 // Limit: 100, Demand: 15
667679 // All transactions fit with room to spare → return default fee
668680 let txs = vec ! [ tx( 10 , 10 ) , tx( 5 , 10 ) , tx( 2 , 10 ) ] ;
681+ let txs_refs: Vec < & MeteredTransaction > = txs. iter ( ) . collect ( ) ;
669682 let quote = compute_estimate (
670683 ResourceKind :: GasUsed ,
671- & txs ,
684+ & txs_refs ,
672685 15 ,
673686 100 , // limit is much larger than total usage
674687 usage_extractor ( ResourceKind :: GasUsed ) ,
@@ -679,16 +692,17 @@ mod tests {
679692 assert_eq ! ( quote. threshold_priority_fee, DEFAULT_FEE ) ;
680693 assert_eq ! ( quote. recommended_priority_fee, DEFAULT_FEE ) ;
681694 assert_eq ! ( quote. cumulative_usage, 30 ) ; // All txs included
682- assert_eq ! ( quote. supporting_transactions , 3 ) ;
695+ assert_eq ! ( quote. threshold_tx_count , 3 ) ;
683696 }
684697
685698 #[ test]
686699 fn compute_estimate_demand_exceeds_limit ( ) {
687700 // Demand > Limit → Error
688701 let txs = vec ! [ tx( 10 , 10 ) , tx( 5 , 10 ) ] ;
702+ let txs_refs: Vec < & MeteredTransaction > = txs. iter ( ) . collect ( ) ;
689703 let result = compute_estimate (
690704 ResourceKind :: GasUsed ,
691- & txs ,
705+ & txs_refs ,
692706 50 , // demand
693707 30 , // limit
694708 usage_extractor ( ResourceKind :: GasUsed ) ,
@@ -712,9 +726,10 @@ mod tests {
712726 // After including tx priority=10: remaining = 20 >= 20 ✓
713727 // After including tx priority=5: remaining = 10 < 20 ✗
714728 let txs = vec ! [ tx( 10 , 10 ) , tx( 5 , 10 ) ] ;
729+ let txs_refs: Vec < & MeteredTransaction > = txs. iter ( ) . collect ( ) ;
715730 let quote = compute_estimate (
716731 ResourceKind :: GasUsed ,
717- & txs ,
732+ & txs_refs ,
718733 20 ,
719734 30 ,
720735 usage_extractor ( ResourceKind :: GasUsed ) ,
@@ -724,16 +739,17 @@ mod tests {
724739 . expect ( "no error" ) ;
725740 assert_eq ! ( quote. threshold_priority_fee, U256 :: from( 10 ) ) ;
726741 assert_eq ! ( quote. cumulative_usage, 10 ) ;
727- assert_eq ! ( quote. supporting_transactions , 1 ) ;
742+ assert_eq ! ( quote. threshold_tx_count , 1 ) ;
728743 }
729744
730745 #[ test]
731746 fn compute_estimate_single_transaction ( ) {
732747 // Single tx that fits
733748 let txs = vec ! [ tx( 10 , 10 ) ] ;
749+ let txs_refs: Vec < & MeteredTransaction > = txs. iter ( ) . collect ( ) ;
734750 let quote = compute_estimate (
735751 ResourceKind :: GasUsed ,
736- & txs ,
752+ & txs_refs ,
737753 15 ,
738754 30 ,
739755 usage_extractor ( ResourceKind :: GasUsed ) ,
@@ -752,9 +768,10 @@ mod tests {
752768 // Limit: 25, Demand: 20
753769 // First tx uses 10, remaining = 15 < 20 → can't even include first tx
754770 let txs = vec ! [ tx( 10 , 10 ) , tx( 5 , 10 ) ] ;
771+ let txs_refs: Vec < & MeteredTransaction > = txs. iter ( ) . collect ( ) ;
755772 let quote = compute_estimate (
756773 ResourceKind :: GasUsed ,
757- & txs ,
774+ & txs_refs ,
758775 20 ,
759776 25 ,
760777 usage_extractor ( ResourceKind :: GasUsed ) ,
@@ -765,16 +782,16 @@ mod tests {
765782 // Need to beat the highest fee since we couldn't include any tx
766783 assert_eq ! ( quote. threshold_priority_fee, U256 :: from( 10 ) ) ;
767784 assert_eq ! ( quote. cumulative_usage, 0 ) ;
768- assert_eq ! ( quote. supporting_transactions , 1 ) ; // Reports first tx as threshold
785+ assert_eq ! ( quote. threshold_tx_count , 0 ) ; // No transactions can coexist
769786 }
770787
771788 #[ test]
772789 fn compute_estimate_empty_transactions ( ) {
773790 // No transactions = uncongested, return default fee
774- let txs : Vec < MeteredTransaction > = vec ! [ ] ;
791+ let txs_refs : Vec < & MeteredTransaction > = vec ! [ ] ;
775792 let quote = compute_estimate (
776793 ResourceKind :: GasUsed ,
777- & txs ,
794+ & txs_refs ,
778795 15 ,
779796 30 ,
780797 usage_extractor ( ResourceKind :: GasUsed ) ,
0 commit comments