Repository: gakonst/ethers-rs
Pull request #644 (2021-12-03 17:24:05)

feat: add moonbeam support
<!--
Thank you for your Pull Request. Please provide a description above and review
the requirements below.

Bug fixes and new features should include tests.

Contributors guide: https://github.com/gakonst/ethers-rs/blob/master/CONTRIBUTING.md

The contributors guide includes instructions for running rustfmt and building the
documentation.
-->

## Motivation
Add utils for polkadot's [moonbeam](https://moonbeam.network/) chain
* chain ids
* dev keys
* example
<!--
Explain the context and why you're making that change. What is the problem
you're trying to solve? In some cases there is not a problem and this can be
thought of as being the motivation for your change.
-->

## Solution

<!--
Summarize the solution and provide any necessary context needed to understand
the code change.
-->

## PR Checklist

- [ ] Added Tests
- [ ] Added Documentation
- [ ] Updated the changelog



Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3e0c2324..d8f551e7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -129,7 +129,7 @@ jobs:
       - name: cargo fmt
         run: cargo +nightly fmt --all -- --check
       - name: cargo clippy
-        run: cargo +nightly clippy --all --all-features -- -D warnings
+        run: cargo +nightly clippy --all-features -- -D warnings
 
   wasm:
     name: WASM
diff --git a/Cargo.lock b/Cargo.lock
index 344c9fd1..55c0a183 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -78,9 +78,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.48"
+version = "1.0.51"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62e1f47f7dc0422027a4e370dd4548d4d66b26782e513e98dca1e689e058a80e"
+checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203"
 
 [[package]]
 name = "arrayref"
@@ -196,9 +196,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
 
 [[package]]
 name = "base64ct"
-version = "1.2.0"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "392c772b012d685a640cdad68a5a21f4a45e696f85a2c2c907aab2fe49a91e19"
+checksum = "43a46022bae2c3bc5a17c2d45d59c1233ce0e2cca9ae9b92e92e9ce529874177"
 
 [[package]]
 name = "bech32"
