Ищу чекер баланса BCH адрессов

Статус
Закрыто для дальнейших ответов.
Python:
#!/usr/bin/env python3
"""
Bitcoin Cash (BCH) Bulk Address Checker with Balance
---------------------------------------------------
This script validates multiple Bitcoin Cash addresses at once and checks their balances.
It supports Legacy, CashAddr, and SLP address formats.

Usage:
1. Run the script
2. Enter addresses or specify an input file
3. View validation results and balances
"""

import re
import sys
import json
import argparse
import time
import requests
from typing import Dict, List, Optional, Tuple


class BCHAddressChecker:
    def __init__(self, api="blockchair"):
        """
        Initialize the BCH address checker with the specified API.
       
        Args:
            api: API provider to use ('blockchair' or 'blockchain')
        """
        # API selection for querying BCH address data
        self.api = api.lower()
       
        # Regular expression patterns for different BCH address formats
        self.legacy_pattern = re.compile(r'^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$')
        self.cashaddr_pattern = re.compile(r'^(bitcoincash:)?[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{42}$')
        self.slp_pattern = re.compile(r'^(simpleledger:)?[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{42}$')

    def validate_address(self, address: str) -> Optional[Dict]:
        """
        Validate a single BCH address and determine its format.
       
        Args:
            address: The BCH address to validate
           
        Returns:
            Dictionary with validation results or None if empty address
        """
        # Remove whitespace and normalize
        address = address.strip()
       
        # Skip empty lines
        if not address:
            return None
           
        # Remove prefix for pattern matching if present
        address_without_prefix = address
        if address.startswith('bitcoincash:'):
            address_without_prefix = address[12:]
        elif address.startswith('simpleledger:'):
            address_without_prefix = address[13:]
           
        # Check against patterns
        is_legacy_valid = bool(self.legacy_pattern.match(address))
        is_cashaddr_valid = (
            bool(self.cashaddr_pattern.match(address)) or
            bool(self.cashaddr_pattern.match(f'bitcoincash:{address_without_prefix}'))
        )
        is_slp_valid = (
            bool(self.slp_pattern.match(address)) or
            bool(self.slp_pattern.match(f'simpleledger:{address_without_prefix}'))
        )
       
        is_valid = is_legacy_valid or is_cashaddr_valid or is_slp_valid
       
        # Determine format type
        format_type = 'Unknown'
        if is_legacy_valid:
            format_type = 'Legacy'
        elif is_cashaddr_valid:
            format_type = 'CashAddr'
        elif is_slp_valid:
            format_type = 'SLP'
           
        return {
            'address': address,
            'is_valid': is_valid,
            'format': format_type if is_valid else 'Invalid',
            'balance': None  # Will be populated later
        }
   
    def get_address_balance(self, address: str) -> float:
        """
        Fetch the balance of a BCH address from the blockchain explorer API.
       
        Args:
            address: The BCH address to check
           
        Returns:
            Balance in BCH as a float, or None if there was an error
        """
        try:
            # Ensure the address has the correct format for the API
            if address.startswith('bitcoincash:'):
                formatted_address = address
            elif address.startswith('simpleledger:'):
                # Convert SLP to regular CashAddr for balance check
                formatted_address = f"bitcoincash:{address[13:]}"
            elif self.legacy_pattern.match(address):
                # For legacy addresses, use as is
                formatted_address = address
            else:
                # For CashAddr without prefix, add the prefix
                formatted_address = f"bitcoincash:{address}"
           
            if self.api == 'blockchair':
                # Use Blockchair API to get address details
                url = f"https://api.blockchair.com/bitcoin-cash/dashboards/address/{formatted_address}"
                response = requests.get(url)
               
                if response.status_code == 200:
                    data = response.json()
                    if data.get('context', {}).get('code') == 200:
                        # Get the address data
                        address_data = data.get('data', {}).get(formatted_address, {})
                        # Balance is returned in satoshis, convert to BCH
                        balance_sats = address_data.get('address', {}).get('balance', 0)
                        return balance_sats / 100000000  # Convert satoshis to BCH
                    else:
                        print(f"API Error: {data.get('context', {}).get('error', 'Unknown error')}")
                        return None
                else:
                    print(f"API Error ({response.status_code}): {response.text}")
                    return None
                   
            elif self.api == 'blockchain':
                # Use Blockchain.com API to get address details
                url = f"https://blockchain.info/rawaddr/{formatted_address}?coin=bch"
                response = requests.get(url)
               
                if response.status_code == 200:
                    data = response.json()
                    # Balance is returned in satoshis, convert to BCH
                    balance_sats = data.get('final_balance', 0)
                    return balance_sats / 100000000  # Convert satoshis to BCH
                else:
                    print(f"API Error ({response.status_code}): {response.text}")
                    return None
           
            else:
                print(f"Unknown API provider: {self.api}")
                return None
               
        except Exception as e:
            print(f"Error fetching balance for {address}: {e}")
            return None
   
    def batch_get_balances(self, addresses: List[str], batch_size=5) -> Dict[str, float]:
        """
        Fetch balances for multiple addresses in batches to avoid rate limiting.
       
        Args:
            addresses: List of BCH addresses
            batch_size: Number of addresses to process in each batch
           
        Returns:
            Dictionary mapping addresses to their balances
        """
        balances = {}
        total_addresses = len(addresses)
       
        for i in range(0, total_addresses, batch_size):
            batch = addresses[i:i+batch_size]
            print(f"Fetching balances for addresses {i+1}-{min(i+batch_size, total_addresses)} of {total_addresses}...")
           
            for address in batch:
                retries = 3
                while retries > 0:
                    try:
                        balance = self.get_address_balance(address)
                        balances[address] = balance
                        # Add a delay to avoid rate limiting
                        time.sleep(1)  # Increased delay for more reliable API responses
                        break
                    except requests.exceptions.RequestException:
                        print(f"Network error, retrying... ({retries} attempts left)")
                        retries -= 1
                        time.sleep(2)  # Wait longer between retries
               
                if retries == 0:
                    print(f"Failed to fetch balance for {address} after multiple attempts.")
                    balances[address] = None
               
        return balances
       
    def validate_addresses(self, addresses: List[str], check_balance=True, batch_size=5) -> List[Dict]:
        """
        Validate multiple BCH addresses and optionally check their balances.
       
        Args:
            addresses: List of BCH addresses to validate
            check_balance: Whether to fetch balances from the blockchain
            batch_size: Number of addresses to process in each batch
           
        Returns:
            List of dictionaries with validation results and balances
        """
        # First validate all addresses
        results = []
        valid_addresses = []
       
        for address in addresses:
            result = self.validate_address(address)
            if result:
                results.append(result)
                if result['is_valid']:
                    valid_addresses.append(result['address'])
       
        # Then fetch balances for valid addresses if requested
        if check_balance and valid_addresses:
            balances = self.batch_get_balances(valid_addresses, batch_size)
           
            # Update results with balance information
            for result in results:
                if result['is_valid']:
                    result['balance'] = balances.get(result['address'])
       
        return results
       
    def print_results(self, results: List[Dict]) -> None:
        """
        Print validation results and balances in a formatted table.
       
        Args:
            results: List of validation result dictionaries
        """
        # Count valid and invalid addresses
        valid_count = sum(1 for result in results if result['is_valid'])
        invalid_count = len(results) - valid_count
       
        # Calculate total balance
        total_balance = sum(result['balance'] or 0 for result in results if result['is_valid'] and result['balance'] is not None)
       
        # Print statistics
        print("\n===== RESULTS =====")
        print(f"Total addresses: {len(results)}")
        print(f"Valid addresses: {valid_count}")
        print(f"Invalid addresses: {invalid_count}")
        print(f"Total balance: {total_balance:.8f} BCH")
        print("===================")
       
        # Print table header
        if any(result['balance'] is not None for result in results):
            print("\n{:<45} {:<10} {:<12} {:<15}".format("ADDRESS", "STATUS", "FORMAT", "BALANCE (BCH)"))
            print("="*85)
           
            # Print each result
            for result in results:
                status = "Valid" if result['is_valid'] else "Invalid"
                balance = f"{result['balance']:.8f}" if result['balance'] is not None else "N/A"
               
                print("{:<45} {:<10} {:<12} {:<15}".format(
                    result['address'][:43] + '...' if len(result['address']) > 45 else result['address'],
                    status,
                    result['format'],
                    balance if result['is_valid'] else "—"
                ))
        else:
            # Print table without balance column if no balances were fetched
            print("\n{:<50} {:<10} {:<10}".format("ADDRESS", "STATUS", "FORMAT"))
            print("="*70)
           
            for result in results:
                status = "Valid" if result['is_valid'] else "Invalid"
                print("{:<50} {:<10} {:<10}".format(
                    result['address'][:48] + '...' if len(result['address']) > 50 else result['address'],
                    status,
                    result['format']
                ))


