mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 20:01:08 +00:00
add wasm support (#700)
* get started * make it work again * make it compile again * Use async-wasm-feature of jsonrpsee from the master branch * Ensure we enable JS feature of getrandom for the wasm target * Update subxt/src/lib.rs * update jsonrpsee * fix CI * cargo fmt * fix wasm test * fix grumbles * exclude wasm-tests from workspace To avoid leaking `jsonrpsee-web` feature into the workspace Co-authored-by: Igor Matuszewski <xanewok@gmail.com>
This commit is contained in:
@@ -42,11 +42,14 @@ jobs:
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@359a70e43a0bb8a13953b04a90f76428b4959bb6 # v2.2.0
|
||||
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1.0.3
|
||||
- name: Install cargo-hack
|
||||
uses: baptiste0928/cargo-install@v1
|
||||
with:
|
||||
command: check
|
||||
args: --all-targets --all-features --workspace
|
||||
crate: cargo-hack
|
||||
version: 0.5
|
||||
|
||||
- name: Cargo check
|
||||
run: cargo hack --exclude-all-features --each-feature check --all-targets --workspace
|
||||
|
||||
fmt:
|
||||
name: Cargo fmt
|
||||
@@ -165,3 +168,28 @@ jobs:
|
||||
with:
|
||||
command: clippy
|
||||
args: --all-targets -- -D warnings
|
||||
|
||||
wasm_tests:
|
||||
name: Test wasm
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.1.0
|
||||
|
||||
- name: Install
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
|
||||
- name: Download Substrate
|
||||
run: |
|
||||
curl $SUBSTRATE_URL --output substrate --location
|
||||
chmod +x substrate
|
||||
mkdir -p ~/.local/bin
|
||||
mv substrate ~/.local/bin
|
||||
|
||||
- name: Run WASM tests
|
||||
run: |
|
||||
substrate --dev --tmp > /dev/null 2>&1 &
|
||||
wasm-pack test --headless --firefox
|
||||
wasm-pack test --headless --chrome
|
||||
pkill substrate
|
||||
working-directory: testing/wasm-tests
|
||||
|
||||
+4
-1
@@ -9,5 +9,8 @@ members = [
|
||||
"testing/ui-tests",
|
||||
"macro",
|
||||
"metadata",
|
||||
"subxt",
|
||||
"subxt"
|
||||
]
|
||||
exclude = ["testing/wasm-tests"]
|
||||
|
||||
resolver = "2"
|
||||
|
||||
+1
-1
@@ -36,6 +36,6 @@ scale = { package = "parity-scale-codec", version = "3.0.0", default-features =
|
||||
# generate the item mod for codegen
|
||||
syn = "1.0.80"
|
||||
# communicate with the substrate nodes
|
||||
jsonrpsee = { version = "0.15.1", features = ["async-client", "client-ws-transport", "http-client"] }
|
||||
jsonrpsee = { version = "0.16.0", features = ["async-client", "client-ws-transport", "http-client"] }
|
||||
# async runtime
|
||||
tokio = { version = "1.8", features = ["rt-multi-thread", "macros", "time"] }
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ quote = "1.0.8"
|
||||
syn = "1.0.58"
|
||||
scale-info = { version = "2.0.0", features = ["bit-vec"] }
|
||||
subxt-metadata = { version = "0.24.0", path = "../metadata" }
|
||||
jsonrpsee = { version = "0.15.1", features = ["async-client", "client-ws-transport", "http-client"] }
|
||||
jsonrpsee = { version = "0.16.0", features = ["async-client", "client-ws-transport", "http-client"] }
|
||||
hex = "0.4.3"
|
||||
tokio = { version = "1.8", features = ["macros", "rt-multi-thread"] }
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ async fn fetch_metadata_http(url: &Uri) -> Result<String, FetchMetadataError> {
|
||||
.request_timeout(Duration::from_secs(180))
|
||||
.build(url.to_string())?;
|
||||
|
||||
Ok(client.request::<String>("state_getMetadata", None).await?)
|
||||
Ok(client.request("state_getMetadata", rpc_params![]).await?)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
+9
-5
@@ -13,7 +13,7 @@ description = "Submit extrinsics (transactions) to a substrate node via RPC"
|
||||
keywords = ["parity", "substrate", "blockchain"]
|
||||
|
||||
[features]
|
||||
default = ["jsonrpsee"]
|
||||
default = ["jsonrpsee-ws"]
|
||||
|
||||
# Activate this to expose functionality only used for integration testing.
|
||||
# The exposed functionality is subject to breaking changes at any point,
|
||||
@@ -22,7 +22,8 @@ integration-tests = []
|
||||
|
||||
# Jsonrpsee if the default RPC provider used in Subxt. However, it can be
|
||||
# swapped out for an alternative implementation, and so is optional.
|
||||
jsonrpsee = ["dep:jsonrpsee"]
|
||||
jsonrpsee-ws = ["jsonrpsee/async-client", "jsonrpsee/client-ws-transport"]
|
||||
jsonrpsee-web = ["jsonrpsee/async-wasm-client", "jsonrpsee/client-web-transport"]
|
||||
|
||||
[dependencies]
|
||||
bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] }
|
||||
@@ -30,11 +31,11 @@ codec = { package = "parity-scale-codec", version = "3.0.0", default-features =
|
||||
scale-info = { version = "2.0.0", features = ["bit-vec"] }
|
||||
scale-value = "0.6.0"
|
||||
scale-decode = "0.4.0"
|
||||
futures = "0.3.13"
|
||||
futures = { version = "0.3.13", default-features = false }
|
||||
hex = "0.4.3"
|
||||
jsonrpsee = { version = "0.15.1", features = ["async-client", "client-ws-transport", "jsonrpsee-types"], optional = true }
|
||||
jsonrpsee = { version = "0.16", optional = true, features = ["jsonrpsee-types"] }
|
||||
serde = { version = "1.0.124", features = ["derive"] }
|
||||
serde_json = "1.0.64"
|
||||
serde_json = { version = "1.0.64", features = ["raw_value"] }
|
||||
thiserror = "1.0.24"
|
||||
tracing = "0.1.34"
|
||||
parking_lot = "0.12.0"
|
||||
@@ -48,5 +49,8 @@ sp-runtime = "6.0.0"
|
||||
frame-metadata = "15.0.0"
|
||||
derivative = "2.2.0"
|
||||
|
||||
[target.wasm32-unknown-unknown.dependencies]
|
||||
getrandom = { version = "0.2", features = ["js"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.8", features = ["macros", "time", "rt-multi-thread"] }
|
||||
|
||||
@@ -61,7 +61,10 @@ impl<T: Config> std::fmt::Debug for OnlineClient<T> {
|
||||
}
|
||||
|
||||
// The default constructors assume Jsonrpsee.
|
||||
#[cfg(feature = "jsonrpsee")]
|
||||
#[cfg(any(
|
||||
feature = "jsonrpsee-ws",
|
||||
all(feature = "jsonrpsee-web", target_arch = "wasm32")
|
||||
))]
|
||||
impl<T: Config> OnlineClient<T> {
|
||||
/// Construct a new [`OnlineClient`] using default settings which
|
||||
/// point to a locally running node on `ws://127.0.0.1:9944`.
|
||||
@@ -72,7 +75,7 @@ impl<T: Config> OnlineClient<T> {
|
||||
|
||||
/// Construct a new [`OnlineClient`], providing a URL to connect to.
|
||||
pub async fn from_url(url: impl AsRef<str>) -> Result<OnlineClient<T>, Error> {
|
||||
let client = jsonrpsee_helpers::ws_client(url.as_ref())
|
||||
let client = jsonrpsee_helpers::client(url.as_ref())
|
||||
.await
|
||||
.map_err(|e| crate::error::RpcError::ClientError(Box::new(e)))?;
|
||||
OnlineClient::from_rpc_client(Arc::new(client)).await
|
||||
@@ -346,7 +349,7 @@ impl Update {
|
||||
}
|
||||
|
||||
// helpers for a jsonrpsee specific OnlineClient.
|
||||
#[cfg(feature = "jsonrpsee")]
|
||||
#[cfg(feature = "jsonrpsee-ws")]
|
||||
mod jsonrpsee_helpers {
|
||||
pub use jsonrpsee::{
|
||||
client_transport::ws::{
|
||||
@@ -366,7 +369,7 @@ mod jsonrpsee_helpers {
|
||||
};
|
||||
|
||||
/// Build WS RPC client from URL
|
||||
pub async fn ws_client(url: &str) -> Result<Client, Error> {
|
||||
pub async fn client(url: &str) -> Result<Client, Error> {
|
||||
let (sender, receiver) = ws_transport(url).await?;
|
||||
Ok(ClientBuilder::default()
|
||||
.max_notifs_per_subscription(4096)
|
||||
@@ -383,3 +386,26 @@ mod jsonrpsee_helpers {
|
||||
.map_err(|e| Error::Transport(e.into()))
|
||||
}
|
||||
}
|
||||
|
||||
// helpers for a jsonrpsee specific OnlineClient.
|
||||
#[cfg(all(feature = "jsonrpsee-web", target_arch = "wasm32"))]
|
||||
mod jsonrpsee_helpers {
|
||||
pub use jsonrpsee::{
|
||||
client_transport::web,
|
||||
core::{
|
||||
client::{
|
||||
Client,
|
||||
ClientBuilder,
|
||||
},
|
||||
Error,
|
||||
},
|
||||
};
|
||||
|
||||
/// Build web RPC client from URL
|
||||
pub async fn client(url: &str) -> Result<Client, Error> {
|
||||
let (sender, receiver) = web::connect(url).await.unwrap();
|
||||
Ok(ClientBuilder::default()
|
||||
.max_notifs_per_subscription(4096)
|
||||
.build_with_wasm(sender, receiver))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,6 +140,13 @@ use tokio as _;
|
||||
|
||||
pub use subxt_macro::subxt;
|
||||
|
||||
// Used to enable the js feature for wasm.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub use getrandom as _;
|
||||
|
||||
#[cfg(all(feature = "jsonrpsee-ws", feature = "jsonrpsee-web"))]
|
||||
std::compile_error!("Both the features `jsonrpsee-ws` and `jsonrpsee-web` are enabled which are mutually exclusive");
|
||||
|
||||
pub mod blocks;
|
||||
pub mod client;
|
||||
pub mod config;
|
||||
|
||||
@@ -12,18 +12,24 @@ use futures::stream::{
|
||||
StreamExt,
|
||||
TryStreamExt,
|
||||
};
|
||||
use jsonrpsee::{
|
||||
core::client::{
|
||||
use jsonrpsee::core::{
|
||||
client::{
|
||||
Client,
|
||||
ClientT,
|
||||
SubscriptionClientT,
|
||||
},
|
||||
types::ParamsSer,
|
||||
};
|
||||
use serde_json::value::{
|
||||
RawValue,
|
||||
Value,
|
||||
traits::ToRpcParams,
|
||||
Error as JsonRpseeError,
|
||||
};
|
||||
use serde_json::value::RawValue;
|
||||
|
||||
struct Params(Option<Box<RawValue>>);
|
||||
|
||||
impl ToRpcParams for Params {
|
||||
fn to_rpc_params(self) -> Result<Option<Box<RawValue>>, JsonRpseeError> {
|
||||
Ok(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl RpcClientT for Client {
|
||||
fn request_raw<'a>(
|
||||
@@ -32,8 +38,7 @@ impl RpcClientT for Client {
|
||||
params: Option<Box<RawValue>>,
|
||||
) -> RpcFuture<'a, Box<RawValue>> {
|
||||
Box::pin(async move {
|
||||
let params = prep_params_for_jsonrpsee(params);
|
||||
let res = ClientT::request(self, method, Some(params))
|
||||
let res = ClientT::request(self, method, Params(params))
|
||||
.await
|
||||
.map_err(|e| RpcError::ClientError(Box::new(e)))?;
|
||||
Ok(res)
|
||||
@@ -47,11 +52,10 @@ impl RpcClientT for Client {
|
||||
unsub: &'a str,
|
||||
) -> RpcFuture<'a, RpcSubscription> {
|
||||
Box::pin(async move {
|
||||
let params = prep_params_for_jsonrpsee(params);
|
||||
let sub = SubscriptionClientT::subscribe::<Box<RawValue>>(
|
||||
let sub = SubscriptionClientT::subscribe::<Box<RawValue>, _>(
|
||||
self,
|
||||
sub,
|
||||
Some(params),
|
||||
Params(params),
|
||||
unsub,
|
||||
)
|
||||
.await
|
||||
@@ -62,21 +66,3 @@ impl RpcClientT for Client {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// This is ugly; we have to encode to Value's to be compat with the jsonrpc interface.
|
||||
// Remove and simplify this once something like https://github.com/paritytech/jsonrpsee/issues/862 is in:
|
||||
fn prep_params_for_jsonrpsee(params: Option<Box<RawValue>>) -> ParamsSer<'static> {
|
||||
let params = match params {
|
||||
Some(params) => params,
|
||||
// No params? avoid any work and bail early.
|
||||
None => return ParamsSer::Array(Vec::new()),
|
||||
};
|
||||
let val = serde_json::to_value(¶ms).expect("RawValue guarantees valid JSON");
|
||||
let arr = match val {
|
||||
Value::Array(arr) => arr,
|
||||
_ => {
|
||||
panic!("RPC Params are expected to be an array but got {params}");
|
||||
}
|
||||
};
|
||||
ParamsSer::Array(arr)
|
||||
}
|
||||
|
||||
@@ -13,4 +13,4 @@ subxt = { path = "../../subxt" }
|
||||
sp-core = "6.0.0"
|
||||
tokio = { version = "1.8", features = ["macros", "rt-multi-thread"] }
|
||||
which = "4.2.2"
|
||||
jsonrpsee = { version = "0.15.1", features = ["async-client", "client-ws-transport"] }
|
||||
jsonrpsee = { version = "0.16.0", features = ["async-client", "client-ws-transport"] }
|
||||
|
||||
@@ -60,7 +60,7 @@ async fn run() {
|
||||
// Thus, the connection might get rejected a few times.
|
||||
use client::ClientT;
|
||||
let res = match client::build(&format!("ws://localhost:{}", port)).await {
|
||||
Ok(c) => c.request("state_getMetadata", None).await,
|
||||
Ok(c) => c.request("state_getMetadata", client::rpc_params![]).await,
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
|
||||
@@ -174,7 +174,10 @@ mod client {
|
||||
},
|
||||
};
|
||||
|
||||
pub use jsonrpsee::core::client::ClientT;
|
||||
pub use jsonrpsee::core::{
|
||||
client::ClientT,
|
||||
rpc_params,
|
||||
};
|
||||
|
||||
/// Build WS RPC client from URL
|
||||
pub async fn build(url: &str) -> Result<Client, Error> {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "wasm-test"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.24"
|
||||
tracing-wasm = "0.2.1"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
serde_json = "1.0.57"
|
||||
subxt = { path = "../../subxt", default-features = false, features = ["jsonrpsee-web"] }
|
||||
@@ -0,0 +1,26 @@
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
|
||||
use subxt::config::PolkadotConfig;
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
/// Run the tests by `$ wasm-pack test --firefox --headless`
|
||||
|
||||
fn init_tracing() {
|
||||
console_error_panic_hook::set_once();
|
||||
tracing_wasm::set_as_global_default();
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
async fn wasm_ws_transport_works() {
|
||||
init_tracing();
|
||||
|
||||
let client =
|
||||
subxt::client::OnlineClient::<PolkadotConfig>::from_url("ws://127.0.0.1:9944")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let chain = client.rpc().system_chain().await.unwrap();
|
||||
assert_eq!(&chain, "Development");
|
||||
}
|
||||
Reference in New Issue
Block a user