Upload 16 files
Browse files- check/.gitignore +112 -0
- check/.local/state/replit/agent/.agent_state_3f44dea3ced50eea70ed33f619266e6106de2f11.bin +3 -0
- check/.local/state/replit/agent/.agent_state_463c51dcb980c6a1c74871fdded99cee48599e9f.bin +3 -0
- check/.local/state/replit/agent/.agent_state_4a9c459c1a931955b949bf88f4b0e0baa9962553.bin +3 -0
- check/.local/state/replit/agent/.agent_state_64e054bde1467c40837361c152fc18d8c61c7222.bin +3 -0
- check/.local/state/replit/agent/.agent_state_8e004c6c49565a75b7faeecd2d322a680a4f0544.bin +3 -0
- check/.local/state/replit/agent/.agent_state_a142ae0b26554e72877f8744d1805d558482e490.bin +3 -0
- check/.local/state/replit/agent/.agent_state_main.bin +3 -0
- check/.local/state/replit/agent/.latest.json +1 -0
- check/.local/state/replit/agent/filesystem/filesystem_state.json +1 -0
- check/.local/state/replit/agent/repl_state.bin +3 -0
- check/.replit +38 -0
- check/README.md +251 -0
- check/app.py +915 -0
- check/replit.md +60 -0
- check/requirements.txt +15 -0
check/.gitignore
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Byte-compiled / optimized / DLL files
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
|
| 6 |
+
# C extensions
|
| 7 |
+
*.so
|
| 8 |
+
|
| 9 |
+
# Distribution / packaging
|
| 10 |
+
.Python
|
| 11 |
+
build/
|
| 12 |
+
develop-eggs/
|
| 13 |
+
dist/
|
| 14 |
+
downloads/
|
| 15 |
+
eggs/
|
| 16 |
+
.eggs/
|
| 17 |
+
lib/
|
| 18 |
+
lib64/
|
| 19 |
+
parts/
|
| 20 |
+
sdist/
|
| 21 |
+
var/
|
| 22 |
+
wheels/
|
| 23 |
+
*.egg-info/
|
| 24 |
+
.installed.cfg
|
| 25 |
+
*.egg
|
| 26 |
+
MANIFEST
|
| 27 |
+
|
| 28 |
+
# PyInstaller
|
| 29 |
+
*.manifest
|
| 30 |
+
*.spec
|
| 31 |
+
|
| 32 |
+
# Installer logs
|
| 33 |
+
pip-log.txt
|
| 34 |
+
pip-delete-this-directory.txt
|
| 35 |
+
|
| 36 |
+
# Unit test / coverage reports
|
| 37 |
+
htmlcov/
|
| 38 |
+
.tox/
|
| 39 |
+
.nox/
|
| 40 |
+
.coverage
|
| 41 |
+
.coverage.*
|
| 42 |
+
.cache
|
| 43 |
+
nosetests.xml
|
| 44 |
+
coverage.xml
|
| 45 |
+
*.cover
|
| 46 |
+
.hypothesis/
|
| 47 |
+
.pytest_cache/
|
| 48 |
+
|
| 49 |
+
# Translations
|
| 50 |
+
*.mo
|
| 51 |
+
*.pot
|
| 52 |
+
|
| 53 |
+
# Django stuff:
|
| 54 |
+
*.log
|
| 55 |
+
local_settings.py
|
| 56 |
+
db.sqlite3
|
| 57 |
+
|
| 58 |
+
# Flask stuff:
|
| 59 |
+
instance/
|
| 60 |
+
.webassets-cache
|
| 61 |
+
|
| 62 |
+
# Scrapy stuff:
|
| 63 |
+
.scrapy
|
| 64 |
+
|
| 65 |
+
# Sphinx documentation
|
| 66 |
+
docs/_build/
|
| 67 |
+
|
| 68 |
+
# PyBuilder
|
| 69 |
+
target/
|
| 70 |
+
|
| 71 |
+
# Jupyter Notebook
|
| 72 |
+
.ipynb_checkpoints
|
| 73 |
+
|
| 74 |
+
# IPython
|
| 75 |
+
profile_default/
|
| 76 |
+
ipython_config.py
|
| 77 |
+
|
| 78 |
+
# pyenv
|
| 79 |
+
.python-version
|
| 80 |
+
|
| 81 |
+
# celery beat schedule file
|
| 82 |
+
celerybeat-schedule
|
| 83 |
+
|
| 84 |
+
# SageMath parsed files
|
| 85 |
+
*.sage.py
|
| 86 |
+
|
| 87 |
+
# Environments
|
| 88 |
+
.env
|
| 89 |
+
.venv
|
| 90 |
+
env/
|
| 91 |
+
venv/
|
| 92 |
+
ENV/
|
| 93 |
+
env.bak/
|
| 94 |
+
venv.bak/
|
| 95 |
+
|
| 96 |
+
# Spyder project settings
|
| 97 |
+
.spyderproject
|
| 98 |
+
.spyproject
|
| 99 |
+
|
| 100 |
+
# Rope project settings
|
| 101 |
+
.ropeproject
|
| 102 |
+
|
| 103 |
+
# mkdocs documentation
|
| 104 |
+
/site
|
| 105 |
+
|
| 106 |
+
# mypy
|
| 107 |
+
.mypy_cache/
|
| 108 |
+
.dmypy.json
|
| 109 |
+
dmypy.json
|
| 110 |
+
|
| 111 |
+
# Pyre type checker
|
| 112 |
+
.pyre/
|
check/.local/state/replit/agent/.agent_state_3f44dea3ced50eea70ed33f619266e6106de2f11.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:348dc2a73d8391d49ef77ec716b2f92e2e9024fa39cc984cebac680c8ce7763c
|
| 3 |
+
size 70772
|
check/.local/state/replit/agent/.agent_state_463c51dcb980c6a1c74871fdded99cee48599e9f.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5e385906b8811e52a23cb2e70c96e9dc0f3e17fdae5693a8117a2cebbaed9610
|
| 3 |
+
size 15354
|
check/.local/state/replit/agent/.agent_state_4a9c459c1a931955b949bf88f4b0e0baa9962553.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1117e7acff36469b266910d8ddc0c9706f8199b05c142914aa83266beed87f74
|
| 3 |
+
size 61030
|
check/.local/state/replit/agent/.agent_state_64e054bde1467c40837361c152fc18d8c61c7222.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e0043f982a8790ee22f682f758e4c3ae332c9e9ef5e4224a332d8a74d427c784
|
| 3 |
+
size 43283
|
check/.local/state/replit/agent/.agent_state_8e004c6c49565a75b7faeecd2d322a680a4f0544.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1224d68482e0e6252197eadc9bfea4a199ec50bb010a27df78001d58a5c0aad3
|
| 3 |
+
size 53225
|
check/.local/state/replit/agent/.agent_state_a142ae0b26554e72877f8744d1805d558482e490.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c42c1cc3765aeaf52cfe3c4e2dda77a0d55dd1bc552a8fa1a2be89783ea81633
|
| 3 |
+
size 15324
|
check/.local/state/replit/agent/.agent_state_main.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9eef0a119374324445ed18e6bc52dad353071ac2e61cacaa8eb774ed8595c8b3
|
| 3 |
+
size 70743
|
check/.local/state/replit/agent/.latest.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"latest": "main"}
|
check/.local/state/replit/agent/filesystem/filesystem_state.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"file_contents":{"README.md":{"content":"title: Solana Key-Miner\nemoji: πͺ\ncolorFrom: green\ncolorTo: gray\nsdk: gradio\nsdk_version: 4.44.0\napp_file: app.py\npinned: false\nπ Solana Key Generator & Live Miner\nGenerate Ed25519 Base-58 keys and mine Solana addresses in real time with Discord notifications.\n\nFeatures\nπ 30-minute mining cycles (30 min active, 30 min break)\nNon-stop background miner (100 wallets per RPC call)\nLive Discord status embed (updates every 60 seconds)\n@everyone ping when a funded wallet is found\nReal-time feed inside the interface\nBulk key generation (up to 1000 keys)\nMultiple RPC endpoint fallback for reliability\nπ Multi-platform compatibility (Hugging Face Spaces & Replit)\nπ Complete Setup Guide for ALL Platforms\nπ€ Hugging Face Spaces (Recommended - FREE)\nStep 1: Create Your Space\nGo to huggingface.co/new-space\nChoose a name for your space (e.g., my-solana-miner)\nSelect Gradio as the SDK\nSet visibility to Public or Private (your choice)\nClick Create Space\nStep 2: Upload Files\nClick Files tab in your new space\nUpload these files from this repository:\napp.py (main application)\nrequirements.txt (dependencies)\nREADME.md (this file)\nStep 3: Set Up Discord (Optional but Recommended)\nGo to your Discord server\nRight-click on the channel where you want notifications\nSelect Edit Channel β Integrations β Webhooks\nClick New Webhook\nCopy the webhook URL\nIn your Hugging Face Space, go to Settings tab\nScroll to Repository secrets\nAdd new secret: DISCORD_WEBHOOK = your_webhook_url_here\nStep 4: Configure Settings (Optional)\nAdd these optional secrets in Hugging Face Settings:\n\nMIN_SOL=0.00001 (minimum SOL to consider \"funded\")\nRPC_BATCH_SIZE=100 (wallets per batch, max 100)\nMINER_SLEEP=1.0 (seconds between mining cycles)\nStep 5: Launch\nYour space will automatically build and deploy\nWait 2-3 minutes for initialization\nClick the app URL to access your miner\nHit Start Mining and watch it work!\nπ» Local Development Setup\nRequirements\nPython 3.8 or higher\nGit\nStep 1: Clone Repository\ngit clone <your-repo-url>\ncd solana-key-miner\nStep 2: Create Virtual Environment\n# Windows\npython -m venv venv\nvenv\\Scripts\\activate\n\n# macOS/Linux\npython3 -m venv venv\nsource venv/bin/activate\nStep 3: Install Dependencies\npip install -r requirements.txt\nStep 4: Set Environment Variables\nCreate a .env file in the project root:\n\n# Required for Discord notifications (optional)\nDISCORD_WEBHOOK=your_discord_webhook_url_here\n\n# Optional settings\nMIN_SOL=0.00001\nRPC_BATCH_SIZE=100\nMINER_SLEEP=1.0\nStep 5: Run the Application\npython app.py\nThe app will be available at http://localhost:7860\n\nπ³ Docker Setup\nStep 1: Create Dockerfile\nCreate Dockerfile in your project root:\n\nFROM python:3.9-slim\n\nWORKDIR /app\nCOPY requirements.txt .\nRUN pip install -r requirements.txt\n\nCOPY . .\n\nEXPOSE 7860\nCMD [\"python\", \"app.py\"]\nStep 2: Build and Run\n# Build the image\ndocker build -t solana-miner .\n\n# Run with environment variables\ndocker run -d \\\n -p 7860:7860 \\\n -e DISCORD_WEBHOOK=your_webhook_url \\\n -e MIN_SOL=0.00001 \\\n --name solana-miner \\\n solana-miner\nβοΈ Cloud Deployment Options\nGoogle Cloud Run\nInstall Google Cloud CLI\nBuild and deploy:\ngcloud run deploy solana-miner \\\n --source . \\\n --platform managed \\\n --region us-central1 \\\n --allow-unauthenticated \\\n --set-env-vars DISCORD_WEBHOOK=your_webhook_url\nAWS EC2\nLaunch Ubuntu 20.04 instance\nSSH into instance\nInstall Python and dependencies:\nsudo apt update\nsudo apt install python3 python3-pip git -y\ngit clone <your-repo>\ncd solana-key-miner\npip3 install -r requirements.txt\nSet environment variables and run\nDigitalOcean Droplet\nCreate Ubuntu droplet\nFollow same steps as AWS EC2\nOptional: Set up nginx reverse proxy\nπ§ Configuration Guide\nEnvironment Variables\nVariable Default Description\nDISCORD_WEBHOOK None Discord webhook URL for notifications\nMIN_SOL 0.00001 Minimum SOL to consider wallet \"funded\"\nRPC_BATCH_SIZE 100 Number of wallets to check per RPC call\nMINER_SLEEP 1.0 Seconds to sleep between mining cycles\nPORT 7860 Port for the web interface\nDiscord Setup Details\nServer Permissions: Ensure the bot/webhook has permission to send messages\nChannel Selection: Use a dedicated channel for miner notifications\nWebhook Security: Keep your webhook URL private\nRate Limits: The app automatically manages Discord rate limits\nRPC Endpoints\nThe app uses multiple free Solana RPC endpoints with automatic failover:\n\napi.mainnet-beta.solana.com\nsolana-api.projectserum.com\nrpc.ankr.com/solana\nsolana.public-rpc.com\nπ‘οΈ Security Considerations\nImportant Notes\nThis tool is for educational purposes\nFound private keys are displayed in the interface\nBe cautious when using Discord notifications\nNever share your webhook URLs\nKeep your environment variables secure\nBest Practices\nUse private Hugging Face Spaces for sensitive operations\nRegularly rotate Discord webhooks\nMonitor your RPC usage\nKeep dependencies updated\nπ± Mobile Access\nAccess via Mobile Browser\nDeploy to any cloud platform above\nAccess via mobile browser\nAdd to home screen for app-like experience\nResponsive Design\nThe Gradio interface is mobile-friendly and works on:\n\niPhone (Safari, Chrome)\nAndroid (Chrome, Firefox)\nTablets and iPads\nπ Troubleshooting\nCommon Issues\n\"All RPC endpoints failed\"\n\nCheck internet connection\nRPC endpoints may be rate-limiting\nWait a few minutes and try again\nDiscord notifications not working\n\nVerify webhook URL is correct\nCheck channel permissions\nEnsure webhook hasn't been deleted\nApp won't start on Hugging Face\n\nCheck build logs in the space\nVerify all files are uploaded correctly\nEnsure requirements.txt is valid\nHigh memory usage\n\nReduce RPC_BATCH_SIZE to 50 or lower\nRestart the application periodically\nGetting Help\nCheck the build logs first\nVerify all environment variables\nTest with minimal configuration\nCheck Discord webhook with a simple test message\nπ Performance Optimization\nFor High-Volume Mining\nSet RPC_BATCH_SIZE=100 (maximum)\nUse multiple RPC endpoints\nMonitor rate limits\nConsider using paid RPC services for better performance\nFor Low-Resource Environments\nSet RPC_BATCH_SIZE=25\nIncrease MINER_SLEEP=2.0\nReduce FEED_MAX_ITEMS=25\nπ― Quick Start Summary\nFastest way to get running:\n\nFork this repository to your GitHub\nCreate Hugging Face Space with Gradio SDK\nUpload files: app.py, requirements.txt, README.md\nAdd Discord webhook (optional) in Space settings\nWait for build (2-3 minutes)\nStart mining and enjoy!\nTotal setup time: 5-10 minutes β‘\n\nCheck the Hugging Face Spaces documentation for more advanced configuration options.\n","size_bytes":6578},"app.py":{"content":"\"\"\"\nHugging Face Space β Solana Key Generator & Miner\nSimple, reliable implementation optimized for cloud deployment\n\"\"\"\nimport os\nimport queue\nimport threading\nimport time\nimport base58\nimport json\nimport requests\nimport collections\nimport hashlib\nimport secrets\nfrom typing import List, Dict, Optional, Tuple\nfrom datetime import datetime\n\nimport gradio as gr\nfrom solana.rpc.api import Client\nfrom solana.rpc.core import RPCException\nfrom solders.keypair import Keypair\nfrom solders.pubkey import Pubkey\n\n# ---------- Safe keypair generator ----------\ndef _safe_keypair() -> Keypair:\n \"\"\"Keep drawing 64-byte seeds until Edwards point is valid.\"\"\"\n while True:\n try:\n return Keypair.from_bytes(os.urandom(64))\n except ValueError:\n continue\n\n# ---------- Enhanced Console Logger ----------\nimport logging\nlogging.basicConfig(level=logging.INFO, format=\"%(asctime)s %(message)s\")\nlog = logging.getLogger(\"solana_miner\")\n\n# ---------- Enhanced Configuration with Multi-Miner Support ----------\nclass Config:\n DISCORD_WEBHOOK = os.getenv(\"DISCORD_WEBHOOK\", \"\")\n MINER_ID = os.getenv(\"MINER_ID\", f\"Miner-{secrets.token_hex(4)}\")\n MINER_NAME = os.getenv(\"MINER_NAME\", f\"SolMiner-{secrets.token_hex(3)}\")\n LOCATION = os.getenv(\"MINER_LOCATION\", \"Unknown\")\n\n RPC_URLS = [\n \"https://api.mainnet-beta.solana.com\",\n \"https://solana-api.projectserum.com\", \n \"https://rpc.ankr.com/solana\",\n \"https://solana.public-rpc.com\"\n ]\n current_rpc_index = 0\n MIN_SOL = float(os.getenv(\"MIN_SOL\", \"0.00001\"))\n RPC_BATCH_SIZE = int(os.getenv(\"RPC_BATCH_SIZE\", \"100\"))\n MINER_SLEEP = float(os.getenv(\"MINER_SLEEP\", \"1.0\"))\n MAX_RETRIES = 2\n FEED_MAX_ITEMS = 50\n\n # Discord isolation settings\n DISCORD_RETRY_LIMIT = 3\n DISCORD_TIMEOUT = 5\n DISCORD_COOLDOWN = 300 # 5 minutes between reconnect attempts\n\n @classmethod\n def get_current_rpc(cls):\n return cls.RPC_URLS[cls.current_rpc_index]\n\n @classmethod\n def rotate_rpc(cls):\n cls.current_rpc_index = (cls.current_rpc_index + 1) % len(cls.RPC_URLS)\n log.info(f\"Switched to RPC: {cls.get_current_rpc()}\")\n\nconfig = Config()\n\n# ---------- Advanced Discord Management ----------\nSTATUS_MESSAGE_ID = None\nSTATUS_EDIT_URL = None\nDISCORD_DISABLED = False\nDISCORD_RETRY_COUNT = 0\nDISCORD_LAST_ERROR = None\nDISCORD_LAST_SUCCESS = time.time()\nMINER_START_TIME = datetime.now()\nDISCORD_QUEUE = queue.Queue(maxsize=1000) # Isolated queue for Discord operations\n\n# ---------- Enhanced Multi-Miner State Management ----------\nclass MinerState:\n def __init__(self):\n self.total_mined = 0\n self.funded_found = 0\n self.errors_count = 0\n self.last_funded_time = None\n self.run_flag = threading.Event()\n self.upload_queue = queue.Queue()\n self.lock = threading.Lock()\n\n # Enhanced tracking\n self.start_time = datetime.now()\n self.last_batch_time = None\n self.current_rpc = None\n self.rpc_switches = 0\n self.total_batches = 0\n self.average_batch_time = 0.0\n self.best_streak = 0\n self.current_streak = 0\n self.wallets_per_second = 0.0\n self.connection_status = \"Initializing\"\n self.last_activity = time.time()\n\n # Performance metrics\n self.batch_times = collections.deque(maxlen=100) # Last 100 batch times\n self.rpc_performance = {} # Track RPC response times\n \n # 30-minute cycle tracking\n self.cycle_start_time = time.time()\n self.cycle_active = True\n self.cycle_break_start = None\n self.total_cycles = 0\n self.in_break_mode = False\n\n def increment_mined(self):\n with self.lock: \n self.total_mined += 1\n\n def increment_errors(self):\n with self.lock: \n self.errors_count += 1\n\n def update_batch_stats(self, batch_size: int, batch_time: float, rpc_url: str):\n with self.lock:\n self.last_batch_time = datetime.now()\n self.total_batches += 1\n self.batch_times.append(batch_time)\n self.current_rpc = rpc_url\n self.last_activity = time.time()\n\n # Calculate averages\n if self.batch_times:\n self.average_batch_time = sum(self.batch_times) / len(self.batch_times)\n total_time = (datetime.now() - self.start_time).total_seconds()\n if total_time > 0:\n self.wallets_per_second = self.total_mined / total_time\n\n # Track RPC performance\n if rpc_url not in self.rpc_performance:\n self.rpc_performance[rpc_url] = []\n self.rpc_performance[rpc_url].append(batch_time)\n if len(self.rpc_performance[rpc_url]) > 50:\n self.rpc_performance[rpc_url].pop(0)\n\n def increment_funded(self):\n with self.lock:\n self.funded_found += 1\n self.last_funded_time = datetime.now()\n self.current_streak += 1\n if self.current_streak > self.best_streak:\n self.best_streak = self.current_streak\n\n def reset_streak(self):\n with self.lock:\n self.current_streak = 0\n\n def get_comprehensive_stats(self) -> Dict:\n with self.lock:\n uptime = datetime.now() - self.start_time\n return {\n # Basic stats\n 'total_mined': self.total_mined,\n 'funded_found': self.funded_found,\n 'errors_count': self.errors_count,\n 'last_funded': self.last_funded_time,\n 'queue_size': self.upload_queue.qsize(),\n 'is_running': self.run_flag.is_set(),\n\n # Enhanced stats\n 'miner_id': config.MINER_ID,\n 'miner_name': config.MINER_NAME,\n 'location': config.LOCATION,\n 'start_time': self.start_time,\n 'uptime_seconds': uptime.total_seconds(),\n 'uptime_str': str(uptime).split('.')[0],\n 'current_rpc': self.current_rpc or \"None\",\n 'rpc_switches': self.rpc_switches,\n 'total_batches': self.total_batches,\n 'average_batch_time': round(self.average_batch_time, 2),\n 'wallets_per_second': round(self.wallets_per_second, 2),\n 'best_streak': self.best_streak,\n 'current_streak': self.current_streak,\n 'connection_status': self.connection_status,\n 'last_activity': self.last_activity,\n\n # 30-minute cycle info\n 'cycle_active': self.cycle_active,\n 'in_break_mode': self.in_break_mode,\n 'total_cycles': self.total_cycles,\n 'cycle_time_remaining': self.get_cycle_time_remaining(),\n\n # Performance metrics\n 'batch_times_count': len(self.batch_times),\n 'rpc_performance': {rpc: round(sum(times)/len(times), 2) \n for rpc, times in self.rpc_performance.items() if times}\n }\n\n def get_cycle_time_remaining(self) -> Dict:\n \"\"\"Calculate remaining time in current cycle (30 min active / 30 min break)\"\"\"\n current_time = time.time()\n cycle_duration = 30 * 60 # 30 minutes in seconds\n \n if self.in_break_mode:\n # In break mode\n if self.cycle_break_start:\n elapsed_break = current_time - self.cycle_break_start\n remaining_break = max(0, cycle_duration - elapsed_break)\n return {\n 'mode': 'break',\n 'remaining_seconds': int(remaining_break),\n 'remaining_minutes': int(remaining_break / 60),\n 'progress_percent': min(100, (elapsed_break / cycle_duration) * 100)\n }\n else:\n # In mining mode\n elapsed_mining = current_time - self.cycle_start_time\n remaining_mining = max(0, cycle_duration - elapsed_mining)\n return {\n 'mode': 'mining',\n 'remaining_seconds': int(remaining_mining),\n 'remaining_minutes': int(remaining_mining / 60),\n 'progress_percent': min(100, (elapsed_mining / cycle_duration) * 100)\n }\n \n return {'mode': 'unknown', 'remaining_seconds': 0, 'remaining_minutes': 0, 'progress_percent': 0}\n\n def check_cycle_status(self) -> bool:\n \"\"\"Check if we should switch between mining and break mode. Returns True if should continue mining.\"\"\"\n current_time = time.time()\n cycle_duration = 30 * 60 # 30 minutes\n \n if self.in_break_mode:\n # Currently in break, check if break time is over\n if self.cycle_break_start and (current_time - self.cycle_break_start) >= cycle_duration:\n # Break is over, switch to mining\n self.in_break_mode = False\n self.cycle_active = True\n self.cycle_start_time = current_time\n self.cycle_break_start = None\n log.info(\"π’ 30-minute break completed, resuming mining...\")\n return True\n return False # Still in break\n else:\n # Currently mining, check if mining time is over\n if (current_time - self.cycle_start_time) >= cycle_duration:\n # Mining period is over, switch to break\n self.in_break_mode = True\n self.cycle_active = False\n self.cycle_break_start = current_time\n self.total_cycles += 1\n log.info(f\"π΄ 30-minute mining cycle #{self.total_cycles} completed, starting 30-minute break...\")\n return False\n return True # Continue mining\n\n def get_stats(self) -> Dict:\n # Maintain backward compatibility\n return self.get_comprehensive_stats()\n\nminer_state = MinerState()\n\n# ---------- Live Feed ----------\nclass LiveFeed:\n def __init__(self, max_items: int = 100):\n self.feed = collections.deque(maxlen=max_items)\n self.lock = threading.Lock()\n\n def add_entry(self, secret: str, sol: float, address: str = \"\"):\n with self.lock:\n timestamp = datetime.now().strftime('%H:%M:%S')\n if sol >= config.MIN_SOL:\n entry = f\"π’ **FUNDED** {sol:.6f} SOL | `{secret[:20]}...` | {address[:20]}... | {timestamp}\"\n log.info(f\"π° FUNDED WALLET: {sol:.6f} SOL\")\n else:\n entry = f\"π΄ Empty | `{secret[:20]}...` | {timestamp}\"\n self.feed.append(entry)\n\n def get_feed(self) -> str:\n with self.lock: \n return \"\\n\".join(self.feed)\n\n def clear_feed(self):\n with self.lock: \n self.feed.clear()\n\nlive_feed = LiveFeed(config.FEED_MAX_ITEMS)\n\n# ---------- Key Generation ----------\nBASE58_ALPHABET = \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\"\n\ndef generate_secure_keypair() -> Tuple[str, str, Keypair]:\n \"\"\"Generate a secure keypair with Base58 encoding\"\"\"\n for attempt in range(1000):\n try:\n keypair = _safe_keypair()\n seed = bytes(keypair)[:32]\n private_key_b58 = base58.b58encode(seed).decode('utf-8')\n public_key = str(keypair.pubkey())\n\n if len(private_key_b58) >= 32 and all(c in BASE58_ALPHABET for c in private_key_b58):\n return private_key_b58, public_key, keypair\n except Exception as e:\n log.warning(f\"Keypair attempt {attempt + 1} failed: {e}\")\n continue\n raise Exception(\"Failed to generate valid keypair after 1000 attempts\")\n\ndef get_key_info(private_key: str) -> Dict:\n try:\n decoded = base58.b58decode(private_key)\n keypair = _safe_keypair()\n return {\n \"private_key\": private_key,\n \"public_key\": str(keypair.pubkey()),\n \"key_length\": len(private_key),\n \"is_valid\": True,\n \"private_key_hex\": decoded.hex(),\n \"public_key_hex\": bytes(keypair.pubkey()).hex(),\n }\n except Exception as e:\n return {\"private_key\": private_key, \"error\": str(e), \"is_valid\": False}\n\n# ---------- Simple Solana Client ----------\ndef create_solana_client() -> Client:\n \"\"\"Create client with simple RPC rotation\"\"\"\n for attempt in range(len(config.RPC_URLS)):\n rpc_url = config.get_current_rpc()\n try:\n client = Client(rpc_url.strip())\n client.get_slot() # Test connection\n log.info(f\"β
Connected to Solana RPC: {rpc_url}\")\n return client\n except Exception as e:\n log.warning(f\"β RPC {rpc_url} failed: {e}\")\n config.rotate_rpc()\n time.sleep(2)\n\n raise Exception(\"All RPC endpoints failed\")\n\ndef get_balances_with_retry(client: Client, addresses: List[str]) -> List[float]:\n \"\"\"Simple balance checking with retry logic\"\"\"\n chunk_size = min(50, len(addresses))\n\n for attempt in range(config.MAX_RETRIES):\n try:\n all_balances = []\n\n for i in range(0, len(addresses), chunk_size):\n chunk = addresses[i:i + chunk_size]\n pubkeys = [Pubkey.from_string(addr) for addr in chunk]\n\n time.sleep(0.5) # Rate limiting\n resp = client.get_multiple_accounts(pubkeys)\n\n chunk_balances = []\n for acc in resp.value:\n chunk_balances.append((acc.lamports / 1e9) if acc else 0.0)\n all_balances.extend(chunk_balances)\n\n # Small delay between chunks\n if i + chunk_size < len(addresses):\n time.sleep(0.2)\n\n return all_balances\n\n except Exception as e:\n log.warning(f\"Balance check attempt {attempt + 1} failed: {e}\")\n miner_state.increment_errors()\n config.rotate_rpc()\n time.sleep(2)\n\n return [0.0] * len(addresses)\n\n# ---------- Isolated Discord Operations ----------\ndef queue_discord_message(message_type: str, data: dict):\n \"\"\"Queue Discord message for isolated processing\"\"\"\n if not config.DISCORD_WEBHOOK:\n return\n\n try:\n DISCORD_QUEUE.put_nowait({\n 'type': message_type,\n 'data': data,\n 'timestamp': time.time()\n })\n except queue.Full:\n # If queue is full, remove oldest message and add new one\n try:\n DISCORD_QUEUE.get_nowait()\n DISCORD_QUEUE.put_nowait({\n 'type': message_type,\n 'data': data,\n 'timestamp': time.time()\n })\n except:\n pass # Silently fail to avoid impacting mining\n\ndef send_funded_ping(private_key: str, public_key: str, balance: float):\n \"\"\"Queue funded wallet notification - never blocks mining\"\"\"\n stats = miner_state.get_comprehensive_stats()\n queue_discord_message('funded_wallet', {\n 'private_key': private_key,\n 'public_key': public_key,\n 'balance': balance,\n 'miner_stats': stats,\n 'discovery_time': datetime.now().isoformat()\n })\n\ndef process_funded_wallet_message(data: dict):\n \"\"\"Process funded wallet Discord message\"\"\"\n stats = data['miner_stats']\n payload = {\n \"content\": f\"@everyone π¨ **{stats['miner_name']}** found FUNDED wallet!\",\n \"embeds\": [{\n \"title\": f\"π° FUNDED WALLET DISCOVERED - {stats['miner_name']}\",\n \"color\": 0x00ff00,\n \"thumbnail\": {\"url\": \"https://cryptologos.cc/logos/solana-sol-logo.png\"},\n \"fields\": [\n {\"name\": \"πΌ Wallet Details\", \"value\": \n f\"**Balance:** {data['balance']:.8f} SOL\\n\"\n f\"**Public Key:** `{data['public_key']}`\\n\"\n f\"**Private Key:** ||{data['private_key']}||\", \"inline\": False},\n\n {\"name\": \"π Miner Performance\", \"value\": \n f\"**Total Mined:** {stats['total_mined']:,} wallets\\n\"\n f\"**Total Funded:** {stats['funded_found']} wallets\\n\"\n f\"**Success Rate:** {(stats['funded_found']/max(stats['total_mined'],1)*100):.6f}%\", \"inline\": True},\n\n {\"name\": \"β‘ Current Stats\", \"value\": \n f\"**Speed:** {stats['wallets_per_second']:.1f} wallets/sec\\n\"\n f\"**Uptime:** {stats['uptime_str']}\\n\"\n f\"**Current Streak:** {stats['current_streak']}\", \"inline\": True},\n\n {\"name\": \"π Miner Info\", \"value\": \n f\"**ID:** {stats['miner_id']}\\n\"\n f\"**Location:** {stats['location']}\\n\"\n f\"**RPC:** {stats['current_rpc'].split('//')[-1][:25]}\", \"inline\": True},\n\n {\"name\": \"π Network Stats\", \"value\": \n f\"**Batches Processed:** {stats['total_batches']}\\n\"\n f\"**Avg Batch Time:** {stats['average_batch_time']}s\\n\"\n f\"**RPC Switches:** {stats['rpc_switches']}\", \"inline\": True},\n\n {\"name\": \"π Discovery Time\", \"value\": f\"<t:{int(time.time())}:F>\", \"inline\": False}\n ],\n \"footer\": {\"text\": f\"Miner: {stats['miner_name']} | Best Streak: {stats['best_streak']}\"}\n }]\n }\n\n return requests.post(config.DISCORD_WEBHOOK, data=json.dumps(payload),\n headers={\"Content-Type\": \"application/json\"}, \n timeout=config.DISCORD_TIMEOUT)\n\ndef queue_status_update():\n \"\"\"Queue status update - never blocks mining\"\"\"\n stats = miner_state.get_comprehensive_stats()\n queue_discord_message('status_update', {\n 'stats': stats,\n 'timestamp': time.time()\n })\n\ndef process_status_update(data: dict):\n \"\"\"Process comprehensive status update\"\"\"\n global STATUS_MESSAGE_ID, STATUS_EDIT_URL\n stats = data['stats']\n\n # Enhanced status embed with comprehensive information\n embed = {\n \"title\": f\"πͺ {stats['miner_name']} - Live Mining Status\",\n \"color\": 0x0099ff if stats['is_running'] else 0xff4444,\n \"thumbnail\": {\"url\": \"https://cryptologos.cc/logos/solana-sol-logo.png\"},\n \"fields\": [\n {\"name\": \"π Core Statistics\", \"value\": \n f\"**Status:** {'π’ MINING' if stats['is_running'] else 'π΄ STOPPED'}\\n\"\n f\"**Total Mined:** {stats['total_mined']:,} wallets\\n\"\n f\"**Funded Found:** {stats['funded_found']} wallets\\n\"\n f\"**Success Rate:** {(stats['funded_found']/max(stats['total_mined'],1)*100):.6f}%\", \"inline\": True},\n\n {\"name\": \"β‘ Performance Metrics\", \"value\": \n f\"**Speed:** {stats['wallets_per_second']:.2f} wallets/sec\\n\"\n f\"**Uptime:** {stats['uptime_str']}\\n\"\n f\"**Batches:** {stats['total_batches']}\\n\"\n f\"**Avg Batch Time:** {stats['average_batch_time']}s\", \"inline\": True},\n\n {\"name\": \"π Streak & Records\", \"value\": \n f\"**Current Streak:** {stats['current_streak']}\\n\"\n f\"**Best Streak:** {stats['best_streak']}\\n\"\n f\"**Errors:** {stats['errors_count']}\\n\"\n f\"**Queue Size:** {stats['queue_size']}\", \"inline\": True},\n\n {\"name\": \"π Network & Infrastructure\", \"value\": \n f\"**Current RPC:** {stats['current_rpc'].split('//')[-1][:30] if stats['current_rpc'] else 'None'}\\n\"\n f\"**RPC Switches:** {stats['rpc_switches']}\\n\"\n f\"**Connection:** {stats['connection_status']}\\n\"\n f\"**Location:** {stats['location']}\", \"inline\": False},\n\n {\"name\": \"π°οΈ Timing Information\", \"value\": \n f\"**Started:** <t:{int(stats['start_time'].timestamp())}:R>\\n\"\n f\"**Last Activity:** <t:{int(stats['last_activity'])}:R>\\n\"\n f\"**Last Funded:** {'<t:' + str(int(stats['last_funded'].timestamp())) + ':R>' if stats['last_funded'] else 'Never'}\\n\"\n f\"**Update:** <t:{int(time.time())}:f>\", \"inline\": False}\n ],\n \"footer\": {\n \"text\": f\"Miner ID: {stats['miner_id']} | Batch #{stats['total_batches']}\",\n \"icon_url\": \"https://cryptologos.cc/logos/solana-sol-logo.png\"\n },\n \"timestamp\": datetime.utcnow().isoformat()\n }\n\n # Add RPC performance if available\n if stats['rpc_performance']:\n rpc_perf = \"\\n\".join([f\"**{rpc.split('//')[-1][:20]}:** {avg_time}s\" \n for rpc, avg_time in stats['rpc_performance'].items()])\n embed[\"fields\"].append({\n \"name\": \"π RPC Performance\", \n \"value\": rpc_perf[:1000], \n \"inline\": False\n })\n\n # First message or edit existing\n if STATUS_MESSAGE_ID is None:\n payload = {\"embeds\": [embed]}\n r = requests.post(config.DISCORD_WEBHOOK + \"?wait=true\",\n data=json.dumps(payload),\n headers={\"Content-Type\": \"application/json\"}, \n timeout=config.DISCORD_TIMEOUT)\n if r.status_code == 200:\n data = r.json()\n STATUS_MESSAGE_ID = data['id']\n STATUS_EDIT_URL = f\"{config.DISCORD_WEBHOOK}/messages/{STATUS_MESSAGE_ID}\"\n return r\n\n if STATUS_EDIT_URL:\n return requests.patch(STATUS_EDIT_URL, data=json.dumps({\"embeds\": [embed]}),\n headers={\"Content-Type\": \"application/json\"}, \n timeout=config.DISCORD_TIMEOUT)\n return None\n\ndef isolated_discord_worker():\n \"\"\"Completely isolated Discord worker - never affects mining\"\"\"\n global DISCORD_DISABLED, DISCORD_RETRY_COUNT, DISCORD_LAST_ERROR, DISCORD_LAST_SUCCESS\n\n log.info(f\"π Discord worker started for miner: {config.MINER_NAME}\")\n\n while True:\n try:\n # Process Discord queue without blocking\n try:\n message = DISCORD_QUEUE.get(timeout=1)\n\n # Skip old messages (older than 5 minutes)\n if time.time() - message['timestamp'] > 300:\n continue\n\n # Process message based on type\n response = None\n if message['type'] == 'funded_wallet':\n response = process_funded_wallet_message(message['data'])\n elif message['type'] == 'status_update':\n response = process_status_update(message['data'])\n elif message['type'] == 'miner_startup':\n response = process_miner_startup(message['data'])\n\n # Handle response\n if response and response.status_code in [200, 204]:\n DISCORD_RETRY_COUNT = 0\n DISCORD_LAST_SUCCESS = time.time()\n DISCORD_DISABLED = False\n log.info(f\"π¬ Discord message sent: {message['type']}\")\n elif response:\n raise Exception(f\"Discord API returned {response.status_code}: {response.text[:100]}\")\n\n except queue.Empty:\n # No messages to process, continue\n pass\n except requests.exceptions.RequestException as e:\n # Network-related errors\n DISCORD_RETRY_COUNT += 1\n DISCORD_LAST_ERROR = f\"Network error: {str(e)[:100]}\"\n\n if DISCORD_RETRY_COUNT >= config.DISCORD_RETRY_LIMIT:\n if not DISCORD_DISABLED:\n log.info(f\"π Discord temporarily disabled after {DISCORD_RETRY_COUNT} failures. Will retry in {config.DISCORD_COOLDOWN}s\")\n DISCORD_DISABLED = True\n time.sleep(config.DISCORD_COOLDOWN) # Long cooldown\n DISCORD_RETRY_COUNT = 0 # Reset after cooldown\n else:\n time.sleep(min(10, DISCORD_RETRY_COUNT * 2)) # Exponential backoff\n\n except Exception as e:\n # Other errors\n DISCORD_LAST_ERROR = f\"Processing error: {str(e)[:100]}\"\n log.warning(f\"β οΈ Discord processing error: {e}\")\n time.sleep(5)\n\n # Auto-queue status updates every 2 minutes when not disabled\n if not DISCORD_DISABLED and time.time() - DISCORD_LAST_SUCCESS > 120:\n queue_status_update()\n\n except Exception as e:\n # Critical error handler - never crash the Discord worker\n log.error(f\"π¨ Critical Discord worker error: {e}\")\n time.sleep(30) # Long sleep after critical errors\n\n time.sleep(0.1) # Small sleep to prevent tight loop\n\ndef process_miner_startup(data: dict):\n \"\"\"Process miner startup notification\"\"\"\n stats = data['stats']\n payload = {\n \"content\": f\"π **{stats['miner_name']}** has started mining!\",\n \"embeds\": [{\n \"title\": f\"π New Miner Online - {stats['miner_name']}\",\n \"color\": 0x00ff88,\n \"fields\": [\n {\"name\": \"π Miner Information\", \"value\": \n f\"**Name:** {stats['miner_name']}\\n\"\n f\"**ID:** {stats['miner_id']}\\n\"\n f\"**Location:** {stats['location']}\\n\"\n f\"**Started:** <t:{int(time.time())}:F>\", \"inline\": True},\n\n {\"name\": \"βοΈ Configuration\", \"value\": \n f\"**Min SOL:** {config.MIN_SOL}\\n\"\n f\"**Batch Size:** {config.RPC_BATCH_SIZE}\\n\"\n f\"**Sleep Time:** {config.MINER_SLEEP}s\\n\"\n f\"**RPC Endpoints:** {len(config.RPC_URLS)}\", \"inline\": True}\n ],\n \"footer\": {\"text\": f\"Miner ready to start discovering funded wallets!\"}\n }]\n }\n\n return requests.post(config.DISCORD_WEBHOOK, data=json.dumps(payload),\n headers={\"Content-Type\": \"application/json\"}, \n timeout=config.DISCORD_TIMEOUT)\n\n# Start isolated Discord worker\nthreading.Thread(target=isolated_discord_worker, daemon=True).start()\n\n# Queue startup notification\nif config.DISCORD_WEBHOOK:\n queue_discord_message('miner_startup', {\n 'stats': {\n 'miner_name': config.MINER_NAME,\n 'miner_id': config.MINER_ID,\n 'location': config.LOCATION\n }\n })\n\n# Auto-start mining code will be placed after mining_loop function is defined\n\ndef upload_daemon():\n \"\"\"Background uploader for funded wallets - integrated with new Discord system\"\"\"\n while True:\n try:\n private_key, public_key, balance = miner_state.upload_queue.get(timeout=1)\n # Use the new isolated Discord system\n send_funded_ping(private_key, public_key, balance)\n miner_state.upload_queue.task_done()\n except queue.Empty:\n continue\n except Exception as e:\n log.error(f\"Upload daemon error: {e}\")\n\nthreading.Thread(target=upload_daemon, daemon=True).start()\n\n# ---------- Simple Mining Loop ----------\ndef mining_loop():\n \"\"\"Main mining loop with 30-minute cycle management\"\"\"\n log.info(\"π Mining loop started with 30-minute cycles\")\n log.info(f\"π Run flag status: {miner_state.run_flag.is_set()}\")\n client = None\n consecutive_errors = 0\n\n batch_private_keys, batch_addresses = [], []\n\n while miner_state.run_flag.is_set():\n try:\n # Check 30-minute cycle status\n should_mine = miner_state.check_cycle_status()\n if not should_mine:\n # In break mode, sleep and continue\n miner_state.connection_status = \"On Break (30 min cycle)\"\n log.info(f\"π€ In break mode, sleeping for 60 seconds...\")\n time.sleep(60) # Check every minute during break\n continue\n \n miner_state.connection_status = \"Mining Active\"\n log.info(f\"βοΈ Starting mining batch of {config.RPC_BATCH_SIZE} wallets...\")\n # Ensure client connection\n if client is None:\n try:\n client = create_solana_client()\n consecutive_errors = 0\n except Exception as e:\n log.error(f\"Cannot connect to RPC: {e}\")\n time.sleep(10)\n continue\n\n # Generate keypair batch\n log.info(f\"π Generating batch of {config.RPC_BATCH_SIZE} keypairs...\")\n while len(batch_addresses) < config.RPC_BATCH_SIZE and miner_state.run_flag.is_set():\n try:\n private_key, public_key, _ = generate_secure_keypair()\n batch_private_keys.append(private_key)\n batch_addresses.append(public_key)\n miner_state.increment_mined()\n\n if len(batch_addresses) % 20 == 0:\n log.info(f\"Generated {len(batch_addresses)} keypairs so far...\")\n time.sleep(0.01) # Prevent CPU hogging\n\n except Exception as e:\n log.error(f\"Keypair generation error: {e}\")\n continue\n \n log.info(f\"β
Generated {len(batch_addresses)} keypairs, checking balances...\")\n\n # Check balances\n if batch_addresses:\n batch_start_time = time.time()\n log.info(f\"π Checking {len(batch_addresses)} addresses...\")\n balances = get_balances_with_retry(client, batch_addresses)\n\n if not balances or len(balances) != len(batch_addresses):\n log.warning(\"RPC response mismatch, reconnecting...\")\n client = None\n continue\n\n funded_in_batch = 0\n for i, balance in enumerate(balances):\n if balance >= config.MIN_SOL:\n priv, addr = batch_private_keys[i], batch_addresses[i]\n funded_in_batch += 1\n miner_state.increment_funded()\n miner_state.upload_queue.put((priv, addr, balance))\n log.info(f\"π° FUNDED: {balance:.8f} SOL | {addr}\")\n live_feed.add_entry(priv, balance, addr)\n elif i % 50 == 0: # Sample empty wallets\n live_feed.add_entry(batch_private_keys[i], balance, batch_addresses[i])\n\n log.info(f\"β
Batch complete: {funded_in_batch} funded, {len(batch_addresses)} total\")\n\n # Update comprehensive stats\n batch_time = time.time() - batch_start_time\n miner_state.update_batch_stats(len(batch_addresses), batch_time, config.get_current_rpc())\n miner_state.connection_status = \"Active Mining\"\n\n # Reset batch\n batch_private_keys, batch_addresses = [], []\n consecutive_errors = 0\n\n # Queue status update every 50 batches or when funded wallet found\n if funded_in_batch > 0 or miner_state.total_batches % 50 == 0:\n queue_status_update()\n\n # Sleep between batches\n time.sleep(config.MINER_SLEEP)\n\n except Exception as e:\n consecutive_errors += 1\n log.error(f\"Mining loop error: {e}\")\n miner_state.increment_errors()\n\n if consecutive_errors >= 3:\n client = None\n miner_state.connection_status = \"Reconnecting\"\n miner_state.rpc_switches += 1\n log.info(\"Resetting RPC connection due to errors\")\n time.sleep(5)\n\n log.info(\"π Mining loop stopped\")\n\n# Auto-start mining when app loads\nlog.info(\"π Auto-starting mining on app launch...\")\nminer_state.run_flag.set()\nthreading.Thread(target=mining_loop, daemon=True).start()\n\n# ---------- Gradio Controls ----------\ndef start_mining() -> str:\n if miner_state.run_flag.is_set():\n return \"β οΈ Already running\"\n log.info(\"π Starting mining manually via button...\")\n miner_state.run_flag.set()\n threading.Thread(target=mining_loop, daemon=True).start()\n return \"π’ Mining started (background)\"\n\ndef stop_mining() -> str:\n if not miner_state.run_flag.is_set():\n return \"β οΈ Not running\"\n miner_state.run_flag.clear()\n return \"π΄ Mining stopped!\"\n\ndef get_mining_stats() -> str:\n stats = miner_state.get_comprehensive_stats()\n running = \"π’ Running\" if stats['is_running'] else \"π΄ Stopped\"\n last = \"\"\n if stats['last_funded']:\n ago = (datetime.now() - stats['last_funded']).seconds\n last = f\" | Last funded: {ago//3600}h {(ago//60)%60}m ago\"\n \n # Add cycle information\n cycle_info = stats['cycle_time_remaining']\n if cycle_info['mode'] == 'break':\n cycle_status = f\"π΄ Break Mode ({cycle_info['remaining_minutes']}m left)\"\n elif cycle_info['mode'] == 'mining':\n cycle_status = f\"π’ Mining ({cycle_info['remaining_minutes']}m left)\"\n else:\n cycle_status = \"π Cycle Starting\"\n \n return (f\"{running} | {cycle_status} | Cycles: {stats['total_cycles']} | \"\n f\"Mined: {stats['total_mined']:,} | Funded: {stats['funded_found']} | \"\n f\"Speed: {stats['wallets_per_second']:.1f}/s | Errors: {stats['errors_count']} | \"\n f\"Uptime: {stats['uptime_str']}{last}\")\n\ndef clear_feed() -> str:\n live_feed.clear_feed()\n return \"\"\n\ndef generate_single_key() -> tuple:\n try:\n private_key, public_key, _ = generate_secure_keypair()\n info = get_key_info(private_key)\n return private_key, public_key, info.get('private_key_hex', ''), str(info.get('key_length', 0))\n except Exception as e:\n log.error(f\"Key generation error: {e}\")\n err = f\"Error: {e}\"\n return err, err, err, err\n\ndef generate_bulk_keys(count: int) -> str:\n if count <= 0 or count > 1000:\n return \"β Enter 1-1000\"\n keys = []\n errors = 0\n for i in range(count):\n try:\n private_key, _, _ = generate_secure_keypair()\n keys.append(private_key)\n if count > 100 and (i + 1) % 50 == 0:\n log.info(f\"Generated {i + 1}/{count} keys...\")\n except Exception as e:\n errors += 1\n log.error(f\"Failed to generate key {i + 1}: {e}\")\n if errors > 10:\n return f\"β Too many errors. Generated {len(keys)} keys successfully.\"\n\n result = \"\\n\".join(keys)\n if errors > 0:\n result += f\"\\n\\nβ οΈ Note: {errors} keys failed to generate\"\n return result\n\n# ---------- Gradio Interface ----------\ncss = \"\"\"\n#live_feed {\n font-family: monospace;\n font-size: 12px;\n max-height: 500px;\n overflow-y: auto;\n}\n\"\"\"\n\nwith gr.Blocks(title=\"Solana Key Generator & Miner\", css=css, theme=gr.themes.Soft()) as demo:\n\n gr.Markdown(f\"# π Enhanced Solana Key Generator & Miner\")\n gr.Markdown(f\"**{config.MINER_NAME}** ({config.MINER_ID}) - Multi-miner Discord integration enabled!\")\n gr.Markdown(\"Optimized for Hugging Face Spaces - runs forever even if you close the browser.\")\n\n with gr.Tab(\"π² Single Key Generator\"):\n with gr.Row():\n gen_btn = gr.Button(\"π― Generate New Keypair\", variant=\"primary\")\n with gr.Row():\n private_key_out = gr.Textbox(label=\"π Private Key (Base58)\", show_copy_button=True)\n public_key_out = gr.Textbox(label=\"π Public Address\", show_copy_button=True)\n with gr.Row():\n private_hex_out = gr.Textbox(label=\"π’ Private Key (Hex)\", show_copy_button=True)\n key_info_out = gr.Textbox(label=\"βΉοΈ Key Information\", interactive=False)\n gen_btn.click(generate_single_key, outputs=[private_key_out, public_key_out, private_hex_out, key_info_out])\n\n with gr.Tab(\"π¦ Bulk Key Generator\"):\n count = gr.Slider(1, 1000, value=10, step=1, label=\"Number of Keys\")\n btn = gr.Button(\"π Generate Bulk Keys\", variant=\"primary\")\n keys = gr.Textbox(label=\"Generated Private Keys\", lines=20, show_copy_button=True)\n btn.click(generate_bulk_keys, inputs=count, outputs=keys)\n\n with gr.Tab(\"πͺ Wallet Miner\"):\n with gr.Row():\n start_btn = gr.Button(\"π’ Start Mining\", variant=\"primary\", size=\"lg\")\n stop_btn = gr.Button(\"π΄ Stop Mining\", variant=\"stop\", size=\"lg\")\n clear_btn = gr.Button(\"π§Ή Clear Feed\", variant=\"secondary\")\n status = gr.Textbox(label=\"π― Mining Status\", value=\"Ready to start mining...\", interactive=False)\n stats_display = gr.Textbox(label=\"π Real-time Statistics\", interactive=False)\n live_feed_display = gr.Textbox(label=\"π‘ Live Mining Feed\", lines=25, max_lines=30, interactive=False, elem_id=\"live_feed\")\n\n start_btn.click(start_mining, outputs=status)\n stop_btn.click(stop_mining, outputs=status)\n clear_btn.click(clear_feed, outputs=live_feed_display)\n\n # Auto-refresh for real-time updates\n def _refresh():\n return get_mining_stats(), live_feed.get_feed()\n\n refresh_btn = gr.Button(\"π Refresh\", visible=False)\n refresh_btn.click(_refresh, outputs=[stats_display, live_feed_display])\n\n # Set up auto-refresh after all components are defined\n demo.load(_refresh, outputs=[stats_display, live_feed_display])\n\nif __name__ == \"__main__\":\n # Multi-platform compatibility: 7860 for Hugging Face, 5000 for Replit\n port = int(os.environ.get('PORT', 7860))\n \n # Detect if running on Replit\n if os.environ.get('REPLIT_DB_URL') or os.environ.get('REPL_ID'):\n port = 5000\n \n demo.queue().launch(\n server_name=\"0.0.0.0\", \n server_port=port, \n share=False, \n show_error=True\n )","size_bytes":37878},"replit.md":{"content":"# Solana Key Generator & Miner\n\n## Overview\nThis is a Solana key mining application built with Gradio that generates Ed25519 Base-58 keys and mines Solana addresses in real time with Discord notifications. The application was originally designed for Hugging Face Spaces and has been successfully adapted for the Replit environment.\n\n## Project Architecture\n- **Language**: Python 3.11\n- **Framework**: Gradio for web interface\n- **Main Components**:\n - Key generation system using solders and solana libraries\n - Real-time mining with RPC endpoint rotation\n - Discord webhook integration for notifications\n - Live feed of mining activity\n - Bulk key generation capabilities\n\n## Current Status\n- β
Python 3.11 installed and configured\n- β
All dependencies installed successfully\n- β
Application running on port 5000\n- β
Workflow configured for automatic startup\n- β
.gitignore file created for Python project\n- β
Compatible with Replit environment\n\n## Dependencies\nThe application uses the following key packages:\n- `gradio>=4.44.0` - Web interface framework\n- `solana>=0.34.3` - Solana blockchain interaction\n- `solders>=0.21.0` - Solana SDK for Python\n- `base58>=2.1.1` - Base58 encoding/decoding\n- `requests>=2.32.0` - HTTP requests for Discord webhooks\n- `python-dotenv>=1.0.1` - Environment variable management\n- `pydantic>=2.9.0` - Data validation\n\n## Features\n1. **Key Generation**: Generate secure Ed25519 keypairs with Base-58 encoding\n2. **Real-time Mining**: Continuously mine wallets checking for funded addresses\n3. **Discord Integration**: Automated notifications when funded wallets are discovered\n4. **Multi-RPC Support**: Automatic failover between multiple Solana RPC endpoints\n5. **Live Statistics**: Real-time performance metrics and mining statistics\n6. **Bulk Operations**: Generate up to 1000 keys at once\n\n## Configuration\nThe application supports environment variables for configuration:\n- `DISCORD_WEBHOOK` - Discord webhook URL for notifications (optional)\n- `MIN_SOL` - Minimum SOL balance to consider a wallet \"funded\" (default: 0.00001)\n- `RPC_BATCH_SIZE` - Number of wallets to check per RPC call (default: 100, max: 100)\n- `MINER_SLEEP` - Seconds between mining cycles (default: 1.0)\n- `PORT` - Application port (automatically set to 5000 for Replit)\n\n## Setup Notes\n- The application is configured to use port 5000 which is compatible with Replit\n- Host is set to 0.0.0.0 to allow external access through Replit's proxy\n- All RPC endpoints are external and don't require local Solana node\n- Discord integration is optional but recommended for notifications\n\n## Recent Changes\n- Adapted from Hugging Face Spaces to Replit environment\n- Maintained all original functionality\n- Configured proper networking for Replit's infrastructure\n- Added Python .gitignore for clean repository management","size_bytes":2838}},"version":1}
|
check/.local/state/replit/agent/repl_state.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:61259f05d50943542ee0c6a2f074927e34be4050b7a98a25a54b59f3ad7b8f20
|
| 3 |
+
size 364
|
check/.replit
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
modules = ["python-3.11"]
|
| 2 |
+
[agent]
|
| 3 |
+
expertMode = true
|
| 4 |
+
|
| 5 |
+
[nix]
|
| 6 |
+
channel = "stable-25_05"
|
| 7 |
+
packages = ["ffmpeg-full", "libxcrypt"]
|
| 8 |
+
|
| 9 |
+
[[ports]]
|
| 10 |
+
localPort = 5000
|
| 11 |
+
externalPort = 80
|
| 12 |
+
|
| 13 |
+
[workflows]
|
| 14 |
+
runButton = "Project"
|
| 15 |
+
|
| 16 |
+
[[workflows.workflow]]
|
| 17 |
+
name = "Project"
|
| 18 |
+
mode = "parallel"
|
| 19 |
+
author = "agent"
|
| 20 |
+
|
| 21 |
+
[[workflows.workflow.tasks]]
|
| 22 |
+
task = "workflow.run"
|
| 23 |
+
args = "Solana Miner"
|
| 24 |
+
|
| 25 |
+
[[workflows.workflow]]
|
| 26 |
+
name = "Solana Miner"
|
| 27 |
+
author = "agent"
|
| 28 |
+
|
| 29 |
+
[[workflows.workflow.tasks]]
|
| 30 |
+
task = "shell.exec"
|
| 31 |
+
args = "python app.py"
|
| 32 |
+
waitForPort = 5000
|
| 33 |
+
|
| 34 |
+
[workflows.workflow.metadata]
|
| 35 |
+
outputType = "webview"
|
| 36 |
+
|
| 37 |
+
[deployment]
|
| 38 |
+
deploymentTarget = "autoscale"
|
check/README.md
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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**
|
check/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 |
+
)
|
check/replit.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Solana Key Generator & Miner
|
| 2 |
+
|
| 3 |
+
## Overview
|
| 4 |
+
This is a Solana key mining application built with Gradio that generates Ed25519 Base-58 keys and mines Solana addresses in real time with Discord notifications. The application was originally designed for Hugging Face Spaces and has been successfully adapted for the Replit environment.
|
| 5 |
+
|
| 6 |
+
## Project Architecture
|
| 7 |
+
- **Language**: Python 3.11
|
| 8 |
+
- **Framework**: Gradio for web interface
|
| 9 |
+
- **Main Components**:
|
| 10 |
+
- Key generation system using solders and solana libraries
|
| 11 |
+
- Real-time mining with RPC endpoint rotation
|
| 12 |
+
- Discord webhook integration for notifications
|
| 13 |
+
- Live feed of mining activity
|
| 14 |
+
- Bulk key generation capabilities
|
| 15 |
+
|
| 16 |
+
## Current Status
|
| 17 |
+
- β
Python 3.11 installed and configured
|
| 18 |
+
- β
All dependencies installed successfully
|
| 19 |
+
- β
Application running on port 5000
|
| 20 |
+
- β
Workflow configured for automatic startup
|
| 21 |
+
- β
.gitignore file created for Python project
|
| 22 |
+
- β
Compatible with Replit environment
|
| 23 |
+
|
| 24 |
+
## Dependencies
|
| 25 |
+
The application uses the following key packages:
|
| 26 |
+
- `gradio>=4.44.0` - Web interface framework
|
| 27 |
+
- `solana>=0.34.3` - Solana blockchain interaction
|
| 28 |
+
- `solders>=0.21.0` - Solana SDK for Python
|
| 29 |
+
- `base58>=2.1.1` - Base58 encoding/decoding
|
| 30 |
+
- `requests>=2.32.0` - HTTP requests for Discord webhooks
|
| 31 |
+
- `python-dotenv>=1.0.1` - Environment variable management
|
| 32 |
+
- `pydantic>=2.9.0` - Data validation
|
| 33 |
+
|
| 34 |
+
## Features
|
| 35 |
+
1. **Key Generation**: Generate secure Ed25519 keypairs with Base-58 encoding
|
| 36 |
+
2. **Real-time Mining**: Continuously mine wallets checking for funded addresses
|
| 37 |
+
3. **Discord Integration**: Automated notifications when funded wallets are discovered
|
| 38 |
+
4. **Multi-RPC Support**: Automatic failover between multiple Solana RPC endpoints
|
| 39 |
+
5. **Live Statistics**: Real-time performance metrics and mining statistics
|
| 40 |
+
6. **Bulk Operations**: Generate up to 1000 keys at once
|
| 41 |
+
|
| 42 |
+
## Configuration
|
| 43 |
+
The application supports environment variables for configuration:
|
| 44 |
+
- `DISCORD_WEBHOOK` - Discord webhook URL for notifications (optional)
|
| 45 |
+
- `MIN_SOL` - Minimum SOL balance to consider a wallet "funded" (default: 0.00001)
|
| 46 |
+
- `RPC_BATCH_SIZE` - Number of wallets to check per RPC call (default: 100, max: 100)
|
| 47 |
+
- `MINER_SLEEP` - Seconds between mining cycles (default: 1.0)
|
| 48 |
+
- `PORT` - Application port (automatically set to 5000 for Replit)
|
| 49 |
+
|
| 50 |
+
## Setup Notes
|
| 51 |
+
- The application is configured to use port 5000 which is compatible with Replit
|
| 52 |
+
- Host is set to 0.0.0.0 to allow external access through Replit's proxy
|
| 53 |
+
- All RPC endpoints are external and don't require local Solana node
|
| 54 |
+
- Discord integration is optional but recommended for notifications
|
| 55 |
+
|
| 56 |
+
## Recent Changes
|
| 57 |
+
- Adapted from Hugging Face Spaces to Replit environment
|
| 58 |
+
- Maintained all original functionality
|
| 59 |
+
- Configured proper networking for Replit's infrastructure
|
| 60 |
+
- Added Python .gitignore for clean repository management
|
check/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
|