def read_addresses_from_file(file_path: str) -> List[str]:
    """
    Read BCH addresses from a file, one address per line.
   
    Args:
        file_path: Path to the file containing addresses
       
    Returns:
        List of addresses
    """
    try:
        with open(file_path, 'r') as file:
            return [line.strip() for line in file.readlines()]
    except Exception as e:
        print(f"Error reading file: {e}")
        return []


def main() -> None:
    """Main function to run the BCH address checker with balance."""
    parser = argparse.ArgumentParser(description='Bitcoin Cash (BCH) Bulk Address Checker with Balance')
    parser.add_argument('-f', '--file', help='Path to file containing BCH addresses (one per line)')
    parser.add_argument('-a', '--addresses', help='Comma-separated list of BCH addresses')
    parser.add_argument('--no-balance', action='store_true', help='Skip balance checking')
    parser.add_argument('--api', choices=['blockchair', 'blockchain'], default='blockchair',
                        help='API provider to use (blockchair or blockchain)')
    parser.add_argument('--batch-size', type=int, default=5, help='Number of addresses to process in each batch')
    args = parser.parse_args()
   
    print(f"Using {args.api} API for balance checking")
    checker = BCHAddressChecker(api=args.api)
    addresses = []
   
    # Get addresses from file, command-line arguments, or user input
    if args.file:
        addresses = read_addresses_from_file(args.file)
    elif args.addresses:
        addresses = [addr.strip() for addr in args.addresses.split(',')]
    else:
        print("Enter BCH addresses (one per line, press Ctrl+D or Ctrl+Z when done):")
        try:
            addresses = [line.strip() for line in sys.stdin.readlines()]
        except KeyboardInterrupt:
            print("\nInput interrupted.")
            return
   
    # Validate addresses and check balances
    if addresses:
        print(f"Processing {len(addresses)} addresses...")
        results = checker.validate_addresses(
            addresses,
            check_balance=not args.no_balance,
            batch_size=args.batch_size
        )
        checker.print_results(results)
    else:
        print("No addresses provided. Exiting.")


