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}