""" 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