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}