mirror of
https://github.com/chainstacklabs/pumpfun-bonkfun-bot.git
synced 2026-08-18 09:48:05 +00:00
minor fixes
This commit is contained in:
@@ -72,10 +72,19 @@ async def main() -> None:
|
|||||||
args = parse_args()
|
args = parse_args()
|
||||||
|
|
||||||
# Get configuration values, preferring command line args over config.py
|
# Get configuration values, preferring command line args over config.py
|
||||||
rpc_endpoint: str | None = args.rpc or os.environ.get("SOLANA_NODE_RPC_ENDPOINT")
|
rpc_endpoint: str | None = (
|
||||||
wss_endpoint: str | None = args.wss or os.environ.get("SOLANA_NODE_WSS_ENDPOINT")
|
args.rpc
|
||||||
|
or os.environ.get("SOLANA_NODE_RPC_ENDPOINT")
|
||||||
|
or config.PUBLIC_RPC_ENDPOINT
|
||||||
|
)
|
||||||
|
wss_endpoint: str | None = (
|
||||||
|
args.wss
|
||||||
|
or os.environ.get("SOLANA_NODE_WSS_ENDPOINT")
|
||||||
|
or config.PUBLIC_WSS_ENDPOINT
|
||||||
|
)
|
||||||
private_key: str | None = args.key or os.environ.get("SOLANA_PRIVATE_KEY")
|
private_key: str | None = args.key or os.environ.get("SOLANA_PRIVATE_KEY")
|
||||||
|
|
||||||
|
# Validate configuration values
|
||||||
if not rpc_endpoint or rpc_endpoint.startswith(("http://", "https://")):
|
if not rpc_endpoint or rpc_endpoint.startswith(("http://", "https://")):
|
||||||
logger.error("Invalid RPC endpoint. Must start with http:// or https://")
|
logger.error("Invalid RPC endpoint. Must start with http:// or https://")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -88,6 +97,7 @@ async def main() -> None:
|
|||||||
logger.error("Invalid private key. Key appears to be missing or too short")
|
logger.error("Invalid private key. Key appears to be missing or too short")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Get trading parameters
|
||||||
buy_amount: float = args.amount if args.amount is not None else config.BUY_AMOUNT
|
buy_amount: float = args.amount if args.amount is not None else config.BUY_AMOUNT
|
||||||
buy_slippage: float = (
|
buy_slippage: float = (
|
||||||
args.buy_slippage if args.buy_slippage is not None else config.BUY_SLIPPAGE
|
args.buy_slippage if args.buy_slippage is not None else config.BUY_SLIPPAGE
|
||||||
@@ -96,6 +106,12 @@ async def main() -> None:
|
|||||||
args.sell_slippage if args.sell_slippage is not None else config.SELL_SLIPPAGE
|
args.sell_slippage if args.sell_slippage is not None else config.SELL_SLIPPAGE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Not implemented parameters
|
||||||
|
enable_dynamic_prior__fee = (
|
||||||
|
config.ENABLE_DYNAMIC_PRIORITY_FEE
|
||||||
|
) # TODO: to be implemented
|
||||||
|
prior_fee_multiplier = config.EXTRA_PRIORITY_FEE # TODO: to be implemented
|
||||||
|
|
||||||
trader: PumpTrader = PumpTrader(
|
trader: PumpTrader = PumpTrader(
|
||||||
rpc_endpoint=rpc_endpoint, # type: ignore
|
rpc_endpoint=rpc_endpoint, # type: ignore
|
||||||
wss_endpoint=wss_endpoint, # type: ignore
|
wss_endpoint=wss_endpoint, # type: ignore
|
||||||
|
|||||||
@@ -6,20 +6,17 @@ Configuration for the pump.fun trading bot.
|
|||||||
BUY_AMOUNT = 0.0001 # Amount of SOL to spend when buying
|
BUY_AMOUNT = 0.0001 # Amount of SOL to spend when buying
|
||||||
BUY_SLIPPAGE = 0.2 # 20% slippage tolerance for buying
|
BUY_SLIPPAGE = 0.2 # 20% slippage tolerance for buying
|
||||||
SELL_SLIPPAGE = 0.2 # 20% slippage tolerance for selling
|
SELL_SLIPPAGE = 0.2 # 20% slippage tolerance for selling
|
||||||
ENABLE_DYNAMIC_PRIORITY_FEE = (
|
ENABLE_DYNAMIC_PRIORITY_FEE = True # TODO: not implemented. getRecentPriorityFee is used to get current priority fee
|
||||||
True # TODO: getRecentPriorityFee is used to get current priority fee
|
EXTRA_PRIORITY_FEE = 0.1 # TODO: not implemented. 10% increase in dynamic priority fee
|
||||||
)
|
|
||||||
EXTRA_PRIORITY_FEE = 0.1 # TODO: 10% increase in dynamic priority fee
|
|
||||||
|
|
||||||
TOKEN_DECIMALS: int = 6
|
# Retries and timeouts
|
||||||
MAX_RETRIES: int = 5
|
MAX_RETRIES: int = 5
|
||||||
WAIT_TIME_AFTER_BUY: int = 20
|
WAIT_TIME_AFTER_BUY: int = 5
|
||||||
WAIT_TIME_BEFORE_NEW_TOKEN: int = 5
|
WAIT_TIME_BEFORE_NEW_TOKEN: int = 5
|
||||||
WAIT_TIME_AFTER_CREATION: int = 15
|
WAIT_TIME_AFTER_CREATION: int = 5
|
||||||
|
|
||||||
# TODO: RPS of your node to avoid rate limit errors
|
# Node provier configuration
|
||||||
# You can also get a trader node https://docs.chainstack.com/docs/solana-trader-nodes
|
# You can also get a trader node https://docs.chainstack.com/docs/solana-trader-nodes
|
||||||
MAX_RPS = 25
|
MAX_RPS = 25 # TODO: not implemented. Max RPS to avoid rate limit errors
|
||||||
|
|
||||||
PUBLIC_RPC_ENDPOINT = "https://api.mainnet-beta.solana.com"
|
PUBLIC_RPC_ENDPOINT = "https://api.mainnet-beta.solana.com"
|
||||||
PUBLIC_WSS_ENDPOINT = "wss://api.mainnet-beta.solana.com"
|
PUBLIC_WSS_ENDPOINT = "wss://api.mainnet-beta.solana.com"
|
||||||
|
|||||||
+1
-2
@@ -3,12 +3,11 @@ Solana client abstraction for blockchain operations.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
from typing import Any, Dict
|
||||||
|
|
||||||
from solana.rpc.async_api import AsyncClient
|
from solana.rpc.async_api import AsyncClient
|
||||||
from solana.rpc.commitment import Confirmed
|
from solana.rpc.commitment import Confirmed
|
||||||
from solana.rpc.types import TxOpts
|
from solana.rpc.types import TxOpts
|
||||||
from solders.instruction import Instruction
|
|
||||||
from solders.keypair import Keypair
|
from solders.keypair import Keypair
|
||||||
from solders.pubkey import Pubkey
|
from solders.pubkey import Pubkey
|
||||||
from solders.transaction import Transaction
|
from solders.transaction import Transaction
|
||||||
|
|||||||
+33
-23
@@ -6,10 +6,8 @@ import asyncio
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any, Dict, List, Optional
|
|
||||||
|
|
||||||
from solders.pubkey import Pubkey
|
|
||||||
|
|
||||||
|
import config
|
||||||
from src.core.client import SolanaClient
|
from src.core.client import SolanaClient
|
||||||
from src.core.curve import BondingCurveManager
|
from src.core.curve import BondingCurveManager
|
||||||
from src.core.pubkeys import PumpAddresses
|
from src.core.pubkeys import PumpAddresses
|
||||||
@@ -120,26 +118,25 @@ class PumpTrader:
|
|||||||
try:
|
try:
|
||||||
await self._save_token_info(token_info)
|
await self._save_token_info(token_info)
|
||||||
|
|
||||||
logger.info("Waiting for 15 seconds for the bonding curve to stabilize...")
|
logger.info(
|
||||||
await asyncio.sleep(15)
|
f"Waiting for {config.WAIT_TIME_AFTER_CREATION} seconds for the bonding curve to stabilize..."
|
||||||
|
)
|
||||||
try:
|
await asyncio.sleep(config.WAIT_TIME_AFTER_CREATION)
|
||||||
token_price = await self.curve_manager.calculate_price(
|
|
||||||
token_info.bonding_curve
|
|
||||||
)
|
|
||||||
logger.info(f"Token price: {token_price:.10f} SOL")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to get token price: {str(e)}")
|
|
||||||
token_price = 0
|
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Buying {self.buy_amount:.6f} SOL worth of {token_info.symbol}..."
|
f"Buying {self.buy_amount:.6f} SOL worth of {token_info.symbol}..."
|
||||||
)
|
)
|
||||||
buy_result = await self.buyer.execute(token_info)
|
buy_result: TradeResult = await self.buyer.execute(token_info)
|
||||||
|
|
||||||
if buy_result.success:
|
if buy_result.success:
|
||||||
logger.info(f"Successfully bought {token_info.symbol}")
|
logger.info(f"Successfully bought {token_info.symbol}")
|
||||||
self._log_trade("buy", token_info, token_price, buy_result.tx_signature)
|
self._log_trade(
|
||||||
|
"buy",
|
||||||
|
token_info,
|
||||||
|
buy_result.price, # type: ignore
|
||||||
|
buy_result.amount, # type: ignore
|
||||||
|
buy_result.tx_signature,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Failed to buy {token_info.symbol}: {buy_result.error_message}"
|
f"Failed to buy {token_info.symbol}: {buy_result.error_message}"
|
||||||
@@ -147,16 +144,22 @@ class PumpTrader:
|
|||||||
|
|
||||||
# Sell token if not in marry mode
|
# Sell token if not in marry mode
|
||||||
if not marry_mode and buy_result.success:
|
if not marry_mode and buy_result.success:
|
||||||
logger.info("Waiting for 20 seconds before selling...")
|
logger.info(
|
||||||
await asyncio.sleep(20)
|
f"Waiting for {config.WAIT_TIME_AFTER_BUY} seconds before selling..."
|
||||||
|
)
|
||||||
|
await asyncio.sleep(config.WAIT_TIME_AFTER_BUY)
|
||||||
|
|
||||||
logger.info(f"Selling {token_info.symbol}...")
|
logger.info(f"Selling {token_info.symbol}...")
|
||||||
sell_result = await self.seller.execute(token_info)
|
sell_result: TradeResult = await self.seller.execute(token_info)
|
||||||
|
|
||||||
if sell_result.success:
|
if sell_result.success:
|
||||||
logger.info(f"Successfully sold {token_info.symbol}")
|
logger.info(f"Successfully sold {token_info.symbol}")
|
||||||
self._log_trade(
|
self._log_trade(
|
||||||
"sell", token_info, token_price, sell_result.tx_signature
|
"sell",
|
||||||
|
token_info,
|
||||||
|
sell_result.price, # type: ignore
|
||||||
|
sell_result.amount, # type: ignore
|
||||||
|
sell_result.tx_signature,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.error(
|
logger.error(
|
||||||
@@ -168,9 +171,9 @@ class PumpTrader:
|
|||||||
# Wait before looking for the next token
|
# Wait before looking for the next token
|
||||||
if yolo_mode:
|
if yolo_mode:
|
||||||
logger.info(
|
logger.info(
|
||||||
"YOLO mode enabled. Waiting 5 seconds before looking for next token..."
|
f"YOLO mode enabled. Waiting {config.WAIT_TIME_BEFORE_NEW_TOKEN} seconds before looking for next token..."
|
||||||
)
|
)
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(config.WAIT_TIME_BEFORE_NEW_TOKEN)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error handling token {token_info.symbol}: {str(e)}")
|
logger.error(f"Error handling token {token_info.symbol}: {str(e)}")
|
||||||
@@ -190,7 +193,12 @@ class PumpTrader:
|
|||||||
logger.info(f"Token information saved to {file_name}")
|
logger.info(f"Token information saved to {file_name}")
|
||||||
|
|
||||||
def _log_trade(
|
def _log_trade(
|
||||||
self, action: str, token_info: TokenInfo, price: float, tx_hash: str | None
|
self,
|
||||||
|
action: str,
|
||||||
|
token_info: TokenInfo,
|
||||||
|
price: float,
|
||||||
|
amount: float,
|
||||||
|
tx_hash: str | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Log trade information.
|
"""Log trade information.
|
||||||
|
|
||||||
@@ -198,6 +206,7 @@ class PumpTrader:
|
|||||||
action: Trade action (buy/sell)
|
action: Trade action (buy/sell)
|
||||||
token_info: Token information
|
token_info: Token information
|
||||||
price: Token price in SOL
|
price: Token price in SOL
|
||||||
|
amount: Trade amount in SOL
|
||||||
tx_hash: Transaction hash
|
tx_hash: Transaction hash
|
||||||
"""
|
"""
|
||||||
os.makedirs("trades", exist_ok=True)
|
os.makedirs("trades", exist_ok=True)
|
||||||
@@ -208,6 +217,7 @@ class PumpTrader:
|
|||||||
"token_address": str(token_info.mint),
|
"token_address": str(token_info.mint),
|
||||||
"symbol": token_info.symbol,
|
"symbol": token_info.symbol,
|
||||||
"price": price,
|
"price": price,
|
||||||
|
"amount": amount,
|
||||||
"tx_hash": tx_hash,
|
"tx_hash": tx_hash,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ Tests websocket monitoring for new pump.fun tokens
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import config
|
||||||
from core.pubkeys import PumpAddresses
|
from core.pubkeys import PumpAddresses
|
||||||
from monitoring.listener import PumpTokenListener
|
from monitoring.listener import PumpTokenListener
|
||||||
from trading.base import TokenInfo
|
from trading.base import TokenInfo
|
||||||
@@ -43,7 +45,7 @@ async def test_pump_token_listener(
|
|||||||
):
|
):
|
||||||
"""Test the token listener functionality"""
|
"""Test the token listener functionality"""
|
||||||
wss_endpoint = os.environ.get(
|
wss_endpoint = os.environ.get(
|
||||||
"SOLANA_NODE_WSS_ENDPOINT", "wss://api.mainnet-beta.solana.com"
|
"SOLANA_NODE_WSS_ENDPOINT", config.PUBLIC_WSS_ENDPOINT
|
||||||
)
|
)
|
||||||
logger.info(f"Connecting to WebSocket: {wss_endpoint}")
|
logger.info(f"Connecting to WebSocket: {wss_endpoint}")
|
||||||
listener = PumpTokenListener(wss_endpoint, PumpAddresses.PROGRAM)
|
listener = PumpTokenListener(wss_endpoint, PumpAddresses.PROGRAM)
|
||||||
@@ -76,7 +78,7 @@ async def test_pump_token_listener(
|
|||||||
|
|
||||||
logger.info(f"Detected {len(callback.detected_tokens)} tokens")
|
logger.info(f"Detected {len(callback.detected_tokens)} tokens")
|
||||||
for token in callback.detected_tokens:
|
for token in callback.detected_tokens:
|
||||||
logger.info(f" - 🪙 {token.name} ({token.symbol}): {token.mint}")
|
logger.info(f" - {token.name} ({token.symbol}): {token.mint}")
|
||||||
|
|
||||||
return callback.detected_tokens
|
return callback.detected_tokens
|
||||||
|
|
||||||
@@ -84,6 +86,7 @@ async def test_pump_token_listener(
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
match_string = None # Update if you want to filter tokens by name/symbol
|
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
|
creator_address = None # Update if you want to filter tokens by creator address
|
||||||
test_duration = 60
|
test_duration = 15
|
||||||
|
|
||||||
logger.info("Starting token listener test")
|
logger.info("Starting token listener test")
|
||||||
asyncio.run(test_pump_token_listener(match_string, creator_address, test_duration))
|
asyncio.run(test_pump_token_listener(match_string, creator_address, test_duration))
|
||||||
|
|||||||
Reference in New Issue
Block a user