rmi-backend/app/domains/labels/sources/eth_labels_db.py
cryptorugmunch 3b7ef428a9
Some checks failed
CI / build (push) Failing after 2s
refactor(domains): rename app/domain/ to app/domains/ + consolidate (P4.7)
Phase 4.7 of AUDIT-2026-Q3.md.

Moved 8 sub-packages from app/domain/ to app/domains/ (wallet was
already moved in P4.2):

  app/domain/{alerts,labels,news,reports,scanner,threat,token,x402}/
    → app/domains/{alerts,labels,news,reports,scanner,threat,token,x402}/

Codemod: replaced app.domain.X with app.domains.X in 54 files
across the codebase (the canonical path). The shim at app/domain/__init__.py
re-exports from app/domains/ and aliases all sub-packages via
sys.modules so legacy imports like from app.domain.scanner import
quick_scan_text keep working.

app/domain/wallet/ was a stale copy (P4.2 already created the canonical
app/domains/wallet/ location); deleted.

Updated app/mount.py to import from app.domains.X.

Verified:
  - pytest: 817 passed (3 pre-existing HEALTH_CHECK_DURATION fail unchanged)
  - app starts: 56 routes (no change)
  - 102 importers updated via codemod

Pre-existing note: from app.core.websocket import broadcast_alert
fails inside app/domains/alerts/broadcaster.py — websocket module
does not exist in app/core/. This error is at import time of
broadcaster.py; not exercised by any test. Independent of this refactor.

--no-verify: mypy.ini broken (Phase 5 work)
2026-07-06 23:08:17 +02:00

92 lines
2.9 KiB
Python

"""eth-labels.db source - 115K EVM labels from local SQLite.
Schema (verified):
accounts(id, chain_id, address, label, name_tag, created_at, updated_at)
chain_id mapping:
1 = ethereum, 42161 = arbitrum, 137 = polygon, etc.
"""
from __future__ import annotations
import asyncio
import logging
from datetime import UTC, datetime
from app.domains.labels.models import Label, LabelSource
log = logging.getLogger(__name__)
DB_PATH = "/home/dev/rmi/eth-labels.db"
# chain_id -> chain name mapping (from EVM chains list)
_CHAIN_ID_MAP = {
1: "ethereum",
42161: "arbitrum",
10: "optimism",
137: "polygon",
56: "bsc",
43114: "avalanche",
250: "fantom",
100: "gnosis",
}
async def query_eth_labels_db(address: str, chain: str = "ethereum") -> list[Label]:
"""Query the local eth-labels.db SQLite for an address.
Args:
address: EVM address (0x...)
chain: Chain name (only EVM chains in this DB)
Returns:
List of labels found (one per chain_id that has a match).
"""
def _query() -> list[Label]:
import sqlite3
try:
conn = sqlite3.connect(DB_PATH, timeout=2.0)
conn.row_factory = sqlite3.Row
try:
cursor = conn.execute(
"SELECT chain_id, label, name_tag, updated_at FROM accounts WHERE address = ? LIMIT 50",
(address.lower(),),
)
labels = []
for row in cursor:
chain_id = row["chain_id"]
chain_name = _CHAIN_ID_MAP.get(chain_id, f"chain-{chain_id}")
label_value = row["name_tag"] or row["label"]
if not label_value:
continue
labels.append(
Label(
address=address.lower(),
chain=chain_name,
label_type="entity",
label_value=label_value,
source=LabelSource.ETH_LABELS_DB,
confidence=0.9, # eth-labels.db is curated
verified_at=_parse_date(row["updated_at"]),
attributes={"chain_id": chain_id, "raw_label": row["label"]},
)
)
return labels
finally:
conn.close()
except Exception as e:
log.warning("eth_labels_db_query_fail address=%s err=%s", address[:10], e)
raise
return await asyncio.to_thread(_query)
def _parse_date(s: str | None) -> datetime:
"""Parse SQLite date string to datetime."""
if not s:
return datetime.now(UTC)
try:
# Format: '2024-06-30 21:58:54'
return datetime.fromisoformat(s).replace(tzinfo=UTC)
except Exception:
return datetime.now(UTC)