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>
17 KiB
Chainstack is the leading suite of services connecting developers with Web3 infrastructure
• Homepage •
Supported protocols •
Chainstack blog •
Blockchain API reference •
• Start for free •
The project allows you to create bots for trading on pump.fun and letsbonk.fun. Its core feature is to snipe new tokens. Besides that, learning examples contain a lot of useful scripts for different types of listeners (new tokens, migrations) and deep dive into calculations required for trading.
For the full walkthrough, see Solana: Creating a trading and sniping pump.fun bot.
For near-instantaneous transaction propagation, you can use the Chainstack Solana Trader nodes.
For instant updates from the network, you can enable Yellowstone gRPC Geyser plugin (Jito ShredStream enabled by default).
The official maintainers are in the MAINTAINERS.md file. Leave your feedback by opening Issues.
Also by Chainstack — if you prefer a terminal interface or want to give an AI agent trading capabilities:
- pumpfun-cli — CLI for trading, launching, and managing tokens on pump.fun; buy, sell, wallet management, and smart routing between bonding curve and PumpSwap AMM.
- pumpclaw — agent skill that equips AI assistants (OpenClaw, Claude Code, Cursor, Codex) with the ability to operate pumpfun-cli.
🚨 SCAM ALERT: Issues section is often targeted by scam bots willing to redirect you to an external resource and drain your funds. I have enabled a GitHub actions script to detect the common patterns and tag them, which obviously is not 100% accurate. This is also why you will see deleted comments in the issues—I only delete the scam bot comments targeting your private keys. Not everyone is a scammer though, sometimes there are helpful outside devs who comment and I absolutely appreciate it.
⚠️ NOT FOR PRODUCTION: This code is for learning purposes only. We assume no responsibility for the code or its usage. Modify for your needs and learn from it (examples, issues, and PRs contain valuable insights).
🚀 Getting started
Prerequisites
- Install uv, a fast Python package manager.
If Python is already installed,
uvwill detect and use it automatically.
Installation
1️⃣ Clone the repository
git clone https://github.com/chainstacklabs/pump-fun-bot.git
cd pump-fun-bot
2️⃣ Set up a virtual environment
# Create virtual environment
uv sync
# Activate (Unix/macOS)
source .venv/bin/activate
# Activate (Windows)
.venv\Scripts\activate
Virtual environments help keep dependencies isolated and prevent conflicts.
3️⃣ Configure the bot
# Copy example config
cp .env.example .env # Unix/macOS
# Windows
copy .env.example .env
Edit the .env file and add your Solana RPC endpoints and private key.
Edit .yaml templates in the bots/ directory. Each file is a separate instance of a trading bot. Examine its parameters and apply your preferred strategy.
For example, to run the pump.fun bot, set platform: "pump_fun"; to run the bonk.fun bot, set platform: "lets_bonk".
4️⃣ Install the bot as a package
uv pip install -e .
Why
-e(editable mode)? Lets you modify the code without reinstalling the package—useful for development!
Running the bot
# Option 1: run as installed package
pump_bot
# Option 2: run directly
uv run src/bot_runner.py
You're all set! 🎉
Note on throughput & limits
Solana is an amazing piece of web3 architecture, but it's also very complex to maintain.
Chainstack is daily (literally, including weekends) working on optimizing our Solana infrastructure to make it the best in the industry.
That said, all node providers have their own setup recommendations & limits, like method availability, requests per second (RPS), free and paid plan specific limitations and so on.
So please make sure you consult the docs of the node provider you are going to use for the bot here. And obviously the public RPC nodes won't work for the heavier use case scenarios like this bot.
For Chainstack, all of the details and limits you need to be aware of are consolidated here: Throughput guidelines <— we are always keeping this piece up to date so you can rely on it.
Built-in RPC Rate Limiting
The bot now includes built-in RPC rate limiting to prevent hitting provider limits:
- Token bucket algorithm: Smoothly controls request rate while allowing short bursts
- Configurable max RPS: Set
max_rpsparameter inSolanaClient(defaults to 25 RPS) - Automatic retry logic: Handles 429 (Too Many Requests) errors with exponential backoff
- Shared session management: Reuses connections for improved performance
This helps ensure reliable operation within your node provider's rate limits without manual throttling.
IDLs
The IDLs under idl/ are vendored from pump-fun/pump-public-docs. To refresh, copy pump.json, pump_amm.json, pump_fees.json from that repo into pump_fun_idl.json, pump_swap_idl.json, pump_fees.json respectively, and reference the upstream commit hash in your commit message.
Currently vendored from upstream commit 9c82f61.
The IDL under-reports the legacy instructions. It omits two PDAs that the on-chain program requires on the pre-v2 path:
bonding-curve-v2— required on every legacy BCbuy(18 accounts) andsell(16/17 accounts). Seed:["bonding-curve-v2", mint]under the pump program.pool-v2— required on every PumpSwapbuy/sell. Seed:["pool-v2", base_mint]under the pump-amm program. Without it, pump-amm throwsAnchorError 6023 (Overflow)after the trade transfers complete — a misleading error code for a missing-account issue.The
buy_v2/sell_v2account lists are complete in the IDL — that's the point of the v2 interface. For anything else, cross-check against a recent successful on-chain tx (getSignaturesForAddress+getTransaction) before trusting the IDL.
Quote assets: SOL and USDC (v2 trade instructions)
Pump.fun added support for quote assets other than SOL, with USDC first. The bonding curve carries a quote_mint field (Pubkey::default() for SOL-paired coins), and trading non-SOL-paired coins requires the newer buy_v2 / sell_v2 instructions — the legacy buy / sell cannot do it at all.
The bot uses buy_v2 (27 accounts) and sell_v2 (26 accounts) for every pump.fun trade. Every account is mandatory and the order is identical for all coins, regardless of quote asset, mayhem mode, or cashback — no more conditional account lists.
To trade a non-SOL quote asset, give it a spend amount. Amounts are in that mint's own whole units, so usdc: 1.0 is one USDC and is not comparable to buy_amount:
trade:
buy_amount: 0.0001 # SOL-paired coins
quote_amounts:
usdc: 1.0 # USDC-paired coins
filters:
allowed_quote_mints: ["sol", "usdc"] # omit to allow any configured quote
Keys accept the aliases sol / wsol / usdc or a raw base58 mint address. A coin whose quote mint has no configured amount is skipped with a log line rather than bought with a wrongly-scaled amount. SOL always falls back to buy_amount, so existing configs keep working untouched.
Buying a USDC-paired coin requires USDC in the wallet plus a little SOL for fees and ATA rent.
Verify the v2 wiring after any program upgrade:
uv run learning-examples/verify_v2_account_layout.py # offline: layouts, PDAs, encoding
uv run learning-examples/simulate_v2_trades.py <MINT> # mainnet simulation, no funds moved
uv run learning-examples/simulate_bot_buy_path.py # whole bot buy path, simulated
PumpSwap: quote against effective reserves
The PumpSwap Pool account gained a trailing virtual_quote_reserves field (an i128 at offset 245; fields end at 261, live accounts are 301 bytes). Price must be computed from effective quote reserves:
effective_quote_reserves = pool_quote_token_account.amount + Pool::virtual_quote_reserves
⚠️ Upstream's release note says
virtual_quote_reservesis 0 on every pool. That is out of date. Verified on mainnet: pool6Bv1JM1deBPeEeovhoRajFtbMVDKQidC9mXYRQWfGoXzholds 17.584505433 SOL of virtual reserves against a 148.455 SOL vault — quoting off the raw vault balance under-prices by ~10.6%. Note it isi128, notu64; an 8-byte read only works while the high half happens to be zero.
learning-examples/pumpswap/manual_buy_pumpswap.py and manual_sell_pumpswap.py read and apply it. Note that pump-amm has no buy_v2/sell_v2 — the AMM instruction names are unchanged; only the pool layout and quoting moved.
2026-04-28 program upgrade
Pump.fun shipped a breaking program upgrade on 2026-04-28 16:00 UTC (BREAKING_FEE_RECIPIENT.md). This section describes the legacy instruction path, which the bot retains only as a fallback (PumpFunInstructionBuilder(..., use_legacy_instructions=True)) — see the quote-assets section above for the v2 path used by default:
- BC
buyix is now 18 accounts (was 17). Trailing account is one of 8BREAKING_FEE_RECIPIENTS(mutable), AFTERbonding-curve-v2. - BC
sellix is now 16 accounts non-cashback / 17 cashback (was 15/16). Same trailing fee recipient. - PumpSwap
buy/sellget +2 accounts appended afterpool-v2: a fee recipient (readonly) and its quote-mint ATA (mutable). Counts: buy = 26 non-cashback / 27 cashback; sell = 24 / 26. Cashback pools insertuser_volume_accumulator_quote_ata(writable) BEFOREpool-v2on buys; sells insert both that ATA anduser_volume_accumulator(both writable) BEFOREpool-v2. Detect cashback via pool data byte 244.
The 8 fee recipients are randomized per tx in code (per pump.fun's recommendation to spread program-tx throughput).
Changelog
Quick note on a couple on a few new scripts in /learning-examples:
(this is basically a changelog now)
Also, here's a quick doc: Listening to pump.fun migrations to Raydium
Bonding curve state check
get_bonding_curve_status.py — checks the state of the bonding curve associated with a token. When the bonding curve state is completed, the token is migrated to Raydium.
To run:
uv run learning-examples/bonding-curve-progress/get_bonding_curve_status.py TOKEN_ADDRESS
Listening to the Pump AMM migration
When the bonding curve state completes, the liquidity and the token graduate to Pump AMM (PumpSwap).
listen_logsubscribe.py — listens to the migration events of the tokens from bonding curves to AMM and prints the signature of the migration, the token address, and the liquidity pool address on Pump AMM.
listen_blocksubscribe_old_raydium.py — listens to the migration events of the tokens from bonding curves to AMM and prints the signature of the migration, the token address, and the liquidity pool address on Pump AMM (previously, tokens migrated to Raydium).
Note that it's using the blockSubscribe method that not all providers support, but Chainstack does and I (although obviously biased) found it pretty reliable.
To run:
uv run learning-examples/listen-migrations/listen_logsubscribe.py
uv run learning-examples/listen-migrations/listen_blocksubscribe_old_raydium.py
The following two new additions are based on this question associatedBondingCurve #26
You can take the compute the associatedBondingCurve address following the Solana docs PDA description logic. Take the following as input as seed (order seems to matter):
- bondingCurve address
- the Solana system token program address:
TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA - the token mint address
And compute against the Solana system associated token account program address: ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL.
The implications of this are kinda huge:
- you can now use
logsSubscribeto snipe the tokens and you are not limited to theblockSubscribemethod - see which one is faster
- not every provider supports
blockSubscribeon lower tier plans or at all, but everyone supportslogsSubscribe
The following script showcase the implementation.
Compute associated bonding curve
compute_associated_bonding_curve.py — computes the associated bonding curve for a given token.
To run:
uv run learning-examples/compute_associated_bonding_curve.py and then enter the token mint address.
Listen to new tokens
listen_logsubscribe_abc.py — listens to new tokens and prints the signature, the token address, the user, the bonding curve address, and the associated bonding curve address using just the logsSubscribe method. Basically everything you need for sniping using just logsSubscribe (with some limitations) and no extra calls like doing getTransaction to get the missing data. It's just computed on the fly now.
To run:
uv run learning-examples/listen-new-tokens/listen_logsubscribe_abc.py
So now you can run compare_listeners.py see which one is faster.
uv run learning-examples/listen-new-tokens/compare_listeners.py
Also here's a doc on this: Solana: Listening to pump.fun token mint using only logsSubscribe
Pump.fun bot development roadmap (March - April 2025, mostly completed)
As of March 21, 2025, the bot from the refactored/main-v2 branch is signficantly better over the main version, so the suggestion is to FAFO with v2.
As of April 30, 2025, all changes from refactored/main-v2 are merged into the main version.
| Stage | Feature | Comments | Implementation status |
|---|---|---|---|
| Stage 1: General updates & QoL | Lib updates | Updating to the latest libraries | ✅ |
| Error handling | Improving error handling | ✅ | |
| Configurable RPS | Ability to set RPS in the config to match your provider's and plan RPS (preferably Chainstack 🤩) | ✅ | |
| Dynamic priority fees | Ability to set dynamic priority fees | ✅ | |
Review & optimize json, jsonParsed, base64 |
Improve speed and traffic for calls, not just getBlock. Helpful overview. |
✅ | |
| Stage 2: Bonding curve and migration management | logsSubscribe integration |
Integrate logsSubscribe instead of blockSubscribe for sniping minted tokens into the main bot |
✅ |
| Dual subscription methods | Keep both logsSubscribe & blockSubscribe in the main bot for flexibility and adapting to Solana node architecture changes |
✅ | |
| Transaction retries | Do retries instead of cooldown and/or keep the cooldown | ✅ | |
| Bonding curve status tracking | Checking a bonding curve status progress. Predict how soon a token will start the migration process | ✅ | |
| Account closure script | Script to close the associated bonding curve account if the rest of the flow txs fails | ✅ | |
| PumpSwap migration listening | pump_fun migrated to their own DEX — PumpSwap, so we need to FAFO with that instead of Raydium (and attempt logSubscribe implementation) |
✅ | |
| Stage 3: Trading experience | Take profit/stop loss | Implement take profit, stop loss exit strategies | ✅ |
| Market cap-based selling | Sell when a specific market cap has been reached | Not started | |
| Copy trading | Enable copy trading functionality | Not started | |
| Token analysis script | Script for basic token analysis (market cap, creator investment, liquidity, token age) | Not started | |
| Archive node integration | Use Solana archive nodes for historical analysis (accounts that consistently print tokens, average mint to raydium time) | Not started | |
| Geyser implementation | Leverage Solana Geyser for real-time data stream processing | ✅ | |
| Stage 4: Minting experience | Token minting | Ability to mint tokens (based on user request - someone minted 18k tokens) | ✅ |