ethers_middleware/gas_escalator/
linear.rs

1use super::GasEscalator;
2use ethers_core::types::U256;
3
4/// Linearly increasing gas price.
5///
6///
7/// Start with `initial_price`, then increase it by fixed amount `increase_by` every `every_secs`
8/// seconds until the transaction gets confirmed. There is an optional upper limit.
9///
10/// <https://github.com/makerdao/pymaker/blob/master/pymaker/gas.py#L129>
11#[derive(Clone, Debug)]
12pub struct LinearGasPrice {
13    every_secs: u64,
14    increase_by: U256,
15    max_price: Option<U256>,
16}
17
18impl LinearGasPrice {
19    /// Constructor
20    pub fn new<T: Into<U256>>(
21        increase_by: T,
22        every_secs: impl Into<u64>,
23        max_price: Option<T>,
24    ) -> Self {
25        LinearGasPrice {
26            every_secs: every_secs.into(),
27            increase_by: increase_by.into(),
28            max_price: max_price.map(Into::into),
29        }
30    }
31}
32
33impl GasEscalator for LinearGasPrice {
34    fn get_gas_price(&self, initial_price: U256, time_elapsed: u64) -> U256 {
35        let mut result = initial_price + self.increase_by * (time_elapsed / self.every_secs);
36        if let Some(max_price) = self.max_price {
37            result = std::cmp::min(result, max_price);
38        }
39        result
40    }
41}
42
43#[cfg(test)]
44// https://github.com/makerdao/pymaker/blob/master/tests/test_gas.py#L107
45mod tests {
46    use super::*;
47
48    #[test]
49    fn gas_price_increases_with_time() {
50        let oracle = LinearGasPrice::new(100, 60u64, None);
51        let initial_price = U256::from(1000);
52
53        assert_eq!(oracle.get_gas_price(initial_price, 0), 1000.into());
54        assert_eq!(oracle.get_gas_price(initial_price, 1), 1000.into());
55        assert_eq!(oracle.get_gas_price(initial_price, 59), 1000.into());
56        assert_eq!(oracle.get_gas_price(initial_price, 60), 1100.into());
57        assert_eq!(oracle.get_gas_price(initial_price, 119), 1100.into());
58        assert_eq!(oracle.get_gas_price(initial_price, 120), 1200.into());
59        assert_eq!(oracle.get_gas_price(initial_price, 1200), 3000.into());
60    }
61
62    #[test]
63    fn gas_price_should_obey_max_value() {
64        let oracle = LinearGasPrice::new(100, 60u64, Some(2500));
65        let initial_price = U256::from(1000);
66
67        assert_eq!(oracle.get_gas_price(initial_price, 0), 1000.into());
68        assert_eq!(oracle.get_gas_price(initial_price, 1), 1000.into());
69        assert_eq!(oracle.get_gas_price(initial_price, 59), 1000.into());
70        assert_eq!(oracle.get_gas_price(initial_price, 60), 1100.into());
71        assert_eq!(oracle.get_gas_price(initial_price, 119), 1100.into());
72        assert_eq!(oracle.get_gas_price(initial_price, 120), 1200.into());
73        assert_eq!(oracle.get_gas_price(initial_price, 1200), 2500.into());
74        assert_eq!(oracle.get_gas_price(initial_price, 3000), 2500.into());
75        assert_eq!(oracle.get_gas_price(initial_price, 1000000), 2500.into());
76    }
77}