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
103
app/domain/wallet/service.py
Normal file
103
app/domain/wallet/service.py
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
"""Service — business logic for wallet analysis and scans.
|
||||
|
||||
Composes the repository (data access) and the analyzer (pure logic).
|
||||
This is the only thing the api/ layer should call.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from app.core.logging import get_logger
|
||||
from app.domain.wallet.analyzer import WalletAnalyzer
|
||||
from app.domain.wallet.models import (
|
||||
Balance,
|
||||
ScanRequest,
|
||||
ScanResult,
|
||||
Transaction,
|
||||
Wallet,
|
||||
WalletAnalysis,
|
||||
)
|
||||
from app.domain.wallet.repository import WalletRepository
|
||||
|
||||
log = get_logger(__name__)
|
||||
|
||||
|
||||
class WalletService:
|
||||
"""Orchestrates wallet data fetch and analysis."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
repo: WalletRepository | None = None,
|
||||
analyzer: WalletAnalyzer | None = None,
|
||||
) -> None:
|
||||
self._repo = repo or WalletRepository()
|
||||
self._analyzer = analyzer or WalletAnalyzer()
|
||||
|
||||
async def analyze(
|
||||
self,
|
||||
address: str,
|
||||
chain: str = "solana",
|
||||
) -> WalletAnalysis:
|
||||
"""Full wallet analysis: balance + transactions + labels → risk-scored result."""
|
||||
wallet = Wallet(address=address, chain=chain)
|
||||
log.info("wallet_analysis_started", address=address[:12], chain=chain)
|
||||
balance = await self._repo.get_balance(wallet)
|
||||
txs = await self._repo.get_transactions(wallet, limit=10)
|
||||
labels = await self._repo.get_labels(wallet)
|
||||
analysis = self._analyzer.analyze(
|
||||
address=address,
|
||||
chain=chain,
|
||||
tokens=balance.tokens,
|
||||
recent_transactions=txs,
|
||||
labels=labels,
|
||||
)
|
||||
# Override total value with the balance's authoritative value
|
||||
analysis.total_value_usd = balance.total_value_usd
|
||||
log.info(
|
||||
"wallet_analysis_complete",
|
||||
address=address[:12],
|
||||
risk_score=analysis.risk_score,
|
||||
risk_level=analysis.risk_level.value,
|
||||
)
|
||||
return analysis
|
||||
|
||||
async def get_balance(self, address: str, chain: str = "solana") -> Balance:
|
||||
"""Just the balance."""
|
||||
return await self._repo.get_balance(Wallet(address=address, chain=chain))
|
||||
|
||||
async def get_transactions(
|
||||
self,
|
||||
address: str,
|
||||
chain: str = "solana",
|
||||
limit: int = 50,
|
||||
) -> list[Transaction]:
|
||||
"""Just the transactions."""
|
||||
return await self._repo.get_transactions(
|
||||
Wallet(address=address, chain=chain),
|
||||
limit=limit,
|
||||
)
|
||||
|
||||
async def scan(self, req: ScanRequest) -> ScanResult:
|
||||
"""Threat scan. Multi-module. Returns a ScanResult.
|
||||
|
||||
The legacy /api/v1/wallet/scan does a freemium rate-limit check
|
||||
before calling the scanner. That check lives in the api/ layer
|
||||
(HTTP concern), not here.
|
||||
"""
|
||||
log.info("wallet_scan_started", address=req.address[:12], chain=req.chain, tier=req.tier)
|
||||
# For now: the scan reuses analyze() + adds module-level flags.
|
||||
analysis = await self.analyze(req.address, req.chain)
|
||||
result = ScanResult(
|
||||
address=analysis.address,
|
||||
chain=analysis.chain,
|
||||
risk_score=analysis.risk_score,
|
||||
risk_level=analysis.risk_level,
|
||||
flags=analysis.flags,
|
||||
modules_run=["analyzer"],
|
||||
)
|
||||
log.info(
|
||||
"wallet_scan_complete",
|
||||
address=req.address[:12],
|
||||
risk_score=result.risk_score,
|
||||
risk_level=result.risk_level.value,
|
||||
flag_count=len(result.flags),
|
||||
)
|
||||
return result
|
||||
Loading…
Add table
Add a link
Reference in a new issue