oxidized_builder/network/
gas.rs1use 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}