""" Intelligent Webhook Processors - Smart event analysis. Processes Helius, Moralis, and custom webhook events with: - Whale detection (large transfers) - Cluster correlation (linked wallets) - Scam pattern matching (known signatures) - Cross-platform enrichment (RAG lookup) - Alert prioritization (critical/high/medium/low) """ import hashlib import logging import time from collections import defaultdict from dataclasses import dataclass, field from datetime import UTC, datetime logger = logging.getLogger(__name__) # ── Alert Severity Levels ─────────────────────────────────── CRITICAL = "critical" # Confirmed scam, large exploit HIGH = "high" # Whale movement, suspicious pattern MEDIUM = "medium" # Cluster detected, unusual activity LOW = "low" # Routine monitoring @dataclass class SmartAlert: """Intelligent alert with enrichment and scoring.""" alert_id: str severity: str alert_type: str source: str # helius, moralis, custom timestamp: datetime wallet: str chain: str description: str amount_usd: float = 0.0 risk_score: float = 0.0 cluster_id: str | None = None related_wallets: list[str] = field(default_factory=list) enriched_data: dict = field(default_factory=dict) actions: list[str] = field(default_factory=list) # Recommended actions class IntelligentWebhookProcessor: """Smart webhook event processor with multi-signal analysis.""" # Thresholds WHALE_THRESHOLD_SOL = 100.0 # SOL WHALE_THRESHOLD_ETH = 50.0 # ETH WHALE_THRESHOLD_USD = 100000.0 # USD # Known scam patterns SCAM_PATTERNS = { # noqa: RUF012 "dust_attack": { "min_amount": 0.000001, "max_amount": 0.001, "pattern": "unsolicited_token", }, "approval_drain": {"method": "setApprovalForAll", "risk": "high"}, "permit2_phishing": {"method": "permit", "risk": "critical"}, } def __init__(self): self._recent_events: dict[str, list[dict]] = defaultdict(list) # wallet -> events self._cluster_cache: dict[str, str] = {} # wallet -> cluster_id self._scam_registry: set[str] = set() # known scam addresses def _generate_alert_id(self, source: str, wallet: str, ts: float) -> str: """Generate unique alert ID.""" data = f"{source}:{wallet}:{int(ts)}" return hashlib.sha256(data.encode()).hexdigest()[:16] async def _enrich_wallet(self, wallet: str, chain: str) -> dict: """Enrich wallet with RAG lookup and cluster data.""" enriched = { "labels": [], "cluster_id": None, "is_scammer": False, "is_exchange": False, "risk_score": 0.0, } try: # Check cluster cache if wallet in self._cluster_cache: enriched["cluster_id"] = self._cluster_cache[wallet] # RAG lookup (if available) try: from app.chain_cache import get_chain_cache cache = get_chain_cache() labels = await cache.get("wallet_labels", wallet) if labels: enriched["labels"] = labels if isinstance(labels, list) else [labels] except Exception: pass # Entity registry check try: from app.entity_registry import get_entity_registry registry = get_entity_registry() match = registry.classify_address(wallet) if match and match.entity_name: enriched["is_exchange"] = match.entity_type in ("cex", "defi") enriched["labels"].append(f"{match.entity_type}:{match.entity_name}") except Exception: pass except Exception as e: logger.debug(f"Wallet enrichment failed: {e}") return enriched async def _check_cluster(self, wallet: str, chain: str) -> str | None: """Check if wallet belongs to known cluster.""" try: from app.cluster_detection import get_cluster_detector detector = get_cluster_detector() # Quick lookup (don't run full detection) if hasattr(detector, "_wallet_cluster_map"): cluster_id = detector._wallet_cluster_map.get(wallet) if cluster_id: self._cluster_cache[wallet] = cluster_id return cluster_id except Exception: pass return None async def _check_scam_registry(self, wallet: str) -> bool: """Check if wallet is in scam database.""" try: # Spam registry from app.spam_registry import get_spam_registry sr = get_spam_registry() if wallet in sr._scam_addresses: return True except Exception: pass # Threat feeds try: from app.threat_feeds import get_threat_feeds feeds = get_threat_feeds() result = await feeds.check_cryptoscamdb(wallet) if result and result.get("is_scam"): return True except Exception: pass return False def _calculate_risk_score(self, event: dict, enriched: dict) -> float: """Calculate composite risk score (0-100).""" score = 0.0 # Amount-based risk amount_usd = event.get("amount_usd", 0) if amount_usd > 1000000: score += 40 elif amount_usd > 100000: score += 25 elif amount_usd > 10000: score += 10 # Scam registry if enriched.get("is_scammer"): score += 50 # Cluster membership if enriched.get("cluster_id"): score += 20 # Exchange wallet (lower risk) if enriched.get("is_exchange"): score *= 0.3 # Pattern matching if event.get("is_dust_attack"): score += 60 if event.get("is_approval"): score += 30 return min(score, 100.0) def _determine_severity(self, risk_score: float, amount_usd: float) -> str: """Determine alert severity.""" if risk_score >= 70 or amount_usd > 500000: return CRITICAL elif risk_score >= 50 or amount_usd > 100000: return HIGH elif risk_score >= 30 or amount_usd > 10000: return MEDIUM else: return LOW def _generate_actions(self, alert: SmartAlert) -> list[str]: """Generate recommended actions based on alert type.""" actions = [] if alert.severity == CRITICAL: actions.extend( [ "🚨 IMMEDIATE: Add to scam watchlist", "🚨 Alert affected users if cluster detected", "🚨 Freeze related accounts (if platform)", ] ) elif alert.severity == HIGH: actions.extend( [ "⚠️ Monitor wallet for 24h", "⚠️ Check for related cluster activity", "⚠️ Log for pattern analysis", ] ) elif alert.severity == MEDIUM: actions.extend( [ "📊 Add to monitoring queue", "📊 Correlate with recent events", ] ) else: actions.append("📝 Log for historical analysis") return actions async def process_helius_event(self, event: dict) -> SmartAlert | None: """Process Helius webhook event with intelligent analysis.""" try: # Extract key data wallet = event.get("feePayer", "") signature = event.get("signature", "") description = event.get("description", "") timestamp = event.get("timestamp", 0) if not wallet: return None # Calculate amounts native_transfers = event.get("nativeTransfers", []) max_transfer = max(native_transfers, key=lambda t: t.get("amount", 0), default={}) amount_sol = max_transfer.get("amount", 0) / 1e9 amount_usd = amount_sol * 150.0 # Approx SOL price token_transfers = event.get("tokenTransfers", []) is_dust = any(float(tt.get("tokenAmount", {}).get("uiAmount", 0) or 0) < 0.001 for tt in token_transfers) # Enrich enriched = await self._enrich_wallet(wallet, "solana") is_scam = await self._check_scam_registry(wallet) enriched["is_scammer"] = is_scam # Risk score risk_score = self._calculate_risk_score( { "amount_usd": amount_usd, "is_dust_attack": is_dust, "is_approval": "approval" in description.lower(), }, enriched, ) severity = self._determine_severity(risk_score, amount_usd) # Only create alert if meaningful if severity == LOW and risk_score < 20 and amount_usd < 1000: return None alert = SmartAlert( alert_id=self._generate_alert_id("helius", wallet, timestamp), severity=severity, alert_type="whale_transfer" if amount_sol > self.WHALE_THRESHOLD_SOL else "transfer", source="helius", timestamp=datetime.fromtimestamp(timestamp, tz=UTC) if timestamp else datetime.now(UTC), wallet=wallet, chain="solana", description=description[:200], amount_usd=amount_usd, risk_score=risk_score, enriched_data=enriched, ) alert.actions = self._generate_actions(alert) # Store for correlation self._recent_events[wallet].append( { "type": "helius", "timestamp": timestamp, "amount": amount_sol, "signature": signature, } ) # Keep only last 50 events per wallet self._recent_events[wallet] = self._recent_events[wallet][-50:] return alert except Exception as e: logger.warning(f"Helius event processing failed: {e}") return None async def process_moralis_event(self, event: dict) -> SmartAlert | None: """Process Moralis Stream event with intelligent analysis.""" try: # Extract key data wallet = event.get("fromAddress", "") to_address = event.get("toAddress", "") tx_hash = event.get("transactionHash", "") chain_id = event.get("chainId", "0x1") timestamp = event.get("blockTimestamp", "") value = event.get("value", "0") if not wallet: return None # Map chain ID to name chain_map = {"0x1": "eth", "0x38": "bsc", "0x89": "polygon", "0xa86a": "avalanche"} chain = chain_map.get(chain_id, "eth") # Calculate amount amount_eth = float(value) / 1e18 if value else 0 amount_usd = amount_eth * 2500.0 # Approx ETH price # Check for token transfers erc20_transfers = event.get("erc20Transfers", []) is_approval = any( t.get("value") == "0" and t.get("toAddress", "").lower() != wallet.lower() for t in erc20_transfers ) # Enrich enriched = await self._enrich_wallet(wallet, chain) is_scam = await self._check_scam_registry(wallet) enriched["is_scammer"] = is_scam # Risk score risk_score = self._calculate_risk_score( { "amount_usd": amount_usd, "is_approval": is_approval, }, enriched, ) severity = self._determine_severity(risk_score, amount_usd) if severity == LOW and risk_score < 20 and amount_usd < 1000: return None alert = SmartAlert( alert_id=self._generate_alert_id("moralis", wallet, time.time()), severity=severity, alert_type="whale_transfer" if amount_eth > self.WHALE_THRESHOLD_ETH else "transfer", source="moralis", timestamp=datetime.fromisoformat(timestamp.replace("Z", "+00:00")) if timestamp else datetime.now(UTC), wallet=wallet, chain=chain, description=f"Transfer to {to_address[:12]}... ({amount_eth:.4f} ETH)", amount_usd=amount_usd, risk_score=risk_score, enriched_data=enriched, related_wallets=[to_address] if to_address else [], ) alert.actions = self._generate_actions(alert) # Store for correlation self._recent_events[wallet].append( { "type": "moralis", "timestamp": time.time(), "amount": amount_eth, "tx_hash": tx_hash, } ) self._recent_events[wallet] = self._recent_events[wallet][-50:] return alert except Exception as e: logger.warning(f"Moralis event processing failed: {e}") return None async def get_correlated_alerts(self, wallet: str, window_hours: int = 24) -> list[dict]: """Get correlated events for a wallet.""" events = self._recent_events.get(wallet, []) cutoff = time.time() - (window_hours * 3600) return [e for e in events if e.get("timestamp", 0) > cutoff] # Singleton _processor: IntelligentWebhookProcessor | None = None def get_intelligent_processor() -> IntelligentWebhookProcessor: global _processor if _processor is None: _processor = IntelligentWebhookProcessor() return _processor