139 lines
4.5 KiB
Python
139 lines
4.5 KiB
Python
"""
|
|
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() |