pdotc/
rpc.rs

1use std::fmt::Debug;
2
3use parity_scale_codec::Decode;
4pub use serde::de::DeserializeOwned;
5use serde::{Deserialize, Serialize};
6use serde_json::{json, to_value, Value};
7use sp_core::H256;
8use sp_storage::StorageKey;
9
10use crate::client::{ClientError, Result};
11use crate::utils::FromHexString;
12
13pub trait RpcClient {
14    fn post(&self, json_req: Value) -> Result<JsonRpcResponse>;
15
16    fn send_extrinsic(&self, xt: &str) -> Result<String> {
17        let json = author_submit_extrinsic(xt);
18        self.post(json)?.into_result()
19    }
20}
21
22#[derive(Debug, Deserialize)]
23#[serde(untagged)]
24pub enum JsonRpcResponse {
25    Success(JsonRpcSuccess),
26    Error(JsonRpcError),
27    String(String),
28}
29
30impl JsonRpcResponse {
31    pub fn into_result<T: DeserializeOwned>(self) -> Result<T, ClientError> {
32        match self {
33            JsonRpcResponse::Success(s) => Ok(serde_json::from_value::<T>(s.result)?),
34            JsonRpcResponse::Error(e) => Err(ClientError::JsonRpcError(e)),
35            JsonRpcResponse::String(s) => Ok(serde_json::from_str(&s)?),
36        }
37    }
38
39    pub fn decode_into<T: Decode>(self) -> Result<Option<T>, ClientError> {
40        match self {
41            JsonRpcResponse::Success(s) => {
42                if !s.result.is_null() {
43                    let v = Vec::from_hex(s.result.to_string())?;
44                    let t = Decode::decode(&mut v.as_slice())?;
45                    Ok(Some(t))
46                } else {
47                    Ok(None)
48                }
49            }
50            JsonRpcResponse::Error(e) => Err(ClientError::JsonRpcError(e)),
51            JsonRpcResponse::String(s) => Ok(Some(Decode::decode(&mut s.as_bytes())?)),
52        }
53    }
54}
55
56#[derive(Debug, Deserialize)]
57pub struct JsonRpcSuccess {
58    pub result: Value,
59    pub jsonrpc: String,
60    pub id: String,
61}
62
63#[derive(Debug, Deserialize, thiserror::Error)]
64#[error("{error}")]
65pub struct JsonRpcError {
66    pub jsonrpc: String,
67    pub id: String,
68    pub error: RpcError,
69}
70
71#[derive(Debug, Deserialize, thiserror::Error)]
72#[error("Json RPC error: [code: {code}, message: {message}, data: {data:?}]")]
73pub struct RpcError {
74    pub code: i64,
75    pub message: String,
76    pub data: Option<Value>,
77}
78
79// JSON RPC REQUESTS
80
81pub fn chain_get_block(hash: Option<H256>) -> Value {
82    chain_get_block_with_id(hash, 1)
83}
84
85pub fn chain_get_block_hash(number: Option<u32>) -> Value {
86    chain_get_block_hash_with_id(number, 1)
87}
88
89pub fn chain_get_genesis_hash() -> Value {
90    chain_get_block_hash(Some(0))
91}
92
93pub fn chain_get_block_with_id(hash: Option<H256>, id: u32) -> Value {
94    json_req("chain_getBlock", vec![hash], id)
95}
96
97pub fn chain_get_block_hash_with_id(number: Option<u32>, id: u32) -> Value {
98    json_req("chain_getBlockHash", vec![number], id)
99}
100
101pub fn state_get_runtime_version() -> Value {
102    state_get_runtime_version_with_id(1)
103}
104
105pub fn state_get_runtime_version_with_id(id: u32) -> Value {
106    json_req("state_getRuntimeVersion", vec![Value::Null], id)
107}
108
109pub(crate) fn state_get_storage(key: StorageKey, at_block: Option<H256>) -> Value {
110    json_req(
111        "state_getStorage",
112        vec![to_value(key).unwrap(), to_value(at_block).unwrap()],
113        1,
114    )
115}
116
117pub fn payment_query_fee_details(xt_hex_prefixed: &str, at_block: Option<H256>) -> Value {
118    json_req(
119        "payment_queryFeeDetails",
120        vec![
121            to_value(xt_hex_prefixed).unwrap(),
122            to_value(at_block).unwrap(),
123        ],
124        1,
125    )
126}
127
128pub fn author_submit_extrinsic(xt_hex_prefixed: &str) -> Value {
129    author_submit_extrinsic_with_id(xt_hex_prefixed, 3)
130}
131
132pub fn author_submit_extrinsic_with_id(xt_hex_prefixed: &str, id: u32) -> Value {
133    json_req("author_submitExtrinsic", vec![xt_hex_prefixed], id)
134}
135
136fn json_req<S: Serialize>(method: &str, params: S, id: u32) -> Value {
137    json!({
138        "method": method,
139        "params": params,
140        "jsonrpc": "2.0",
141        "id": id.to_string(),
142    })
143}