forked from gakonst/ethers-rs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Pass reqwest Client to constructors * Add Median oracle aggregator * DRY * Weighted median * Add cache layer * Simplify lifetimes * Add with_client constructors * Update GasNow urls * Add u256_from_f64_saturating * Add polygon oracle * Fixes * Fix lints * Remove dbg statements
- Loading branch information
Remco Bloemen
authored
May 6, 2022
1 parent
ce3ebae
commit 18b4ef4
Showing
13 changed files
with
503 additions
and
38 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
use ethabi::ethereum_types::U256; | ||
|
||
/// Convert a floating point value to its nearest f64 integer. | ||
/// | ||
/// It is saturating, so values $\ge 2^{256}$ will be rounded | ||
/// to [`U256::max_value()`] and values $< 0$ to zero. This includes | ||
/// positive and negative infinity. | ||
/// | ||
/// TODO: Move to ethabi::ethereum_types::U256. | ||
/// TODO: Add [`super::I256`] version. | ||
/// | ||
/// # Panics | ||
/// | ||
/// Panics if `f` is NaN. | ||
pub fn u256_from_f64_saturating(mut f: f64) -> U256 { | ||
if f.is_nan() { | ||
panic!("NaN is not a valid value for U256"); | ||
} | ||
if f < 0.5 { | ||
return U256::zero() | ||
} | ||
if f >= 1.157_920_892_373_162e77_f64 { | ||
return U256::max_value() | ||
} | ||
// All non-normal cases should have been handled above | ||
assert!(f.is_normal()); | ||
// Turn nearest rounding into truncated rounding | ||
f += 0.5; | ||
|
||
// Parse IEEE-754 double into U256 | ||
// Sign should be zero, exponent should be >= 0. | ||
let bits = f.to_bits(); | ||
let sign = bits >> 63; | ||
assert!(sign == 0); | ||
let biased_exponent = (bits >> 52) & 0x7ff; | ||
assert!(biased_exponent >= 1023); | ||
let exponent = biased_exponent - 1023; | ||
let fraction = bits & 0xfffffffffffff; | ||
let mantissa = 0x10000000000000 | fraction; | ||
if exponent > 255 { | ||
U256::max_value() | ||
} else if exponent < 52 { | ||
// Truncate mantissa | ||
U256([mantissa, 0, 0, 0]) >> (52 - exponent) | ||
} else { | ||
U256([mantissa, 0, 0, 0]) << (exponent - 52) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use std::f64; | ||
|
||
#[test] | ||
fn test_small_integers() { | ||
for i in 0..=255 { | ||
let f = i as f64; | ||
let u = u256_from_f64_saturating(f); | ||
assert_eq!(u, U256::from(i)); | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_small_integers_round_down() { | ||
for i in 0..=255 { | ||
let f = (i as f64) + 0.499; | ||
let u = u256_from_f64_saturating(f); | ||
assert_eq!(u, U256::from(i)); | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_small_integers_round_up() { | ||
for i in 0..=255 { | ||
let f = (i as f64) - 0.5; | ||
let u = u256_from_f64_saturating(f); | ||
assert_eq!(u, U256::from(i)); | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_infinities() { | ||
assert_eq!(u256_from_f64_saturating(f64::INFINITY), U256::max_value()); | ||
assert_eq!(u256_from_f64_saturating(f64::NEG_INFINITY), U256::zero()); | ||
} | ||
|
||
#[test] | ||
fn test_saturating() { | ||
assert_eq!(u256_from_f64_saturating(-1.0), U256::zero()); | ||
assert_eq!(u256_from_f64_saturating(1e90_f64), U256::max_value()); | ||
} | ||
|
||
#[test] | ||
fn test_large() { | ||
// Check with e.g. `python3 -c 'print(int(1.0e36))'` | ||
assert_eq!( | ||
u256_from_f64_saturating(1.0e36_f64), | ||
U256::from_dec_str("1000000000000000042420637374017961984").unwrap() | ||
); | ||
assert_eq!( | ||
u256_from_f64_saturating(f64::consts::PI * 2.0e60_f64), | ||
U256::from_dec_str("6283185307179586084560863929317662625677330590403879287914496") | ||
.unwrap() | ||
); | ||
assert_eq!( | ||
u256_from_f64_saturating(5.78960446186581e76_f64), | ||
U256::from_dec_str( | ||
"57896044618658097711785492504343953926634992332820282019728792003956564819968" | ||
) | ||
.unwrap() | ||
); | ||
assert_eq!( | ||
u256_from_f64_saturating(1.157920892373161e77_f64), | ||
U256::from_dec_str( | ||
"115792089237316105435040506505232477503392813560534822796089932171514352762880" | ||
) | ||
.unwrap() | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,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>>, | ||
{ | ||
// Try with a read lock | ||
{ | ||
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()) | ||
} | ||
} | ||
} | ||
// Acquire a write lock | ||
{ | ||
let mut lock = self.0.write().await; | ||
// Check again, a concurrent thread may have raced us to the write. | ||
if let Some((last_fetch, value)) = lock.as_ref() { | ||
if Instant::now().duration_since(*last_fetch) < validity { | ||
return Ok(value.clone()) | ||
} | ||
} | ||
// Set a fresh value | ||
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.