Repository: gakonst/ethers-rs
Pull request #1875 (2022-11-21 03:11:54)

chore(deps): bump rayon from 1.5.3 to 1.6.0
Bumps [rayon](https://github.com/rayon-rs/rayon) from 1.5.3 to 1.6.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/rayon-rs/rayon/blob/master/RELEASES.md">rayon's changelog</a>.</em></p>
<blockquote>
<h1>Release rayon 1.6.0 / rayon-core 1.10.0 (2022-11-18)</h1>
<ul>
<li>The minimum supported <code>rustc</code> is now 1.56.</li>
<li>The new <code>IndexedParallelIterator::fold_chunks</code> and <code>fold_chunks_with</code> methods
work like <code>ParallelIterator::fold</code> and <code>fold_with</code> with fixed-size chunks of
items. This may be useful for predictable batching performance, without the
allocation overhead of <code>IndexedParallelIterator::chunks</code>.</li>
<li>New &quot;broadcast&quot; methods run a given function on all threads in the pool.
These run at a sort of reduced priority after each thread has exhausted their
local work queue, but before they attempt work-stealing from other threads.
<ul>
<li>The global <code>broadcast</code> function and <code>ThreadPool::broadcast</code> method will
block until completion, returning a <code>Vec</code> of all return values.</li>
<li>The global <code>spawn_broadcast</code> function and methods on <code>ThreadPool</code>, <code>Scope</code>,
and <code>ScopeFifo</code> will run detached, without blocking the current thread.</li>
</ul>
</li>
<li>Panicking methods now use <code>#[track_caller]</code> to report the caller's location.</li>
<li>Fixed a truncated length in <code>vec::Drain</code> when given an empty range.</li>
</ul>
<h2>Contributors</h2>
<p>Thanks to all of the contributors for this release!</p>
<ul>
<li><a href="https://github.com/cuviper"><code>@​cuviper</code></a></li>
<li><a href="https://github.com/idanmuze"><code>@​idanmuze</code></a></li>
<li><a href="https://github.com/JoeyBF"><code>@​JoeyBF</code></a></li>
<li><a href="https://github.com/JustForFun88"><code>@​JustForFun88</code></a></li>
<li><a href="https://github.com/kianmeng"><code>@​kianmeng</code></a></li>
<li><a href="https://github.com/kornelski"><code>@​kornelski</code></a></li>
<li><a href="https://github.com/ritchie46"><code>@​ritchie46</code></a></li>
<li><a href="https://github.com/ryanrussell"><code>@​ryanrussell</code></a></li>
<li><a href="https://github.com/steffahn"><code>@​steffahn</code></a></li>
<li><a href="https://github.com/TheIronBorn"><code>@​TheIronBorn</code></a></li>
<li><a href="https://github.com/willcrozi"><code>@​willcrozi</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/rayon-rs/rayon/commit/60cdb431731d471add01281f63ccf8f8bc0451f3"><code>60cdb43</code></a> Merge <a href="https://github-redirect.dependabot.com/rayon-rs/rayon/issues/686">#686</a></li>
<li><a href="https://github.com/rayon-rs/rayon/commit/efeeace277a08b1cc5c2cf076ff2e7e948030e6f"><code>efeeace</code></a> Release rayon 1.2.0 / rayon-core 1.6.0</li>
<li><a href="https://github.com/rayon-rs/rayon/commit/d993d7a666722578a3e793e0fb34e201dd759f01"><code>d993d7a</code></a> Merge <a href="https://github-redirect.dependabot.com/rayon-rs/rayon/issues/683">#683</a></li>
<li><a href="https://github.com/rayon-rs/rayon/commit/2720d8d55bc2f9df9ee34ccd5e75c7b65fb0754c"><code>2720d8d</code></a> Merge <a href="https://github-redirect.dependabot.com/rayon-rs/rayon/issues/682">#682</a></li>
<li><a href="https://github.com/rayon-rs/rayon/commit/c6a294b62f299c86f25c2a421169c1ee9938cd4d"><code>c6a294b</code></a> impl ParallelExtend&lt;()&gt; for ()</li>
<li><a href="https://github.com/rayon-rs/rayon/commit/9bfb3c6d55c7e05f23f7d9979808f1645539be78"><code>9bfb3c6</code></a> clippy::inconsistent_digit_grouping</li>
<li><a href="https://github.com/rayon-rs/rayon/commit/3cd762e22589e17e73417f01f32ba01736be1929"><code>3cd762e</code></a> clippy::toplevel_ref_arg</li>
<li><a href="https://github.com/rayon-rs/rayon/commit/b216c3e0eba85b9e67897eee6b3bffc6c4dba467"><code>b216c3e</code></a> Update bare traits to <code>dyn Trait</code></li>
<li><a href="https://github.com/rayon-rs/rayon/commit/58b0275a14987dea37c9d06cc1b3864de516a1fd"><code>58b0275</code></a> Once:new() is const-stable since Rust 1.24</li>
<li><a href="https://github.com/rayon-rs/rayon/commit/83b67e27f254c5bde039fdc0ea0840f309ce5f73"><code>83b67e2</code></a> Merge <a href="https://github-redirect.dependabot.com/rayon-rs/rayon/issues/673">#673</a></li>
<li>Additional commits viewable in <a href="https://github.com/rayon-rs/rayon/compare/v1.5.3...rayon-core-v1.6.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=rayon&package-manager=cargo&previous-version=1.5.3&new-version=1.6.0)](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..d4259024 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",
 ]
@@ -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",
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..889e287b 100644
--- a/ethers-core/Cargo.toml
+++ b/ethers-core/Cargo.toml
@@ -29,7 +29,7 @@ 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"
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-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");