Skip to main content

foundry_config/
fee.rs

1//! EIP-1559 fee estimation configuration.
2
3use clap::ValueEnum;
4use serde::{Deserialize, Serialize};
5use std::{fmt, str::FromStr};
6
7/// Controls how EIP-1559 fees are estimated, analogous to wallet UIs that offer
8/// "low" / "market" / "aggressive" gas options.
9///
10/// Used both as a CLI value (`--estimate <preset>`) and as a TOML
11/// configuration value (`eip1559_fee_estimate = "market"`).
12///
13/// The estimate controls two parameters:
14/// - the `eth_feeHistory` reward percentile used to derive the priority fee, and
15/// - the multiplier applied to the base fee when building `maxFeePerGas`.
16#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, ValueEnum)]
17#[serde(rename_all = "lowercase")]
18pub enum Eip1559FeeEstimatePreset {
19    /// Lower priority-fee percentile (10th) and a tighter base-fee buffer
20    /// (`base_fee * 1.5`). This only lowers the tip estimate and the max-fee cap,
21    /// not the base fee that is actually paid, and risks the transaction stalling
22    /// if the base fee rises.
23    Low,
24    /// Default: `base_fee * 2` plus a 20th-percentile priority fee.
25    #[default]
26    Market,
27    /// Higher priority-fee percentile (50th) to bid a larger tip for faster
28    /// inclusion.
29    Aggressive,
30}
31
32impl Eip1559FeeEstimatePreset {
33    /// The reward percentile sampled from `eth_feeHistory` to estimate the
34    /// priority fee.
35    pub const fn reward_percentile(&self) -> f64 {
36        match self {
37            Self::Low => 10.0,
38            Self::Market => 20.0,
39            Self::Aggressive => 50.0,
40        }
41    }
42
43    /// The base-fee multiplier, expressed as `(numerator, denominator)`, applied
44    /// when building `maxFeePerGas` so the transaction stays includable if the
45    /// base fee rises before it is mined.
46    pub const fn base_fee_multiplier(&self) -> (u128, u128) {
47        match self {
48            Self::Low => (3, 2),
49            Self::Market => (2, 1),
50            Self::Aggressive => (2, 1),
51        }
52    }
53}
54
55impl fmt::Display for Eip1559FeeEstimatePreset {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        let s = match self {
58            Self::Low => "low",
59            Self::Market => "market",
60            Self::Aggressive => "aggressive",
61        };
62        f.write_str(s)
63    }
64}
65
66impl FromStr for Eip1559FeeEstimatePreset {
67    type Err = String;
68
69    fn from_str(s: &str) -> Result<Self, Self::Err> {
70        match s.to_lowercase().as_str() {
71            "low" => Ok(Self::Low),
72            "market" => Ok(Self::Market),
73            "aggressive" => Ok(Self::Aggressive),
74            other => Err(format!(
75                "invalid EIP-1559 fee estimate preset: {other} (expected one of: low, market, aggressive)"
76            )),
77        }
78    }
79}