@@ -55,6 +55,8 @@ const UPDATE_COMMITMENTS_INTERVAL: Duration = Duration::from_secs(30);
5555const UPDATE_COMMITMENTS_THRESHOLD_FACTOR : f64 = 0.95 ;
5656/// Rety last N blocks
5757const RETRY_PREVIOUS_BLOCKS : u64 = 100 ;
58+ /// By default, we scale the gas estimate by 25% when submitting the tx.
59+ const DEFAULT_GAS_ESTIMATE_MULTIPLIER_PCT : u64 = 125 ;
5860
5961#[ derive( Clone , Debug , Hash , PartialEq , Eq , EncodeLabelSet ) ]
6062pub struct AccountLabel {
@@ -254,6 +256,7 @@ pub async fn run_keeper_threads(
254256 } ,
255257 contract. clone ( ) ,
256258 gas_limit,
259+ chain_eth_config. backoff_gas_multiplier_pct ,
257260 chain_state. clone ( ) ,
258261 metrics. clone ( ) ,
259262 fulfilled_requests_cache. clone ( ) ,
@@ -279,6 +282,7 @@ pub async fn run_keeper_threads(
279282 rx,
280283 Arc :: clone ( & contract) ,
281284 gas_limit,
285+ chain_eth_config. backoff_gas_multiplier_pct ,
282286 metrics. clone ( ) ,
283287 fulfilled_requests_cache. clone ( ) ,
284288 )
@@ -303,7 +307,9 @@ pub async fn run_keeper_threads(
303307 chain_state. provider_address ,
304308 ADJUST_FEE_INTERVAL ,
305309 chain_eth_config. legacy_tx ,
306- chain_eth_config. gas_limit ,
310+ // NOTE: we adjust fees based on the maximum gas that the keeper will submit a callback with.
311+ // This number is *larger* than the configured gas limit, as we pad gas on transaction submission for reliability.
312+ ( chain_eth_config. gas_limit * DEFAULT_GAS_ESTIMATE_MULTIPLIER_PCT ) / 100 ,
307313 chain_eth_config. min_profit_pct ,
308314 chain_eth_config. target_profit_pct ,
309315 chain_eth_config. max_profit_pct ,
@@ -372,6 +378,7 @@ pub async fn process_event_with_backoff(
372378 chain_state : BlockchainState ,
373379 contract : Arc < InstrumentedSignablePythContract > ,
374380 gas_limit : U256 ,
381+ backoff_gas_multiplier_pct : u64 ,
375382 metrics : Arc < KeeperMetrics > ,
376383) {
377384 let start_time = std:: time:: Instant :: now ( ) ;
@@ -388,13 +395,35 @@ pub async fn process_event_with_backoff(
388395 max_elapsed_time : Some ( Duration :: from_secs ( 300 ) ) , // retry for 5 minutes
389396 ..Default :: default ( )
390397 } ;
398+
399+ let current_multiplier = Arc :: new ( AtomicU64 :: new ( DEFAULT_GAS_ESTIMATE_MULTIPLIER_PCT ) ) ;
400+
391401 match backoff:: future:: retry_notify (
392402 backoff,
393403 || async {
394- process_event ( & event, & chain_state, & contract, gas_limit, metrics. clone ( ) ) . await
404+ let multiplier = current_multiplier. load ( std:: sync:: atomic:: Ordering :: Relaxed ) ;
405+ process_event (
406+ & event,
407+ & chain_state,
408+ & contract,
409+ gas_limit,
410+ multiplier,
411+ metrics. clone ( ) ,
412+ )
413+ . await
395414 } ,
396415 |e, dur| {
397- tracing:: error!( "Error happened at {:?}: {}" , dur, e) ;
416+ let multiplier = current_multiplier. load ( std:: sync:: atomic:: Ordering :: Relaxed ) ;
417+ tracing:: error!(
418+ "Error at duration {:?} with gas multiplier {}: {}" ,
419+ dur,
420+ multiplier,
421+ e
422+ ) ;
423+ current_multiplier. store (
424+ multiplier. saturating_mul ( backoff_gas_multiplier_pct) / 100 ,
425+ std:: sync:: atomic:: Ordering :: Relaxed ,
426+ ) ;
398427 } ,
399428 )
400429 . await
@@ -436,6 +465,8 @@ pub async fn process_event(
436465 chain_config : & BlockchainState ,
437466 contract : & InstrumentedSignablePythContract ,
438467 gas_limit : U256 ,
468+ // A value of 100 submits the tx with the same gas as the estimate.
469+ gas_estimate_multiplier_pct : u64 ,
439470 metrics : Arc < KeeperMetrics > ,
440471) -> Result < ( ) , backoff:: Error < anyhow:: Error > > {
441472 // ignore requests that are not for the configured provider
@@ -466,6 +497,8 @@ pub async fn process_event(
466497 backoff:: Error :: transient ( anyhow ! ( "Error estimating gas for reveal: {:?}" , e) )
467498 } ) ?;
468499
500+ // The gas limit on the simulated transaction is the configured gas limit on the chain,
501+ // but we are willing to pad the gas a bit to ensure reliable submission.
469502 if gas_estimate > gas_limit {
470503 return Err ( backoff:: Error :: permanent ( anyhow ! (
471504 "Gas estimate for reveal with callback is higher than the gas limit {} > {}" ,
@@ -474,8 +507,10 @@ pub async fn process_event(
474507 ) ) ) ;
475508 }
476509
477- // Pad the gas estimate by 25% after checking it against the gas limit
478- let gas_estimate = gas_estimate. saturating_mul ( 5 . into ( ) ) / 4 ;
510+ // Pad the gas estimate after checking it against the simulation gas limit, ensuring that
511+ // the padded gas estimate doesn't exceed the maximum amount of gas we are willing to use.
512+ let gas_estimate = gas_estimate. saturating_mul ( gas_estimate_multiplier_pct. into ( ) ) / 100 ;
513+ let gas_estimate = gas_estimate. min ( ( gas_limit * DEFAULT_GAS_ESTIMATE_MULTIPLIER_PCT ) / 100 ) ;
479514
480515 let contract_call = contract
481516 . reveal_with_callback (
@@ -589,6 +624,7 @@ pub async fn process_block_range(
589624 block_range : BlockRange ,
590625 contract : Arc < InstrumentedSignablePythContract > ,
591626 gas_limit : U256 ,
627+ backoff_gas_multiplier_pct : u64 ,
592628 chain_state : api:: BlockchainState ,
593629 metrics : Arc < KeeperMetrics > ,
594630 fulfilled_requests_cache : Arc < RwLock < HashSet < u64 > > > ,
@@ -612,6 +648,7 @@ pub async fn process_block_range(
612648 } ,
613649 contract. clone ( ) ,
614650 gas_limit,
651+ backoff_gas_multiplier_pct,
615652 chain_state. clone ( ) ,
616653 metrics. clone ( ) ,
617654 fulfilled_requests_cache. clone ( ) ,
@@ -634,6 +671,7 @@ pub async fn process_single_block_batch(
634671 block_range : BlockRange ,
635672 contract : Arc < InstrumentedSignablePythContract > ,
636673 gas_limit : U256 ,
674+ backoff_gas_multiplier_pct : u64 ,
637675 chain_state : api:: BlockchainState ,
638676 metrics : Arc < KeeperMetrics > ,
639677 fulfilled_requests_cache : Arc < RwLock < HashSet < u64 > > > ,
@@ -660,6 +698,7 @@ pub async fn process_single_block_batch(
660698 chain_state. clone ( ) ,
661699 contract. clone ( ) ,
662700 gas_limit,
701+ backoff_gas_multiplier_pct,
663702 metrics. clone ( ) ,
664703 )
665704 . in_current_span ( ) ,
@@ -806,6 +845,7 @@ pub async fn process_new_blocks(
806845 mut rx : mpsc:: Receiver < BlockRange > ,
807846 contract : Arc < InstrumentedSignablePythContract > ,
808847 gas_limit : U256 ,
848+ backoff_gas_multiplier_pct : u64 ,
809849 metrics : Arc < KeeperMetrics > ,
810850 fulfilled_requests_cache : Arc < RwLock < HashSet < u64 > > > ,
811851) {
@@ -816,6 +856,7 @@ pub async fn process_new_blocks(
816856 block_range,
817857 Arc :: clone ( & contract) ,
818858 gas_limit,
859+ backoff_gas_multiplier_pct,
819860 chain_state. clone ( ) ,
820861 metrics. clone ( ) ,
821862 fulfilled_requests_cache. clone ( ) ,
@@ -832,6 +873,7 @@ pub async fn process_backlog(
832873 backlog_range : BlockRange ,
833874 contract : Arc < InstrumentedSignablePythContract > ,
834875 gas_limit : U256 ,
876+ backoff_gas_multiplier_pct : u64 ,
835877 chain_state : BlockchainState ,
836878 metrics : Arc < KeeperMetrics > ,
837879 fulfilled_requests_cache : Arc < RwLock < HashSet < u64 > > > ,
@@ -841,6 +883,7 @@ pub async fn process_backlog(
841883 backlog_range,
842884 contract,
843885 gas_limit,
886+ backoff_gas_multiplier_pct,
844887 chain_state,
845888 metrics,
846889 fulfilled_requests_cache,
0 commit comments