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
245
app/routers/x402_cross_chain_whale.py
Normal file
245
app/routers/x402_cross_chain_whale.py
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
"""
|
||||
x402 Router: cross_chain_whale
|
||||
================================
|
||||
Wraps CrossChainWhaleTracker with:
|
||||
- Address validation
|
||||
- x402 payment middleware integration
|
||||
- Caching (Redis if available)
|
||||
- Trial quota tracking
|
||||
- Rate limiting support
|
||||
|
||||
TOOL : cross_chain_whale
|
||||
TIER : intelligence
|
||||
PRICE : $0.08 (80000 atoms)
|
||||
TRIAL : 2 free checks
|
||||
ROUTER: /api/v1/x402-tools/cross_chain_whale
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from contextlib import suppress
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Request
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
from app.cross_chain_whale import (
|
||||
CrossChainWhaleTracker,
|
||||
format_whale_report,
|
||||
get_whale_tracker,
|
||||
is_valid_address,
|
||||
)
|
||||
|
||||
logger = logging.getLogger("x402_cross_chain_whale")
|
||||
|
||||
router = APIRouter(prefix="/api/v1/x402-tools", tags=["x402-tools"])
|
||||
|
||||
# ── Redis helpers ─────────────────────────────────────────────
|
||||
|
||||
_redis = None
|
||||
|
||||
|
||||
def _get_redis():
|
||||
global _redis
|
||||
if _redis is None:
|
||||
try:
|
||||
import redis as redis_mod
|
||||
|
||||
_redis = 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,
|
||||
)
|
||||
_redis.ping()
|
||||
except Exception:
|
||||
_redis = False # Sentinel for "not available"
|
||||
return _redis if _redis is not False else None
|
||||
|
||||
|
||||
_CACHE_TTL = 300 # 5 minutes
|
||||
|
||||
|
||||
# ── Request Models ─────────────────────────────────────────────
|
||||
|
||||
|
||||
class CrossChainWhaleRequest(BaseModel):
|
||||
"""Request body for cross-chain whale tracking."""
|
||||
|
||||
token_address: str = Field(..., description="Token contract/mint address to track")
|
||||
chains: list[str] | None = Field(
|
||||
default=None,
|
||||
description="Chains to check (default: solana, ethereum, base, bsc)",
|
||||
)
|
||||
|
||||
@field_validator("token_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
|
||||
|
||||
|
||||
class CrossChainWhaleWalletRequest(BaseModel):
|
||||
"""Request body for wallet-level cross-chain tracking."""
|
||||
|
||||
wallet_address: str = Field(..., description="Wallet address to track across chains")
|
||||
chains: list[str] | None = Field(
|
||||
default=None,
|
||||
description="Chains to check (default: all supported chains)",
|
||||
)
|
||||
|
||||
@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 wallet address: {v}")
|
||||
return v
|
||||
|
||||
|
||||
# ── Endpoints ──────────────────────────────────────────────────
|
||||
|
||||
|
||||
@router.post("/cross_chain_whale")
|
||||
async def track_cross_chain_whale(
|
||||
request: Request,
|
||||
body: CrossChainWhaleRequest,
|
||||
):
|
||||
"""Track whale holdings for a token across multiple chains.
|
||||
|
||||
Returns a comprehensive report of holder concentrations,
|
||||
cross-chain whale positions, and risk scoring.
|
||||
"""
|
||||
redis_client = _get_redis()
|
||||
cache_key = f"x402:whale:{body.token_address}:{','.join(body.chains or [])}"
|
||||
|
||||
# Check cache
|
||||
if redis_client:
|
||||
with suppress(Exception):
|
||||
cached = redis_client.get(cache_key)
|
||||
if cached:
|
||||
return {"success": True, "data": json.loads(cached), "cached": True}
|
||||
|
||||
try:
|
||||
tracker: CrossChainWhaleTracker = get_whale_tracker()
|
||||
|
||||
# Determine chains to scan
|
||||
chains = body.chains or ["solana", "ethereum", "base", "bsc"]
|
||||
|
||||
report = await tracker.track_token(
|
||||
token_address=body.token_address,
|
||||
chains=chains,
|
||||
)
|
||||
|
||||
# Build response data
|
||||
response_data = {
|
||||
"token_address": report.token_address,
|
||||
"token_symbol": report.token_symbol,
|
||||
"token_name": report.token_name,
|
||||
"chains_found": report.chains_found,
|
||||
"chains_no_data": report.chains_no_data,
|
||||
"total_holders_tracked": report.total_holders,
|
||||
"total_value_tracked_usd": round(report.total_value_tracked, 2),
|
||||
"concentration_score": report.concentration_score,
|
||||
"cross_chain_whale_count": len(report.cross_chain_whales),
|
||||
"top_holders_by_chain": report.top_holders_by_chain,
|
||||
"cross_chain_whales": [
|
||||
{
|
||||
"address": cw.primary_address,
|
||||
"address_short": f"{cw.primary_address[:6]}...{cw.primary_address[-4:]}",
|
||||
"chain_count": cw.chain_count,
|
||||
"total_value_usd": round(cw.total_value_usd, 2),
|
||||
"chains": list({p.chain for p in cw.positions}),
|
||||
"risk_score": cw.risk_score,
|
||||
"risk_factors": cw.risk_factors[:5],
|
||||
}
|
||||
for cw in report.cross_chain_whales[:20]
|
||||
],
|
||||
"top_whale": (
|
||||
{
|
||||
"address": report.cross_chain_whales[0].primary_address,
|
||||
"chain_count": report.cross_chain_whales[0].chain_count,
|
||||
"total_value_usd": round(report.cross_chain_whales[0].total_value_usd, 2),
|
||||
}
|
||||
if report.cross_chain_whales
|
||||
else None
|
||||
),
|
||||
"human_readable": format_whale_report(report),
|
||||
"errors": report.errors[:5],
|
||||
"scan_timestamp": report.scan_timestamp,
|
||||
}
|
||||
|
||||
# Cache it
|
||||
if redis_client and not report.errors:
|
||||
with suppress(Exception):
|
||||
redis_client.setex(cache_key, _CACHE_TTL, json.dumps(response_data))
|
||||
|
||||
return {"success": True, "data": response_data}
|
||||
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e)) from e
|
||||
except Exception as e:
|
||||
logger.exception(f"cross_chain_whale failed: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Cross-chain whale tracking failed: {e!s}",
|
||||
) from e
|
||||
|
||||
|
||||
@router.post("/cross_chain_whale_wallet")
|
||||
async def track_cross_chain_wallet(
|
||||
request: Request,
|
||||
body: CrossChainWhaleWalletRequest,
|
||||
):
|
||||
"""Track a specific wallet's token positions across multiple chains.
|
||||
|
||||
Returns a comprehensive view of what tokens a whale wallet holds
|
||||
across all supported chains.
|
||||
"""
|
||||
redis_client = _get_redis()
|
||||
cache_key = f"x402:whale_wallet:{body.wallet_address}:{','.join(body.chains or [])}"
|
||||
|
||||
if redis_client:
|
||||
with suppress(Exception):
|
||||
cached = redis_client.get(cache_key)
|
||||
if cached:
|
||||
return {"success": True, "data": json.loads(cached), "cached": True}
|
||||
|
||||
try:
|
||||
tracker: CrossChainWhaleTracker = get_whale_tracker()
|
||||
|
||||
chains = body.chains or [
|
||||
"solana",
|
||||
"ethereum",
|
||||
"base",
|
||||
"bsc",
|
||||
"polygon",
|
||||
"arbitrum",
|
||||
"optimism",
|
||||
]
|
||||
|
||||
result = await tracker.track_wallet(
|
||||
wallet_address=body.wallet_address,
|
||||
chains=chains,
|
||||
)
|
||||
|
||||
if redis_client:
|
||||
with suppress(Exception):
|
||||
redis_client.setex(cache_key, _CACHE_TTL, json.dumps(result))
|
||||
|
||||
return {"success": True, "data": result}
|
||||
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e)) from e
|
||||
except Exception as e:
|
||||
logger.exception(f"cross_chain_whale_wallet failed: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Wallet cross-chain tracking failed: {e!s}",
|
||||
) from e
|
||||
Loading…
Add table
Add a link
Reference in a new issue