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