From ab0e3ca0d677ccb319d75edc5980a535f73021db Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Thu, 19 Aug 2021 18:44:15 +0200 Subject: [PATCH] fix: backwards compatibility for eth_feeHistory (#395) * fix: backwards compatibility for eth_feeHistor * fix: convenience for calling the fee_history method --- ethers-providers/src/lib.rs | 31 ++++++++++++++++++++++++++----- ethers-providers/src/provider.rs | 27 ++++++++++++++++++++++----- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/ethers-providers/src/lib.rs b/ethers-providers/src/lib.rs index 70638a41..cf833b73 100644 --- a/ethers-providers/src/lib.rs +++ b/ethers-providers/src/lib.rs @@ -84,8 +84,8 @@ pub use pubsub::{PubsubClient, SubscriptionStream}; use async_trait::async_trait; use auto_impl::auto_impl; use ethers_core::types::transaction::{eip2718::TypedTransaction, eip2930::AccessListWithGasUsed}; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use std::{error::Error, fmt::Debug, future::Future, pin::Pin}; +use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize}; +use std::{error::Error, fmt::Debug, future::Future, pin::Pin, str::FromStr}; pub use provider::{FilterKind, Provider, ProviderError}; @@ -704,9 +704,9 @@ pub trait Middleware: Sync + Send + Debug { .map_err(FromErr::from) } - async fn fee_history( + async fn fee_history + serde::Serialize + Send + Sync>( &self, - block_count: u64, + block_count: T, last_block: BlockNumber, reward_percentiles: &[f64], ) -> Result { @@ -733,10 +733,31 @@ pub trait Middleware: Sync + Send + Debug { pub struct FeeHistory { pub base_fee_per_gas: Vec, pub gas_used_ratio: Vec, - pub oldest_block: u64, + #[serde(deserialize_with = "from_int_or_hex")] + /// oldestBlock is returned as an unsigned integer up to geth v1.10.6. From + /// geth v1.10.7, this has been updated to return in the hex encoded form. + /// The custom deserializer allows backward compatibility for those clients + /// not running v1.10.7 yet. + pub oldest_block: U256, pub reward: Vec>, } +fn from_int_or_hex<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + #[derive(Deserialize)] + #[serde(untagged)] + enum IntOrHex { + Int(u64), + Hex(String), + } + match IntOrHex::deserialize(deserializer)? { + IntOrHex::Int(n) => Ok(U256::from(n)), + IntOrHex::Hex(s) => U256::from_str(s.as_str()).map_err(serde::de::Error::custom), + } +} + #[cfg(feature = "celo")] #[async_trait] pub trait CeloMiddleware: Middleware { diff --git a/ethers-providers/src/provider.rs b/ethers-providers/src/provider.rs index facb3f66..58b9078e 100644 --- a/ethers-providers/src/provider.rs +++ b/ethers-providers/src/provider.rs @@ -769,20 +769,37 @@ impl Middleware for Provider

{ self.subscribe([logs, filter]).await } - async fn fee_history( + async fn fee_history + serde::Serialize + Send + Sync>( &self, - block_count: u64, + block_count: T, last_block: BlockNumber, reward_percentiles: &[f64], ) -> Result { - let block_count = utils::serialize(&block_count); let last_block = utils::serialize(&last_block); let reward_percentiles = utils::serialize(&reward_percentiles); + + // The blockCount param is expected to be an unsigned integer up to geth v1.10.6. + // Geth v1.10.7 onwards, this has been updated to a hex encoded form. Failure to + // decode the param from client side would fallback to the old API spec. self.request( "eth_feeHistory", - [block_count, last_block, reward_percentiles], + [ + utils::serialize(&block_count), + last_block.clone(), + reward_percentiles.clone(), + ], ) .await + .or(self + .request( + "eth_feeHistory", + [ + utils::serialize(&block_count.into().as_u64()), + last_block, + reward_percentiles, + ], + ) + .await) } } @@ -1123,7 +1140,7 @@ mod tests { .unwrap(); let history = provider - .fee_history(10, BlockNumber::Latest, &[10.0, 40.0]) + .fee_history(10u64, BlockNumber::Latest, &[10.0, 40.0]) .await .unwrap(); dbg!(&history);