diff --git a/config.py b/config.py index 2ce3ba1..518f442 100644 --- a/config.py +++ b/config.py @@ -2,39 +2,6 @@ Configuration for the pump.fun trading bot. """ -from typing import Final - -from solders.pubkey import Pubkey - -# System & pump.fun addresses -PUMP_PROGRAM: Final[Pubkey] = Pubkey.from_string( - "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" -) -PUMP_GLOBAL: Final[Pubkey] = Pubkey.from_string( - "4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf" -) -PUMP_EVENT_AUTHORITY: Final[Pubkey] = Pubkey.from_string( - "Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1" -) -PUMP_FEE: Final[Pubkey] = Pubkey.from_string( - "CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM" -) -PUMP_LIQUIDITY_MIGRATOR: Final[Pubkey] = Pubkey.from_string( - "39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg" -) -SYSTEM_PROGRAM: Final[Pubkey] = Pubkey.from_string("11111111111111111111111111111111") -SYSTEM_TOKEN_PROGRAM: Final[Pubkey] = Pubkey.from_string( - "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" -) -SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM: Final[Pubkey] = Pubkey.from_string( - "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" -) -SYSTEM_RENT: Final[Pubkey] = Pubkey.from_string( - "SysvarRent111111111111111111111111111111111" -) -SOL: Final[Pubkey] = Pubkey.from_string("So11111111111111111111111111111111111111112") -LAMPORTS_PER_SOL: Final[int] = 1_000_000_000 - # Trading parameters BUY_AMOUNT = 0.0001 # Amount of SOL to spend when buying BUY_SLIPPAGE = 0.2 # 20% slippage tolerance for buying @@ -44,7 +11,7 @@ ENABLE_DYNAMIC_PRIORITY_FEE = ( ) EXTRA_PRIORITY_FEE = 0.1 # TODO: 10% increase in dynamic priority fee -TOKEN_DECIMALS: Final[int] = 6 +TOKEN_DECIMALS: int = 6 MAX_RETRIES: int = 5 WAIT_TIME_AFTER_BUY: int = 20 WAIT_TIME_BEFORE_NEW_TOKEN: int = 5 @@ -53,3 +20,6 @@ WAIT_TIME_AFTER_CREATION: int = 15 # TODO: RPS of your node to avoid rate limit errors # You can also get a trader node https://docs.chainstack.com/docs/solana-trader-nodes MAX_RPS = 25 + +PUBLIC_RPC_ENDPOINT = "https://api.mainnet-beta.solana.com" +PUBLIC_WSS_ENDPOINT = "wss://api.mainnet-beta.solana.com" diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..28ea38b --- /dev/null +++ b/setup.py @@ -0,0 +1,8 @@ +from setuptools import find_packages, setup + +setup( + name="pump_bot", + version="0.1", + package_dir={"": "src"}, + packages=find_packages(where="src"), +) diff --git a/src/monitoring/events.py b/src/monitoring/events.py index d23c52d..0069e32 100644 --- a/src/monitoring/events.py +++ b/src/monitoring/events.py @@ -5,13 +5,13 @@ Event processing for pump.fun tokens. import base64 import json import struct -from typing import Any, Callable, Dict, List, Optional +from typing import Any, Dict, List, Optional from solders.pubkey import Pubkey from solders.transaction import VersionedTransaction -from src.trading.base import TokenInfo -from src.utils.logger import get_logger +from trading.base import TokenInfo +from utils.logger import get_logger logger = get_logger(__name__) @@ -38,7 +38,7 @@ class PumpEventProcessor: IDL as dictionary """ try: - with open("idl/pump_fun_idl.json", "r") as f: + with open("idl/pump_fun_idl.json") as f: return json.load(f) except Exception as e: logger.error(f"Failed to load IDL: {str(e)}") diff --git a/src/monitoring/listener.py b/src/monitoring/listener.py index f679aad..b529243 100644 --- a/src/monitoring/listener.py +++ b/src/monitoring/listener.py @@ -4,15 +4,14 @@ WebSocket monitoring for pump.fun tokens. import asyncio import json -import time -from typing import Any, Awaitable, Callable, Dict, Optional +from typing import Awaitable, Callable, Optional import websockets from solders.pubkey import Pubkey -from src.monitoring.events import PumpEventProcessor -from src.trading.base import TokenInfo -from src.utils.logger import get_logger +from monitoring.events import PumpEventProcessor +from trading.base import TokenInfo +from utils.logger import get_logger logger = get_logger(__name__) @@ -61,7 +60,6 @@ class PumpTokenListener: f"New token detected: {token_info.name} ({token_info.symbol})" ) - # Apply filters if match_string and not ( match_string.lower() in token_info.name.lower() or match_string.lower() in token_info.symbol.lower() diff --git a/tests/test_listener.py b/tests/test_listener.py new file mode 100644 index 0000000..e729a6e --- /dev/null +++ b/tests/test_listener.py @@ -0,0 +1,89 @@ +""" +Test script for PumpTokenListener +Tests websocket monitoring for new pump.fun tokens +""" + +import asyncio +import logging +import os +from core.pubkeys import PumpAddresses +from monitoring.listener import PumpTokenListener +from trading.base import TokenInfo + +logging.basicConfig( + level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" +) +logger = logging.getLogger("token-listener-test") + + +class TestTokenCallback: + def __init__(self): + self.detected_tokens = [] + + async def on_token_created(self, token_info: TokenInfo) -> None: + """Process detected token""" + logger.info(f"New token detected: {token_info.name} ({token_info.symbol})") + logger.info(f"Mint: {token_info.mint}") + self.detected_tokens.append(token_info) + print(f"\n{'=' * 50}") + print(f"NEW TOKEN: {token_info.name}") + print(f"Symbol: {token_info.symbol}") + print(f"Mint: {token_info.mint}") + print(f"URI: {token_info.uri}") + print(f"Creator: {token_info.user}") + print(f"Bonding Curve: {token_info.bonding_curve}") + print(f"Associated Bonding Curve: {token_info.associated_bonding_curve}") + print(f"{'=' * 50}\n") + + +async def test_pump_token_listener( + match_string: str | None = None, + creator_address: str | None = None, + test_duration: int = 60, +): + """Test the token listener functionality""" + wss_endpoint = os.environ.get( + "SOLANA_NODE_WSS_ENDPOINT", "wss://api.mainnet-beta.solana.com" + ) + logger.info(f"Connecting to WebSocket: {wss_endpoint}") + listener = PumpTokenListener(wss_endpoint, PumpAddresses.PROGRAM) + callback = TestTokenCallback() + + if match_string: + logger.info(f"Filtering tokens matching: {match_string}") + if creator_address: + logger.info(f"Filtering tokens by creator: {creator_address}") + + listen_task = asyncio.create_task( + listener.listen_for_tokens( + callback.on_token_created, + match_string=match_string, + creator_address=creator_address, + ) + ) + + logger.info(f"Listening for {test_duration} seconds...") + try: + await asyncio.sleep(test_duration) + except KeyboardInterrupt: + logger.info("Test interrupted by user") + finally: + listen_task.cancel() + try: + await listen_task + except asyncio.CancelledError: + pass + + logger.info(f"Detected {len(callback.detected_tokens)} tokens") + for token in callback.detected_tokens: + logger.info(f" - 🪙 {token.name} ({token.symbol}): {token.mint}") + + return callback.detected_tokens + + +if __name__ == "__main__": + match_string = None # Update if you want to filter tokens by name/symbol + creator_address = None # Update if you want to filter tokens by creator address + test_duration = 60 + logger.info("Starting token listener test") + asyncio.run(test_pump_token_listener(match_string, creator_address, test_duration))