merge: chore/cleanup-remove-bloat-and-secrets into main
This commit is contained in:
commit
bde2f3a97d
1173 changed files with 437609 additions and 0 deletions
186
app/routers/x402_insider_network.py
Normal file
186
app/routers/x402_insider_network.py
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
"""
|
||||
x402 Router: insider_network
|
||||
=============================
|
||||
Wraps InsiderNetworkAnalyzer with:
|
||||
- Address validation
|
||||
- x402 payment middleware integration
|
||||
- Caching (Redis if available)
|
||||
- Trial quota tracking
|
||||
- Rate limiting support
|
||||
|
||||
TOOL : insider_network
|
||||
TIER : premium / intelligence
|
||||
PRICE : $0.10 (100000 atoms)
|
||||
TRIAL : 1 free check
|
||||
ROUTER: /api/v1/x402-tools/insider_network
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from contextlib import suppress
|
||||
|
||||
import redis as _redis_mod
|
||||
from fastapi import APIRouter, HTTPException, Request
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
from app.insider_network import (
|
||||
format_insider_network_report,
|
||||
get_insider_network_analyzer,
|
||||
is_valid_address,
|
||||
)
|
||||
|
||||
logger = logging.getLogger("x402_insider_network")
|
||||
|
||||
router = APIRouter(prefix="/api/v1/x402-tools", tags=["x402-tools"])
|
||||
|
||||
# ── Redis helpers ─────────────────────────────────────────────
|
||||
|
||||
_redis: "_redis_mod.Redis | None" = None
|
||||
|
||||
|
||||
def _get_redis():
|
||||
global _redis
|
||||
if _redis is None:
|
||||
try:
|
||||
r = _redis_mod.Redis(
|
||||
host=os.getenv("REDIS_HOST", "localhost"),
|
||||
port=int(os.getenv("REDIS_PORT", 6379)),
|
||||
db=int(os.getenv("REDIS_DB", 0)),
|
||||
password=os.getenv("REDIS_PASSWORD", None),
|
||||
decode_responses=True,
|
||||
)
|
||||
r.ping()
|
||||
_redis = r
|
||||
except Exception:
|
||||
_redis = None # Not available
|
||||
return _redis
|
||||
|
||||
|
||||
_CACHE_TTL = 600 # 10 minutes — insider networks don't change rapidly
|
||||
|
||||
|
||||
# ── Request Models ─────────────────────────────────────────────
|
||||
|
||||
|
||||
class InsiderNetworkRequest(BaseModel):
|
||||
"""Request body for insider network analysis."""
|
||||
|
||||
wallet_address: str = Field(
|
||||
...,
|
||||
description="Wallet address to investigate for insider connections",
|
||||
)
|
||||
chains: list[str] | None = Field(
|
||||
default=None,
|
||||
description="Chains to search (default: solana, ethereum, bsc, base)",
|
||||
)
|
||||
deep_scan: bool = Field(
|
||||
default=False,
|
||||
description="If True, perform deeper (slower) analysis with more API calls",
|
||||
)
|
||||
|
||||
@field_validator("wallet_address")
|
||||
@classmethod
|
||||
def validate_address(cls, v: str) -> str:
|
||||
v = v.strip()
|
||||
if not is_valid_address(v):
|
||||
raise ValueError(f"Invalid address: {v}. Must be a valid Solana (base58) or EVM (0x-prefixed hex) address.")
|
||||
return v
|
||||
|
||||
@field_validator("chains")
|
||||
@classmethod
|
||||
def validate_chains(cls, v: list[str] | None) -> list[str] | None:
|
||||
if v is not None:
|
||||
valid = {"solana", "ethereum", "bsc", "polygon", "arbitrum", "base"}
|
||||
for c in v:
|
||||
if c not in valid:
|
||||
raise ValueError(f"Unsupported chain: {c}. Supported: {', '.join(sorted(valid))}")
|
||||
return v
|
||||
|
||||
|
||||
# ── Endpoints ──────────────────────────────────────────────────
|
||||
|
||||
|
||||
@router.post("/insider_network")
|
||||
async def analyze_insider_network(request: Request, body: InsiderNetworkRequest) -> dict:
|
||||
"""
|
||||
Analyze a wallet for insider connections across token projects.
|
||||
Maps shared funding sources, coordinated trading, and team relationships.
|
||||
"""
|
||||
wallet = body.wallet_address.strip()
|
||||
|
||||
# Check cache
|
||||
cache_key = f"insider_network:{wallet}:{body.deep_scan}"
|
||||
r = _get_redis()
|
||||
if r:
|
||||
with suppress(Exception):
|
||||
cached = r.get(cache_key)
|
||||
if cached:
|
||||
return json.loads(cached)
|
||||
|
||||
# Check trial quota (via x402 middleware headers if available)
|
||||
trial_used = False
|
||||
x402_headers = getattr(request.state, "x402", None) or {}
|
||||
quota = x402_headers.get("remaining_quota", {})
|
||||
if isinstance(quota, dict) and quota.get("insider_network", 0) > 0:
|
||||
trial_used = True
|
||||
logger.info(
|
||||
"Trial quota used for insider_network on wallet %s",
|
||||
wallet[:10],
|
||||
)
|
||||
|
||||
try:
|
||||
analyzer = get_insider_network_analyzer()
|
||||
report = await analyzer.analyze(
|
||||
wallet_address=wallet,
|
||||
chains=body.chains,
|
||||
deep_scan=body.deep_scan,
|
||||
)
|
||||
|
||||
response = {
|
||||
"success": True,
|
||||
"tool": "insider_network",
|
||||
"wallet_address": wallet,
|
||||
"total_connected_wallets": report.total_connected_wallets,
|
||||
"total_clusters": report.total_clusters,
|
||||
"highest_risk_score": round(report.highest_risk_score, 1),
|
||||
"clusters": [
|
||||
{
|
||||
"cluster_id": c.cluster_id,
|
||||
"member_count": c.member_count,
|
||||
"risk_score": round(c.risk_score, 1),
|
||||
"projects_involved": c.projects_involved[:5],
|
||||
"risk_factors": c.risk_factors[:3],
|
||||
"top_relationships": c.top_relationships[:5],
|
||||
}
|
||||
for c in report.clusters
|
||||
],
|
||||
"summary": report.summary,
|
||||
"report_text": format_insider_network_report(report),
|
||||
"trial_used": trial_used,
|
||||
}
|
||||
|
||||
if not trial_used and r:
|
||||
with suppress(Exception):
|
||||
r.setex(cache_key, _CACHE_TTL, json.dumps(response))
|
||||
|
||||
return response
|
||||
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e)) from e
|
||||
except Exception as e:
|
||||
logger.exception("Insider network analysis failed for %s", wallet[:10])
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Analysis failed: {e!s}",
|
||||
) from e
|
||||
|
||||
|
||||
@router.get("/insider_network/health")
|
||||
async def insider_network_health() -> dict:
|
||||
"""Health check endpoint."""
|
||||
return {
|
||||
"status": "ok",
|
||||
"tool": "insider_network",
|
||||
"version": "1.0.0",
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue