1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use jsonrpsee::{
async_client::ClientBuilder,
client_transport::ws::{
Uri,
WsTransportClientBuilder,
},
core::{
client::ClientT,
Error,
},
http_client::HttpClientBuilder,
rpc_params,
};
use std::time::Duration;
pub fn fetch_metadata_bytes_blocking(url: &Uri) -> Result<Vec<u8>, FetchMetadataError> {
tokio_block_on(fetch_metadata_bytes(url))
}
pub fn fetch_metadata_hex_blocking(url: &Uri) -> Result<String, FetchMetadataError> {
tokio_block_on(fetch_metadata_hex(url))
}
fn tokio_block_on<T, Fut: std::future::Future<Output = T>>(fut: Fut) -> T {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(fut)
}
pub async fn fetch_metadata_bytes(url: &Uri) -> Result<Vec<u8>, FetchMetadataError> {
let hex = fetch_metadata_hex(url).await?;
let bytes = hex::decode(hex.trim_start_matches("0x"))?;
Ok(bytes)
}
pub async fn fetch_metadata_hex(url: &Uri) -> Result<String, FetchMetadataError> {
let hex_data = match url.scheme_str() {
Some("http") | Some("https") => fetch_metadata_http(url).await,
Some("ws") | Some("wss") => fetch_metadata_ws(url).await,
invalid_scheme => {
let scheme = invalid_scheme.unwrap_or("no scheme");
Err(FetchMetadataError::InvalidScheme(scheme.to_owned()))
}
}?;
Ok(hex_data)
}
async fn fetch_metadata_ws(url: &Uri) -> Result<String, FetchMetadataError> {
let (sender, receiver) = WsTransportClientBuilder::default()
.build(url.to_string().parse::<Uri>().unwrap())
.await
.map_err(|e| Error::Transport(e.into()))?;
let client = ClientBuilder::default()
.request_timeout(Duration::from_secs(180))
.max_notifs_per_subscription(4096)
.build_with_tokio(sender, receiver);
Ok(client.request("state_getMetadata", rpc_params![]).await?)
}
async fn fetch_metadata_http(url: &Uri) -> Result<String, FetchMetadataError> {
let client = HttpClientBuilder::default()
.request_timeout(Duration::from_secs(180))
.build(url.to_string())?;
Ok(client.request("state_getMetadata", rpc_params![]).await?)
}
#[derive(Debug)]
pub enum FetchMetadataError {
DecodeError(hex::FromHexError),
RequestError(jsonrpsee::core::Error),
InvalidScheme(String),
}
impl std::fmt::Display for FetchMetadataError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FetchMetadataError::DecodeError(e) => {
write!(f, "Cannot decode hex value: {e}")
}
FetchMetadataError::RequestError(e) => write!(f, "Request error: {e}"),
FetchMetadataError::InvalidScheme(s) => {
write!(
f,
"'{s}' not supported, supported URI schemes are http, https, ws or wss."
)
}
}
}
}
impl std::error::Error for FetchMetadataError {}
impl From<hex::FromHexError> for FetchMetadataError {
fn from(e: hex::FromHexError) -> Self {
FetchMetadataError::DecodeError(e)
}
}
impl From<jsonrpsee::core::Error> for FetchMetadataError {
fn from(e: jsonrpsee::core::Error) -> Self {
FetchMetadataError::RequestError(e)
}
}