Vertex-Risk-Engine / app /engine /web3_engine.py
Denisijcu's picture
Upload 19 files
2a0f014 verified
raw
history blame
4.09 kB
import os
import requests
import json
from dotenv import load_dotenv
load_dotenv()
class VertexWeb3Engine:
def __init__(self, contract_address, network="ethereum"):
self.address = contract_address.strip()
self.network = network
self.api_key = os.getenv("ETHERSCAN_API_KEY", "").strip()
# Configuración V2 (Obligatoria en 2026)
self.networks = {
"ethereum": {"url": "https://api.etherscan.io/v2/api", "id": "1"},
"bsc": {"url": "https://api.bscscan.com/v2/api", "id": "56"},
"polygon": {"url": "https://api.polygonscan.com/v2/api", "id": "137"}
}
config = self.networks.get(network, self.networks["ethereum"])
self.base_url = config["url"]
self.chain_id = config["id"]
def get_contract_source(self):
"""Descarga el código fuente usando Etherscan API V2"""
params = {
"chainid": self.chain_id, # Requerido para V2
"module": "contract",
"action": "getsourcecode",
"address": self.address,
"apikey": self.api_key
}
try:
response = requests.get(self.base_url, params=params, timeout=15)
data = response.json()
# Verificamos status de Etherscan
if data.get("status") == "1" and data.get("result"):
result = data["result"][0]
raw_source = result.get("SourceCode", "")
if not raw_source:
return {"success": False, "error": "Contract not verified"}
# Parseo corregido para evitar el error de 'slice'
source_code = self._parse_source_code(raw_source)
return {
"success": True,
"source_code": source_code,
"contract_info": {
"name": result.get("ContractName", "Unknown"),
"compiler": result.get("CompilerVersion", "Unknown")
}
}
return {"success": False, "error": data.get("result", "API Error")}
except Exception as e:
return {"success": False, "error": str(e)}
def _parse_source_code(self, raw_source):
"""Maneja formatos plano y multi-archivo (JSON)"""
# Verificamos que sea un string antes de recortar
if not isinstance(raw_source, str) or not raw_source.startswith("{"):
return raw_source
try:
# FIX: Aseguramos que el recorte se haga solo si es un string de verdad
if raw_source.startswith("{{") and raw_source.endswith("}}"):
clean_json = raw_source[1:-1] # Quitamos solo una pareja de llaves
else:
clean_json = raw_source
parsed = json.loads(clean_json)
if "sources" in parsed:
all_code = []
for filename, file_data in parsed["sources"].items():
if "content" in file_data:
all_code.append(f"// FILE: {filename}\n{file_data['content']}")
return "\n\n".join(all_code)
return raw_source
except:
return raw_source
def scan_basic_vulnerabilities(self, source_code):
"""Análisis de patrones de riesgo"""
if not source_code or not isinstance(source_code, str):
return []
red_flags = []
patterns = {
"selfdestruct": "CRITICAL: Contract can be destroyed.",
"mint(": "HIGH: Infinite minting risk.",
"delegatecall": "HIGH: Proxy execution risk."
}
for pattern, risk in patterns.items():
if pattern in source_code.lower():
red_flags.append({"pattern": pattern, "description": risk})
return red_flags