Skip to content

Commit

Permalink
eth_feeHistory implementation (tomusdrw#558)
Browse files Browse the repository at this point in the history
* eth_feeHistory implementation

* Update src/api/eth.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update src/api/eth.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update src/types/fee_history.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update src/types/fee_history.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* added unit tests

* changed example because cant use assert_approx_eq for floats here

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
  • Loading branch information
2 people authored and TraceBundy committed Nov 9, 2021
1 parent 8318e6a commit 01e277e
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 5 deletions.
52 changes: 48 additions & 4 deletions src/api/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use crate::{
api::Namespace,
helpers::{self, CallFuture},
types::{
Address, Block, BlockHeader, BlockId, BlockNumber, Bytes, CallRequest, Filter, Index, Log, SyncState,
Transaction, TransactionId, TransactionReceipt, TransactionRequest, Work, H256, H520, H64, U256, U64,
Address, Block, BlockHeader, BlockId, BlockNumber, Bytes, CallRequest, FeeHistory, Filter, Index, Log,
SyncState, Transaction, TransactionId, TransactionReceipt, TransactionRequest, Work, H256, H520, H64, U256,
U64,
},
Transport,
};
Expand Down Expand Up @@ -88,6 +89,24 @@ impl<T: Transport> Eth<T> {
CallFuture::new(self.transport.execute("eth_gasPrice", vec![]))
}

/// Returns a collection of historical gas information. This can be used for evaluating the max_fee_per_gas
/// and max_priority_fee_per_gas to send the future transactions.
pub fn fee_history(
&self,
block_count: U256,
newest_block: BlockNumber,
reward_percentiles: Option<Vec<f64>>,
) -> CallFuture<FeeHistory, T::Out> {
let block_count = helpers::serialize(&block_count);
let newest_block = helpers::serialize(&newest_block);
let reward_percentiles = helpers::serialize(&reward_percentiles);

CallFuture::new(
self.transport
.execute("eth_feeHistory", vec![block_count, newest_block, reward_percentiles]),
)
}

/// Get balance of given address
pub fn balance(&self, address: Address, block: Option<BlockNumber>) -> CallFuture<U256, T::Out> {
let address = helpers::serialize(&address);
Expand Down Expand Up @@ -356,8 +375,8 @@ mod tests {
api::Namespace,
rpc::Value,
types::{
Address, Block, BlockHeader, BlockId, BlockNumber, CallRequest, FilterBuilder, Log, SyncInfo, SyncState,
Transaction, TransactionId, TransactionReceipt, TransactionRequest, Work, H256, H520, H64,
Address, Block, BlockHeader, BlockId, BlockNumber, CallRequest, FeeHistory, FilterBuilder, Log, SyncInfo,
SyncState, Transaction, TransactionId, TransactionReceipt, TransactionRequest, Work, H256, H520, H64,
},
};
use hex_literal::hex;
Expand Down Expand Up @@ -473,6 +492,25 @@ mod tests {
"effectiveGasPrice": "0x100"
}"#;

const EXAMPLE_FEE_HISTORY: &str = r#"{
"baseFeePerGas": [
"0x15f794d04b",
"0x1730fe199f",
"0x176212b802",
"0x165bce08cb",
"0x16c6235c9d",
"0x1539ff7ccd"
],
"gasUsedRatio": [
0.722926465013414,
0.53306761204479,
0.32474768127264964,
0.574309529134573,
0.2282121795900929
],
"oldestBlock": "0xcd1df9"
}"#;

rpc_test! (
Eth:accounts => "eth_accounts";
Value::Array(vec![Value::String("0x0000000000000000000000000000000000000123".into())]) => vec![Address::from_low_u64_be(0x123)]
Expand Down Expand Up @@ -560,6 +598,12 @@ mod tests {
Value::String("0x123".into()) => 0x123
);

rpc_test! (
Eth:fee_history, 0x3, BlockNumber::Latest, None => "eth_feeHistory", vec![r#""0x3""#, r#""latest""#, r#"null"#];
::serde_json::from_str(EXAMPLE_FEE_HISTORY).unwrap()
=> ::serde_json::from_str::<FeeHistory>(EXAMPLE_FEE_HISTORY).unwrap()
);

rpc_test! (
Eth:balance, Address::from_low_u64_be(0x123), None
=>
Expand Down
52 changes: 51 additions & 1 deletion src/types/block.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::types::{Bytes, H160, H2048, H256, H64, U256, U64};
use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer};
use serde::{de::Error, ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer};

/// The block header type returned from RPC calls.
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
Expand Down Expand Up @@ -158,6 +158,24 @@ impl Serialize for BlockNumber {
}
}

impl<'a> Deserialize<'a> for BlockNumber {
fn deserialize<D>(deserializer: D) -> Result<BlockNumber, D::Error>
where
D: Deserializer<'a>,
{
let value = String::deserialize(deserializer)?;
match value.as_str() {
"latest" => Ok(BlockNumber::Latest),
"earliest" => Ok(BlockNumber::Earliest),
"pending" => Ok(BlockNumber::Pending),
_ if value.starts_with("0x") => U64::from_str_radix(&value[2..], 16)
.map(BlockNumber::Number)
.map_err(|e| D::Error::custom(format!("invalid block number: {}", e))),
_ => Err(D::Error::custom("invalid block number: missing 0x prefix".to_string())),
}
}
}

/// Block Identifier
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum BlockId {
Expand Down Expand Up @@ -291,4 +309,36 @@ mod tests {
let block: Block<()> = serde_json::from_value(json.clone()).unwrap();
assert_eq!(block.base_fee_per_gas, Some(U256::from(7)));
}

#[test]
fn serialize_deserialize_block_number() {
// BlockNumber::Latest
let serialized = serde_json::to_value(BlockNumber::Latest).unwrap();
assert_eq!(serialized, "latest");
let deserialized = serde_json::from_value::<BlockNumber>(serialized).unwrap();
assert_eq!(deserialized, BlockNumber::Latest);

// BlockNumber::Earliest
let serialized = serde_json::to_value(BlockNumber::Earliest).unwrap();
assert_eq!(serialized, "earliest");
let deserialized = serde_json::from_value::<BlockNumber>(serialized).unwrap();
assert_eq!(deserialized, BlockNumber::Earliest);

// BlockNumber::Pending
let serialized = serde_json::to_value(BlockNumber::Pending).unwrap();
assert_eq!(serialized, "pending");
let deserialized = serde_json::from_value::<BlockNumber>(serialized).unwrap();
assert_eq!(deserialized, BlockNumber::Pending);

// BlockNumber::Number
let serialized = serde_json::to_value(BlockNumber::Number(100.into())).unwrap();
assert_eq!(serialized, "0x64");
let deserialized = serde_json::from_value::<BlockNumber>(serialized).unwrap();
assert_eq!(deserialized, BlockNumber::Number(100.into()));
let deserialized = serde_json::from_value::<BlockNumber>("64".into());
assert_eq!(
deserialized.unwrap_err().to_string(),
"invalid block number: missing 0x prefix"
);
}
}
37 changes: 37 additions & 0 deletions src/types/fee_history.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use crate::types::{BlockNumber, U256};
use serde::{Deserialize, Serialize};

/// The fee history type returned from `eth_feeHistory` call.
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FeeHistory {
/// Lowest number block of the returned range.
pub oldest_block: BlockNumber,
/// A vector of block base fees per gas. This includes the next block after the newest of the returned range, because this value can be derived from the newest block. Zeroes are returned for pre-EIP-1559 blocks.
pub base_fee_per_gas: Vec<U256>,
/// A vector of block gas used ratios. These are calculated as the ratio of gas used and gas limit.
pub gas_used_ratio: Vec<f64>,
/// A vector of effective priority fee per gas data points from a single block. All zeroes are returned if the block is empty. Returned only if requested.
pub reward: Option<Vec<Vec<U256>>>,
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn fee_history() {
let fee_history = FeeHistory {
oldest_block: BlockNumber::Number(123456.into()),
base_fee_per_gas: vec![100.into(), 110.into()],
gas_used_ratio: vec![1.0, 2.0, 3.0],
reward: None,
};

let serialized = serde_json::to_value(fee_history.clone()).unwrap();
assert_eq!(serialized.to_string(), "{\"baseFeePerGas\":[\"0x64\",\"0x6e\"],\"gasUsedRatio\":[1.0,2.0,3.0],\"oldestBlock\":\"0x1e240\",\"reward\":null}");

let deserialized = serde_json::from_value(serialized).unwrap();
assert_eq!(fee_history, deserialized);
}
}
2 changes: 2 additions & 0 deletions src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
mod block;
mod bytes;
mod bytes_array;
mod fee_history;
mod log;
mod parity_peers;
mod parity_pending_transaction;
Expand All @@ -22,6 +23,7 @@ pub use self::{
block::{Block, BlockHeader, BlockId, BlockNumber},
bytes::Bytes,
bytes_array::BytesArray,
fee_history::FeeHistory,
log::{Filter, FilterBuilder, Log},
parity_peers::{
EthProtocolInfo, ParityPeerInfo, ParityPeerType, PeerNetworkInfo, PeerProtocolsInfo, PipProtocolInfo,
Expand Down

0 comments on commit 01e277e

Please sign in to comment.