if __name__ == "__main__":
    main()
сделано на скорую руку с помощью ИИ
запуск python bch_address_checker_.py -f addresses.txt
===== RESULTS =====
Total addresses: 28
Valid addresses: 28
Invalid addresses: 0
Total balance: 5056820.43172305 BCH
===================

ADDRESS STATUS FORMAT BALANCE (BCH)
=====================================================================================
qrmfkegyf83zh5kauzwgygf82sdahd5a55x9wse7ve Valid CashAddr 946800.97440087
qre24q38ghy6k3pegpyvtxahu8q8hqmxmqqn28z85p Valid CashAddr 696238.35714644
qrwcmu77ltr3dn0hymy58gvcg3qys4hp2qn4e0z839 Valid CashAddr 500546.25875301
qqwj7gnylkerx7cgsvma03f0e7t3k24g8gvvyyk82r Valid CashAddr 365986.74056161
qpug04qa7xwkg44mytstlcpgelld2ucpg5df99ph8e Valid CashAddr 283001.39419370
qpz8ucuucyuf8slyntzvktyaczs20f9edqr7mwzsl9 Valid CashAddr 248949.04188410
qzh96ajgz9ufvs4zmrzq8jr95s7nm0qlnclvk3vpe0 Valid CashAddr 245378.57552000
qzdhcrevp5kqp5e732p4cupy6anxexncxqqgktkcq2 Valid CashAddr 184102.12569000
qrs4zx5dcrae0ra6kndpaf3ccwe8vp39pqtaf69wem Valid CashAddr 162964.03001000
qz55fgk3d6mdlyyxs4gshqs5xsjtkuud4sqkt787ns Valid CashAddr 129707.83302316
qp028nlln35nwnv5a9dssw9w57z5n765rgenr3suw6 Valid CashAddr 39077.09009048
qzv7tmnunqa70hkvh4ca5vvmamgcknfumqqs87vegr Valid CashAddr 117376.52653586
qrf8kceax3a0j2r45346gkp2leqa2luuxc6hku4rfn Valid CashAddr 114121.09166390
qr6pg2xy87shw2jtvz4w2d52e9y35wke8gkw0rec8j Valid CashAddr 100000.00152768
qrd4ln7x6mw823uyyc78tf2hf34w6uukcg8fakl20d Valid CashAddr 91311.75404482
qzstp4swtxg40rkn0j769vta3vkwyw4jj5fmdl2vtm Valid CashAddr 79957.17444986
qzdgfvt0c33nwvd2qwc9jwhlkgsp75sluqxfa2vk5h Valid CashAddr 77075.95771410
qrsey6ert2y7lcuyp2d0mlaewlv9ntn09gc5rjqx0n Valid CashAddr 75722.88520627
qpsrsmpe02jja7nwkrvkkg5u208y2wndz5na50h3ve Valid CashAddr 74435.60003330
qrv32f9l3gznqhxjsh8tzd6eu4sgem4375z8nlnd4r Valid CashAddr 69390.87896558
qqrkjml7h3ymnc7ydd9m5r9s9hnqectmluwpxezd9a Valid CashAddr 69370.18366005
qp64wu4ac8gx08jgk4nlg75828mzphdhe56a2y2art Valid CashAddr 62601.87049908
qry0exa64ehduuj92c5glm58yck3w4s6nyferyygfz Valid CashAddr 60969.04238628
qrt5m62lv4ue0yl3dwg7mzs4yygx2tf7cq7hgt858h Valid CashAddr 53880.07090088
qr8qfn76e5jm5qg0956zump75epay05evgsvemh6zq Valid CashAddr 53872.94508000
qq8lk3eaehr94rdqstg3a49q4tljyj7jy5q6p6vh8m Valid CashAddr 51573.77444331
qrcc584eyw2lssz70yhagutsv9tag7k08cntm038m0 Valid CashAddr 51247.20116504
qzzj98n0lymzg09aer6re56hskg933254svepgyfew Valid CashAddr 51161.05217367
 
Статус
Закрыто для дальнейших ответов.
Верх