@@ -437,9 +437,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "2.33.3"
+version = "2.34.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
+checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
 dependencies = [
  "bitflags",
  "textwrap",
@@ -575,6 +575,16 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
+dependencies = [
+ "cfg-if 1.0.0",
+ "wasm-bindgen",
+]
+
 [[package]]
 name = "const-oid"
 version = "0.6.2"
@@ -583,9 +593,9 @@ checksum = "9d6f2aa4d0537bcc1c74df8755072bd31c1ef1a3a1b85a68e8404a8c353b7b8b"
 
 [[package]]
 name = "const-oid"
-version = "0.7.0"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d355758f44afa81c21e66e1301d47cbffbbcde4b405cbe46b8b19f213abf9f60"
+checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3"
 
 [[package]]
 name = "constant_time_eq"
@@ -626,9 +636,9 @@ dependencies = [
 
 [[package]]
 name = "crc32fast"
-version = "1.2.2"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3825b1e8580894917dc4468cb634a1b4e9745fddc854edad72d9c04644c0319f"
+checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836"
 dependencies = [
  "cfg-if 1.0.0",
 ]
@@ -726,9 +736,9 @@ dependencies = [
 
 [[package]]
 name = "der"
-version = "0.4.4"
+version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28e98c534e9c8a0483aa01d6f6913bc063de254311bd267c9cf535e9b70e15b2"
+checksum = "79b71cca7d95d7681a4b3b9cdf63c8dbc3730d0584c2c74e31416d64a90493f4"
 dependencies = [
  "const-oid 0.6.2",
 ]
@@ -739,7 +749,7 @@ version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c"
 dependencies = [
- "const-oid 0.7.0",
+ "const-oid 0.7.1",
 ]
 
 [[package]]
@@ -799,7 +809,7 @@ version = "0.12.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "43ee23aa5b4f68c7a092b5c3beb25f50c406adc75e2363634f242f28ab255372"
 dependencies = [
- "der 0.4.4",
+ "der 0.4.5",
  "elliptic-curve 0.10.6",
  "hmac",
  "signature",
@@ -1179,6 +1189,7 @@ dependencies = [
  "colored",
  "ethers-core",
  "futures-util",
+ "getrandom 0.2.3",
  "glob",
  "hex",
  "home",
@@ -1197,6 +1208,23 @@ dependencies = [
  "walkdir",
 ]
 
+[[package]]
+name = "ethers-wasm"
+version = "0.1.0"
+dependencies = [
+ "console_error_panic_hook",
+ "ethers",
+ "hex",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-bindgen-test",
+ "web-sys",
+ "wee_alloc",
+]
+
 [[package]]
 name = "fake-simd"
 version = "0.1.2"
@@ -1801,6 +1829,12 @@ version = "2.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
 
+[[package]]
+name = "memory_units"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3"
+
 [[package]]
 name = "mime"
 version = "0.3.16"
@@ -2124,7 +2158,7 @@ version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ee3ef9b64d26bad0536099c816c6734379e45bbd5f14798def6809e5cc350447"
 dependencies = [
- "der 0.4.4",
+ "der 0.4.5",
  "spki",
 ]
 
@@ -2629,9 +2663,9 @@ dependencies = [
 
 [[package]]
 name = "ryu"
-version = "1.0.5"
+version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
+checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568"
 
 [[package]]
 name = "salsa20"
@@ -2661,6 +2695,12 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "scoped-tls"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
+
 [[package]]
 name = "scopeguard"
 version = "1.1.0"
@@ -2771,9 +2811,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.71"
+version = "1.0.72"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "063bf466a64011ac24040a49009724ee60a57da1b437617ceb32e53ad61bfb19"
+checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527"
 dependencies = [
  "itoa",
  "ryu",
@@ -2955,7 +2995,7 @@ version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5c01a0c15da1b0b0e1494112e7af814a678fec9bd157881b49beac661e9b6f32"
 dependencies = [
- "der 0.4.4",
+ "der 0.4.5",
 ]
 
 [[package]]
@@ -3507,6 +3547,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
 dependencies = [
  "cfg-if 1.0.0",
+ "serde",
+ "serde_json",
  "wasm-bindgen-macro",
 ]
 
@@ -3566,6 +3608,30 @@ version = "0.2.78"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
 
+[[package]]
+name = "wasm-bindgen-test"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96f1aa7971fdf61ef0f353602102dbea75a56e225ed036c1e3740564b91e6b7e"
+dependencies = [
+ "console_error_panic_hook",
+ "js-sys",
+ "scoped-tls",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-bindgen-test-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-test-macro"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6006f79628dfeb96a86d4db51fbf1344cd7fd8408f06fc9aa3c84913a4789688"
+dependencies = [
+ "proc-macro2",
+ "quote",
+]
+
 [[package]]
 name = "wasm-timer"
 version = "0.2.5"
@@ -3620,6 +3686,18 @@ dependencies = [
  "webpki 0.21.4",
 ]
 
+[[package]]
+name = "wee_alloc"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e"
+dependencies = [
+ "cfg-if 0.1.10",
+ "libc",
+ "memory_units",
+ "winapi",
+]
+
 [[package]]
 name = "winapi"
 version = "0.3.9"
diff --git a/Cargo.toml b/Cargo.toml
index c051efcb..12434931 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,7 +20,8 @@ members = [
     "ethers-core",
     "ethers-middleware",
     "ethers-etherscan",
-    "ethers-solc"
+    "ethers-solc",
+    "examples/ethers-wasm",
 ]
 
 default-members = [
@@ -30,11 +31,7 @@ default-members = [
     "ethers-core",
     "ethers-middleware",
     "ethers-etherscan",
-    "ethers-solc"
-]
-
-exclude = [
-    "examples/ethers-wasm",
+    "ethers-solc",
 ]
 
 [package.metadata.docs.rs]
@@ -102,3 +99,9 @@ serde_json = "1.0.64"
 tokio = { version = "1.5", features = ["macros", "rt-multi-thread"] }
 hex = "0.4.3"
 bytes = "1.1.0"
+
+
+# profile for the wasm example
+[profile.release.package.ethers-wasm]
+# Tell `rustc` to optimize for small code size.
+opt-level = "s"
\ No newline at end of file
diff --git a/ethers-core/src/types/chain.rs b/ethers-core/src/types/chain.rs
index 28ad23c3..7dd6fefb 100644
--- a/ethers-core/src/types/chain.rs
+++ b/ethers-core/src/types/chain.rs
@@ -2,7 +2,7 @@ use std::fmt;
 
 use crate::types::U256;
 
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
 pub enum Chain {
     Mainnet,
     Ropsten,
@@ -15,6 +15,9 @@ pub enum Chain {
     Avalanche,
     AvalancheFuji,
     Sepolia,
+    Moonbeam,
+    MoonbeamDev,
+    Moonriver,
 }
 
 impl fmt::Display for Chain {
@@ -37,6 +40,9 @@ impl From<Chain> for u32 {
             Chain::Avalanche => 43114,
             Chain::AvalancheFuji => 43113,
             Chain::Sepolia => 11155111,
+            Chain::Moonbeam => 1287,
+            Chain::MoonbeamDev => 1281,
+            Chain::Moonriver => 1285,
         }
     }
 }
@@ -46,3 +52,9 @@ impl From<Chain> for U256 {
         u32::from(chain).into()
     }
 }
+
+impl From<Chain> for u64 {
+    fn from(chain: Chain) -> Self {
+        u32::from(chain).into()
+    }
+}
diff --git a/ethers-core/src/utils/mod.rs b/ethers-core/src/utils/mod.rs
index 7b16f1a5..722d0e83 100644
--- a/ethers-core/src/utils/mod.rs
+++ b/ethers-core/src/utils/mod.rs
@@ -10,6 +10,9 @@ mod geth;
 #[cfg(not(target_arch = "wasm32"))]
 pub use geth::{Geth, GethInstance};
 
+/// Moonbeam utils
+pub mod moonbeam;
+
 mod hash;
 pub use hash::{hash_message, id, keccak256, serialize};
 
diff --git a/ethers-core/src/utils/moonbeam.rs b/ethers-core/src/utils/moonbeam.rs
new file mode 100644
index 00000000..d054d8e7
--- /dev/null
+++ b/ethers-core/src/utils/moonbeam.rs
@@ -0,0 +1,146 @@
+//! Moonbeam utilities
+
+use std::collections::BTreeMap;
+
+use k256::SecretKey;
+
+/// Returns the private developer keys https://docs.moonbeam.network/snippets/code/setting-up-node/dev-accounts/
+pub fn dev_keys() -> Vec<SecretKey> {
+    MoonbeamDev::default().into_keys().collect()
+}
+
+/// Holds private developer keys with their names
+#[derive(Debug, Clone)]
+pub struct MoonbeamDev {
+    keys: BTreeMap<&'static str, SecretKey>,
+}
+
+impl MoonbeamDev {
+    pub fn keys(&self) -> impl Iterator<Item = &SecretKey> {
+        self.keys.values()
+    }
+
+    pub fn into_keys(self) -> impl Iterator<Item = SecretKey> {
+        self.keys.into_values()
+    }
+
+    /// Get a key by then, like `Alith`
+    pub fn get(&self, name: impl AsRef<str>) -> Option<&SecretKey> {
+        self.keys.get(name.as_ref())
+    }
+
+    pub fn alith(&self) -> &SecretKey {
+        self.get("Alith").unwrap()
+    }
+
+    pub fn baltathar(&self) -> &SecretKey {
+        self.get("Baltathar").unwrap()
+    }
+
+    pub fn charleth(&self) -> &SecretKey {
+        self.get("Charleth").unwrap()
+    }
+
+    pub fn ethan(&self) -> &SecretKey {
+        self.get("Ethan").unwrap()
+    }
+}
+
+impl Default for MoonbeamDev {
+    fn default() -> Self {
+        Self {
+            keys: BTreeMap::from([
+                (
+                    "Alith",
+                    SecretKey::from_bytes(
+                        hex::decode(
+                            "5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133",
+                        )
+                        .unwrap(),
+                    )
+                    .unwrap(),
+                ),
+                (
+                    "Baltathar",
+                    SecretKey::from_bytes(
+                        hex::decode(
+                            "8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b",
+                        )
+                        .unwrap(),
+                    )
+                    .unwrap(),
+                ),
+                (
+                    "Charleth",
+                    SecretKey::from_bytes(
+                        hex::decode(
+                            "0b6e18cafb6ed99687ec547bd28139cafdd2bffe70e6b688025de6b445aa5c5b",
+                        )
+                        .unwrap(),
+                    )
+                    .unwrap(),
+                ),
+                (
+                    "Dorothy",
+                    SecretKey::from_bytes(
+                        hex::decode(
+                            "39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68",
+                        )
+                        .unwrap(),
+                    )
+                    .unwrap(),
+                ),
+                (
+                    "Faith",
+                    SecretKey::from_bytes(
+                        hex::decode(
+                            "b9d2ea9a615f3165812e8d44de0d24da9bbd164b65c4f0573e1ce2c8dbd9c8df",
+                        )
+                        .unwrap(),
+                    )
+                    .unwrap(),
+                ),
+                (
+                    "Goliath",
+                    SecretKey::from_bytes(
+                        hex::decode(
+                            "96b8a38e12e1a31dee1eab2fffdf9d9990045f5b37e44d8cc27766ef294acf18",
+                        )
+                        .unwrap(),
+                    )
+                    .unwrap(),
+                ),
+                (
+                    "Heath",
+                    SecretKey::from_bytes(
+                        hex::decode(
+                            "0d6dcaaef49272a5411896be8ad16c01c35d6f8c18873387b71fbc734759b0ab",
+                        )
+                        .unwrap(),
+                    )
+                    .unwrap(),
+                ),
+                (
+                    "Ida",
+                    SecretKey::from_bytes(
+                        hex::decode(
+                            "4c42532034540267bf568198ccec4cb822a025da542861fcb146a5fab6433ff8",
+                        )
+                        .unwrap(),
+                    )
+                    .unwrap(),
+                ),
+                (
+                    "Judith",
+                    SecretKey::from_bytes(
+                        hex::decode(
+                            "94c49300a58d576011096bcb006aa06f5a91b34b4383891e8029c21dc39fbb8b",
+                        )
+                        .unwrap(),
+                    )
+                    .unwrap(),
+                ),
+            ]),
+        }
+    }
+}
diff --git a/ethers-etherscan/src/lib.rs b/ethers-etherscan/src/lib.rs
index 400bcf82..772e64ca 100644
--- a/ethers-etherscan/src/lib.rs
+++ b/ethers-etherscan/src/lib.rs
@@ -78,7 +78,11 @@ impl Client {
             Chain::Mainnet | Chain::Ropsten | Chain::Kovan | Chain::Rinkeby | Chain::Goerli => {
                 std::env::var("ETHERSCAN_API_KEY")?
             }
+
             Chain::XDai | Chain::Sepolia => String::default(),
+            Chain::Moonbeam | Chain::MoonbeamDev | Chain::Moonriver => {
+                std::env::var("MOONSCAN_API_KEY")?
+            }
         };
         Self::new(chain, api_key)
     }
diff --git a/ethers-solc/Cargo.toml b/ethers-solc/Cargo.toml
index 743afd9a..296b002b 100644
--- a/ethers-solc/Cargo.toml
+++ b/ethers-solc/Cargo.toml
@@ -31,12 +31,16 @@ svm = { package = "svm-rs", version = "0.2.0", optional = true }
 glob = "0.3.0"
 tracing = "0.1.29"
 
-[target.'cfg(target_arch = "wasm32")'.dependencies]
-sha2 = { version = "0.9.8" }
+[target.'cfg(not(any(target_arch = "x86", target_arch = "x86_64")))'.dependencies]
+sha2 = { version = "0.9.8", default-features = false }
 
-[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
+[target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies]
 home = "0.5.3"
-sha2 = { version = "0.9.8", features = ["asm"] }
+sha2 = { version = "0.9.8", default-features = false, features = ["asm"] }
+
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+# NOTE: this enables wasm compatibility for getrandom indirectly
+getrandom = { version = "0.2", features = ["js"] }
 
 [dev-dependencies]
 tokio = { version = "1.12.0", features = ["full"] }
diff --git a/examples/ethers-wasm/Cargo.toml b/examples/ethers-wasm/Cargo.toml
index 24b2164b..34e71d86 100644
--- a/examples/ethers-wasm/Cargo.toml
+++ b/examples/ethers-wasm/Cargo.toml
@@ -43,10 +43,4 @@ hex = "0.4.3"
 web-sys = "0.3.51"
 
 [dev-dependencies]
-wasm-bindgen-test = "0.3.24"
-
-# profile for the wasm example
-[profile.release.package.ethers-wasm]
-# Tell `rustc` to optimize for small code size.
-opt-level = "s"
-
+wasm-bindgen-test = "0.3.24"
\ No newline at end of file
diff --git a/examples/ethers-wasm/src/lib.rs b/examples/ethers-wasm/src/lib.rs
index 84be0071..e4816a55 100644
--- a/examples/ethers-wasm/src/lib.rs
+++ b/examples/ethers-wasm/src/lib.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::all)]
+
 use std::sync::Arc;
 
 use wasm_bindgen::prelude::*;
@@ -51,12 +53,7 @@ pub async fn deploy() {
     let factory = ContractFactory::new(SIMPLECONTRACT_ABI.clone(), bytecode.into(), client.clone());
 
     log!("Deploying contract...");
-    let contract = factory
-        .deploy("hello WASM!".to_string())
-        .unwrap()
-        .send()
-        .await
-        .unwrap();
+    let contract = factory.deploy("hello WASM!".to_string()).unwrap().send().await.unwrap();
     let addr = contract.address();
     log!("Deployed contract with address: {:?}", addr);
 
@@ -64,25 +61,11 @@ pub async fn deploy() {
 
     let value = "bye from WASM!";
     log!("Setting value... `{}`", value);
-    let receipt = contract
-        .set_value(value.to_owned())
-        .send()
-        .await
-        .unwrap()
-        .await
-        .unwrap();
-    console::log_2(
-        &"Set value receipt: ".into(),
-        &JsValue::from_serde(&receipt).unwrap(),
-    );
+    let receipt = contract.set_value(value.to_owned()).send().await.unwrap().await.unwrap();
+    console::log_2(&"Set value receipt: ".into(), &JsValue::from_serde(&receipt).unwrap());
 
     log!("Fetching logs...");
-    let logs = contract
-        .value_changed_filter()
-        .from_block(0u64)
-        .query()
-        .await
-        .unwrap();
+    let logs = contract.value_changed_filter().from_block(0u64).query().await.unwrap();
 
     let value = contract.get_value().call().await.unwrap();
 
diff --git a/examples/ethers-wasm/src/utils.rs b/examples/ethers-wasm/src/utils.rs
index 091ca848..70bdd53e 100644
--- a/examples/ethers-wasm/src/utils.rs
+++ b/examples/ethers-wasm/src/utils.rs
@@ -1,5 +1,7 @@
-use ethers::prelude::{LocalWallet, MnemonicBuilder};
-use ethers::signers::coins_bip39::English;
+use ethers::{
+    prelude::{LocalWallet, MnemonicBuilder},
+    signers::coins_bip39::English,
+};
 
 pub fn set_panic_hook() {
     // When the `console_error_panic_hook` feature is enabled, we can call the
@@ -17,12 +19,7 @@ pub const PHRASE: &str =
     "stuff inherit faith park genre spread huge knee ecology private marble supreme";
 
 pub fn key(index: u32) -> LocalWallet {
-    MnemonicBuilder::<English>::default()
-        .phrase(PHRASE)
-        .index(index)
-        .unwrap()
-        .build()
-        .unwrap()
+    MnemonicBuilder::<English>::default().phrase(PHRASE).index(index).unwrap().build().unwrap()
 }
 
 /// Bytecode of the `SimpleContract`
diff --git a/examples/moonbeam_with_abi.rs b/examples/moonbeam_with_abi.rs
new file mode 100644
index 00000000..c455d5f3
--- /dev/null
+++ b/examples/moonbeam_with_abi.rs
@@ -0,0 +1,75 @@
+use anyhow::Result;
+use ethers::prelude::*;
+use std::{convert::TryFrom, path::Path, sync::Arc, time::Duration};
+
+abigen!(
+    SimpleContract,
+    "./examples/contract_abi.json",
+    event_derives(serde::Deserialize, serde::Serialize)
+);
+
+const MOONBEAM_DEV_ENDPOINT: &str = "http://localhost:9933";
+
+/// This requires a running moonbeam dev instance on `localhost:9933`
+/// See `https://docs.moonbeam.network/builders/get-started/moonbeam-dev/` for reference
+///
+/// This has been tested against:
+///
+/// ```bash
+///  docker run --rm --name moonbeam_development -p 9944:9944 -p 9933:9933 purestake/moonbeam:v0.14.2 --dev --ws-external --rpc-external
+/// ```
+///
+/// Also requires the `legacy` feature to send Legacy transaction instead of an EIP-1559
+#[tokio::main]
+#[cfg(feature = "legacy")]
+async fn main() -> Result<()> {
+    // set the path to the contract, `CARGO_MANIFEST_DIR` points to the directory containing the
+    // manifest of `ethers`. which will be `../` relative to this file
+    let source = Path::new(&env!("CARGO_MANIFEST_DIR")).join("examples/contract.sol");
+    let compiled = Solc::default().compile_source(source).expect("Could not compile contracts");
+    let (abi, bytecode, _runtime_bytecode) =
+        compiled.find("SimpleStorage").expect("could not find contract").into_parts_or_default();
+
+    // 1. get a moonbeam dev key
+    let key = ethers::core::utils::moonbeam::dev_keys()[0].clone();
+
+    // 2. instantiate our wallet with chain id
+    let wallet: LocalWallet = LocalWallet::from(key).with_chain_id(Chain::MoonbeamDev);
+
+    // 3. connect to the network
+    let provider =
+        Provider::<Http>::try_from(MOONBEAM_DEV_ENDPOINT)?.interval(Duration::from_millis(10u64));
+
+    // 4. instantiate the client with the wallet
+    let client = SignerMiddleware::new(provider, wallet);
+    let client = Arc::new(client);
+
+    // 5. create a factory which will be used to deploy instances of the contract
+    let factory = ContractFactory::new(abi, bytecode, client.clone());
+
+    // 6. deploy it with the constructor arguments, note the `legacy` call
+    let contract = factory.deploy("initial value".to_string())?.legacy().send().await?;
+
+    // 7. get the contract's address
+    let addr = contract.address();
+
+    // 8. instantiate the contract
+    let contract = SimpleContract::new(addr, client.clone());
+
+    // 9. call the `setValue` method
+    // (first `await` returns a PendingTransaction, second one waits for it to be mined)
+    let _receipt = contract.set_value("hi".to_owned()).legacy().send().await?.await?;
+
+    // 10. get all events
+    let logs = contract.value_changed_filter().from_block(0u64).query().await?;
+
+    // 11. get the new value
+    let value = contract.get_value().call().await?;
+
+    println!("Value: {}. Logs: {}", value, serde_json::to_string(&logs)?);
+
+    Ok(())
+}
+
+#[cfg(not(feature = "legacy"))]
+fn main() {}


Test patch:
diff --git a/examples/ethers-wasm/tests/contract_with_abi.rs b/examples/ethers-wasm/tests/contract_with_abi.rs
index 8dc61db6..678f54b2 100644
--- a/examples/ethers-wasm/tests/contract_with_abi.rs
+++ b/examples/ethers-wasm/tests/contract_with_abi.rs
@@ -6,8 +6,7 @@ use ethers::prelude::{
     abigen, ContractFactory, Http, JsonRpcClient, LocalWallet, Provider, SignerMiddleware, Ws,
 };
 
-use std::convert::TryFrom;
-use std::sync::Arc;
+use std::{convert::TryFrom, sync::Arc};
 
 wasm_bindgen_test_configure!(run_in_browser);
 
@@ -38,12 +37,8 @@ async fn deploy<T: JsonRpcClient>(provider: Provider<T>, wallet: LocalWallet) {
 
     let bytecode = hex::decode(ethers_wasm::utils::SIMPLECONTRACT_BIN).unwrap();
     let factory = ContractFactory::new(SIMPLECONTRACT_ABI.clone(), bytecode.into(), client.clone());
-    let contract = factory
-        .deploy("Hello from Contract!".to_string())
-        .unwrap()
-        .send()
-        .await
-        .unwrap();
+    let contract =
+        factory.deploy("Hello from Contract!".to_string()).unwrap().send().await.unwrap();
     let addr = contract.address();
     console_log!("deployed to {}", addr);