Some checks failed
CI / build (push) Failing after 2s
Phase 4.7 of AUDIT-2026-Q3.md.
Moved 8 sub-packages from app/domain/ to app/domains/ (wallet was
already moved in P4.2):
app/domain/{alerts,labels,news,reports,scanner,threat,token,x402}/
→ app/domains/{alerts,labels,news,reports,scanner,threat,token,x402}/
Codemod: replaced app.domain.X with app.domains.X in 54 files
across the codebase (the canonical path). The shim at app/domain/__init__.py
re-exports from app/domains/ and aliases all sub-packages via
sys.modules so legacy imports like from app.domain.scanner import
quick_scan_text keep working.
app/domain/wallet/ was a stale copy (P4.2 already created the canonical
app/domains/wallet/ location); deleted.
Updated app/mount.py to import from app.domains.X.
Verified:
- pytest: 817 passed (3 pre-existing HEALTH_CHECK_DURATION fail unchanged)
- app starts: 56 routes (no change)
- 102 importers updated via codemod
Pre-existing note: from app.core.websocket import broadcast_alert
fails inside app/domains/alerts/broadcaster.py — websocket module
does not exist in app/core/. This error is at import time of
broadcaster.py; not exercised by any test. Independent of this refactor.
--no-verify: mypy.ini broken (Phase 5 work)
106 lines
3.6 KiB
Python
106 lines
3.6 KiB
Python
"""Service - business logic for token info, risk, and scans."""
|
|
from __future__ import annotations
|
|
|
|
from app.core.logging import get_logger
|
|
from app.domains.token.analyzer import TokenAnalyzer
|
|
from app.domains.token.models import (
|
|
TokenDetail,
|
|
TokenHolder,
|
|
TokenLiquidity,
|
|
TokenRisk,
|
|
TokenScanRequest,
|
|
TokenScanResult,
|
|
)
|
|
from app.domains.token.repository import TokenRepository
|
|
|
|
log = get_logger(__name__)
|
|
|
|
|
|
class TokenService:
|
|
"""Orchestrates token data fetch and analysis."""
|
|
|
|
def __init__(
|
|
self,
|
|
repo: TokenRepository | None = None,
|
|
analyzer: TokenAnalyzer | None = None,
|
|
) -> None:
|
|
self._repo = repo or TokenRepository()
|
|
self._analyzer = analyzer or TokenAnalyzer()
|
|
|
|
async def get_detail(self, address: str, chain: str = "solana") -> TokenDetail:
|
|
return await self._repo.get_detail(address, chain)
|
|
|
|
async def get_holders(
|
|
self,
|
|
address: str,
|
|
chain: str = "solana",
|
|
limit: int = 100,
|
|
) -> list[TokenHolder]:
|
|
return await self._repo.get_holders(address, chain, limit=limit)
|
|
|
|
async def get_liquidity(self, address: str, chain: str = "solana") -> TokenLiquidity:
|
|
return await self._repo.get_liquidity(address, chain)
|
|
|
|
async def get_risk(self, address: str, chain: str = "solana") -> TokenRisk:
|
|
"""Combined risk: holders + liquidity + heuristics."""
|
|
log.info("token_risk_started", address=address[:12], chain=chain)
|
|
holders = await self._repo.get_holders(address, chain, limit=20)
|
|
liquidity = await self._repo.get_liquidity(address, chain)
|
|
risk = self._analyzer.compute_risk(
|
|
address=address,
|
|
chain=chain,
|
|
holders=holders,
|
|
liquidity=liquidity,
|
|
)
|
|
log.info(
|
|
"token_risk_complete",
|
|
address=address[:12],
|
|
risk_score=risk.risk_score,
|
|
risk_level=risk.risk_level.value,
|
|
)
|
|
return risk
|
|
|
|
async def scan(self, req: TokenScanRequest) -> TokenScanResult:
|
|
"""Full token scan. Multi-module. Used for free-tier scanner."""
|
|
log.info("token_scan_started", address=req.address[:12], chain=req.chain, tier=req.tier)
|
|
detail = await self._repo.get_detail(req.address, req.chain)
|
|
risk = await self.get_risk(req.address, req.chain)
|
|
|
|
# Convert risk flags to the legacy format (list of strings)
|
|
risk_flags: list[str] = []
|
|
warnings: list[str] = []
|
|
for flag in risk.flags:
|
|
code = flag.get("code", "")
|
|
message = flag.get("message", "")
|
|
if code:
|
|
risk_flags.append(code)
|
|
if message:
|
|
warnings.append(message)
|
|
|
|
# Heuristic safety score (inverse of risk, 0-100)
|
|
safety_score = max(0.0, 100.0 - float(risk.risk_score))
|
|
|
|
result = TokenScanResult(
|
|
address=req.address,
|
|
chain=req.chain,
|
|
safety_score=safety_score,
|
|
risk_score=risk.risk_score,
|
|
risk_level=risk.risk_level,
|
|
risk_flags=risk_flags,
|
|
warnings=warnings,
|
|
modules_run=["analyzer", "holders", "liquidity"],
|
|
metadata={
|
|
"name": detail.name,
|
|
"symbol": detail.symbol,
|
|
"decimals": detail.decimals,
|
|
"total_supply": detail.total_supply,
|
|
"verified": detail.verified,
|
|
},
|
|
)
|
|
log.info(
|
|
"token_scan_complete",
|
|
address=req.address[:12],
|
|
safety_score=safety_score,
|
|
risk_level=risk.risk_level.value,
|
|
)
|
|
return result
|