neo3/neo_clients/
mod.rs

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