From 8158efec4e7ec65760c79ba9f96fec29320a4d4e Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Mon, 13 Jan 2025 14:46:19 -0800 Subject: [PATCH 1/6] feat(fortuna): Add a couple more config values --- apps/fortuna/Cargo.lock | 2 +- apps/fortuna/Cargo.toml | 2 +- apps/fortuna/src/config.rs | 38 +++++++++++++++++++++++++++++--------- apps/fortuna/src/keeper.rs | 2 +- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/apps/fortuna/Cargo.lock b/apps/fortuna/Cargo.lock index 9da01ca49..da418bd62 100644 --- a/apps/fortuna/Cargo.lock +++ b/apps/fortuna/Cargo.lock @@ -1503,7 +1503,7 @@ dependencies = [ [[package]] name = "fortuna" -version = "7.1.0" +version = "7.3.0" dependencies = [ "anyhow", "axum", diff --git a/apps/fortuna/Cargo.toml b/apps/fortuna/Cargo.toml index eb7cf2ded..923ccafd0 100644 --- a/apps/fortuna/Cargo.toml +++ b/apps/fortuna/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fortuna" -version = "7.1.0" +version = "7.3.0" edition = "2021" [dependencies] diff --git a/apps/fortuna/src/config.rs b/apps/fortuna/src/config.rs index 0687d7846..9a3fb7950 100644 --- a/apps/fortuna/src/config.rs +++ b/apps/fortuna/src/config.rs @@ -143,17 +143,17 @@ pub struct EthereumConfig { pub escalation_policy: EscalationPolicyConfig, /// The minimum percentage profit to earn as a function of the callback cost. - /// For example, 20 means a profit of 20% over the cost of the callback. + /// For example, 20 means a profit of 20% over the cost of a callback that uses the full gas limit. /// The fee will be raised if the profit is less than this number. pub min_profit_pct: u64, /// The target percentage profit to earn as a function of the callback cost. - /// For example, 20 means a profit of 20% over the cost of the callback. + /// For example, 20 means a profit of 20% over the cost of a callback that uses the full gas limit. /// The fee will be set to this target whenever it falls outside the min/max bounds. pub target_profit_pct: u64, /// The maximum percentage profit to earn as a function of the callback cost. - /// For example, 100 means a profit of 100% over the cost of the callback. + /// For example, 100 means a profit of 100% over the cost of a callback that uses the full gas limit. /// The fee will be lowered if it is more profitable than specified here. /// Must be larger than min_profit_pct. pub max_profit_pct: u64, @@ -182,27 +182,41 @@ fn default_priority_fee_multiplier_pct() -> u64 { #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct EscalationPolicyConfig { - /// The initial gas multiplier to apply to the gas limit. + // The keeper will perform the callback as long as the tx is within this percentage of the configured gas limit. + // Default value is 110, meaning a 10% tolerance over the configured value. + #[serde(default = "default_gas_limit_tolerance_pct")] + pub gas_limit_tolerance_pct: u64, + + /// The initial gas multiplier to apply to the tx gas estimate #[serde(default = "default_initial_gas_multiplier_pct")] pub initial_gas_multiplier_pct: u64, - /// The gas multiplier to apply to the gas limit during backoff retries. + /// The gas multiplier to apply to the tx gas estimate during backoff retries. /// The gas on each successive retry is multiplied by this value, with the maximum multiplier capped at `gas_multiplier_cap_pct`. #[serde(default = "default_gas_multiplier_pct")] pub gas_multiplier_pct: u64, - /// The maximum gas multiplier to apply to the gas limit during backoff retries. + /// The maximum gas multiplier to apply to the tx gas estimate during backoff retries. #[serde(default = "default_gas_multiplier_cap_pct")] pub gas_multiplier_cap_pct: u64, + /// The initial fee multiplier to apply to the fee estimate + #[serde(default = "default_initial_fee_multiplier_pct")] + pub initial_fee_multiplier_pct: u64, + /// The fee multiplier to apply to the fee during backoff retries. - /// The initial fee is 100% of the estimate (which itself may be padded based on our chain configuration) - /// The fee on each successive retry is multiplied by this value, with the maximum multiplier capped at `fee_multiplier_cap_pct`. + /// The fee estimate for the chain (which itself may be padded based on our chain configuration) is multiplied by the fee multiplier. + /// The initial multiplier is initial_fee_multiplier_pct, which is multiplied by fee_multiplier_pct on each successive retry. + /// The maximum multiplier capped at `fee_multiplier_cap_pct`. #[serde(default = "default_fee_multiplier_pct")] pub fee_multiplier_pct: u64, #[serde(default = "default_fee_multiplier_cap_pct")] pub fee_multiplier_cap_pct: u64, } +fn default_gas_limit_tolerance_pct() -> u64 { + 110 +} + fn default_initial_gas_multiplier_pct() -> u64 { 125 } @@ -215,6 +229,10 @@ fn default_gas_multiplier_cap_pct() -> u64 { 600 } +fn default_initial_fee_multiplier_pct() -> u64 { + 100 +} + fn default_fee_multiplier_pct() -> u64 { 110 } @@ -226,9 +244,11 @@ fn default_fee_multiplier_cap_pct() -> u64 { impl Default for EscalationPolicyConfig { fn default() -> Self { Self { + gas_limit_tolerance_pct: default_gas_limit_tolerance_pct(), initial_gas_multiplier_pct: default_initial_gas_multiplier_pct(), gas_multiplier_pct: default_gas_multiplier_pct(), gas_multiplier_cap_pct: default_gas_multiplier_cap_pct(), + initial_fee_multiplier_pct: default_initial_fee_multiplier_pct(), fee_multiplier_pct: default_fee_multiplier_pct(), fee_multiplier_cap_pct: default_fee_multiplier_cap_pct(), } @@ -248,7 +268,7 @@ impl EscalationPolicyConfig { pub fn get_fee_multiplier_pct(&self, num_retries: u64) -> u64 { self.apply_escalation_policy( num_retries, - 100, + self.initial_fee_multiplier_pct, self.fee_multiplier_pct, self.fee_multiplier_cap_pct, ) diff --git a/apps/fortuna/src/keeper.rs b/apps/fortuna/src/keeper.rs index 49a518a8d..71c602fe6 100644 --- a/apps/fortuna/src/keeper.rs +++ b/apps/fortuna/src/keeper.rs @@ -435,7 +435,7 @@ pub async fn process_event_with_backoff( &event, &chain_state, &contract, - gas_limit, + gas_limit.saturating_mul(escalation_policy.gas_limit_tolerance_pct.into()) / 100, gas_multiplier_pct, fee_multiplier_pct, metrics.clone(), From 79c9f6c3f3a6a4a7bf322c0aeee4840832c9cf99 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Mon, 13 Jan 2025 14:50:34 -0800 Subject: [PATCH 2/6] doc --- apps/fortuna/src/config.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/fortuna/src/config.rs b/apps/fortuna/src/config.rs index 9a3fb7950..114b37b12 100644 --- a/apps/fortuna/src/config.rs +++ b/apps/fortuna/src/config.rs @@ -199,7 +199,10 @@ pub struct EscalationPolicyConfig { #[serde(default = "default_gas_multiplier_cap_pct")] pub gas_multiplier_cap_pct: u64, - /// The initial fee multiplier to apply to the fee estimate + /// The initial fee multiplier to apply to the fee estimate. + /// Note: consider carefully whether you want to adjust this value or priority_fee_multiplier_pct in the chain configuration. + /// The difference between these is that priority_fee_multiplier_pct additionally influences the fees charged by the keeper. + /// Setting this value to >100 means that the keeper may lose money on callbacks -- only do this if you know what you are doing. #[serde(default = "default_initial_fee_multiplier_pct")] pub initial_fee_multiplier_pct: u64, From 31c4839d0be7fc55393b1200314b5e99b864c36f Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Mon, 13 Jan 2025 14:56:02 -0800 Subject: [PATCH 3/6] gr --- apps/fortuna/src/config.rs | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/apps/fortuna/src/config.rs b/apps/fortuna/src/config.rs index 114b37b12..6d7a7e3ec 100644 --- a/apps/fortuna/src/config.rs +++ b/apps/fortuna/src/config.rs @@ -199,17 +199,9 @@ pub struct EscalationPolicyConfig { #[serde(default = "default_gas_multiplier_cap_pct")] pub gas_multiplier_cap_pct: u64, - /// The initial fee multiplier to apply to the fee estimate. - /// Note: consider carefully whether you want to adjust this value or priority_fee_multiplier_pct in the chain configuration. - /// The difference between these is that priority_fee_multiplier_pct additionally influences the fees charged by the keeper. - /// Setting this value to >100 means that the keeper may lose money on callbacks -- only do this if you know what you are doing. - #[serde(default = "default_initial_fee_multiplier_pct")] - pub initial_fee_multiplier_pct: u64, - /// The fee multiplier to apply to the fee during backoff retries. - /// The fee estimate for the chain (which itself may be padded based on our chain configuration) is multiplied by the fee multiplier. - /// The initial multiplier is initial_fee_multiplier_pct, which is multiplied by fee_multiplier_pct on each successive retry. - /// The maximum multiplier capped at `fee_multiplier_cap_pct`. + /// The initial fee is 100% of the estimate (which itself may be padded based on our chain configuration) + /// The fee on each successive retry is multiplied by this value, with the maximum multiplier capped at `fee_multiplier_cap_pct`. #[serde(default = "default_fee_multiplier_pct")] pub fee_multiplier_pct: u64, #[serde(default = "default_fee_multiplier_cap_pct")] @@ -232,10 +224,6 @@ fn default_gas_multiplier_cap_pct() -> u64 { 600 } -fn default_initial_fee_multiplier_pct() -> u64 { - 100 -} - fn default_fee_multiplier_pct() -> u64 { 110 } @@ -251,7 +239,6 @@ impl Default for EscalationPolicyConfig { initial_gas_multiplier_pct: default_initial_gas_multiplier_pct(), gas_multiplier_pct: default_gas_multiplier_pct(), gas_multiplier_cap_pct: default_gas_multiplier_cap_pct(), - initial_fee_multiplier_pct: default_initial_fee_multiplier_pct(), fee_multiplier_pct: default_fee_multiplier_pct(), fee_multiplier_cap_pct: default_fee_multiplier_cap_pct(), } @@ -271,7 +258,7 @@ impl EscalationPolicyConfig { pub fn get_fee_multiplier_pct(&self, num_retries: u64) -> u64 { self.apply_escalation_policy( num_retries, - self.initial_fee_multiplier_pct, + 100, self.fee_multiplier_pct, self.fee_multiplier_cap_pct, ) From e7898e3cf37fce25c11ae0067252eb02dddfba78 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Mon, 13 Jan 2025 15:03:48 -0800 Subject: [PATCH 4/6] allow negative profits --- apps/fortuna/src/config.rs | 9 ++++++--- apps/fortuna/src/keeper.rs | 13 +++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/apps/fortuna/src/config.rs b/apps/fortuna/src/config.rs index 6d7a7e3ec..70698f3fa 100644 --- a/apps/fortuna/src/config.rs +++ b/apps/fortuna/src/config.rs @@ -145,18 +145,21 @@ pub struct EthereumConfig { /// The minimum percentage profit to earn as a function of the callback cost. /// For example, 20 means a profit of 20% over the cost of a callback that uses the full gas limit. /// The fee will be raised if the profit is less than this number. - pub min_profit_pct: u64, + /// The minimum value for this is -100. If set to < 0, it means the keeper may lose money on callbacks that use the full gas limit. + pub min_profit_pct: i64, /// The target percentage profit to earn as a function of the callback cost. /// For example, 20 means a profit of 20% over the cost of a callback that uses the full gas limit. /// The fee will be set to this target whenever it falls outside the min/max bounds. - pub target_profit_pct: u64, + /// The minimum value for this is -100. If set to < 0, it means the keeper may lose money on callbacks that use the full gas limit. + pub target_profit_pct: i64, /// The maximum percentage profit to earn as a function of the callback cost. /// For example, 100 means a profit of 100% over the cost of a callback that uses the full gas limit. /// The fee will be lowered if it is more profitable than specified here. /// Must be larger than min_profit_pct. - pub max_profit_pct: u64, + /// The minimum value for this is -100. If set to < 0, it means the keeper may lose money on callbacks that use the full gas limit. + pub max_profit_pct: i64, /// Minimum wallet balance for the keeper. If the balance falls below this level, the keeper will /// withdraw fees from the contract to top up. This functionality requires the keeper to be the fee diff --git a/apps/fortuna/src/keeper.rs b/apps/fortuna/src/keeper.rs index 71c602fe6..132ef8aee 100644 --- a/apps/fortuna/src/keeper.rs +++ b/apps/fortuna/src/keeper.rs @@ -338,9 +338,10 @@ pub async fn run_keeper_threads( // In the unlikely event that the keeper fees aren't sufficient, the solution to this is to configure the target // fee percentage to be higher on that specific chain. chain_eth_config.gas_limit, - chain_eth_config.min_profit_pct, - chain_eth_config.target_profit_pct, - chain_eth_config.max_profit_pct, + // NOTE: unwrap() here so we panic early if someone configures these values below -100. + u64::try_from(100 + chain_eth_config.min_profit_pct).unwrap(), + u64::try_from(100 + chain_eth_config.target_profit_pct).unwrap(), + u64::try_from(100 + chain_eth_config.max_profit_pct).unwrap(), chain_eth_config.fee, ) .in_current_span(), @@ -1253,15 +1254,15 @@ pub async fn adjust_fee_if_necessary( .await .map_err(|e| anyhow!("Could not estimate transaction cost. error {:?}", e))?; let target_fee_min = std::cmp::max( - (max_callback_cost * (100 + u128::from(min_profit_pct))) / 100, + (max_callback_cost * u128::from(min_profit_pct)) / 100, min_fee_wei, ); let target_fee = std::cmp::max( - (max_callback_cost * (100 + u128::from(target_profit_pct))) / 100, + (max_callback_cost * u128::from(target_profit_pct)) / 100, min_fee_wei, ); let target_fee_max = std::cmp::max( - (max_callback_cost * (100 + u128::from(max_profit_pct))) / 100, + (max_callback_cost * u128::from(max_profit_pct)) / 100, min_fee_wei, ); From e9882d903f76adc3ff58f315e5a72888c2df2dad Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Mon, 13 Jan 2025 15:08:23 -0800 Subject: [PATCH 5/6] right version --- apps/fortuna/Cargo.lock | 2 +- apps/fortuna/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/fortuna/Cargo.lock b/apps/fortuna/Cargo.lock index da418bd62..78a359166 100644 --- a/apps/fortuna/Cargo.lock +++ b/apps/fortuna/Cargo.lock @@ -1503,7 +1503,7 @@ dependencies = [ [[package]] name = "fortuna" -version = "7.3.0" +version = "7.2.0" dependencies = [ "anyhow", "axum", diff --git a/apps/fortuna/Cargo.toml b/apps/fortuna/Cargo.toml index 923ccafd0..959fa1c03 100644 --- a/apps/fortuna/Cargo.toml +++ b/apps/fortuna/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fortuna" -version = "7.3.0" +version = "7.2.0" edition = "2021" [dependencies] From 82509955cef42d7083209b429da79b27515fc0a9 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Mon, 13 Jan 2025 19:40:03 -0800 Subject: [PATCH 6/6] fix error --- apps/fortuna/src/keeper.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/fortuna/src/keeper.rs b/apps/fortuna/src/keeper.rs index 132ef8aee..a836adea4 100644 --- a/apps/fortuna/src/keeper.rs +++ b/apps/fortuna/src/keeper.rs @@ -339,9 +339,12 @@ pub async fn run_keeper_threads( // fee percentage to be higher on that specific chain. chain_eth_config.gas_limit, // NOTE: unwrap() here so we panic early if someone configures these values below -100. - u64::try_from(100 + chain_eth_config.min_profit_pct).unwrap(), - u64::try_from(100 + chain_eth_config.target_profit_pct).unwrap(), - u64::try_from(100 + chain_eth_config.max_profit_pct).unwrap(), + u64::try_from(100 + chain_eth_config.min_profit_pct) + .expect("min_profit_pct must be >= -100"), + u64::try_from(100 + chain_eth_config.target_profit_pct) + .expect("target_profit_pct must be >= -100"), + u64::try_from(100 + chain_eth_config.max_profit_pct) + .expect("max_profit_pct must be >= -100"), chain_eth_config.fee, ) .in_current_span(),