- Organized 252 files across project - Root directory: 187 → 2 files (98.9% reduction) - Moved configuration guides to docs/04-configuration/ - Moved troubleshooting guides to docs/09-troubleshooting/ - Moved quick start guides to docs/01-getting-started/ - Moved reports to reports/ directory - Archived temporary files - Generated comprehensive reports and documentation - Created maintenance scripts and guides All files organized according to established standards.
263 lines
8.9 KiB
Python
Executable File
263 lines
8.9 KiB
Python
Executable File
#!/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()
|
|
|