avail_rust_client/client.rs
1//! High-level Avail client combining RPC access with helper APIs for blocks and transactions.
2
3use super::clients::OnlineClient;
4use crate::{
5 block::Block,
6 chain::{Best, Chain, Finalized},
7 subxt_rpcs::RpcClient,
8 transaction_api::TransactionApi,
9};
10use avail_rust_core::{rpc::Error as RpcError, types::metadata::HashStringNumber};
11#[cfg(feature = "tracing")]
12use tracing_subscriber::util::TryInitError;
13
14/// Primary entry point used throughout the SDK to interact with the node.
15#[derive(Clone)]
16pub struct Client {
17 online_client: OnlineClient,
18 pub rpc_client: RpcClient,
19}
20
21impl Client {
22 #[cfg(feature = "reqwest")]
23 /// Connects to an HTTP endpoint and returns a ready-to-use client.
24 ///
25 /// # Arguments
26 /// * `endpoint` - RPC URL (HTTP/S) exposed by an Avail node.
27 ///
28 /// # Returns
29 /// Returns a [`Client`] that clones its transports internally.
30 ///
31 /// # Errors
32 /// Returns `Err(Error)` if the HTTP transport cannot be initialized or the handshake fails.
33 ///
34 /// # Examples
35 /// ```no_run
36 /// # use avail_rust_client::Client;
37 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
38 /// let client = Client::new("https://turing-rpc.avail.so/rpc").await?;
39 /// let best = client.best().block_header().await?;
40 /// println!("Best block: {:?}", best.hash());
41 /// # Ok(()) }
42 /// ```
43 pub async fn new(endpoint: &str) -> Result<Client, crate::Error> {
44 Self::new_ext(endpoint, true).await
45 }
46
47 #[cfg(feature = "reqwest")]
48 /// Connects to an endpoint with optional retry behaviour during startup.
49 ///
50 /// # Arguments
51 /// * `endpoint` - RPC URL (HTTP/S) exposed by an Avail node.
52 /// * `retry` - When `true`, transient connection failures are retried before giving up.
53 ///
54 /// # Returns
55 /// Returns a fully initialised [`Client`].
56 ///
57 /// # Errors
58 /// Returns `Err(Error)` if the HTTP transport cannot be initialised or metadata bootstrap fails.
59 ///
60 /// # Examples
61 /// ```no_run
62 /// # use avail_rust_client::Client;
63 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
64 /// let client = Client::new_ext("http://127.0.0.1:9944", false).await?;
65 /// println!("Retries enabled? {}", client.is_global_retries_enabled());
66 /// # Ok(()) }
67 /// ```
68 pub async fn new_ext(endpoint: &str, retry: bool) -> Result<Client, crate::Error> {
69 use super::clients::ReqwestClient;
70
71 let op = async || -> Result<Client, crate::Error> {
72 let rpc_client = ReqwestClient::new(endpoint);
73 let rpc_client = RpcClient::new(rpc_client);
74
75 Self::from_rpc_client(rpc_client).await.map_err(|e| e.into())
76 };
77
78 crate::utils::with_retry_on_error(op, retry).await
79 }
80
81 /// Builds a client from an existing RPC transport.
82 ///
83 /// # Arguments
84 /// * `rpc_client` - Transport implementing the JSON-RPC client trait.
85 ///
86 /// # Returns
87 /// Returns a [`Client`] sharing the provided transport.
88 ///
89 /// # Errors
90 /// Propagates any failure returned by [`OnlineClient::new`].
91 pub async fn from_rpc_client(rpc_client: RpcClient) -> Result<Client, RpcError> {
92 let online_client = OnlineClient::new(&rpc_client).await?;
93 Self::from_components(rpc_client, online_client).await
94 }
95
96 /// Wraps pre-built components into a handy client handle.
97 ///
98 /// # Arguments
99 /// * `rpc_client` - Transport used for RPC calls.
100 /// * `online_client` - Metadata cache and retry configuration.
101 ///
102 /// # Returns
103 /// Returns a [`Client`] that reuses the provided components.
104 pub async fn from_components(rpc_client: RpcClient, online_client: OnlineClient) -> Result<Client, RpcError> {
105 Ok(Self { online_client, rpc_client })
106 }
107
108 #[cfg(feature = "tracing")]
109 /// Initialises tracing in plain text or JSON format.
110 ///
111 /// # Arguments
112 /// * `json_format` - When `true`, installs a JSON formatter; otherwise plain text.
113 ///
114 /// # Returns
115 /// Returns `Ok(())` once the global subscriber is installed.
116 ///
117 /// # Errors
118 /// Returns `Err(TryInitError)` when tracing was already initialised elsewhere.
119 ///
120 /// # Examples
121 /// ```no_run
122 /// # use avail_rust_client::Client;
123 /// Client::init_tracing(false).expect("tracing already initialised");
124 /// ```
125 pub fn init_tracing(json_format: bool) -> Result<(), TryInitError> {
126 use tracing_subscriber::util::SubscriberInitExt;
127
128 let builder = tracing_subscriber::fmt::SubscriberBuilder::default();
129 if json_format {
130 let builder = builder.json();
131 builder.finish().try_init()
132 } else {
133 builder.finish().try_init()
134 }
135 }
136
137 /// Hands back the underlying [`OnlineClient`] for advanced uses.
138 /// # Returns
139 /// Returns a clone of the cached online client state.
140 pub fn online_client(&self) -> OnlineClient {
141 self.online_client.clone()
142 }
143
144 /// Returns a transaction helper for crafting and submitting extrinsics.
145 ///
146 /// # Returns
147 /// Returns a [`TransactionApi`] that clones this client and issues RPCs lazily.
148 pub fn tx(&self) -> TransactionApi {
149 TransactionApi(self.clone())
150 }
151
152 /// Builds a block helper rooted at the supplied height or hash.
153 ///
154 /// # Arguments
155 /// * `block_id` - Hash, height, or string convertible into a [`HashStringNumber`].
156 ///
157 /// # Returns
158 /// Returns a [`Block`] helper exposing extrinsic, event, and metadata views.
159 pub fn block(&self, block_id: impl Into<HashStringNumber>) -> Block {
160 Block::new(self.clone(), block_id)
161 }
162
163 /// Provides low-level RPC helpers when you need finer control.
164 ///
165 /// # Returns
166 /// Returns a [`Chain`] handle exposing retry controls and raw RPC wrappers.
167 pub fn chain(&self) -> Chain {
168 Chain::new(self.clone())
169 }
170
171 /// Provides quick access to the best (head) block view.
172 ///
173 /// # Returns
174 /// Returns a [`Best`] helper optimised for repeated head queries.
175 pub fn best(&self) -> Best {
176 Best::new(self.clone())
177 }
178
179 /// Provides quick access to finalised block information.
180 ///
181 /// # Returns
182 /// Returns a [`Finalized`] helper mirroring [`Best`] convenience methods.
183 pub fn finalized(&self) -> Finalized {
184 Finalized::new(self.clone())
185 }
186
187 /// Reports whether automatic retries are currently enabled.
188 ///
189 /// # Returns
190 /// Returns `true` when global retries are on, otherwise `false`.
191 pub fn is_global_retries_enabled(&self) -> bool {
192 self.online_client.is_global_retries_enabled()
193 }
194
195 /// Turns automatic retries on or off for new requests.
196 ///
197 /// # Arguments
198 /// * `value` - `true` to enable retries for new helpers, `false` to disable.
199 pub fn set_global_retries_enabled(&self, value: bool) {
200 self.online_client.set_global_retries_enabled(value);
201 }
202}
203
204// use crate::{ExtrinsicEvent, ExtrinsicEvents, clients::Client, subxt_core::events::Phase};
205// use avail_rust_core::{H256, HashNumber, decoded_events::RawEvent, rpc::system::fetch_events};
206
207// pub const EVENTS_STORAGE_ADDRESS: &str = "0x26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7";
208
209// #[derive(Debug, Clone)]
210// pub struct HistoricalEvent {
211// pub phase: Phase,
212// // [Pallet_index, Variant_index, Event_data...]
213// pub bytes: RawEvent,
214// pub topics: Vec<H256>,
215// }
216
217// impl HistoricalEvent {
218// pub fn emitted_index(&self) -> (u8, u8) {
219// (self.bytes.pallet_index(), self.bytes.variant_index())
220// }
221
222// pub fn pallet_index(&self) -> u8 {
223// self.bytes.pallet_index()
224// }
225
226// pub fn variant_index(&self) -> u8 {
227// self.bytes.variant_index()
228// }
229
230// pub fn event_bytes(&self) -> &[u8] {
231// &self.bytes.0
232// }
233
234// pub fn event_data(&self) -> &[u8] {
235// self.bytes.event_data()
236// }
237// }
238
239// #[derive(Clone)]
240// pub struct EventClient {
241// client: Client,
242// }
243
244// impl EventClient {
245// pub fn new(client: Client) -> Self {
246// Self { client }
247// }
248
249// /// Use this function in case where `transaction_events` or `block_events` do not work.
250// /// Both mentioned functions require the runtime to have a specific runtime api available which
251// /// older blocks (runtime) do not have.
252// pub async fn historical_block_events(&self, at: H256) -> Result<Vec<HistoricalEvent>, RpcError> {
253// use crate::{config::AvailConfig, subxt_core::events::Events};
254
255// let entries = self
256// .client
257// .rpc()
258// .state_get_storage(EVENTS_STORAGE_ADDRESS, Some(at))
259// .await?;
260// let Some(event_bytes) = entries else {
261// return Ok(Vec::new());
262// };
263
264// let mut result: Vec<HistoricalEvent> = Vec::with_capacity(5);
265// let raw_events = Events::<AvailConfig>::decode_from(event_bytes, self.client.online_client().metadata());
266// for raw in raw_events.iter() {
267// let Ok(raw) = raw else {
268// continue;
269// };
270// let mut bytes: Vec<u8> = Vec::with_capacity(raw.field_bytes().len() + 2);
271// bytes.push(raw.pallet_index());
272// bytes.push(raw.variant_index());
273// bytes.append(&mut raw.field_bytes().to_vec());
274
275// let Ok(bytes) = RawEvent::try_from(bytes) else {
276// continue;
277// };
278
279// let value = HistoricalEvent { phase: raw.phase(), bytes, topics: raw.topics().to_vec() };
280// result.push(value);
281// }
282
283// Ok(result)
284// }
285// }