""" Parser for the Jupiter Aggregator swap instructions """ import base64 from typing import Dict, List, Optional, Tuple from app.parsers.base import SwapParser # Jupiter Program IDs JUPITER_PROGRAM_ID = "JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB" # Jupiter V4 JUPITER_V3_PROGRAM_ID = "JUP3c2Uh3WA4Ng34tw6kPd2G4C5BB21Xo36Je1s32Ph" # Jupiter V3 JUPITER_V6_PROGRAM_ID = "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4" # Jupiter V6 class JupiterParser(SwapParser): """ Parser for the Jupiter Aggregator swap instructions """ @property def program_id(self) -> str: return JUPITER_PROGRAM_ID def can_handle(self, program_id: str) -> bool: """ Check if this parser can handle instructions from a given program """ return program_id in [JUPITER_PROGRAM_ID, JUPITER_V3_PROGRAM_ID, JUPITER_V6_PROGRAM_ID] def parse_instruction(self, instruction: Dict, accounts: List[str], instruction_data: bytes) -> Dict: """ Parse a Jupiter instruction """ if not instruction_data: return {"type": "unknown", "error": "No instruction data"} # Get accounts referenced by the instruction instruction_accounts = instruction.get("accounts", []) referenced_accounts = [accounts[idx] for idx in instruction_accounts if idx < len(accounts)] # Basic metadata about the instruction result = { "type": "jupiter_swap", "program": "jupiter_aggregator", "program_id": instruction.get("programId", JUPITER_PROGRAM_ID), "accounts": referenced_accounts, "data": base64.b64encode(instruction_data).decode("utf-8"), } # Jupiter instructions are complex and version-dependent # Here we'll implement a simplified version for demonstration # In Jupiter V4+, the accounts typically follow this pattern: # 0: Token program # 1: User's token account (source) # 2: User's token account (destination) # 3: User's wallet # (additional accounts depend on the route) if len(referenced_accounts) >= 4: result.update({ "source_account": referenced_accounts[1], "destination_account": referenced_accounts[2], "user_wallet": referenced_accounts[3], }) # Note: Parsing the exact amount and path would require deeper # instruction-specific knowledge and likely additional context return result def extract_swap_info(self, parsed_instruction: Dict) -> Tuple[Optional[str], Optional[str], Optional[float], Optional[float]]: """ Extract swap information from a parsed instruction Returns: Tuple of (token_in_address, token_out_address, amount_in, amount_out) """ if parsed_instruction.get("type") != "jupiter_swap": return None, None, None, None # For a full implementation, we would need to: # 1. Look up token mint addresses from the token accounts # 2. Parse amounts from instruction data # 3. Handle different Jupiter versions # This is a simplified implementation source_account = parsed_instruction.get("source_account") destination_account = parsed_instruction.get("destination_account") # We don't have enough information to determine amounts from just the instruction return None, source_account, destination_account, None