feat(core): add support for creator fee update

This commit is contained in:
smypmsa
2025-05-12 20:07:51 +00:00
parent d979427662
commit e41a5f4f4f
18 changed files with 5951 additions and 441 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ rpc_endpoint: "${SOLANA_NODE_RPC_ENDPOINT}"
wss_endpoint: "${SOLANA_NODE_WSS_ENDPOINT}"
private_key: "${SOLANA_PRIVATE_KEY}"
enabled: true # You can turn off the bot w/o removing its config
enabled: false # You can turn off the bot w/o removing its config
separate_process: true
# Geyser configuration (fastest method for getting updates)
+72
View File
@@ -0,0 +1,72 @@
# This file defines comprehensive parameters and settings for the trading bot.
# Carefully review and adjust values to match your trading strategy and risk tolerance.
# Bot identification and connection settings
name: "bot-sniper-2"
env_file: ".env"
rpc_endpoint: "${SOLANA_NODE_RPC_ENDPOINT}"
wss_endpoint: "${SOLANA_NODE_WSS_ENDPOINT}"
private_key: "${SOLANA_PRIVATE_KEY}"
enabled: true # You can turn off the bot w/o removing its config
separate_process: true
# Geyser configuration (fastest method for getting updates)
geyser:
endpoint: "${GEYSER_ENDPOINT}"
api_token: "${GEYSER_API_TOKEN}"
auth_type: "basic" # or "x-token"
# Trading parameters
# Control trade execution: amount of SOL per trade and acceptable price deviation
trade:
buy_amount: 0.0001 # Amount of SOL to spend when buying (in SOL)
buy_slippage: 0.3 # Maximum acceptable price deviation (0.3 = 30%)
sell_slippage: 0.3
# EXTREME FAST mode configuration
# When enabled, skips waiting for the bonding curve to stabilize and RPC price check.
# The bot buys the specified number of tokens directly, making the process faster but less precise.
extreme_fast_mode: true
extreme_fast_token_amount: 20 # Amount of tokens to buy
# Priority fee configuration
# Manage transaction speed and cost on the Solana network.
# Note: dynamic mode requires an additional RPC call, which slows down the buying process.
priority_fees:
enable_dynamic: false # Use latest transactions to estimate required fee (getRecentPrioritizationFees)
enable_fixed: true # Use fixed amount below
fixed_amount: 200_000 # Base fee in microlamports
extra_percentage: 0.0 # Percentage increase on riority fee regardless of the calculation method (0.1 = 10%)
hard_cap: 200_000 # Maximum allowable fee in microlamports to prevent excessive spending
# Filters for token selection
filters:
match_string: null # Only process tokens with this string in name/symbol
bro_address: null # Only trade tokens created by this user address
listener_type: "blocks" # Method for detecting new tokens: "logs", "blocks", or "geyser"
max_token_age: 0.001 # Maximum token age in seconds for processing
marry_mode: false # Only buy tokens, skip selling
yolo_mode: false # Continuously trade tokens
# Retry and timeout settings
retries:
max_attempts: 1 # Number of attempts for transaction submission
wait_after_creation: 15 # Seconds to wait after token creation (only if EXTREME FAST is disabled)
wait_after_buy: 15 # Holding period after buy transaction
wait_before_new_token: 15 # Pause between token trades
# Token and account management
cleanup:
# Cleanup mode determines when to manage token accounts. Options:
# "disabled": no cleanup will occur.
# "on_fail": only clean up if a buy transaction fails.
# "after_sell": clean up after selling.
# "post_session": clean up all empty accounts after a trading session ends.
mode: "post_session"
force_close_with_burn: false # Force burning remaining tokens before closing account
with_priority_fee: false # Use priority fees for cleanup transactions
# Node provider configuration (not implemented)
node:
max_rps: 25 # Maximum requests per second
+2718 -421
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -11,6 +11,7 @@ import json
import os
import struct
import base58
import websockets
from dotenv import load_dotenv
from solders.pubkey import Pubkey
@@ -37,8 +38,8 @@ def decode_create_instruction(ix_data, ix_def, accounts):
offset += 4
value = ix_data[offset : offset + length].decode("utf-8")
offset += length
elif arg["type"] == "publicKey":
value = base64.b64encode(ix_data[offset : offset + 32]).decode("utf-8")
elif arg["type"] == "pubkey":
value = base58.b58encode(ix_data[offset : offset + 32]).decode("utf-8")
offset += 32
else:
raise ValueError(f"Unsupported type: {arg['type']}")
@@ -77,14 +77,22 @@ def decode_create_instruction(ix_data: bytes, keys, accounts) -> dict:
offset += length
return value
def read_pubkey():
nonlocal offset
value = base58.b58encode(ix_data[offset : offset + 32]).decode("utf-8")
offset += 32
return value
name = read_string()
symbol = read_string()
uri = read_string()
creator = read_pubkey()
token_info = {
"name": name,
"symbol": symbol,
"uri": uri,
"creator": creator,
"mint": get_account_key(0),
"metadata": get_account_key(1),
"bonding_curve": get_account_key(2),
@@ -105,6 +113,7 @@ def print_token_info(info, signature):
print(f"Mint: {info['mint']}")
print(f"Bonding curve: {info['bonding_curve']}")
print(f"Associated bonding curve: {info['associated_bonding_curve']}")
print(f"Creator: {info['creator']}")
print(f"Signature: {signature}")
@@ -54,6 +54,7 @@ def parse_create_instruction(data):
("mint", "publicKey"),
("bondingCurve", "publicKey"),
("user", "publicKey"),
("creator", "publicKey"),
]
try:
@@ -36,6 +36,7 @@ def parse_create_instruction(data):
("mint", "publicKey"),
("bondingCurve", "publicKey"),
("user", "publicKey"),
("creator", "publicKey"),
]
try:
+8 -3
View File
@@ -5,7 +5,7 @@ Bonding curve operations for pump.fun tokens.
import struct
from typing import Final
from construct import Flag, Int64ul, Struct
from construct import Bytes, Flag, Int64ul, Struct
from solders.pubkey import Pubkey
from core.client import SolanaClient
@@ -28,6 +28,7 @@ class BondingCurveState:
"real_sol_reserves" / Int64ul,
"token_total_supply" / Int64ul,
"complete" / Flag,
"creator" / Bytes(32), # Added new creator field - 32 bytes for Pubkey
)
def __init__(self, data: bytes) -> None:
@@ -44,6 +45,10 @@ class BondingCurveState:
parsed = self._STRUCT.parse(data[8:])
self.__dict__.update(parsed)
# Convert raw bytes to Pubkey for creator field
if hasattr(self, 'creator') and isinstance(self.creator, bytes):
self.creator = Pubkey.from_bytes(self.creator)
def calculate_price(self) -> float:
"""Calculate token price in SOL.
@@ -103,8 +108,8 @@ class BondingCurveManager:
return BondingCurveState(account.data)
except Exception as e:
logger.error(f"Failed to get curve state: {str(e)}")
raise ValueError(f"Invalid curve state: {str(e)}")
logger.error(f"Failed to get curve state: {e!s}")
raise ValueError(f"Invalid curve state: {e!s}")
async def calculate_price(self, curve_address: Pubkey) -> float:
"""Calculate the current price of a token.
+29 -4
View File
@@ -7,9 +7,11 @@ import json
import struct
from typing import Any
import base58
from solders.pubkey import Pubkey
from solders.transaction import VersionedTransaction
from core.pubkeys import PumpAddresses
from trading.base import TokenInfo
from utils.logger import get_logger
@@ -41,7 +43,7 @@ class PumpEventProcessor:
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)}")
logger.error(f"Failed to load IDL: {e!s}")
# Create a minimal IDL with just what we need
return {
"instructions": [
@@ -111,6 +113,8 @@ class PumpEventProcessor:
decoded_args = self._decode_create_instruction(
ix_data, create_ix, account_keys
)
creator = Pubkey.from_string(decoded_args["creator"])
creator_vault = self._find_creator_vault(creator)
return TokenInfo(
name=decoded_args["name"],
@@ -122,10 +126,12 @@ class PumpEventProcessor:
decoded_args["associatedBondingCurve"]
),
user=Pubkey.from_string(decoded_args["user"]),
creator=creator,
creator_vault=creator_vault,
)
except Exception as e:
logger.error(f"Error processing transaction: {str(e)}")
logger.error(f"Error processing transaction: {e!s}")
return None
@@ -151,8 +157,8 @@ class PumpEventProcessor:
offset += 4
value = ix_data[offset : offset + length].decode("utf-8")
offset += length
elif arg["type"] == "publicKey":
value = base64.b64encode(ix_data[offset : offset + 32]).decode("utf-8")
elif arg["type"] == "pubkey":
value = base58.b58encode(ix_data[offset : offset + 32]).decode("utf-8")
offset += 32
else:
logger.warning(f"Unsupported type: {arg['type']}")
@@ -166,3 +172,22 @@ class PumpEventProcessor:
args["user"] = str(accounts[7])
return args
def _find_creator_vault(self, creator: Pubkey) -> Pubkey:
"""
Find the creator vault for a creator.
Args:
creator: Creator address
Returns:
Creator vault address
"""
derived_address, _ = Pubkey.find_program_address(
[
b"creator-vault",
bytes(creator)
],
PumpAddresses.PROGRAM,
)
return derived_address
+32
View File
@@ -5,8 +5,10 @@ Event processing for pump.fun tokens using Geyser data.
import struct
from typing import Final
import base58
from solders.pubkey import Pubkey
from core.pubkeys import PumpAddresses
from trading.base import TokenInfo
from utils.logger import get_logger
@@ -55,6 +57,12 @@ class GeyserEventProcessor:
offset += length
return value
def read_pubkey():
nonlocal offset
value = base58.b58encode(instruction_data[offset : offset + 32]).decode("utf-8")
offset += 32
return Pubkey.from_string(value)
# Helper to get account key
def get_account_key(index):
if index >= len(accounts):
@@ -67,11 +75,14 @@ class GeyserEventProcessor:
name = read_string()
symbol = read_string()
uri = read_string()
creator = read_pubkey()
mint = get_account_key(0)
bonding_curve = get_account_key(2)
associated_bonding_curve = get_account_key(3)
user = get_account_key(7)
creator_vault = self._find_creator_vault(creator)
if not all([mint, bonding_curve, associated_bonding_curve, user]):
logger.warning("Missing required account keys in token creation")
@@ -85,8 +96,29 @@ class GeyserEventProcessor:
bonding_curve=bonding_curve,
associated_bonding_curve=associated_bonding_curve,
user=user,
creator=creator,
creator_vault=creator_vault,
)
except Exception as e:
logger.error(f"Failed to process transaction data: {e}")
return None
def _find_creator_vault(self, creator: Pubkey) -> Pubkey:
"""
Find the creator vault for a creator.
Args:
creator: Creator address
Returns:
Creator vault address
"""
derived_address, _ = Pubkey.find_program_address(
[
b"creator-vault",
bytes(creator)
],
PumpAddresses.PROGRAM,
)
return derived_address
+25 -1
View File
@@ -9,7 +9,7 @@ from typing import Final
import base58
from solders.pubkey import Pubkey
from core.pubkeys import SystemAddresses
from core.pubkeys import PumpAddresses, SystemAddresses
from trading.base import TokenInfo
from utils.logger import get_logger
@@ -62,6 +62,8 @@ class LogsEventProcessor:
associated_curve = self._find_associated_bonding_curve(
mint, bonding_curve
)
creator = Pubkey.from_string(parsed_data["creator"])
creator_vault = self._find_creator_vault(creator)
return TokenInfo(
name=parsed_data["name"],
@@ -71,6 +73,8 @@ class LogsEventProcessor:
bonding_curve=bonding_curve,
associated_bonding_curve=associated_curve,
user=Pubkey.from_string(parsed_data["user"]),
creator=creator,
creator_vault=creator_vault,
)
except Exception as e:
logger.error(f"Failed to process log data: {e}")
@@ -106,6 +110,7 @@ class LogsEventProcessor:
("mint", "publicKey"),
("bondingCurve", "publicKey"),
("user", "publicKey"),
("creator", "publicKey"),
]
try:
@@ -149,3 +154,22 @@ class LogsEventProcessor:
SystemAddresses.ASSOCIATED_TOKEN_PROGRAM,
)
return derived_address
def _find_creator_vault(self, creator: Pubkey) -> Pubkey:
"""
Find the creator vault for a creator.
Args:
creator: Creator address
Returns:
Creator vault address
"""
derived_address, _ = Pubkey.find_program_address(
[
b"creator-vault",
bytes(creator)
],
PumpAddresses.PROGRAM,
)
return derived_address
+6
View File
@@ -22,6 +22,8 @@ class TokenInfo:
bonding_curve: Pubkey
associated_bonding_curve: Pubkey
user: Pubkey
creator: Pubkey
creator_vault: Pubkey
@classmethod
def from_dict(cls, data: dict[str, Any]) -> "TokenInfo":
@@ -41,6 +43,8 @@ class TokenInfo:
bonding_curve=Pubkey.from_string(data["bondingCurve"]),
associated_bonding_curve=Pubkey.from_string(data["associatedBondingCurve"]),
user=Pubkey.from_string(data["user"]),
creator=Pubkey.from_string(data["creator"]),
creator_vault=Pubkey.from_string(data["creator_vault"]),
)
def to_dict(self) -> dict[str, str]:
@@ -57,6 +61,8 @@ class TokenInfo:
"bondingCurve": str(self.bonding_curve),
"associatedBondingCurve": str(self.associated_bonding_curve),
"user": str(self.user),
"creator": str(self.creator),
"creatorVault": str(self.creator_vault),
}
+1 -1
View File
@@ -176,7 +176,7 @@ class TokenBuyer(Trader):
pubkey=SystemAddresses.TOKEN_PROGRAM, is_signer=False, is_writable=False
),
AccountMeta(
pubkey=SystemAddresses.RENT, is_signer=False, is_writable=False
pubkey=token_info.creator_vault, is_signer=False, is_writable=True
),
AccountMeta(
pubkey=PumpAddresses.EVENT_AUTHORITY, is_signer=False, is_writable=False
+3 -5
View File
@@ -128,7 +128,7 @@ class TokenSeller(Trader):
)
except Exception as e:
logger.error(f"Sell operation failed: {str(e)}")
logger.error(f"Sell operation failed: {e!s}")
return TradeResult(success=False, error_message=str(e))
async def _send_sell_transaction(
@@ -175,9 +175,7 @@ class TokenSeller(Trader):
pubkey=SystemAddresses.PROGRAM, is_signer=False, is_writable=False
),
AccountMeta(
pubkey=SystemAddresses.ASSOCIATED_TOKEN_PROGRAM,
is_signer=False,
is_writable=False,
pubkey=token_info.creator_vault, is_signer=False, is_writable=True,
),
AccountMeta(
pubkey=SystemAddresses.TOKEN_PROGRAM, is_signer=False, is_writable=False
@@ -209,5 +207,5 @@ class TokenSeller(Trader):
),
)
except Exception as e:
logger.error(f"Sell transaction failed: {str(e)}")
logger.error(f"Sell transaction failed: {e!s}")
raise
+2 -1
View File
@@ -39,7 +39,8 @@ class TestTokenCallback:
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"User: {token_info.user}")
print(f"Creator: {token_info.creator}")
print(f"Bonding Curve: {token_info.bonding_curve}")
print(f"Associated Bonding Curve: {token_info.associated_bonding_curve}")
print(f"{'=' * 50}\n")
+2 -1
View File
@@ -39,7 +39,8 @@ class TestTokenCallback:
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"User: {token_info.user}")
print(f"Creator: {token_info.creator}")
print(f"Bonding Curve: {token_info.bonding_curve}")
print(f"Associated Bonding Curve: {token_info.associated_bonding_curve}")
print(f"{'=' * 50}\n")
+2 -1
View File
@@ -39,7 +39,8 @@ class TestTokenCallback:
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"User: {token_info.user}")
print(f"Creator: {token_info.creator}")
print(f"Bonding Curve: {token_info.bonding_curve}")
print(f"Associated Bonding Curve: {token_info.associated_bonding_curve}")
print(f"{'=' * 50}\n")