├── .DS_Store ├── .env.example ├── .gitignore ├── README.md ├── data ├── ethereum_address_balance.txt ├── ethereum_addresses.txt ├── ethereum_and_token_balances.txt └── ethereum_private_keys.txt ├── main.py └── src ├── __init__.py ├── core ├── __init__.py ├── balance_checker.py ├── output_formatter.py └── web3_manager.py ├── scripts ├── __init__.py ├── ethereum_address_balance_checker.py └── ethereum_private_key_balance_checker.py └── utils ├── __init__.py ├── config.py ├── exceptions.py └── helpers.py /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asimd/ethercheck/c3e23175abc5601c7e5da06979d03bdb3e00896e/.DS_Store -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # Multiple Infura IDs for wallet scanner (comma-separated) 2 | INFURA_PROJECT_IDS=id1,id2,id3,id4,id5 3 | 4 | # Multicall Configuration 5 | MULTICALL_ADDRESS=0xeefBa1e63905eF1D7ACbA5a8513c70307C1cE441 6 | MIN_BALANCE=0.01 7 | 8 | # Token Addresses (Default Mainnet) 9 | USDT_ADDRESS=0xdAC17F958D2ee523a2206206994597C13D831ec7 10 | USDC_ADDRESS=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 11 | BNB_ADDRESS=0xB8c77482e45F1F44dE1745F52C74426C631bDD52 12 | BUSD_ADDRESS=0x4Fabb145d64652a948d72533023f6E7A623C7C53 13 | LINK_ADDRESS=0x514910771AF9Ca656af840dff83E8264EcF986CA 14 | MATIC_ADDRESS=0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0 15 | UNI_ADDRESS=0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 16 | WBTC_ADDRESS=0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599 17 | DAI_ADDRESS=0x6B175474E89094C44Da98b954EedeAC495271d0F 18 | SHIB_ADDRESS=0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE 19 | 20 | # Token Decimals 21 | USDT_DECIMALS=6 22 | USDC_DECIMALS=6 23 | BNB_DECIMALS=18 24 | BUSD_DECIMALS=18 25 | LINK_DECIMALS=18 26 | MATIC_DECIMALS=18 27 | UNI_DECIMALS=18 28 | WBTC_DECIMALS=8 29 | DAI_DECIMALS=18 30 | SHIB_DECIMALS=18 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Environment variables 2 | .env 3 | .env.* 4 | !.env.example 5 | 6 | # Python 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | *.so 11 | 12 | # Data files 13 | data/ 14 | ethereum_and_token_balances.txt 15 | ethereum_address_balance.txt 16 | 17 | # Virtual environment 18 | venv/ 19 | env/ 20 | ENV/ 21 | 22 | # IDE 23 | .vscode/ 24 | .idea/ 25 | *.swp 26 | *.swo 27 | 28 | # OS 29 | .DS_Store 30 | Thumbs.db 31 | data/ethereum_address_balance.txt 32 | data/ethereum_and_token_balances.txt 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ethereum Balance Checker Scripts 2 | 3 | A set of Python scripts to check ETH and ERC-20 token balances for Ethereum addresses and private keys. 4 | 5 | ## 🚀 Features 6 | 7 | - Check ETH balance for multiple addresses/private keys 8 | - Check balances for 100+ popular ERC-20 tokens 9 | - Detects even tiny dust amounts of ETH (down to 1 wei) 10 | - Concurrent processing for faster results 11 | - Progress bar to track checking process 12 | - Formatted output for easy readability 13 | - Results saved to text files 14 | - Non-destructive operation (keeps input files intact) 15 | 16 | ## 📋 Prerequisites 17 | 18 | - Python 3.7 or higher 19 | - pip (Python package installer) 20 | 21 | ## 🛠️ Setup 22 | 23 | 1. Clone this repository: 24 | ```bash 25 | git clone https://github.com/asimd/ethercheck.git 26 | cd ethercheck 27 | ``` 28 | 29 | 2. Install required packages: 30 | ```bash 31 | pip3 install web3 tqdm python-dotenv 32 | ``` 33 | 34 | 3. Create your environment file: 35 | ```bash 36 | cp .env.example .env 37 | ``` 38 | 39 | 4. Add your Infura Project IDs to `.env`: 40 | ``` 41 | INFURA_PROJECT_IDS=your_project_id_1,your_project_id_2,your_project_id_3 42 | ``` 43 | Note: Multiple IDs are recommended to handle rate limiting 44 | 45 | 5. Create input files in the `data` directory: 46 | - For addresses: `data/ethereum_addresses.txt` 47 | - For private keys: `data/ethereum_private_keys.txt` 48 | 49 | ## 💻 Usage 50 | 51 | ### Run the interactive menu: 52 | ```bash 53 | python3 main.py 54 | ``` 55 | 56 | ### Choose from the following options: 57 | 58 | 1. Check Private Keys 59 | 2. Check Addresses 60 | 3. Exit 61 | 62 | ## 📝 Input Files Format 63 | #### Private Keys (data/ethereum_private_keys.txt): 64 | One private key per line (64 hex characters) 65 | ``` 66 | 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef 67 | 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdeg 68 | ``` 69 | 70 | #### Addresses (data/ethereum_addresses.txt): 71 | One address per line 72 | 73 | ``` 74 | 0x123456789abcdef0123456789abcdef0123456789abcdef 75 | 0x123456789abcdef0123456789abcdef0123456789abcdec 76 | ``` 77 | 78 | ## 📝 Output Format 79 | 80 | Results are displayed in the console and saved to text files: 81 | - Address checker: `data/ethereum_address_balance.txt` 82 | - Private key checker: `data/ethereum_and_token_balances.txt` 83 | 84 | The script will detect and display: 85 | - Any non-zero ETH balance (even dust amounts) 86 | - Any ERC-20 token balances 87 | - Formatted amounts based on balance size: 88 | - < 1 Gwei: 18 decimal places 89 | - < 0.00001 ETH: 12 decimal places 90 | - < 1 ETH: 8 decimal places 91 | - ≥ 1 ETH: 6 decimal places 92 | 93 | ## ⚙️ Customization 94 | 95 | You can customize the script behavior by modifying these variables: 96 | 97 | - `TOKENS_TO_CHECK`: Add or remove ERC-20 tokens to check 98 | - `TOKEN_DECIMALS`: Modify decimal places for specific tokens 99 | 100 | ## 🔒 Security Considerations 101 | 102 | - Never share your private keys 103 | - Use this tool in a secure environment 104 | - Verify addresses and balances independently 105 | - Keep your .env file secure and never commit it to version control 106 | 107 | ## ⚠️ Disclaimer 108 | 109 | This tool is for educational and personal use only. Always verify important financial information through official sources. Use at your own risk. 110 | 111 | ## 🤝 Contributing 112 | 113 | Contributions are welcome! Here's how you can help: 114 | 115 | 1. Fork the repository 116 | 2. Create a new branch (`git checkout -b feature/amazing-feature`) 117 | 3. Commit your changes (`git commit -m 'Add amazing feature'`) 118 | 4. Push to the branch (`git push origin feature/amazing-feature`) 119 | 5. Open a Pull Request 120 | 121 | ## 📄 License 122 | 123 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 124 | 125 | ## 🙏 Acknowledgments 126 | 127 | - [Web3.py](https://web3py.readthedocs.io/) for Ethereum interaction 128 | - [tqdm](https://github.com/tqdm/tqdm) for progress bars 129 | - The Ethereum community for inspiration 130 | 131 | ## 📬 Contact 132 | 133 | If you have any questions or suggestions, feel free to open an issue or reach out to the maintainers. 134 | 135 | --- 136 | ⭐ Found this project helpful? Give it a star! -------------------------------------------------------------------------------- /data/ethereum_address_balance.txt: -------------------------------------------------------------------------------- 1 | Address: 0x0000000070cbd78aBe3441c9700aF934eA297041 2 | ETH: 0.01802595 ETH 3 | -------------------------------------------------------------------------------- 4 | Address: 0x0000000000000000000000000000000000000000 5 | ETH: 13483.709672 ETH 6 | USDT: 96510.168307 USDT 7 | DAI: 12893.468335 DAI 8 | -------------------------------------------------------------------------------- 9 | -------------------------------------------------------------------------------- /data/ethereum_addresses.txt: -------------------------------------------------------------------------------- 1 | 0x0000000000000000000000000000000000000000 2 | 0x0000000070cbd78aBe3441c9700aF934eA297041 -------------------------------------------------------------------------------- /data/ethereum_and_token_balances.txt: -------------------------------------------------------------------------------- 1 | Address: 0x61755118c2B3eb37D76C57aB9b8f11F434B5F431 | Private Key: 0123456701234567012345670123456701234567012345670123456701234567 2 | ETH: 0.000000000000002005 ETH 3 | -------------------------------------------------------------------------------- 4 | -------------------------------------------------------------------------------- /data/ethereum_private_keys.txt: -------------------------------------------------------------------------------- 1 | 0123456701234567012345670123456701234567012345670123456701234567 2 | 522156c942354028c43a4c3858d2d57ae903e418bb8351c76ff8e478537a4645 -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | from src.scripts.ethereum_private_key_balance_checker import main as check_private_keys 4 | from src.scripts.ethereum_address_balance_checker import main as check_addresses 5 | 6 | def print_menu(): 7 | print("\n🔍 Ethereum Balance Checker") 8 | print("=" * 50) 9 | print("1. Check Private Keys") 10 | print("2. Check Addresses") 11 | print("3. Exit") 12 | print("=" * 50) 13 | 14 | def main(): 15 | while True: 16 | print_menu() 17 | choice = input("\nEnter your choice (1-3): ") 18 | 19 | if choice == "1": 20 | check_private_keys() 21 | elif choice == "2": 22 | check_addresses() 23 | elif choice == "3": 24 | print("\nGoodbye! 👋") 25 | sys.exit(0) 26 | else: 27 | print("\n⚠️ Invalid choice. Please try again.") 28 | 29 | input("\nPress Enter to continue...") 30 | 31 | if __name__ == "__main__": 32 | main() -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asimd/ethercheck/c3e23175abc5601c7e5da06979d03bdb3e00896e/src/__init__.py -------------------------------------------------------------------------------- /src/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asimd/ethercheck/c3e23175abc5601c7e5da06979d03bdb3e00896e/src/core/__init__.py -------------------------------------------------------------------------------- /src/core/balance_checker.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3 2 | from eth_utils import to_checksum_address 3 | from ..utils.config import ( 4 | MULTICALL_ADDRESS, MULTICALL_ABI, ERC20_ABI, 5 | TOKENS_TO_CHECK, TOKEN_DECIMALS, MIN_BALANCE 6 | ) 7 | 8 | class BalanceChecker: 9 | def __init__(self, w3): 10 | self.w3 = w3 11 | 12 | def format_balance(self, balance, decimals): 13 | if balance == 0: 14 | return None 15 | 16 | adjusted_balance = balance / (10 ** decimals) 17 | 18 | # Return any non-zero balance 19 | if adjusted_balance > 0: 20 | if adjusted_balance < 0.000000001: # Less than 1 Gwei 21 | formatted = f"{adjusted_balance:.18f}" 22 | return formatted 23 | elif adjusted_balance < 0.00001: 24 | formatted = f"{adjusted_balance:.12f}" 25 | return formatted 26 | elif adjusted_balance < 1: 27 | formatted = f"{adjusted_balance:.8f}" 28 | return formatted 29 | else: 30 | formatted = f"{adjusted_balance:.6f}" 31 | return formatted 32 | 33 | return None 34 | 35 | def get_all_balances(self, address): 36 | try: 37 | multicall = self.w3.eth.contract(address=MULTICALL_ADDRESS, abi=MULTICALL_ABI) 38 | 39 | # Fetch ETH balance 40 | eth_balance = self.w3.eth.get_balance(address) 41 | 42 | calls = [] 43 | tokens = list(TOKENS_TO_CHECK.items()) 44 | 45 | for token_name, token_address in tokens: 46 | token_contract = self.w3.eth.contract( 47 | address=to_checksum_address(token_address), 48 | abi=ERC20_ABI 49 | ) 50 | call_data = token_contract.functions.balanceOf(address)._encode_transaction_data() 51 | calls.append(( 52 | to_checksum_address(token_address), 53 | call_data 54 | )) 55 | 56 | # Execute the multicall to fetch token balances 57 | _, return_data = multicall.functions.aggregate(calls).call() 58 | 59 | # Process balances 60 | balances = {} 61 | 62 | # Add ETH balance 63 | eth_formatted = self.format_balance(eth_balance, 18) 64 | if eth_formatted: 65 | balances['ETH'] = eth_formatted 66 | 67 | # Process token balances 68 | for (token_name, _), data in zip(tokens, return_data): 69 | try: 70 | balance = int(data.hex(), 16) 71 | if balance > 0: 72 | decimals = TOKEN_DECIMALS.get(token_name, 18) 73 | formatted_balance = self.format_balance(balance, decimals) 74 | if formatted_balance: 75 | balances[token_name] = formatted_balance 76 | except ValueError as ve: 77 | print(f"Error parsing balance for {token_name}: {ve}") 78 | except Exception as e: 79 | print(f"Unexpected error for {token_name}: {e}") 80 | 81 | return balances 82 | except Exception as e: 83 | print(f"Error getting balances for {address}: {e}") 84 | return None -------------------------------------------------------------------------------- /src/core/output_formatter.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from ..utils.helpers import ensure_data_directory 4 | 5 | class OutputFormatter: 6 | @staticmethod 7 | def display_results(results, include_private_key=False): 8 | print("\n🎉 Found balances:") 9 | print("=" * 80) 10 | 11 | for result in results: 12 | if include_private_key: 13 | addr, balances, priv = result 14 | print(f"\n📍 Address: {addr} | Private Key: {priv}") 15 | else: 16 | addr, balances = result 17 | print(f"\n📍 Address: {addr}") 18 | 19 | # Display ETH balance first if it exists 20 | if 'ETH' in balances: 21 | print(f" • ETH: {balances['ETH']} ETH") 22 | 23 | # Display other token balances 24 | for token, amount in balances.items(): 25 | if token != 'ETH': 26 | print(f" • {token}: {amount} {token}") 27 | 28 | print("-" * 80) 29 | 30 | print(f"\n✨ Total addresses with balances: {len(results)}") 31 | print(f"💾 Results saved to: {OutputFormatter.get_output_filename(include_private_key)}") 32 | 33 | @staticmethod 34 | def save_results_to_txt(results, include_private_key=False): 35 | filename = OutputFormatter.get_output_filename(include_private_key) 36 | with open(filename, 'w') as txtfile: 37 | for result in results: 38 | if include_private_key: 39 | addr, balances, priv = result 40 | txtfile.write(f"Address: {addr} | Private Key: {priv}\n") 41 | else: 42 | addr, balances = result 43 | txtfile.write(f"Address: {addr}\n") 44 | 45 | if 'ETH' in balances: 46 | txtfile.write(f"ETH: {balances['ETH']} ETH\n") 47 | 48 | for token, amount in balances.items(): 49 | if token != 'ETH': 50 | txtfile.write(f"{token}: {amount} {token}\n") 51 | txtfile.write("-" * 80 + "\n") 52 | 53 | @staticmethod 54 | def get_output_filename(include_private_key): 55 | ensure_data_directory() # Make sure data directory exists 56 | return 'data/ethereum_and_token_balances.txt' if include_private_key else 'data/ethereum_address_balance.txt' -------------------------------------------------------------------------------- /src/core/web3_manager.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3 2 | import os 3 | import re 4 | from ..utils.exceptions import ConfigurationError 5 | 6 | class Web3Manager: 7 | def __init__(self): 8 | self.w3_instances = [] 9 | self.current_index = 0 10 | 11 | def is_valid_infura_id(self, infura_id): 12 | """Check if the ID is not a placeholder""" 13 | # Skip if it matches pattern 'id1', 'id2', etc. 14 | if re.match(r'^id\d+$', infura_id): 15 | return False 16 | # Skip if shorter than 32 chars (Infura Project IDs are 32+ chars) 17 | if len(infura_id) < 32: 18 | return False 19 | return True 20 | 21 | def initialize_web3_instances(self): 22 | """Initialize multiple Web3 instances from Infura Project IDs""" 23 | infura_ids = os.getenv('INFURA_PROJECT_IDS', '').split(',') 24 | infura_ids = [id.strip() for id in infura_ids if id.strip() and self.is_valid_infura_id(id.strip())] 25 | 26 | if not infura_ids: 27 | raise ConfigurationError( 28 | "\n⚠️ ERROR: No valid Infura Project IDs configured!\n" 29 | "Please add your Infura Project IDs to the .env file:\n" 30 | "1. Create a .env file in the project root\n" 31 | "2. Add your Infura Project IDs as:\n" 32 | " INFURA_PROJECT_IDS=your_id1,your_id2,your_id3\n" 33 | "3. Make sure the .env file is in the same directory as this script\n\n" 34 | "Current values are invalid or empty." 35 | ) 36 | 37 | for infura_id in infura_ids: 38 | infura_url = f'https://mainnet.infura.io/v3/{infura_id}' 39 | w3 = Web3(Web3.HTTPProvider( 40 | infura_url, 41 | request_kwargs={'timeout': 30, 'verify': False} 42 | )) 43 | 44 | try: 45 | w3.eth.block_number 46 | self.w3_instances.append(w3) 47 | except Exception as e: 48 | print(f"Warning: Could not initialize Web3 with ID {infura_id[:8]}...{infura_id[-4:]}: {e}") 49 | 50 | if not self.w3_instances: 51 | raise ConfigurationError( 52 | "\n⚠️ ERROR: Could not connect to any Infura endpoints.\n" 53 | "Please check your Project IDs and internet connection." 54 | ) 55 | 56 | def get_next_w3(self): 57 | """Get next available Web3 instance using round-robin""" 58 | if not self.w3_instances: 59 | raise ConfigurationError("No Web3 instances available") 60 | 61 | w3 = self.w3_instances[self.current_index] 62 | self.current_index = (self.current_index + 1) % len(self.w3_instances) 63 | return w3 -------------------------------------------------------------------------------- /src/scripts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asimd/ethercheck/c3e23175abc5601c7e5da06979d03bdb3e00896e/src/scripts/__init__.py -------------------------------------------------------------------------------- /src/scripts/ethereum_address_balance_checker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import os 4 | import warnings 5 | from concurrent.futures import ThreadPoolExecutor, as_completed 6 | from tqdm import tqdm 7 | from urllib3.exceptions import InsecureRequestWarning 8 | from eth_utils import to_checksum_address 9 | 10 | sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) 11 | 12 | from src.core.web3_manager import Web3Manager 13 | from src.core.balance_checker import BalanceChecker 14 | from src.core.output_formatter import OutputFormatter 15 | from src.utils.helpers import ensure_data_directory, validate_addresses_file 16 | from src.utils.exceptions import ConfigurationError 17 | 18 | # Suppress SSL warnings 19 | warnings.simplefilter('ignore', InsecureRequestWarning) 20 | 21 | def process_address(address, w3_manager, balance_checker): 22 | try: 23 | checksum_address = to_checksum_address(address) 24 | balances = balance_checker.get_all_balances(checksum_address) 25 | 26 | if balances and any(balances.values()): 27 | return (checksum_address, balances) 28 | except Exception as e: 29 | print(f"Error processing address: {e}") 30 | return None 31 | 32 | def main(): 33 | try: 34 | w3_manager = Web3Manager() 35 | w3_manager.initialize_web3_instances() 36 | 37 | addresses = validate_addresses_file() 38 | balance_checker = BalanceChecker(w3_manager.get_next_w3()) 39 | 40 | print(f"\nChecking {len(addresses)} addresses...") 41 | results = [] 42 | 43 | with ThreadPoolExecutor(max_workers=5) as executor: 44 | futures = [ 45 | executor.submit(process_address, addr, w3_manager, balance_checker) 46 | for addr in addresses 47 | ] 48 | 49 | with tqdm(total=len(addresses), desc="Checking addresses", unit="addr") as pbar: 50 | for future in as_completed(futures): 51 | result = future.result() 52 | if result: 53 | results.append(result) 54 | pbar.update(1) 55 | 56 | if results: 57 | OutputFormatter.save_results_to_txt(results) 58 | OutputFormatter.display_results(results) 59 | else: 60 | print("\n🔍 No addresses with balances found.") 61 | 62 | except ConfigurationError as e: 63 | print(str(e)) 64 | sys.exit(1) 65 | 66 | if __name__ == "__main__": 67 | main() -------------------------------------------------------------------------------- /src/scripts/ethereum_private_key_balance_checker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import re 3 | from web3 import Web3 4 | from eth_account import Account 5 | from concurrent.futures import ThreadPoolExecutor, as_completed 6 | from tqdm import tqdm 7 | import sys 8 | import os 9 | import warnings 10 | from urllib3.exceptions import InsecureRequestWarning 11 | from dotenv import load_dotenv 12 | from eth_utils import to_checksum_address 13 | 14 | sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) 15 | 16 | from src.core.web3_manager import Web3Manager 17 | from src.core.balance_checker import BalanceChecker 18 | from src.core.output_formatter import OutputFormatter 19 | from src.utils.helpers import ensure_data_directory, validate_private_keys_file 20 | from src.utils.exceptions import ConfigurationError 21 | from src.utils.config import ( 22 | MULTICALL_ADDRESS, MULTICALL_ABI, ERC20_ABI, 23 | TOKENS_TO_CHECK, TOKEN_DECIMALS, MIN_BALANCE 24 | ) 25 | 26 | # Suppress SSL warnings 27 | warnings.simplefilter('ignore', InsecureRequestWarning) 28 | 29 | # Load environment variables 30 | load_dotenv() 31 | 32 | # Global variables for Web3 instances 33 | w3_instances = [] 34 | current_w3_index = 0 35 | rate_limited_endpoints = set() 36 | terminate_script = False 37 | 38 | # Global set to keep track of processed keys 39 | processed_keys = set() 40 | 41 | def remove_processed_keys_from_file(filename): 42 | global processed_keys 43 | processed_count = len(processed_keys) 44 | processed_keys.clear() 45 | print(f"Found {processed_count} addresses with balances") 46 | 47 | def is_valid_private_key(key): 48 | return bool(re.fullmatch(r'[a-fA-F0-9]{64}', key)) 49 | 50 | def process_key(key, w3_manager, balance_checker): 51 | try: 52 | if not is_valid_private_key(key): 53 | return None 54 | 55 | account = Account.from_key(key) 56 | address = account.address 57 | 58 | balances = balance_checker.get_all_balances(address) 59 | 60 | # Only return if balances is not None and not empty 61 | if balances and len(balances) > 0: 62 | processed_keys.add(key) 63 | return (address, balances, key) 64 | 65 | except Exception as e: 66 | print(f"Error processing key: {e}") 67 | return None 68 | 69 | def main(): 70 | try: 71 | w3_manager = Web3Manager() 72 | w3_manager.initialize_web3_instances() 73 | 74 | private_keys = validate_private_keys_file() 75 | balance_checker = BalanceChecker(w3_manager.get_next_w3()) 76 | 77 | print(f"\nChecking {len(private_keys)} private keys...") 78 | results = [] 79 | 80 | with ThreadPoolExecutor(max_workers=5) as executor: 81 | futures = [ 82 | executor.submit(process_key, key, w3_manager, balance_checker) 83 | for key in private_keys 84 | ] 85 | 86 | with tqdm(total=len(private_keys), desc="Checking keys", unit="key") as pbar: 87 | for future in as_completed(futures): 88 | result = future.result() 89 | if result: 90 | results.append(result) 91 | pbar.update(1) 92 | if terminate_script: 93 | break 94 | 95 | if results: 96 | OutputFormatter.save_results_to_txt(results, include_private_key=True) 97 | OutputFormatter.display_results(results, include_private_key=True) 98 | else: 99 | print("\n🔍 No addresses with balances found.") 100 | 101 | except ConfigurationError as e: 102 | print(str(e)) 103 | sys.exit(1) 104 | 105 | if __name__ == "__main__": 106 | main() -------------------------------------------------------------------------------- /src/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asimd/ethercheck/c3e23175abc5601c7e5da06979d03bdb3e00896e/src/utils/__init__.py -------------------------------------------------------------------------------- /src/utils/config.py: -------------------------------------------------------------------------------- 1 | from web3 import Web3 2 | from dotenv import load_dotenv 3 | import json 4 | import os 5 | 6 | # Load environment variables 7 | load_dotenv() 8 | 9 | # Multicall settings 10 | MULTICALL_ADDRESS = os.getenv('MULTICALL_ADDRESS', '0xeefBa1e63905eF1D7ACbA5a8513c70307C1cE441') 11 | MIN_BALANCE = float(os.getenv('MIN_BALANCE', '0.000000000000001')) # 1 wei 12 | 13 | # ABIs 14 | MULTICALL_ABI = json.loads(os.getenv('MULTICALL_ABI', '''[{ 15 | "constant":false, 16 | "inputs":[{"components":[{"name":"target","type":"address"},{"name":"callData","type":"bytes"}],"name":"calls","type":"tuple[]"}], 17 | "name":"aggregate", 18 | "outputs":[{"name":"blockNumber","type":"uint256"},{"name":"returnData","type":"bytes[]"}], 19 | "type":"function" 20 | }]''')) 21 | 22 | ERC20_ABI = json.loads(os.getenv('ERC20_ABI', '''[{ 23 | "constant":true, 24 | "inputs":[{"name":"_owner","type":"address"}], 25 | "name":"balanceOf", 26 | "outputs":[{"name":"balance","type":"uint256"}], 27 | "type":"function" 28 | }]''')) 29 | 30 | # Token configurations 31 | TOKENS_TO_CHECK = { 32 | 'USDT': os.getenv('USDT_ADDRESS', '0xdAC17F958D2ee523a2206206994597C13D831ec7'), 33 | 'USDC': os.getenv('USDC_ADDRESS', '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'), 34 | 'BNB': os.getenv('BNB_ADDRESS', '0xB8c77482e45F1F44dE1745F52C74426C631bDD52'), 35 | 'BUSD': os.getenv('BUSD_ADDRESS', '0x4Fabb145d64652a948d72533023f6E7A623C7C53'), 36 | 'LINK': os.getenv('LINK_ADDRESS', '0x514910771AF9Ca656af840dff83E8264EcF986CA'), 37 | 'MATIC': os.getenv('MATIC_ADDRESS', '0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0'), 38 | 'UNI': os.getenv('UNI_ADDRESS', '0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984'), 39 | 'WBTC': os.getenv('WBTC_ADDRESS', '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599'), 40 | 'DAI': os.getenv('DAI_ADDRESS', '0x6B175474E89094C44Da98b954EedeAC495271d0F'), 41 | 'SHIB': os.getenv('SHIB_ADDRESS', '0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE') 42 | } 43 | 44 | TOKEN_DECIMALS = { 45 | 'USDT': int(os.getenv('USDT_DECIMALS', '6')), 46 | 'USDC': int(os.getenv('USDC_DECIMALS', '6')), 47 | 'BNB': int(os.getenv('BNB_DECIMALS', '18')), 48 | 'BUSD': int(os.getenv('BUSD_DECIMALS', '18')), 49 | 'LINK': int(os.getenv('LINK_DECIMALS', '18')), 50 | 'MATIC': int(os.getenv('MATIC_DECIMALS', '18')), 51 | 'UNI': int(os.getenv('UNI_DECIMALS', '18')), 52 | 'WBTC': int(os.getenv('WBTC_DECIMALS', '8')), 53 | 'DAI': int(os.getenv('DAI_DECIMALS', '18')), 54 | 'SHIB': int(os.getenv('SHIB_DECIMALS', '18')) 55 | } -------------------------------------------------------------------------------- /src/utils/exceptions.py: -------------------------------------------------------------------------------- 1 | class ConfigurationError(Exception): 2 | """Raised when there's a configuration issue with the script""" 3 | pass -------------------------------------------------------------------------------- /src/utils/helpers.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | def ensure_data_directory(): 5 | """Create data directory if it doesn't exist""" 6 | os.makedirs('data', exist_ok=True) 7 | 8 | def validate_private_keys_file(): 9 | """Validate and create private keys input file if it doesn't exist""" 10 | input_file = 'data/ethereum_private_keys.txt' 11 | ensure_data_directory() 12 | 13 | if not os.path.exists(input_file): 14 | with open(input_file, 'w') as f: 15 | f.write("# Add your Ethereum private keys here (one per line)\n") 16 | f.write("# Example:\n") 17 | f.write("# 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\n") 18 | print(f"\n⚠️ Created {input_file}") 19 | print("Please add your Ethereum private keys to this file and run the script again.") 20 | sys.exit(1) 21 | 22 | with open(input_file, 'r') as f: 23 | keys = [line.strip() for line in f if line.strip() and not line.startswith('#')] 24 | 25 | if not keys: 26 | print(f"\n⚠️ No private keys found in {input_file}") 27 | print("Please add your Ethereum private keys to this file and run the script again.") 28 | sys.exit(1) 29 | 30 | return keys 31 | 32 | def validate_addresses_file(): 33 | """Validate and create addresses input file if it doesn't exist""" 34 | input_file = 'data/ethereum_addresses.txt' 35 | ensure_data_directory() 36 | 37 | if not os.path.exists(input_file): 38 | with open(input_file, 'w') as f: 39 | f.write("# Add your Ethereum addresses here (one per line)\n") 40 | f.write("# Example:\n") 41 | f.write("# 0x742d35Cc6634C0532925a3b844Bc454e4438f44e\n") 42 | print(f"\n⚠️ Created {input_file}") 43 | print("Please add your Ethereum addresses to this file and run the script again.") 44 | sys.exit(1) 45 | 46 | with open(input_file, 'r') as f: 47 | addresses = [line.strip() for line in f if line.strip() and not line.startswith('#')] 48 | 49 | if not addresses: 50 | print(f"\n⚠️ No addresses found in {input_file}") 51 | print("Please add your Ethereum addresses to this file and run the script again.") 52 | sys.exit(1) 53 | 54 | return addresses --------------------------------------------------------------------------------