from typing import Dict, List from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException from sqlalchemy.orm import Session from app.db.session import get_db from app.models.models import ArbitrageEvent from app.schemas.schemas import (ArbitrageEventResponse, ScanRequest, ScanStatusResponse) from app.services.scanner import BlockScanner from app.services.solana_client import SolanaClient router = APIRouter() @router.post("/scan", response_model=ScanStatusResponse) async def scan_for_arbitrage( background_tasks: BackgroundTasks, scan_request: ScanRequest, db: Session = Depends(get_db), ): """ Scan the blockchain for arbitrage transactions """ scanner = BlockScanner(db) # Check if scan is already in progress if scanner.scan_in_progress: return ScanStatusResponse( last_scanned_block=scanner.get_last_scanned_block() or 0, last_scan_time=scanner.get_scan_status()["last_scan_time"], arbitrage_events_count=scanner.get_scan_status()["arbitrage_events_count"], scan_in_progress=True, message="Scan already in progress, please wait for it to complete" ) try: # Verify Solana connection before starting scan client = SolanaClient() latest_block = client.get_latest_block() if not latest_block: return ScanStatusResponse( last_scanned_block=scanner.get_last_scanned_block() or 0, last_scan_time=scanner.get_scan_status()["last_scan_time"], arbitrage_events_count=scanner.get_scan_status()["arbitrage_events_count"], scan_in_progress=False, message="Failed to connect to Solana RPC. Please check your connection and try again." ) except Exception as e: return ScanStatusResponse( last_scanned_block=scanner.get_last_scanned_block() or 0, last_scan_time=scanner.get_scan_status()["last_scan_time"], arbitrage_events_count=scanner.get_scan_status()["arbitrage_events_count"], scan_in_progress=False, message=f"Error connecting to Solana RPC: {str(e)}" ) # Add scanning task to background tasks background_tasks.add_task( scanner.scan_blocks, num_blocks=scan_request.num_blocks, start_slot=scan_request.start_slot, ) # Return current scan status status = scanner.get_scan_status() status["message"] = "Scan started successfully" return ScanStatusResponse(**status) @router.get("/status", response_model=ScanStatusResponse) async def get_scan_status( db: Session = Depends(get_db), ): """ Get the current status of the arbitrage scanner """ scanner = BlockScanner(db) status = scanner.get_scan_status() # Add helpful message based on status if status["scan_in_progress"]: status["message"] = "Scan is currently in progress" elif status["last_scanned_block"] == 0: status["message"] = "No blocks have been scanned yet. Use the /scan endpoint to start scanning." else: status["message"] = f"Last scan completed at {status['last_scan_time']}. Found {status['arbitrage_events_count']} arbitrage events." return ScanStatusResponse(**status) @router.get("/events", response_model=List[ArbitrageEventResponse]) async def get_arbitrage_events( skip: int = 0, limit: int = 100, min_confidence: float = 0.0, token_address: str = None, db: Session = Depends(get_db), ): """ Get detected arbitrage events """ query = db.query(ArbitrageEvent) # Apply filters if min_confidence > 0: query = query.filter(ArbitrageEvent.confidence_score >= min_confidence) if token_address: query = query.filter(ArbitrageEvent.profit_token_address == token_address) # Get paginated results events = query.order_by(ArbitrageEvent.detected_at.desc()).offset(skip).limit(limit).all() # Convert to response format result = [] for event in events: # Get transaction signature and block slot transaction = event.transaction block_slot = transaction.block_id if transaction else 0 result.append( ArbitrageEventResponse( id=event.id, transaction_signature=transaction.signature if transaction else "", profit_token_address=event.profit_token_address, profit_amount=event.profit_amount, profit_usd=event.profit_usd, path=event.path, confidence_score=event.confidence_score, detected_at=event.detected_at, block_slot=block_slot, ) ) return result @router.get("/events/{event_id}", response_model=ArbitrageEventResponse) async def get_arbitrage_event( event_id: int, db: Session = Depends(get_db), ): """ Get a specific arbitrage event by ID """ event = db.query(ArbitrageEvent).filter(ArbitrageEvent.id == event_id).first() if not event: raise HTTPException(status_code=404, detail="Arbitrage event not found") # Get transaction signature and block slot transaction = event.transaction block_slot = transaction.block_id if transaction else 0 return ArbitrageEventResponse( id=event.id, transaction_signature=transaction.signature if transaction else "", profit_token_address=event.profit_token_address, profit_amount=event.profit_amount, profit_usd=event.profit_usd, path=event.path, confidence_score=event.confidence_score, detected_at=event.detected_at, block_slot=block_slot, ) @router.get("/debug/solana-connection", response_model=Dict) async def debug_solana_connection(): """ Debug endpoint to test Solana RPC connection """ client = SolanaClient() results = {} try: # Test getting latest blockhash latest_block = client.get_latest_block() results["latest_blockhash"] = latest_block # Try to get a slot slot_response = client.client.get_slot() if "result" in slot_response: results["current_slot"] = slot_response["result"] # Try to get a block try: block_data = client.get_block(slot_response["result"]) results["block_data"] = {"has_data": block_data is not None} if block_data: results["block_data"]["parent_slot"] = block_data.get("parentSlot") results["block_data"]["transactions_count"] = len(block_data.get("transactions", [])) except Exception as e: results["block_error"] = str(e) except Exception as e: results["error"] = str(e) # Add RPC URL info results["rpc_url"] = client.rpc_url return results @router.get("/validate-connection", response_model=Dict) async def validate_solana_connection(): """ Validate the Solana RPC connection and return detailed status """ client = SolanaClient() result = { "status": "success", "rpc_url": client.rpc_url, "connection_valid": False, "message": "", } try: # Test 1: Get latest blockhash latest_blockhash = client.get_latest_block() result["blockhash_test"] = { "success": latest_blockhash is not None, "data": latest_blockhash if latest_blockhash else "Failed to retrieve" } # Test 2: Get current slot try: slot_response = client.client.get_slot() current_slot = slot_response.get("result") if "result" in slot_response else None result["slot_test"] = { "success": current_slot is not None, "data": current_slot if current_slot else "Failed to retrieve" } # If we have a slot, try to get a block if current_slot: try: block = client.get_block(current_slot) result["block_test"] = { "success": block is not None, "data": { "has_transactions": block is not None and "transactions" in block, "transaction_count": len(block.get("transactions", [])) if block else 0 } } except Exception as e: result["block_test"] = { "success": False, "error": str(e) } except Exception as e: result["slot_test"] = { "success": False, "error": str(e) } # Determine overall status if result.get("blockhash_test", {}).get("success", False) and result.get("slot_test", {}).get("success", False): result["connection_valid"] = True result["message"] = "Solana RPC connection is valid and functioning properly." else: result["status"] = "error" result["message"] = "Solana RPC connection is not fully functional." except Exception as e: result["status"] = "error" result["message"] = f"Error validating Solana RPC connection: {str(e)}" return result