Mirrors the four learning-examples commits (9b48418, c25e2ed, 57ffe8b,
ad6d2cc) into the production code paths under src/. Affects only pump_fun;
lets_bonk untouched. PumpSwap is not implemented under src/ so the
pool-v2 + breaking-fee-recipient pumpswap accounts are out of scope here.
Changes
-------
src/platforms/pumpfun/address_provider.py
- Adds BREAKING_FEE_RECIPIENTS: ClassVar[list[Pubkey]] with the 8
addresses pump.fun published for the 2026-04-28 upgrade.
- Adds pick_breaking_fee_recipient() (random.choice across the 8 to
spread program-tx throughput per pump.fun's recommendation).
- get_buy_instruction_accounts and get_sell_instruction_accounts now
expose "breaking_fee_recipient" alongside the existing keys.
src/platforms/pumpfun/instruction_builder.py
- BC buy ix: appends breaking_fee_recipient (mutable) AFTER
bonding_curve_v2 → 18 accounts total (was 17).
- BC sell ix: appends breaking_fee_recipient (mutable) AFTER
bonding_curve_v2 for both cashback and non-cashback paths
→ 16 accounts non-cashback / 17 cashback (was 15 / 16).
- get_required_accounts_for_{buy,sell} updated to include the new
account so priority-fee scraping covers it.
src/platforms/pumpfun/event_parser.py
- is_mayhem_mode now extracted into TokenInfo on both paths
(CreateEvent logs path and create / create_v2 instruction path);
previously only is_cashback_coin was carried forward, leaving
extreme_fast_mode buys to use the wrong fee_recipient on
Mayhem-Mode tokens.
src/trading/platform_aware.py
- Buy non-extreme path now also refreshes is_cashback_coin from
pool_state (was only refreshing is_mayhem_mode).
- Sell path now refreshes both flags from curve state before building
the sell ix, so a coin that flips cashback after buy still gets the
correct 16-vs-17-account layout. Wrapped in try/except so a transient
RPC failure logs a warning and falls back to token_info defaults
rather than failing the sell outright.
Live-validation (mainnet, all four bots/*.yaml configs)
-------------------------------------------------------
bot-sniper-2-logs.yaml buy 4GVJJwYtGixSPAQimgUhNYjEUBG6TB9XeQjKxs9EWpw2QfWuq7LHmUivPrsjhESgaPsPJQ9N6CCxECqxm9csqrH9 (18 accts) → sell 2ijgoPgxPmcSX4mFqz1PVkZ3yAvu36vj3Jy1yWZofxrK7jR92j2DWP3tRNy9PmUd1kiTaoYzVsrpGr6286KCnTp7 (16 accts) ✓
bot-sniper-3-blocks.yaml buy 4siRLG7tYk9iHyXAvnp3DH54vQXVEDHmCqZRmFgYhnRB9mVGjhh43ywohKjg8CpWctt5TyCtDkLRTRWT6VDwVyZi → sell 46jShWJ3sfuCP4YgTcpXWZdyqVmw4ULQSTC3Kwvbc9hE2y4yDosBww8Un8anXZpR1Xf96rN9QusV1G92oNcbp1o3 ✓
bot-sniper-1-geyser.yaml buy 4UEZy7LPm7Rxgw22d1bduQHTWHHHYJSpuaoq2iCyPFpnBPGt4C3QUCu9x77vwWugXnyGv4pQQeTbEZEjvyxxiDH2 → sell 21hSHVPKtqse25XybxhdGrpqK1fSxKKeNZGDCD5j568mzRPfyYR1TQs6mAc2moHKT7kaAAfdMC8DPt8VXmu2rviF ✓
bot-sniper-4-pp.yaml buy 4P91y4iPK6y1gPjz65VL6XAcVkT4DbqjHE5vHfX8PLnzqViCy1qZQ44eWiNDUPzoWCejibB7UJPKYAEj4K3Z3c3S ✓ ; sell hit slippage protection (Custom 6003 — program-side, the ix passed deserialization)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three small ergonomic fixes uncovered while running every learning
example end-to-end:
copytrading/listen_wallet_transactions.py
Was hardcoded to WALLET_TO_TRACK = "...". Now accepts argv[1] so
you can run it without editing the file.
pumpswap/get_pumpswap_pools.py
Same pattern (TOKEN_MINT placeholder) and the underlying
getProgramAccounts call would also time out at the default 30s
on busy mainnet RPCs — bumped both AsyncClient timeouts to 120s.
bonding-curve-progress/get_graduating_tokens.py
Bumped RPC timeout 120s → 240s for the same reason. The threshold
scan over all pump.fun bonding curves still occasionally exceeds
even that on overloaded RPCs (it's a known-heavy query); a smaller
threshold or memcmp-narrowed filter is the proper long-term fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PumpSwap (manual_buy_pumpswap.py / manual_sell_pumpswap.py)
- Real on-chain pump-swap buys use 24 accounts; the IDL is incomplete
(lists 23). Missing 24th account is pool-v2 PDA, derived from seed
["pool-v2", base_mint] under the pump-amm program.
- Without it, pump-amm throws AnchorError 6023 (Overflow) at buy.rs:400/414
after the trade transfers complete (the source of the earlier "pre-existing
overflow" — was actually a missing account, not a math bug).
- find_pool_v2(base_mint) added; appended to both buy and sell account lists
after fee_program. The April-28 breaking-fee accounts (still gated behind
INCLUDE_BREAKING_FEE_ACCOUNTS) come after pool-v2 — matching the doc's
"All accounts till bonding-curve-v2 and pool-v2 remain the same."
- AsyncClient timeout bumped to 120s so the heavy getProgramAccounts pool
lookup doesn't time out on slower RPC paths.
- Live-verified on mainnet with 9PwadsGz...pump:
buy 2xgdJJsDMzoMyxJzrCATKwGfpX7M... err=None (24 accounts)
sell 4vdBkGpCMQYEWncj5k1suGzKPUMQ... err=None (24 accounts)
manual_buy_geyser.py
- Fixes IndexError when create tx references ALT-loaded keys
(matches the same fix applied to manual_buy.py).
- Re-enables the 15s sleep + on-chain price/fee_recipient lookup that
were commented out for testing — needed for buys on real pools.
- Live-verified: 45wSgX2Pu1LLZWLgu3e9qdhRaGJjqwMqQP5HdeV7zMBqNXziB2uEgxQd5bAqvAhBLFnAqzbuK3LAWowhxZxdemJ
(18-account buy, err=None) on Token-2022 cashback-enabled INSIDER mint.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends the 2026-04-28 program-upgrade prep (commit c25e2ed) to the
remaining trade scripts:
mint_and_buy.py — 18-account buy after bonding-curve-v2.
Tested live with a fresh TEST mint (Solscan
sig 62EZFCKVuxvqmzj...), succeeded.
mint_and_buy_v2.py — 18-account buy. Tested live with a
mayhem-mode TEST2 token (Token-2022 + 5
mayhem accounts), Solscan sig 44SvQSrhmHy...,
succeeded.
pumpswap/manual_buy_pumpswap.py / manual_sell_pumpswap.py
— Adds the 2 new pump-swap accounts (fee
recipient + its quote-mint ATA) gated behind
INCLUDE_BREAKING_FEE_ACCOUNTS = False; flip
to True after 2026-04-28 16:00 UTC.
Pre-existing program-side overflow at
buy.rs:400 (AnchorError 6023) flagged in
a comment — affects current pump-swap
buys, unrelated to this upgrade.
Both pumpswap scripts now also accept TOKEN_MINT via argv[1].
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pump.fun program upgrade scheduled for 2026-04-28 16:00 UTC adds one new
mutable account (one of 8 fee recipients) at the end of buy/sell ixs,
after bonding-curve-v2. The new format is already accepted on mainnet
ahead of the cutover, so applying now keeps scripts working on both
sides of the upgrade.
Doc: github.com/pump-fun/pump-public-docs/blob/main/docs/BREAKING_FEE_RECIPIENT.md
Per file:
manual_buy.py — 18-account buy. Also fixes a pre-existing
listener crash on Address-Lookup-Table create
txs (skips them) and adds bool/OptionBool
handling to decode_create_instruction so
create_v2 doesn't raise on its trailing args.
manual_buy_cu_optimized.py — 18-account buy. Bumps the CU-optimization
account-data limit to 16MB (tested) — older
values trigger MaxLoadedAccountsDataSizeExceeded
on Token-2022/cashback coins. Same listener
ALT-skip and decoder bool fixes.
manual_buy_geyser.py — 18-account buy. Re-enables price calculation
and mayhem-mode-aware fee_recipient detection
(was scaffolded out for testing).
manual_sell.py — 16-account non-cashback / 17-account cashback
sell. Reads is_cashback_coin from BC byte 82
and inserts user_volume_accumulator before
bonding-curve-v2 when needed. TOKEN_MINT now
accepts argv[1] override.
Live-verified on mainnet:
buy manual_buy.py 3AVnLC3sdBD598cs3... + 3bWU9bB8U9aemA2BFhS2...
buy manual_buy_cu_optimized 2PfH5rHw62o3KkQ3N4z8...
sell manual_sell.py 3E7dyPBPBRe95BtPQXmb... + 5L4wWSzPV36m6XqXAswB...
All four show the expected 18 (buy) / 16 (sell non-cashback) account count
with one of the 8 BREAKING_FEE_RECIPIENTS as the trailing mutable account.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refreshes the read-only learning examples (Phase A3) against the post
Feb–Apr 2026 IDL changes:
- CreateEvent now has 15 fields (timestamp, 4×u64 reserves, token_program,
is_mayhem_mode, is_cashback_enabled). Listener parsers were truncating
after `creator`; the v2 ones were also reading is_mayhem_mode at the
wrong offset.
- BondingCurve account is now 83 bytes (added is_cashback_coin).
- PumpSwap Pool account is now 245 bytes (added is_cashback_coin) — the
listen_programsubscribe dataSize filter was matching nothing.
- IDL instruction `createV2` was renamed to `create_v2`; the legacy
`create` instruction added a `creator: pubkey` arg in March 2026.
- decode_from_blockSubscribe.py decoder now handles bool, i64, u32, u16,
u8, and the OptionBool defined type (1-byte wire format).
Files touched (all under learning-examples/):
bonding-curve-progress/get_bonding_curve_status.py — V4 struct (cashback)
bonding-curve-progress/poll_bonding_curve_progress.py
decode_from_blockSubscribe.py
decode_from_getAccountInfo.py
decode_from_getTransaction.py
listen-migrations/compare_migration_listeners.py
listen-migrations/listen_programsubscribe.py
listen-new-tokens/compare_listeners.py
listen-new-tokens/listen_blocksubscribe.py
listen-new-tokens/listen_geyser.py
listen-new-tokens/listen_logsubscribe.py
listen-new-tokens/listen_logsubscribe_abc.py
Verified live against mainnet — listeners decode tokens including
mayhem-mode and cashback-enabled curves correctly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace idl/pump_fun_idl.json and idl/pump_fees.json with the latest from
pump-fun/pump-public-docs @ 7de0b95 (2026-04-23). pump_swap_idl.json was
already up to date.
Drops the idl/upstream/ vendor copy added in 7864504 in favor of
overwriting in place — the diff lives in git history. README updated to
point at pump-public-docs as the source of truth.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Snapshot of pump-fun/pump-public-docs @ 7de0b95 (2026-04-23) under
idl/upstream/ — pump.json, pump_amm.json, pump_fees.json and their .ts
counterparts. Used for local diffing against the IDLs the bot loads from
idl/; not imported at runtime. README links to the new directory.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(examples): add bonding_curve_v2 remaining account to learning examples
Add the required bonding_curve_v2 PDA as a remaining account to buy/sell
instructions in manual_buy, manual_sell, mint_and_buy, and mint_and_buy_v2
learning examples, matching the pump.fun program upgrade.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(examples): add bonding_curve_v2 to manual_buy_geyser and manual_buy_cu_optimized
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Update all 3 pump.fun IDL files (pump_fun, pump_swap, pump_fees) with
new cashback rewards and fee-sharing features. Fix IDL parser to handle
tuple struct types (OptionBool). Add bonding_curve_v2 remaining account
to all buy/sell instructions as required by the pump.fun program upgrade.
Key changes:
- Fix IDL parser crash on tuple struct fields (string vs dict)
- Add bonding_curve_v2 PDA derivation and append as remaining account
- Add is_cashback_coin field to TokenInfo and BondingCurve decoding
- Propagate is_cashback_enabled from CreateEvent/create_v2 to TokenInfo
- Conditionally include user_volume_accumulator for cashback sell txs
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix(core): disable account_data_size limit and add meta.err checking
The 12.5MB setLoadedAccountsDataSizeLimit was causing all buy transactions
to fail with MaxLoadedAccountsDataSizeExceeded since pump.fun migrated to
Token-2022. Transactions landed on-chain (fees paid) but inner instructions
were rejected — the bot paid gas for nothing.
Changes:
- Disable account_data_size in all bot configs (Token-2022 needs >12.5MB)
- Add meta.err check in get_buy_transaction_details() for clear error
reporting when transactions fail on-chain
- Include tx signature in platform_aware.py error messages for debugging
Tested: full buy→sell→cleanup cycle works on pump_fun with geyser listener.
* fix(core): check meta.err in confirm_transaction to detect failed txs
Solana transactions can be "confirmed" (included in a block) but still
fail execution if inner program instructions are rejected. Previously,
confirm_transaction only checked that the tx landed on-chain, causing
silent failures in buy, sell, and cleanup operations.
Now fetches the transaction result after confirmation and checks
meta.err, returning False when the transaction failed. This fixes
the ATA cleanup issue where sells were silently failing with
Custom: 6003 errors, leaving non-zero token balances.
* fix(core): treat failed tx fetch as unconfirmed in confirm_transaction
When _get_transaction_result returns None (RPC failure, timeout, etc.),
the function was falling through to return True — silently treating an
unknown state as success. Now returns False with a warning log.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(core): add RPC rate limiting, retry logic, and 429 handling
Addresses #44 — users on free-tier RPC endpoints hit HTTP 429 errors
during buy transactions due to no rate limiting or retry handling.
- Add TokenBucketRateLimiter (new file: src/core/rpc_rate_limiter.py)
- Gate all RPC methods through rate limiter (both post_rpc and solana-py calls)
- Rewrite post_rpc() with retry loop, exponential backoff, jitter, and
specific 429 detection with Retry-After header support
- Replace per-call aiohttp session with shared persistent session
- Wire node.max_rps from YAML bot config through to SolanaClient
- Fix cleanup manager and learning example to use SolanaClient abstraction
instead of bypassing it via get_client()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(core): address CodeRabbitAI review feedback on rate limiting PR
Validate max_rps > 0 in TokenBucketRateLimiter to prevent ZeroDivisionError
and infinite loops with fractional values. Add asyncio.Lock to _get_session
to fix race condition, handle non-numeric Retry-After headers gracefully,
replace dead json.JSONDecodeError with aiohttp.ContentTypeError, and combine
burn+close into a single transaction in cleanup example.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: document RPC rate limiting feature in README
- Add section on built-in RPC rate limiting with token bucket algorithm
- Document configurable max RPS and automatic retry logic
- Update roadmap to mark "Configurable RPS" as completed
- Clarify benefits of rate limiting for provider compliance
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: use math.ceil for burst_size to handle fractional max_rps
- Replace int(max_rps) with math.ceil(max_rps) in burst_size calculation
- Prevents infinite loop when max_rps < 1.0 (e.g., 0.5 RPS would result in burst_size=0)
- Ensures burst_size is always at least 1 for valid fractional rates
- Addresses CodeRabbit feedback on rpc_rate_limiter.py:27
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix(core): validate burst_size and fix table pipe consistency
Address remaining CodeRabbitAI review feedback: add burst_size
validation guard, fix TRY003 lint (use msg variable for ValueError),
break long line under 88 chars, and fix MD055 table pipe style in README.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(core): separate 429 retry budget from error retries in post_rpc
429 responses no longer count against max_retries — they use a dedicated
max_429_retries counter (default 10) so free-tier users hitting rate
limits won't exhaust retries prematurely. Also refresh the aiohttp
session inside the retry loop to avoid stale references after network
failures, and fix cleanup log message accuracy.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Anton Sauchyk <antonsauchyk@gmail.com>
Fixeschainstacklabs/pumpfun-bonkfun-bot#152
Changes:
- Make uvloop optional with platform-specific markers (Unix only)
- Add winloop as Windows alternative for performance optimization
- Update code to gracefully fall back to standard asyncio when event loop
libraries are unavailable
- Both bot_runner.py and universal_trader.py now detect platform and use
appropriate event loop implementation
This resolves the blocking installation issue for Windows users while
maintaining performance benefits on all platforms. Windows users can now
install and run the bot with winloop for improved performance, or use
standard asyncio as fallback.
Related: https://github.com/smypmsa/gh-triage-reports/blob/main/issues/chainstacklabs-pumpfun-bonkfun-bot/issue-152-2026-02-08.md
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
* feat(trading): enhance sell execution with token amount and price parameters to reduce RPC delays
* feat(trading): add method to fetch actual token balance after transaction to improve accuracy
* feat(trading): update account data size limit and enhance price calculations
* feat(core): support multiple initialize instruction variants in LetsBonkEventParser
* feat(core): add creator and platform fee vault derivation methods and update account handling in buy/sell instructions
* feat(letsbonk): add global_config and platform_config to TokenInfo and update address provider logic
This change adds the standard [build-system] table to pyproject.toml,
which allows entry points like 'pump_bot' to be installed correctly
with 'uv pip install -e .'.
This resolves the warning: 'Skipping installation of entry points
because this project is not packaged'.