ethers_middleware/gas_oracle/
mod.rs

1pub mod blocknative;
2pub use blocknative::BlockNative;
3
4pub mod eth_gas_station;
5#[allow(deprecated)]
6pub use eth_gas_station::EthGasStation;
7
8pub mod etherchain;
9pub use etherchain::Etherchain;
10
11#[cfg(feature = "etherscan")]
12pub mod etherscan;
13#[cfg(feature = "etherscan")]
14pub use etherscan::Etherscan;
15
16pub mod middleware;
17pub use middleware::{GasOracleMiddleware, MiddlewareError};
18
19pub mod median;
20pub use median::Median;
21
22pub mod cache;
23pub use cache::Cache;
24
25pub mod polygon;
26pub use polygon::Polygon;
27
28pub mod gas_now;
29pub use gas_now::GasNow;
30
31pub mod provider_oracle;
32pub use provider_oracle::ProviderOracle;
33
34use async_trait::async_trait;
35use auto_impl::auto_impl;
36use ethers_core::types::U256;
37use reqwest::Error as ReqwestError;
38use std::{error::Error, fmt::Debug};
39use thiserror::Error;
40
41pub(crate) const GWEI_TO_WEI: u64 = 1_000_000_000;
42pub(crate) const GWEI_TO_WEI_U256: U256 = U256([GWEI_TO_WEI, 0, 0, 0]);
43
44pub type Result<T, E = GasOracleError> = std::result::Result<T, E>;
45
46/// Generic [`GasOracle`] gas price categories.
47#[derive(Clone, Copy, Default, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
48pub enum GasCategory {
49    SafeLow,
50    #[default]
51    Standard,
52    Fast,
53    Fastest,
54}
55
56/// Error thrown by a [`GasOracle`].
57#[derive(Debug, Error)]
58pub enum GasOracleError {
59    /// An internal error in the HTTP request made from the underlying
60    /// gas oracle
61    #[error(transparent)]
62    HttpClientError(#[from] ReqwestError),
63
64    /// An error decoding JSON response from gas oracle
65    #[error(transparent)]
66    SerdeJsonError(#[from] serde_json::Error),
67
68    /// An error with oracle response type
69    #[error("invalid oracle response")]
70    InvalidResponse,
71
72    /// An internal error in the Etherscan client request made from the underlying
73    /// gas oracle
74    #[error(transparent)]
75    #[cfg(feature = "etherscan")]
76    EtherscanError(#[from] ethers_etherscan::errors::EtherscanError),
77
78    /// An internal error thrown when the required gas category is not
79    /// supported by the gas oracle API
80    #[error("gas category not supported")]
81    GasCategoryNotSupported,
82
83    #[error("EIP-1559 gas estimation not supported")]
84    Eip1559EstimationNotSupported,
85
86    #[error("None of the oracles returned a value")]
87    NoValues,
88
89    #[error("Chain is not supported by the oracle")]
90    UnsupportedChain,
91
92    /// Error thrown when the provider failed.
93    #[error("Provider error: {0}")]
94    ProviderError(#[from] Box<dyn Error + Send + Sync>),
95    #[error("Failed to parse gas values: {0}")]
96    ConversionError(#[from] ethers_core::utils::ConversionError),
97}
98
99/// An Ethereum gas price oracle.
100///
101/// # Example
102///
103/// ```no_run
104/// use ethers_core::types::U256;
105/// use ethers_middleware::gas_oracle::{GasCategory, GasNow, GasOracle};
106///
107/// # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
108/// let oracle = GasNow::default().category(GasCategory::SafeLow);
109/// let gas_price = oracle.fetch().await?;
110/// assert!(gas_price > U256::zero());
111/// # Ok(())
112/// # }
113/// ```
114#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
115#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
116#[auto_impl(&, Box, Arc)]
117pub trait GasOracle: Send + Sync + Debug {
118    /// Makes an asynchronous HTTP query to the underlying [`GasOracle`] to fetch the current gas
119    /// price estimate.
120    ///
121    /// # Example
122    ///
123    /// ```no_run
124    /// use ethers_core::types::U256;
125    /// use ethers_middleware::gas_oracle::{GasCategory, GasNow, GasOracle};
126    ///
127    /// # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
128    /// let oracle = GasNow::default().category(GasCategory::SafeLow);
129    /// let gas_price = oracle.fetch().await?;
130    /// assert!(gas_price > U256::zero());
131    /// # Ok(())
132    /// # }
133    /// ```
134    async fn fetch(&self) -> Result<U256>;
135
136    /// Makes an asynchronous HTTP query to the underlying [`GasOracle`] to fetch the current max
137    /// gas fee and priority gas fee estimates.
138    ///
139    /// # Example
140    ///
141    /// ```no_run
142    /// use ethers_core::types::U256;
143    /// use ethers_middleware::gas_oracle::{GasCategory, GasNow, GasOracle};
144    ///
145    /// # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
146    /// let oracle = GasNow::default().category(GasCategory::SafeLow);
147    /// let (max_fee, priority_fee) = oracle.estimate_eip1559_fees().await?;
148    /// assert!(max_fee > U256::zero());
149    /// assert!(priority_fee > U256::zero());
150    /// # Ok(())
151    /// # }
152    /// ```
153    async fn estimate_eip1559_fees(&self) -> Result<(U256, U256)>;
154}
155
156#[inline]
157#[doc(hidden)]
158pub fn from_gwei_f64(gwei: f64) -> U256 {
159    U256::from((gwei * GWEI_TO_WEI as f64).ceil() as u64)
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165
166    #[test]
167    fn test_gwei_wei_constants() {
168        let as_u256: U256 = GWEI_TO_WEI.into();
169        assert_eq!(as_u256, GWEI_TO_WEI_U256);
170        assert_eq!(GWEI_TO_WEI_U256.as_u64(), GWEI_TO_WEI);
171    }
172
173    #[test]
174    fn test_gwei_conversion() {
175        let max_priority_fee: f64 = 1.8963421368;
176
177        let result = from_gwei_f64(max_priority_fee);
178        assert_eq!(result, U256::from(1896342137));
179    }
180}