oxidized_builder/network/
gas.rs

1use crate::common::error::AppError;
2use crate::common::retry::retry_async;
3use crate::network::provider::HttpProvider;
4use alloy::providers::Provider;
5use alloy::rpc::types::eth::FeeHistory;
6use alloy::rpc::types::BlockNumberOrTag;
7use std::time::Duration;
8
9#[derive(Clone)]
10pub struct GasOracle {
11    provider: HttpProvider,
12}
13
14#[derive(Debug)]
15pub struct GasFees {
16    pub max_fee_per_gas: u128,
17    pub max_priority_fee_per_gas: u128,
18}
19
20impl GasOracle {
21    pub fn new(provider: HttpProvider) -> Self {
22        Self { provider }
23    }
24
25    pub async fn estimate_eip1559_fees(&self) -> Result<GasFees, AppError> {
26        let history: FeeHistory = self
27            .with_retry_history()
28            .await?;
29
30        let last_base_fee = history
31            .base_fee_per_gas
32            .last()
33            .ok_or(AppError::Initialization("No base fee history".into()))?;
34
35        let next_base_fee = (last_base_fee * 1125) / 1000;
36
37
38        let priority_fee = if let Some(rewards) = history.reward {
39            let mut sum = 0u128;
40            let mut count = 0;
41            for block_reward in rewards {
42                if let Some(r) = block_reward.first() {
43                    sum += *r; 
44                    count += 1;
45                }
46            }
47            if count > 0 {
48                sum / count
49            } else {
50                2_000_000_000
51            }
52        } else {
53            2_000_000_000
54        };
55
56        Ok(GasFees {
57            max_fee_per_gas: next_base_fee + priority_fee,
58            max_priority_fee_per_gas: priority_fee,
59        })
60    }
61}
62
63impl GasOracle {
64    async fn with_retry_history(&self) -> Result<FeeHistory, AppError> {
65        let provider = self.provider.clone();
66        retry_async(
67            move |_| {
68                let provider = provider.clone();
69                async move { provider.get_fee_history(5, BlockNumberOrTag::Latest, &[50.0f64]).await }
70            },
71            3,
72            Duration::from_millis(100),
73        )
74        .await
75        .map_err(|e| AppError::Connection(format!("Fee History failed: {}", e)))
76    }
77}