mirror of
https://github.com/chainstacklabs/pumpfun-bonkfun-bot.git
synced 2026-08-04 19:27:44 +00:00
feat(pumpfun): migrate to buy_v2/sell_v2 and support non-SOL quote assets (#176)
Refresh the vendored IDLs from pump-fun/pump-public-docs @ 9c82f61 and move all pump.fun trading onto the v2 instruction interface. This is required, not optional: legacy buy/sell cannot trade coins paired against a quote asset other than SOL, and USDC is already whitelisted in the on-chain Global account. Protocol changes absorbed: - buy_v2 (27 accounts) / sell_v2 (26 accounts) replace the legacy instructions. Every account is mandatory and the order is identical for all coins, so the conditional cashback/mayhem account lists are gone. Legacy remains available via PumpFunInstructionBuilder(use_legacy_instructions=True). - BondingCurve is 151 bytes: virtual_sol_reserves -> virtual_quote_reserves, real_sol_reserves -> real_quote_reserves, plus quote_mint at offset 83. Old field names are kept as aliases so existing callers keep working. - v2 instruction data drops the track_volume OptionBool; amounts are in the quote mint's raw units rather than always lamports. - create_v2 carries a non-SOL quote mint as optional remaining accounts 17-19, and CreateEvent gained quote_mint, so extreme_fast_mode can resolve the quote asset without an extra fetch. USDC support: new trade.quote_amounts and filters.allowed_quote_mints config, accepting "sol"/"usdc" aliases or raw mints. Amounts are per-quote-mint because 1 USDC and 1 SOL are not interchangeable. A coin whose quote mint has no configured amount is skipped rather than traded at the wrong size, so SOL-only configs are unaffected. Bug fixes found while verifying: - The logs and blocks listeners set no websocket max_size, so any frame over 1 MiB closed the connection with 1009 and the token in it was lost. Raised to 32 MiB. - PumpSwap priced against the raw quote vault balance, ignoring the new Pool.virtual_quote_reserves (i128 at offset 245; live pools are 301 bytes). Upstream's note that this field is 0 everywhere is out of date: a live pool carries 17.58 SOL against a 148 SOL vault, a 10.15% price error. - The seller read curve state once at confirmed commitment and silently fell back to create-time values, risking a stale creator_vault and ConstraintSeeds. It now retries at processed, matching the buyer. - Account cleanup would burn wrapped SOL when force_burn was set, destroying value that closing the account returns. WSOL is now closed without burning. - The mint scripts treated a landed transaction as a successful one, so a reverted buy printed as success. They now assert the on-chain result. Compute unit limits retuned from mainnet measurements: buy 100k -> 180k, sell 60k -> 120k. Mint-and-buy is no longer atomic, because create_v2 plus buy_v2 exceeds the 1232-byte transaction limit; both mint scripts send two transactions. Adds learning-examples/pump_v2.py as one shared, standalone v2 toolkit for the example scripts, and three verification scripts: an offline layout check against the IDL, a no-funds mainnet simulation, and a live listener matrix that buys, sells and closes the ATA per listener. Verified on mainnet: all four listeners (geyser, logs, blocks, pumpportal) and all eight example scripts completed a real buy, sell and ATA close, each confirmed by reading the transaction result back rather than trusting confirmation alone. The USDC path is verified structurally only; no USDC-paired coin could be found on-chain to exercise it. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -188,6 +188,10 @@ def validate_config(config: dict) -> None:
|
||||
if "Missing required config key" not in str(e):
|
||||
raise
|
||||
|
||||
# Quote-asset configuration must resolve before the bot starts trading,
|
||||
# otherwise a bad mint alias only surfaces on the first non-SOL coin.
|
||||
validate_quote_config(config)
|
||||
|
||||
# Platform-specific validation
|
||||
platform_str = config.get("platform", "pump_fun")
|
||||
try:
|
||||
@@ -201,6 +205,35 @@ def validate_config(config: dict) -> None:
|
||||
raise
|
||||
|
||||
|
||||
def validate_quote_config(config: dict) -> None:
|
||||
"""Validate trade.quote_amounts and filters.allowed_quote_mints.
|
||||
|
||||
Args:
|
||||
config: Loaded bot configuration
|
||||
|
||||
Raises:
|
||||
ValueError: If a quote mint alias/address or amount is invalid
|
||||
"""
|
||||
from core.pubkeys import resolve_quote_amounts, resolve_quote_mint
|
||||
|
||||
quote_amounts = config.get("trade", {}).get("quote_amounts")
|
||||
if quote_amounts is not None:
|
||||
if not isinstance(quote_amounts, dict):
|
||||
raise ValueError(
|
||||
"trade.quote_amounts must be a mapping of quote mint to amount"
|
||||
)
|
||||
resolve_quote_amounts(quote_amounts)
|
||||
|
||||
allowed = config.get("filters", {}).get("allowed_quote_mints")
|
||||
if allowed is not None:
|
||||
if not isinstance(allowed, list) or not allowed:
|
||||
raise ValueError(
|
||||
"filters.allowed_quote_mints must be a non-empty list of quote mints"
|
||||
)
|
||||
for mint in allowed:
|
||||
resolve_quote_mint(mint)
|
||||
|
||||
|
||||
def validate_platform_config(config: dict, platform: Platform) -> None:
|
||||
"""Validate platform-specific configuration requirements."""
|
||||
# Check if platform is supported
|
||||
|
||||
Reference in New Issue
Block a user