ethers_middleware/gas_escalator/
geometric.rs1use super::GasEscalator;
2use ethers_core::types::U256;
3
4#[derive(Clone, Debug)]
12pub struct GeometricGasPrice {
13 every_secs: u64,
14 coefficient: f64,
15 max_price: Option<U256>,
16}
17
18impl GeometricGasPrice {
19 pub fn new<T: Into<U256>, K: Into<u64>>(
24 coefficient: f64,
25 every_secs: K,
26 max_price: Option<T>,
27 ) -> Self {
28 GeometricGasPrice {
29 every_secs: every_secs.into(),
30 coefficient,
31 max_price: max_price.map(Into::into),
32 }
33 }
34}
35
36impl GasEscalator for GeometricGasPrice {
37 fn get_gas_price(&self, initial_price: U256, time_elapsed: u64) -> U256 {
38 let mut result = initial_price.as_u64() as f64;
39
40 if time_elapsed >= self.every_secs {
41 let iters = time_elapsed / self.every_secs;
42 for _ in 0..iters {
43 result *= self.coefficient;
44 }
45 }
46
47 let mut result = U256::from(result.ceil() as u64);
48 if let Some(max_price) = self.max_price {
49 result = std::cmp::min(result, max_price);
50 }
51 result
52 }
53}
54
55#[cfg(test)]
56mod tests {
58 use super::*;
59
60 #[test]
61 fn gas_price_increases_with_time() {
62 let oracle = GeometricGasPrice::new(1.125, 10u64, None::<u64>);
63 let initial_price = U256::from(100);
64
65 assert_eq!(oracle.get_gas_price(initial_price, 0), 100.into());
66 assert_eq!(oracle.get_gas_price(initial_price, 1), 100.into());
67 assert_eq!(oracle.get_gas_price(initial_price, 10), 113.into());
68 assert_eq!(oracle.get_gas_price(initial_price, 15), 113.into());
69 assert_eq!(oracle.get_gas_price(initial_price, 20), 127.into());
70 assert_eq!(oracle.get_gas_price(initial_price, 30), 143.into());
71 assert_eq!(oracle.get_gas_price(initial_price, 50), 181.into());
72 assert_eq!(oracle.get_gas_price(initial_price, 100), 325.into());
73 }
74
75 #[test]
76 fn gas_price_should_obey_max_value() {
77 let oracle = GeometricGasPrice::new(1.125, 60u64, Some(2500));
78 let initial_price = U256::from(1000);
79
80 assert_eq!(oracle.get_gas_price(initial_price, 0), 1000.into());
81 assert_eq!(oracle.get_gas_price(initial_price, 1), 1000.into());
82 assert_eq!(oracle.get_gas_price(initial_price, 59), 1000.into());
83 assert_eq!(oracle.get_gas_price(initial_price, 60), 1125.into());
84 assert_eq!(oracle.get_gas_price(initial_price, 119), 1125.into());
85 assert_eq!(oracle.get_gas_price(initial_price, 120), 1266.into());
86 assert_eq!(oracle.get_gas_price(initial_price, 1200), 2500.into());
87 assert_eq!(oracle.get_gas_price(initial_price, 3000), 2500.into());
88 assert_eq!(oracle.get_gas_price(initial_price, 1000000), 2500.into());
89 }
90
91 #[test]
92 #[allow(clippy::float_cmp)]
93 fn behaves_with_realistic_values() {
94 let oracle = GeometricGasPrice::new(1.25, 10u64, None::<u64>);
95 const GWEI: f64 = 1000000000.0;
96 let initial_price = U256::from(100 * GWEI as u64);
97
98 for seconds in &[0u64, 1, 10, 12, 30, 60] {
99 println!(
100 "gas price after {} seconds is {}",
101 seconds,
102 oracle.get_gas_price(initial_price, *seconds).as_u64() as f64 / GWEI
103 );
104 }
105
106 let normalized = |time| oracle.get_gas_price(initial_price, time).as_u64() as f64 / GWEI;
107
108 assert_eq!(normalized(0), 100.0);
109 assert_eq!(normalized(1), 100.0);
110 assert_eq!(normalized(10), 125.0);
111 assert_eq!(normalized(12), 125.0);
112 assert_eq!(normalized(30), 195.3125);
113 assert_eq!(normalized(60), 381.469726563);
114 }
115}