mirror of
https://github.com/chainstacklabs/pumpfun-bonkfun-bot.git
synced 2026-11-02 06:16:10 +00:00
55ae2afd82d2e58fae902a686c5ea922037b0c51
11
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
55ae2afd82 |
feat(pumpfun): resync with the 2026-09-15 program upgrade (#202)
* feat(idl): refresh to upstream 8109141 and decode omitted OptionU64 args Vendors pump.json, pump_amm.json and pump_fees.json from pump-fun/pump-public-docs@8109141 (holder rewards coins, cashback deprecation). create_v2 gains two trailing optionals: creator_fee_bps (OptionU64) and is_holder_reward (OptionBool). _is_optional_type only recognised OptionBool, so a create_v2 that omits creator_fee_bps failed the whole decode and the coin dropped out of the instruction path — the same failure #184 fixed for is_cashback_enabled. Reproduced against two live mainnet create_v2 transactions captured 2026-09-15: one omits creator_fee_bps (157-byte instruction data), one sends it (156 bytes) — both are added as getTransaction fixtures under learning-examples/. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(learning-examples): accept 125-byte bonding curves in dataSize filters Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(learning-examples): replace unrun/recycled curve-size claims with dated live measurements The 45s/205-update/24-curve figures and the logsSubscribe/same-tx extend_account claim in the two graduating-token docstrings were carried over from before the 125/151 filter change and never re-verified under it. Replaced with fresh 2026-09-15 measurements: a length-unfiltered programSubscribe run, a direct 125->151 transition observed on one curve, and a live run of the Geyser script's own two-group build_subscribe_request confirming both filter groups actually deliver. Unreproduced claims are now marked UNVERIFIED in-line instead of stated flatly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(learning-examples): stop enumerating bonding-curve dataSize lengths extend_account can grow a curve to any length the program allows, so the CURVE_ACCOUNT_LENS = (125, 151) allowlist in both graduating-token scripts was whack-a-mole -- a live 256-byte curve was already missed. Drop the dataSize/datasize server-side filter entirely (discriminator + complete memcmp already restrict delivery to BondingCurve accounts) and replace the allowlist with a MIN_CURVE_LEN client-side floor. Collapse the Geyser script's two named filter groups back to one now that there's no length to fan out over. Extend verify_curve_account_sizes.py to assert neither script constructs a dataSize filter and that 125/151/256-byte curves all decode. Measured live 2026-09-15, filtered vs unfiltered subscribed simultaneously so both see identical trade activity: ~0.6-0.7% extra traffic on WebSocket, statistically zero on Geyser -- dropping the filter is effectively free. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(learning-examples): remove untraceable bandwidth figure, session wording Review of the dataSize-enumeration fix found a docstring figure (90s window, 2/224/866 updates at 256/125/151 bytes) that no command in the fix report actually produced -- it was a byproduct of the discarded sequential-window run, never written down with its command+output. Drop it and rely only on the properly-documented 120s simultaneous measurement already backing the rest of the paragraph. Also replace two "carried from an earlier session" UNVERIFIED markers with the repo's own convention (UNVERIFIED: <what>, no reference to how or when the file was edited), and give both the update and byte ratio (1.006x / 1.007x) instead of a single derived percentage. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat(pumpfun): surface is_holder_reward and creator_fee_bps on TokenInfo Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(pumpfun): verify the USDC half of the creator_fee_bps claim The comment on TokenInfo.creator_fee_bps stated flatly that SOL- and USDC-paired coins always carry bps 0, but only the SOL half had been observed live. Found 10 USDC-paired bonding curves (via Dune's indexed CreateEvent table) and read each one directly through PumpFunCurveManager.get_pool_state: all 10 show creator_fee_bps=0, matching every custom-pair coin observed showing nonzero. Dated the verification in the comment instead of leaving it an unmarked claim. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat(examples): create holder-reward coins; mark cashback legacy-only create_v2 now rejects is_cashback_enabled=[true] with 6082 CashbackDeprecated as of the 2026-09-15 upgrade. Add ENABLE_HOLDER_REWARD to mint_and_buy_v2.py so the trailing create_v2 args can reach is_holder_reward instead, and relabel the still-live cashback code paths in core.py, instruction_builder.py and curve_manager.py with the deprecation date. No cashback account handling is removed anywhere - existing cashback coins keep trading and accruing exactly as before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat(pumpfun): resolve quote mints to their real token program A 2026-09-15 pump.fun upgrade lets create_v2 pair a coin with a Token-2022 quote mint (error 6064 now accepts "SPL Token or Token-2022"), and such coins already trade on mainnet. quote_token_program() only knew WSOL/USDC and silently defaulted everything else to SPL Token, deriving associated token accounts that don't exist on chain for any other quote mint. Add an async resolver that reads a mint's owner from chain and caches it for the process, pre-seeded with WSOL/USDC so they stay free. The bot warms this cache once at startup for every configured quote mint; the hot path (event parsing, address resolution) only ever reads the cache, preserving extreme_fast_mode's zero-RPC contract between detection and submission. quote_token_program() itself is untouched so its existing call sites keep their non-optional Pubkey return. Also fixes the standalone pump_v2.py learning-examples helper, which had the same bug in a form that ignored its argument entirely, and gives simulate_bot_buy_path.py a quote_amounts entry so it can exercise a non-SOL-paired coin instead of unconditionally skipping it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat(core): name Anchor error codes when reporting a reverted transaction confirm_transaction only answers whether a transaction landed; meta.err carries the revert. Logging it raw as {'InstructionError': [idx, {'Custom': n}]} told an operator nothing, and pump.fun's 2026-09-15 upgrade added 22 new error codes to the pump program and 10 to pump-amm, so an unnamed number is now more likely to be wrong than useful. IDLParser.describe_error_code() looks a Custom(N) code up in an IDL's error table; SolanaClient logs the match alongside the raw meta.err at both revert sites, so a buy reverting with BuybackFeeRecipientMissing (issue #175) now prints "6062 BuybackFeeRecipientMissing" instead of a bare 6062. SolanaClient is shared across pump.fun and letsbonk.fun and has no record of which program a given transaction invoked, so this only checks pump.fun's table -- the program the bot's own buy_v2/sell_v2 call directly. A revert on a different program is described against the wrong table if the code number collides, or left unnamed otherwise; see the docstring on _describe_program_error. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(core): correct get_account_info's return type annotation It was typed dict[str, Any] but solana-py's AsyncClient.get_account_info actually returns a solders Account object. Every real caller already reads attributes (.data, .owner) rather than subscripting, so this was latent rather than a live bug -- but the wrong annotation is what let those callers work by accident, and would mislead anyone who trusted the type and reached for result["owner"] instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(core): tag the revert description with the table it came from The name appended to a reverted transaction's log line looked like a fact about whichever program actually reverted, but it is only ever looked up in pump.fun's error table. This bot also trades letsbonk.fun, so a letsbonk revert whose numeric code happens to also exist in pump's table would get a confident, wrong name -- worse than the bare number it replaced, since a wrong name stops the reader from checking. Prefixing the description with "pump.fun IDL:" carries the same limitation the docstring already stated out to the log line itself, so it's visible without reading the source. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: resync CLAUDE.md and README.md with the 2026-09-15 program upgrade The bonding curve is 125 bytes now (not 151, no more padding), create_v2 takes eight args instead of six, cashback creation is deprecated (6082 CashbackDeprecated) while existing cashback coins keep trading, quote assets are no longer just SOL/USDC (QuoteControl, not Global, is the authoritative mint registry), and holder-reward coins were undocumented entirely. Add verify_curve_account_sizes.py to both docs' verification lists, retire the superseded CU comments in instruction_builder.py (the 180k/120k limits are untouched), and fix two stale cashback comments plus a docstring in client.py that showed a msg suffix code 6062 doesn't have. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(trading): refresh quote_token_program_id alongside quote_mint _refresh_quote_mint synced token_info.quote_mint from freshly-read curve state but left quote_token_program_id at its construction-time value. A stale non-None program id wins the `or` in PumpFunAddressProvider.resolve_quote, so a curve refresh that reveals a Token-2022 quote mint still resolves the associated_quote_* ATAs under SPL Token -- an address that does not exist on chain. cached_quote_token_program is a synchronous dict read, so this keeps extreme_fast_mode's zero-RPC-call contract between detection and submission. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(examples): resolve the quote mint's real token program in manual buy/sell manual_buy.py and manual_sell.py derived the quote ATA's owning program through pump_v2.quote_token_program, which defaults any mint outside the WSOL/USDC map to SPL Token. A pump.fun upgrade verified 2026-09-15 lets create_v2 pair a coin with a Token-2022 quote mint (error 6064 now accepts "SPL Token or Token-2022"), so that default derives an ATA that does not exist on a Token-2022-quoted coin -- and these scripts submit real transactions, so the failure burns one. Both now call pump_v2.resolve_quote_token_program (free for WSOL/USDC via its cache, one RPC call otherwise) and thread the resolved program into both the buy_v2/sell_v2 instruction and the quote ATA creation. Verified against a real Token-2022-quoted pump.fun coin found via Dune (pumpdotfun_solana.pump_evt_createevent, mint G5Ve125Poi7KoDRP5uLsgu5NQdm3KoqfyUkq4cqtTo4r, quote_mint Xsc9qvGR1efVDFGLrVsmkzv3qi45LTBjeUKSPmx9qEh, confirmed Token-2022-owned by RPC read): the old default derives associated_quote_bonding_curve FViEZvbcYjoSwGE2b37HiCXmPT3N7kSd1nQavFzcD7Yb, which does not exist on chain; the fixed resolution derives DfxcBKXTLDqobNEmR5JkaBeoKqNbJE1vKbtQVbowXz4x, which exists on chain and is owned by the Token-2022 program. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(core): correct get_multiple_accounts's return type annotation Declared as list[Any] while it actually returns a list of solders Account objects (or None for missing accounts) -- the same stale-annotation pattern already corrected on get_account_info. Checked the one caller, PumpFunCurveManager.get_pool_state_and_token_program, which already unpacks the two entries and reads .data / .owner off them directly rather than subscripting -- no live bug behind the wrong annotation here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor(examples): use the public decode API in the optional-args verifier check_omitted_option_u64_decodes and check_option_u64_present_decodes reached into IDLParser._decode_instruction_args behind # noqa: SLF001 instead of the public decode_instruction(data, keys, accounts) already used by every other check in this file. The fixtures already carry valid accounts/keys (previously discarded as data, _, _), so the public method expresses the same check -- confirmed it returns byte-for-byte identical decoded args as the private call did. Confirmed the check stays load-bearing: reverting _is_optional_type to a narrow == "OptionBool" comparison drops the verifier to 2/7 checks passed, exit 1, including the two checks touched here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: correct unverified claims from the final branch review Text-only fixes from the whole-branch review — no code defects found. - CLAUDE.md: create_v2 trailing-arg claim was circular (sourced from our own minter script) and wrong — the three committed fixtures show 0, 1 and 2 trailing args, never 3. Rewritten with the counted byte lengths and dated; the unobserved 3-arg form is now marked UNVERIFIED instead of asserted. - src/interfaces/core.py, CLAUDE.md: corrected an inflated "25 SOL-paired" sample to the actual 21 (25 coins captured live, 4 of them custom-pair). - instruction_builder.py: buy_v2/sell_v2 CU comments cited figures from a task brief that were never re-measured on this branch. Re-measured via simulate_v2_trades.py on 2026-09-15 (106,677 / ~77,196 CU) and updated the comments; the configured 180k/120k limits are unchanged. - simulate_bot_buy_path.py: removed a working-session reference ("this task") from a comment. - get_graduating_tokens.py, get_graduating_tokens_geyser.py's sibling docs (pump_v2.py, fetch_price.py, get_bonding_curve_status.py, verify_pumpportal_buy_path.py): made the curve-length story consistent (125/151/256, don't enumerate lengths) instead of a stale "125 or 151" framing; resolved an UNVERIFIED marker that mint_and_buy_v2.py already answers (extend_account can share a transaction with create_v2); removed a redundant restated clause in the bandwidth-measurement paragraph. Verified: all 7 verify_*/simulate offline+mainnet checks still exit 0, buy_v2/sell_v2 account counts unchanged at 27/26. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(trading): resolve quote-mint decimals instead of defaulting to 9 quote_decimals()/quote_units_per_token() defaulted every quote mint outside the WSOL/USDC map to SOL's 9 decimals, mis-scaling max_sol_cost/min_sol_output by up to 1000x for a custom Token-2022 quote mint (verified: 6 decimals). Extend resolve_quote_token_program's existing mint-account fetch to also parse and cache decimals from the same read (byte offset 44, shared by SPL Token and Token-2022), so the fix costs zero extra RPC calls and the zero-RPC extreme_fast_mode contract holds. Mirrored the same treatment in learning-examples/pump_v2.py's standalone copy. Also decode is_holder_reward in the instruction-only create_v2 parse path (a landed transaction proves it, since the program rejects the flag when globally disabled) while leaving creator_fee_bps at its default, since upstream docs say it's ignored for SOL/USDC-paired coins and nothing reads it today. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(examples): strengthen optional-arg verifier assertions Fix verify_create_v2_optional_args.py to properly assert value equality rather than just presence when checking optional arguments: - check_omitted_option_u64_decodes: verify both creator_fee_bps and is_holder_reward are exactly None (not just present in dict) - check_option_u64_present_decodes: decode the fixture, verify creator_fee_bps shape is {"field_0": <value>}, and assert the actual value (0) rather than just non-null - Fixture decodes to creator_fee_bps={'field_0': 0}, is_holder_reward=None Also fix README.md line 166: "Three examples" → "Four examples" to match the four verification commands listed. Temporary revert of idl_parser.py's _is_optional_type confirms the strengthened assertions are load-bearing: changing from startswith("Option") back to == "OptionBool" causes 4 of 7 checks to fail. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EkvFUgSiZoaF7w4xugrgBc --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
a33f4fd035 |
fix(learning-examples): repair crashing listeners and decoders, prune obsolete paths, unify naming (#181)
Audited every script in learning-examples/ against mainnet. Broken — found by running them, all invisible offline: - listen_geyser.py crashed with IndexError after ~11 coins: it never resolved v0 address-lookup-table accounts, which geyser reports in meta.loaded_writable_addresses / loaded_readonly_addresses. Resolving them removes the crash and brings detections level with the WebSocket listeners, 35 coins each per 150 s. - compare_listeners.py logged 13,090,862 error lines / 888 MB in 150 s and never printed its own 30-second report: the inner recv() loop caught ConnectionClosed in a broad handler that only logged, so every following recv() raised at once and the outer reconnect handler was unreachable. Now 12 KB and exit 0. Same shape fixed in compare_migration_listeners.py, listen_blocksubscribe.py and extract_blocksubscribe_transactions.py; the last two also gained the reconnect loop their siblings already had. - decode_from_gettransaction.py matched instructions on account count instead of discriminator, reporting a real 19-account create_v2 as claim_cashback with every account under the wrong name. It also walked only top-level instructions, and in 40 consecutive pump.fun transactions there was 1 top-level pump instruction against 8 inner ones. - decode_from_blocksubscribe.py crashed on every real create_v2: on chain the trailing args are variable length, 0001 in one tx and 00 in another, so is_cashback_enabled can be absent entirely. - poll_bonding_curve_progress.py polled a hardcoded dead mint and took no argv. Obsolete: - Delete listen_blocksubscribe_old_raydium.py. Seven minutes on mainnet produced 0 initialize2 events while the wrapper listener caught 3 real migrations. - Delete the duplicate geyser stubs and protos under listen-new-tokens/. The protos were byte-identical to src/geyser/proto and the stubs had drifted; both geyser examples now import src.geyser.generated. - Recapture all four fixtures. The old ones were from Aug 2024 and included a 49-byte pre-creator bonding curve. Behind the protocol: - fetch_price.py, get_bonding_curve_status.py, poll_bonding_curve_progress.py and decode_from_getaccountinfo.py never read quote_mint and scaled by a hardcoded 1e9. Against a live USDC-paired curve the price was off by 1000x. - get_pumpswap_pools.py stopped parsing at coin_creator and missed the i128 virtual_quote_reserves. Live pools carry 17.5845 SOL of them, which under-prices by 3.5-23.9% when ignored. Duplication and naming: - Merge manual_buy_cu_optimized.py into manual_buy.py --cu-optimized. The deleted file's docstring said 512 KB while its code used 16 MB; simulation confirms 512 KB and 4 MB both fail MaxLoadedAccountsDataSizeExceeded on Token-2022 mints, so 16 MB is the correct value. - Merge listen_logsubscribe_abc.py into listen_logsubscribe.py. Its ATA derivation hardcoded the legacy token program, so every Associated BC it printed for a Token2022 coin was an address that does not exist on chain. Fixed on merge and cross-checked 59/59 against on-chain accounts. - Remove 19 dead symbols. BREAKING_FEE_RECIPIENTS is still live in the PumpSwap scripts and stays there. - Normalize naming: kebab-case directories, RPC method names as one lowercase token, scripts verb-first. Rules documented in CLAUDE.md. get_graduating_tokens.py is knowingly left broken: getProgramAccounts over the whole pump program is now rejected by providers and it needs a getProgramAccountsV2 rewrite, which belongs in its own PR. Verified: both offline gates pass, all 41 examples parse, every read-only script exercised on mainnet against SOL- and USDC-paired coins, no new ruff findings (427 -> 413). No script that spends real funds was run. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
836d873d27 |
fix: report on-chain trade outcomes correctly, and repair every broken learning example (#177)
* fix(tx-status): read meta.err before reporting a trade succeeded
confirm_transaction answers "did this signature land in a block?", never
"did it succeed". A landed transaction can have reverted, and RPC reports
that only in meta.err. Reading whether a call threw instead of reading
meta.err produced false results in both directions.
False success — a reverted trade reported as confirmed:
- ten examples (pump.fun, pumpswap, letsbonk) printed "Transaction
confirmed" without checking meta.err
- src/cleanup/manager.py discarded confirm_transaction's boolean and
logged "Closed successfully" unconditionally, so a reverted close
reported rent as reclaimed while the account stayed open
- learning-examples/cleanup_accounts.py did the same
False failure — a good trade reported as unconfirmed:
- _get_transaction_result omitted maxSupportedTransactionVersion, so the
RPC answered -32015 for every versioned (v0) transaction. meta.err was
unreadable and a successful trade read back as failed. The bot sends
legacy transactions, which is the only reason this was survivable.
- confirm_transaction raised TypeError on a base58 str (solana-py wants a
Signature) while _get_transaction_result raised on a Signature (not
JSON serializable). Both were swallowed by a broad except into "not
confirmed". The annotations pointed the wrong way too:
build_and_send_transaction returns Signature, not str.
Changes:
- add learning-examples/tx_status.py — assert_transaction_succeeded and
confirm_and_assert, replacing the copy duplicated in mint_and_buy{,_v2}
- wire it into the ten examples that confirmed without checking
- read the boolean in both cleanup paths
- normalize str/Signature at the client boundary; correct the annotations
- send maxSupportedTransactionVersion: 0 on getTransaction
- split verify_transaction_succeeded out of confirm_transaction so the
meta.err check can run against a transaction that landed earlier
Two reporting bugs found while testing the above:
- live_v2_round_trip read balances at solana-py's default (finalized)
commitment while confirming trades at "confirmed", so the end read saw
pre-trade state and it printed "net change: +0.000000000 SOL" after a
real round trip. Verified: on a busy account finalized trails confirmed
by ~263k lamports.
- cleanup_accounts produced no output at all, success or failure, because
get_logger attaches no handler and only the bot installs one. httpx is
pinned to WARNING alongside it — the RPC endpoint carries an API key.
Adds learning-examples/verify_tx_status_checks.py: offline stub checks, a
scan that fails if an example confirms without checking meta.err, an AST
check that nothing in src/ discards the boolean, a guard that
getTransaction opts into v0, and --live, which replays issue #175's three
signatures against mainnet and requires both layers to reject them on
meta.err (Custom: 6062) rather than on a failed fetch. The two src/ guards
were mutation-tested: reintroducing each bug makes them fail.
The BuybackFeeRecipientMissing (6062) half of #175 was already fixed by
the buy_v2/sell_v2 migration in
|
||
|
|
03a4e7bcbc |
Add mayhem mode support and Token2022 integration (#149)
* feat: mayhem update in idl * feat(examples): update bonding curve scripts * feat(example): update listen_blocksubscribe * feat(examples): update geyser listener * feat(examples): update all new token listeners * feat(examples): add comments, fix printing, formatting * feat(examples): pumpswap buy and sell update with mayhem mode * fix(examples): sell pump amm fee recipient * feat(examples): update decode scripts * feat(examples): update fetch price * feat(examples): buy and sell bonding curve scripts * feat(examples): add mint with mayhem mode enabled * feat(examples): improve listening to wallet txs * feat(examples): migration listener improvements * feat(examples): global vol accumulator is not writable * feat(examples): support token/token2022 programs in buy instructions * feat(examples): token/token2022 for pumpswap buy * feat(examples): token/token2022 supprot for sell instructions * feat(bot): support create_v2 with token2022, mayhem mode, other fixes * fix(bot): support only token2022 in logs and pumportal listeners * feat(bot): token2022 support in cleanup flow * fix(bot): update token program handling and improve price validation in trading logic * feat(bot): enhance token program handling for LetsBonk integration |
||
|
|
b6928d1f5f |
feat(core): integrate account data size limit (#144)
* feat(core): integrate account data size limit in buyer and seller transaction * refactor: format code for improved readability and consistency |
||
|
|
d64b51da44 |
fix(examples): mint and buy script (#141)
* fix(examples): remove hardcoded token mints * feat(core): update pump idls * feat(examples): add track volume bool to mint script * feat(examples): add extend acc instr to mint |
||
|
|
49504f5106 | fix: correct the name of file | ||
|
|
c5a0258aa7 | fix: use base64 explicitly | ||
|
|
ca91c879c3 | fix: align learning examples with refactored modules | ||
|
|
8bf3700187 | fixed formatting | ||
|
|
dcd8475920 | Add the full code |