wip(core): platform aware trading

This commit is contained in:
smypmsa
2025-08-02 13:27:07 +00:00
parent 9ec9eeb793
commit 227ef0b3bd
9 changed files with 699 additions and 506 deletions
+15 -9
View File
@@ -32,7 +32,7 @@ class PlatformRegistry:
def __init__(self):
self._implementations: dict[Platform, dict[str, type]] = {}
self._instances: dict[Platform, PlatformImplementations] = {}
self._instances: dict[tuple[Platform, str], PlatformImplementations] = {}
def register_platform(
self,
@@ -80,13 +80,16 @@ class PlatformRegistry:
if platform not in self._implementations:
raise ValueError(f"Platform {platform} is not registered")
# Check if we already have instances for this platform
if platform in self._instances:
return self._instances[platform]
# Use client address as cache key to allow multiple clients
cache_key = (platform, str(client.rpc_endpoint))
# Check if we already have instances for this platform + client combo
if cache_key in self._instances:
return self._instances[cache_key]
impl_classes = self._implementations[platform]
# Create instances
# Create instances - only curve_manager needs client
address_provider = impl_classes['address_provider']()
instruction_builder = impl_classes['instruction_builder']()
curve_manager = impl_classes['curve_manager'](client)
@@ -100,20 +103,22 @@ class PlatformRegistry:
)
# Cache the instances
self._instances[platform] = implementations
self._instances[cache_key] = implementations
return implementations
def get_platform_implementations(self, platform: Platform) -> PlatformImplementations | None:
def get_platform_implementations(self, platform: Platform, client_endpoint: str) -> PlatformImplementations | None:
"""Get cached platform implementations.
Args:
platform: Platform to get implementations for
client_endpoint: Client endpoint for cache lookup
Returns:
PlatformImplementations if available, None otherwise
"""
return self._instances.get(platform)
cache_key = (platform, client_endpoint)
return self._instances.get(cache_key)
def get_supported_platforms(self) -> list[Platform]:
"""Get list of supported platforms.
@@ -144,7 +149,7 @@ class PlatformFactory:
def _setup_default_platforms(self) -> None:
"""Setup default platform registrations."""
# Import platform implementations dynamically to avoid circular imports
# Import and register pump.fun platform
try:
from platforms.pumpfun import (
PumpFunAddressProvider,
@@ -164,6 +169,7 @@ class PlatformFactory:
except ImportError as e:
print(f"Warning: Could not register pump.fun platform: {e}")
# Import and register LetsBonk platform
try:
from platforms.letsbonk import (
LetsBonkAddressProvider,
+105 -30
View File
@@ -5,11 +5,13 @@ This module parses LetsBonk-specific token creation events from various sources
by implementing the EventParser interface.
"""
import base64
import struct
from time import monotonic
from typing import Any, Final
from solders.pubkey import Pubkey
from solders.transaction import VersionedTransaction
from interfaces.core import EventParser, Platform, TokenInfo
from platforms.letsbonk.address_provider import LetsBonkAddressProvider
@@ -184,6 +186,108 @@ class LetsBonkEventParser(EventParser):
"""
return [self.INITIALIZE_DISCRIMINATOR]
def parse_token_creation_from_block(self, block_data: dict) -> TokenInfo | None:
"""Parse token creation from block data (for block listener).
Args:
block_data: Block data from WebSocket
Returns:
TokenInfo if token creation found, None otherwise
"""
try:
if "transactions" not in block_data:
return None
for tx in block_data["transactions"]:
if not isinstance(tx, dict) or "transaction" not in tx:
continue
# Decode base64 transaction data if needed
tx_data = tx["transaction"]
if isinstance(tx_data, list) and len(tx_data) > 0:
try:
tx_data_encoded = tx_data[0]
tx_data_decoded = base64.b64decode(tx_data_encoded)
transaction = VersionedTransaction.from_bytes(tx_data_decoded)
for ix in transaction.message.instructions:
program_id = transaction.message.account_keys[ix.program_id_index]
# Check if instruction is from LetsBonk program
if str(program_id) != str(self.get_program_id()):
continue
ix_data = bytes(ix.data)
# Check for initialize discriminator
if len(ix_data) >= 8:
discriminator = struct.unpack("<Q", ix_data[:8])[0]
if discriminator == self.INITIALIZE_DISCRIMINATOR_INT:
# Token creation should have substantial data and many accounts
if len(ix_data) <= 8 or len(ix.accounts) < 10:
continue
# Parse the instruction
token_info = self.parse_token_creation_from_instruction(
ix_data, ix.accounts, transaction.message.account_keys
)
if token_info:
return token_info
except Exception:
continue
# Handle already decoded transaction data
elif isinstance(tx_data, dict) and "message" in tx_data:
try:
message = tx_data["message"]
if "instructions" not in message or "accountKeys" not in message:
continue
for ix in message["instructions"]:
if "programIdIndex" not in ix or "accounts" not in ix or "data" not in ix:
continue
program_idx = ix["programIdIndex"]
if program_idx >= len(message["accountKeys"]):
continue
program_id_str = message["accountKeys"][program_idx]
if program_id_str != str(self.get_program_id()):
continue
# Decode instruction data
ix_data = base64.b64decode(ix["data"])
if len(ix_data) >= 8:
discriminator = struct.unpack("<Q", ix_data[:8])[0]
if discriminator == self.INITIALIZE_DISCRIMINATOR_INT:
if len(ix_data) <= 8 or len(ix["accounts"]) < 10:
continue
# Convert account keys to bytes for parsing
account_keys_bytes = [
Pubkey.from_string(key).to_bytes()
for key in message["accountKeys"]
]
token_info = self.parse_token_creation_from_instruction(
ix_data, ix["accounts"], account_keys_bytes
)
if token_info:
return token_info
except Exception:
continue
return None
except Exception:
return None
def _parse_initialize_instruction_data(self, data: bytes) -> dict | None:
"""Parse the initialize instruction data from LetsBonk.
@@ -240,33 +344,4 @@ class LetsBonkEventParser(EventParser):
return parsed_data
except Exception:
return None
def parse_token_creation_from_block(self, block_data: dict) -> list[TokenInfo]:
"""Parse token creations from block data (for block listener).
Args:
block_data: Block data from WebSocket
Returns:
List of TokenInfo for any token creations found
"""
tokens = []
try:
if "transactions" not in block_data:
return tokens
for tx in block_data["transactions"]:
if not isinstance(tx, dict) or "transaction" not in tx:
continue
# Process transaction (implementation would be similar to pump.fun)
# This is a simplified version - full implementation would decode
# the transaction and check for LetsBonk initialize instructions
pass
return tokens
except Exception:
return tokens
return None