"""Pydantic v2 models for the token domain. No FastAPI imports. Pure data shapes. """ from __future__ import annotations from datetime import datetime from enum import StrEnum from typing import Any from pydantic import BaseModel, ConfigDict, Field, field_validator class RiskLevel(StrEnum): """Token risk classification.""" LOW = "low" MEDIUM = "medium" HIGH = "high" CRITICAL = "critical" @classmethod def from_score(cls, score: int) -> RiskLevel: if score < 20: return cls.LOW if score < 50: return cls.MEDIUM if score < 80: return cls.HIGH return cls.CRITICAL class Token(BaseModel): """Token identity.""" model_config = ConfigDict(str_strip_whitespace=True) address: str = Field(..., min_length=1, max_length=256) chain: str = Field(default="solana") name: str = "" symbol: str = "" decimals: int = 9 class TokenDetail(BaseModel): """Full token details — metadata + supply + verification.""" address: str chain: str name: str = "" symbol: str = "" decimals: int = 9 total_supply: float = 0.0 circulating_supply: float = 0.0 holders: int = 0 verified: bool = False metadata: dict[str, Any] = Field(default_factory=dict) fetched_at: datetime = Field(default_factory=datetime.utcnow) class TokenHolder(BaseModel): """A token holder entry.""" address: str balance: float = 0.0 percentage: float = 0.0 label: str | None = None is_contract: bool = False is_locked: bool = False class TokenLiquidity(BaseModel): """Token liquidity summary.""" address: str chain: str total_liquidity_usd: float = 0.0 pools: list[dict[str, Any]] = Field(default_factory=list) locked_percentage: float = 0.0 fetched_at: datetime = Field(default_factory=datetime.utcnow) class TokenRisk(BaseModel): """Token risk assessment.""" address: str chain: str risk_score: int = Field(default=0, ge=0, le=100) risk_level: RiskLevel flags: list[dict[str, Any]] = Field(default_factory=list) honeypot: bool = False can_sell: bool = True can_buy: bool = True take_back: bool = False owner_change_balance: bool = False transfer_pausable: bool = False analyzed_at: datetime = Field(default_factory=datetime.utcnow) class TokenScanRequest(BaseModel): """Request body for the token scan endpoint.""" model_config = ConfigDict(str_strip_whitespace=True) address: str = Field(..., min_length=1, max_length=256) chain: str = Field(default="solana") tier: str = Field(default="free") @field_validator("chain", "tier") @classmethod def _lowercase(cls, v: str) -> str: return v.lower().strip() class TokenScanResult(BaseModel): """Full token scan result.""" address: str chain: str safety_score: float = 100.0 risk_score: int = 0 risk_level: RiskLevel risk_flags: list[str] = Field(default_factory=list) warnings: list[str] = Field(default_factory=list) modules_run: list[str] = Field(default_factory=list) metadata: dict[str, Any] = Field(default_factory=dict) scanned_at: datetime = Field(default_factory=datetime.utcnow)