174 lines
6.3 KiB
Python
174 lines
6.3 KiB
Python
"""
|
|
Parser for Raydium AMM instructions
|
|
"""
|
|
import base64
|
|
from typing import Dict, List, Optional, Tuple, Any
|
|
|
|
from app.parsers.base import SwapParser, LiquidityParser
|
|
|
|
|
|
# Raydium Program IDs
|
|
RAYDIUM_LIQUIDITY_PROGRAM_ID = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"
|
|
RAYDIUM_SWAP_PROGRAM_ID = "9qvG1zUp8xF1Bi4m6UdRNby1BAAuaDrUxSpv4CmRRMjL"
|
|
|
|
|
|
class RaydiumSwapParser(SwapParser):
|
|
"""
|
|
Parser for Raydium swap instructions
|
|
"""
|
|
|
|
@property
|
|
def program_id(self) -> str:
|
|
return RAYDIUM_SWAP_PROGRAM_ID
|
|
|
|
def parse_instruction(self, instruction: Dict, accounts: List[str], instruction_data: bytes) -> Dict:
|
|
"""
|
|
Parse a Raydium swap 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)]
|
|
|
|
# For Raydium swap, the account list typically follows this pattern:
|
|
# 0: Token program
|
|
# 1: AMM ID
|
|
# 2: AMM authority
|
|
# 3: AMM open orders
|
|
# 4: AMM target orders
|
|
# 5: Pool coin token account
|
|
# 6: Pool pc token account
|
|
# 7: Serum program
|
|
# 8: Serum market
|
|
# 9: Serum bids
|
|
# 10: Serum asks
|
|
# 11: Serum event queue
|
|
# 12: Serum coin vault
|
|
# 13: Serum pc vault
|
|
# 14: Serum vault signer
|
|
# 15: User source token account
|
|
# 16: User destination token account
|
|
# 17: User owner
|
|
|
|
# Basic metadata about the instruction
|
|
result = {
|
|
"type": "raydium_swap",
|
|
"program": "raydium_swap",
|
|
"program_id": RAYDIUM_SWAP_PROGRAM_ID,
|
|
"accounts": referenced_accounts,
|
|
"data": base64.b64encode(instruction_data).decode("utf-8"),
|
|
}
|
|
|
|
# Add specific account references if available
|
|
if len(referenced_accounts) >= 18:
|
|
result.update({
|
|
"amm_id": referenced_accounts[1],
|
|
"pool_coin_account": referenced_accounts[5],
|
|
"pool_pc_account": referenced_accounts[6],
|
|
"serum_market": referenced_accounts[8],
|
|
"user_source_account": referenced_accounts[15],
|
|
"user_destination_account": referenced_accounts[16],
|
|
"user_owner": referenced_accounts[17],
|
|
})
|
|
|
|
# Parse instruction data to get amount_in
|
|
# Note: This is a simplified version. Actual layout may vary.
|
|
try:
|
|
if len(instruction_data) >= 9:
|
|
# First byte is instruction type, next 8 bytes are the amount
|
|
amount_in = int.from_bytes(instruction_data[1:9], byteorder="little")
|
|
result["amount_in"] = amount_in
|
|
|
|
# For demonstration - in a real implementation, we would parse
|
|
# minimum amount out as well
|
|
except Exception as e:
|
|
result["parse_error"] = str(e)
|
|
|
|
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") != "raydium_swap":
|
|
return None, None, None, None
|
|
|
|
# For Raydium, we don't directly get token mint addresses from the instruction
|
|
# We would need to look up the token accounts' mint addresses
|
|
source_account = parsed_instruction.get("user_source_account")
|
|
destination_account = parsed_instruction.get("user_destination_account")
|
|
amount_in = parsed_instruction.get("amount_in")
|
|
|
|
# We don't know the amount_out from just the instruction
|
|
return None, source_account, destination_account, amount_in
|
|
|
|
|
|
class RaydiumLiquidityParser(LiquidityParser):
|
|
"""
|
|
Parser for Raydium liquidity instructions
|
|
"""
|
|
|
|
@property
|
|
def program_id(self) -> str:
|
|
return RAYDIUM_LIQUIDITY_PROGRAM_ID
|
|
|
|
def parse_instruction(self, instruction: Dict, accounts: List[str], instruction_data: bytes) -> Dict:
|
|
"""
|
|
Parse a Raydium liquidity instruction
|
|
"""
|
|
if not instruction_data:
|
|
return {"type": "unknown", "error": "No instruction data"}
|
|
|
|
# First byte is the instruction type
|
|
instruction_type = instruction_data[0]
|
|
|
|
# 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 = {
|
|
"program": "raydium_liquidity",
|
|
"program_id": RAYDIUM_LIQUIDITY_PROGRAM_ID,
|
|
"accounts": referenced_accounts,
|
|
"data": base64.b64encode(instruction_data).decode("utf-8"),
|
|
}
|
|
|
|
# Parse specific instruction types
|
|
if instruction_type == 1: # Deposit liquidity
|
|
result["type"] = "raydium_deposit_liquidity"
|
|
elif instruction_type == 2: # Withdraw liquidity
|
|
result["type"] = "raydium_withdraw_liquidity"
|
|
else:
|
|
result["type"] = f"raydium_instruction_{instruction_type}"
|
|
|
|
return result
|
|
|
|
def extract_liquidity_info(self, parsed_instruction: Dict) -> Dict[str, Any]:
|
|
"""
|
|
Extract liquidity information from a parsed instruction
|
|
"""
|
|
result = {
|
|
"action": None,
|
|
"pool_address": None,
|
|
"tokens": [],
|
|
"amounts": [],
|
|
"user": None,
|
|
}
|
|
|
|
if parsed_instruction.get("type") == "raydium_deposit_liquidity":
|
|
result["action"] = "add"
|
|
elif parsed_instruction.get("type") == "raydium_withdraw_liquidity":
|
|
result["action"] = "remove"
|
|
else:
|
|
return result
|
|
|
|
# In a full implementation, we would extract pool address, tokens, amounts
|
|
# and user from the instruction accounts and data
|
|
|
|
return result |