polymesh_api_client/
rpc.rs

1use jsonrpsee::core::{
2  client::{BatchResponse, ClientT, Subscription, SubscriptionClientT},
3  params::{ArrayParams, BatchRequestBuilder},
4};
5#[cfg(target_arch = "wasm32")]
6use jsonrpsee::wasm_client::{Client as WasmClient, WasmClientBuilder};
7#[cfg(not(target_arch = "wasm32"))]
8use jsonrpsee::{
9  http_client::{HttpClient, HttpClientBuilder},
10  ws_client::{WsClient, WsClientBuilder},
11};
12
13use sp_std::prelude::*;
14
15#[cfg(feature = "serde")]
16use serde::de::DeserializeOwned;
17
18use crate::error::*;
19
20#[derive(Debug)]
21enum InnerRpcClient {
22  #[cfg(not(target_arch = "wasm32"))]
23  Ws(WsClient),
24  #[cfg(not(target_arch = "wasm32"))]
25  Http(HttpClient),
26  #[cfg(target_arch = "wasm32")]
27  Wasm(WasmClient),
28}
29
30#[derive(Debug)]
31pub struct RpcClient {
32  client: InnerRpcClient,
33}
34
35impl RpcClient {
36  pub async fn new(url: &str) -> Result<Self> {
37    #[cfg(not(target_arch = "wasm32"))]
38    {
39      if url.starts_with("http") {
40        Self::new_http(url)
41      } else if url.starts_with("ws") {
42        Self::new_ws(url).await
43      } else {
44        Err(Error::RpcClient(format!("Unsupported url: {url}")))
45      }
46    }
47    #[cfg(target_arch = "wasm32")]
48    Self::new_wasm(url).await
49  }
50
51  #[cfg(not(target_arch = "wasm32"))]
52  async fn new_ws(url: &str) -> Result<Self> {
53    // Check if url has a `port`.  The websocket backend doesn't support a default port.
54    let url = url.parse::<http::Uri>()?;
55    let url = if url.port().is_none() {
56      // Need to rebuild the url to add the default port `80` (ws) or `443` (wss).
57      let (scheme, port) = match url.scheme().map(|s| s.as_str()) {
58        Some("wss") => ("wss", 443),
59        _ => ("ws", 80),
60      };
61      let host = url.authority().map(|a| a.as_str()).unwrap_or_else(|| "");
62      let authority = format!("{}:{}", host, port);
63      let path = url
64        .path_and_query()
65        .map(|p| p.as_str())
66        .unwrap_or_else(|| "");
67      let url = http::Uri::builder()
68        .scheme(scheme)
69        .authority(authority)
70        .path_and_query(path)
71        .build()?;
72      url.to_string()
73    } else {
74      url.to_string()
75    };
76    let client = WsClientBuilder::default()
77      .max_concurrent_requests(16 * 1024)
78      .max_request_size(1024 * 1024 * 1024)
79      .build(&url)
80      .await?;
81    Ok(Self {
82      client: InnerRpcClient::Ws(client),
83    })
84  }
85
86  #[cfg(not(target_arch = "wasm32"))]
87  fn new_http(url: &str) -> Result<Self> {
88    let client = HttpClientBuilder::default()
89      .max_request_size(1024 * 1024 * 1024)
90      .build(&url)?;
91    Ok(Self {
92      client: InnerRpcClient::Http(client),
93    })
94  }
95
96  #[cfg(target_arch = "wasm32")]
97  async fn new_wasm(url: &str) -> Result<Self> {
98    let client = WasmClientBuilder::default().build(&url).await?;
99    Ok(Self {
100      client: InnerRpcClient::Wasm(client),
101    })
102  }
103
104  #[cfg(feature = "serde")]
105  pub async fn subscribe<'a, Notif>(
106    &self,
107    subscribe_method: &'a str,
108    params: ArrayParams,
109    unsubscribe_method: &'a str,
110  ) -> Result<Subscription<Notif>>
111  where
112    Notif: DeserializeOwned,
113  {
114    Ok(match &self.client {
115      #[cfg(not(target_arch = "wasm32"))]
116      InnerRpcClient::Ws(ws) => {
117        ws.subscribe(subscribe_method, params, unsubscribe_method)
118          .await
119      }
120      #[cfg(not(target_arch = "wasm32"))]
121      InnerRpcClient::Http(http) => {
122        http
123          .subscribe(subscribe_method, params, unsubscribe_method)
124          .await
125      }
126      #[cfg(target_arch = "wasm32")]
127      InnerRpcClient::Wasm(http) => {
128        http
129          .subscribe(subscribe_method, params, unsubscribe_method)
130          .await
131      }
132    }?)
133  }
134
135  #[cfg(feature = "serde")]
136  pub async fn request<'a, R>(&self, method: &'a str, params: ArrayParams) -> Result<R>
137  where
138    R: DeserializeOwned,
139  {
140    Ok(match &self.client {
141      #[cfg(not(target_arch = "wasm32"))]
142      InnerRpcClient::Ws(ws) => ws.request(method, params).await,
143      #[cfg(not(target_arch = "wasm32"))]
144      InnerRpcClient::Http(http) => http.request(method, params).await,
145      #[cfg(target_arch = "wasm32")]
146      InnerRpcClient::Wasm(http) => http.request(method, params).await,
147    }?)
148  }
149
150  #[cfg(feature = "serde")]
151  pub async fn batch_request<'a, R>(
152    &self,
153    batch: BatchRequestBuilder<'a>,
154  ) -> Result<BatchResponse<'a, R>>
155  where
156    R: DeserializeOwned + Default + Clone + alloc::fmt::Debug + 'a,
157  {
158    Ok(match &self.client {
159      #[cfg(not(target_arch = "wasm32"))]
160      InnerRpcClient::Ws(ws) => ws.batch_request(batch).await,
161      #[cfg(not(target_arch = "wasm32"))]
162      InnerRpcClient::Http(http) => http.batch_request(batch).await,
163      #[cfg(target_arch = "wasm32")]
164      InnerRpcClient::Wasm(http) => http.batch_request(batch).await,
165    }?)
166  }
167}