""" Registry of instruction parsers """ import base64 import logging from typing import Dict, List, Optional from app.parsers.base import InstructionParser from app.parsers.token_program import TokenProgramParser from app.parsers.serum_dex import SerumDexParser from app.parsers.jupiter import JupiterParser from app.parsers.raydium import RaydiumSwapParser, RaydiumLiquidityParser logger = logging.getLogger(__name__) class InstructionParserRegistry: """ Registry of instruction parsers for different Solana programs """ def __init__(self): """ Initialize the registry with available parsers """ self.parsers: Dict[str, InstructionParser] = {} self.register_default_parsers() def register_parser(self, parser: InstructionParser) -> None: """ Register a parser for a specific program Args: parser: Parser instance to register """ self.parsers[parser.program_id] = parser logger.debug(f"Registered parser for program: {parser.program_id}") def register_default_parsers(self) -> None: """ Register the default set of parsers """ # Register SPL Token Program parser self.register_parser(TokenProgramParser()) # Register DEX parsers self.register_parser(SerumDexParser()) self.register_parser(JupiterParser()) self.register_parser(RaydiumSwapParser()) self.register_parser(RaydiumLiquidityParser()) def get_parser_for_program(self, program_id: str) -> Optional[InstructionParser]: """ Get a parser for a specific program ID Args: program_id: The program ID to get a parser for Returns: Parser instance if one exists for the program, None otherwise """ # First try exact match if program_id in self.parsers: return self.parsers[program_id] # Then try parsers that can handle multiple program IDs for parser in self.parsers.values(): if parser.can_handle(program_id): return parser return None def parse_transaction_instructions(self, transaction_data: Dict) -> List[Dict]: """ Parse all instructions in a transaction Args: transaction_data: Transaction data from RPC response Returns: List of parsed instructions """ parsed_instructions = [] if not transaction_data or "transaction" not in transaction_data: return parsed_instructions tx = transaction_data["transaction"] if "message" not in tx: return parsed_instructions message = tx["message"] # Extract account addresses accounts = message.get("accountKeys", []) # Parse each instruction instructions = message.get("instructions", []) for instruction in instructions: program_id = instruction.get("programId") if not program_id: continue # Get parser for this program parser = self.get_parser_for_program(program_id) # Parse instruction data instruction_data_b64 = instruction.get("data", "") try: instruction_data = base64.b64decode(instruction_data_b64) except Exception: # If not base64, try other formats or skip instruction_data = b"" # Parse the instruction if parser: try: parsed = parser.parse_instruction(instruction, accounts, instruction_data) parsed_instructions.append(parsed) except Exception as e: logger.error(f"Error parsing instruction: {str(e)}") parsed_instructions.append({ "type": "error", "program_id": program_id, "error": str(e), }) else: # No parser available for this program parsed_instructions.append({ "type": "unknown", "program_id": program_id, "data": instruction_data_b64, }) return parsed_instructions # Singleton instance parser_registry = InstructionParserRegistry()