neo3/neo_clients/
mod.rs

1#![allow(clippy::type_complexity)]
2#![warn(missing_docs)]
3#![deny(unsafe_code)]
4
5//! # Neo Clients
6//!
7//! Client interfaces for interacting with Neo N3 blockchain nodes.
8//!
9//! ## Overview
10//!
11//! The neo_clients module provides a comprehensive set of client interfaces for connecting to
12//! and interacting with Neo N3 blockchain nodes. It includes:
13//!
14//! - RPC clients for making JSON-RPC calls to Neo nodes
15//! - Multiple transport providers (HTTP, WebSocket, IPC)
16//! - Subscription support for real-time blockchain events
17//! - Mock clients for testing
18//! - Extension traits for domain-specific functionality
19//! - Error handling for network and protocol issues
20//!
21//! The module is designed to be flexible, allowing developers to choose the appropriate
22//! client implementation and transport mechanism for their specific use case.
23//!
24//! ## Examples
25//!
26//! ### Connecting to a Neo N3 node using HTTP
27//!
28//! ```rust,no_run
29//! use neo3::neo_clients::{HttpProvider, RpcClient, APITrait};
30//!
31//! #[tokio::main]
32//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
33//!     // Create an HTTP provider connected to a Neo N3 TestNet node
34//!     let provider = HttpProvider::new("https://testnet1.neo.org:443")?;
35//!     
36//!     // Create an RPC client with the provider
37//!     let client = RpcClient::new(provider);
38//!     
39//!     // Get the current block count
40//!     let block_count = client.get_block_count().await?;
41//!     println!("Current block count: {}", block_count);
42//!     
43//!     // Get information about the blockchain
44//!     let version = client.get_version().await?;
45//!     println!("Node version: {}", version.user_agent);
46//!     
47//!     Ok(())
48//! }
49//! ```
50//!
51//! ### Using WebSocket for real-time updates
52//!
53//! ```ignore
54//! # #[cfg(feature = "ws")]
55//! use neo3::neo_clients::{Ws, RpcClient, APITrait};
56//!
57//! # #[cfg(feature = "ws")]
58//! #[tokio::main]
59//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
60//!     // Connect to a Neo N3 node using WebSocket
61//!     let ws = Ws::connect("wss://testnet1.neo.org:4443/ws").await?;
62//!     let client = RpcClient::new(ws);
63//!     
64//!     // Get basic blockchain information over WebSocket
65//!     let block_count = client.get_block_count().await?;
66//!     println!("Current block count: {}", block_count);
67//!     
68//!     let version = client.get_version().await?;
69//!     println!("Node version: {}", version.user_agent);
70//!     
71//!     Ok(())
72//! }
73//! ```
74
75use lazy_static::lazy_static;
76
77use crate::config::NeoConstants;
78pub use api_trait::*;
79pub use cache::{Cache, CacheConfig, CacheStats, RpcCache};
80pub use circuit_breaker::{
81	CircuitBreaker, CircuitBreakerConfig, CircuitBreakerStats, CircuitState,
82};
83pub use connection_pool::{ConnectionPool, PoolConfig, PoolStats};
84pub use errors::ProviderError;
85pub use ext::*;
86pub use mock_client::MockClient;
87pub use production_client::{ProductionClientConfig, ProductionClientStats, ProductionRpcClient};
88pub use rpc::*;
89#[allow(deprecated)]
90pub use test_provider::{MAINNET, TESTNET};
91pub use utils::*;
92
93mod api_trait;
94mod cache;
95mod circuit_breaker;
96mod connection_pool;
97/// Errors
98mod errors;
99mod ext;
100mod mock_blocks;
101mod mock_client;
102mod production_client;
103mod rpc;
104mod rx;
105/// Crate utilities and type aliases
106mod utils;
107
108lazy_static! {
109	pub static ref HTTP_PROVIDER: RpcClient<Http> = {
110		let url_str =
111			std::env::var("ENDPOINT").unwrap_or_else(|_| NeoConstants::SEED_1.to_string());
112		let url = url::Url::parse(&url_str).expect("Failed to parse URL");
113		let http_provider = Http::new(url).expect("Failed to create HTTP provider");
114		RpcClient::new(http_provider)
115	};
116}
117
118#[allow(missing_docs)]
119/// Pre-instantiated Infura HTTP clients which rotate through multiple API keys
120/// to prevent rate limits
121mod test_provider {
122	use std::{convert::TryFrom, iter::Cycle, slice::Iter, sync::Mutex};
123
124	use once_cell::sync::Lazy;
125
126	use super::*;
127
128	// List of infura keys to rotate through so we don't get rate limited
129	const INFURA_KEYS: &[&str] = &["15e8aaed6f894d63a0f6a0206c006cdd"];
130
131	pub static MAINNET: Lazy<TestProvider> =
132		Lazy::new(|| TestProvider::new(INFURA_KEYS, "mainnet"));
133
134	pub static TESTNET: Lazy<TestProvider> =
135		Lazy::new(|| TestProvider::new(INFURA_KEYS, "testnet"));
136
137	#[derive(Debug)]
138	pub struct TestProvider {
139		network: String,
140		keys: Mutex<Cycle<Iter<'static, &'static str>>>,
141	}
142
143	impl TestProvider {
144		pub fn new(keys: &'static [&'static str], network: impl Into<String>) -> Self {
145			Self { keys: keys.iter().cycle().into(), network: network.into() }
146		}
147
148		pub fn url(&self) -> String {
149			let Self { network, keys } = self;
150			let key = keys.lock().unwrap().next().unwrap();
151			format!("https://{network}.infura.io/v3/{key}")
152		}
153
154		pub fn provider(&self) -> RpcClient<Http> {
155			let url_str = self.url();
156			let url = url::Url::parse(&url_str).expect("Failed to parse URL");
157			let http_provider = Http::new(url).expect("Failed to create HTTP provider");
158			RpcClient::new(http_provider)
159		}
160
161		#[cfg(feature = "ws")]
162		pub async fn ws(&self) -> RpcClient<crate::Ws> {
163			let url = format!(
164				"wss://{}.infura.neo.io/ws/v3/{}",
165				self.network,
166				self.keys.lock().unwrap().next().unwrap()
167			);
168			RpcClient::connect(url.as_str()).await.unwrap()
169		}
170	}
171}