Repository: gakonst/ethers-rs
Pull request #1878 (2022-11-22 03:27:44)

chore(deps): bump cargo_metadata from 0.15.1 to 0.15.2
Bumps [cargo_metadata](https://github.com/oli-obk/cargo_metadata) from 0.15.1 to 0.15.2.
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/oli-obk/cargo_metadata/commit/8319bd683d02a6b977beccbc66a5730ee6b95de3"><code>8319bd6</code></a> Publish new version</li>
<li><a href="https://github.com/oli-obk/cargo_metadata/commit/711710b5e9bd2bed6fb28182d5b41b6c90ebdc10"><code>711710b</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/oli-obk/cargo_metadata/issues/213">#213</a> from messense/fix-212</li>
<li><a href="https://github.com/oli-obk/cargo_metadata/commit/ad8bf920a42c1a487a118875c0a37cc63d5e0174"><code>ad8bf92</code></a> Add <code>#[serde(default)]</code> to <code>Artifact::manifest_path</code></li>
<li><a href="https://github.com/oli-obk/cargo_metadata/commit/1af69dfe9249ffa732986cb13ba650603551f69b"><code>1af69df</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/oli-obk/cargo_metadata/issues/211">#211</a> from messense/stderr</li>
<li><a href="https://github.com/oli-obk/cargo_metadata/commit/d9cd00d68864b8ce880f79483e30102f5efd982b"><code>d9cd00d</code></a> Allow <code>MetadataCommand</code> to inherit stderr from parent</li>
<li><a href="https://github.com/oli-obk/cargo_metadata/commit/a0f55ac03a14f3842096a4f8cd61064a17f670da"><code>a0f55ac</code></a> Disable semver again</li>
<li>See full diff in <a href="https://github.com/oli-obk/cargo_metadata/compare/v0.15.1...v0.15.2">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=cargo_metadata&package-manager=cargo&previous-version=0.15.1&new-version=0.15.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 992be8ad..59f25388 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 ### Unreleased
 
+-   An `Event` builder can be instantiated specifying the event filter type, without the need to instantiate a contract.
 -   Add 'ethers_core::types::OpCode' and use in 'ethers_core::types::VMOperation' [1857](https://github.com/gakonst/ethers-rs/issues/1857)
 -   Remove rust_decimals dependency for ethers-core
 -   Add support for numbers greater than 2^96 for `ethers_core::utils::parse_units` [#1822](https://github.com/gakonst/ethers-rs/issues/1822)
@@ -348,4 +349,4 @@
 ### 0.5.3
 
 -   Added Time Lagged middleware
-    [#457](https://github.com/gakonst/ethers-rs/pull/457)
+    [#457](https://github.com/gakonst/ethers-rs/pull/457)
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index d5784d56..db879812 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -372,9 +372,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
 
 [[package]]
 name = "bytes"
-version = "1.2.1"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
+checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
 dependencies = [
  "serde",
 ]
@@ -420,9 +420,9 @@ dependencies = [
 
 [[package]]
 name = "cargo_metadata"
-version = "0.15.1"
+version = "0.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "406c859255d568f4f742b3146d51851f3bfd49f734a2c289d9107c4395ee0062"
+checksum = "982a0cf6a99c350d7246035613882e376d58cebe571785abc5da4f648d53ac0a"
 dependencies = [
  "camino",
  "cargo-platform",
@@ -3013,11 +3013,10 @@ dependencies = [
 
 [[package]]
 name = "rayon"
-version = "1.5.3"
+version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
+checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b"
 dependencies = [
- "autocfg",
  "crossbeam-deque",
  "either",
  "rayon-core",
@@ -3025,9 +3024,9 @@ dependencies = [
 
 [[package]]
 name = "rayon-core"
-version = "1.9.2"
+version = "1.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f51245e1e62e1f1629cbfec37b5793bbabcaeb90f30e94d2ba03564687353e4"
+checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
 dependencies = [
  "crossbeam-channel",
  "crossbeam-deque",
@@ -3477,9 +3476,9 @@ dependencies = [
 
 [[package]]
 name = "serde-aux"
-version = "4.1.0"
+version = "4.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb6a3148cb21f1afb585b9ce6aeea9e58bd02c37ddb336277af10396ca3574fd"
+checksum = "c599b3fd89a75e0c18d6d2be693ddb12cccaf771db4ff9e39097104808a014c0"
 dependencies = [
  "serde",
  "serde_json",
diff --git a/Cargo.toml b/Cargo.toml
index f6c40001..d2eea1dd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -118,7 +118,7 @@ serde = { version = "1.0.124", features = ["derive"] }
 serde_json = "1.0.64"
 tokio = { version = "1.18", features = ["macros", "rt-multi-thread"] }
 hex = "0.4.3"
-bytes = "1.2.1"
+bytes = "1.3.0"
 
 # profile for the wasm example
 [profile.release.package.ethers-wasm]
diff --git a/ethers-contract/src/contract.rs b/ethers-contract/src/contract.rs
index 2a4e2c8c..7da01b7c 100644
--- a/ethers-contract/src/contract.rs
+++ b/ethers-contract/src/contract.rs
@@ -191,6 +191,19 @@ impl<M> Contract<M> {
     }
 }
 
+impl<M: Middleware> Contract<M> {
+    /// Returns an [`Event`](crate::builders::Event) builder for the provided event.
+    /// This function operates in a static context, then it does not require a `self`
+    /// to reference to instantiate an [`Event`](crate::builders::Event) builder.
+    pub fn event_of_type<'a, D: EthEvent>(client: &'a Arc<M>) -> Event<'a, M, D> {
+        Event {
+            provider: client,
+            filter: Filter::new().event(&D::abi_signature()),
+            datatype: PhantomData,
+        }
+    }
+}
+
 impl<M: Middleware> Contract<M> {
     /// Creates a new contract from the provided client, abi and address
     pub fn new(
diff --git a/ethers-contract/src/event.rs b/ethers-contract/src/event.rs
index b4a922d4..23864128 100644
--- a/ethers-contract/src/event.rs
+++ b/ethers-contract/src/event.rs
@@ -2,8 +2,8 @@
 
 use crate::{log::LogMeta, stream::EventStream, ContractError, EthLogDecode};
 use ethers_core::{
-    abi::{Detokenize, RawLog},
-    types::{BlockNumber, Filter, Log, Topic, H256},
+    abi::{Address, Detokenize, RawLog},
+    types::{BlockNumber, Filter, Log, Topic, ValueOrArray, H256},
 };
 use ethers_providers::{FilterWatcher, Middleware, PubsubClient, SubscriptionStream};
 use std::{borrow::Cow, marker::PhantomData};
@@ -108,6 +108,12 @@ impl<M, D: EthLogDecode> Event<'_, M, D> {
         self.filter.topics[3] = Some(topic.into());
         self
     }
+
+    /// Sets the filter's address.
+    pub fn address(mut self, address: ValueOrArray<Address>) -> Self {
+        self.filter = self.filter.address(address);
+        self
+    }
 }
 
 impl<'a, M, D> Event<'a, M, D>
diff --git a/ethers-contract/src/multicall/multicall_contract.rs b/ethers-contract/src/multicall/multicall_contract.rs
index 661ac998..d3a15266 100644
--- a/ethers-contract/src/multicall/multicall_contract.rs
+++ b/ethers-contract/src/multicall/multicall_contract.rs
@@ -8,6 +8,22 @@ pub mod multicall_3 {
     mod ethers_contract {
         pub use crate::*;
     }
+
+    // This is a hack to guarantee all ethers-derive macros can find the types.
+    // See [`ethers_core::macros::determine_ethers_crates`]
+    #[doc(hidden)]
+    mod ethers {
+        pub mod core {
+            pub use ethers_core::*;
+        }
+        pub mod contract {
+            pub use crate::*;
+        }
+        pub mod providers {
+            pub use ethers_providers::*;
+        }
+    }
+
     use self::ethers_contract::{
         builders::{ContractCall, Event},
         Contract, Lazy,
@@ -17,6 +33,7 @@ pub mod multicall_3 {
         types::*,
     };
     use ethers_providers::Middleware;
+
     #[doc = "Multicall3 was auto-generated with ethers-rs Abigen. More information at: https://github.com/gakonst/ethers-rs"]
     use std::sync::Arc;
     # [rustfmt :: skip] const __ABI : & str = "[{\"type\":\"function\",\"name\":\"aggregate\",\"inputs\":[{\"internalType\":\"struct Multicall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"address\"},{\"type\":\"bytes\"}]}],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"returnData\",\"type\":\"bytes[]\"}],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"aggregate3\",\"inputs\":[{\"internalType\":\"struct Multicall3.Call3[]\",\"name\":\"calls\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"address\"},{\"type\":\"bool\"},{\"type\":\"bytes\"}]}],\"outputs\":[{\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"bool\"},{\"type\":\"bytes\"}]}],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"aggregate3Value\",\"inputs\":[{\"internalType\":\"struct Multicall3.Call3Value[]\",\"name\":\"calls\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"address\"},{\"type\":\"bool\"},{\"type\":\"uint256\"},{\"type\":\"bytes\"}]}],\"outputs\":[{\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"bool\"},{\"type\":\"bytes\"}]}],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"blockAndAggregate\",\"inputs\":[{\"internalType\":\"struct Multicall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"address\"},{\"type\":\"bytes\"}]}],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"bool\"},{\"type\":\"bytes\"}]}],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"getBasefee\",\"inputs\":[],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"basefee\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getBlockHash\",\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getBlockNumber\",\"inputs\":[],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getChainId\",\"inputs\":[],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"chainid\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getCurrentBlockCoinbase\",\"inputs\":[],\"outputs\":[{\"internalType\":\"address\",\"name\":\"coinbase\",\"type\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getCurrentBlockDifficulty\",\"inputs\":[],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"difficulty\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getCurrentBlockGasLimit\",\"inputs\":[],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"gaslimit\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getCurrentBlockTimestamp\",\"inputs\":[],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getEthBalance\",\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getLastBlockHash\",\"inputs\":[],\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"tryAggregate\",\"inputs\":[{\"internalType\":\"bool\",\"name\":\"requireSuccess\",\"type\":\"bool\"},{\"internalType\":\"struct Multicall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"address\"},{\"type\":\"bytes\"}]}],\"outputs\":[{\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"bool\"},{\"type\":\"bytes\"}]}],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"tryBlockAndAggregate\",\"inputs\":[{\"internalType\":\"bool\",\"name\":\"requireSuccess\",\"type\":\"bool\"},{\"internalType\":\"struct Multicall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"address\"},{\"type\":\"bytes\"}]}],\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"struct Multicall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\",\"components\":[{\"type\":\"bool\"},{\"type\":\"bytes\"}]}],\"stateMutability\":\"payable\"}]" ;
diff --git a/ethers-core/Cargo.toml b/ethers-core/Cargo.toml
index eb64e1e3..e92e2929 100644
--- a/ethers-core/Cargo.toml
+++ b/ethers-core/Cargo.toml
@@ -29,14 +29,14 @@ chrono = { version = "0.4", default-features = false }
 serde = { version = "1.0.124", default-features = false, features = ["derive"] }
 serde_json = { version = "1.0.64", default-features = false }
 thiserror = { version = "1.0", default-features = false }
-bytes = { version = "1.2.1", features = ["serde"] }
+bytes = { version = "1.3.0", features = ["serde"] }
 hex = { version = "0.4.3", default-features = false, features = ["std"] }
 once_cell = { version = "1.16.0", optional = true }
 unicode-xid = "0.2.4"
 strum = { version = "0.24", features = ["derive"] }
 
 # macros feature enabled dependencies
-cargo_metadata = { version = "0.15.1", optional = true }
+cargo_metadata = { version = "0.15.2", optional = true }
 
 # eip712 feature enabled dependencies
 convert_case = { version = "0.6.0", optional = true }
diff --git a/ethers-core/src/utils/mod.rs b/ethers-core/src/utils/mod.rs
index 32aa1f84..21da3047 100644
--- a/ethers-core/src/utils/mod.rs
+++ b/ethers-core/src/utils/mod.rs
@@ -35,7 +35,10 @@ use crate::types::{Address, Bytes, ParseI256Error, I256, U256};
 use elliptic_curve::sec1::ToEncodedPoint;
 use ethabi::ethereum_types::FromDecStrErr;
 use k256::{ecdsa::SigningKey, PublicKey as K256PublicKey};
-use std::convert::{TryFrom, TryInto};
+use std::{
+    convert::{TryFrom, TryInto},
+    fmt,
+};
 use thiserror::Error;
 
 /// Re-export of serde-json
@@ -80,7 +83,7 @@ pub const EIP1559_FEE_ESTIMATION_THRESHOLD_MAX_CHANGE: i64 = 200;
 
 /// This enum holds the numeric types that a possible to be returned by `parse_units` and
 /// that are taken by `format_units`.
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
 pub enum ParseUnits {
     U256(U256),
     I256(I256),
@@ -95,6 +98,15 @@ impl From<ParseUnits> for U256 {
     }
 }
 
+impl fmt::Display for ParseUnits {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            ParseUnits::U256(val) => val.fmt(f),
+            ParseUnits::I256(val) => val.fmt(f),
+        }
+    }
+}
+
 macro_rules! construct_format_units_from {
     ($( $t:ty[$convert:ident] ),*) => {
         $(
diff --git a/ethers-etherscan/Cargo.toml b/ethers-etherscan/Cargo.toml
index 7a71e0f9..84347f42 100644
--- a/ethers-etherscan/Cargo.toml
+++ b/ethers-etherscan/Cargo.toml
@@ -23,7 +23,7 @@ ethers-solc = { version = "^1.0.0", path = "../ethers-solc", default-features =
 reqwest = { version = "0.11.13", default-features = false, features = ["json"] }
 serde = { version = "1.0.124", default-features = false, features = ["derive"] }
 serde_json = { version = "1.0.64", default-features = false }
-serde-aux = { version = "4.1.0", default-features = false }
+serde-aux = { version = "4.1.2", default-features = false }
 thiserror = "1.0"
 tracing = "0.1.37"
 semver = "1.0.14"
diff --git a/ethers-providers/Cargo.toml b/ethers-providers/Cargo.toml
index e4d189f2..7c25401e 100644
--- a/ethers-providers/Cargo.toml
+++ b/ethers-providers/Cargo.toml
@@ -39,7 +39,7 @@ pin-project = { version = "1.0.11", default-features = false }
 tracing = { version = "0.1.37", default-features = false }
 tracing-futures = { version = "0.2.5", default-features = false, features = ["std-future"] }
 
-bytes = { version = "1.2.1", default-features = false, optional = true }
+bytes = { version = "1.3.0", default-features = false, optional = true }
 once_cell = "1.16.0"
 hashers = "1.0.1"
 
diff --git a/ethers-providers/src/transports/mod.rs b/ethers-providers/src/transports/mod.rs
index 2abf77c7..5623675d 100644
--- a/ethers-providers/src/transports/mod.rs
+++ b/ethers-providers/src/transports/mod.rs
@@ -38,9 +38,7 @@ pub use quorum::{JsonRpcClientWrapper, Quorum, QuorumError, QuorumProvider, Weig
 mod rw;
 pub use rw::{RwClient, RwClientError};
 
-#[cfg(not(target_arch = "wasm32"))]
 mod retry;
-#[cfg(not(target_arch = "wasm32"))]
 pub use retry::*;
 
 mod mock;
diff --git a/ethers-providers/src/transports/retry.rs b/ethers-providers/src/transports/retry.rs
index 947a0495..11b7386b 100644
--- a/ethers-providers/src/transports/retry.rs
+++ b/ethers-providers/src/transports/retry.rs
@@ -206,6 +206,7 @@ pub enum RetryClientError {
     TimeoutError,
     #[error(transparent)]
     SerdeJson(serde_json::Error),
+    TimerError,
 }
 
 impl std::fmt::Display for RetryClientError {
@@ -220,11 +221,13 @@ impl From<RetryClientError> for ProviderError {
             RetryClientError::ProviderError(err) => err,
             RetryClientError::TimeoutError => ProviderError::JsonRpcClientError(Box::new(src)),
             RetryClientError::SerdeJson(err) => err.into(),
+            RetryClientError::TimerError => ProviderError::JsonRpcClientError(Box::new(src)),
         }
     }
 }
 
 #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
+#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
 impl<T> JsonRpcClient for RetryClient<T>
 where
     T: JsonRpcClient + 'static,
@@ -310,6 +313,13 @@ where
                 next_backoff += Duration::from_secs(seconds_to_wait_for_compute_budget);
 
                 trace!("retrying and backing off for {:?}", next_backoff);
+
+                #[cfg(target_arch = "wasm32")]
+                wasm_timer::Delay::new(next_backoff)
+                    .await
+                    .map_err(|_| RetryClientError::TimerError)?;
+
+                #[cfg(not(target_arch = "wasm32"))]
                 tokio::time::sleep(next_backoff).await;
             } else {
                 let err: ProviderError = err.into();
@@ -346,6 +356,12 @@ impl RetryPolicy<ClientError> for HttpRateLimitRetryPolicy {
                 if *code == 429 {
                     return true
                 }
+
+                // alternative alchemy error for specific IPs
+                if *code == -32016 && message.contains("rate limit") {
+                    return true
+                }
+
                 match message.as_str() {
                     // this is commonly thrown by infura and is apparently a load balancer issue, see also <https://github.com/MetaMask/metamask-extension/issues/7234>
                     "header not found" => true,
@@ -407,9 +423,15 @@ fn compute_unit_offset_in_secs(
 /// `request::Error::TimedOut`
 fn maybe_connectivity(err: &ProviderError) -> bool {
     if let ProviderError::HTTPError(reqwest_err) = err {
-        if reqwest_err.is_timeout() || reqwest_err.is_connect() {
+        if reqwest_err.is_timeout() {
+            return true
+        }
+
+        #[cfg(not(target_arch = "wasm32"))]
+        if reqwest_err.is_connect() {
             return true
         }
+
         // Error HTTP codes (5xx) are considered connectivity issues and will prompt retry
         if let Some(status) = reqwest_err.status() {
             let code = status.as_u16();
@@ -494,4 +516,14 @@ mod tests {
         let backoff = HttpRateLimitRetryPolicy.backoff_hint(&err);
         assert!(backoff.is_none());
     }
+
+    #[test]
+    fn test_alchemy_ip_rate_limit() {
+        let s = "{\"code\":-32016,\"message\":\"Your IP has exceeded its requests per second capacity. To increase your rate limits, please sign up for a free Alchemy account at https://www.alchemy.com/optimism.\"}";
+        let err: JsonRpcError = serde_json::from_str(s).unwrap();
+        let err = ClientError::JsonRpcError(err);
+
+        let should_retry = HttpRateLimitRetryPolicy::default().should_retry(&err);
+        assert!(should_retry);
+    }
 }
diff --git a/ethers-solc/Cargo.toml b/ethers-solc/Cargo.toml
index a3f9b5a5..a804559f 100644
--- a/ethers-solc/Cargo.toml
+++ b/ethers-solc/Cargo.toml
@@ -38,7 +38,7 @@ fs_extra = { version = "1.2.0", optional = true }
 sha2 = { version = "0.10.6", default-features = false, optional = true }
 dunce = "1.0.3"
 solang-parser = { default-features = false, version = "=0.1.18" }
-rayon = "1.5.3"
+rayon = "1.6.0"
 rand = { version = "0.8.5", optional = true }
 path-slash = "0.2.1"
 cfg-if = "1.0.0"
diff --git a/examples/subscribe_events_by_type.rs b/examples/subscribe_events_by_type.rs
new file mode 100644
index 00000000..9a3aa56f
--- /dev/null
+++ b/examples/subscribe_events_by_type.rs
@@ -0,0 +1,52 @@
+use ethers::{contract::Contract, prelude::*};
+use std::{error::Error, sync::Arc};
+abigen!(
+    AggregatorInterface,
+    r#"[
+        event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt)
+    ]"#,
+);
+
+const PRICE_FEED_1: &str = "0x7de93682b9b5d80d45cd371f7a14f74d49b0914c";
+const PRICE_FEED_2: &str = "0x0f00392fcb466c0e4e4310d81b941e07b4d5a079";
+const PRICE_FEED_3: &str = "0xebf67ab8cff336d3f609127e8bbf8bd6dd93cd81";
+
+/// Subscribe to a typed event stream without requiring a `Contract` instance.
+/// In this example we subscribe Chainlink price feeds and filter out them
+/// by address.
+/// -------------------------------------------------------------------------------
+/// In order to run this example you need to include Ws and TLS features
+/// Run this example with
+/// `cargo run -p ethers --example subscribe_events_by_type --features="ws","rustls"`
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+    let client = get_client().await;
+    let client = Arc::new(client);
+
+    // Build an Event by type. We are not tied to a contract instance. We use builder functions to
+    // refine the event filter
+    let event = Contract::event_of_type::<AnswerUpdatedFilter>(&client)
+        .from_block(16022082)
+        .to_block(16022282)
+        .address(ValueOrArray::Array(vec![
+            PRICE_FEED_1.parse()?,
+            PRICE_FEED_2.parse()?,
+            PRICE_FEED_3.parse()?,
+        ]));
+
+    let mut stream = event.subscribe_with_meta().await?;
+
+    // Note that `log` has type AnswerUpdatedFilter
+    while let Some(Ok((log, meta))) = stream.next().await {
+        println!("{:?}", log);
+        println!("{:?}", meta)
+    }
+
+    Ok(())
+}
+
+async fn get_client() -> Provider<Ws> {
+    Provider::<Ws>::connect("wss://mainnet.infura.io/ws/v3/c60b0bb42f8a4c6481ecd229eddaca27")
+        .await
+        .unwrap()
+}


Test patch:
diff --git a/ethers-contract/tests/it/contract.rs b/ethers-contract/tests/it/contract.rs
index 5968b19d..0c28bcde 100644
--- a/ethers-contract/tests/it/contract.rs
+++ b/ethers-contract/tests/it/contract.rs
@@ -6,7 +6,7 @@ use ethers_core::types::{Filter, ValueOrArray, H256};
 #[cfg(not(feature = "celo"))]
 mod eth_tests {
     use super::*;
-    use ethers_contract::{LogMeta, Multicall, MulticallVersion};
+    use ethers_contract::{EthEvent, LogMeta, Multicall, MulticallVersion};
     use ethers_core::{
         abi::{encode, Detokenize, Token, Tokenizable},
         types::{transaction::eip712::Eip712, Address, BlockId, Bytes, I256, U256},
@@ -324,6 +324,21 @@ mod eth_tests {
         assert_eq!(log_2.address, contract_2.address());
     }
 
+    #[tokio::test]
+    async fn build_event_of_type() {
+        abigen!(
+            AggregatorInterface,
+            r#"[
+                event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt)
+            ]"#,
+        );
+
+        let anvil = Anvil::new().spawn();
+        let client = connect(&anvil, 0);
+        let event = ethers_contract::Contract::event_of_type::<AnswerUpdatedFilter>(&client);
+        assert_eq!(event.filter, Filter::new().event(&AnswerUpdatedFilter::abi_signature()));
+    }
+
     #[tokio::test]
     async fn signer_on_node() {
         let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol");