contract_extrinsics/
rpc.rs1use std::str::FromStr;
18
19use contract_transcode::AccountId32;
20use subxt::{
21 backend::rpc::{
22 RawValue,
23 RpcClient,
24 RpcParams,
25 },
26 ext::scale_value::{
27 stringify::{
28 from_str_custom,
29 ParseError,
30 },
31 Value,
32 },
33};
34
35use crate::url_to_string;
36use anyhow::{
37 anyhow,
38 bail,
39 Result,
40};
41
42pub struct RawParams(Option<Box<RawValue>>);
43
44impl RawParams {
45 pub fn new(params: &[String]) -> Result<Self> {
48 let mut str_parser = from_str_custom();
49 str_parser = str_parser.add_custom_parser(custom_hex_parse);
50 str_parser = str_parser.add_custom_parser(custom_ss58_parse);
51
52 let value_params = params
53 .iter()
54 .map(|e| str_parser.parse(e).0)
55 .collect::<Result<Vec<_>, ParseError>>()
56 .map_err(|e| anyhow::anyhow!("Method parameters parsing failed: {e}"))?;
57
58 let params = match value_params.is_empty() {
59 true => None,
60 false => {
61 value_params
62 .iter()
63 .try_fold(RpcParams::new(), |mut v, e| {
64 v.push(e)?;
65 Ok(v)
66 })
67 .map_err(|e: subxt::Error| {
68 anyhow::anyhow!("Building method parameters failed: {e}")
69 })?
70 .build()
71 }
72 };
73
74 Ok(Self(params))
75 }
76}
77
78pub struct RpcRequest(RpcClient);
79
80impl RpcRequest {
81 pub async fn new(url: &url::Url) -> Result<Self> {
83 let rpc = RpcClient::from_url(url_to_string(url)).await?;
84 Ok(Self(rpc))
85 }
86
87 pub async fn raw_call<'a>(
91 &'a self,
92 method: &'a str,
93 params: RawParams,
94 ) -> Result<Box<RawValue>> {
95 let methods = self.get_supported_methods().await?;
96 if !methods.iter().any(|e| e == method) {
97 bail!(
98 "Method not found, supported methods: {}",
99 methods.join(", ")
100 );
101 }
102 self.0
103 .request_raw(method, params.0)
104 .await
105 .map_err(|e| anyhow!("Raw RPC call failed: {e}"))
106 }
107
108 async fn get_supported_methods(&self) -> Result<Vec<String>> {
112 let result = self
113 .0
114 .request_raw("rpc_methods", None)
115 .await
116 .map_err(|e| anyhow!("Rpc call 'rpc_methods' failed: {e}"))?;
117
118 let result_value: serde_json::Value = serde_json::from_str(result.get())?;
119
120 let methods = result_value
121 .get("methods")
122 .and_then(|v| v.as_array())
123 .ok_or_else(|| anyhow!("Methods field parsing failed!"))?;
124
125 let patterns = ["watch", "unstable", "subscribe"];
127 let filtered_methods: Vec<String> = methods
128 .iter()
129 .filter_map(|v| v.as_str().map(String::from))
130 .filter(|s| {
131 patterns
132 .iter()
133 .all(|&pattern| !s.to_lowercase().contains(pattern))
134 })
135 .collect();
136
137 Ok(filtered_methods)
138 }
139}
140
141fn custom_hex_parse(s: &mut &str) -> Option<Result<Value<()>, ParseError>> {
143 if !s.starts_with("0x") {
144 return None
145 }
146
147 let end_idx = s
148 .find(|c: char| !c.is_ascii_alphanumeric())
149 .unwrap_or(s.len());
150 let hex = &s[..end_idx];
151 *s = &s[end_idx..];
152 Some(Ok(Value::string(hex.to_string())))
153}
154
155fn custom_ss58_parse(s: &mut &str) -> Option<Result<Value<()>, ParseError>> {
157 let end_idx = s
158 .find(|c: char| !c.is_ascii_alphanumeric())
159 .unwrap_or(s.len());
160 let account = AccountId32::from_str(&s[..end_idx]).ok()?;
161
162 *s = &s[end_idx..];
163 Some(Ok(Value::string(format!("0x{}", hex::encode(account.0)))))
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169 fn assert_raw_params_value(input: &[&str], expected: &str) {
170 let input = input.iter().map(|e| e.to_string()).collect::<Vec<String>>();
171 let raw_params = RawParams::new(&input).expect("Raw param shall be created");
172 let expected = expected
173 .chars()
174 .filter(|&c| !c.is_whitespace())
175 .collect::<String>();
176 assert_eq!(raw_params.0.unwrap().get(), expected);
177 }
178
179 #[test]
180 fn parse_ss58_works() {
181 let expected = r#"["0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d","sr25"]"#;
182 let input = &[
183 "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
184 "\"sr25\"",
185 ];
186 assert_raw_params_value(input, expected);
187 }
188
189 #[test]
190 fn parse_seq_works() {
191 let expected = r#"[[1,"0x1234",true]]"#;
192 let input = &["(1, 0x1234, true)"];
193 assert_raw_params_value(input, expected);
194 }
195
196 #[test]
197 fn parse_map_works() {
198 let expected = r#"[{
199 "hello": true,
200 "a": 4,
201 "b": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
202 "c": "test"
203 }]"#;
204 let input = &["{hello: true, a: 4, b: \
205 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY, c: \"test\"}"];
206 assert_raw_params_value(input, expected);
207 }
208}