rmi-backend/app/wallet_label_loader.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

149 lines
4.7 KiB
Python

"""
Wallet Label Loader - Loads curated wallet address labels from open-source datasets.
Sources:
- crypto-wallet-address-labels (105K+ Solana, 16K BSC/ETH, 15K phishing)
- OFAC crypto-sanctions-list
"""
import csv
import json
import logging
import os
logger = logging.getLogger(__name__)
LABELS_BASE = "/app/data/wallet-labels"
def load_all_labels(redis_client) -> dict:
"""Load all wallet label datasets into Redis. Returns counts."""
counts = {}
# 1. Solana CEX labels
path = f"{LABELS_BASE}/solana_cex_labels.csv"
if os.path.exists(path):
count = _load_csv_labels(
redis_client,
path,
"solana",
"address",
{
"label_type": "label_type",
"label_subtype": "label_subtype",
"name": "address_name",
"project": "project_name",
},
)
counts["solana_cex"] = count
# 2. Solana DApp labels
path = f"{LABELS_BASE}/solana_dapp_labels.csv"
if os.path.exists(path):
count = _load_csv_labels(redis_client, path, "solana", "address", {"name": "dapp_name"})
counts["solana_dapp"] = count
# 3. Solana DeFi labels
path = f"{LABELS_BASE}/solana_defi_labels.csv"
if os.path.exists(path):
count = _load_csv_labels(redis_client, path, "solana", "address", {"name": "protocol_name"})
counts["solana_defi"] = count
# 4. Ethereum phishing and scam
path = f"{LABELS_BASE}/etherscan_malicious_labels.csv"
if os.path.exists(path):
count = _load_csv_labels(
redis_client,
path,
"ethereum",
"banned_address",
{"tag": "wallet_tag", "source": "data_source"},
)
counts["eth_phishing"] = count
# 5. Malicious smart contracts
path = f"{LABELS_BASE}/malicious_smart_contracts.csv"
if os.path.exists(path):
count = _load_csv_labels(
redis_client,
path,
"ethereum",
"contract_address",
{"tag": "contract_tag", "notes": "notes", "source": "source"},
)
counts["eth_malicious_contracts"] = count
# 6. OFAC sanctions
path = f"{LABELS_BASE}/ofac_sanctions.json"
if os.path.exists(path):
try:
with open(path) as f:
sanctions = json.load(f)
for entry in sanctions:
addr = entry.get("address", "")
if addr:
redis_client.set(f"rmi:label:ofac:{addr.lower()}", json.dumps(entry))
redis_client.sadd("rmi:labels:ofac", addr.lower())
redis_client.set("rmi:labels:ofac:count", len(sanctions))
counts["ofac"] = len(sanctions)
except Exception as e:
logger.warning(f"OFAC load failed: {e}")
# Store total counts
redis_client.set("rmi:labels:counts", json.dumps(counts))
logger.info(f"Loaded wallet labels: {counts}")
return counts
def _load_csv_labels(redis_client, path: str, chain: str, addr_col: str, label_cols: dict[str, str]) -> int:
"""Load a CSV of labeled addresses into Redis."""
count = 0
try:
with open(path, newline="", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
addr = row.get(addr_col, "").strip().lower()
if not addr or len(addr) < 10:
continue
# Build label entry
label = {"chain": chain, "address": addr}
for key, col in label_cols.items():
val = row.get(col, "").strip()
if val:
label[key] = val
redis_client.set(f"rmi:label:{chain}:{addr}", json.dumps(label))
redis_client.sadd(f"rmi:labels:{chain}", addr)
count += 1
except Exception as e:
logger.warning(f"Failed to load {path}: {e}")
return count
def lookup_wallet_label(address: str, chain: str | None = None, redis_client=None) -> dict | None:
"""Look up a wallet address in the label database."""
if not redis_client:
return None
addr = address.lower().strip()
# Check specific chain
if chain:
label = redis_client.get(f"rmi:label:{chain}:{addr}")
if label:
return json.loads(label)
# Check all chains
for ch in ["solana", "ethereum", "bsc"]:
label = redis_client.get(f"rmi:label:{ch}:{addr}")
if label:
return json.loads(label)
# Check OFAC
ofac = redis_client.get(f"rmi:label:ofac:{addr}")
if ofac:
result = json.loads(ofac)
result["is_sanctioned"] = True
return result
return None