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}