1use crate::{
4 fillers::{BlobGasFiller, ChainIdFiller, GasFiller, JoinFill, NonceFiller},
5 Identity,
6};
7use alloy_primitives::{U128, U64};
8use std::{fmt, fmt::Formatter};
9
10pub use alloy_eips::eip1559::Eip1559Estimation;
11
12pub const EIP1559_FEE_ESTIMATION_PAST_BLOCKS: u64 = 10;
14pub const EIP1559_BASE_FEE_MULTIPLIER: u128 = 2;
16pub const EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE: f64 = 20.0;
18pub const EIP1559_MIN_PRIORITY_FEE: u128 = 1;
20
21pub type EstimatorFunction = fn(u128, &[Vec<u128>]) -> Eip1559Estimation;
23
24pub trait Eip1559EstimatorFn: Send + Unpin {
26 fn estimate(&self, base_fee: u128, rewards: &[Vec<u128>]) -> Eip1559Estimation;
28}
29
30#[derive(Default)]
32pub enum Eip1559Estimator {
33 #[default]
35 Default,
36 Custom(Box<dyn Eip1559EstimatorFn>),
38}
39
40impl Eip1559Estimator {
41 pub fn new<F>(f: F) -> Self
43 where
44 F: Fn(u128, &[Vec<u128>]) -> Eip1559Estimation + Send + Unpin + 'static,
45 {
46 Self::new_estimator(f)
47 }
48
49 pub fn new_estimator<F: Eip1559EstimatorFn + 'static>(f: F) -> Self {
51 Self::Custom(Box::new(f))
52 }
53
54 pub fn estimate(self, base_fee: u128, rewards: &[Vec<u128>]) -> Eip1559Estimation {
56 match self {
57 Self::Default => eip1559_default_estimator(base_fee, rewards),
58 Self::Custom(val) => val.estimate(base_fee, rewards),
59 }
60 }
61}
62
63impl<F> Eip1559EstimatorFn for F
64where
65 F: Fn(u128, &[Vec<u128>]) -> Eip1559Estimation + Send + Unpin,
66{
67 fn estimate(&self, base_fee: u128, rewards: &[Vec<u128>]) -> Eip1559Estimation {
68 (self)(base_fee, rewards)
69 }
70}
71
72impl fmt::Debug for Eip1559Estimator {
73 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
74 f.debug_struct("Eip1559Estimator")
75 .field(
76 "estimator",
77 &match self {
78 Self::Default => "default",
79 Self::Custom(_) => "custom",
80 },
81 )
82 .finish()
83 }
84}
85
86fn estimate_priority_fee(rewards: &[Vec<u128>]) -> u128 {
87 let mut rewards =
88 rewards.iter().filter_map(|r| r.first()).filter(|r| **r > 0_u128).collect::<Vec<_>>();
89 if rewards.is_empty() {
90 return EIP1559_MIN_PRIORITY_FEE;
91 }
92
93 rewards.sort_unstable();
94
95 let n = rewards.len();
96
97 let median =
98 if n % 2 == 0 { (*rewards[n / 2 - 1] + *rewards[n / 2]) / 2 } else { *rewards[n / 2] };
99
100 std::cmp::max(median, EIP1559_MIN_PRIORITY_FEE)
101}
102
103pub fn eip1559_default_estimator(
108 base_fee_per_gas: u128,
109 rewards: &[Vec<u128>],
110) -> Eip1559Estimation {
111 let max_priority_fee_per_gas = estimate_priority_fee(rewards);
112 let potential_max_fee = base_fee_per_gas * EIP1559_BASE_FEE_MULTIPLIER;
113
114 Eip1559Estimation {
115 max_fee_per_gas: potential_max_fee + max_priority_fee_per_gas,
116 max_priority_fee_per_gas,
117 }
118}
119
120pub(crate) fn convert_u128(r: U128) -> u128 {
122 r.to::<u128>()
123}
124
125pub(crate) fn convert_u64(r: U64) -> u64 {
126 r.to::<u64>()
127}
128
129pub(crate) fn convert_to_hashes<BlockResp: alloy_network::BlockResponse>(
130 r: Option<BlockResp>,
131) -> Option<BlockResp> {
132 r.map(|mut block| {
133 if block.transactions().is_empty() {
134 block.transactions_mut().convert_to_hashes();
135 }
136
137 block
138 })
139}
140
141pub type JoinedRecommendedFillers = JoinFill<
144 Identity,
145 JoinFill<GasFiller, JoinFill<BlobGasFiller, JoinFill<NonceFiller, ChainIdFiller>>>,
146>;
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use std::vec;
152
153 #[test]
154 fn test_estimate_priority_fee() {
155 let rewards =
156 vec![vec![10_000_000_000_u128], vec![200_000_000_000_u128], vec![3_000_000_000_u128]];
157 assert_eq!(super::estimate_priority_fee(&rewards), 10_000_000_000_u128);
158
159 let rewards = vec![
160 vec![400_000_000_000_u128],
161 vec![2_000_000_000_u128],
162 vec![5_000_000_000_u128],
163 vec![3_000_000_000_u128],
164 ];
165
166 assert_eq!(super::estimate_priority_fee(&rewards), 4_000_000_000_u128);
167
168 let rewards = vec![vec![0_u128], vec![0_u128], vec![0_u128]];
169
170 assert_eq!(super::estimate_priority_fee(&rewards), EIP1559_MIN_PRIORITY_FEE);
171
172 assert_eq!(super::estimate_priority_fee(&[]), EIP1559_MIN_PRIORITY_FEE);
173 }
174
175 #[test]
176 fn test_eip1559_default_estimator() {
177 let base_fee_per_gas = 1_000_000_000_u128;
178 let rewards = vec![
179 vec![200_000_000_000_u128],
180 vec![200_000_000_000_u128],
181 vec![300_000_000_000_u128],
182 ];
183 assert_eq!(
184 super::eip1559_default_estimator(base_fee_per_gas, &rewards),
185 Eip1559Estimation {
186 max_fee_per_gas: 202_000_000_000_u128,
187 max_priority_fee_per_gas: 200_000_000_000_u128
188 }
189 );
190
191 let base_fee_per_gas = 0u128;
192 let rewards = vec![
193 vec![200_000_000_000_u128],
194 vec![200_000_000_000_u128],
195 vec![300_000_000_000_u128],
196 ];
197
198 assert_eq!(
199 super::eip1559_default_estimator(base_fee_per_gas, &rewards),
200 Eip1559Estimation {
201 max_fee_per_gas: 200_000_000_000_u128,
202 max_priority_fee_per_gas: 200_000_000_000_u128
203 }
204 );
205 }
206}