ethers_middleware/gas_oracle/
cache.rs1use super::{GasOracle, Result};
2use async_trait::async_trait;
3use ethers_core::types::U256;
4use futures_locks::RwLock;
5use instant::{Duration, Instant};
6use std::{fmt::Debug, future::Future};
7
8#[derive(Debug)]
9pub struct Cache<T: GasOracle> {
10 inner: T,
11 validity: Duration,
12 fee: Cached<U256>,
13 eip1559: Cached<(U256, U256)>,
14}
15
16#[derive(Default, Debug)]
17struct Cached<T: Clone>(RwLock<Option<(Instant, T)>>);
18
19#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
20#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
21impl<T: GasOracle> GasOracle for Cache<T> {
22 async fn fetch(&self) -> Result<U256> {
23 self.fee.get(self.validity, || self.inner.fetch()).await
24 }
25
26 async fn estimate_eip1559_fees(&self) -> Result<(U256, U256)> {
27 self.eip1559.get(self.validity, || self.inner.estimate_eip1559_fees()).await
28 }
29}
30
31impl<T: GasOracle> Cache<T> {
32 pub fn new(validity: Duration, inner: T) -> Self {
33 Self { inner, validity, fee: Cached::default(), eip1559: Cached::default() }
34 }
35}
36
37impl<T: Clone> Cached<T> {
38 async fn get<F, E, Fut>(&self, validity: Duration, fetch: F) -> Result<T, E>
39 where
40 F: FnOnce() -> Fut,
41 Fut: Future<Output = Result<T, E>>,
42 {
43 {
45 let lock = self.0.read().await;
46 if let Some((last_fetch, value)) = lock.as_ref() {
47 if Instant::now().duration_since(*last_fetch) < validity {
48 return Ok(value.clone())
49 }
50 }
51 }
52 {
54 let mut lock = self.0.write().await;
55 if let Some((last_fetch, value)) = lock.as_ref() {
57 if Instant::now().duration_since(*last_fetch) < validity {
58 return Ok(value.clone())
59 }
60 }
61 let value = fetch().await?;
63 *lock = Some((Instant::now(), value.clone()));
64 Ok(value)
65 }
66 }
67}