1+ mod estimate_fee;
2+
13mod geometric;
4+ use estimate_fee:: estimate_eip1559_fees_default;
25pub use geometric:: GeometricGasPrice ;
36
47mod linear;
@@ -13,7 +16,12 @@ use thiserror::Error;
1316use tracing:: { self , instrument} ;
1417use tracing_futures:: Instrument ;
1518
16- use ethers_core:: types:: { transaction:: eip2718:: TypedTransaction , BlockId , TxHash , H256 , U256 } ;
19+ use ethers_core:: {
20+ types:: {
21+ transaction:: eip2718:: TypedTransaction , Block , BlockId , BlockNumber , TxHash , H256 , U256 ,
22+ } ,
23+ utils:: parse_units,
24+ } ;
1725use ethers_providers:: { interval, FromErr , Middleware , PendingTransaction , StreamExt } ;
1826
1927#[ cfg( not( target_arch = "wasm32" ) ) ]
@@ -26,6 +34,9 @@ type WatcherFuture<'a> = Pin<Box<dyn futures_util::stream::Stream<Item = ()> + '
2634#[ cfg( not( target_arch = "wasm32" ) ) ]
2735type WatcherFuture < ' a > = Pin < Box < dyn futures_util:: stream:: Stream < Item = ( ) > + Send + ' a > > ;
2836
37+ const GAS_PRICE_MULTIPLIER_NUMERATOR : u64 = 110 ;
38+ const GAS_PRICE_MULTIPLIER_DENOMINATOR : u64 = 100 ;
39+
2940/// Trait for fetching updated gas prices after a transaction has been first
3041/// broadcast
3142pub trait GasEscalator : Send + Sync + std:: fmt:: Debug {
@@ -68,7 +79,12 @@ pub struct MonitoredTransaction {
6879}
6980
7081impl MonitoredTransaction {
71- fn escalate_gas_price < E : GasEscalator > ( & self , escalator : E ) -> Option < TypedTransaction > {
82+ async fn escalate_gas_price < E : GasEscalator , M : Middleware > (
83+ & self ,
84+ escalator : E ,
85+ provider : & M ,
86+ latest_block : Option < Block < TxHash > > ,
87+ ) -> Option < TypedTransaction > {
7288 // Get the new gas price based on how much time passed since the
7389 // tx was last broadcast
7490 let time_elapsed = self . creation_time . elapsed ( ) . as_secs ( ) ;
@@ -77,7 +93,17 @@ impl MonitoredTransaction {
7793 let Some ( gas_price) = tx. gas_price else {
7894 return None ;
7995 } ;
80- let new_gas_price = escalator. get_gas_price ( gas_price, time_elapsed) ;
96+ // read current gas price from the provider
97+ // and multiply it by 1.1 to have some safety margin
98+ let current_network_gas_price =
99+ mulitply_gas_price ( provider. get_gas_price ( ) . await . unwrap_or_default ( ) ) ;
100+ let escalated_gas_price = escalator. get_gas_price ( gas_price, time_elapsed) ;
101+ tracing:: debug!(
102+ escalated_gas_price = ?escalated_gas_price,
103+ current_network_gas_price = ?current_network_gas_price,
104+ "comparing escalated gas price with current network gas price"
105+ ) ;
106+ let new_gas_price = escalated_gas_price. max ( current_network_gas_price) ;
81107 let mut updated_tx = tx. clone ( ) ;
82108 updated_tx. gas_price = Some ( new_gas_price) ;
83109 Some ( updated_tx. into ( ) )
@@ -98,9 +124,42 @@ impl MonitoredTransaction {
98124 let Some ( max_priority_fee_per_gas) = tx. max_priority_fee_per_gas else {
99125 return None ;
100126 } ;
101- let new_max_fee_per_gas = escalator. get_gas_price ( max_fee_per_gas, time_elapsed) ;
102- let new_max_priority_fee_per_gas =
127+
128+ let base_fee_per_gas =
129+ match latest_block. map ( |block| block. base_fee_per_gas ) . flatten ( ) {
130+ Some ( base_fee_per_gas) => base_fee_per_gas,
131+ None => {
132+ tracing:: warn!(
133+ "No base fee per gas found in latest block, defaulting to 50 gwei"
134+ ) ;
135+ parse_units ( 50 , 9 ) . unwrap ( ) . into ( )
136+ }
137+ } ;
138+ // read current gas price from the provider
139+ // and multiply it by 1.1 to have some safety margin
140+ let ( _, current_max_fee_per_gas, current_max_priority_fee_per_gas) =
141+ estimate_eip1559_fees_default ( provider, base_fee_per_gas)
142+ . await
143+ . unwrap_or_default ( ) ;
144+ let ( multiplied_max_fee_per_gas, multiplied_max_priority_fee_per_gas) = (
145+ mulitply_gas_price ( current_max_fee_per_gas) ,
146+ mulitply_gas_price ( current_max_priority_fee_per_gas) ,
147+ ) ;
148+ let escalated_max_fee_per_gas =
149+ escalator. get_gas_price ( max_fee_per_gas, time_elapsed) ;
150+ let escalated_max_priority_fee_per_gas =
103151 escalator. get_gas_price ( max_priority_fee_per_gas, time_elapsed) ;
152+ let new_max_fee_per_gas = escalated_max_fee_per_gas. max ( multiplied_max_fee_per_gas) ;
153+ let new_max_priority_fee_per_gas =
154+ escalated_max_priority_fee_per_gas. max ( multiplied_max_priority_fee_per_gas) ;
155+
156+ tracing:: debug!(
157+ escalated_max_fee_per_gas = ?escalated_max_fee_per_gas,
158+ escalated_max_priority_fee_per_gas = ?escalated_max_priority_fee_per_gas,
159+ multiplied_max_fee_per_gas = ?multiplied_max_fee_per_gas,
160+ multiplied_max_priority_fee_per_gas = ?multiplied_max_priority_fee_per_gas,
161+ "comparing escalated gas price with current network gas price"
162+ ) ;
104163 let mut updated_tx = tx. clone ( ) ;
105164 updated_tx. max_fee_per_gas = Some ( new_max_fee_per_gas) ;
106165 updated_tx. max_priority_fee_per_gas = Some ( new_max_priority_fee_per_gas) ;
@@ -110,6 +169,12 @@ impl MonitoredTransaction {
110169 }
111170}
112171
172+ fn mulitply_gas_price ( gas_price : U256 ) -> U256 {
173+ let numerator = U256 :: from ( GAS_PRICE_MULTIPLIER_NUMERATOR ) ;
174+ let denominator = U256 :: from ( GAS_PRICE_MULTIPLIER_DENOMINATOR ) ;
175+ gas_price * numerator / denominator
176+ }
177+
113178/// A Gas escalator allows bumping transactions' gas price to avoid getting them
114179/// stuck in the memory pool.
115180///
@@ -366,6 +431,8 @@ impl<M, E: Clone> EscalationTask<M, E> {
366431 tracing:: trace!( ?monitored_txs, "In the escalator watcher loop. Monitoring txs" ) ;
367432 }
368433 let mut new_txs_to_monitor = vec ! [ ] ;
434+ let maybe_latest_block =
435+ self . inner . get_block ( BlockId :: Number ( BlockNumber :: Latest ) ) . await . ok ( ) . flatten ( ) ;
369436 for old_monitored_tx in monitored_txs {
370437 let receipt = if let Some ( tx_hash) = old_monitored_tx. hash {
371438 tracing:: trace!( tx_hash = ?old_monitored_tx. hash, "checking if exists" ) ;
@@ -382,7 +449,10 @@ impl<M, E: Clone> EscalationTask<M, E> {
382449 tracing:: debug!( tx = ?receipt. transaction_hash, "Transaction was included onchain, dropping from escalator" ) ;
383450 continue ;
384451 }
385- let Some ( new_tx) = old_monitored_tx. escalate_gas_price ( self . escalator . clone ( ) ) else {
452+ let Some ( new_tx) = old_monitored_tx
453+ . escalate_gas_price ( self . escalator . clone ( ) , & self . inner , maybe_latest_block. clone ( ) )
454+ . await
455+ else {
386456 tracing:: error!( tx=?old_monitored_tx. hash, "gas price is not set for transaction, dropping from escalator" ) ;
387457 continue ;
388458 } ;
0 commit comments