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>
10 KiB
Pump Bot Development Guide
This is a trading bot for pump.fun and letsbonk.fun platforms that snipes new tokens and implements various trading strategies.
Project Structure
src/- Main source codelearning-examples/- Educational scripts and examplesbots/- Bot configuration files (YAML)logs/- Log files from bot executionsidl/- Interface definition files for Solana programs
Bash Commands & Development
Setup Commands
# Install dependencies
uv sync
# Activate virtual environment (Unix/macOS)
source .venv/bin/activate
# Install as editable package
uv pip install -e .
Running the Bot
# Run as installed package
pump_bot
# Run directly
uv run src/bot_runner.py
Learning Examples
# Bonding curve status
uv run learning-examples/bonding-curve-progress/get_bonding_curve_status.py TOKEN_ADDRESS
# Listen to migrations
uv run learning-examples/listen-migrations/listen_logsubscribe.py
uv run learning-examples/listen-migrations/listen_blocksubscribe_old_raydium.py
# Compute associated bonding curve
uv run learning-examples/compute_associated_bonding_curve.py
# Listen to new tokens
uv run learning-examples/listen-new-tokens/listen_logsubscribe_abc.py
uv run learning-examples/listen-new-tokens/compare_listeners.py
Verifying pump.fun v2 trade instructions
# Offline: cross-check buy_v2/sell_v2 account layouts, PDA/ATA derivations,
# instruction encoding and quote-asset config against idl/pump_fun_idl.json
uv run learning-examples/verify_v2_account_layout.py
# Mainnet, no funds moved: simulate buy_v2/sell_v2 for one coin, report CU
uv run learning-examples/simulate_v2_trades.py <MINT>
# Mainnet, no funds moved: run the bot's whole buy path against a fresh coin
uv run learning-examples/simulate_bot_buy_path.py
uv run learning-examples/simulate_bot_buy_path.py --no-extreme-fast
Run all three after any pump.fun program upgrade. The simulations report
unitsConsumed; use it to retune get_buy_compute_unit_limit /
get_sell_compute_unit_limit in platforms/pumpfun/instruction_builder.py.
Code Quality
# Format code
ruff format
# Lint code
ruff check
# Fix linting issues
ruff check --fix
Pump.fun protocol notes (gotchas)
The IDLs under idl/ are vendored verbatim from github.com/pump-fun/pump-public-docs
(idl/pump.json → pump_fun_idl.json, pump_amm.json → pump_swap_idl.json,
pump_fees.json). Refresh them from upstream rather than hand-editing.
Quote assets and the v2 trade instructions (current path)
- pump.fun supports quote assets other than SOL.
BondingCurve.quote_mintisPubkey::default()(all zeros) for SOL-paired coins; USDC (EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v) is whitelisted inGlobal. Legacybuy/sellcannot trade non-SOL-paired coins at all. - The bot uses
buy_v2(27 accounts) andsell_v2(26 accounts). Every account is mandatory and the order is identical for every coin — SOL or USDC paired, mayhem or not, cashback or not.sell_v2isbuy_v2minusglobal_volume_accumulator. Layouts live in_BUY_V2_ACCOUNTS/_SELL_V2_ACCOUNTSinplatforms/pumpfun/instruction_builder.pyand are machine-checked against the IDL bylearning-examples/verify_v2_account_layout.py. - v2 args carry no
track_volumeOptionBool (24-byte data: discriminator + two u64). Volume tracking is unconditional now thatuser_volume_accumulatoris mandatory.max_sol_cost/min_sol_outputare in the quote mint's raw units — lamports for SOL, 1e-6 for USDC. - Even for SOL-paired coins you must pass wrapped SOL as
quote_mint, notPubkey::default(). Transfers still happen in native SOL, and theassociated_quote_*accounts are only seed-constrained — do not create the user's WSOL ATA, it would burn ~0.002 SOL of rent for nothing. - Fee recipients: 24 total, in three sets of 8 (
NORMAL_FEE_RECIPIENTS,RESERVED_FEE_RECIPIENTSfor mayhem coins,BUYBACK_FEE_RECIPIENTS). Every v2 buy/sell needs afee_recipientand abuyback_fee_recipient. sharing_config(PDA["sharing-config", base_mint]) lives under the pump fees program, not the pump program. Easy to derive against the wrong program.
BondingCurve account layout
- The account is 151 bytes: 8-byte discriminator, then
virtual_token_reserves, virtual_quote_reserves, real_token_reserves, real_quote_reserves, token_total_supply(u64 each),complete(1B, offset 48),creator(32B, offset 49),is_mayhem_mode(offset 81),is_cashback_coin(offset 82),quote_mint(32B, offset 83), then 36 reserved zero bytes. The documented struct is 115 bytes; the extra 36 are padding. - The SOL-named fields were renamed:
virtual_sol_reserves→virtual_quote_reserves,real_sol_reserves→real_quote_reserves. The curve manager still exposes the old names as aliases, so pre-existing callers keep working for SOL-paired coins — but anything doing arithmetic must scale by the quote mint's decimals (quote_units_per_token), not a hardcoded 1e9. - PumpSwap
Poolgained a trailingvirtual_quote_reserves: i128(16 bytes, offset 245). Pool fields end at 261; live accounts are 301 bytes with trailing padding. Quote against effective reserves:pool_quote_token_account.amount + virtual_quote_reserves. Upstream's release note claims it is 0 on all pools — that is out of date. Verified on mainnet: pool6Bv1JM1deBPe…carries 17.584505433 SOL of virtual reserves against a 148.455 SOL vault, so quoting off the raw vault balance under-prices by ~10.6%. It isi128, notu64— reading 8 bytes happens to work only while the high half is zero.
Coin creation
- The IDL instruction is
create_v2(snake_case). Args:name (str), symbol (str), uri (str), creator (pubkey), is_mayhem_mode (bool), is_cashback_enabled (OptionBool 1B).OptionBoolis a struct wrapping a single bool — serialized as 1 byte, not 2. create_v2accounts 1-16 are in the IDL; accounts 17-19 are optional remaining accounts (quote_mint,associated_quote_bonding_curve,quote_token_program) appended only for a non-SOL quote mint. All three or none. This is the only way to read a new coin's quote asset from the instruction rather than the event.extreme_fast_modeskips the curve-state price fetch but still refreshes mayhem/cashback/creator/quote_mint from chain, because the wrong quote mint means spending the wrong balance entirely. Event parsers also populatequote_mintfromCreateEvent(which gainedquote_mintandvirtual_quote_reservesas trailing fields).
Legacy instructions (fallback only)
Retained behind PumpFunInstructionBuilder(..., use_legacy_instructions=True).
The IDL under-reports these: buy is 18 accounts on-chain (IDL lists 16) and
sell is 16 non-cashback / 17 cashback (IDL lists 14). The extras are
bonding-curve-v2 (PDA ["bonding-curve-v2", mint]) followed by a buyback fee
recipient (mutable); the cashback sell path also inserts
user_volume_accumulator before bonding-curve-v2. Prefer v2 — it is the
interface pump.fun maintains.
Code Style & Conventions
Python Style (Ruff Configuration)
- Line length: 88 characters
- Indentation: 4 spaces
- Target Python: 3.11+
- Quote style: Double quotes
- Import sorting: Enabled
Linting Rules
- Security best practices (S)
- Type annotations (ANN)
- Exception handling (BLE, TRY)
- Code complexity (C90)
- Pylint conventions (PL)
- No commented-out code (ERA)
Code Organization
- Imports: Standard library, third-party, local imports
- Docstrings: Google-style for functions and classes
- Type hints: Required for all public functions
- Logging: Use
get_logger(__name__)pattern - Error handling: Comprehensive try-catch with proper logging
File Structure Patterns
__init__.pyfiles for all packages- Separate concerns: client, trading, monitoring, platforms
- Abstract base classes in
interfaces/ - Platform-specific implementations in
platforms/
Workflow & Development Practices
Configuration Management
- Environment variables in
.envfile - Bot configurations in YAML files under
bots/ - Platform detection from config files
- Validation of platform-listener combinations
Logging
- Timestamped log files in
logs/directory - Format:
{bot_name}_{timestamp}.log - Different log levels for development vs production
- Centralized logger utility in
utils/logger.py
Trading Architecture
- Universal trader pattern for platform abstraction
- Platform-specific implementations (pumpfun, letsbonk)
- Position tracking and management
- Priority fee management (dynamic/fixed)
Monitoring Systems
- Multiple listener types: logs, blocks, geyser, pumpportal
- Universal listeners with platform abstraction
- Event parsing and processing
- Real-time data stream handling
Development Workflow
- Make changes to source code
- Run
ruff check --fixfor linting - Run
ruff formatfor formatting - Test with learning examples (standalone scripts) before deploying bots
- Use separate processes for production bot instances
Bot Configuration
- YAML-based configuration files
- Environment variable interpolation
- Platform-specific settings
- Trading parameters (slippage, amounts, timeouts)
- Filter configurations for token selection
- Cleanup and account management settings
Testing Strategy
- Learning examples serve as integration tests
- Manual testing with learning scripts
- Configuration validation before bot startup
- Logging verification for debugging
Key Features
- Multi-platform support: pump.fun and letsbonk.fun
- Multiple listening methods: WebSocket logs, block subscription, Geyser
- Trading strategies: Time-based, take profit/stop loss, manual
- Priority fee management: Dynamic and fixed fee strategies
- Account cleanup: Automated token account management
- Extreme fast mode: Skip validation for faster execution
Security Considerations
- Private keys stored in environment variables
- No sensitive data in configuration files
- Comprehensive input validation
- Error handling to prevent crashes
- Rate limiting and retry mechanisms