1pub mod eip1559 {
6 use num_bigint::BigUint;
7 use num_traits::One;
8 use std::cmp::{self, Ordering};
9
10 const BASEFEE_MAX_CHANGE_DENOMINATOR: u64 = 8;
11
12 pub fn compute_base_fee(
31 parent_base_fee: &BigUint,
32 parent_gas_used: &BigUint,
33 parent_target_gas_used: &BigUint,
34 ) -> BigUint {
35 match parent_gas_used.cmp(&parent_target_gas_used) {
36 Ordering::Equal => parent_base_fee.clone(),
37 Ordering::Greater => {
38 compute_base_fee_increase(parent_base_fee, parent_gas_used, parent_target_gas_used)
39 }
40 Ordering::Less => {
41 compute_base_fee_decrease(parent_base_fee, parent_gas_used, parent_target_gas_used)
42 }
43 }
44 }
45
46 fn compute_base_fee_increase(
47 parent_base_fee: &BigUint,
48 parent_gas_used: &BigUint,
49 parent_target_gas_used: &BigUint,
50 ) -> BigUint {
51 let gas_delta = parent_gas_used - parent_target_gas_used;
52 let fee_delta = cmp::max(
53 (parent_base_fee * gas_delta) / parent_target_gas_used / BASEFEE_MAX_CHANGE_DENOMINATOR,
54 One::one(),
55 );
56 parent_base_fee + fee_delta
57 }
58
59 fn compute_base_fee_decrease(
60 parent_base_fee: &BigUint,
61 parent_gas_used: &BigUint,
62 parent_target_gas_used: &BigUint,
63 ) -> BigUint {
64 let gas_delta = parent_target_gas_used - parent_gas_used;
65 let fee_delta =
66 (parent_base_fee * gas_delta) / parent_target_gas_used / BASEFEE_MAX_CHANGE_DENOMINATOR;
67 parent_base_fee - fee_delta
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use num_bigint::BigUint;
75 use serde::{Deserialize, Serialize};
76 use std::{cmp::Ordering, fs, path::PathBuf};
77
78 #[test]
79 fn assert_basefee_computation_works() {
80 let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
81 d.push("resources/test/eip1559/basefee/reference-test.json");
82 let path = d.as_path().to_str().unwrap();
83 let data = fs::read_to_string(path);
84 let data = match data {
85 Ok(c) => c,
86 Err(error) => panic!("Problem opening the file: {:?}", error),
87 };
88
89 let test_cases = json_to_testcase(&data);
90 for case in &test_cases {
91 let parent_base_fee = BigUint::from(case.parent_base_fee);
92 let parent_gas_used = BigUint::from(case.parent_gas_used);
93 let parent_target_gas_used = BigUint::from(case.parent_target_gas_used);
94 let base_fee = eip1559::compute_base_fee(
95 &parent_base_fee,
96 &parent_gas_used,
97 &parent_target_gas_used,
98 );
99 assert_eq!(
100 true,
101 base_fee.cmp(&BigUint::from(case.expected_base_fee)) == Ordering::Equal
102 );
103 }
104 }
105
106 pub fn json_to_testcase(data: &str) -> Vec<TestCase> {
107 let t = serde_json::from_str(data);
108 match t {
109 Ok(c) => c,
110 Err(error) => panic!("Problem opening the file: {:?}", error),
111 }
112 }
113
114 #[derive(Debug, Serialize, Deserialize)]
115 pub struct TestCase {
116 #[serde(rename = "parentBaseFee")]
117 pub parent_base_fee: u64,
118 #[serde(rename = "parentGasUsed")]
119 pub parent_gas_used: u64,
120 #[serde(rename = "parentTargetGasUsed")]
121 pub parent_target_gas_used: u64,
122 #[serde(rename = "expectedBaseFee")]
123 pub expected_base_fee: u64,
124 }
125}