1#[cfg(not(feature = "std"))]
2use alloc::{collections::btree_map::BTreeMap, sync::Arc};
3#[cfg(feature = "std")]
4use std::{collections::BTreeMap, sync::Arc};
5
6use jsonrpsee::core::client::BatchResponse;
7pub use jsonrpsee::core::client::Subscription;
8use jsonrpsee::core::params::{ArrayParams, BatchRequestBuilder};
9use jsonrpsee::rpc_params;
10
11use codec::Decode;
12
13#[cfg(feature = "serde")]
14use serde::{de::DeserializeOwned, Deserialize, Serialize};
15#[cfg(feature = "serde")]
16use serde_json::Value;
17
18#[cfg(not(feature = "std"))]
19use alloc::{format, string::String};
20use sp_std::prelude::*;
21
22#[cfg(feature = "type_info")]
23use frame_metadata::RuntimeMetadataPrefixed;
24
25use crate::rpc::*;
26use crate::*;
27
28#[derive(Clone, Debug, Default)]
29#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
30#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
31pub struct RuntimeVersion {
32 pub spec_name: String,
33 pub impl_name: String,
34 pub authoring_version: u32,
35 pub spec_version: u32,
36 pub impl_version: u32,
37 #[cfg_attr(feature = "serde", serde(default))]
38 pub transaction_version: u32,
39
40 #[cfg_attr(feature = "serde", serde(flatten))]
41 #[cfg(feature = "serde")]
42 pub extra: BTreeMap<String, Value>,
43}
44
45#[derive(Clone, Debug)]
46#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
47#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
48pub struct SystemProperties {
49 pub ss58_format: u16,
50 pub token_decimals: u32,
51 pub token_symbol: String,
52}
53
54struct InnerClient {
55 rpc: RpcClient,
56 runtime_version: RuntimeVersion,
57 #[cfg(feature = "type_info")]
58 metadata: RuntimeMetadataPrefixed,
59 genesis_hash: BlockHash,
60}
61
62impl InnerClient {
63 async fn new(url: &str) -> Result<Self> {
64 let rpc = RpcClient::new(url).await?;
65 let runtime_version = Self::rpc_get_runtime_version(&rpc, None)
66 .await?
67 .ok_or_else(|| Error::RpcClient(format!("Failed to get RuntimeVersion")))?;
68 #[cfg(feature = "type_info")]
69 let metadata = Self::rpc_get_metadata(&rpc, None)
70 .await?
71 .ok_or_else(|| Error::RpcClient(format!("Failed to get chain metadata")))?;
72 let genesis_hash = Self::rpc_get_block_hash(&rpc, 0)
73 .await?
74 .ok_or_else(|| Error::RpcClient(format!("Failed to get chain Genesis hash")))?;
75 Ok(Self {
76 rpc,
77 runtime_version,
78 #[cfg(feature = "type_info")]
79 metadata,
80 genesis_hash,
81 })
82 }
83
84 fn get_transaction_version(&self) -> i64 {
85 self.runtime_version.transaction_version as i64
86 }
87
88 #[cfg(feature = "type_info")]
89 fn get_metadata(&self) -> &RuntimeMetadataPrefixed {
90 &self.metadata
91 }
92
93 fn get_genesis_hash(&self) -> BlockHash {
94 self.genesis_hash
95 }
96
97 async fn get_additional_signed(&self, lifetime: Option<u64>) -> Result<(AdditionalSigned, Era)> {
98 let mut addititional = AdditionalSigned {
99 spec_version: self.runtime_version.spec_version,
100 tx_version: self.runtime_version.transaction_version,
101 genesis_hash: self.genesis_hash,
102 current_hash: self.genesis_hash,
103 metadata_hash: None,
104 };
105 let era = match lifetime {
106 Some(0) => Era::immortal(),
107 lifetime => {
108 let current = self
109 .get_block_header(None)
110 .await?
111 .ok_or_else(|| Error::RpcClient("Failed to get current block".into()))?;
112 let number = current.number;
113 addititional.current_hash = current.hash();
115 Era::mortal(number, lifetime)
116 }
117 };
118
119 Ok((addititional, era))
120 }
121
122 #[cfg(feature = "serde")]
123 async fn request<'a, R>(&self, method: &'a str, params: ArrayParams) -> Result<R>
124 where
125 R: DeserializeOwned,
126 {
127 self.rpc.request(method, params).await
128 }
129
130 #[cfg(feature = "serde")]
131 async fn batch_request<'a, R>(
132 &self,
133 batch: BatchRequestBuilder<'a>,
134 ) -> Result<BatchResponse<'a, R>>
135 where
136 R: DeserializeOwned + Default + Clone + alloc::fmt::Debug + 'a,
137 {
138 self.rpc.batch_request(batch).await
139 }
140
141 #[cfg(feature = "serde")]
142 async fn subscribe<'a, Notif>(
143 &self,
144 subscribe_method: &'a str,
145 params: ArrayParams,
146 unsubscribe_method: &'a str,
147 ) -> Result<Subscription<Notif>>
148 where
149 Notif: DeserializeOwned,
150 {
151 self
152 .rpc
153 .subscribe(subscribe_method, params, unsubscribe_method)
154 .await
155 }
156
157 async fn rpc_get_block_hash(rpc: &RpcClient, block_number: u32) -> Result<Option<BlockHash>> {
158 let params = rpc_params!(block_number);
159 Ok(rpc.request("chain_getBlockHash", params).await?)
160 }
161
162 async fn get_block_header(&self, block: Option<BlockHash>) -> Result<Option<Header>> {
164 Ok(self.request("chain_getHeader", rpc_params!(block)).await?)
165 }
166
167 async fn get_block_hash(&self, block_number: u32) -> Result<Option<BlockHash>> {
169 Self::rpc_get_block_hash(&self.rpc, block_number).await
170 }
171
172 async fn rpc_get_runtime_version(
173 rpc: &RpcClient,
174 block: Option<BlockHash>,
175 ) -> Result<Option<RuntimeVersion>> {
176 let params = rpc_params!(block);
177 rpc.request("state_getRuntimeVersion", params).await
178 }
179
180 async fn get_block_runtime_version(
182 &self,
183 block: Option<BlockHash>,
184 ) -> Result<Option<RuntimeVersion>> {
185 Self::rpc_get_runtime_version(&self.rpc, block).await
186 }
187
188 #[cfg(feature = "type_info")]
189 async fn rpc_get_metadata(
190 rpc: &RpcClient,
191 block: Option<BlockHash>,
192 ) -> Result<Option<RuntimeMetadataPrefixed>> {
193 use hex::FromHex;
194 let params = rpc_params!(block);
195 let hex: String = rpc.request("state_getMetadata", params).await?;
196
197 let bytes = Vec::from_hex(&hex[2..])?;
198 Ok(Some(RuntimeMetadataPrefixed::decode(
199 &mut bytes.as_slice(),
200 )?))
201 }
202
203 #[cfg(feature = "type_info")]
205 async fn get_block_metadata(
206 &self,
207 block: Option<BlockHash>,
208 ) -> Result<Option<RuntimeMetadataPrefixed>> {
209 Self::rpc_get_metadata(&self.rpc, block).await
210 }
211}
212
213#[derive(Clone)]
214pub struct Client {
215 inner: Arc<InnerClient>,
216}
217
218impl Client {
219 pub async fn new(url: &str) -> Result<Self> {
220 Ok(Self {
221 inner: Arc::new(InnerClient::new(url).await?),
222 })
223 }
224
225 pub fn get_transaction_version(&self) -> i64 {
226 self.inner.get_transaction_version()
227 }
228
229 #[cfg(feature = "type_info")]
230 pub fn get_metadata(&self) -> &RuntimeMetadataPrefixed {
231 self.inner.get_metadata()
232 }
233
234 pub fn get_genesis_hash(&self) -> BlockHash {
235 self.inner.get_genesis_hash()
236 }
237
238 pub async fn get_additional_signed(
239 &self,
240 lifetime: Option<u64>,
241 ) -> Result<(AdditionalSigned, Era)> {
242 self.inner.get_additional_signed(lifetime).await
243 }
244
245 pub async fn get_system_properties(&self) -> Result<SystemProperties> {
247 self.request("system_properties", rpc_params!()).await
248 }
249
250 pub async fn get_storage_keys_paged(
251 &self,
252 prefix: &StorageKey,
253 count: usize,
254 start_key: Option<&StorageKey>,
255 at: Option<BlockHash>,
256 ) -> Result<Vec<StorageKey>> {
257 let params = rpc_params!(prefix, count, start_key.unwrap_or(prefix), at);
258 self
259 .request::<Vec<StorageKey>>("state_getKeysPaged", params)
260 .await
261 }
262
263 pub async fn get_storage_by_key<T: Decode>(
264 &self,
265 key: StorageKey,
266 at: Option<BlockHash>,
267 ) -> Result<Option<T>> {
268 let value = self
269 .get_storage_data_by_key(key, at)
270 .await?
271 .map(|data| T::decode(&mut data.0.as_slice()))
272 .transpose()?;
273 Ok(value)
274 }
275
276 pub async fn get_storage_data_by_key(
277 &self,
278 key: StorageKey,
279 at: Option<BlockHash>,
280 ) -> Result<Option<StorageData>> {
281 Ok(
282 self
283 .request("state_getStorage", rpc_params!(key, at))
284 .await?,
285 )
286 }
287
288 pub async fn subscribe_blocks(&self) -> Result<Subscription<Header>> {
290 Ok(
291 self
292 .subscribe(
293 "chain_subscribeNewHeads",
294 rpc_params!(),
295 "chain_unsubscribeNewHeads",
296 )
297 .await?,
298 )
299 }
300
301 pub async fn subscribe_finalized_blocks(&self) -> Result<Subscription<Header>> {
303 Ok(
304 self
305 .subscribe(
306 "chain_subscribeFinalizedHeads",
307 rpc_params!(),
308 "chain_unsubscribeFinalizedHeads",
309 )
310 .await?,
311 )
312 }
313
314 pub async fn submit_and_watch(&self, tx_hex: String) -> Result<Subscription<TransactionStatus>> {
316 Ok(
317 self
318 .subscribe(
319 "author_submitAndWatchExtrinsic",
320 rpc_params!(tx_hex),
321 "author_unwatchExtrinsic",
322 )
323 .await?,
324 )
325 }
326
327 #[cfg(feature = "serde")]
329 pub async fn request<'a, R>(&self, method: &'a str, params: ArrayParams) -> Result<R>
330 where
331 R: DeserializeOwned,
332 {
333 self.inner.request(method, params).await
334 }
335
336 #[cfg(feature = "serde")]
338 pub async fn batch_request<'a, R>(
339 &self,
340 batch: BatchRequestBuilder<'a>,
341 ) -> Result<BatchResponse<'a, R>>
342 where
343 R: DeserializeOwned + Default + Clone + alloc::fmt::Debug + 'a,
344 {
345 self.inner.batch_request(batch).await
346 }
347
348 #[cfg(feature = "serde")]
350 pub async fn subscribe<'a, Notif>(
351 &self,
352 subscribe_method: &'a str,
353 params: ArrayParams,
354 unsubscribe_method: &'a str,
355 ) -> Result<Subscription<Notif>>
356 where
357 Notif: DeserializeOwned,
358 {
359 self
360 .inner
361 .subscribe(subscribe_method, params, unsubscribe_method)
362 .await
363 }
364
365 pub async fn get_finalized_block(&self) -> Result<BlockHash> {
367 Ok(
368 self
369 .request("chain_getFinalizedHead", rpc_params!())
370 .await?,
371 )
372 }
373
374 pub async fn get_signed_block(&self, block: Option<BlockHash>) -> Result<Option<SignedBlock>> {
376 Ok(self.request("chain_getBlock", rpc_params!(block)).await?)
377 }
378
379 pub async fn get_block(&self, block: Option<BlockHash>) -> Result<Option<Block>> {
381 let block = self.get_signed_block(block).await?;
382 Ok(block.map(|b| b.block))
383 }
384
385 pub async fn find_extrinsic_block_index(
388 &self,
389 block: BlockHash,
390 tx_hash: TxHash,
391 ) -> Result<Option<usize>> {
392 let block = self.get_block(Some(block)).await?;
393 Ok(block.and_then(|b| b.find_extrinsic(tx_hash)))
394 }
395
396 pub async fn get_block_header(&self, block: Option<BlockHash>) -> Result<Option<Header>> {
398 self.inner.get_block_header(block).await
399 }
400
401 pub async fn get_block_hash(&self, block_number: u32) -> Result<Option<BlockHash>> {
403 self.inner.get_block_hash(block_number).await
404 }
405
406 pub async fn subscribe_runtime_version(&self) -> Result<Subscription<RuntimeVersion>> {
408 Ok(
409 self
410 .subscribe(
411 "chain_subscribeRuntimeVersion",
412 rpc_params!(),
413 "chain_unsubscribeRuntimeVersion",
414 )
415 .await?,
416 )
417 }
418
419 pub async fn get_block_runtime_version(
421 &self,
422 block: Option<BlockHash>,
423 ) -> Result<Option<RuntimeVersion>> {
424 self.inner.get_block_runtime_version(block).await
425 }
426
427 #[cfg(feature = "type_info")]
429 pub async fn get_block_metadata(
430 &self,
431 block: Option<BlockHash>,
432 ) -> Result<Option<RuntimeMetadataPrefixed>> {
433 self.inner.get_block_metadata(block).await
434 }
435}