feat: wasm support (#390)
* feat: partial wasm32 support * make compile for wasm32 * feat: att async-trait attr * make compatible with wasm * add type alias for archs * rustfmt * add wasm ci * make compile with wasm-pack test * make compile with wasm-pack test * make compile with wasm-pack test * make compile with wasm-pack test * ci: disable wasmpack * feat: use wasm timer delay * feat: add wasm provider * rustfmt * misc refactor * add wasm example * make example a directory * untrack error log * move profile to root * fix unused imports * ci: enable wasm-pack test * style: unify websocket implementations * fix: typos * fix: make policy compatible with wasm target * chore: do not include ethers-wasm example as top level workspace member * chore: modify wasm32 dependencies * chore: make note about getrandom Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
This commit is contained in:
parent
8587b3e9b3
commit
ea566663d4
|
@ -115,3 +115,47 @@ jobs:
|
|||
run: cargo fmt --all -- --check
|
||||
- name: cargo clippy
|
||||
run: cargo clippy -- -D warnings
|
||||
|
||||
wasm:
|
||||
name: WASM
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 10
|
||||
- name: Install rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
target: wasm32-unknown-unknown
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- name: Check
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --target wasm32-unknown-unknown
|
||||
|
||||
- name: Launch Ganache
|
||||
run: |
|
||||
cd examples/ethers-wasm
|
||||
npm install
|
||||
npm run ganache &
|
||||
|
||||
- name: Install wasm-pack
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
|
||||
- name: Wasm-pack test firefox
|
||||
run: |
|
||||
cd examples/ethers-wasm
|
||||
wasm-pack test --headless --firefox
|
||||
|
||||
- name: Wasm-pack test chrome
|
||||
run: |
|
||||
cd examples/ethers-wasm
|
||||
wasm-pack test --headless --chrome
|
|
@ -111,6 +111,17 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async_io_stream"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "541b3487bf601cf3a63dfba621d6d0252611f120aaf27b198f018c0e1714f0df"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"pharos",
|
||||
"rustc_version 0.3.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "auto_impl"
|
||||
version = "0.4.1"
|
||||
|
@ -144,6 +155,12 @@ dependencies = [
|
|||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base-x"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b"
|
||||
|
||||
[[package]]
|
||||
name = "base58"
|
||||
version = "0.1.0"
|
||||
|
@ -355,7 +372,7 @@ checksum = "c297bd3135f558552f99a0daa180876984ea2c4ffa7470314540dff8c654109a"
|
|||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform",
|
||||
"semver",
|
||||
"semver 1.0.4",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
@ -400,7 +417,7 @@ dependencies = [
|
|||
"num-integer",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"time",
|
||||
"time 0.1.43",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
|
@ -509,6 +526,12 @@ version = "0.6.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c32f031ea41b4291d695026c023b95d59db2d8a2c7640800ed56bc8f510f22"
|
||||
|
||||
[[package]]
|
||||
name = "const_fn"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7"
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.5"
|
||||
|
@ -676,6 +699,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "discard"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
|
||||
|
||||
[[package]]
|
||||
name = "ecdsa"
|
||||
version = "0.12.4"
|
||||
|
@ -851,7 +880,9 @@ dependencies = [
|
|||
"Inflector",
|
||||
"anyhow",
|
||||
"cargo_metadata",
|
||||
"cfg-if 1.0.0",
|
||||
"ethers-core",
|
||||
"getrandom 0.2.3",
|
||||
"hex",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
|
@ -916,6 +947,7 @@ dependencies = [
|
|||
"ethers-signers",
|
||||
"futures-util",
|
||||
"hex",
|
||||
"instant",
|
||||
"rand 0.8.4",
|
||||
"reqwest",
|
||||
"serde",
|
||||
|
@ -942,6 +974,7 @@ dependencies = [
|
|||
"futures-timer",
|
||||
"futures-util",
|
||||
"hex",
|
||||
"parking_lot",
|
||||
"pin-project",
|
||||
"reqwest",
|
||||
"serde",
|
||||
|
@ -954,6 +987,11 @@ dependencies = [
|
|||
"tracing",
|
||||
"tracing-futures",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-timer",
|
||||
"web-sys",
|
||||
"ws_stream_wasm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1434,6 +1472,19 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"time 0.2.27",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.3.1"
|
||||
|
@ -1498,6 +1549,15 @@ dependencies = [
|
|||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
|
@ -1753,6 +1813,31 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.2.2"
|
||||
|
@ -1783,6 +1868,25 @@ version = "2.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
|
||||
dependencies = [
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pharos"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "235c4b2ebc9552f5eba94ec982acb6c12f224980878e5b74a7d61806bb9c3591"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"rustc_version 0.4.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.0.8"
|
||||
|
@ -2175,7 +2279,7 @@ dependencies = [
|
|||
"log",
|
||||
"rusoto_credential",
|
||||
"rusoto_signature",
|
||||
"rustc_version",
|
||||
"rustc_version 0.4.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
|
@ -2234,7 +2338,7 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rusoto_credential",
|
||||
"rustc_version",
|
||||
"rustc_version 0.4.0",
|
||||
"serde",
|
||||
"sha2 0.9.5",
|
||||
"tokio",
|
||||
|
@ -2252,13 +2356,31 @@ version = "2.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
|
||||
dependencies = [
|
||||
"semver 0.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver",
|
||||
"semver 1.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2311,6 +2433,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "scrypt"
|
||||
version = "0.7.0"
|
||||
|
@ -2358,6 +2486,24 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
dependencies = [
|
||||
"semver-parser 0.7.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
|
||||
dependencies = [
|
||||
"semver-parser 0.10.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.4"
|
||||
|
@ -2367,6 +2513,27 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
|
||||
dependencies = [
|
||||
"pest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "send_wrapper"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.127"
|
||||
|
@ -2433,6 +2600,12 @@ dependencies = [
|
|||
"opaque-debug 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.8.2"
|
||||
|
@ -2554,12 +2727,70 @@ dependencies = [
|
|||
"der",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "standback"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "stdweb"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
|
||||
dependencies = [
|
||||
"discard",
|
||||
"rustc_version 0.2.3",
|
||||
"stdweb-derive",
|
||||
"stdweb-internal-macros",
|
||||
"stdweb-internal-runtime",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stdweb-derive"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stdweb-internal-macros"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
|
||||
dependencies = [
|
||||
"base-x",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sha1",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stdweb-internal-runtime"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.4.1"
|
||||
|
@ -2648,6 +2879,44 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.2.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242"
|
||||
dependencies = [
|
||||
"const_fn",
|
||||
"libc",
|
||||
"standback",
|
||||
"stdweb",
|
||||
"time-macros",
|
||||
"version_check",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"time-macros-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-macros-impl"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"standback",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
|
@ -2891,6 +3160,12 @@ version = "1.13.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||
|
||||
[[package]]
|
||||
name = "uint"
|
||||
version = "0.9.1"
|
||||
|
@ -3066,6 +3341,21 @@ version = "0.2.76"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acdb075a845574a1fa5f09fd77e43f7747599301ea3417a9fbffdeedfc1f4a29"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-timer"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"js-sys",
|
||||
"parking_lot",
|
||||
"pin-utils",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.53"
|
||||
|
@ -3126,6 +3416,24 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ws_stream_wasm"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47ca1ab42f5afed7fc332b22b6e932ca5414b209465412c8cdf0ad23bc0de645"
|
||||
dependencies = [
|
||||
"async_io_stream",
|
||||
"futures",
|
||||
"js-sys",
|
||||
"pharos",
|
||||
"rustc_version 0.4.0",
|
||||
"send_wrapper",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.2.0"
|
||||
|
|
|
@ -21,6 +21,9 @@ members = [
|
|||
"ethers-middleware",
|
||||
]
|
||||
|
||||
exclude = [
|
||||
"examples/ethers-wasm",
|
||||
]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -28,8 +28,9 @@ ethers = { version = "0.4.0", path = ".." }
|
|||
ethers-providers = { version = "0.4.6", path = "../ethers-providers", default-features = false, features = ["ws"] }
|
||||
ethers-signers = { version = "0.4.6", path = "../ethers-signers" }
|
||||
ethers-middleware = { version = "0.4.8", path = "../ethers-middleware" }
|
||||
tokio = { version = "1.5", default-features = false, features = ["macros"] }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||
tokio = { version = "1.5", default-features = false, features = ["macros"] }
|
||||
|
||||
[features]
|
||||
abigen = ["ethers-contract-abigen", "ethers-contract-derive"]
|
||||
|
|
|
@ -24,6 +24,11 @@ hex = { version = "0.4.2", default-features = false, features = ["std"] }
|
|||
reqwest = { version = "0.11.3", features = ["blocking"] }
|
||||
once_cell = { version = "1.8.0", default-features = false }
|
||||
cargo_metadata = "0.14.0"
|
||||
cfg-if = "1.0.0"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
# NOTE: this enables wasm compatibility for getrandom indirectly
|
||||
getrandom = { version = "0.2", features = ["js"] }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -3,6 +3,7 @@ use super::util;
|
|||
use ethers_core::types::Address;
|
||||
|
||||
use anyhow::{anyhow, Context, Error, Result};
|
||||
use cfg_if::cfg_if;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
env, fs,
|
||||
|
@ -77,8 +78,21 @@ impl Source {
|
|||
P: AsRef<Path>,
|
||||
S: AsRef<str>,
|
||||
{
|
||||
cfg_if! {
|
||||
if #[cfg(target_arch = "wasm32")] {
|
||||
let root = root.as_ref();
|
||||
let root = if root.starts_with("/") {
|
||||
format!("file:://{}", root.display())
|
||||
} else {
|
||||
format!("{}", root.display())
|
||||
};
|
||||
let base = Url::parse(&root)
|
||||
.map_err(|_| anyhow!("root path '{}' is not absolute"))?;
|
||||
} else {
|
||||
let base = Url::from_directory_path(root)
|
||||
.map_err(|_| anyhow!("root path '{}' is not absolute"))?;
|
||||
}
|
||||
}
|
||||
let url = base.join(source.as_ref())?;
|
||||
|
||||
match url.scheme() {
|
||||
|
@ -134,8 +148,18 @@ impl Source {
|
|||
|
||||
/// Retrieves the source JSON of the artifact this will either read the JSON
|
||||
/// from the file system or retrieve a contract ABI from the network
|
||||
/// dependending on the source type.
|
||||
/// depending on the source type.
|
||||
pub fn get(&self) -> Result<String> {
|
||||
cfg_if! {
|
||||
if #[cfg(target_arch = "wasm32")] {
|
||||
match self {
|
||||
Source::Local(path) => get_local_contract(path),
|
||||
Source::Http(_) => panic!("Http abi location are not supported for wasm"),
|
||||
Source::Etherscan(_) => panic!("Etherscan abi location are not supported for wasm"),
|
||||
Source::Npm(_) => panic!("npm abi location are not supported for wasm"),
|
||||
Source::String(abi) => Ok(abi.clone()),
|
||||
}
|
||||
} else {
|
||||
match self {
|
||||
Source::Local(path) => get_local_contract(path),
|
||||
Source::Http(url) => get_http_contract(url),
|
||||
|
@ -145,6 +169,8 @@ impl Source {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Source {
|
||||
type Err = Error;
|
||||
|
@ -179,6 +205,7 @@ fn get_local_contract(path: &Path) -> Result<String> {
|
|||
}
|
||||
|
||||
/// Retrieves a Truffle artifact or ABI from an HTTP URL.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn get_http_contract(url: &Url) -> Result<String> {
|
||||
let json = util::http_get(url.as_str())
|
||||
.with_context(|| format!("failed to retrieve JSON from {}", url))?;
|
||||
|
@ -187,6 +214,7 @@ fn get_http_contract(url: &Url) -> Result<String> {
|
|||
|
||||
/// Retrieves a contract ABI from the Etherscan HTTP API and wraps it in an
|
||||
/// artifact JSON for compatibility with the code generation facilities.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn get_etherscan_contract(address: Address) -> Result<String> {
|
||||
// NOTE: We do not retrieve the bytecode since deploying contracts with the
|
||||
// same bytecode is unreliable as the libraries have already linked and
|
||||
|
@ -206,6 +234,7 @@ fn get_etherscan_contract(address: Address) -> Result<String> {
|
|||
}
|
||||
|
||||
/// Retrieves a Truffle artifact or ABI from an npm package through `unpkg.io`.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn get_npm_contract(package: &str) -> Result<String> {
|
||||
let unpkg_url = format!("https://unpkg.io/{}", package);
|
||||
let json = util::http_get(&unpkg_url)
|
||||
|
|
|
@ -103,6 +103,7 @@ where
|
|||
Ok(address_str[2..].parse()?)
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
/// Perform an HTTP GET request and return the contents of the response.
|
||||
pub fn http_get(url: &str) -> Result<String> {
|
||||
Ok(reqwest::blocking::get(url)?.text()?)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![cfg(not(target_arch = "wasm32"))]
|
||||
mod derive;
|
||||
use ethers_core::{
|
||||
abi::Abi,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![cfg(not(target_arch = "wasm32"))]
|
||||
use ethers::{
|
||||
contract::ContractFactory,
|
||||
types::{Filter, ValueOrArray, H256},
|
||||
|
|
|
@ -31,6 +31,7 @@ glob = { version = "0.3.0", default-features = false }
|
|||
bytes = { version = "1.0.1", features = ["serde"] }
|
||||
hex = { version = "0.4.3", default-features = false, features = ["std"] }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
# async
|
||||
tokio = { version = "1.5", default-features = false, optional = true}
|
||||
futures-util = { version = "0.3.16", optional = true }
|
||||
|
|
|
@ -291,6 +291,7 @@ fn base_fee_surged(base_fee_per_gas: U256) -> U256 {
|
|||
///
|
||||
/// Does not guarantee that the given port is unused after the function exists, just that it was
|
||||
/// unused before the function started (i.e., it does not reserve a port).
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) fn unused_port() -> u16 {
|
||||
let listener = std::net::TcpListener::bind("127.0.0.1:0")
|
||||
.expect("Failed to create TCP listener to find unused port");
|
||||
|
|
|
@ -32,12 +32,17 @@ reqwest = { version = "0.11.4", default-features = false, features = ["json", "r
|
|||
url = { version = "2.2.2", default-features = false }
|
||||
|
||||
serde_json = { version = "1.0.64", default-features = false }
|
||||
instant = {version = "0.1.10", features = ["now"] }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
tokio = { version = "1.5" }
|
||||
|
||||
[dev-dependencies]
|
||||
ethers = { version = "0.4.0", path = ".." }
|
||||
hex = { version = "0.4.3", default-features = false, features = ["std"] }
|
||||
rand = { version = "0.8.4", default-features = false }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||
tokio = { version = "1.5", default-features = false, features = ["rt", "macros", "time"] }
|
||||
|
||||
[features]
|
||||
|
|
|
@ -9,12 +9,18 @@ use async_trait::async_trait;
|
|||
use ethers_core::types::{BlockId, TransactionRequest, TxHash, U256};
|
||||
use ethers_providers::{interval, FromErr, Middleware, PendingTransaction, StreamExt};
|
||||
use futures_util::lock::Mutex;
|
||||
use instant::Instant;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::{pin::Pin, time::Instant};
|
||||
use thiserror::Error;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::spawn;
|
||||
use tracing_futures::Instrument;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
type WatcherFuture<'a> = Pin<Box<dyn futures_util::stream::Stream<Item = ()> + 'a>>;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
type WatcherFuture<'a> = Pin<Box<dyn futures_util::stream::Stream<Item = ()> + Send + 'a>>;
|
||||
|
||||
/// Trait for fetching updated gas prices after a transaction has been first
|
||||
/// broadcast
|
||||
|
@ -69,7 +75,8 @@ pub struct GasEscalatorMiddleware<M, E> {
|
|||
frequency: Frequency,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl<M, E> Middleware for GasEscalatorMiddleware<M, E>
|
||||
where
|
||||
M: Middleware,
|
||||
|
@ -118,11 +125,14 @@ where
|
|||
/// Initializes the middleware with the provided gas escalator and the chosen
|
||||
/// escalation frequency (per block or per second)
|
||||
#[allow(clippy::let_and_return)]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn new(inner: M, escalator: E, frequency: Frequency) -> Self
|
||||
where
|
||||
E: Clone + 'static,
|
||||
M: Clone + 'static,
|
||||
{
|
||||
use tracing_futures::Instrument;
|
||||
|
||||
let this = Self {
|
||||
inner: Arc::new(inner),
|
||||
escalator,
|
||||
|
@ -146,9 +156,8 @@ where
|
|||
|
||||
/// Re-broadcasts pending transactions with a gas price escalator
|
||||
pub async fn escalate(&self) -> Result<(), GasEscalatorError<M>> {
|
||||
// the escalation frequency is either on a per-block basis, or on a duratoin basis
|
||||
let mut watcher: Pin<Box<dyn futures_util::stream::Stream<Item = ()> + Send>> =
|
||||
match self.frequency {
|
||||
// the escalation frequency is either on a per-block basis, or on a duration basis
|
||||
let mut watcher: WatcherFuture = match self.frequency {
|
||||
Frequency::PerBlock => Box::pin(
|
||||
self.inner
|
||||
.watch_blocks()
|
||||
|
|
|
@ -93,7 +93,8 @@ impl EthGasStation {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl GasOracle for EthGasStation {
|
||||
async fn fetch(&self) -> Result<U256, GasOracleError> {
|
||||
let res = self.query().await?;
|
||||
|
|
|
@ -64,7 +64,8 @@ impl Etherchain {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl GasOracle for Etherchain {
|
||||
async fn fetch(&self) -> Result<U256, GasOracleError> {
|
||||
let res = self.query().await?;
|
||||
|
|
|
@ -92,7 +92,8 @@ impl Etherscan {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl GasOracle for Etherscan {
|
||||
async fn fetch(&self) -> Result<U256, GasOracleError> {
|
||||
if matches!(self.gas_category, GasCategory::Fastest) {
|
||||
|
|
|
@ -67,7 +67,8 @@ impl GasNow {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl GasOracle for GasNow {
|
||||
async fn fetch(&self) -> Result<U256, GasOracleError> {
|
||||
let res = self.query().await?;
|
||||
|
|
|
@ -39,7 +39,8 @@ impl<M: Middleware> FromErr<M::Error> for MiddlewareError<M> {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl<M, G> Middleware for GasOracleMiddleware<M, G>
|
||||
where
|
||||
M: Middleware,
|
||||
|
|
|
@ -65,7 +65,8 @@ pub enum GasOracleError {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait GasOracle: Send + Sync + std::fmt::Debug {
|
||||
/// Makes an asynchronous HTTP query to the underlying `GasOracle`
|
||||
///
|
||||
|
|
|
@ -69,7 +69,8 @@ impl<M: Middleware> FromErr<M::Error> for NonceManagerError<M> {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl<M> Middleware for NonceManagerMiddleware<M>
|
||||
where
|
||||
M: Middleware,
|
||||
|
|
|
@ -6,7 +6,8 @@ use std::fmt::Debug;
|
|||
use thiserror::Error;
|
||||
|
||||
/// Basic trait to ensure that transactions about to be sent follow certain rules.
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait Policy: Sync + Send + Debug {
|
||||
type Error: Sync + Send + Debug;
|
||||
|
||||
|
@ -20,7 +21,8 @@ pub trait Policy: Sync + Send + Debug {
|
|||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct AllowEverything;
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl Policy for AllowEverything {
|
||||
type Error = ();
|
||||
|
||||
|
@ -33,7 +35,8 @@ impl Policy for AllowEverything {
|
|||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RejectEverything;
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl Policy for RejectEverything {
|
||||
type Error = ();
|
||||
|
||||
|
@ -77,7 +80,8 @@ pub enum PolicyMiddlewareError<M: Middleware, P: Policy> {
|
|||
MiddlewareError(M::Error),
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl<M, P> Middleware for PolicyMiddleware<M, P>
|
||||
where
|
||||
M: Middleware,
|
||||
|
|
|
@ -152,7 +152,8 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl<M, S> Middleware for SignerMiddleware<M, S>
|
||||
where
|
||||
M: Middleware,
|
||||
|
@ -246,7 +247,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, not(feature = "celo")))]
|
||||
#[cfg(all(test, not(feature = "celo"), not(target_arch = "wasm32")))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ethers::{providers::Provider, signers::LocalWallet};
|
||||
|
|
|
@ -39,7 +39,8 @@ impl<M: Middleware> FromErr<M::Error> for TransformerMiddlewareError<M> {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl<M, T> Middleware for TransformerMiddleware<M, T>
|
||||
where
|
||||
M: Middleware,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![cfg(not(target_arch = "wasm32"))]
|
||||
use ethers_core::types::*;
|
||||
use ethers_middleware::{
|
||||
gas_escalator::{Frequency, GasEscalatorMiddleware, GeometricGasPrice},
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![cfg(not(target_arch = "wasm32"))]
|
||||
use ethers_core::{types::*, utils::Ganache};
|
||||
use ethers_middleware::gas_oracle::{
|
||||
EthGasStation, Etherchain, Etherscan, GasCategory, GasNow, GasOracle, GasOracleMiddleware,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![cfg(not(target_arch = "wasm32"))]
|
||||
#[tokio::test]
|
||||
#[cfg(not(feature = "celo"))]
|
||||
async fn nonce_manager() {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![cfg(not(target_arch = "wasm32"))]
|
||||
use ethers_providers::{Http, Middleware, Provider};
|
||||
|
||||
use ethers_core::types::TransactionRequest;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(not(feature = "celo"))]
|
||||
mod tests {
|
||||
use ethers_core::{rand::thread_rng, types::TransactionRequest, utils::Ganache};
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![cfg(not(target_arch = "wasm32"))]
|
||||
use ethers_contract::{BaseContract, ContractFactory};
|
||||
use ethers_core::{
|
||||
types::*,
|
||||
|
|
|
@ -36,14 +36,27 @@ pin-project = { version = "1.0.7", default-features = false }
|
|||
tracing = { version = "0.1.26", default-features = false }
|
||||
tracing-futures = { version = "0.2.5", default-features = false, features = ["std-future"] }
|
||||
|
||||
bytes = { version = "1.0.1", default-features = false, optional = true }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
# tokio
|
||||
tokio-util = { version = "0.6.7", default-features = false, features = ["io"], optional = true }
|
||||
tokio = { version = "1.5", default-features = false, optional = true }
|
||||
tokio-tungstenite = { version = "0.15.0", default-features = false, features = ["connect"], optional = true }
|
||||
tokio-util = { version = "0.6.7", default-features = false, features = ["io"], optional = true }
|
||||
bytes = { version = "1.0.1", default-features = false, optional = true }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
ws_stream_wasm = "0.7"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
web-sys = { version = "0.3", features = ["console"] }
|
||||
wasm-timer = "0.2"
|
||||
# this is currently necessary for `wasm-timer::Delay` to work
|
||||
parking_lot = { version = "0.11", features = ["wasm-bindgen"] }
|
||||
|
||||
[dev-dependencies]
|
||||
ethers = { version = "0.4.0", path = ".." }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||
tokio = { version = "1.5", default-features = false, features = ["rt", "macros"] }
|
||||
tempfile = "3.2.0"
|
||||
|
||||
|
|
|
@ -90,10 +90,14 @@ use std::{error::Error, fmt::Debug, future::Future, pin::Pin, str::FromStr};
|
|||
pub use provider::{FilterKind, Provider, ProviderError};
|
||||
|
||||
// Helper type alias
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub(crate) type PinBoxFut<'a, T> = Pin<Box<dyn Future<Output = Result<T, ProviderError>> + 'a>>;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) type PinBoxFut<'a, T> =
|
||||
Pin<Box<dyn Future<Output = Result<T, ProviderError>> + Send + 'a>>;
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
#[auto_impl(&, Box, Arc)]
|
||||
/// Trait which must be implemented by data transports to be used with the Ethereum
|
||||
/// JSON-RPC provider.
|
||||
|
@ -125,8 +129,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[auto_impl(&, Box, Arc)]
|
||||
/// A middleware allows customizing requests send and received from an ethereum node.
|
||||
///
|
||||
/// Writing a middleware is as simple as:
|
||||
|
@ -182,6 +184,9 @@ where
|
|||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
#[auto_impl(&, Box, Arc)]
|
||||
pub trait Middleware: Sync + Send + Debug {
|
||||
type Error: Sync + Send + Error + FromErr<<Self::Inner as Middleware>::Error>;
|
||||
type Provider: JsonRpcClient;
|
||||
|
@ -759,7 +764,8 @@ where
|
|||
}
|
||||
|
||||
#[cfg(feature = "celo")]
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait CeloMiddleware: Middleware {
|
||||
async fn get_validators_bls_public_keys<T: Into<BlockId> + Send + Sync>(
|
||||
&self,
|
||||
|
|
|
@ -5,7 +5,6 @@ use crate::{
|
|||
};
|
||||
use ethers_core::types::{Transaction, TransactionReceipt, TxHash, U64};
|
||||
use futures_core::stream::Stream;
|
||||
use futures_timer::Delay;
|
||||
use futures_util::stream::StreamExt;
|
||||
use pin_project::pin_project;
|
||||
use std::{
|
||||
|
@ -17,6 +16,11 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use futures_timer::Delay;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_timer::Delay;
|
||||
|
||||
/// A pending transaction is a transaction which has been submitted but is not yet mined.
|
||||
/// `await`'ing on a pending transaction will resolve to a transaction receipt
|
||||
/// once the transaction has enough `confirmations`. The default number of confirmations
|
||||
|
@ -258,7 +262,7 @@ impl<'a, P> Deref for PendingTransaction<'a, P> {
|
|||
// We box the TransactionReceipts to keep the enum small.
|
||||
enum PendingTxState<'a> {
|
||||
/// Initial delay to ensure the GettingTx loop doesn't immediately fail
|
||||
InitialDelay(Pin<Box<futures_timer::Delay>>),
|
||||
InitialDelay(Pin<Box<Delay>>),
|
||||
|
||||
/// Waiting for interval to elapse before calling API again
|
||||
PausedGettingTx,
|
||||
|
|
|
@ -2,9 +2,12 @@ use crate::{
|
|||
ens,
|
||||
pubsub::{PubsubClient, SubscriptionStream},
|
||||
stream::{FilterWatcher, DEFAULT_POLL_INTERVAL},
|
||||
FeeHistory, FromErr, Http as HttpProvider, JsonRpcClient, MockProvider, PendingTransaction,
|
||||
FeeHistory, FromErr, JsonRpcClient, MockProvider, PendingTransaction,
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use crate::Http as HttpProvider;
|
||||
|
||||
use ethers_core::{
|
||||
abi::{self, Detokenize, ParamType},
|
||||
types::{
|
||||
|
@ -23,6 +26,7 @@ use async_trait::async_trait;
|
|||
use hex::FromHex;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use thiserror::Error;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use url::{ParseError, Url};
|
||||
|
||||
use std::{convert::TryFrom, fmt::Debug, time::Duration};
|
||||
|
@ -167,7 +171,8 @@ impl<P: JsonRpcClient> Provider<P> {
|
|||
}
|
||||
|
||||
#[cfg(feature = "celo")]
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl<P: JsonRpcClient> CeloMiddleware for Provider<P> {
|
||||
async fn get_validators_bls_public_keys<T: Into<BlockId> + Send + Sync>(
|
||||
&self,
|
||||
|
@ -179,7 +184,8 @@ impl<P: JsonRpcClient> CeloMiddleware for Provider<P> {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl<P: JsonRpcClient> Middleware for Provider<P> {
|
||||
type Error = ProviderError;
|
||||
type Provider = P;
|
||||
|
@ -870,14 +876,23 @@ impl<P: JsonRpcClient> Provider<P> {
|
|||
#[cfg(feature = "ws")]
|
||||
impl Provider<crate::Ws> {
|
||||
/// Direct connection to a websocket endpoint
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub async fn connect(
|
||||
url: impl tokio_tungstenite::tungstenite::client::IntoClientRequest + Unpin,
|
||||
) -> Result<Self, ProviderError> {
|
||||
let ws = crate::Ws::connect(url).await?;
|
||||
Ok(Self::new(ws))
|
||||
}
|
||||
|
||||
/// Direct connection to a websocket endpoint
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub async fn connect(url: &str) -> Result<Self, ProviderError> {
|
||||
let ws = crate::Ws::connect(url).await?;
|
||||
Ok(Self::new(ws))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(feature = "ipc")]
|
||||
impl Provider<crate::Ipc> {
|
||||
/// Direct connection to an IPC socket.
|
||||
|
@ -926,6 +941,7 @@ fn decode_bytes<T: Detokenize>(param: ParamType, bytes: Bytes) -> T {
|
|||
T::from_tokens(tokens).expect("could not parse tokens as address")
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
impl TryFrom<&str> for Provider<HttpProvider> {
|
||||
type Error = ParseError;
|
||||
|
||||
|
@ -934,6 +950,7 @@ impl TryFrom<&str> for Provider<HttpProvider> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
impl TryFrom<String> for Provider<HttpProvider> {
|
||||
type Error = ParseError;
|
||||
|
||||
|
@ -943,8 +960,13 @@ impl TryFrom<String> for Provider<HttpProvider> {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod ens_tests {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::Http;
|
||||
use ethers_core::types::{TransactionRequest, H256};
|
||||
use ethers_core::utils::Geth;
|
||||
use futures_util::StreamExt;
|
||||
|
||||
const INFURA: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27";
|
||||
|
||||
|
@ -989,15 +1011,6 @@ mod ens_tests {
|
|||
.await
|
||||
.unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::Http;
|
||||
use ethers_core::types::{TransactionRequest, H256};
|
||||
use ethers_core::utils::Geth;
|
||||
use futures_util::StreamExt;
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "celo", ignore)]
|
||||
|
|
|
@ -4,7 +4,6 @@ use ethers_core::types::{Transaction, TxHash, U256};
|
|||
|
||||
use futures_core::stream::Stream;
|
||||
use futures_core::Future;
|
||||
use futures_timer::Delay;
|
||||
use futures_util::stream::FuturesUnordered;
|
||||
use futures_util::{stream, FutureExt, StreamExt};
|
||||
use pin_project::pin_project;
|
||||
|
@ -18,6 +17,11 @@ use std::{
|
|||
vec::IntoIter,
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use futures_timer::Delay;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_timer::Delay;
|
||||
|
||||
// https://github.com/tomusdrw/rust-web3/blob/befcb2fb8f3ca0a43e3081f68886fa327e64c8e6/src/api/eth_filter.rs#L20
|
||||
pub fn interval(duration: Duration) -> impl Stream<Item = ()> + Send + Unpin {
|
||||
stream::unfold((), move |_| Delay::new(duration).map(|_| Some(((), ())))).map(drop)
|
||||
|
@ -254,6 +258,7 @@ where
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{Http, Ws};
|
||||
|
|
|
@ -58,7 +58,8 @@ impl From<ClientError> for ProviderError {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl JsonRpcClient for Provider {
|
||||
type Error = ClientError;
|
||||
|
||||
|
|
|
@ -23,7 +23,8 @@ impl Default for MockProvider {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl JsonRpcClient for MockProvider {
|
||||
type Error = MockError;
|
||||
|
||||
|
@ -103,6 +104,7 @@ impl From<MockError> for ProviderError {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::Middleware;
|
||||
|
|
|
@ -1,17 +1,34 @@
|
|||
mod common;
|
||||
|
||||
macro_rules! if_wasm {
|
||||
($($item:item)*) => {$(
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
$item
|
||||
)*}
|
||||
}
|
||||
|
||||
macro_rules! if_not_wasm {
|
||||
($($item:item)*) => {$(
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
$item
|
||||
)*}
|
||||
}
|
||||
|
||||
if_not_wasm! {
|
||||
mod http;
|
||||
pub use http::Provider as Http;
|
||||
|
||||
|
||||
#[cfg(feature = "ipc")]
|
||||
mod ipc;
|
||||
#[cfg(feature = "ipc")]
|
||||
pub use ipc::Ipc;
|
||||
}
|
||||
|
||||
#[cfg(feature = "ws")]
|
||||
mod ws;
|
||||
#[cfg(feature = "ws")]
|
||||
pub use ws::Ws;
|
||||
|
||||
#[cfg(feature = "ipc")]
|
||||
mod ipc;
|
||||
#[cfg(feature = "ipc")]
|
||||
pub use ipc::Ipc;
|
||||
|
||||
mod mock;
|
||||
pub use mock::{MockError, MockProvider};
|
||||
|
|
|
@ -22,14 +22,46 @@ use std::{
|
|||
},
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
if_wasm! {
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
use ws_stream_wasm::*;
|
||||
|
||||
type Message = WsMessage;
|
||||
type WsError = ws_stream_wasm::WsErr;
|
||||
type WsStreamItem = Message;
|
||||
|
||||
macro_rules! error {
|
||||
( $( $t:tt )* ) => {
|
||||
web_sys::console::error_1(&format!( $( $t )* ).into());
|
||||
}
|
||||
}
|
||||
macro_rules! warn {
|
||||
( $( $t:tt )* ) => {
|
||||
web_sys::console::warn_1(&format!( $( $t )* ).into());
|
||||
}
|
||||
}
|
||||
macro_rules! debug {
|
||||
( $( $t:tt )* ) => {
|
||||
web_sys::console::log_1(&format!( $( $t )* ).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if_not_wasm! {
|
||||
use tokio_tungstenite::{
|
||||
connect_async,
|
||||
tungstenite::{
|
||||
self,
|
||||
protocol::{CloseFrame, Message},
|
||||
protocol::CloseFrame,
|
||||
},
|
||||
};
|
||||
use tracing::{error, warn};
|
||||
type Message = tungstenite::protocol::Message;
|
||||
type WsError = tungstenite::Error;
|
||||
type WsStreamItem = Result<Message, WsError>;
|
||||
use tracing::{debug, error, warn};
|
||||
}
|
||||
|
||||
type Pending = oneshot::Sender<Result<serde_json::Value, JsonRpcError>>;
|
||||
type Subscription = mpsc::UnboundedSender<serde_json::Value>;
|
||||
|
@ -84,11 +116,7 @@ impl Ws {
|
|||
/// The websocket connection must be initiated separately.
|
||||
pub fn new<S: 'static>(ws: S) -> Self
|
||||
where
|
||||
S: Send
|
||||
+ Sync
|
||||
+ Stream<Item = Result<Message, tungstenite::Error>>
|
||||
+ Sink<Message, Error = tungstenite::Error>
|
||||
+ Unpin,
|
||||
S: Send + Sync + Stream<Item = WsStreamItem> + Sink<Message, Error = WsError> + Unpin,
|
||||
{
|
||||
let (sink, stream) = mpsc::unbounded();
|
||||
|
||||
|
@ -107,6 +135,17 @@ impl Ws {
|
|||
}
|
||||
|
||||
/// Initializes a new WebSocket Client
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub async fn connect(url: &str) -> Result<Self, ClientError> {
|
||||
let (_, wsio) = WsMeta::connect(url, None)
|
||||
.await
|
||||
.expect_throw("Could not create websocket");
|
||||
|
||||
Ok(Self::new(wsio))
|
||||
}
|
||||
|
||||
/// Initializes a new WebSocket Client
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub async fn connect(
|
||||
url: impl tungstenite::client::IntoClientRequest + Unpin,
|
||||
) -> Result<Self, ClientError> {
|
||||
|
@ -121,7 +160,8 @@ impl Ws {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl JsonRpcClient for Ws {
|
||||
type Error = ClientError;
|
||||
|
||||
|
@ -181,11 +221,7 @@ struct WsServer<S> {
|
|||
|
||||
impl<S> WsServer<S>
|
||||
where
|
||||
S: Send
|
||||
+ Sync
|
||||
+ Stream<Item = Result<Message, tungstenite::Error>>
|
||||
+ Sink<Message, Error = tungstenite::Error>
|
||||
+ Unpin,
|
||||
S: Send + Sync + Stream<Item = WsStreamItem> + Sink<Message, Error = WsError> + Unpin,
|
||||
{
|
||||
/// Instantiates the Websocket Server
|
||||
fn new(ws: S, requests: mpsc::UnboundedReceiver<Instruction>) -> Self {
|
||||
|
@ -215,12 +251,12 @@ where
|
|||
let f = async move {
|
||||
loop {
|
||||
if self.is_done() {
|
||||
tracing::info!("work complete");
|
||||
debug!("work complete");
|
||||
break;
|
||||
}
|
||||
match self.tick().await {
|
||||
Err(ClientError::UnexpectedClose) => {
|
||||
tracing::error!("{}", ClientError::UnexpectedClose);
|
||||
error!("{}", ClientError::UnexpectedClose);
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -231,6 +267,10 @@ where
|
|||
}
|
||||
};
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
spawn_local(f);
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
tokio::spawn(f);
|
||||
}
|
||||
|
||||
|
@ -284,6 +324,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
async fn handle_ping(&mut self, inner: Vec<u8>) -> Result<(), ClientError> {
|
||||
self.ws.send(Message::Pong(inner)).await?;
|
||||
Ok(())
|
||||
|
@ -315,6 +356,15 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
async fn handle(&mut self, resp: Message) -> Result<(), ClientError> {
|
||||
match resp {
|
||||
Message::Text(inner) => self.handle_text(inner).await,
|
||||
Message::Binary(buf) => Err(ClientError::UnexpectedBinary(buf)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
async fn handle(&mut self, resp: Message) -> Result<(), ClientError> {
|
||||
match resp {
|
||||
Message::Text(inner) => self.handle_text(inner).await,
|
||||
|
@ -328,6 +378,28 @@ where
|
|||
|
||||
/// Processes 1 instruction or 1 incoming websocket message
|
||||
#[allow(clippy::single_match)]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
async fn tick(&mut self) -> Result<(), ClientError> {
|
||||
futures_util::select! {
|
||||
// Handle requests
|
||||
instruction = self.instructions.select_next_some() => {
|
||||
self.service(instruction).await?;
|
||||
},
|
||||
// Handle ws messages
|
||||
resp = self.ws.next() => match resp {
|
||||
Some(resp) => self.handle(resp).await?,
|
||||
None => {
|
||||
return Err(ClientError::UnexpectedClose);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Processes 1 instruction or 1 incoming websocket message
|
||||
#[allow(clippy::single_match)]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
async fn tick(&mut self) -> Result<(), ClientError> {
|
||||
futures_util::select! {
|
||||
// Handle requests
|
||||
|
@ -371,7 +443,7 @@ pub enum ClientError {
|
|||
|
||||
/// Thrown if there's an error over the WS connection
|
||||
#[error(transparent)]
|
||||
TungsteniteError(#[from] tungstenite::Error),
|
||||
TungsteniteError(#[from] WsError),
|
||||
|
||||
#[error("{0}")]
|
||||
ChannelError(String),
|
||||
|
@ -381,8 +453,14 @@ pub enum ClientError {
|
|||
|
||||
/// Remote server sent a Close message
|
||||
#[error("Websocket closed with info: {0:?}")]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
WsClosed(CloseFrame<'static>),
|
||||
|
||||
/// Remote server sent a Close message
|
||||
#[error("Websocket closed with info")]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
WsClosed,
|
||||
|
||||
/// Something caused the websocket to close
|
||||
#[error("WebSocket connection closed unexpectedly")]
|
||||
UnexpectedClose,
|
||||
|
@ -396,6 +474,7 @@ impl From<ClientError> for ProviderError {
|
|||
|
||||
#[cfg(test)]
|
||||
#[cfg(not(feature = "celo"))]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ethers_core::types::{Block, TxHash, U256};
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![cfg(not(target_arch = "wasm32"))]
|
||||
use ethers::providers::{Http, Middleware, Provider};
|
||||
use std::{convert::TryFrom, time::Duration};
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![cfg(not(target_arch = "wasm32"))]
|
||||
use ethers::{
|
||||
providers::{Http, Middleware, Provider},
|
||||
types::TransactionRequest,
|
||||
|
|
|
@ -19,7 +19,6 @@ thiserror = { version = "1.0.24", default-features = false }
|
|||
coins-bip32 = "0.3.0"
|
||||
coins-bip39 = "0.3.0"
|
||||
coins-ledger = { version = "0.3.0", default-features = false, optional = true }
|
||||
eth-keystore = { version = "0.3.0" }
|
||||
hex = { version = "0.4.3", default-features = false, features = ["std"] }
|
||||
async-trait = { version = "0.1.50", default-features = false }
|
||||
elliptic-curve = { version = "0.10.5", default-features = false }
|
||||
|
@ -36,14 +35,18 @@ tracing = { version = "0.1.26", optional = true }
|
|||
tracing-futures = { version = "0.2.5", optional = true }
|
||||
spki = { version = "0.4.0", optional = true }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
eth-keystore = { version = "0.3.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
ethers = { version = "0.4.0", path = ".." }
|
||||
tracing-subscriber = "0.2.20"
|
||||
yubihsm = { version = "0.39.0", features = ["secp256k1", "usb", "mockhsm"] }
|
||||
|
||||
tempfile = "3.2.0"
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||
yubihsm = { version = "0.39.0", features = ["secp256k1", "usb", "mockhsm"] }
|
||||
tokio = { version = "1.5", default-features = false, features = ["macros"] }
|
||||
|
||||
tracing-subscriber = "0.2.20"
|
||||
tempfile = "3.2.0"
|
||||
|
||||
[features]
|
||||
celo = ["ethers-core/celo"]
|
||||
|
|
|
@ -7,7 +7,8 @@ use async_trait::async_trait;
|
|||
use ethers_core::types::{transaction::eip2718::TypedTransaction, Address, Signature};
|
||||
use types::LedgerError;
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl Signer for LedgerEthereum {
|
||||
type Error = LedgerError;
|
||||
|
||||
|
|
|
@ -81,7 +81,8 @@ pub fn to_eip155_v<T: Into<u8>>(recovery_id: T, chain_id: u64) -> u64 {
|
|||
/// Trait for signing transactions and messages
|
||||
///
|
||||
/// Implement this trait to support different signing modes, e.g. Ledger, hosted etc.
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait Signer: std::fmt::Debug + Send + Sync {
|
||||
type Error: Error + Send + Sync;
|
||||
/// Signs the hash of the provided message after prefixing it
|
||||
|
|
|
@ -189,6 +189,7 @@ impl<W: Wordlist> MnemonicBuilder<W> {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
|
|
|
@ -64,7 +64,8 @@ pub struct Wallet<D: DigestSigner<Sha256Proxy, RecoverableSignature>> {
|
|||
pub(crate) chain_id: u64,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl<D: Sync + Send + DigestSigner<Sha256Proxy, RecoverableSignature>> Signer for Wallet<D> {
|
||||
type Error = std::convert::Infallible;
|
||||
|
||||
|
|
|
@ -4,14 +4,18 @@ use super::Wallet;
|
|||
use crate::wallet::mnemonic::MnemonicBuilderError;
|
||||
use coins_bip32::Bip32Error;
|
||||
use coins_bip39::MnemonicError;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use elliptic_curve::rand_core;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use eth_keystore::KeystoreError;
|
||||
use ethers_core::{
|
||||
k256::ecdsa::{self, SigningKey},
|
||||
rand::{CryptoRng, Rng},
|
||||
utils::secret_key_to_address,
|
||||
};
|
||||
use std::{path::Path, str::FromStr};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
|
@ -24,6 +28,7 @@ pub enum WalletError {
|
|||
#[error(transparent)]
|
||||
Bip39Error(#[from] MnemonicError),
|
||||
/// Underlying eth keystore error
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[error(transparent)]
|
||||
EthKeystoreError(#[from] KeystoreError),
|
||||
/// Error propagated from k256's ECDSA module
|
||||
|
@ -54,6 +59,7 @@ impl Clone for Wallet<SigningKey> {
|
|||
impl Wallet<SigningKey> {
|
||||
/// Creates a new random encrypted JSON with the provided password and stores it in the
|
||||
/// provided directory
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn new_keystore<P, R, S>(dir: P, rng: &mut R, password: S) -> Result<Self, WalletError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
|
@ -71,6 +77,7 @@ impl Wallet<SigningKey> {
|
|||
}
|
||||
|
||||
/// Decrypts an encrypted JSON from the provided path to construct a Wallet instance
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn decrypt_keystore<P, S>(keypath: P, password: S) -> Result<Self, WalletError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
|
@ -145,6 +152,7 @@ impl FromStr for Wallet<SigningKey> {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::Signer;
|
||||
|
|
|
@ -64,6 +64,7 @@ impl From<YubiSigner<Secp256k1>> for Wallet<YubiSigner<Secp256k1>> {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::Signer;
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
bin/
|
||||
pkg/
|
||||
wasm-pack.log
|
||||
node_modules/
|
||||
yarn-error.log
|
|
@ -0,0 +1,52 @@
|
|||
[package]
|
||||
name = "ethers-wasm"
|
||||
version = "0.1.0"
|
||||
authors = ["Matthias Seitz <matthias.seitz@outlook.de>"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/ethers"
|
||||
repository = "https://github.com/gakonst/ethers-rs"
|
||||
homepage = "https://docs.rs/ethers"
|
||||
description = """
|
||||
How to use ethers in the browser with WASM.
|
||||
"""
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
default = ["console_error_panic_hook"]
|
||||
|
||||
[dependencies]
|
||||
ethers = { path = "../..", features = ["abigen", "legacy", "ws"] }
|
||||
serde_derive = "1.0.126"
|
||||
wasm-bindgen-futures = "0.4.24"
|
||||
serde_json = "1.0.64"
|
||||
wasm-bindgen = { version = "0.2.74", features = ["serde-serialize"] }
|
||||
|
||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||
# logging them with `console.error`. This is great for development, but requires
|
||||
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
|
||||
# code size when deploying.
|
||||
console_error_panic_hook = { version = "0.1.6", optional = true }
|
||||
|
||||
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
|
||||
# compared to the default allocator's ~10K. It is slower than the default
|
||||
# allocator, however.
|
||||
#
|
||||
# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
|
||||
wee_alloc = { version = "0.4.5", optional = true }
|
||||
|
||||
serde = { version = "1.0.126", features = ["derive"] }
|
||||
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"
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
## Example usage of ethers-rs from WASM
|
||||
|
||||
Install wasm-pack with
|
||||
|
||||
yarn install
|
||||
|
||||
Start a local ganache instance
|
||||
|
||||
yarn ganache
|
||||
|
||||
Then you can build the example locally with:
|
||||
|
||||
yarn serve
|
||||
|
||||
and then visiting http://localhost:8080 in a browser should run the example!
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>ethers WASM example</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="index.js"></script>
|
||||
<h1>ethers WASM</h1>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,8 @@
|
|||
const ethers = import('./pkg');
|
||||
|
||||
ethers
|
||||
.then(m => {
|
||||
m.setup();
|
||||
m.deploy().catch(console.error);
|
||||
})
|
||||
.catch(console.error);
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "ethers-wasm",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"scripts": {
|
||||
"build": "webpack",
|
||||
"serve": "webpack-dev-server",
|
||||
"ganache": "ganache-cli --blockTime 5 --seed ethers-wasm-seed"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@wasm-tool/wasm-pack-plugin": "1.0.1",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"text-encoding": "^0.7.0",
|
||||
"webpack": "^4.29.4",
|
||||
"webpack-cli": "^3.1.1",
|
||||
"webpack-dev-server": "^3.1.0",
|
||||
"ganache-cli": "^6.12.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
pub mod utils;
|
||||
|
||||
use crate::utils::SIMPLECONTRACT_BIN;
|
||||
use ethers::{
|
||||
contract::abigen,
|
||||
prelude::{ContractFactory, LocalWallet, Provider, SignerMiddleware},
|
||||
providers::{Middleware, Ws},
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::console;
|
||||
|
||||
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
|
||||
// allocator.
|
||||
#[cfg(feature = "wee_alloc")]
|
||||
#[global_allocator]
|
||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
fn alert(s: &str);
|
||||
}
|
||||
|
||||
macro_rules! log {
|
||||
( $( $t:tt )* ) => {
|
||||
web_sys::console::log_1(&format!( $( $t )* ).into());
|
||||
}
|
||||
}
|
||||
|
||||
abigen!(
|
||||
SimpleContract,
|
||||
"./../contract_abi.json",
|
||||
event_derives(serde::Deserialize, serde::Serialize)
|
||||
);
|
||||
|
||||
/// key[0] of ganache with custom seed `ethers-wasm-seed`
|
||||
pub const KEY: &str = "817169e55f14ede54f4fd6a4f2ab4209db14aeeb1b9972b3b28f1560af0a061a";
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn setup() {
|
||||
utils::set_panic_hook();
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub async fn deploy() {
|
||||
console::log_2(
|
||||
&"ABI: ".into(),
|
||||
&JsValue::from_serde(&*SIMPLECONTRACT_ABI).unwrap(),
|
||||
);
|
||||
let wallet: LocalWallet = KEY.parse().unwrap();
|
||||
log!("Wallet: {:?}", wallet);
|
||||
|
||||
let endpoint = "ws://127.0.0.1:8545";
|
||||
let provider = Provider::new(Ws::connect(endpoint).await.unwrap());
|
||||
let client = Arc::new(SignerMiddleware::new(provider, wallet));
|
||||
log!("provider connected to `{}`", endpoint);
|
||||
let version = client.client_version().await;
|
||||
log!("version {:?}", version);
|
||||
let account = client.get_accounts().await.unwrap()[0];
|
||||
log!("account {:?}", account);
|
||||
|
||||
let bytecode = hex::decode(SIMPLECONTRACT_BIN).unwrap();
|
||||
let factory = ContractFactory::new(SIMPLECONTRACT_ABI.clone(), bytecode.into(), client.clone());
|
||||
let init = "hello WASM!";
|
||||
let contract = factory
|
||||
.deploy(init.to_string())
|
||||
.unwrap()
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
let addr = contract.address();
|
||||
log!("deployed contract with address {}", addr);
|
||||
|
||||
let contract = SimpleContract::new(addr, client.clone());
|
||||
|
||||
let value = contract.get_value().call().await.unwrap();
|
||||
assert_eq!(init, &value);
|
||||
|
||||
let _receipt = contract
|
||||
.set_value("bye WASM!".to_owned())
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.await
|
||||
.unwrap();
|
||||
log!("set value");
|
||||
// 10. get all events
|
||||
let logs = contract
|
||||
.value_changed_filter()
|
||||
.from_block(0u64)
|
||||
.query()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let value = contract.get_value().call().await.unwrap();
|
||||
|
||||
log!(
|
||||
"Value: {}. Logs: {:?}",
|
||||
value,
|
||||
JsValue::from_serde(&logs).unwrap()
|
||||
);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
pub fn set_panic_hook() {
|
||||
// When the `console_error_panic_hook` feature is enabled, we can call the
|
||||
// `set_panic_hook` function at least once during initialization, and then
|
||||
// we will get better error messages if our code ever panics.
|
||||
//
|
||||
// For more details see
|
||||
// https://github.com/rustwasm/console_error_panic_hook#readme
|
||||
#[cfg(feature = "console_error_panic_hook")]
|
||||
console_error_panic_hook::set_once();
|
||||
}
|
||||
|
||||
pub const SIMPLECONTRACT_BIN: &str = "608060405234801561001057600080fd5b5060405161073b38038061073b8339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b8382019150602082018581111561006957600080fd5b825186600182028301116401000000008211171561008657600080fd5b8083526020830192505050908051906020019080838360005b838110156100ba57808201518184015260208101905061009f565b50505050905090810190601f1680156100e75780820380516001836020036101000a031916815260200191505b506040525050503373ffffffffffffffffffffffffffffffffffffffff167fe826f71647b8486f2bae59832124c70792fba044036720a54ec8dacdd5df4fcb6000836040518080602001806020018381038352858181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156101b65780601f1061018b576101008083540402835291602001916101b6565b820191906000526020600020905b81548152906001019060200180831161019957829003601f168201915b5050838103825284818151815260200191508051906020019080838360005b838110156101f05780820151818401526020810190506101d5565b50505050905090810190601f16801561021d5780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a28060009080519060200190610242929190610249565b50506102e6565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061028a57805160ff19168380011785556102b8565b828001600101855582156102b8579182015b828111156102b757825182559160200191906001019061029c565b5b5090506102c591906102c9565b5090565b5b808211156102e25760008160009055506001016102ca565b5090565b610446806102f56000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063209652551461003b57806393a09352146100be575b600080fd5b610043610179565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610083578082015181840152602081019050610068565b50505050905090810190601f1680156100b05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610177600480360360208110156100d457600080fd5b81019080803590602001906401000000008111156100f157600080fd5b82018360208201111561010357600080fd5b8035906020019184600183028401116401000000008311171561012557600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061021b565b005b606060008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102115780601f106101e657610100808354040283529160200191610211565b820191906000526020600020905b8154815290600101906020018083116101f457829003601f168201915b5050505050905090565b3373ffffffffffffffffffffffffffffffffffffffff167fe826f71647b8486f2bae59832124c70792fba044036720a54ec8dacdd5df4fcb6000836040518080602001806020018381038352858181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156102e35780601f106102b8576101008083540402835291602001916102e3565b820191906000526020600020905b8154815290600101906020018083116102c657829003601f168201915b5050838103825284818151815260200191508051906020019080838360005b8381101561031d578082015181840152602081019050610302565b50505050905090810190601f16801561034a5780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a2806000908051906020019061036f929190610373565b5050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106103b457805160ff19168380011785556103e2565b828001600101855582156103e2579182015b828111156103e15782518255916020019190600101906103c6565b5b5090506103ef91906103f3565b5090565b5b8082111561040c5760008160009055506001016103f4565b509056fea26469706673582212202d397d3d0e6cf9afdeed7d5192e3abff386699395df0409eb5c3ec494832c57a64736f6c63430007000033";
|
|
@ -0,0 +1,71 @@
|
|||
//! Test suite for the Web and headless browsers.
|
||||
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
use ethers::{
|
||||
contract::abigen,
|
||||
prelude::{ContractFactory, LocalWallet, Provider, SignerMiddleware},
|
||||
providers::Ws,
|
||||
};
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
// Generate the type-safe contract bindings by providing the ABI
|
||||
// definition in human readable format
|
||||
abigen!(
|
||||
SimpleContract,
|
||||
"../contract_abi.json",
|
||||
event_derives(serde::Deserialize, serde::Serialize)
|
||||
);
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
async fn connect_and_deploy() {
|
||||
console_log!("starting");
|
||||
|
||||
// a private key of a launched ganache `yarn ganache`
|
||||
let wallet: LocalWallet = ethers_wasm::KEY.parse().unwrap();
|
||||
|
||||
let provider = Provider::new(Ws::connect("ws://localhost:8545").await.unwrap());
|
||||
let client = Arc::new(SignerMiddleware::new(provider, wallet));
|
||||
|
||||
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("initial value".to_string())
|
||||
.unwrap()
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
let addr = contract.address();
|
||||
console_log!("deployed to {}", addr);
|
||||
|
||||
let contract = SimpleContract::new(addr, client.clone());
|
||||
let _receipt = contract
|
||||
.set_value("hi".to_owned())
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// get all events
|
||||
let logs = contract
|
||||
.value_changed_filter()
|
||||
.from_block(0u64)
|
||||
.query()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let value = contract.get_value().call().await.unwrap();
|
||||
|
||||
console_log!(
|
||||
"Value: {}. Logs: {:?}",
|
||||
value,
|
||||
JsValue::from_serde(&logs).unwrap()
|
||||
);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
const path = require('path');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const webpack = require('webpack');
|
||||
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
|
||||
|
||||
module.exports = {
|
||||
entry: './index.js',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'index.js',
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
template: './index.html'
|
||||
}
|
||||
),
|
||||
new WasmPackPlugin({
|
||||
crateDirectory: path.resolve(__dirname, ".")
|
||||
}),
|
||||
// Have this example work in Edge which doesn't ship `TextEncoder` or
|
||||
// `TextDecoder` at this time.
|
||||
new webpack.ProvidePlugin({
|
||||
TextDecoder: ['text-encoding', 'TextDecoder'],
|
||||
TextEncoder: ['text-encoding', 'TextEncoder']
|
||||
})
|
||||
],
|
||||
mode: 'development'
|
||||
};
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue