subxt_rpcs/lib.rs
1// Copyright 2019-2025 Parity Technologies (UK) Ltd.
2// This file is dual-licensed as Apache-2.0 or GPL-3.0.
3// see LICENSE for license details.
4
5//! This crate provides a low level RPC interface to Substrate based nodes.
6//!
7//! See the [`client`] module for a [`client::RpcClient`] which is driven by implementations
8//! of [`client::RpcClientT`] (several of which are provided behind feature flags).
9//!
10//! See the [`methods`] module for structs which implement sets of concrete RPC calls for
11//! communicating with Substrate based nodes. These structs are all driven by a [`client::RpcClient`].
12//!
13//! The RPC clients/methods here are made use of in `subxt`. Enabling the `subxt` feature flag ensures
14//! that all Subxt configurations are also valid RPC configurations.
15//!
16//! The provided RPC client implementations can be used natively (with the default `native` feature
17//! flag) or in WASM based web apps (with the `web` feature flag).
18
19#![cfg_attr(docsrs, feature(doc_cfg))]
20
21#[cfg(any(
22 all(feature = "web", feature = "native"),
23 not(any(feature = "web", feature = "native"))
24))]
25compile_error!("subxt-rpcs: exactly one of the 'web' and 'native' features should be used.");
26
27mod macros;
28
29pub mod client;
30pub mod methods;
31pub mod utils;
32
33// Used to enable the js feature for wasm.
34#[cfg(feature = "web")]
35#[allow(unused_imports)]
36pub use getrandom as _;
37
38// Expose the most common things at the top level:
39pub use client::{RpcClient, RpcClientT};
40pub use methods::{ChainHeadRpcMethods, LegacyRpcMethods};
41
42/// Configuration used by some of the RPC methods to determine the shape of
43/// some of the inputs or responses.
44pub trait RpcConfig {
45 /// The block header type.
46 type Header: Header;
47 /// The block hash type.
48 type Hash: Hash;
49 /// The Account ID type.
50 type AccountId: AccountId;
51}
52
53/// A trait which is applied to any type that is a valid block header.
54pub trait Header: std::fmt::Debug + codec::Decode + serde::de::DeserializeOwned {}
55impl<T> Header for T where T: std::fmt::Debug + codec::Decode + serde::de::DeserializeOwned {}
56
57/// A trait which is applied to any type that is a valid block hash.
58pub trait Hash: serde::de::DeserializeOwned + serde::Serialize {}
59impl<T> Hash for T where T: serde::de::DeserializeOwned + serde::Serialize {}
60
61/// A trait which is applied to any type that is a valid Account ID.
62pub trait AccountId: serde::Serialize {}
63impl<T> AccountId for T where T: serde::Serialize {}
64
65// When the subxt feature is enabled, ensure that any valid `subxt::Config`
66// is also a valid `RpcConfig`.
67#[cfg(feature = "subxt")]
68mod impl_config {
69 use super::*;
70 use subxt_core::config::HashFor;
71
72 impl<T> RpcConfig for T
73 where
74 T: subxt_core::Config,
75 {
76 type Header = T::Header;
77 type Hash = HashFor<T>;
78 type AccountId = T::AccountId;
79 }
80}
81
82/// This encapsulates any errors that could be emitted in this crate.
83#[derive(Debug, thiserror::Error)]
84#[non_exhaustive]
85pub enum Error {
86 /// An error which indicates a user fault.
87 #[error("User error: {0}")]
88 User(#[from] UserError),
89 // Dev note: We need the error to be safely sent between threads
90 // for `subscribe_to_block_headers_filling_in_gaps` and friends.
91 /// An error coming from the underlying RPC Client.
92 #[error("RPC error: client error: {0}")]
93 Client(Box<dyn std::error::Error + Send + Sync + 'static>),
94 /// The connection was lost and the client will automatically reconnect. Clients
95 /// should only emit this if they are internally reconnecting, and will buffer any
96 /// calls made to them in the meantime until the connection is re-established.
97 #[error("RPC error: the connection was lost ({0}); reconnect automatically initiated")]
98 DisconnectedWillReconnect(String),
99 /// Cannot deserialize the response.
100 #[error("RPC error: cannot deserialize response: {0}")]
101 Deserialization(serde_json::Error),
102 /// Cannot SCALE decode some part of the response.
103 #[error("RPC error: cannot SCALE decode some part of the response: {0}")]
104 Decode(codec::Error),
105 /// The requested URL is insecure.
106 #[error("RPC error: insecure URL: {0}")]
107 InsecureUrl(String),
108}
109
110impl Error {
111 /// Is the error the `DisconnectedWillReconnect` variant? This should be true
112 /// only if the underlying `RpcClient` implementation was disconnected and is
113 /// automatically reconnecting behind the scenes.
114 pub fn is_disconnected_will_reconnect(&self) -> bool {
115 matches!(self, Error::DisconnectedWillReconnect(_))
116 }
117}
118
119/// This error should be returned when the user is at fault making a call,
120/// for instance because the method name was wrong, parameters invalid or some
121/// invariant not upheld. Implementations of [`RpcClientT`] should turn any such
122/// errors into this, so that they can be handled appropriately. By contrast,
123/// [`Error::Client`] is emitted when the underlying RPC Client implementation
124/// has some problem that isn't user specific (eg network issues or similar).
125#[derive(Debug, Clone, serde::Deserialize, thiserror::Error)]
126#[serde(deny_unknown_fields)]
127pub struct UserError {
128 /// Code
129 pub code: i32,
130 /// Message
131 pub message: String,
132 /// Optional data
133 pub data: Option<Box<serde_json::value::RawValue>>,
134}
135
136impl UserError {
137 /// Returns a standard JSON-RPC "method not found" error.
138 pub fn method_not_found() -> UserError {
139 UserError {
140 code: -32601,
141 message: "Method not found".to_owned(),
142 data: None,
143 }
144 }
145}
146
147impl core::fmt::Display for UserError {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 write!(f, "{} ({})", &self.message, &self.code)
150 }
151}