Solana Recover

A high-performance Solana wallet scanner and SOL recovery library for Rust. This crate provides a simple yet powerful API for scanning Solana wallets to find empty token accounts and recover SOL from them.
Features
- Simple API: Easy-to-use functions for quick wallet scanning
- High Performance: Optimized for scanning multiple wallets concurrently
- Feature-based: Only compile what you need with feature flags
- Type Safe: Full Rust type safety with comprehensive error handling
- Async First: Built on tokio for efficient asynchronous operations
- Extensible: Modular design allows for custom implementations
Installation
Add this to your Cargo.toml:
[dependencies]
solana-recover = "1.0.0"
Feature Flags
Use only the features you need to keep your binary small:
solana-recover = "1.0.0"
solana-recover = { version = "1.0.0", default-features = false }
solana-recover = { version = "1.0.0", default-features = false, features = ["scanner"] }
solana-recover = { version = "1.0.0", features = ["full"] }
Available features:
scanner - Core wallet scanning functionality
recovery - SOL recovery operations
nft - NFT scanning and analysis (optional)
api - REST API server functionality
database - Database persistence support
cache - Advanced caching capabilities
metrics - Prometheus metrics collection
security - Enhanced security features
config - Configuration file support
full - Enables all features including NFT
Quick Start
Basic Wallet Scan
use solana_recover::scan_wallet;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let result = scan_wallet("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM", None).await?;
println!("Wallet: {}", result.wallet_address);
println!("Total accounts: {}", result.total_accounts);
println!("Empty accounts: {}", result.empty_accounts.len());
println!("Recoverable SOL: {}", result.recoverable_sol);
println!("Scan time: {}ms", result.scan_time_ms);
Ok(())
}
Advanced Usage with Scanner
use solana_recover::{WalletScanner, ScanConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ScanConfig {
rpc_endpoint: "https://api.mainnet-beta.solana.com".to_string(),
max_concurrent: 20,
timeout_seconds: 60,
enable_cache: true,
};
let scanner = WalletScanner::with_config(config).await?;
let wallets = vec![
"9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
];
for wallet in wallets {
let result = scanner.scan_wallet(wallet).await?;
println!("{} has {} recoverable SOL", wallet, result.recoverable_sol);
}
Ok(())
}
Batch Processing
use solana_recover::{BatchProcessor, BatchScanRequest};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let processor = BatchProcessor::new().await?;
let request = BatchScanRequest {
wallet_addresses: vec![
"9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM".to_string(),
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v".to_string(),
],
fee_percentage: Some(0.15),
};
let results = processor.process_batch(request).await?;
for result in results.results {
println!("Wallet {}: {} SOL recoverable",
result.wallet_address,
result.recoverable_sol);
}
Ok(())
}
Configuration
Environment Variables
export SOLANA_RPC_ENDPOINT="https://api.mainnet-beta.solana.com"
export SOLANA_TIMEOUT_SECONDS=30
export SOLANA_MAX_CONCURRENT=10
export SOLANA_ENABLE_CACHE=true
Configuration File (with config feature)
[scanner]
rpc_endpoint = "https://api.mainnet-beta.solana.com"
max_concurrent = 20
timeout_seconds = 60
enable_cache = true
[cache]
ttl_seconds = 300
max_size = 1000
[fees]
default_percentage = 0.15
minimum_lamports = 1000000
Examples
Command Line Interface
The CLI tool provides both simple and advanced commands for wallet scanning and SOL recovery:
Simple Usage (Recommended)
Quick scan:
solana-recover --wallet <ADDRESS>
Scan and reclaim in one command:
solana-recover --wallet <ADDRESS> --destination <DESTINATION>
Developer mode (show detailed information):
solana-recover --wallet <ADDRESS> --dev
solana-recover --wallet <ADDRESS> -D
Advanced Usage
Show total claimable SOL:
solana-recover show --targets "wallet:addr1,addr2,addr3"
solana-recover show --targets "key:privkey1,privkey2"
solana-recover show --targets "wallet:addr1,addr2,addr3" --dev
Reclaim SOL:
solana-recover reclaim --targets "wallet:addr1,addr2" --destination "destination_wallet_address"
solana-recover reclaim --targets "key:privkey1,privkey2" --destination "dest_wallet_address"
solana-recover reclaim --targets "wallet:addr1,addr2" --destination "dest_wallet_address" --dev
Batch processing:
solana-recover batch wallets.txt
solana-recover batch wallets.txt --dev
NFT scanning (with nft feature):
solana-recover nft <ADDRESS>
solana-recover nft <ADDRESS> --detailed --security
solana-recover nft-batch wallets.txt
Examples
solana-recover --wallet B7bQUSYnD56Vk7jEAqU4MWLJQ9LgVnKyWskivPhZQcHg
solana-recover --wallet B7bQUSYnD56Vk7jEAqU4MWLJQ9LgVnKyWskivPhZQcHg --destination 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM
solana-recover show --targets "wallet:B7bQUSYnD56Vk7jEAqU4MWLJQ9LgVnKyWskivPhZQcHg,9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"
solana-recover --wallet <ADDRESS> --destination <DEST> --force
solana-recover --wallet <ADDRESS> --dev
solana-recover --wallet <ADDRESS> -D
solana-recover nft B7bQUSYnD56Vk7jEAqU4MWLJQ9LgVnKyWskivPhZQcHg
solana-recover nft B7bQUSYnD56Vk7jEAqU4MWLJQ9LgVnKyWskivPhZQcHg --detailed --security
Simple CLI Tool
use solana_recover::scan_wallet;
use std::env;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
eprintln!("Usage: {} <wallet_address>", args[0]);
std::process::exit(1);
}
let wallet_address = &args[1];
let result = scan_wallet(wallet_address, None).await?;
println!("Scan Results:");
println!(" Wallet: {}", result.wallet_address);
println!(" Total Accounts: {}", result.total_accounts);
println!(" Empty Accounts: {}", result.empty_accounts.len());
println!(" Recoverable SOL: {:.9}", result.recoverable_sol);
println!(" Scan Time: {}ms", result.scan_time_ms);
Ok(())
}
Run with:
cargo run --example simple_scan -- 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM
SOL Recovery Example
use solana_recover::{recover_sol, RecoveryRequest};
use uuid::Uuid;
use chrono::Utc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let request = RecoveryRequest {
id: Uuid::new_v4(),
wallet_address: "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM".to_string(),
empty_accounts: vec!["empty_account_address".to_string()],
destination_address: "destination_address".to_string(),
wallet_connection_id: Some("wallet_connection".to_string()),
max_fee_lamports: Some(5_000_000),
priority_fee_lamports: None,
user_id: None,
created_at: Utc::now(),
};
let result = recover_sol(&request, None).await?;
println!("Recovered {} SOL", result.net_sol);
Ok(())
}
NFT Scanning (with nft feature)
use solana_recover::{scan_wallet_nfts, ScanMode};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let result = scan_wallet_nfts("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM", None).await?;
println!("Found {} NFTs", result.nfts.len());
println!("Total estimated value: {:.9} SOL",
result.total_estimated_value_lamports as f64 / 1_000_000_000.0);
for nft in result.nfts {
println!("NFT: {} ({})", nft.name, nft.mint_address);
}
Ok(())
}
Web Server (with api feature)
use axum::{routing::post, Json, Router};
use serde_json::{json, Value};
use solana_recover::scan_wallet;
use std::net::SocketAddr;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let app = Router::new()
.route("/scan", post(scan_wallet_handler));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
println!("Server running on http://{}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await?;
Ok(())
}
async fn scan_wallet_handler(Json(payload): Json<Value>) -> Result<Json<Value>, String> {
let wallet_address = payload["wallet_address"]
.as_str()
.ok_or("Missing wallet_address")?;
let rpc_endpoint = payload["rpc_endpoint"]
.as_str();
match scan_wallet(wallet_address, rpc_endpoint).await {
Ok(result) => Ok(Json(json!({
"success": true,
"data": result
}))),
Err(e) => Ok(Json(json!({
"success": false,
"error": e.to_string()
}))),
}
}
Error Handling
The library provides comprehensive error handling with the SolanaRecoverError enum:
use solana_recover::{scan_wallet, SolanaRecoverError};
#[tokio::main]
async fn main() {
match scan_wallet("invalid_address", None).await {
Ok(result) => println!("Success: {:?}", result),
Err(SolanaRecoverError::InvalidAddress(addr)) => {
eprintln!("Invalid wallet address: {}", addr);
}
Err(SolanaRecoverError::NetworkError(e)) => {
eprintln!("Network error: {}", e);
}
Err(e) => {
eprintln!("Other error: {}", e);
}
}
}
Testing
Run the test suite:
cargo test
cargo test --features "full"
cargo test --features "nft"
cargo test --test integration
Documentation
Contributing
Contributions are welcome! Please read our Contributing Guide for details.
Development Setup
git clone https://github.com/Genius740Code/Sol-account-cleaner.git
cd Sol-account-cleaner
cargo build
cargo test
License
This project is licensed under the MIT License - see the LICENSE file for details.
Built for the Solana ecosystem