rmi-backend/app/routers/forensics_router.py
opencode c762564d40 style(rmi-backend): complete lint cleanup — 1175→0 ruff errors
- Fix 71 invalid-syntax files (class-body newline-broken assignments)
- Add from/None chain to 307 B904 raise-without-from sites
- Add B008 ignore to ruff.toml (already in pyproject.toml)
- Noqa F401 on __init__.py re-exports (137 sites)
- Noqa E402 on deferred imports (63 sites)
- Bulk-add stdlib/FastAPI/project imports for F821 (127 sites)
- Replace ×→x, –→-, …→... in docstrings (4093 chars)
- Manual refactor of 5 SIM103/SIM116 patterns

Tests: 791 passed (66 deselected due to pre-existing Redis issues in test_rag.py)
Co-authored-by: opencode <opencode@rugmunch.io>
2026-07-06 15:43:20 +02:00

120 lines
4.1 KiB
Python

"""
Forensics API Router - Deep contract scan + cross-chain correlation + risk reports.
Connects to /api/v1/forensics/*
"""
from fastapi import APIRouter, HTTPException, Query
from pydantic import BaseModel
router = APIRouter(prefix="/api/v1/forensics", tags=["forensics"])
class DeepScanRequest(BaseModel):
contract_address: str
chain: str = "base"
class BatchScanRequest(BaseModel):
contracts: list[str]
chain: str = "base"
class RiskReportRequest(BaseModel):
token_address: str
chain: str = "solana"
include_graph: bool = False
class ThreatCheckRequest(BaseModel):
address: str
chain_id: str = "1" # 1=ethereum, 56=bsc, 8453=base, solana=solana
@router.post("/deep-scan")
async def deep_scan(req: DeepScanRequest):
"""Deep contract scan using slither/mythril."""
try:
from app.contract_deepscan import deep_scan_contract
result = deep_scan_contract(req.contract_address, chain=req.chain)
return {"contract": req.contract_address, "chain": req.chain, "scan": result}
except ImportError as e:
raise HTTPException(status_code=503, detail=f"Scanner unavailable: {e}") from e
except Exception as e:
raise HTTPException(status_code=500, detail=str(e)[:500]) from e
@router.post("/batch-scan")
async def batch_deep_scan(req: BatchScanRequest):
"""Batch deep scan multiple contracts."""
try:
from app.contract_deepscan import batch_deep_scan
result = batch_deep_scan(req.contracts, chain=req.chain)
return {"contracts": len(req.contracts), "chain": req.chain, "results": result}
except ImportError as e:
raise HTTPException(status_code=503, detail=str(e)) from e
except Exception as e:
raise HTTPException(status_code=500, detail=str(e)[:500]) from e
@router.post("/risk-report")
async def risk_report(req: RiskReportRequest):
"""Generate comprehensive rug risk report."""
try:
from app.advanced_analysis import RugRiskReport
report = RugRiskReport(token_address=req.token_address, chain=req.chain)
return {"token_address": req.token_address, "chain": req.chain, "report": str(report)}
except ImportError as e:
raise HTTPException(status_code=503, detail=str(e)) from e
except Exception as e:
raise HTTPException(status_code=500, detail=str(e)[:500]) from e
@router.post("/cross-chain")
async def cross_chain_correlate(addresses: list[str] = Query(...), chains: list[str] | None = Query(None)):
"""Correlate wallets across chains using behavioral fingerprinting + CEX patterns."""
try:
from app.cross_chain_correlator import get_cross_chain_correlator
cc = get_cross_chain_correlator()
profiles = await cc.correlate(addresses=addresses, chains=chains)
return {
"addresses_scanned": len(addresses),
"profiles_found": len(profiles),
"profiles": [
{
"entity_id": p.entity_id,
"total_addresses": p.total_addresses,
"chains": dict(p.chains),
"links": len(p.links),
"risk_score": p.risk_score,
}
for p in profiles
],
}
except ImportError as e:
raise HTTPException(status_code=503, detail=str(e)) from e
except Exception as e:
raise HTTPException(status_code=500, detail=str(e)[:500]) from e
@router.get("/health")
async def forensics_health():
return {"status": "ok", "service": "forensics-engine"}
@router.post("/threat-check")
async def threat_check(req: ThreatCheckRequest):
"""Multi-source threat intel check: CryptoScamDB + GoPlus + Januus (all free)."""
try:
from app.threat_feeds import get_threat_feeds
feeds = get_threat_feeds()
result = await feeds.full_check(address=req.address, chain_id=req.chain_id)
return result
except ImportError as e:
raise HTTPException(status_code=503, detail=str(e)) from e
except Exception as e:
raise HTTPException(status_code=500, detail=str(e)[:500]) from e