mirror of
https://github.com/tcsenpai/ethereum-vanity-address-generator-cuda.git
synced 2025-06-04 09:40:26 +00:00
working commit
This commit is contained in:
commit
6cf7c775de
9
.env
Normal file
9
.env
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Enable balance checking (true/false)
|
||||||
|
CHECK_BALANCES=true
|
||||||
|
# RPC URL for Ethereum node
|
||||||
|
RPC_URL=https://eth.llamarpc.com
|
||||||
|
# How many addresses to check in each batch
|
||||||
|
BALANCE_BATCH_SIZE=100
|
||||||
|
# Desired address prefix
|
||||||
|
PREFIX=dEAD000000000000000042069420694206942069
|
||||||
|
SYNC_MODE=false # Set to true for synchronous balance checking
|
81
README.md
Normal file
81
README.md
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
# Ethereum Vanity Address Generator with CUDA
|
||||||
|
|
||||||
|
A high-performance Ethereum vanity address generator that uses CUDA GPU acceleration to quickly generate Ethereum addresses matching a desired prefix pattern. It also includes optional balance checking functionality across multiple RPC endpoints.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- 🚀 CUDA GPU acceleration for fast address generation
|
||||||
|
- 🔍 Configurable prefix matching with support for hex characters
|
||||||
|
- 💰 Optional balance checking across multiple RPC endpoints
|
||||||
|
- ⚡ Batch processing for efficient balance checks
|
||||||
|
- 🔄 Automatic RPC failover and rate limit handling
|
||||||
|
- 📊 Real-time status updates and progress tracking
|
||||||
|
- 💾 Automatic saving of addresses with balances to file
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Python 3.7+
|
||||||
|
- CUDA-capable NVIDIA GPU
|
||||||
|
- PyCUDA
|
||||||
|
- Web3.py
|
||||||
|
- Other dependencies listed in requirements.txt
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Clone the repository:
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/tcsenpai/eth-vanity-address-generator-cuda.git
|
||||||
|
```
|
||||||
|
2. Install dependencies:
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Configure settings in `.env` file:
|
||||||
|
```bash
|
||||||
|
# Enable balance checking (true/false)
|
||||||
|
CHECK_BALANCES=true
|
||||||
|
# RPC URL for Ethereum node
|
||||||
|
RPC_URL=https://eth.llamarpc.com
|
||||||
|
# How many addresses to check in each batch
|
||||||
|
BALANCE_BATCH_SIZE=100
|
||||||
|
# Desired address prefix
|
||||||
|
PREFIX=dEAD000000000000000042069420694206942069
|
||||||
|
SYNC_MODE=false # Set to true for synchronous balance checking
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Run the script:
|
||||||
|
```bash
|
||||||
|
python main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Enter your desired address prefix when prompted, or configure it in the `.env` file.
|
||||||
|
|
||||||
|
The script will begin generating addresses and checking balances if enabled. Status updates are printed every 10 seconds showing:
|
||||||
|
|
||||||
|
- Time elapsed
|
||||||
|
- Total attempts
|
||||||
|
- Generation speed
|
||||||
|
- Balance check status
|
||||||
|
- Best match found so far
|
||||||
|
|
||||||
|
When a matching address is found, it will be displayed along with its private key.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
The following settings can be configured in the `.env` file:
|
||||||
|
|
||||||
|
- `CHECK_BALANCES`: Enable/disable balance checking
|
||||||
|
- `RPC_URL`: Ethereum RPC endpoint URL
|
||||||
|
- `BALANCE_BATCH_SIZE`: Number of addresses to check in each batch
|
||||||
|
- `PREFIX`: Target address prefix
|
||||||
|
- `SYNC_MODE`: Use synchronous or asynchronous balance checking
|
||||||
|
|
||||||
|
## Tips
|
||||||
|
|
||||||
|
- Longer prefixes will take exponentially more time to find
|
||||||
|
- Consider using shorter prefixes for testing
|
||||||
|
- Multiple RPC endpoints are used for redundancy
|
||||||
|
- Found addresses with balances are saved to `found.txt`
|
452
main.py
Normal file
452
main.py
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
import pycuda.autoinit
|
||||||
|
import pycuda.driver as cuda
|
||||||
|
from pycuda.compiler import SourceModule
|
||||||
|
import numpy as np
|
||||||
|
from eth_keys import keys
|
||||||
|
from eth_utils import to_checksum_address
|
||||||
|
import time
|
||||||
|
import dotenv
|
||||||
|
import os
|
||||||
|
from web3 import Web3, AsyncWeb3
|
||||||
|
import asyncio
|
||||||
|
from collections import deque
|
||||||
|
import aiohttp
|
||||||
|
import concurrent.futures
|
||||||
|
from threading import Lock
|
||||||
|
|
||||||
|
cuda.init()
|
||||||
|
|
||||||
|
dotenv.load_dotenv()
|
||||||
|
|
||||||
|
# Add near the top after imports
|
||||||
|
RPC_URLS = [
|
||||||
|
"https://eth.llamarpc.com",
|
||||||
|
"https://rpc.ankr.com/eth",
|
||||||
|
"https://ethereum.publicnode.com",
|
||||||
|
"https://1rpc.io/eth",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Add new configuration from .env
|
||||||
|
CHECK_BALANCES = os.getenv("CHECK_BALANCES", "false").lower() == "true"
|
||||||
|
BALANCE_BATCH_SIZE = int(os.getenv("BALANCE_BATCH_SIZE", "100"))
|
||||||
|
SYNC_MODE = os.getenv("SYNC_MODE", "false").lower() == "true"
|
||||||
|
BATCH_SIZE = int(os.getenv("BATCH_SIZE", "500")) # Number of addresses to check at once
|
||||||
|
|
||||||
|
# Create a round-robin RPC selector
|
||||||
|
rpc_index = 0
|
||||||
|
|
||||||
|
# Add near the top after other global variables
|
||||||
|
last_balance_check = {"address": None, "balance": None, "rpc": None}
|
||||||
|
|
||||||
|
|
||||||
|
def get_next_web3():
|
||||||
|
global rpc_index
|
||||||
|
web3 = Web3(Web3.HTTPProvider(RPC_URLS[rpc_index]))
|
||||||
|
rpc_index = (rpc_index + 1) % len(RPC_URLS)
|
||||||
|
return web3
|
||||||
|
|
||||||
|
|
||||||
|
# Create a queue for addresses to check
|
||||||
|
address_queue = deque()
|
||||||
|
|
||||||
|
# Add to imports and configuration
|
||||||
|
print_lock = Lock()
|
||||||
|
executor = concurrent.futures.ThreadPoolExecutor(max_workers=4)
|
||||||
|
pending_checks = []
|
||||||
|
|
||||||
|
# Add this near the top of the file, after imports
|
||||||
|
cuda_code = """
|
||||||
|
#include <curand_kernel.h>
|
||||||
|
|
||||||
|
__device__ __forceinline__ void generate_private_key(unsigned char *out, const int idx, curandState *state) {
|
||||||
|
// Generate 32 bytes (256 bits) for the private key using vectorized operations
|
||||||
|
uint4 *out_vec = (uint4*)&out[idx * 32];
|
||||||
|
|
||||||
|
// Generate 4 random 32-bit values using CUDA's optimized RNG
|
||||||
|
uint4 rand_val;
|
||||||
|
rand_val.x = curand(state);
|
||||||
|
rand_val.y = curand(state);
|
||||||
|
rand_val.z = curand(state);
|
||||||
|
rand_val.w = curand(state);
|
||||||
|
|
||||||
|
// Store using vector operation (more efficient than byte-by-byte)
|
||||||
|
*out_vec = rand_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" __global__ void generate_random(unsigned char *out, const int n) {
|
||||||
|
const int idx = blockIdx.x * blockDim.x + threadIdx.x;
|
||||||
|
if (idx >= n) return;
|
||||||
|
|
||||||
|
// Initialize CUDA RNG state
|
||||||
|
curandState state;
|
||||||
|
curand_init(clock64() + idx, 0, 0, &state);
|
||||||
|
|
||||||
|
// Generate private key using vectorized operations
|
||||||
|
generate_private_key(out, idx, &state);
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def check_single_balance(address, private_key):
|
||||||
|
"""Non-blocking balance check with RPC failover"""
|
||||||
|
for _ in range(len(RPC_URLS)): # Try each RPC once
|
||||||
|
try:
|
||||||
|
web3 = get_next_web3()
|
||||||
|
balance = web3.eth.get_balance(address)
|
||||||
|
# Update last balance check info
|
||||||
|
global last_balance_check
|
||||||
|
last_balance_check = {
|
||||||
|
"address": address,
|
||||||
|
"balance": Web3.from_wei(balance, "ether"),
|
||||||
|
"rpc": web3.provider.endpoint_uri
|
||||||
|
}
|
||||||
|
if balance > 0:
|
||||||
|
with print_lock:
|
||||||
|
print(f"\n{'='*50}")
|
||||||
|
print(f"🔥 Found address with balance!")
|
||||||
|
print(f"Address: {address}")
|
||||||
|
print(f"Balance: {Web3.from_wei(balance, 'ether')} ETH")
|
||||||
|
print(f"Private key: {private_key}")
|
||||||
|
print(f"{'='*50}\n")
|
||||||
|
with open("found.txt", "a") as f:
|
||||||
|
f.write(f"{address} {private_key}\n")
|
||||||
|
return balance
|
||||||
|
except Exception as e:
|
||||||
|
if "429" in str(e): # Rate limit error
|
||||||
|
with print_lock:
|
||||||
|
print(f"⚠️ Rate limit hit on {web3.provider.endpoint_uri}: {str(e)}")
|
||||||
|
time.sleep(1) # Back off a bit
|
||||||
|
else:
|
||||||
|
with print_lock:
|
||||||
|
print(f"⚠️ Error on {web3.provider.endpoint_uri}, trying next RPC: {str(e)}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
with print_lock:
|
||||||
|
print(f"❌ All RPCs failed to check balance for {address}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
async def generate_vanity_address(prefix, num_attempts=0):
|
||||||
|
mod = SourceModule(
|
||||||
|
cuda_code, no_extern_c=True, include_dirs=["/usr/local/cuda/include"]
|
||||||
|
)
|
||||||
|
generate_random = mod.get_function("generate_random")
|
||||||
|
|
||||||
|
# Use up to 4GB of GPU memory
|
||||||
|
max_memory = 4 * 1024 * 1024 * 1024 # 4GB in bytes
|
||||||
|
max_batch = max_memory // 32 # Each key is 32 bytes
|
||||||
|
current_batch = min(max_batch, 4000000) # Start with 4 million addresses
|
||||||
|
|
||||||
|
# Create pinned memory directly instead of converting
|
||||||
|
private_keys = cuda.pagelocked_empty((current_batch, 32), dtype=np.uint8)
|
||||||
|
try:
|
||||||
|
gpu_private_keys = cuda.mem_alloc(private_keys.nbytes)
|
||||||
|
except cuda.LogicError as e:
|
||||||
|
print(
|
||||||
|
f"⚠️ GPU memory allocation failed with batch size {current_batch}. Trying smaller batch..."
|
||||||
|
)
|
||||||
|
current_batch = current_batch // 2
|
||||||
|
private_keys = cuda.pagelocked_empty((current_batch, 32), dtype=np.uint8)
|
||||||
|
gpu_private_keys = cuda.mem_alloc(private_keys.nbytes)
|
||||||
|
|
||||||
|
# Optimize thread configuration for RTX 4060
|
||||||
|
block_size = 1024 # Maximum threads per block
|
||||||
|
grid_size = (current_batch + block_size - 1) // block_size
|
||||||
|
|
||||||
|
print(f"\n🔍 Starting search for prefix: {prefix}")
|
||||||
|
print(
|
||||||
|
f"💾 Batch size: {current_batch:,} addresses ({private_keys.nbytes / 1024 / 1024:.1f} MB)"
|
||||||
|
)
|
||||||
|
print(f"🧮 Grid size: {grid_size} blocks × {block_size} threads")
|
||||||
|
print(f"💡 This might take a while depending on prefix length...")
|
||||||
|
|
||||||
|
start_time = time.time()
|
||||||
|
total_attempts = 0
|
||||||
|
prefixes_checked = 0
|
||||||
|
best_match = {"address": None, "similarity": 0, "private_key": None}
|
||||||
|
last_status_time = time.time()
|
||||||
|
total_balance_checks = 0
|
||||||
|
active_checks = 0
|
||||||
|
|
||||||
|
def check_callback(future):
|
||||||
|
nonlocal active_checks, total_balance_checks
|
||||||
|
active_checks -= 1
|
||||||
|
total_balance_checks += 1
|
||||||
|
|
||||||
|
address_batch = []
|
||||||
|
private_key_batch = []
|
||||||
|
|
||||||
|
while True:
|
||||||
|
generate_random(
|
||||||
|
gpu_private_keys,
|
||||||
|
np.int32(current_batch),
|
||||||
|
block=(block_size, 1, 1),
|
||||||
|
grid=(grid_size, 1),
|
||||||
|
)
|
||||||
|
|
||||||
|
cuda.memcpy_dtoh(private_keys, gpu_private_keys)
|
||||||
|
total_attempts += current_batch
|
||||||
|
|
||||||
|
# Process addresses without waiting for balance checks
|
||||||
|
for priv_key in private_keys:
|
||||||
|
priv_key_hex = "".join(format(x, "02x") for x in priv_key)
|
||||||
|
|
||||||
|
try:
|
||||||
|
private_key = keys.PrivateKey(bytes.fromhex(priv_key_hex))
|
||||||
|
public_key = private_key.public_key
|
||||||
|
address = to_checksum_address(public_key.to_address())
|
||||||
|
addr_without_prefix = address[2:].lower()
|
||||||
|
|
||||||
|
# Check for exact match first (fast path)
|
||||||
|
if addr_without_prefix.startswith(prefix.lower()):
|
||||||
|
# Found a match! Queue balance check if needed
|
||||||
|
if CHECK_BALANCES:
|
||||||
|
address_batch.append(address)
|
||||||
|
private_key_batch.append(priv_key_hex)
|
||||||
|
return private_key, address
|
||||||
|
|
||||||
|
# Track best partial match
|
||||||
|
similarity = calculate_similarity(addr_without_prefix, prefix.lower())
|
||||||
|
if similarity > best_match["similarity"]:
|
||||||
|
best_match = {
|
||||||
|
"address": address,
|
||||||
|
"similarity": similarity,
|
||||||
|
"private_key": priv_key_hex,
|
||||||
|
}
|
||||||
|
print(
|
||||||
|
f"🎯 Best match so far: {best_match['address']} ({best_match['similarity']} chars)"
|
||||||
|
)
|
||||||
|
# Immediately check for balance if best match without waiting
|
||||||
|
ethBalance = check_single_balance(address, priv_key_hex)
|
||||||
|
print(f"💰 Balance: {ethBalance} ETH")
|
||||||
|
# Queue balance check if enabled (without waiting)
|
||||||
|
if CHECK_BALANCES:
|
||||||
|
address_batch.append(address)
|
||||||
|
private_key_batch.append(priv_key_hex)
|
||||||
|
if len(address_batch) >= BATCH_SIZE:
|
||||||
|
# Schedule balance check without awaiting
|
||||||
|
asyncio.create_task(
|
||||||
|
batch_check_balances(
|
||||||
|
address_batch.copy(), private_key_batch.copy()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
address_batch.clear()
|
||||||
|
private_key_batch.clear()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Status update every 10 seconds
|
||||||
|
current_time = time.time()
|
||||||
|
if current_time - last_status_time >= 10:
|
||||||
|
elapsed_time = current_time - start_time
|
||||||
|
speed = total_attempts / elapsed_time
|
||||||
|
prefix_speed = prefixes_checked / elapsed_time
|
||||||
|
print(f"\n{'='*30} Status Update {'='*30}")
|
||||||
|
print(f"⏱️ Time elapsed: {elapsed_time:.1f}s")
|
||||||
|
print(f"🔢 Attempts: {total_attempts:,}")
|
||||||
|
print(f"🔍 Prefixes checked: {prefixes_checked:,}")
|
||||||
|
print(f"⚡ Speed: {speed:,.2f} addr/s")
|
||||||
|
print(f"🚀 Prefix check speed: {prefix_speed:,.2f} prefixes/s")
|
||||||
|
if CHECK_BALANCES:
|
||||||
|
print(f"💰 Balance checks queued: {len(address_batch)}")
|
||||||
|
print(f"✓ Total balance checks: {total_balance_checks:,}")
|
||||||
|
if last_balance_check["address"]:
|
||||||
|
print(
|
||||||
|
f"📊 Last check: {last_balance_check['address']} - {last_balance_check['balance']} ETH"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"🎯 Best match so far: {best_match['address']} ({best_match['similarity']} chars)"
|
||||||
|
)
|
||||||
|
print(f"{'='*72}\n")
|
||||||
|
last_status_time = current_time
|
||||||
|
|
||||||
|
prefixes_checked += 1 # we checked a key
|
||||||
|
|
||||||
|
# Only break if num_attempts is positive
|
||||||
|
if num_attempts > 0 and total_attempts >= num_attempts:
|
||||||
|
print(f"\n⚠️ Reached maximum attempts: {num_attempts:,}")
|
||||||
|
break
|
||||||
|
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
|
def suggest_leet_alternatives(text):
|
||||||
|
# Only include leet mappings that result in valid hex characters
|
||||||
|
leet_map = {
|
||||||
|
"a": "4",
|
||||||
|
"e": "3",
|
||||||
|
"i": "1",
|
||||||
|
"o": "0",
|
||||||
|
"s": "5",
|
||||||
|
"b": "8",
|
||||||
|
# Removed 't':'7' and 'g':'9' as they're not valid hex
|
||||||
|
}
|
||||||
|
alternatives = [text]
|
||||||
|
|
||||||
|
# Generate variations
|
||||||
|
for char, leet_char in leet_map.items():
|
||||||
|
for existing in alternatives.copy():
|
||||||
|
if char in existing.lower():
|
||||||
|
alternatives.append(existing.lower().replace(char, leet_char))
|
||||||
|
|
||||||
|
# Filter to ensure only valid hex strings are suggested
|
||||||
|
valid_chars = set("0123456789abcdefABCDEF")
|
||||||
|
valid_alternatives = [
|
||||||
|
alt for alt in alternatives if all(c in valid_chars for c in alt)
|
||||||
|
]
|
||||||
|
|
||||||
|
return list(set(valid_alternatives))[:5] # Return up to 5 unique suggestions
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize_prefix(prefix):
|
||||||
|
# Remove '0x' if present
|
||||||
|
prefix = prefix.replace("0x", "")
|
||||||
|
|
||||||
|
# Check for valid hex characters
|
||||||
|
valid_chars = set("0123456789abcdefABCDEF")
|
||||||
|
if not all(c in valid_chars for c in prefix):
|
||||||
|
invalid_chars = [c for c in prefix if c not in valid_chars]
|
||||||
|
suggestions = suggest_leet_alternatives(prefix)
|
||||||
|
raise ValueError(
|
||||||
|
f"Invalid characters in prefix: {invalid_chars}\n"
|
||||||
|
f"Prefix must only contain hex characters (0-9, a-f)\n"
|
||||||
|
f"Try these leet alternatives: {', '.join(suggestions)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return prefix.lower()
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_similarity(address, prefix):
|
||||||
|
"""Count matching characters from start of address with prefix"""
|
||||||
|
for i, (a, p) in enumerate(zip(address, prefix)):
|
||||||
|
if a != p:
|
||||||
|
return i
|
||||||
|
return len(prefix)
|
||||||
|
|
||||||
|
|
||||||
|
async def batch_check_balances(addresses, private_keys):
|
||||||
|
"""Check multiple balances in a single RPC call with failover"""
|
||||||
|
for _ in range(len(RPC_URLS)): # Try each RPC once
|
||||||
|
try:
|
||||||
|
web3 = get_next_web3()
|
||||||
|
|
||||||
|
# Create batch payload
|
||||||
|
payload = [
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_getBalance",
|
||||||
|
"params": [addr, "latest"],
|
||||||
|
"id": i
|
||||||
|
}
|
||||||
|
for i, addr in enumerate(addresses)
|
||||||
|
]
|
||||||
|
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.post(web3.provider.endpoint_uri, json=payload) as response:
|
||||||
|
results = await response.json()
|
||||||
|
|
||||||
|
# Process results
|
||||||
|
for result, address, private_key in zip(results, addresses, private_keys):
|
||||||
|
if "result" in result:
|
||||||
|
balance = int(result["result"], 16) # Convert hex to int
|
||||||
|
# Update last balance check info
|
||||||
|
global last_balance_check
|
||||||
|
last_balance_check = {
|
||||||
|
"address": address,
|
||||||
|
"balance": Web3.from_wei(balance, "ether"),
|
||||||
|
"rpc": web3.provider.endpoint_uri
|
||||||
|
}
|
||||||
|
if balance > 0:
|
||||||
|
with print_lock:
|
||||||
|
print(f"\n{'='*50}")
|
||||||
|
print(f"🔥 Found address with balance!")
|
||||||
|
print(f"Address: {address}")
|
||||||
|
print(f"Balance: {Web3.from_wei(balance, 'ether')} ETH")
|
||||||
|
print(f"Private key: {private_key}")
|
||||||
|
print(f"{'='*50}\n")
|
||||||
|
with open("found.txt", "a") as f:
|
||||||
|
f.write(f"{address} {private_key}\n")
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
if "429" in str(e): # Rate limit error
|
||||||
|
with print_lock:
|
||||||
|
print(f"⚠️ Rate limit hit on {web3.provider.endpoint_uri}: {str(e)}")
|
||||||
|
time.sleep(1) # Back off a bit
|
||||||
|
else:
|
||||||
|
with print_lock:
|
||||||
|
print(f"⚠️ Batch check error on {web3.provider.endpoint_uri}, trying next RPC: {str(e)}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
with print_lock:
|
||||||
|
print(f"❌ All RPCs failed for batch balance check of {len(addresses)} addresses")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
async def determine_optimal_batch_size(rpc_url):
|
||||||
|
print("🔍 Testing RPC batch size limits...")
|
||||||
|
optimal_size = 100 # Default fallback
|
||||||
|
|
||||||
|
test_sizes = [2000, 1000, 500, 100]
|
||||||
|
for batch_size in test_sizes:
|
||||||
|
try:
|
||||||
|
web3 = Web3(Web3.HTTPProvider(rpc_url))
|
||||||
|
addresses = ["0x" + "0" * 40] * batch_size
|
||||||
|
|
||||||
|
payload = [
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_getBalance",
|
||||||
|
"params": [addr, "latest"],
|
||||||
|
"id": i,
|
||||||
|
}
|
||||||
|
for i, addr in enumerate(addresses)
|
||||||
|
]
|
||||||
|
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.post(
|
||||||
|
web3.provider.endpoint_uri, json=payload
|
||||||
|
) as response:
|
||||||
|
results = await response.json()
|
||||||
|
if isinstance(results, list) and len(results) == batch_size:
|
||||||
|
optimal_size = batch_size
|
||||||
|
print(f"✅ Found optimal batch size: {optimal_size}")
|
||||||
|
return optimal_size
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Batch size {batch_size} failed: {str(e)}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(f"ℹ️ Using conservative batch size: {optimal_size}")
|
||||||
|
return optimal_size
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
prefix = os.getenv("PREFIX")
|
||||||
|
if not prefix:
|
||||||
|
prefix = input("Enter desired address prefix (without 0x): ")
|
||||||
|
|
||||||
|
try:
|
||||||
|
prefix = sanitize_prefix(prefix)
|
||||||
|
private_key, address = await generate_vanity_address(prefix)
|
||||||
|
|
||||||
|
# Cleanup thread pool
|
||||||
|
executor.shutdown(wait=False)
|
||||||
|
|
||||||
|
return private_key, address
|
||||||
|
|
||||||
|
except ValueError as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
SUPPORTED_SIZES = []
|
||||||
|
for url in RPC_URLS:
|
||||||
|
SUPPORTED_SIZES.append(asyncio.run(determine_optimal_batch_size(url)))
|
||||||
|
BALANCE_BATCH_SIZE = min(SUPPORTED_SIZES)
|
||||||
|
print(f"🎯 Using safe batch size of {BALANCE_BATCH_SIZE} for all RPCs\n")
|
||||||
|
|
||||||
|
asyncio.run(main())
|
Loading…
x
Reference in New Issue
Block a user