@@ -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 {
@@ -270,6 +272,7 @@ pub async fn run_keeper_threads(
270272 } ,
271273 contract. clone ( ) ,
272274 gas_limit,
275+ chain_eth_config. backoff_gas_multiplier_pct ,
273276 chain_state. clone ( ) ,
274277 metrics. clone ( ) ,
275278 fulfilled_requests_cache. clone ( ) ,
@@ -295,6 +298,7 @@ pub async fn run_keeper_threads(
295298 rx,
296299 Arc :: clone ( & contract) ,
297300 gas_limit,
301+ chain_eth_config. backoff_gas_multiplier_pct ,
298302 metrics. clone ( ) ,
299303 fulfilled_requests_cache. clone ( ) ,
300304 )
@@ -319,7 +323,9 @@ pub async fn run_keeper_threads(
319323 chain_state. provider_address ,
320324 ADJUST_FEE_INTERVAL ,
321325 chain_eth_config. legacy_tx ,
322- chain_eth_config. gas_limit ,
326+ // NOTE: we adjust fees based on the maximum gas that the keeper will submit a callback with.
327+ // This number is *larger* than the configured gas limit, as we pad gas on transaction submission for reliability.
328+ ( chain_eth_config. gas_limit * DEFAULT_GAS_ESTIMATE_MULTIPLIER_PCT ) / 100 ,
323329 chain_eth_config. min_profit_pct ,
324330 chain_eth_config. target_profit_pct ,
325331 chain_eth_config. max_profit_pct ,
@@ -388,6 +394,7 @@ pub async fn process_event_with_backoff(
388394 chain_state : BlockchainState ,
389395 contract : Arc < InstrumentedSignablePythContract > ,
390396 gas_limit : U256 ,
397+ backoff_gas_multiplier_pct : u64 ,
391398 metrics : Arc < KeeperMetrics > ,
392399) {
393400 let start_time = std:: time:: Instant :: now ( ) ;
@@ -403,13 +410,34 @@ pub async fn process_event_with_backoff(
403410 ..Default :: default ( )
404411 } ;
405412
413+ let current_multiplier = Arc :: new ( AtomicU64 :: new ( DEFAULT_GAS_ESTIMATE_MULTIPLIER_PCT ) ) ;
414+
406415 let success = backoff:: future:: retry_notify (
407416 backoff,
408417 || async {
409- process_event ( & event, & chain_state, & contract, gas_limit, metrics. clone ( ) ) . await
418+ let multiplier = current_multiplier. load ( std:: sync:: atomic:: Ordering :: Relaxed ) ;
419+ process_event (
420+ & event,
421+ & chain_state,
422+ & contract,
423+ gas_limit,
424+ multiplier,
425+ metrics. clone ( ) ,
426+ )
427+ . await
410428 } ,
411429 |e, dur| {
412- tracing:: error!( "Error happened at {:?}: {}" , dur, e) ;
430+ let multiplier = current_multiplier. load ( std:: sync:: atomic:: Ordering :: Relaxed ) ;
431+ tracing:: error!(
432+ "Error at duration {:?} with gas multiplier {}: {}" ,
433+ dur,
434+ multiplier,
435+ e
436+ ) ;
437+ current_multiplier. store (
438+ multiplier. saturating_mul ( backoff_gas_multiplier_pct) / 100 ,
439+ std:: sync:: atomic:: Ordering :: Relaxed ,
440+ ) ;
413441 } ,
414442 )
415443 . await ;
@@ -467,6 +495,8 @@ pub async fn process_event(
467495 chain_config : & BlockchainState ,
468496 contract : & InstrumentedSignablePythContract ,
469497 gas_limit : U256 ,
498+ // A value of 100 submits the tx with the same gas as the estimate.
499+ gas_estimate_multiplier_pct : u64 ,
470500 metrics : Arc < KeeperMetrics > ,
471501) -> Result < ( ) , backoff:: Error < anyhow:: Error > > {
472502 // ignore requests that are not for the configured provider
@@ -497,6 +527,8 @@ pub async fn process_event(
497527 backoff:: Error :: transient ( anyhow ! ( "Error estimating gas for reveal: {:?}" , e) )
498528 } ) ?;
499529
530+ // The gas limit on the simulated transaction is the configured gas limit on the chain,
531+ // but we are willing to pad the gas a bit to ensure reliable submission.
500532 if gas_estimate > gas_limit {
501533 return Err ( backoff:: Error :: permanent ( anyhow ! (
502534 "Gas estimate for reveal with callback is higher than the gas limit {} > {}" ,
@@ -505,8 +537,10 @@ pub async fn process_event(
505537 ) ) ) ;
506538 }
507539
508- // Pad the gas estimate by 25% after checking it against the gas limit
509- let gas_estimate = gas_estimate. saturating_mul ( 5 . into ( ) ) / 4 ;
540+ // Pad the gas estimate after checking it against the simulation gas limit, ensuring that
541+ // the padded gas estimate doesn't exceed the maximum amount of gas we are willing to use.
542+ let gas_estimate = gas_estimate. saturating_mul ( gas_estimate_multiplier_pct. into ( ) ) / 100 ;
543+ let gas_estimate = gas_estimate. min ( ( gas_limit * DEFAULT_GAS_ESTIMATE_MULTIPLIER_PCT ) / 100 ) ;
510544
511545 let contract_call = contract
512546 . reveal_with_callback (
@@ -620,6 +654,7 @@ pub async fn process_block_range(
620654 block_range : BlockRange ,
621655 contract : Arc < InstrumentedSignablePythContract > ,
622656 gas_limit : U256 ,
657+ backoff_gas_multiplier_pct : u64 ,
623658 chain_state : api:: BlockchainState ,
624659 metrics : Arc < KeeperMetrics > ,
625660 fulfilled_requests_cache : Arc < RwLock < HashSet < u64 > > > ,
@@ -643,6 +678,7 @@ pub async fn process_block_range(
643678 } ,
644679 contract. clone ( ) ,
645680 gas_limit,
681+ backoff_gas_multiplier_pct,
646682 chain_state. clone ( ) ,
647683 metrics. clone ( ) ,
648684 fulfilled_requests_cache. clone ( ) ,
@@ -665,6 +701,7 @@ pub async fn process_single_block_batch(
665701 block_range : BlockRange ,
666702 contract : Arc < InstrumentedSignablePythContract > ,
667703 gas_limit : U256 ,
704+ backoff_gas_multiplier_pct : u64 ,
668705 chain_state : api:: BlockchainState ,
669706 metrics : Arc < KeeperMetrics > ,
670707 fulfilled_requests_cache : Arc < RwLock < HashSet < u64 > > > ,
@@ -691,6 +728,7 @@ pub async fn process_single_block_batch(
691728 chain_state. clone ( ) ,
692729 contract. clone ( ) ,
693730 gas_limit,
731+ backoff_gas_multiplier_pct,
694732 metrics. clone ( ) ,
695733 )
696734 . in_current_span ( ) ,
@@ -837,6 +875,7 @@ pub async fn process_new_blocks(
837875 mut rx : mpsc:: Receiver < BlockRange > ,
838876 contract : Arc < InstrumentedSignablePythContract > ,
839877 gas_limit : U256 ,
878+ backoff_gas_multiplier_pct : u64 ,
840879 metrics : Arc < KeeperMetrics > ,
841880 fulfilled_requests_cache : Arc < RwLock < HashSet < u64 > > > ,
842881) {
@@ -847,6 +886,7 @@ pub async fn process_new_blocks(
847886 block_range,
848887 Arc :: clone ( & contract) ,
849888 gas_limit,
889+ backoff_gas_multiplier_pct,
850890 chain_state. clone ( ) ,
851891 metrics. clone ( ) ,
852892 fulfilled_requests_cache. clone ( ) ,
@@ -863,6 +903,7 @@ pub async fn process_backlog(
863903 backlog_range : BlockRange ,
864904 contract : Arc < InstrumentedSignablePythContract > ,
865905 gas_limit : U256 ,
906+ backoff_gas_multiplier_pct : u64 ,
866907 chain_state : BlockchainState ,
867908 metrics : Arc < KeeperMetrics > ,
868909 fulfilled_requests_cache : Arc < RwLock < HashSet < u64 > > > ,
@@ -872,6 +913,7 @@ pub async fn process_backlog(
872913 backlog_range,
873914 contract,
874915 gas_limit,
916+ backoff_gas_multiplier_pct,
875917 chain_state,
876918 metrics,
877919 fulfilled_requests_cache,
0 commit comments