How to convert WsClient into RpcClient in Rust?

Viewed 91

I want to make an API call that requires an RPCClient:

pub fn set_client<C: Into<RpcClient>>(mut self, client: C) -> Self

But I face the error:

 the trait `From<WsClient>` is not implemented for `RpcClient`
help: the following implementations were found:
             <RpcClient as From<Arc<jsonrpsee_http_client::client::HttpClient>>>
             <RpcClient as From<Arc<jsonrpsee_ws_client::client::WsClient>>>
             <RpcClient as From<jsonrpsee_http_client::client::HttpClient>>
             <RpcClient as From<jsonrpsee_ws_client::client::WsClient>>
   = note: required because of the requirements on the impl of `Into<RpcClient>` for `WsClient`

The RpcClient here is:

rpc::RpcClient

The function set_client is defined here.

I first build a WsClient in order to have custom connection timeout configuration. Then I set the client while building a subxt client:

Even though the help in the error states that an implementation: RpcClient as From<jsonrpsee_ws_client::client::WsClient was found; when I try to use it, it does not work.

use jsonrpsee_ws_client;
use jsonrpsee_ws_client::WsClientBuilder;
use std::time::Duration;
use subxt::ClientBuilder;
// use htrpc::RpcClient;
use subxt::RpcClient;

#[subxt::subxt(runtime_metadata_path = "polkadot_metadata.scale")]
pub mod polkadot {}

#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    env_logger::init();
    // let connection_timeout_secs = Duration::new(secs: 60, nanos: 0);
    let connection_timeout_duration = Duration::new(120, 0);

    let client: jsonrpsee_ws_client::client::WsClient = WsClientBuilder::default()
        .connection_timeout(connection_timeout_duration)
        .build("wss://pub.elara.patract.io:9944/statemine")
        .await
        .unwrap()
        .into();

    let api = ClientBuilder::new()
        .set_client(client)
        .set_url("wss://pub.elara.patract.io:9944/statemine")
        .build()
        .await?
        .to_runtime_api::<polkadot::RuntimeApi<polkadot::DefaultConfig>>();

    let mut iter = api.storage().system().account_iter(None).await?;

    while let Some((key, account)) = iter.next().await? {
        println!("{}: {}", hex::encode(key), account.data.free);
    }
    Ok(())
}

How do I convert a WsClient into RpcClient so that I can make the function call?

2 Answers

I don't like the approach at all, but to answer your question:

// Public RPC URL for testing.
let url = "wss://rpc.polkadot.io:443";

// Create a jsonrpsee client with custom timeout.
// The code is copied from RpcClient::try_from_url, please don't do this in production.
let ws = WsClientBuilder::default()
    .max_notifs_per_subscription(4096)
    .connection_timeout(Duration::from_secs(120)) // Custom timeout.
    .build(url)
    .await?;

// Create an RpcClient. Copied from the same spot.
let rpc = RpcClient::WebSocket(Arc::new(ws));

let api = ClientBuilder::new()
    .set_client(rpc)            // Set the RpcClient.
    .set_url(url)
    .build()
    .await?
    .to_runtime_api::<polkadot::RuntimeApi<polkadot::DefaultConfig>>();

IMHO it would be better to sacrifice the custom timeout and directly use RpcClient::try_from_url.

Your code above should work.

The only thing I can think of is that your code is using a different version of jsonrpsee than subxt does but I can't know without checking your Cargo.toml.

Related