From d0091b7b614eca1bb0341351c39943336f20b7aa Mon Sep 17 00:00:00 2001 From: Rainbow Trout <0xrainbowtrout@proton.me> Date: Fri, 14 Apr 2023 03:57:17 +0000 Subject: [PATCH 1/6] ENH: Add get_block_by_timestamp to ethers-etherscan --- ethers-etherscan/src/blocks.rs | 37 ++++++++++++++++++++++++++ ethers-etherscan/src/errors.rs | 2 ++ ethers-etherscan/src/lib.rs | 1 + ethers-etherscan/tests/it/blocks.rs | 41 +++++++++++++++++++++++++++++ ethers-etherscan/tests/it/main.rs | 1 + 5 files changed, 82 insertions(+) create mode 100644 ethers-etherscan/src/blocks.rs create mode 100644 ethers-etherscan/tests/it/blocks.rs diff --git a/ethers-etherscan/src/blocks.rs b/ethers-etherscan/src/blocks.rs new file mode 100644 index 000000000..1c36c91d9 --- /dev/null +++ b/ethers-etherscan/src/blocks.rs @@ -0,0 +1,37 @@ +use std::collections::HashMap; +use std::str::FromStr; + +use ethers_core::types::BlockNumber; +use serde::{Deserialize, Serialize}; + +use crate::{Client, EtherscanError, Response, Result}; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct BlockNumberByTimestamp { + pub timestamp: u64, + pub block_number: BlockNumber, +} + + +impl Client { + /// Returns block by timestamp + pub async fn get_block_by_timestamp(&self, timestamp: u64, closest: &str) -> Result { + let query = self.create_query( + "block", + "getblocknobytime", + HashMap::from([("timestamp", timestamp.to_string()), ("closest", closest.to_string())]), + ); + let response: Response = self.get_json(&query).await?; + + match response.status.as_str() { + "0" => Err(EtherscanError::BlockNumberByTimestampFailed), + "1" => Ok( + BlockNumberByTimestamp{ + timestamp: timestamp, + block_number: response.result.parse::().unwrap() + } + ), + err => Err(EtherscanError::BadStatusCode(err.to_string())), + } + } +} diff --git a/ethers-etherscan/src/errors.rs b/ethers-etherscan/src/errors.rs index 5f71f52d9..d3208e7a3 100644 --- a/ethers-etherscan/src/errors.rs +++ b/ethers-etherscan/src/errors.rs @@ -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")] diff --git a/ethers-etherscan/src/lib.rs b/ethers-etherscan/src/lib.rs index c1ea855e4..12827955b 100644 --- a/ethers-etherscan/src/lib.rs +++ b/ethers-etherscan/src/lib.rs @@ -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; diff --git a/ethers-etherscan/tests/it/blocks.rs b/ethers-etherscan/tests/it/blocks.rs new file mode 100644 index 000000000..9dcdfb6fe --- /dev/null +++ b/ethers-etherscan/tests/it/blocks.rs @@ -0,0 +1,41 @@ +use ethers_core::types::BlockNumber; +use serial_test::serial; + +use crate::*; + +#[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.result; + let block_no = block_no.unwrap().block_number; + assert_eq!(block_no, "9193265".parse::().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; + // assert!(block_no.is_ok()); + + let block_no = block_no.unwrap().block_number; + assert_eq!(block_no, "9193266".parse::().unwrap()); + }) + .await +} diff --git a/ethers-etherscan/tests/it/main.rs b/ethers-etherscan/tests/it/main.rs index e0548705e..0eac9aa17 100644 --- a/ethers-etherscan/tests/it/main.rs +++ b/ethers-etherscan/tests/it/main.rs @@ -10,6 +10,7 @@ use std::{ }; mod account; +mod blocks; mod contract; mod gas; mod transaction; From df5a415d1c0a232cd612cbae3cafa3fe5f053523 Mon Sep 17 00:00:00 2001 From: Rainbow Trout <0xrainbowtrout@proton.me> Date: Fri, 14 Apr 2023 04:08:43 +0000 Subject: [PATCH 2/6] Add doc string --- ethers-etherscan/src/blocks.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/ethers-etherscan/src/blocks.rs b/ethers-etherscan/src/blocks.rs index 1c36c91d9..9c8442aab 100644 --- a/ethers-etherscan/src/blocks.rs +++ b/ethers-etherscan/src/blocks.rs @@ -14,7 +14,19 @@ pub struct BlockNumberByTimestamp { impl Client { - /// Returns block by timestamp + /// 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> { + /// // 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 { let query = self.create_query( "block", From 3dc727d6965de46b6ff37882960cd6c81f7860c1 Mon Sep 17 00:00:00 2001 From: Rainbow Trout <0xrainbowtrout@proton.me> Date: Mon, 24 Apr 2023 03:29:12 +0000 Subject: [PATCH 3/6] Remove empty lines --- ethers-etherscan/src/blocks.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/ethers-etherscan/src/blocks.rs b/ethers-etherscan/src/blocks.rs index 9c8442aab..371e94bc1 100644 --- a/ethers-etherscan/src/blocks.rs +++ b/ethers-etherscan/src/blocks.rs @@ -1,9 +1,7 @@ use std::collections::HashMap; use std::str::FromStr; - use ethers_core::types::BlockNumber; use serde::{Deserialize, Serialize}; - use crate::{Client, EtherscanError, Response, Result}; #[derive(Clone, Debug, Deserialize, Serialize)] From ea7208ab7dcec8addf0188bec825d462890cb0b4 Mon Sep 17 00:00:00 2001 From: Rainbow Trout <0xrainbowtrout@proton.me> Date: Thu, 27 Apr 2023 04:22:32 +0000 Subject: [PATCH 4/6] Update formatting --- ethers-etherscan/src/blocks.rs | 13 ++++++++----- ethers-etherscan/tests/it/blocks.rs | 16 ++-------------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/ethers-etherscan/src/blocks.rs b/ethers-etherscan/src/blocks.rs index 371e94bc1..0d100989a 100644 --- a/ethers-etherscan/src/blocks.rs +++ b/ethers-etherscan/src/blocks.rs @@ -1,8 +1,7 @@ -use std::collections::HashMap; -use std::str::FromStr; +use crate::{Client, EtherscanError, Response, Result}; use ethers_core::types::BlockNumber; use serde::{Deserialize, Serialize}; -use crate::{Client, EtherscanError, Response, Result}; +use std::collections::HashMap; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct BlockNumberByTimestamp { @@ -25,7 +24,11 @@ impl Client { /// 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 { + pub async fn get_block_by_timestamp( + &self, + timestamp: u64, + closest: &str + ) -> Result { let query = self.create_query( "block", "getblocknobytime", @@ -37,7 +40,7 @@ impl Client { "0" => Err(EtherscanError::BlockNumberByTimestampFailed), "1" => Ok( BlockNumberByTimestamp{ - timestamp: timestamp, + timestamp, block_number: response.result.parse::().unwrap() } ), diff --git a/ethers-etherscan/tests/it/blocks.rs b/ethers-etherscan/tests/it/blocks.rs index 9dcdfb6fe..b1487663b 100644 --- a/ethers-etherscan/tests/it/blocks.rs +++ b/ethers-etherscan/tests/it/blocks.rs @@ -7,15 +7,9 @@ use crate::*; #[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; + let block_no = client.get_block_by_timestamp(1577836800, "before").await; assert!(block_no.is_ok()); - // let block_no = block_no.result; let block_no = block_no.unwrap().block_number; assert_eq!(block_no, "9193265".parse::().unwrap()); }) @@ -26,13 +20,7 @@ async fn check_get_block_by_timestamp_before() { #[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; - // assert!(block_no.is_ok()); + 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::().unwrap()); From 540e383d53a586304469b3c2ecc1791aa111baa5 Mon Sep 17 00:00:00 2001 From: Rainbow Trout <0xrainbowtrout@proton.me> Date: Sat, 29 Apr 2023 20:43:32 +0000 Subject: [PATCH 5/6] Fix fmt --- ethers-etherscan/src/blocks.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/ethers-etherscan/src/blocks.rs b/ethers-etherscan/src/blocks.rs index 0d100989a..65ff1c449 100644 --- a/ethers-etherscan/src/blocks.rs +++ b/ethers-etherscan/src/blocks.rs @@ -27,7 +27,7 @@ impl Client { pub async fn get_block_by_timestamp( &self, timestamp: u64, - closest: &str + closest: &str, ) -> Result { let query = self.create_query( "block", @@ -38,12 +38,10 @@ impl Client { match response.status.as_str() { "0" => Err(EtherscanError::BlockNumberByTimestampFailed), - "1" => Ok( - BlockNumberByTimestamp{ - timestamp, - block_number: response.result.parse::().unwrap() - } - ), + "1" => Ok(BlockNumberByTimestamp{ + timestamp, + block_number: response.result.parse::().unwrap() + }), err => Err(EtherscanError::BadStatusCode(err.to_string())), } } From f36cda690e097dc9e7206d82f084a4dc88f31b2a Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 23 May 2023 14:09:21 +0200 Subject: [PATCH 6/6] chore: fmt --- ethers-etherscan/src/blocks.rs | 5 ++--- ethers-etherscan/tests/it/blocks.rs | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/ethers-etherscan/src/blocks.rs b/ethers-etherscan/src/blocks.rs index 65ff1c449..f009e32ba 100644 --- a/ethers-etherscan/src/blocks.rs +++ b/ethers-etherscan/src/blocks.rs @@ -9,7 +9,6 @@ pub struct BlockNumberByTimestamp { 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 @@ -38,9 +37,9 @@ impl Client { match response.status.as_str() { "0" => Err(EtherscanError::BlockNumberByTimestampFailed), - "1" => Ok(BlockNumberByTimestamp{ + "1" => Ok(BlockNumberByTimestamp { timestamp, - block_number: response.result.parse::().unwrap() + block_number: response.result.parse::().unwrap(), }), err => Err(EtherscanError::BadStatusCode(err.to_string())), } diff --git a/ethers-etherscan/tests/it/blocks.rs b/ethers-etherscan/tests/it/blocks.rs index b1487663b..9c7ee6a24 100644 --- a/ethers-etherscan/tests/it/blocks.rs +++ b/ethers-etherscan/tests/it/blocks.rs @@ -1,8 +1,7 @@ +use crate::*; use ethers_core::types::BlockNumber; use serial_test::serial; -use crate::*; - #[tokio::test] #[serial] async fn check_get_block_by_timestamp_before() {