0% found this document useful (0 votes)
7 views

etherscan.io multichain checker

This document is a Python script that interacts with the Etherscan API to retrieve cryptocurrency balances across multiple supported chains using mnemonic phrases. It includes functions for generating public addresses, querying balances, saving data to a file, and managing rate limits. The main function orchestrates the process by reading decrypted wallet mnemonics and compiling a portfolio of balances for each address.

Uploaded by

courtnayfuchsia
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views

etherscan.io multichain checker

This document is a Python script that interacts with the Etherscan API to retrieve cryptocurrency balances across multiple supported chains using mnemonic phrases. It includes functions for generating public addresses, querying balances, saving data to a file, and managing rate limits. The main function orchestrates the process by reading decrypted wallet mnemonics and compiling a portfolio of balances for each address.

Uploaded by

courtnayfuchsia
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 4

import time

import json
import requests
from web3 import Web3
from eth_account import Account
from pathlib import Path

# Enable mnemonic-based account features in eth-account (Web3)


Account.enable_unaudited_hdwallet_features()

# Base URL for Etherscan API V2


ETHERSCAN_API_BASE_URL = "https://api.etherscan.io/v2/api"
API_KEY = "" # Replace with your Etherscan-compatible API key

# List of supported chains (add as needed)


SUPPORTED_CHAINS = {
"ethereum_mainnet": 1,
"bnb_mainnet": 56,
"polygon_mainnet": 137,
"arbitrum_one": 42161,
"optimism_mainnet": 10,
"mantle_mainnet": 5000,
"base_mainnet": 8453,
"avalanche_mainnet": 43114,
"gnosis_mainnet": 100,
"fantom_mainnet": 250,
"zkSync_mainnet": 324
# Add additional chains here
}

def generate_public_address(mnemonic):
"""
Generate Ethereum public address from a mnemonic phrase.
"""
try:
wallet_account = Account.from_mnemonic(mnemonic)
return wallet_account.address
except Exception as e:
print(f"[ERROR] Failed to generate public address: {e}")
return None

def query_balance_v2(address, chain_name, chain_id, counter):


"""
Query the Etherscan API V2 for the balance of an address on a specific chain.
Handles rate limits and logs raw responses for debugging.
"""
try:
print(f"[INFO] Querying {chain_name.capitalize()} (Chain ID: {chain_id})
for address: {address}")

params = {
"chainid": chain_id,
"module": "account",
"action": "balance",
"address": address,
"tag": "latest",
"apikey": API_KEY,
}

# Make the API call


response = requests.get(ETHERSCAN_API_BASE_URL, params=params)

# Debugging: Log raw response for troubleshooting


print(f"[DEBUG] Raw response for {chain_name.capitalize()} (Chain ID:
{chain_id}): {response.text}")

# Check for empty or invalid JSON


if response.status_code != 200 or not response.text:
print(f"[WARNING] {chain_name.capitalize()} API returned no data or an
invalid response.")
return {"error": "Empty or invalid response"}

response_data = response.json()

if response_data.get("status") != "1":
print(f"[WARNING] {chain_name.capitalize()} API returned an error:
{response_data.get('message')}")
return {"error": response_data.get("message")} # Add error for
tracking

# Convert the balance from Wei to Ether (or chain native token units)
balance_wei = int(response_data.get("result", "0"))
balance_eth = Web3.from_wei(balance_wei, "ether")
return {"balance": float(balance_eth)} # Convert to float for JSON
serialization

except ValueError as e:
# JSONDecodeError sometimes manifests as ValueError
print(f"[ERROR] Failed to parse JSON response from
{chain_name.capitalize()} API: {e}")
return {"error": "Invalid or empty JSON response"}
except Exception as e:
print(f"[ERROR] Failed to query {chain_name.capitalize()} API: {e}")
return {"error": str(e)}

finally:
# Add a small delay every 2-3 checks to avoid hitting rate limits
if counter % 3 == 0: # Pause after every 3 requests
print("[INFO] Adding a 0.5 second delay to avoid throttling...")
time.sleep(0.5)

def query_multichain_portfolio_v2(address):
"""
Query balances across all chains provided in SUPPORTED_CHAINS.
Implements rate limiting to stay within 5 calls per second allowed by
Etherscan's API.
"""
portfolio = {}
counter = 0 # Counter to manage delays

for chain_name, chain_id in SUPPORTED_CHAINS.items():


counter += 1
portfolio[chain_name] = query_balance_v2(address, chain_name, chain_id,
counter)
return portfolio

def save_to_file(data, output_file):


"""
Save the portfolio data to a JSON file.
"""
try:
with open(output_file, "w", encoding="utf-8") as file:
json.dump(data, file, indent=4)
print(f"[INFO] Portfolio saved to {output_file}")
except Exception as e:
print(f"[ERROR] Failed to save portfolio to file: {e}")

def main():
"""
Main program for retrieving balances across multiple chains via Etherscan V2
with rate limiting.
"""
# Load decrypted wallets from the previously generated file
decrypted_wallets_file = Path("decrypted_mnemonics.json")
if not decrypted_wallets_file.exists():
print("[ERROR] Decrypted wallets file not found!")
return

# Read the decrypted wallets


with open(decrypted_wallets_file, "r", encoding="utf-8") as file:
decrypted_wallets = json.load(file)

output_file = Path("multichain_portfolio_v2.json")
all_portfolio_data = []

for wallet in decrypted_wallets:


mnemonic = wallet.get("mnemonic")
subfolder = wallet.get("subfolder")

if not mnemonic:
print(f"[WARNING] Missing mnemonic for wallet in folder: {subfolder}")
continue

# Generate public address


public_address = generate_public_address(mnemonic)
if public_address:
print(f"[INFO] Public Address: {public_address}")

# Query multi-chain portfolio via V2 API


portfolio_data = query_multichain_portfolio_v2(public_address)
all_portfolio_data.append({
"mnemonic": mnemonic,
"subfolder": subfolder,
"address": public_address,
"portfolio": portfolio_data,
})

# Save results to output file


save_to_file(all_portfolio_data, output_file)
if __name__ == "__main__":
main()

You might also like