#!/usr/bin/env python3 """ Automated Ethereum Mainnet Contract Verification Script Handles Standard JSON Input verification via Etherscan API """ import json import sys import time import urllib.parse import urllib.request from pathlib import Path # Colors for terminal output class Colors: RED = '\033[0;31m' GREEN = '\033[0;32m' YELLOW = '\033[1;33m' BLUE = '\033[0;34m' CYAN = '\033[0;36m' NC = '\033[0m' # No Color def log_info(msg): print(f"{Colors.BLUE}[INFO]{Colors.NC} {msg}") def log_success(msg): print(f"{Colors.GREEN}[✓]{Colors.NC} {msg}") def log_warn(msg): print(f"{Colors.YELLOW}[⚠]{Colors.NC} {msg}") def log_error(msg): print(f"{Colors.RED}[✗]{Colors.NC} {msg}") def log_step(msg): print(f"{Colors.CYAN}[STEP]{Colors.NC} {msg}") # Contract configuration CONTRACTS = { "0x89dd12025bfCD38A168455A44B400e913ED33BE2": { "name": "CCIPWETH9Bridge", "constructor_args": "0x00000000000000000000000080226fc0ee2b096224eeac085bb9a8cba1146f7d000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca", "standard_json_file": "docs/CCIPWETH9Bridge_standard_json.json" } } def load_env_file(env_path): """Load environment variables from .env file""" env_vars = {} try: with open(env_path, 'r') as f: for line in f: line = line.strip() if line and not line.startswith('#') and '=' in line: key, value = line.split('=', 1) env_vars[key.strip()] = value.strip().strip('"').strip("'") except FileNotFoundError: log_error(f".env file not found: {env_path}") sys.exit(1) return env_vars def check_verification_status(address, api_key): """Check if contract is already verified on Etherscan""" url = f"https://api.etherscan.io/api?module=contract&action=getsourcecode&address={address}&apikey={api_key}" try: with urllib.request.urlopen(url) as response: data = json.loads(response.read().decode()) if data.get('status') == '1' and data.get('result'): result = data['result'][0] source_code = result.get('SourceCode', '') # Check if source code exists (verified) if source_code and source_code not in ['', '{{']: return True return False except Exception as e: log_warn(f"Could not check verification status: {e}") return False def verify_contract(address, contract_info, api_key): """Verify contract using Standard JSON Input via Etherscan API""" contract_name = contract_info['name'] constructor_args = contract_info['constructor_args'] standard_json_file = contract_info['standard_json_file'] # Load Standard JSON file project_root = Path(__file__).parent.parent json_path = project_root / standard_json_file if not json_path.exists(): log_error(f"Standard JSON file not found: {json_path}") return False log_step(f"Loading Standard JSON from {json_path}") try: with open(json_path, 'r') as f: standard_json = json.load(f) except Exception as e: log_error(f"Failed to load Standard JSON: {e}") return False # Convert to compact JSON string json_str = json.dumps(standard_json, separators=(',', ':')) # Prepare API request api_url = "https://api.etherscan.io/api" params = { 'module': 'contract', 'action': 'verifysourcecode', 'apikey': api_key, 'contractaddress': address, 'codeformat': 'solidity-standard-json-input', 'contractname': contract_name, 'compilerversion': 'v0.8.20+commit.a1b79de6', 'optimizationUsed': '1', 'runs': '200', 'constructorArguements': constructor_args, 'sourceCode': json_str } # Encode parameters data = urllib.parse.urlencode(params).encode('utf-8') log_step("Submitting verification request to Etherscan...") try: req = urllib.request.Request(api_url, data=data) with urllib.request.urlopen(req, timeout=30) as response: result = json.loads(response.read().decode()) if result.get('status') == '1': guid = result.get('result', '') if guid and guid != 'null': log_success(f"Verification submitted successfully!") log_info(f"GUID: {guid}") log_info(f"Check status: https://etherscan.io/address/{address}#code") return True else: log_error(f"Verification failed: {result.get('result', 'Unknown error')}") return False else: error_msg = result.get('result', 'Unknown error') log_error(f"Verification failed: {error_msg}") return False except urllib.error.HTTPError as e: log_error(f"HTTP Error: {e}") try: error_body = e.read().decode() log_info(f"Error details: {error_body}") except: pass return False except Exception as e: log_error(f"Request failed: {e}") return False def monitor_verification(address, api_key, max_attempts=12): """Monitor verification status after submission""" log_info("Waiting for verification to complete...") for attempt in range(1, max_attempts + 1): time.sleep(5) if check_verification_status(address, api_key): log_success("Contract is now verified!") return True log_info(f"Attempt {attempt}/{max_attempts}: Still processing...") log_warn("Verification may still be processing. Check Etherscan manually.") return False def main(): log_info("=" * 50) log_info("Ethereum Mainnet Contract Verification") log_info("Using Standard JSON Input Method") log_info("=" * 50) log_info("") # Load environment variables project_root = Path(__file__).parent.parent source_project = project_root.parent / "smom-dbis-138" env_path = source_project / ".env" if not env_path.exists(): log_error(f".env file not found: {env_path}") sys.exit(1) env_vars = load_env_file(env_path) api_key = env_vars.get('ETHERSCAN_API_KEY') if not api_key: log_error("ETHERSCAN_API_KEY not found in .env file") sys.exit(1) # Process each contract verified_count = 0 already_verified_count = 0 failed_count = 0 for address, contract_info in CONTRACTS.items(): log_info("") log_info("=" * 50) log_info(f"Verifying: {contract_info['name']}") log_info(f"Address: {address}") log_info("=" * 50) log_info("") # Check if already verified log_step("Checking current verification status...") if check_verification_status(address, api_key): log_success("Contract is already verified on Etherscan!") log_info(f"View contract: https://etherscan.io/address/{address}#code") already_verified_count += 1 continue log_info("Contract is not yet verified") # Verify contract log_step("Submitting verification request...") if verify_contract(address, contract_info, api_key): log_info("") log_step("Monitoring verification status...") if monitor_verification(address, api_key): verified_count += 1 else: log_warn("Verification submitted but status unclear. Check Etherscan manually.") else: failed_count += 1 log_info("") log_info("Manual verification steps:") log_info(f"1. Go to: https://etherscan.io/address/{address}#code") log_info("2. Click 'Contract' tab → 'Verify and Publish'") log_info("3. Select 'Standard JSON Input'") log_info(f"4. Upload: {project_root / contract_info['standard_json_file']}") log_info(f"5. Enter constructor args: {contract_info['constructor_args']}") log_info("6. Submit") # Summary log_info("") log_info("=" * 50) log_info("Verification Summary") log_info("=" * 50) log_info("") log_success(f"Already Verified: {already_verified_count}") log_success(f"Newly Verified: {verified_count}") log_error(f"Failed: {failed_count}") log_info("") total = already_verified_count + verified_count + failed_count log_info(f"Total Contracts: {total}") if failed_count == 0: log_success("All contracts processed successfully!") else: log_warn("Some contracts require manual verification.") if __name__ == "__main__": main()