From d35444cc493e6f577801f743fb07c5993b44b96f Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 24 Aug 2021 00:28:05 +0200 Subject: [PATCH] feat: add wasm http provider support (#403) * fix: use on wasm only * feat: enable http provider in wasm * add http example * chore: add more verbosity to example * fix: double webpack issue * use mnemonic builder --- .../ethers-contract-abigen/Cargo.toml | 2 +- ethers-providers/src/provider.rs | 8 +- ethers-providers/src/transports/mod.rs | 7 +- examples/ethers-wasm/index.js | 1 - examples/ethers-wasm/package.json | 2 +- examples/ethers-wasm/src/lib.rs | 77 ++++++++----------- examples/ethers-wasm/src/utils.rs | 17 ++++ .../ethers-wasm/tests/contract_with_abi.rs | 51 ++++-------- examples/ethers-wasm/webpack.config.js | 3 +- 9 files changed, 76 insertions(+), 92 deletions(-) diff --git a/ethers-contract/ethers-contract-abigen/Cargo.toml b/ethers-contract/ethers-contract-abigen/Cargo.toml index 338efd1c..60d89824 100644 --- a/ethers-contract/ethers-contract-abigen/Cargo.toml +++ b/ethers-contract/ethers-contract-abigen/Cargo.toml @@ -26,7 +26,7 @@ once_cell = "1.8.0" cargo_metadata = "0.14.0" cfg-if = "1.0.0" -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +[target.'cfg(target_arch = "wasm32")'.dependencies] # NOTE: this enables wasm compatibility for getrandom indirectly getrandom = { version = "0.2", features = ["js"] } diff --git a/ethers-providers/src/provider.rs b/ethers-providers/src/provider.rs index 71a72be1..446c41a1 100644 --- a/ethers-providers/src/provider.rs +++ b/ethers-providers/src/provider.rs @@ -2,12 +2,9 @@ use crate::{ ens, pubsub::{PubsubClient, SubscriptionStream}, stream::{FilterWatcher, DEFAULT_POLL_INTERVAL}, - FeeHistory, FromErr, JsonRpcClient, MockProvider, PendingTransaction, + FeeHistory, FromErr, Http as HttpProvider, JsonRpcClient, MockProvider, PendingTransaction, }; -#[cfg(not(target_arch = "wasm32"))] -use crate::Http as HttpProvider; - use ethers_core::{ abi::{self, Detokenize, ParamType}, types::{ @@ -26,7 +23,6 @@ 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}; @@ -941,7 +937,6 @@ fn decode_bytes(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 { type Error = ParseError; @@ -950,7 +945,6 @@ impl TryFrom<&str> for Provider { } } -#[cfg(not(target_arch = "wasm32"))] impl TryFrom for Provider { type Error = ParseError; diff --git a/ethers-providers/src/transports/mod.rs b/ethers-providers/src/transports/mod.rs index 0550872b..535dd413 100644 --- a/ethers-providers/src/transports/mod.rs +++ b/ethers-providers/src/transports/mod.rs @@ -15,16 +15,15 @@ macro_rules! if_not_wasm { } if_not_wasm! { - mod http; - pub use http::Provider as Http; - - #[cfg(feature = "ipc")] mod ipc; #[cfg(feature = "ipc")] pub use ipc::Ipc; } +mod http; +pub use http::Provider as Http; + #[cfg(feature = "ws")] mod ws; #[cfg(feature = "ws")] diff --git a/examples/ethers-wasm/index.js b/examples/ethers-wasm/index.js index 1771e1a6..4d235834 100644 --- a/examples/ethers-wasm/index.js +++ b/examples/ethers-wasm/index.js @@ -2,7 +2,6 @@ const ethers = import('./pkg'); ethers .then(m => { - m.setup(); m.deploy().catch(console.error); }) .catch(console.error); \ No newline at end of file diff --git a/examples/ethers-wasm/package.json b/examples/ethers-wasm/package.json index 97ae273c..4a5d39eb 100644 --- a/examples/ethers-wasm/package.json +++ b/examples/ethers-wasm/package.json @@ -4,7 +4,7 @@ "scripts": { "build": "webpack", "serve": "webpack-dev-server", - "ganache": "ganache-cli --blockTime 5 --seed ethers-wasm-seed" + "ganache": "ganache-cli --blockTime 2 -m \"stuff inherit faith park genre spread huge knee ecology private marble supreme\"" }, "devDependencies": { "@wasm-tool/wasm-pack-plugin": "1.0.1", diff --git a/examples/ethers-wasm/src/lib.rs b/examples/ethers-wasm/src/lib.rs index 3a211b77..84be0071 100644 --- a/examples/ethers-wasm/src/lib.rs +++ b/examples/ethers-wasm/src/lib.rs @@ -1,90 +1,82 @@ -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; +use ethers::{ + contract::abigen, + prelude::{ContractFactory, Provider, SignerMiddleware}, + providers::Ws, +}; + +use crate::utils::SIMPLECONTRACT_BIN; + +pub mod utils; + // 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() { + utils::set_panic_hook(); + console::log_2( - &"ABI: ".into(), + &"SimpleContract ABI: ".into(), &JsValue::from_serde(&*SIMPLECONTRACT_ABI).unwrap(), ); - let wallet: LocalWallet = KEY.parse().unwrap(); + + let wallet = utils::key(0); 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); + log!("Provider connected to `{}`", endpoint); let bytecode = hex::decode(SIMPLECONTRACT_BIN).unwrap(); let factory = ContractFactory::new(SIMPLECONTRACT_ABI.clone(), bytecode.into(), client.clone()); - let init = "hello WASM!"; + + log!("Deploying contract..."); let contract = factory - .deploy(init.to_string()) + .deploy("hello WASM!".to_string()) .unwrap() .send() .await .unwrap(); let addr = contract.address(); - log!("deployed contract with address {}", addr); + 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()) + let value = "bye from WASM!"; + log!("Setting value... `{}`", value); + let receipt = contract + .set_value(value.to_owned()) .send() .await .unwrap() .await .unwrap(); - log!("set value"); - // 10. get all events + console::log_2( + &"Set value receipt: ".into(), + &JsValue::from_serde(&receipt).unwrap(), + ); + + log!("Fetching logs..."); let logs = contract .value_changed_filter() .from_block(0u64) @@ -94,9 +86,8 @@ pub async fn deploy() { let value = contract.get_value().call().await.unwrap(); - log!( - "Value: {}. Logs: {:?}", - value, - JsValue::from_serde(&logs).unwrap() + console::log_2( + &format!("Value: `{}`. Logs: ", value).into(), + &JsValue::from_serde(&logs).unwrap(), ); } diff --git a/examples/ethers-wasm/src/utils.rs b/examples/ethers-wasm/src/utils.rs index 8d678d3b..091ca848 100644 --- a/examples/ethers-wasm/src/utils.rs +++ b/examples/ethers-wasm/src/utils.rs @@ -1,3 +1,6 @@ +use ethers::prelude::{LocalWallet, MnemonicBuilder}; +use ethers::signers::coins_bip39::English; + 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 @@ -9,4 +12,18 @@ pub fn set_panic_hook() { console_error_panic_hook::set_once(); } +/// The mnemonic phrase used by ganache +pub const PHRASE: &str = + "stuff inherit faith park genre spread huge knee ecology private marble supreme"; + +pub fn key(index: u32) -> LocalWallet { + MnemonicBuilder::::default() + .phrase(PHRASE) + .index(index) + .unwrap() + .build() + .unwrap() +} + +/// Bytecode of the `SimpleContract` pub const SIMPLECONTRACT_BIN: &str = "608060405234801561001057600080fd5b5060405161073b38038061073b8339818101604052602081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b8382019150602082018581111561006957600080fd5b825186600182028301116401000000008211171561008657600080fd5b8083526020830192505050908051906020019080838360005b838110156100ba57808201518184015260208101905061009f565b50505050905090810190601f1680156100e75780820380516001836020036101000a031916815260200191505b506040525050503373ffffffffffffffffffffffffffffffffffffffff167fe826f71647b8486f2bae59832124c70792fba044036720a54ec8dacdd5df4fcb6000836040518080602001806020018381038352858181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156101b65780601f1061018b576101008083540402835291602001916101b6565b820191906000526020600020905b81548152906001019060200180831161019957829003601f168201915b5050838103825284818151815260200191508051906020019080838360005b838110156101f05780820151818401526020810190506101d5565b50505050905090810190601f16801561021d5780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a28060009080519060200190610242929190610249565b50506102e6565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061028a57805160ff19168380011785556102b8565b828001600101855582156102b8579182015b828111156102b757825182559160200191906001019061029c565b5b5090506102c591906102c9565b5090565b5b808211156102e25760008160009055506001016102ca565b5090565b610446806102f56000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063209652551461003b57806393a09352146100be575b600080fd5b610043610179565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610083578082015181840152602081019050610068565b50505050905090810190601f1680156100b05780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610177600480360360208110156100d457600080fd5b81019080803590602001906401000000008111156100f157600080fd5b82018360208201111561010357600080fd5b8035906020019184600183028401116401000000008311171561012557600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061021b565b005b606060008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102115780601f106101e657610100808354040283529160200191610211565b820191906000526020600020905b8154815290600101906020018083116101f457829003601f168201915b5050505050905090565b3373ffffffffffffffffffffffffffffffffffffffff167fe826f71647b8486f2bae59832124c70792fba044036720a54ec8dacdd5df4fcb6000836040518080602001806020018381038352858181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156102e35780601f106102b8576101008083540402835291602001916102e3565b820191906000526020600020905b8154815290600101906020018083116102c657829003601f168201915b5050838103825284818151815260200191508051906020019080838360005b8381101561031d578082015181840152602081019050610302565b50505050905090810190601f16801561034a5780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a2806000908051906020019061036f929190610373565b5050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106103b457805160ff19168380011785556103e2565b828001600101855582156103e2579182015b828111156103e15782518255916020019190600101906103c6565b5b5090506103ef91906103f3565b5090565b5b8082111561040c5760008160009055506001016103f4565b509056fea26469706673582212202d397d3d0e6cf9afdeed7d5192e3abff386699395df0409eb5c3ec494832c57a64736f6c63430007000033"; diff --git a/examples/ethers-wasm/tests/contract_with_abi.rs b/examples/ethers-wasm/tests/contract_with_abi.rs index f7610a2a..8dc61db6 100644 --- a/examples/ethers-wasm/tests/contract_with_abi.rs +++ b/examples/ethers-wasm/tests/contract_with_abi.rs @@ -1,16 +1,12 @@ -//! 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 ethers::prelude::{ + abigen, ContractFactory, Http, JsonRpcClient, LocalWallet, Provider, SignerMiddleware, Ws, }; +use std::convert::TryFrom; use std::sync::Arc; wasm_bindgen_test_configure!(run_in_browser); @@ -24,19 +20,26 @@ abigen!( ); #[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(); +async fn http_connect_and_deploy() { + console_log!("connecting http..."); + let provider = Provider::::try_from("http://localhost:8545").unwrap(); + deploy(provider, ethers_wasm::utils::key(0)).await; +} +#[wasm_bindgen_test] +async fn ws_connect_and_deploy() { + console_log!("connecting ws..."); let provider = Provider::new(Ws::connect("ws://localhost:8545").await.unwrap()); + deploy(provider, ethers_wasm::utils::key(1)).await; +} + +async fn deploy(provider: Provider, wallet: LocalWallet) { 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()) + .deploy("Hello from Contract!".to_string()) .unwrap() .send() .await @@ -45,27 +48,7 @@ async fn connect_and_deploy() { 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() - ); + console_log!("value: {:?}", value); } diff --git a/examples/ethers-wasm/webpack.config.js b/examples/ethers-wasm/webpack.config.js index 53fb28d9..fd30d023 100644 --- a/examples/ethers-wasm/webpack.config.js +++ b/examples/ethers-wasm/webpack.config.js @@ -11,7 +11,8 @@ module.exports = { }, plugins: [ new HtmlWebpackPlugin({ - template: './index.html' + template: './index.html', + inject: false } ), new WasmPackPlugin({