rmi-backend/app/domain/token/repository.py

94 lines
3.5 KiB
Python

"""Repository — async data fetch for token info via Databus."""
from __future__ import annotations
from typing import Any
from app.core.logging import get_logger
from app.domain.token.models import (
TokenDetail,
TokenHolder,
TokenLiquidity,
)
log = get_logger(__name__)
class TokenRepository:
"""Async data access for token info via Databus."""
async def get_detail(self, address: str, chain: str) -> TokenDetail:
"""Fetch token metadata + supply."""
try:
from app.databus.client import get_databus_client
client = get_databus_client()
raw = await client.get_token_info(address, chain=chain)
return self._parse_detail(address, chain, raw or {})
except Exception as e:
log.warning("token_detail_fetch_failed", address=address[:12], error=str(e))
return TokenDetail(address=address, chain=chain)
async def get_holders(
self,
address: str,
chain: str,
limit: int = 100,
) -> list[TokenHolder]:
"""Fetch top holders."""
try:
from app.databus.client import get_databus_client
client = get_databus_client()
raw = await client.get_token_holders(address, chain=chain, limit=limit)
return [self._parse_holder(h) for h in (raw or [])]
except Exception as e:
log.warning("token_holders_fetch_failed", address=address[:12], error=str(e))
return []
async def get_liquidity(self, address: str, chain: str) -> TokenLiquidity:
"""Fetch liquidity + pool info."""
try:
from app.databus.client import get_databus_client
client = get_databus_client()
raw = await client.get_token_liquidity(address, chain=chain)
return self._parse_liquidity(address, chain, raw or {})
except Exception as e:
log.warning("token_liquidity_fetch_failed", address=address[:12], error=str(e))
return TokenLiquidity(address=address, chain=chain)
@staticmethod
def _parse_detail(address: str, chain: str, raw: dict[str, Any]) -> TokenDetail:
return TokenDetail(
address=address,
chain=chain,
name=raw.get("name", ""),
symbol=raw.get("symbol", ""),
decimals=int(raw.get("decimals", 9) or 9),
total_supply=float(raw.get("total_supply", 0) or 0),
circulating_supply=float(raw.get("circulating_supply", 0) or 0),
holders=int(raw.get("holders", 0) or 0),
verified=bool(raw.get("verified", False)),
metadata=raw.get("metadata", {}) or {},
)
@staticmethod
def _parse_holder(raw: dict[str, Any]) -> TokenHolder:
return TokenHolder(
address=raw.get("address", ""),
balance=float(raw.get("balance", 0) or 0),
percentage=float(raw.get("percentage", raw.get("pct", 0)) or 0),
label=raw.get("label"),
is_contract=bool(raw.get("is_contract", False)),
is_locked=bool(raw.get("is_locked", False)),
)
@staticmethod
def _parse_liquidity(address: str, chain: str, raw: dict[str, Any]) -> TokenLiquidity:
return TokenLiquidity(
address=address,
chain=chain,
total_liquidity_usd=float(raw.get("total_usd", 0) or 0),
pools=raw.get("pools", []) or [],
locked_percentage=float(raw.get("locked_percentage", 0) or 0),
)