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 };
104 let era = match lifetime {
105 Some(0) => Era::immortal(),
106 lifetime => {
107 let current = self
108 .get_block_header(None)
109 .await?
110 .ok_or_else(|| Error::RpcClient("Failed to get current block".into()))?;
111 let number = current.number;
112 addititional.current_hash = current.hash();
114 Era::mortal(number, lifetime)
115 }
116 };
117
118 Ok((addititional, era))
119 }
120
121 #[cfg(feature = "serde")]
122 async fn request<'a, R>(&self, method: &'a str, params: ArrayParams) -> Result<R>
123 where
124 R: DeserializeOwned,
125 {
126 self.rpc.request(method, params).await
127 }
128
129 #[cfg(feature = "serde")]
130 async fn batch_request<'a, R>(
131 &self,
132 batch: BatchRequestBuilder<'a>,
133 ) -> Result<BatchResponse<'a, R>>
134 where
135 R: DeserializeOwned + Default + Clone + alloc::fmt::Debug + 'a,
136 {
137 self.rpc.batch_request(batch).await
138 }
139
140 #[cfg(feature = "serde")]
141 async fn subscribe<'a, Notif>(
142 &self,
143 subscribe_method: &'a str,
144 params: ArrayParams,
145 unsubscribe_method: &'a str,
146 ) -> Result<Subscription<Notif>>
147 where
148 Notif: DeserializeOwned,
149 {
150 self
151 .rpc
152 .subscribe(subscribe_method, params, unsubscribe_method)
153 .await
154 }
155
156 async fn rpc_get_block_hash(rpc: &RpcClient, block_number: u32) -> Result<Option<BlockHash>> {
157 let params = rpc_params!(block_number);
158 Ok(rpc.request("chain_getBlockHash", params).await?)
159 }
160
161 async fn get_block_header(&self, block: Option<BlockHash>) -> Result<Option<Header>> {
163 Ok(self.request("chain_getHeader", rpc_params!(block)).await?)
164 }
165
166 async fn get_block_hash(&self, block_number: u32) -> Result<Option<BlockHash>> {
168 Self::rpc_get_block_hash(&self.rpc, block_number).await
169 }
170
171 async fn rpc_get_runtime_version(
172 rpc: &RpcClient,
173 block: Option<BlockHash>,
174 ) -> Result<Option<RuntimeVersion>> {
175 let params = rpc_params!(block);
176 rpc.request("state_getRuntimeVersion", params).await
177 }
178
179 async fn get_block_runtime_version(
181 &self,
182 block: Option<BlockHash>,
183 ) -> Result<Option<RuntimeVersion>> {
184 Self::rpc_get_runtime_version(&self.rpc, block).await
185 }
186
187 #[cfg(feature = "type_info")]
188 async fn rpc_get_metadata(
189 rpc: &RpcClient,
190 block: Option<BlockHash>,
191 ) -> Result<Option<RuntimeMetadataPrefixed>> {
192 use hex::FromHex;
193 let params = rpc_params!(block);
194 let hex: String = rpc.request("state_getMetadata", params).await?;
195
196 let bytes = Vec::from_hex(&hex[2..])?;
197 Ok(Some(RuntimeMetadataPrefixed::decode(
198 &mut bytes.as_slice(),
199 )?))
200 }
201
202 #[cfg(feature = "type_info")]
204 async fn get_block_metadata(
205 &self,
206 block: Option<BlockHash>,
207 ) -> Result<Option<RuntimeMetadataPrefixed>> {
208 Self::rpc_get_metadata(&self.rpc, block).await
209 }
210}
211
212#[derive(Clone)]
213pub struct Client {
214 inner: Arc<InnerClient>,
215}
216
217impl Client {
218 pub async fn new(url: &str) -> Result<Self> {
219 Ok(Self {
220 inner: Arc::new(InnerClient::new(url).await?),
221 })
222 }
223
224 pub fn get_transaction_version(&self) -> i64 {
225 self.inner.get_transaction_version()
226 }
227
228 #[cfg(feature = "type_info")]
229 pub fn get_metadata(&self) -> &RuntimeMetadataPrefixed {
230 self.inner.get_metadata()
231 }
232
233 pub fn get_genesis_hash(&self) -> BlockHash {
234 self.inner.get_genesis_hash()
235 }
236
237 pub async fn get_additional_signed(
238 &self,
239 lifetime: Option<u64>,
240 ) -> Result<(AdditionalSigned, Era)> {
241 self.inner.get_additional_signed(lifetime).await
242 }
243
244 pub async fn get_system_properties(&self) -> Result<SystemProperties> {
246 self.request("system_properties", rpc_params!()).await
247 }
248
249 pub async fn get_storage_keys_paged(
250 &self,
251 prefix: &StorageKey,
252 count: usize,
253 start_key: Option<&StorageKey>,
254 at: Option<BlockHash>,
255 ) -> Result<Vec<StorageKey>> {
256 let params = rpc_params!(prefix, count, start_key.unwrap_or(prefix), at);
257 self
258 .request::<Vec<StorageKey>>("state_getKeysPaged", params)
259 .await
260 }
261
262 pub async fn get_storage_by_key<T: Decode>(
263 &self,
264 key: StorageKey,
265 at: Option<BlockHash>,
266 ) -> Result<Option<T>> {
267 let value = self
268 .get_storage_data_by_key(key, at)
269 .await?
270 .map(|data| T::decode(&mut data.0.as_slice()))
271 .transpose()?;
272 Ok(value)
273 }
274
275 pub async fn get_storage_data_by_key(
276 &self,
277 key: StorageKey,
278 at: Option<BlockHash>,
279 ) -> Result<Option<StorageData>> {
280 Ok(
281 self
282 .request("state_getStorage", rpc_params!(key, at))
283 .await?,
284 )
285 }
286
287 pub async fn subscribe_blocks(&self) -> Result<Subscription<Header>> {
289 Ok(
290 self
291 .subscribe(
292 "chain_subscribeNewHeads",
293 rpc_params!(),
294 "chain_unsubscribeNewHeads",
295 )
296 .await?,
297 )
298 }
299
300 pub async fn subscribe_finalized_blocks(&self) -> Result<Subscription<Header>> {
302 Ok(
303 self
304 .subscribe(
305 "chain_subscribeFinalizedHeads",
306 rpc_params!(),
307 "chain_unsubscribeFinalizedHeads",
308 )
309 .await?,
310 )
311 }
312
313 pub async fn submit_and_watch(&self, tx_hex: String) -> Result<Subscription<TransactionStatus>> {
315 Ok(
316 self
317 .subscribe(
318 "author_submitAndWatchExtrinsic",
319 rpc_params!(tx_hex),
320 "author_unwatchExtrinsic",
321 )
322 .await?,
323 )
324 }
325
326 #[cfg(feature = "serde")]
328 pub async fn request<'a, R>(&self, method: &'a str, params: ArrayParams) -> Result<R>
329 where
330 R: DeserializeOwned,
331 {
332 self.inner.request(method, params).await
333 }
334
335 #[cfg(feature = "serde")]
337 pub async fn batch_request<'a, R>(
338 &self,
339 batch: BatchRequestBuilder<'a>,
340 ) -> Result<BatchResponse<'a, R>>
341 where
342 R: DeserializeOwned + Default + Clone + alloc::fmt::Debug + 'a,
343 {
344 self.inner.batch_request(batch).await
345 }
346
347 #[cfg(feature = "serde")]
349 pub async fn subscribe<'a, Notif>(
350 &self,
351 subscribe_method: &'a str,
352 params: ArrayParams,
353 unsubscribe_method: &'a str,
354 ) -> Result<Subscription<Notif>>
355 where
356 Notif: DeserializeOwned,
357 {
358 self
359 .inner
360 .subscribe(subscribe_method, params, unsubscribe_method)
361 .await
362 }
363
364 pub async fn get_finalized_block(&self) -> Result<BlockHash> {
366 Ok(
367 self
368 .request("chain_getFinalizedHead", rpc_params!())
369 .await?,
370 )
371 }
372
373 pub async fn get_signed_block(&self, block: Option<BlockHash>) -> Result<Option<SignedBlock>> {
375 Ok(self.request("chain_getBlock", rpc_params!(block)).await?)
376 }
377
378 pub async fn get_block(&self, block: Option<BlockHash>) -> Result<Option<Block>> {
380 let block = self.get_signed_block(block).await?;
381 Ok(block.map(|b| b.block))
382 }
383
384 pub async fn find_extrinsic_block_index(
387 &self,
388 block: BlockHash,
389 tx_hash: TxHash,
390 ) -> Result<Option<usize>> {
391 let block = self.get_block(Some(block)).await?;
392 Ok(block.and_then(|b| b.find_extrinsic(tx_hash)))
393 }
394
395 pub async fn get_block_header(&self, block: Option<BlockHash>) -> Result<Option<Header>> {
397 self.inner.get_block_header(block).await
398 }
399
400 pub async fn get_block_hash(&self, block_number: u32) -> Result<Option<BlockHash>> {
402 self.inner.get_block_hash(block_number).await
403 }
404
405 pub async fn subscribe_runtime_version(&self) -> Result<Subscription<RuntimeVersion>> {
407 Ok(
408 self
409 .subscribe(
410 "chain_subscribeRuntimeVersion",
411 rpc_params!(),
412 "chain_unsubscribeRuntimeVersion",
413 )
414 .await?,
415 )
416 }
417
418 pub async fn get_block_runtime_version(
420 &self,
421 block: Option<BlockHash>,
422 ) -> Result<Option<RuntimeVersion>> {
423 self.inner.get_block_runtime_version(block).await
424 }
425
426 #[cfg(feature = "type_info")]
428 pub async fn get_block_metadata(
429 &self,
430 block: Option<BlockHash>,
431 ) -> Result<Option<RuntimeMetadataPrefixed>> {
432 self.inner.get_block_metadata(block).await
433 }
434}