Skip to content

Commit

Permalink
feat(fortuna): configurable escalation policy for transactions. (#2244)
Browse files Browse the repository at this point in the history
* feat: add backoff_gas_multiplier_cap_pct to fortuna config

Co-Authored-By: Jayant Krishnamurthy <[email protected]>

* feat: replace hardcoded gas ceiling with configurable cap in process_event

Co-Authored-By: Jayant Krishnamurthy <[email protected]>

* feat: apply gas multiplier cap in process_event_with_backoff

Co-Authored-By: Jayant Krishnamurthy <[email protected]>

* fix: initialize backoff_gas_multiplier_cap_pct in BlockchainState

Co-Authored-By: Jayant Krishnamurthy <[email protected]>

* gr

* make this sane

* config defaults

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Jayant Krishnamurthy <[email protected]>
Co-authored-by: Jayant Krishnamurthy <[email protected]>
  • Loading branch information
3 people authored Jan 13, 2025
1 parent 4a409be commit 3ea2ac7
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 49 deletions.
2 changes: 1 addition & 1 deletion apps/fortuna/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/fortuna/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "fortuna"
version = "6.8.1"
version = "7.0.0"
edition = "2021"

[dependencies]
Expand Down
27 changes: 20 additions & 7 deletions apps/fortuna/config.sample.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,32 @@ chains:
# Keeper configuration for the chain
reveal_delay_blocks: 0
gas_limit: 500000
# Increase the transaction gas limit by 10% each time the callback fails
# defaults to 100 (i.e., don't change the gas limit) if not specified.
backoff_gas_multiplier_pct: 110

# Multiplier for the priority fee estimate, as a percentage (i.e., 100 = no change).
# Defaults to 100 if the field is omitted.
priority_fee_multiplier_pct: 100

escalation_policy:
# Pad the first callback transaction's gas estimate by 25%,
# then multiply each successive callback transaction's gas estimate by 10% until the cap is reached.
# All numbers are expressed as percentages where 100 = no change.
initial_gas_multiplier_pct: 125
gas_multiplier_pct: 110
gas_multiplier_cap_pct: 600

# Multiply successive callback transaction's fees by 10% until the cap is reached.
# All numbers are expressed as percentages where 100 = no change.
# (See also priority_fee_multiplier_pct above to generically adjust the priority fee estimates for the chain --
# adjusting that parameter will influence the fee of the first transaction, in addition to other things)
fee_multiplier_pct: 110
fee_multiplier_cap_pct: 200

min_keeper_balance: 100000000000000000

# Provider configuration
# How much to charge in fees
fee: 1500000000000000

# Multiplier for the priority fee estimate, as a percentage (i.e., 100 = no change).
# Defaults to 100 if the field is omitted.
priority_fee_multiplier_pct: 100

# Configuration for dynamic fees under high gas prices. The keeper will set
# on-chain fees to make between [min_profit_pct, max_profit_pct] of the max callback
# cost in profit per transaction.
Expand Down
112 changes: 100 additions & 12 deletions apps/fortuna/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,13 @@ pub struct EthereumConfig {
/// The gas limit to use for entropy callback transactions.
pub gas_limit: u64,

/// The percentage multiplier to apply to the gas limit for each backoff.
#[serde(default = "default_backoff_gas_multiplier_pct")]
pub backoff_gas_multiplier_pct: u64,
/// The percentage multiplier to apply to priority fee estimates (100 = no change, e.g. 150 = 150% of base fee)
#[serde(default = "default_priority_fee_multiplier_pct")]
pub priority_fee_multiplier_pct: u64,

/// The escalation policy governs how the gas limit and fee are increased during backoff retries.
#[serde(default)]
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.
Expand Down Expand Up @@ -170,16 +174,104 @@ pub struct EthereumConfig {
/// Maximum number of hashes to record in a request.
/// This should be set according to the maximum gas limit the provider supports for callbacks.
pub max_num_hashes: Option<u32>,

/// The percentage multiplier to apply to the priority fee (100 = no change, e.g. 150 = 150% of base fee)
#[serde(default = "default_priority_fee_multiplier_pct")]
pub priority_fee_multiplier_pct: u64,
}

fn default_backoff_gas_multiplier_pct() -> u64 {
fn default_priority_fee_multiplier_pct() -> u64 {
100
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct EscalationPolicyConfig {
/// The initial gas multiplier to apply to the gas limit.
#[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 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.
#[serde(default = "default_gas_multiplier_cap_pct")]
pub gas_multiplier_cap_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`.
#[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_initial_gas_multiplier_pct() -> u64 {
125
}

fn default_gas_multiplier_pct() -> u64 {
110
}

fn default_gas_multiplier_cap_pct() -> u64 {
600
}

fn default_fee_multiplier_pct() -> u64 {
110
}

fn default_fee_multiplier_cap_pct() -> u64 {
200
}

impl Default for EscalationPolicyConfig {
fn default() -> Self {
Self {
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(),
fee_multiplier_pct: default_fee_multiplier_pct(),
fee_multiplier_cap_pct: default_fee_multiplier_cap_pct(),
}
}
}

impl EscalationPolicyConfig {
pub fn get_gas_multiplier_pct(&self, num_retries: u64) -> u64 {
self.apply_escalation_policy(
num_retries,
self.initial_gas_multiplier_pct,
self.gas_multiplier_pct,
self.gas_multiplier_cap_pct,
)
}

pub fn get_fee_multiplier_pct(&self, num_retries: u64) -> u64 {
self.apply_escalation_policy(
num_retries,
100,
self.fee_multiplier_pct,
self.fee_multiplier_cap_pct,
)
}

fn apply_escalation_policy(
&self,
num_retries: u64,
initial: u64,
multiplier: u64,
cap: u64,
) -> u64 {
let mut current = initial;
let mut i = 0;
while i < num_retries && current < cap {
current = current.saturating_mul(multiplier) / 100;
i += 1;
}

current.min(cap)
}
}

/// A commitment that the provider used to generate random numbers at some point in the past.
/// These historical commitments need to be stored in the configuration to support transition points where
/// the commitment changes. In theory, this information is stored on the blockchain, but unfortunately it
Expand Down Expand Up @@ -227,10 +319,6 @@ fn default_chain_sample_interval() -> u64 {
1
}

fn default_priority_fee_multiplier_pct() -> u64 {
100
}

/// Configuration values for the keeper service that are shared across chains.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct KeeperConfig {
Expand Down
Loading

0 comments on commit 3ea2ac7

Please sign in to comment.