Files
proxmox/scripts/verify-ethereum-mainnet.py

263 lines
8.9 KiB
Python
Raw Permalink Normal View History

#!/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()