Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

feat(etherscan): Add get_block_by_timestamp to ethers-etherscan #2349

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions ethers-etherscan/src/blocks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use crate::{Client, EtherscanError, Response, Result};
use ethers_core::types::BlockNumber;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct BlockNumberByTimestamp {
pub timestamp: u64,
pub block_number: BlockNumber,
}

impl Client {
/// Returns either (1) the oldest block since a particular timestamp occurred or (2) the newest
/// block that occurred prior to that timestamp
///
/// # Examples
///
/// ```no_run
/// # async fn foo(client: ethers_etherscan::Client) -> Result<(), Box<dyn std::error::Error>> {
/// // The newest block that occurred prior to 1 January 2020
/// let block_number_before = client.get_block_by_timestamp(1577836800, "before");
/// // The oldest block that occurred after 1 January 2020
/// let block_number_after = client.get_block_by_timestamp(1577836800, "after");
/// # Ok(()) }
/// ```
pub async fn get_block_by_timestamp(
&self,
timestamp: u64,
closest: &str,
) -> Result<BlockNumberByTimestamp> {
let query = self.create_query(
"block",
"getblocknobytime",
HashMap::from([("timestamp", timestamp.to_string()), ("closest", closest.to_string())]),
);
let response: Response<String> = self.get_json(&query).await?;

match response.status.as_str() {
"0" => Err(EtherscanError::BlockNumberByTimestampFailed),
"1" => Ok(BlockNumberByTimestamp {
timestamp,
block_number: response.result.parse::<BlockNumber>().unwrap(),
}),
err => Err(EtherscanError::BadStatusCode(err.to_string())),
}
}
}
2 changes: 2 additions & 0 deletions ethers-etherscan/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub enum EtherscanError {
ExecutionFailed(String),
#[error("Balance failed")]
BalanceFailed,
#[error("Block by timestamp failed")]
BlockNumberByTimestampFailed,
#[error("Transaction receipt failed")]
TransactionReceiptFailed,
#[error("Gas estimation failed")]
Expand Down
1 change: 1 addition & 0 deletions ethers-etherscan/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use std::{
use tracing::{error, trace};

pub mod account;
pub mod blocks;
pub mod contract;
pub mod errors;
pub mod gas;
Expand Down
28 changes: 28 additions & 0 deletions ethers-etherscan/tests/it/blocks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::*;
use ethers_core::types::BlockNumber;
use serial_test::serial;

#[tokio::test]
#[serial]
async fn check_get_block_by_timestamp_before() {
run_with_client(Chain::Mainnet, |client| async move {
let block_no = client.get_block_by_timestamp(1577836800, "before").await;
assert!(block_no.is_ok());

let block_no = block_no.unwrap().block_number;
assert_eq!(block_no, "9193265".parse::<BlockNumber>().unwrap());
})
.await
}

#[tokio::test]
#[serial]
async fn check_get_block_by_timestamp_after() {
run_with_client(Chain::Mainnet, |client| async move {
let block_no = client.get_block_by_timestamp(1577836800, "after").await;

let block_no = block_no.unwrap().block_number;
assert_eq!(block_no, "9193266".parse::<BlockNumber>().unwrap());
})
.await
}
1 change: 1 addition & 0 deletions ethers-etherscan/tests/it/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::{
};

mod account;
mod blocks;
mod contract;
mod gas;
mod transaction;
Expand Down