Commit Graph
10 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>
2026-09-16 09:36:44 +02:00
02343b775b 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>
2026-07-28 17:58:33 +02:00
4a48f545c1 feat(pumpfun): update IDLs and add bonding_curve_v2 + cashback support (#159)
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>
2026-02-27 23:39:52 +01:00
Anton SauchykandGitHub 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
2025-11-18 13:09:37 +01:00
Anton SauchykandGitHub 7ae8b560bd feat(pumpswap): update pool account to writable for buy/sell instructions (#146) 2025-11-04 17:06:22 +01:00
AntonandGitHub 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
2025-10-25 15:01:04 +02:00
smypmsa bdb5da4a1f feat: update pump idls 2025-08-29 14:50:52 +00:00
smypmsa f061a75498 feat: updated IDL from pump fun (volume accumulators) 2025-08-14 08:11:24 +00:00
smypmsa 9bfbb7f4eb chore: update pump idl 2025-08-01 04:47:34 +00:00
smypmsa e41a5f4f4f feat(core): add support for creator fee update 2025-05-12 20:07:51 +00:00