Files
proxmox/scripts/verify-ethereum-mainnet.py
defiQUG dbd517b279 Sync workspace: config, docs, scripts, CI, operator rules, and submodule pointers.
- Update dbis_core, cross-chain-pmm-lps, explorer-monorepo, metamask-integration, pr-workspace/chains
- Omit embedded publish git dirs and empty placeholders from index

Made-with: Cursor
2026-04-12 06:12:20 -07:00

304 lines
10 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Ethereum Mainnet contract verification helper.
- Checks verification status via Etherscan API V2.
- Optionally submits Standard JSON Input bundles when a source bundle path is
configured locally.
- Can be used as a status-only inventory check for historical deployments whose
source bundle is documented separately.
"""
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"
},
"0xa9F284eD010f4F7d7F8F201742b49b9f58e29b84": {
"name": "DODOPMMIntegration",
"verification_note": "docs/03-deployment/ETHEREUM_MAINNET_DODOPMMINTEGRATION_VERIFICATION.md"
}
}
API_URL = "https://api.etherscan.io/v2/api"
CHAIN_ID = "1"
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"{API_URL}?chainid={CHAIN_ID}&module=contract&action=getsourcecode"
f"&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 check_submission_status(guid, api_key):
"""Check the status of a submitted verification job."""
url = (
f"{API_URL}?chainid={CHAIN_ID}&module=contract&action=checkverifystatus"
f"&guid={guid}&apikey={api_key}"
)
with urllib.request.urlopen(url) as response:
data = json.loads(response.read().decode())
return data.get('status'), data.get('result', '')
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.get('constructor_args', '')
standard_json_file = contract_info.get('standard_json_file')
if not standard_json_file:
log_warn("No Standard JSON bundle configured for automated submission.")
note = contract_info.get('verification_note')
if note:
log_info(f"See verification note: {note}")
return None
# 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=(',', ':'))
params = {
'chainid': CHAIN_ID,
'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',
'constructorArguments': 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 guid
else:
log_error(f"Verification failed: {result.get('result', 'Unknown error')}")
return None
else:
error_msg = result.get('result', 'Unknown error')
log_error(f"Verification failed: {error_msg}")
return None
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 None
except Exception as e:
log_error(f"Request failed: {e}")
return None
def monitor_verification(address, guid, 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)
try:
status, result = check_submission_status(guid, api_key)
except Exception as e:
log_warn(f"Could not query verification job status: {e}")
status, result = None, ''
if status == '1':
log_success("Contract is now verified!")
return True
if result:
log_info(f"Attempt {attempt}/{max_attempts}: {result}")
else:
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...")
guid = verify_contract(address, contract_info, api_key)
if guid:
log_info("")
log_step("Monitoring verification status...")
if monitor_verification(address, guid, api_key):
verified_count += 1
else:
log_warn("Verification submitted but status unclear. Check Etherscan manually.")
elif contract_info.get('standard_json_file') or contract_info.get('verification_note'):
failed_count += 1
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()