diff --git a/ethers-providers/src/lib.rs b/ethers-providers/src/lib.rs index 628dfeae..4fe63157 100644 --- a/ethers-providers/src/lib.rs +++ b/ethers-providers/src/lib.rs @@ -182,6 +182,10 @@ pub trait Middleware: Sync + Send + Debug { self.inner().provider() } + async fn client_version(&self) -> Result { + self.inner().client_version().await.map_err(FromErr::from) + } + async fn get_block_number(&self) -> Result { self.inner().get_block_number().await.map_err(FromErr::from) } @@ -289,6 +293,16 @@ pub trait Middleware: Sync + Send + Debug { .map_err(FromErr::from) } + async fn get_block_receipts + Send + Sync>( + &self, + block: T, + ) -> Result, Self::Error> { + self.inner() + .get_block_receipts(block) + .await + .map_err(FromErr::from) + } + async fn get_gas_price(&self) -> Result { self.inner().get_gas_price().await.map_err(FromErr::from) } diff --git a/ethers-providers/src/provider.rs b/ethers-providers/src/provider.rs index 37ddbc58..fe9a92d8 100644 --- a/ethers-providers/src/provider.rs +++ b/ethers-providers/src/provider.rs @@ -50,12 +50,16 @@ use tracing_futures::Instrument; /// # } /// ``` #[derive(Clone, Debug)] -// TODO: Convert to proper struct -pub struct Provider

(P, Option

, Option, Option
); +pub struct Provider

{ + inner: P, + ens: Option

, + interval: Option, + from: Option
, +} impl

AsRef

for Provider

{ fn as_ref(&self) -> &P { - &self.0 + &self.inner } } @@ -103,11 +107,16 @@ pub enum FilterKind<'a> { impl Provider

{ /// Instantiate a new provider with a backend. pub fn new(provider: P) -> Self { - Self(provider, None, None, None) + Self { + inner: provider, + ens: None, + interval: None, + from: None, + } } pub fn with_sender(mut self, address: impl Into

) -> Self { - self.3 = Some(address.into()); + self.from = Some(address.into()); self } @@ -121,7 +130,11 @@ impl Provider

{ // https://docs.rs/tracing/0.1.22/tracing/span/struct.Span.html#in-asynchronous-code let res = async move { trace!("tx"); - let res: R = self.0.request(method, params).await.map_err(Into::into)?; + let res: R = self + .inner + .request(method, params) + .await + .map_err(Into::into)?; trace!(rx = ?serde_json::to_string(&res)?); Ok::<_, ProviderError>(res) } @@ -183,6 +196,11 @@ impl Middleware for Provider

{ // // Functions for querying the state of the blockchain + /// Returns the current client version using the `web3_clientVersion` RPC. + async fn client_version(&self) -> Result { + self.request("web3_clientVersion", ()).await + } + /// Gets the latest block number via the `eth_BlockNumber` API async fn get_block_number(&self) -> Result { self.request("eth_blockNumber", ()).await @@ -222,6 +240,17 @@ impl Middleware for Provider

{ self.request("eth_getTransactionReceipt", [hash]).await } + /// Returns all receipts for a block. + /// + /// Note that this uses the `eth_getBlockReceipts` RPC, which is + /// non-standard and currently supported by Erigon. + async fn get_block_receipts + Send + Sync>( + &self, + block: T, + ) -> Result, Self::Error> { + self.request("eth_getBlockReceipts", [block.into()]).await + } + /// Gets the current gas price as estimated by the node async fn get_gas_price(&self) -> Result { self.request("eth_gasPrice", ()).await @@ -301,7 +330,7 @@ impl Middleware for Provider

{ _: Option, ) -> Result, ProviderError> { if tx.from.is_none() { - tx.from = self.3; + tx.from = self.from; } if tx.gas.is_none() { @@ -335,7 +364,7 @@ impl Middleware for Provider

{ /// The JSON-RPC provider is at the bottom-most position in the middleware stack. Here we check /// if it has the key for the sender address unlocked, as well as supports the `eth_sign` call. async fn is_signer(&self) -> bool { - match self.3 { + match self.from { Some(sender) => self.sign(vec![], &sender).await.is_ok(), None => false, } @@ -675,7 +704,7 @@ impl Provider

{ selector: Selector, ) -> Result { // Get the ENS address, prioritize the local override variable - let ens_addr = self.1.unwrap_or(ens::ENS_ADDRESS); + let ens_addr = self.ens.unwrap_or(ens::ENS_ADDRESS); // first get the resolver responsible for this name // the call will return a Bytes array which we convert to an address @@ -700,7 +729,7 @@ impl Provider

{ /// ganache-only function for mining empty blocks pub async fn mine(&self, num_blocks: usize) -> Result<(), ProviderError> { for _ in 0..num_blocks { - self.0 + self.inner .request::<_, U256>("evm_mine", None::<()>) .await .map_err(Into::into)?; @@ -710,21 +739,21 @@ impl Provider

{ /// Sets the ENS Address (default: mainnet) pub fn ens>(mut self, ens: T) -> Self { - self.1 = Some(ens.into()); + self.ens = Some(ens.into()); self } /// Sets the default polling interval for event filters and pending transactions /// (default: 7 seconds) pub fn interval>(mut self, interval: T) -> Self { - self.2 = Some(interval.into()); + self.interval = Some(interval.into()); self } /// Gets the polling interval which the provider currently uses for event filters /// and pending transactions (default: 7 seconds) pub fn get_interval(&self) -> Duration { - self.2.unwrap_or(DEFAULT_POLL_INTERVAL) + self.interval.unwrap_or(DEFAULT_POLL_INTERVAL) } } @@ -791,12 +820,7 @@ impl TryFrom<&str> for Provider { type Error = ParseError; fn try_from(src: &str) -> Result { - Ok(Provider( - HttpProvider::new(Url::parse(src)?), - None, - None, - None, - )) + Ok(Provider::new(HttpProvider::new(Url::parse(src)?))) } } diff --git a/ethers-providers/tests/provider.rs b/ethers-providers/tests/provider.rs index 08a586e0..ba35d2a6 100644 --- a/ethers-providers/tests/provider.rs +++ b/ethers-providers/tests/provider.rs @@ -40,6 +40,21 @@ mod eth_tests { .is_none()); } + #[tokio::test] + async fn client_version() { + let provider = Provider::::try_from( + "https://rinkeby.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27", + ) + .unwrap(); + + // e.g., Geth/v1.10.6-omnibus-1af33248/linux-amd64/go1.16.6 + assert!(provider + .client_version() + .await + .expect("Could not make web3_clientVersion call to provider") + .starts_with("Geth/v")); + } + // Without TLS this would error with "TLS Support not compiled in" #[tokio::test] async fn ssl_websocket() {