diff --git a/apps/fortuna/Cargo.lock b/apps/fortuna/Cargo.lock index 941437cfdf..dcb4180ec8 100644 --- a/apps/fortuna/Cargo.lock +++ b/apps/fortuna/Cargo.lock @@ -1503,7 +1503,7 @@ dependencies = [ [[package]] name = "fortuna" -version = "6.5.5" +version = "6.6.0" dependencies = [ "anyhow", "axum", diff --git a/apps/fortuna/Cargo.toml b/apps/fortuna/Cargo.toml index ab898b4864..4a99a676c0 100644 --- a/apps/fortuna/Cargo.toml +++ b/apps/fortuna/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fortuna" -version = "6.5.5" +version = "6.6.0" edition = "2021" [dependencies] diff --git a/apps/fortuna/config.sample.yaml b/apps/fortuna/config.sample.yaml index 74231be6a9..73ffe3da5a 100644 --- a/apps/fortuna/config.sample.yaml +++ b/apps/fortuna/config.sample.yaml @@ -12,6 +12,10 @@ chains: # 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. diff --git a/apps/fortuna/src/chain/eth_gas_oracle.rs b/apps/fortuna/src/chain/eth_gas_oracle.rs index 869c00eab7..4035f99b0b 100644 --- a/apps/fortuna/src/chain/eth_gas_oracle.rs +++ b/apps/fortuna/src/chain/eth_gas_oracle.rs @@ -39,11 +39,15 @@ pub const EIP1559_FEE_ESTIMATION_THRESHOLD_MAX_CHANGE: i64 = 200; #[must_use] pub struct EthProviderOracle { provider: M, + priority_fee_multiplier_pct: u64, } impl EthProviderOracle { - pub fn new(provider: M) -> Self { - Self { provider } + pub fn new(provider: M, priority_fee_multiplier_pct: u64) -> Self { + Self { + provider, + priority_fee_multiplier_pct, + } } } @@ -61,10 +65,19 @@ where } async fn estimate_eip1559_fees(&self) -> Result<(U256, U256)> { - self.provider + let (max_fee_per_gas, max_priority_fee_per_gas) = self + .provider .estimate_eip1559_fees(Some(eip1559_default_estimator)) .await - .map_err(|err| GasOracleError::ProviderError(Box::new(err))) + .map_err(|err| GasOracleError::ProviderError(Box::new(err)))?; + + // Apply the multiplier to max_priority_fee_per_gas + let max_priority_fee_per_gas = max_priority_fee_per_gas + .checked_mul(U256::from(self.priority_fee_multiplier_pct)) + .and_then(|x| x.checked_div(U256::from(100))) + .unwrap_or(max_priority_fee_per_gas); + + Ok((max_fee_per_gas, max_priority_fee_per_gas)) } } @@ -79,12 +92,14 @@ pub fn eip1559_default_estimator(base_fee_per_gas: U256, rewards: Vec> U256::from(EIP1559_FEE_ESTIMATION_DEFAULT_PRIORITY_FEE), ) }; + let potential_max_fee = base_fee_surged(base_fee_per_gas); let max_fee_per_gas = if max_priority_fee_per_gas > potential_max_fee { max_priority_fee_per_gas + potential_max_fee } else { potential_max_fee }; + (max_fee_per_gas, max_priority_fee_per_gas) } diff --git a/apps/fortuna/src/chain/ethereum.rs b/apps/fortuna/src/chain/ethereum.rs index f4f97a07df..be626523f0 100644 --- a/apps/fortuna/src/chain/ethereum.rs +++ b/apps/fortuna/src/chain/ethereum.rs @@ -211,7 +211,8 @@ impl SignablePythContractInner { provider: Provider, ) -> Result> { let chain_id = provider.get_chainid().await?; - let gas_oracle = EthProviderOracle::new(provider.clone()); + let gas_oracle = + EthProviderOracle::new(provider.clone(), chain_config.priority_fee_multiplier_pct); let wallet__ = private_key .parse::()? .with_chain_id(chain_id.as_u64()); diff --git a/apps/fortuna/src/config.rs b/apps/fortuna/src/config.rs index 87707aee20..45bf8569a0 100644 --- a/apps/fortuna/src/config.rs +++ b/apps/fortuna/src/config.rs @@ -166,6 +166,10 @@ 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, + + /// 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, } /// A commitment that the provider used to generate random numbers at some point in the past. @@ -215,6 +219,10 @@ 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 { diff --git a/apps/fortuna/src/keeper.rs b/apps/fortuna/src/keeper.rs index b091d94fa8..172012b2c6 100644 --- a/apps/fortuna/src/keeper.rs +++ b/apps/fortuna/src/keeper.rs @@ -2,7 +2,6 @@ use { crate::{ api::{self, BlockchainState, ChainId}, chain::{ - eth_gas_oracle::eip1559_default_estimator, ethereum::{ InstrumentedPythContract, InstrumentedSignablePythContract, PythContractCall, }, @@ -1208,9 +1207,11 @@ pub async fn estimate_tx_cost( .try_into() .map_err(|e| anyhow!("gas price doesn't fit into 128 bits. error: {:?}", e))? } else { - let (max_fee_per_gas, max_priority_fee_per_gas) = middleware - .estimate_eip1559_fees(Some(eip1559_default_estimator)) - .await?; + // This is not obvious but the implementation of estimate_eip1559_fees in ethers.rs + // for a middleware that has a GasOracleMiddleware inside is to ignore the passed-in callback + // and use whatever the gas oracle returns. + let (max_fee_per_gas, max_priority_fee_per_gas) = + middleware.estimate_eip1559_fees(None).await?; (max_fee_per_gas + max_priority_fee_per_gas) .try_into()