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// }