import logging import time from typing import Dict, List, Optional, Any import httpx from app.services.dex.base import BaseDexService from app.services.solana import USDC_TOKEN_ADDRESS logger = logging.getLogger(__name__) # Jupiter API V6 endpoints JUPITER_API_BASE = "https://quote-api.jup.ag/v6" PRICE_ENDPOINT = f"{JUPITER_API_BASE}/price" QUOTE_ENDPOINT = f"{JUPITER_API_BASE}/quote" class JupiterDexService(BaseDexService): """Service for Jupiter DEX price monitoring""" def __init__(self): super().__init__("jupiter") self.http_client = httpx.AsyncClient(timeout=10.0) async def get_token_price(self, token_address: str, quote_token_address: Optional[str] = None) -> Dict[str, Any]: """Get token price from Jupiter""" if not quote_token_address: quote_token_address = USDC_TOKEN_ADDRESS try: params = { "ids": token_address, "vsToken": quote_token_address } response = await self.http_client.get(PRICE_ENDPOINT, params=params) response.raise_for_status() data = response.json() if "data" in data and token_address in data["data"]: price_data = data["data"][token_address] return { "price": float(price_data["price"]), "liquidity": float(price_data.get("liquidity", 0)), "timestamp": int(time.time()), "metadata": { "raw_data": price_data } } else: logger.warning(f"No price data returned from Jupiter for {token_address}") return { "price": 0.0, "liquidity": 0.0, "timestamp": int(time.time()), "metadata": { "error": "No price data returned" } } except Exception as e: logger.error(f"Error getting Jupiter price for {token_address}: {str(e)}") return { "price": 0.0, "liquidity": 0.0, "timestamp": int(time.time()), "metadata": { "error": str(e) } } async def get_token_prices(self, token_addresses: List[str], quote_token_address: Optional[str] = None) -> Dict[str, Dict[str, Any]]: """Get prices for multiple tokens from Jupiter""" if not quote_token_address: quote_token_address = USDC_TOKEN_ADDRESS try: params = { "ids": ",".join(token_addresses), "vsToken": quote_token_address } response = await self.http_client.get(PRICE_ENDPOINT, params=params) response.raise_for_status() data = response.json() result = {} if "data" in data: price_data = data["data"] timestamp = int(time.time()) for token_address in token_addresses: if token_address in price_data: token_price_data = price_data[token_address] result[token_address] = { "price": float(token_price_data["price"]), "liquidity": float(token_price_data.get("liquidity", 0)), "timestamp": timestamp, "metadata": { "raw_data": token_price_data } } else: result[token_address] = { "price": 0.0, "liquidity": 0.0, "timestamp": timestamp, "metadata": { "error": "No price data returned" } } return result except Exception as e: logger.error(f"Error getting Jupiter prices: {str(e)}") timestamp = int(time.time()) return { token_address: { "price": 0.0, "liquidity": 0.0, "timestamp": timestamp, "metadata": { "error": str(e) } } for token_address in token_addresses } async def get_swap_quote(self, input_token: str, output_token: str, amount: float, slippage_bps: int = 50) -> Dict[str, Any]: """ Get a swap quote from Jupiter Args: input_token: Address of the input token output_token: Address of the output token amount: Amount of input token to swap slippage_bps: Slippage tolerance in basis points (1 bps = 0.01%) Returns: Quote data or error """ try: # Convert amount to raw format raw_amount = self.parse_token_amount(input_token, amount) params = { "inputMint": input_token, "outputMint": output_token, "amount": str(raw_amount), "slippageBps": slippage_bps, "onlyDirectRoutes": False, "asLegacyTransaction": False, } response = await self.http_client.get(QUOTE_ENDPOINT, params=params) response.raise_for_status() data = response.json() return data except Exception as e: logger.error(f"Error getting Jupiter swap quote: {str(e)}") return { "error": str(e) }