1use alloc::vec::Vec;
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5#[doc(alias = "TransactionGasAndReward")]
6pub struct TxGasAndReward {
7 pub gas_used: u64,
9 pub reward: u128,
11}
12
13impl PartialOrd for TxGasAndReward {
14 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
15 Some(self.cmp(other))
16 }
17}
18
19impl Ord for TxGasAndReward {
20 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
21 self.reward.cmp(&other.reward)
25 }
26}
27
28#[derive(Clone, Debug, Default, PartialEq)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
31#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
32pub struct FeeHistory {
33 #[cfg_attr(
42 feature = "serde",
43 serde(default, skip_serializing_if = "Vec::is_empty", with = "alloy_serde::quantity::vec")
44 )]
45 pub base_fee_per_gas: Vec<u128>,
46 #[cfg_attr(feature = "serde", serde(deserialize_with = "alloy_serde::null_as_default"))]
49 pub gas_used_ratio: Vec<f64>,
50 #[cfg_attr(
54 feature = "serde",
55 serde(default, skip_serializing_if = "Vec::is_empty", with = "alloy_serde::quantity::vec")
56 )]
57 pub base_fee_per_blob_gas: Vec<u128>,
58 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Vec::is_empty"))]
61 pub blob_gas_used_ratio: Vec<f64>,
62 #[cfg_attr(feature = "serde", serde(default, with = "alloy_serde::quantity"))]
64 pub oldest_block: u64,
65 #[cfg_attr(
68 feature = "serde",
69 serde(
70 default,
71 skip_serializing_if = "Option::is_none",
72 with = "alloy_serde::quantity::u128_vec_vec_opt"
73 )
74 )]
75 pub reward: Option<Vec<Vec<u128>>>,
76}
77
78impl FeeHistory {
79 pub fn latest_block_base_fee(&self) -> Option<u128> {
81 self.base_fee_per_gas.iter().rev().nth(1).copied()
83 }
84
85 pub fn next_block_base_fee(&self) -> Option<u128> {
87 self.base_fee_per_gas.last().copied()
88 }
89
90 pub fn next_block_blob_base_fee(&self) -> Option<u128> {
94 self.base_fee_per_blob_gas
95 .last()
96 .copied()
97 .filter(|fee| *fee != 0)
99 }
100
101 pub fn latest_block_blob_base_fee(&self) -> Option<u128> {
105 self.base_fee_per_blob_gas
107 .iter()
108 .rev()
109 .nth(1)
110 .copied()
111 .filter(|fee| *fee != 0)
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use crate::FeeHistory;
119 use similar_asserts::assert_eq;
120
121 #[test]
122 #[cfg(feature = "serde")]
123 fn test_fee_history_serde() {
124 let sample = r#"{"baseFeePerGas":["0x342770c0","0x2da282a8"],"gasUsedRatio":[0.0],"baseFeePerBlobGas":["0x0","0x0"],"blobGasUsedRatio":[0.0],"oldestBlock":"0x1"}"#;
125 let fee_history: FeeHistory = serde_json::from_str(sample).unwrap();
126 let expected = FeeHistory {
127 base_fee_per_blob_gas: vec![0, 0],
128 base_fee_per_gas: vec![875000000, 765625000],
129 blob_gas_used_ratio: vec![0.0],
130 gas_used_ratio: vec![0.0],
131 oldest_block: 1,
132 reward: None,
133 };
134
135 assert_eq!(fee_history, expected);
136 assert_eq!(serde_json::to_string(&fee_history).unwrap(), sample);
137 }
138
139 #[test]
140 #[cfg(feature = "serde")]
141 fn test_fee_history_serde_2() {
142 let json = r#"{"baseFeePerBlobGas":["0xc0","0xb2","0xab","0x98","0x9e","0x92","0xa4","0xb9","0xd0","0xea","0xfd"],"baseFeePerGas":["0x4cb8cf181","0x53075988e","0x4fb92ee18","0x45c209055","0x4e790dca2","0x58462e84e","0x5b7659f4e","0x5d66ea3aa","0x6283c6e45","0x5ecf0e1e5","0x5da59cf89"],"blobGasUsedRatio":[0.16666666666666666,0.3333333333333333,0,0.6666666666666666,0.16666666666666666,1,1,1,1,0.8333333333333334],"gasUsedRatio":[0.8288135,0.3407616666666667,0,0.9997232,0.999601,0.6444664333333333,0.5848306333333333,0.7189564,0.34952733333333336,0.4509799666666667],"oldestBlock":"0x59f94f","reward":[["0x59682f00"],["0x59682f00"],["0x0"],["0x59682f00"],["0x59682f00"],["0x3b9aca00"],["0x59682f00"],["0x59682f00"],["0x3b9aca00"],["0x59682f00"]]}"#;
143 let _actual = serde_json::from_str::<FeeHistory>(json).unwrap();
144 }
145
146 #[test]
147 #[cfg(feature = "serde")]
148 fn test_fee_history_serde_3() {
149 let json = r#"{"oldestBlock":"0xdee807","baseFeePerGas":["0x4ccf46253","0x4457de658","0x4531c5aee","0x3cfa33972","0x3d33403eb","0x399457884","0x40bdf9772","0x48d55e7c4","0x51e9ebf14","0x55f460bf9","0x4e31607e4"],"gasUsedRatio":[0.05909575012589385,0.5498182666666667,0.0249864,0.5146185,0.2633512,0.997582061117319,0.999914966153302,0.9986873805040722,0.6973219148223686,0.13879896448917434],"baseFeePerBlobGas":["0x0","0x0","0x0","0x0","0x0","0x0","0x0","0x0","0x0","0x0","0x0"],"blobGasUsedRatio":[0,0,0,0,0,0,0,0,0,0]}"#;
150 let _actual = serde_json::from_str::<FeeHistory>(json).unwrap();
151 }
152
153 #[test]
154 #[cfg(feature = "serde")]
155 fn test_fee_hist_null_gas_used_ratio() {
156 let json = r#"{"oldestBlock": "0x0", "gasUsedRatio": null}"#;
157 let _actual = serde_json::from_str::<FeeHistory>(json).unwrap();
158 }
159}