#!/usr/bin/env python3 """ On-chain Data Router GET /api/onchain/{chain}/{address} """ from fastapi import APIRouter, HTTPException, Path, Query from fastapi.responses import JSONResponse from typing import Optional, List, Dict, Any from datetime import datetime, timezone import logging from backend.services.provider_fallback_manager import fallback_manager from backend.services.data_resolver import get_data_resolver from backend.services.persistence_service import PersistenceService logger = logging.getLogger(__name__) router = APIRouter( prefix="/api/onchain", tags=["On-chain Data"] ) persistence_service = PersistenceService() def create_response(data: Any, source: str, attempted: List[str] = None) -> Dict[str, Any]: """Create standardized response""" return { "data": data, "meta": { "source": source, "generated_at": datetime.now(timezone.utc).isoformat(), "cache_ttl": 120, "attempted": attempted or [source] } } @router.get("/{chain}/{address}") async def get_onchain_data( chain: str = Path(..., description="Blockchain (ethereum, bsc, tron)"), address: str = Path(..., description="Wallet address"), limit: int = Query(50, description="Number of transactions") ): """Get on-chain data for address""" attempted = [] # Try HF Space first try: resolver = await get_data_resolver() onchain_data = await resolver.resolve_onchain_data(chain, address, limit) if onchain_data: attempted.append("hf") return create_response(onchain_data, "hf", attempted) except Exception as e: logger.warning(f"HF Space unavailable: {e}") attempted.append("hf") # Fallback to external APIs based on chain try: endpoint_map = { "ethereum": "/api", "bsc": "/api", "tron": "/api/v1/accounts" } endpoint = endpoint_map.get(chain.lower(), "/api") params_map = { "ethereum": {"module": "account", "action": "txlist", "address": address, "limit": limit}, "bsc": {"module": "account", "action": "txlist", "address": address, "limit": limit}, "tron": {"address": address, "limit": limit} } params = params_map.get(chain.lower(), {"address": address, "limit": limit}) result = await fallback_manager.fetch_with_fallback( endpoint=endpoint, params=params, transform_func=lambda x: x.get("result", []) if isinstance(x, dict) and "result" in x else (x if isinstance(x, list) else []) ) attempted.extend(result.attempted) if result.success: return create_response(result.data, result.source, attempted) except Exception as e: logger.error(f"External APIs failed: {e}") attempted.append("external_apis") # Return error with attempted sources raise HTTPException( status_code=503, detail={ "error": "ONCHAIN_DATA_NOT_AVAILABLE", "chain": chain, "address": address, "attempted": attempted } )