diff --git a/examples/async_config_based_creation.rs b/examples/async_config_based_creation.rs new file mode 100644 index 0000000..e23b543 --- /dev/null +++ b/examples/async_config_based_creation.rs @@ -0,0 +1,23 @@ +use helius::config::Config; +use helius::error::Result; +use helius::types::Cluster; +use helius::Helius; + +/// Demonstrates creating a Helius client with async Solana capabilities using the config-based approach +#[tokio::main] +async fn main() -> Result<()> { + let api_key: &str = "your_api_key"; + let cluster: Cluster = Cluster::MainnetBeta; + + let config: Config = Config::new(api_key, cluster)?; + let async_client: Helius = config.create_client_with_async()?; + + if let Ok(async_conn) = async_client.async_connection() { + println!( + "Async client - Get Block Height: {:?}", + async_conn.get_block_height().await + ); + } + + Ok(()) +} diff --git a/src/config.rs b/src/config.rs index 3630d66..1700980 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,12 @@ use crate::error::{HeliusError, Result}; +use crate::rpc_client::RpcClient; use crate::types::{Cluster, HeliusEndpoints, MintApiAuthority}; +use crate::websocket::{EnhancedWebsocket, ENHANCED_WEBSOCKET_URL}; +use crate::Helius; +use reqwest::Client; +use solana_client::nonblocking::rpc_client::RpcClient as AsyncSolanaRpcClient; +use std::sync::Arc; +use url::{ParseError, Url}; /// Configuration settings for the Helius client /// @@ -41,6 +48,115 @@ impl Config { }) } + pub fn rpc_client_with_reqwest_client(&self, client: Client) -> Result { + RpcClient::new(Arc::new(client), Arc::new(self.clone())) + } + + /// Creates a basic Helius client from this configuration + /// + /// # Returns + /// A `Result` containing a Helius client with basic RPC capabilities + pub fn create_client(self) -> Result { + let client: Client = Client::builder().build().map_err(HeliusError::ReqwestError)?; + let rpc_client: Arc = Arc::new(self.rpc_client_with_reqwest_client(client.clone())?); + + Ok(Helius { + config: Arc::new(self), + client, + rpc_client, + async_rpc_client: None, + ws_client: None, + }) + } + + /// Creates a Helius client with async Solana capabilities + /// + /// # Returns + /// A `Result` containing a Helius client with both RPC and async Solana capabilities + pub fn create_client_with_async(self) -> Result { + let client: Client = Client::builder().build().map_err(HeliusError::ReqwestError)?; + let mut rpc_url: Url = Url::parse(&self.endpoints.rpc) + .map_err(|e: ParseError| HeliusError::InvalidInput(format!("Invalid RPC URL: {}", e)))?; + + rpc_url.query_pairs_mut().append_pair("api-key", &self.api_key); + + let async_solana_client: Arc = Arc::new(AsyncSolanaRpcClient::new(rpc_url.to_string())); + let rpc_client: Arc = Arc::new(self.rpc_client_with_reqwest_client(client.clone())?); + + Ok(Helius { + config: Arc::new(self), + client, + rpc_client, + async_rpc_client: Some(async_solana_client), + ws_client: None, + }) + } + + /// Creates a Helius client with websocket support + /// + /// # Arguments + /// * `ping_interval_secs` - Optional duration in seconds between ping messages + /// * `pong_timeout_secs` - Optional duration in seconds to wait for pong response + /// + /// # Returns + /// A `Result` containing a Helius client with websocket support + pub async fn create_client_with_ws( + self, + ping_interval_secs: Option, + pong_timeout_secs: Option, + ) -> Result { + let client: Client = Client::builder().build().map_err(HeliusError::ReqwestError)?; + let rpc_client: Arc = Arc::new(self.rpc_client_with_reqwest_client(client.clone())?); + + let wss: String = format!("{}{}", ENHANCED_WEBSOCKET_URL, self.api_key); + let ws_client: Arc = + Arc::new(EnhancedWebsocket::new(&wss, ping_interval_secs, pong_timeout_secs).await?); + + Ok(Helius { + config: Arc::new(self), + client, + rpc_client, + async_rpc_client: None, + ws_client: Some(ws_client), + }) + } + + /// Creates a full-featured Helius client with both async and websocket support + /// + /// # Arguments + /// * `ping_interval_secs` - Optional duration in seconds between ping messages + /// * `pong_timeout_secs` - Optional duration in seconds to wait for pong response + /// + /// # Returns + /// A `Result` containing a fully-featured Helius client + pub async fn create_full_client( + self, + ping_interval_secs: Option, + pong_timeout_secs: Option, + ) -> Result { + let client: Client = Client::builder().build().map_err(HeliusError::ReqwestError)?; + let rpc_client: Arc = Arc::new(self.rpc_client_with_reqwest_client(client.clone())?); + + // Setup async client + let mut rpc_url: Url = Url::parse(&self.endpoints.rpc) + .map_err(|e: ParseError| HeliusError::InvalidInput(format!("Invalid RPC URL: {}", e)))?; + rpc_url.query_pairs_mut().append_pair("api-key", &self.api_key); + let async_solana_client = Arc::new(AsyncSolanaRpcClient::new(rpc_url.to_string())); + + // Setup websocket + let wss: String = format!("{}{}", ENHANCED_WEBSOCKET_URL, self.api_key); + let ws_client: Arc = + Arc::new(EnhancedWebsocket::new(&wss, ping_interval_secs, pong_timeout_secs).await?); + + Ok(Helius { + config: Arc::new(self), + client, + rpc_client, + async_rpc_client: Some(async_solana_client), + ws_client: Some(ws_client), + }) + } + pub fn mint_api_authority(&self) -> MintApiAuthority { MintApiAuthority::from_cluster(&self.cluster) } diff --git a/tests/test_config.rs b/tests/test_config.rs index 75568aa..83f99e3 100644 --- a/tests/test_config.rs +++ b/tests/test_config.rs @@ -1,6 +1,7 @@ use helius::config::Config; use helius::error::{HeliusError, Result}; use helius::types::Cluster; +use helius::Helius; #[test] fn test_config_new_with_empty_api_key() { @@ -18,3 +19,25 @@ fn test_config_new_with_valid_api_key() { assert_eq!(config.endpoints.api, "https://api-devnet.helius-rpc.com/"); assert_eq!(config.endpoints.rpc, "https://devnet.helius-rpc.com/"); } + +#[test] +fn test_create_basic_client() { + let config: Config = Config::new("valid-api-key", Cluster::Devnet).unwrap(); + let result: Result = config.create_client(); + assert!(result.is_ok()); + + let client: Helius = result.unwrap(); + assert!(client.async_rpc_client.is_none()); + assert!(client.ws_client.is_none()); +} + +#[test] +fn test_create_async_client() { + let config: Config = Config::new("valid-api-key", Cluster::Devnet).unwrap(); + let result: Result = config.create_client_with_async(); + assert!(result.is_ok()); + + let client: Helius = result.unwrap(); + assert!(client.async_rpc_client.is_some()); + assert!(client.ws_client.is_none()); +}