99 traced_client:: { RpcMetrics , TracedClient } ,
1010 } ,
1111 config:: EthereumConfig ,
12+ config:: EscalationPolicyConfig ,
1213 } ,
1314 anyhow:: { anyhow, Result } ,
1415 backoff:: ExponentialBackoff ,
@@ -248,10 +249,6 @@ pub async fn run_keeper_threads(
248249 let latest_safe_block = get_latest_safe_block ( & chain_state) . in_current_span ( ) . await ;
249250 tracing:: info!( "latest safe block: {}" , & latest_safe_block) ;
250251
251- // Update BlockchainState with the gas multiplier cap from config
252- let mut chain_state = chain_state;
253- chain_state. backoff_gas_multiplier_cap_pct = chain_eth_config. backoff_gas_multiplier_cap_pct ;
254-
255252 let contract = Arc :: new (
256253 InstrumentedSignablePythContract :: from_config (
257254 & chain_eth_config,
@@ -276,7 +273,7 @@ pub async fn run_keeper_threads(
276273 } ,
277274 contract. clone ( ) ,
278275 gas_limit,
279- chain_eth_config. backoff_gas_multiplier_pct ,
276+ chain_eth_config. escalation_policy ,
280277 chain_state. clone ( ) ,
281278 metrics. clone ( ) ,
282279 fulfilled_requests_cache. clone ( ) ,
@@ -302,7 +299,7 @@ pub async fn run_keeper_threads(
302299 rx,
303300 Arc :: clone ( & contract) ,
304301 gas_limit,
305- chain_eth_config. backoff_gas_multiplier_pct ,
302+ chain_eth_config. escalation_policy ,
306303 metrics. clone ( ) ,
307304 fulfilled_requests_cache. clone ( ) ,
308305 )
@@ -327,9 +324,14 @@ pub async fn run_keeper_threads(
327324 chain_state. provider_address ,
328325 ADJUST_FEE_INTERVAL ,
329326 chain_eth_config. legacy_tx ,
330- // NOTE: we adjust fees based on the maximum gas that the keeper will submit a callback with.
331- // This number is *larger* than the configured gas limit, as we pad gas on transaction submission for reliability.
332- ( chain_eth_config. gas_limit * DEFAULT_GAS_ESTIMATE_MULTIPLIER_PCT ) / 100 ,
327+ // NOTE: we are adjusting the fees based on the maximum configured gas for user transactions.
328+ // However, the keeper will pad the gas limit for transactions (per the escalation policy) to ensure reliable submission.
329+ // Consequently, fees can be adjusted such that transactions are still unprofitable.
330+ // While we could scale up this value based on the padding, that ends up overcharging users as most transactions cost nowhere
331+ // near the maximum gas limit.
332+ // In the unlikely event that the keeper fees aren't sufficient, the solution to this is to configure the target
333+ // fee percentage to be higher on that specific chain.
334+ chain_eth_config. gas_limit ,
333335 chain_eth_config. min_profit_pct ,
334336 chain_eth_config. target_profit_pct ,
335337 chain_eth_config. max_profit_pct ,
@@ -398,7 +400,7 @@ pub async fn process_event_with_backoff(
398400 chain_state : BlockchainState ,
399401 contract : Arc < InstrumentedSignablePythContract > ,
400402 gas_limit : U256 ,
401- backoff_gas_multiplier_pct : u64 ,
403+ escalation_policy : EscalationPolicyConfig ,
402404 metrics : Arc < KeeperMetrics > ,
403405) {
404406 let start_time = std:: time:: Instant :: now ( ) ;
@@ -414,36 +416,35 @@ pub async fn process_event_with_backoff(
414416 ..Default :: default ( )
415417 } ;
416418
417- let current_multiplier = Arc :: new ( AtomicU64 :: new ( DEFAULT_GAS_ESTIMATE_MULTIPLIER_PCT ) ) ;
419+ let num_retries = Arc :: new ( AtomicU64 :: new ( 0 ) ) ;
418420
419421 let success = backoff:: future:: retry_notify (
420422 backoff,
421423 || async {
422- let multiplier = current_multiplier. load ( std:: sync:: atomic:: Ordering :: Relaxed ) ;
424+ let num_retries = num_retries. load ( std:: sync:: atomic:: Ordering :: Relaxed ) ;
425+
426+ let gas_multiplier_pct = escalation_policy. get_gas_multiplier_pct ( num_retries) ;
427+ let fee_multiplier_pct = escalation_policy. get_fee_multiplier_pct ( num_retries) ;
423428 process_event (
424429 & event,
425430 & chain_state,
426431 & contract,
427432 gas_limit,
428- multiplier,
433+ gas_multiplier_pct,
434+ fee_multiplier_pct,
429435 metrics. clone ( ) ,
430436 )
431437 . await
432438 } ,
433439 |e, dur| {
434- let multiplier = current_multiplier . load ( std:: sync:: atomic:: Ordering :: Relaxed ) ;
440+ let retry_number = num_retries . load ( std:: sync:: atomic:: Ordering :: Relaxed ) ;
435441 tracing:: error!(
436- "Error at duration {:?} with gas multiplier {}: {}" ,
442+ "Error on retry {} at duration {:?}: {}" ,
443+ retry_number,
437444 dur,
438- multiplier,
439445 e
440446 ) ;
441- // Calculate new multiplier with backoff, capped by backoff_gas_multiplier_cap_pct
442- let new_multiplier = multiplier. saturating_mul ( backoff_gas_multiplier_pct) / 100 ;
443- current_multiplier. store (
444- std:: cmp:: min ( new_multiplier, chain_state. backoff_gas_multiplier_cap_pct ) ,
445- std:: sync:: atomic:: Ordering :: Relaxed ,
446- ) ;
447+ num_retries. store ( retry_number + 1 , std:: sync:: atomic:: Ordering :: Relaxed ) ;
447448 } ,
448449 )
449450 . await ;
@@ -501,8 +502,9 @@ pub async fn process_event(
501502 chain_config : & BlockchainState ,
502503 contract : & InstrumentedSignablePythContract ,
503504 gas_limit : U256 ,
504- // A value of 100 submits the tx with the same gas as the estimate.
505+ // A value of 100 submits the tx with the same gas/fee as the estimate.
505506 gas_estimate_multiplier_pct : u64 ,
507+ fee_estimate_multiplier_pct : u64 ,
506508 metrics : Arc < KeeperMetrics > ,
507509) -> Result < ( ) , backoff:: Error < anyhow:: Error > > {
508510 // ignore requests that are not for the configured provider
@@ -546,9 +548,7 @@ pub async fn process_event(
546548 // Pad the gas estimate after checking it against the simulation gas limit, ensuring that
547549 // the padded gas estimate doesn't exceed the maximum amount of gas we are willing to use.
548550 let gas_estimate = gas_estimate. saturating_mul ( gas_estimate_multiplier_pct. into ( ) ) / 100 ;
549- // Apply the configurable cap from backoff_gas_multiplier_cap_pct
550- let max_gas_estimate = gas_limit. saturating_mul ( chain_config. backoff_gas_multiplier_cap_pct . into ( ) ) / 100 ;
551- let gas_estimate = gas_estimate. min ( max_gas_estimate) ;
551+
552552
553553 let contract_call = contract
554554 . reveal_with_callback (
@@ -561,13 +561,19 @@ pub async fn process_event(
561561
562562 let client = contract. client ( ) ;
563563 let mut transaction = contract_call. tx . clone ( ) ;
564+
564565 // manually fill the tx with the gas info, so we can log the details in case of error
565566 client
566567 . fill_transaction ( & mut transaction, None )
567568 . await
568569 . map_err ( |e| {
569570 backoff:: Error :: transient ( anyhow ! ( "Error filling the reveal transaction: {:?}" , e) )
570571 } ) ?;
572+
573+ // Apply the fee escalation policy. Note: the unwrap_or_default should never default as we have a gas oracle
574+ // in the client that sets the gas price.
575+ transaction. set_gas_price ( transaction. gas_price ( ) . unwrap_or_default ( ) . saturating_mul ( fee_estimate_multiplier_pct. into ( ) ) / 100 ) ;
576+
571577 let pending_tx = client
572578 . send_transaction ( transaction. clone ( ) , None )
573579 . await
@@ -883,7 +889,7 @@ pub async fn process_new_blocks(
883889 mut rx : mpsc:: Receiver < BlockRange > ,
884890 contract : Arc < InstrumentedSignablePythContract > ,
885891 gas_limit : U256 ,
886- backoff_gas_multiplier_pct : u64 ,
892+ escalation_policy : EscalationPolicyConfig ,
887893 metrics : Arc < KeeperMetrics > ,
888894 fulfilled_requests_cache : Arc < RwLock < HashSet < u64 > > > ,
889895) {
@@ -894,7 +900,7 @@ pub async fn process_new_blocks(
894900 block_range,
895901 Arc :: clone ( & contract) ,
896902 gas_limit,
897- backoff_gas_multiplier_pct ,
903+ escalation_policy ,
898904 chain_state. clone ( ) ,
899905 metrics. clone ( ) ,
900906 fulfilled_requests_cache. clone ( ) ,
@@ -911,7 +917,7 @@ pub async fn process_backlog(
911917 backlog_range : BlockRange ,
912918 contract : Arc < InstrumentedSignablePythContract > ,
913919 gas_limit : U256 ,
914- backoff_gas_multiplier_pct : u64 ,
920+ escalation_policy : EscalationPolicyConfig ,
915921 chain_state : BlockchainState ,
916922 metrics : Arc < KeeperMetrics > ,
917923 fulfilled_requests_cache : Arc < RwLock < HashSet < u64 > > > ,
@@ -921,7 +927,7 @@ pub async fn process_backlog(
921927 backlog_range,
922928 contract,
923929 gas_limit,
924- backoff_gas_multiplier_pct ,
930+ escalation_policy ,
925931 chain_state,
926932 metrics,
927933 fulfilled_requests_cache,
0 commit comments