solana_recover/lib.rs
1//! # Solana Recover
2//!
3//! A high-performance Solana wallet scanner and SOL recovery library.
4//!
5//! This library provides functionality to:
6//! - Scan Solana wallets for empty token accounts
7//! - Calculate recoverable SOL from empty accounts
8//! - Perform automated SOL recovery operations
9//! - Handle batch processing of multiple wallets
10//! - Provide connection pooling and caching for optimal performance
11//!
12//! ## Quick Start
13//!
14//! ```rust,no_run
15//! use solana_recover::{scan_wallet};
16//!
17//! #[tokio::main]
18//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
19//! let result = scan_wallet("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM", None).await?;
20//!
21//! println!("Found {} recoverable SOL", result.recoverable_sol);
22//!
23//! Ok(())
24//! }
25//! ```
26//!
27//! ## Feature Flags
28//!
29//! - `default`: Enables `scanner` and `client` features
30//! - `scanner`: Core wallet scanning functionality
31//! - `client`: HTTP client for external APIs
32//! - `api`: REST API server functionality
33//! - `full`: Enables all features
34//! - `database`: Database persistence support
35//! - `cache`: Advanced caching capabilities
36//! - `metrics`: Prometheus metrics collection
37//! - `security`: Enhanced security features
38//! - `config`: Configuration file support
39
40#![cfg_attr(docsrs, feature(doc_cfg))]
41
42// Core dependencies
43use serde::{Deserialize, Serialize};
44use std::sync::Arc;
45
46// Export core modules
47pub mod core;
48pub mod rpc;
49pub mod storage;
50
51pub mod wallet;
52pub mod utils;
53pub mod config;
54pub mod api;
55
56// NFT module (when feature is enabled)
57#[cfg(feature = "nft")]
58pub mod nft;
59
60// Re-export commonly used types
61pub use core::*;
62
63/// Re-export NFT types when feature is enabled
64#[cfg(feature = "nft")]
65pub use nft::scanner::NftScanResult;
66#[cfg(feature = "nft")]
67pub use nft::types::NftInfo;
68
69/// Unified scan result containing both SOL and NFT information
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct UnifiedScanResult {
72 /// SOL wallet information
73 pub sol_info: Option<WalletInfo>,
74 /// NFT scan information
75 #[cfg(feature = "nft")]
76 pub nft_info: Option<NftScanResult>,
77 /// Scan mode used
78 pub scan_mode: ScanMode,
79 /// Total scan duration in milliseconds
80 pub total_scan_time_ms: u64,
81 /// Wallet address scanned
82 pub wallet_address: String,
83}
84
85/// Scan modes for different types of scanning
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub enum ScanMode {
88 /// Scan SOL accounts only
89 SolOnly,
90 /// Scan NFT accounts only
91 NftOnly,
92 /// Scan both SOL and NFT accounts
93 Both,
94}
95
96/// Library version
97pub const VERSION: &str = env!("CARGO_PKG_VERSION");
98
99/// Default RPC endpoint for mainnet
100pub const DEFAULT_MAINNET_ENDPOINT: &str = "https://api.mainnet-beta.solana.com";
101
102/// Default RPC endpoint for devnet
103pub const DEFAULT_DEVNET_ENDPOINT: &str = "https://api.devnet.solana.com";
104
105/// Represents an empty token account
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct EmptyAccount {
108 /// The account address
109 pub address: String,
110 /// The mint address of token
111 pub mint: String,
112 /// The owner address
113 pub owner: String,
114 /// Balance in lamports (recoverable amount after rent exemption)
115 pub lamports: u64,
116}
117
118/// Ultra-fast unified wallet scanning with SOL and NFT support
119///
120/// This function provides the fastest possible wallet scanning using:
121/// - Predictive prefetching
122/// - Connection multiplexing
123/// - Smart batching
124/// - Fast path scanning for common patterns
125/// - Maximum parallelization
126/// - Unified SOL and NFT scanning
127///
128/// # Arguments
129///
130/// * `wallet_address` - The Solana wallet address to scan
131/// * `rpc_endpoint` - Optional RPC endpoint (defaults to mainnet)
132/// * `scan_mode` - Scan mode: sol, nft, or both
133///
134/// # Returns
135///
136/// Returns a `UnifiedScanResult` containing both SOL and NFT scan results.
137///
138/// # Example
139///
140/// ```rust,no_run
141/// use solana_recover::{scan_wallet_unified, ScanMode};
142///
143/// #[tokio::main]
144/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
145/// let result = scan_wallet_unified("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM", None, ScanMode::Both).await?;
146/// if let Some(sol_info) = &result.sol_info {
147/// println!("Found {} recoverable SOL", sol_info.recoverable_sol);
148/// }
149/// if let Some(nft_info) = &result.nft_info {
150/// println!("Found {} NFTs", nft_info.nfts.len());
151/// }
152/// Ok(())
153/// }
154/// ```
155#[cfg(feature = "nft")]
156pub async fn scan_wallet_unified(
157 wallet_address: &str,
158 rpc_endpoint: Option<&str>,
159 scan_mode: ScanMode,
160) -> core::Result<UnifiedScanResult> {
161 use core::ScannerFactory;
162 use core::unified_scanner::UnifiedScannerConfig;
163 use core::unified_scanner::PerformanceMode;
164 use core::types::RpcEndpoint;
165 use rpc::ConnectionPool;
166 use nft::scanner::{NftScanner, NftScannerConfig};
167 use std::sync::Arc;
168
169 let start_time = std::time::Instant::now();
170 let endpoint = rpc_endpoint.unwrap_or(DEFAULT_MAINNET_ENDPOINT);
171 let rpc_endpoint = RpcEndpoint {
172 url: endpoint.to_string(),
173 priority: 0,
174 rate_limit_rps: 200, // Higher rate limit for ultra-fast
175 timeout_ms: 5000, // Shorter timeout for ultra-fast
176 healthy: true,
177 };
178
179 let connection_pool = Arc::new(ConnectionPool::new(vec![rpc_endpoint], 16));
180
181 // Initialize results
182 let mut sol_info = None;
183 let mut nft_info = None;
184
185 // Scan SOL accounts if requested
186 if matches!(scan_mode, ScanMode::SolOnly | ScanMode::Both) {
187 let config = UnifiedScannerConfig {
188 performance_mode: PerformanceMode::UltraFast,
189 max_concurrent_scans: 500,
190 scan_timeout: std::time::Duration::from_secs(2),
191 batch_size: 100,
192 enable_optimizations: true,
193 enable_caching: true,
194 enable_parallel_processing: true,
195 };
196
197 let scanner = ScannerFactory::create_with_config(connection_pool.clone(), config)?;
198 let scan_result = scanner.scan_wallet(wallet_address).await?;
199 sol_info = scan_result.result;
200 }
201
202 // Scan NFT accounts if requested
203 if matches!(scan_mode, ScanMode::NftOnly | ScanMode::Both) {
204 let nft_config = NftScannerConfig {
205 performance_mode: nft::types::PerformanceMode::UltraFast,
206 max_concurrent_scans: 50,
207 scan_timeout_seconds: 60,
208 enable_batch_processing: true,
209 ..Default::default()
210 };
211
212 let nft_scanner = NftScanner::new(connection_pool.clone(), nft_config)?;
213 nft_info = Some(nft_scanner.scan_wallet_nfts(wallet_address).await?);
214 }
215
216 let total_scan_time_ms = start_time.elapsed().as_millis() as u64;
217
218 #[cfg(feature = "nft")]
219 {
220 Ok(UnifiedScanResult {
221 sol_info,
222 nft_info,
223 scan_mode,
224 total_scan_time_ms,
225 wallet_address: wallet_address.to_string(),
226 })
227 }
228 #[cfg(not(feature = "nft"))]
229 {
230 Ok(UnifiedScanResult {
231 sol_info,
232 scan_mode,
233 total_scan_time_ms,
234 wallet_address: wallet_address.to_string(),
235 })
236 }
237}
238
239/// Calculate total claimable assets for multiple wallets with unified scanning
240#[cfg(feature = "nft")]
241pub async fn calculate_total_claimable_unified(
242 targets: &str,
243 rpc_endpoint: Option<&str>,
244 dev: bool,
245 scan_mode: ScanMode,
246) -> core::Result<UnifiedTotalClaimResult> {
247 let (wallets, _is_private_key) = parse_targets_wrapper(targets)?;
248
249 let mut total_recoverable_sol = 0.0;
250 let mut total_nfts = 0usize;
251 let mut total_nft_value = 0u64;
252 let mut wallet_results = Vec::new();
253
254 for wallet_address in wallets {
255 let scan_result = scan_wallet_unified(&wallet_address, rpc_endpoint, scan_mode.clone()).await?;
256
257 if let Some(sol_info) = &scan_result.sol_info {
258 total_recoverable_sol += sol_info.recoverable_sol;
259 }
260
261 if let Some(nft_info) = &scan_result.nft_info {
262 total_nfts += nft_info.nfts.len();
263 total_nft_value += nft_info.total_estimated_value_lamports;
264 }
265
266 if dev {
267 wallet_results.push((wallet_address, scan_result));
268 }
269 }
270
271 Ok(UnifiedTotalClaimResult {
272 total_wallets: wallet_results.len(),
273 total_recoverable_sol,
274 total_nfts,
275 total_nft_value_lamports: total_nft_value,
276 wallet_results,
277 scan_mode,
278 })
279}
280
281/// Parse targets string into wallet addresses
282/// Wrapper function to parse targets in the same format as the CLI
283pub fn parse_targets_wrapper(targets: &str) -> core::Result<(Vec<String>, bool)> {
284 if targets.starts_with("wallet:") {
285 let addresses = targets.strip_prefix("wallet:")
286 .unwrap()
287 .split(',')
288 .map(|s| s.trim().to_string())
289 .filter(|s| !s.is_empty())
290 .collect();
291 Ok((addresses, false))
292 } else if targets.starts_with("key:") {
293 // For private keys, we would need to derive addresses
294 // This is a placeholder - in a real implementation, you'd parse the private keys
295 // and derive the corresponding wallet addresses
296 Err(core::SolanaRecoverError::InternalError("Private key parsing not implemented in unified scanner".to_string()))
297 } else {
298 // Assume it's a single wallet address
299 Ok((vec![targets.trim().to_string()], false))
300 }
301}
302
303/// Unified total claim result containing both SOL and NFT information
304#[derive(Debug, Clone, Serialize, Deserialize)]
305pub struct UnifiedTotalClaimResult {
306 /// Total wallets scanned
307 pub total_wallets: usize,
308 /// Total recoverable SOL
309 pub total_recoverable_sol: f64,
310 /// Total NFTs found
311 pub total_nfts: usize,
312 /// Total NFT value in lamports
313 pub total_nft_value_lamports: u64,
314 /// Individual wallet results (only included if dev mode is enabled)
315 pub wallet_results: Vec<(String, UnifiedScanResult)>,
316 /// Scan mode used
317 pub scan_mode: ScanMode,
318}
319///
320/// This function provides the fastest possible wallet scanning using:
321/// - Predictive prefetching
322/// - Connection multiplexing
323/// - Smart batching
324/// - Fast path scanning for common patterns
325/// - Maximum parallelization
326///
327/// # Arguments
328///
329/// * `wallet_address` - The Solana wallet address to scan
330/// * `rpc_endpoint` - Optional RPC endpoint (defaults to mainnet)
331///
332/// # Returns
333///
334/// Returns a `WalletInfo` containing scan results in sub-second time.
335///
336/// # Example
337///
338/// ```rust,no_run
339/// use solana_recover::scan_wallet_ultra_fast;
340///
341/// #[tokio::main]
342/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
343/// let result = scan_wallet_ultra_fast("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM", None).await?;
344/// println!("Found {} recoverable SOL in {}ms", result.recoverable_sol, result.scan_time_ms);
345/// Ok(())
346/// }
347/// ```
348pub async fn scan_wallet_ultra_fast(
349 wallet_address: &str,
350 rpc_endpoint: Option<&str>,
351) -> core::Result<WalletInfo> {
352 use core::ScannerFactory;
353 use core::unified_scanner::UnifiedScannerConfig;
354 use core::unified_scanner::PerformanceMode;
355 use core::types::RpcEndpoint;
356 use rpc::ConnectionPool;
357 use std::sync::Arc;
358
359 let endpoint = rpc_endpoint.unwrap_or(DEFAULT_MAINNET_ENDPOINT);
360 let rpc_endpoint = RpcEndpoint {
361 url: endpoint.to_string(),
362 priority: 0,
363 rate_limit_rps: 200, // Higher rate limit for ultra-fast
364 timeout_ms: 5000, // Shorter timeout for ultra-fast
365 healthy: true,
366 };
367
368 let connection_pool = Arc::new(ConnectionPool::new(vec![rpc_endpoint], 16));
369
370 // Ultra-fast configuration using new unified architecture
371 let config = UnifiedScannerConfig {
372 performance_mode: PerformanceMode::UltraFast,
373 max_concurrent_scans: 500,
374 scan_timeout: std::time::Duration::from_secs(2),
375 batch_size: 100,
376 enable_optimizations: true,
377 enable_caching: true,
378 enable_parallel_processing: true,
379 };
380
381 let scanner = ScannerFactory::create_with_config(connection_pool, config)?;
382 let scan_result = scanner.scan_wallet(wallet_address).await?;
383
384 scan_result.result.ok_or_else(||
385 SolanaRecoverError::InternalError("Scan result is empty".to_string())
386 )
387}
388
389/// Convenience function for quick wallet scanning using the unified scanner
390///
391/// This is the simplest way to scan a wallet for empty accounts using the new
392/// unified architecture with strategy pattern.
393///
394/// # Arguments
395///
396/// * `wallet_address` - The Solana wallet address to scan
397/// * `rpc_endpoint` - Optional RPC endpoint (defaults to mainnet)
398///
399/// # Returns
400///
401/// Returns a `WalletInfo` containing scan results.
402///
403/// # Example
404///
405/// ```rust,no_run
406/// use solana_recover::scan_wallet;
407///
408/// #[tokio::main]
409/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
410/// let result = scan_wallet("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM", None).await?;
411/// println!("Found {} recoverable SOL", result.recoverable_sol);
412/// Ok(())
413/// }
414/// ```
415pub async fn scan_wallet(
416 wallet_address: &str,
417 rpc_endpoint: Option<&str>,
418) -> core::Result<WalletInfo> {
419 use core::ScannerFactory;
420 use core::types::RpcEndpoint;
421 use rpc::ConnectionPool;
422 use std::sync::Arc;
423
424 let endpoint = rpc_endpoint.unwrap_or(DEFAULT_MAINNET_ENDPOINT);
425 let rpc_endpoint = RpcEndpoint {
426 url: endpoint.to_string(),
427 priority: 0,
428 rate_limit_rps: 100,
429 timeout_ms: 30000,
430 healthy: true,
431 };
432
433 let connection_pool = Arc::new(ConnectionPool::new(vec![rpc_endpoint], 8));
434
435 // Use balanced mode for general scanning
436 let scanner = ScannerFactory::create_balanced(connection_pool)?;
437 let scan_result = scanner.scan_wallet(wallet_address).await?;
438
439 scan_result.result.ok_or_else(||
440 SolanaRecoverError::InternalError("Scan result is empty".to_string())
441 )
442}
443
444/// Convenience function for SOL recovery using the core recovery manager
445///
446/// This is the simplest way to recover SOL from empty accounts.
447///
448/// # Arguments
449///
450/// * `request` - The recovery request containing wallet and destination info
451/// * `rpc_endpoint` - Optional RPC endpoint (defaults to mainnet)
452/// * `wallet_manager` - Optional shared wallet manager instance
453///
454/// # Returns
455///
456/// Returns a `RecoveryResult` containing recovery operation results.
457///
458/// # Example
459///
460/// ```rust,no_run
461/// use solana_recover::{recover_sol, RecoveryRequest, wallet::WalletManager};
462///
463/// #[tokio::main]
464/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
465/// let wallet_manager = std::sync::Arc::new(WalletManager::new());
466/// let request = RecoveryRequest {
467/// // ... populate request fields
468/// id: uuid::Uuid::new_v4(),
469/// wallet_address: "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM".to_string(),
470/// destination_address: "destination_address_here".to_string(),
471/// empty_accounts: vec![],
472/// max_fee_lamports: Some(10_000_000),
473/// priority_fee_lamports: None,
474/// wallet_connection_id: None,
475/// user_id: None,
476/// created_at: chrono::Utc::now(),
477/// };
478///
479/// let result = recover_sol(&request, None, Some(wallet_manager)).await?;
480/// println!("Recovered {} SOL", result.net_sol);
481/// Ok(())
482/// }
483/// ```
484pub async fn recover_sol(
485 request: &RecoveryRequest,
486 rpc_endpoint: Option<&str>,
487 wallet_manager: Option<std::sync::Arc<wallet::WalletManager>>,
488) -> core::Result<RecoveryResult> {
489 use rpc::ConnectionPool;
490 use core::{RpcEndpoint, RecoveryManager, RecoveryConfig};
491 use wallet::WalletManager;
492
493 let endpoint = rpc_endpoint.unwrap_or(DEFAULT_MAINNET_ENDPOINT);
494 let rpc_endpoint = RpcEndpoint {
495 url: endpoint.to_string(),
496 priority: 0,
497 rate_limit_rps: 100,
498 timeout_ms: 30000,
499 healthy: true,
500 };
501 let connection_pool = Arc::new(ConnectionPool::new(vec![rpc_endpoint], 8));
502
503 let config = RecoveryConfig::default();
504 let fee_structure = core::FeeStructure::default(); // Can be customized
505 let wallet_manager = wallet_manager.unwrap_or_else(|| Arc::new(WalletManager::new()));
506 let recovery_manager = RecoveryManager::new(connection_pool, wallet_manager, config, fee_structure);
507
508 recovery_manager.recover_sol(request).await
509}
510
511/// Convenience function for ultra-fast NFT scanning
512///
513/// This function provides the fastest possible NFT scanning using:
514/// - Ultra-fast performance mode
515/// - Maximum parallelization
516/// - Optimized caching
517/// - Minimal validation
518///
519/// # Arguments
520///
521/// * `wallet_address` - The Solana wallet address to scan for NFTs
522/// * `rpc_endpoint` - Optional RPC endpoint (defaults to mainnet)
523///
524/// # Returns
525///
526/// Returns an `NftScanResult` containing comprehensive NFT analysis.
527///
528/// # Example
529///
530/// ```rust,no_run
531/// use solana_recover::scan_wallet_nfts_ultra_fast;
532///
533/// #[tokio::main]
534/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
535/// let result = scan_wallet_nfts_ultra_fast("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM", None).await?;
536/// println!("Found {} NFTs with total value {} SOL",
537/// result.nfts.len(), result.total_estimated_value_lamports as f64 / 1_000_000_000.0);
538/// Ok(())
539/// }
540/// ```
541#[cfg(feature = "nft")]
542pub async fn scan_wallet_nfts_ultra_fast(
543 wallet_address: &str,
544 rpc_endpoint: Option<&str>,
545) -> core::Result<NftScanResult> {
546 use nft::scanner::create_ultra_fast_nft_scanner;
547 use core::types::RpcEndpoint;
548 use rpc::ConnectionPool;
549 use std::sync::Arc;
550
551 let endpoint = rpc_endpoint.unwrap_or(DEFAULT_MAINNET_ENDPOINT);
552 let rpc_endpoint = RpcEndpoint {
553 url: endpoint.to_string(),
554 priority: 0,
555 rate_limit_rps: 200, // Higher rate limit for ultra-fast
556 timeout_ms: 5000, // Shorter timeout for ultra-fast
557 healthy: true,
558 };
559
560 let connection_pool = Arc::new(ConnectionPool::new(vec![rpc_endpoint], 16));
561 let scanner = create_ultra_fast_nft_scanner(connection_pool)?;
562
563 let scan_result = scanner.scan_wallet_nfts(wallet_address).await?;
564
565 Ok(scan_result)
566}
567
568/// Convenience function for balanced NFT scanning
569///
570/// This function provides balanced NFT scanning with comprehensive analysis
571/// including metadata, valuation, security validation, and portfolio analysis.
572///
573/// # Arguments
574///
575/// * `wallet_address` - The Solana wallet address to scan for NFTs
576/// * `rpc_endpoint` - Optional RPC endpoint (defaults to mainnet)
577///
578/// # Returns
579///
580/// Returns an `NftScanResult` containing comprehensive NFT analysis.
581///
582/// # Example
583///
584/// ```rust,no_run
585/// use solana_recover::scan_wallet_nfts;
586///
587/// #[tokio::main]
588/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
589/// let result = scan_wallet_nfts("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM", None).await?;
590/// println!("Found {} NFTs with {} security issues",
591/// result.nfts.len(), result.security_issues.len());
592/// Ok(())
593/// }
594/// ```
595#[cfg(feature = "nft")]
596pub async fn scan_wallet_nfts(
597 wallet_address: &str,
598 rpc_endpoint: Option<&str>,
599) -> core::Result<NftScanResult> {
600 use nft::scanner::create_nft_scanner;
601 use core::types::RpcEndpoint;
602 use rpc::ConnectionPool;
603 use std::sync::Arc;
604
605 let endpoint = rpc_endpoint.unwrap_or(DEFAULT_MAINNET_ENDPOINT);
606 let rpc_endpoint = RpcEndpoint {
607 url: endpoint.to_string(),
608 priority: 0,
609 rate_limit_rps: 100,
610 timeout_ms: 30000,
611 healthy: true,
612 };
613
614 let connection_pool = Arc::new(ConnectionPool::new(vec![rpc_endpoint], 8));
615 let scanner = create_nft_scanner(connection_pool)?;
616
617 let scan_result = scanner.scan_wallet_nfts(wallet_address).await?;
618
619 Ok(scan_result)
620}
621
622/// Convenience function for thorough NFT scanning
623///
624/// This function provides thorough NFT scanning with maximum validation
625/// and comprehensive security analysis.
626///
627/// # Arguments
628///
629/// * `wallet_address` - The Solana wallet address to scan for NFTs
630/// * `rpc_endpoint` - Optional RPC endpoint (defaults to mainnet)
631///
632/// # Returns
633///
634/// Returns an `NftScanResult` containing thorough NFT analysis.
635///
636/// # Example
637///
638/// ```rust,no_run
639/// use solana_recover::scan_wallet_nfts_thorough;
640///
641/// #[tokio::main]
642/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
643/// let result = scan_wallet_nfts_thorough("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM", None).await?;
644/// println!("Thorough scan completed with {} insights",
645/// result.portfolio.as_ref().map(|p| p.quality_metrics.diversity_score).unwrap_or(0.0));
646/// Ok(())
647/// }
648/// ```
649#[cfg(feature = "nft")]
650pub async fn scan_wallet_nfts_thorough(
651 wallet_address: &str,
652 rpc_endpoint: Option<&str>,
653) -> core::Result<NftScanResult> {
654 use nft::scanner::create_thorough_nft_scanner;
655 use core::types::RpcEndpoint;
656 use rpc::ConnectionPool;
657 use std::sync::Arc;
658
659 let endpoint = rpc_endpoint.unwrap_or(DEFAULT_MAINNET_ENDPOINT);
660 let rpc_endpoint = RpcEndpoint {
661 url: endpoint.to_string(),
662 priority: 0,
663 rate_limit_rps: 50, // Lower rate limit for thorough scanning
664 timeout_ms: 60000, // Longer timeout for thorough scanning
665 healthy: true,
666 };
667
668 let connection_pool = Arc::new(ConnectionPool::new(vec![rpc_endpoint], 4));
669 let scanner = create_thorough_nft_scanner(connection_pool)?;
670
671 let scan_result = scanner.scan_wallet_nfts(wallet_address).await?;
672
673 Ok(scan_result)
674}
675
676/// Convenience function for batch NFT scanning
677///
678/// This function scans multiple wallets in parallel with optimized performance.
679///
680/// # Arguments
681///
682/// * `wallet_addresses` - Vector of Solana wallet addresses to scan
683/// * `rpc_endpoint` - Optional RPC endpoint (defaults to mainnet)
684///
685/// # Returns
686///
687/// Returns a vector of `NftScanResult` for each wallet.
688///
689/// # Example
690///
691/// ```rust,no_run
692/// use solana_recover::scan_wallets_nfts_batch;
693///
694/// #[tokio::main]
695/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
696/// let wallets = vec![
697/// "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM".to_string(),
698/// "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v".to_string(),
699/// ];
700/// let results = scan_wallets_nfts_batch(&wallets, None).await?;
701/// for result in results {
702/// println!("Wallet {}: {} NFTs", result.wallet_address, result.nfts.len());
703/// }
704/// Ok(())
705/// }
706/// ```
707#[cfg(feature = "nft")]
708pub async fn scan_wallets_nfts_batch(
709 wallet_addresses: &[String],
710 rpc_endpoint: Option<&str>,
711) -> core::Result<Vec<NftScanResult>> {
712 use nft::scanner::create_nft_scanner;
713 use core::types::RpcEndpoint;
714 use rpc::ConnectionPool;
715 use std::sync::Arc;
716
717 let endpoint = rpc_endpoint.unwrap_or(DEFAULT_MAINNET_ENDPOINT);
718 let rpc_endpoint = RpcEndpoint {
719 url: endpoint.to_string(),
720 priority: 0,
721 rate_limit_rps: 100,
722 timeout_ms: 30000,
723 healthy: true,
724 };
725
726 let connection_pool = Arc::new(ConnectionPool::new(vec![rpc_endpoint], 8));
727 let scanner = create_nft_scanner(connection_pool)?;
728
729 let scan_results = scanner.scan_wallets_batch(wallet_addresses).await?;
730
731 Ok(scan_results)
732}
733
734/// Convenience function for NFT metadata fetching only
735///
736/// This function fetches NFT metadata without valuation or security analysis.
737///
738/// # Arguments
739///
740/// * `mint_address` - The NFT mint address
741/// * `rpc_endpoint` - Optional RPC endpoint (defaults to mainnet)
742///
743/// # Returns
744///
745/// Returns an `NftInfo` containing the NFT metadata.
746///
747/// # Example
748///
749/// ```rust,no_run
750/// use solana_recover::fetch_nft_metadata;
751///
752/// #[tokio::main]
753/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
754/// let nft_info = fetch_nft_metadata("mint_address_here", None).await?;
755/// println!("NFT Name: {:?}", nft_info.name);
756/// println!("NFT Symbol: {:?}", nft_info.symbol);
757/// Ok(())
758/// }
759/// ```
760#[cfg(feature = "nft")]
761pub async fn fetch_nft_metadata(
762 mint_address: &str,
763 rpc_endpoint: Option<&str>,
764) -> core::Result<NftInfo> {
765 use nft::scanner::create_nft_scanner;
766 use nft::cache::CacheManager;
767 use nft::metadata::MetadataFetcher;
768 use nft::metadata::MetadataConfig;
769 use core::types::RpcEndpoint;
770 use rpc::ConnectionPool;
771 use std::sync::Arc;
772
773 let endpoint = rpc_endpoint.unwrap_or(DEFAULT_MAINNET_ENDPOINT);
774 let rpc_endpoint = RpcEndpoint {
775 url: endpoint.to_string(),
776 priority: 0,
777 rate_limit_rps: 100,
778 timeout_ms: 30000,
779 healthy: true,
780 };
781
782 let connection_pool = Arc::new(ConnectionPool::new(vec![rpc_endpoint], 8));
783 let cache_manager = Arc::new(CacheManager::new(Default::default()));
784 let metadata_config = MetadataConfig::default();
785
786 let metadata_fetcher = Arc::new(MetadataFetcher::new(
787 connection_pool,
788 metadata_config,
789 cache_manager,
790 )?);
791
792 let nft_info = metadata_fetcher.fetch_nft_metadata(mint_address).await?;
793
794 Ok(nft_info)
795}
796
797#[cfg(test)]
798mod tests {
799 use super::*;
800
801 #[test]
802 fn test_version() {
803 assert!(!VERSION.is_empty());
804 }
805
806 #[test]
807 fn test_default_endpoints() {
808 assert_eq!(DEFAULT_MAINNET_ENDPOINT, "https://api.mainnet-beta.solana.com");
809 assert_eq!(DEFAULT_DEVNET_ENDPOINT, "https://api.devnet.solana.com");
810 }
811}