* chore(deps): upgrade solders to 0.29 and solana to 0.40 in cookbook and tools
solana 0.40 moves TxOpts (now TxOptsModel), MemcmpOpts and TokenAccountOpts
from solana.rpc.types to solana.rpc.core, and spl.token's *Params dataclasses
to spl.token.models as pydantic models — validated and keyword-only. Three
call sites only failed against mainnet: MemcmpOpts.bytes now takes a base58
str, SyncNativeParams was built positionally, and confirm_transaction was
passed a bare "confirmed".
Also moves every cookbook and tools send path off the legacy Transaction onto
VersionedTransaction + MessageV0, and teaches the getTransaction decoder the
json and base64 encodings alongside jsonParsed — the base64 v1 fixture needs
solders 0.29 to deserialize at all.
src/ is untouched and still on the old imports; the offline suite is 3/19
until that lands.
Verified on mainnet: a 0.003 SOL buy_v2/sell_v2 round trip confirmed and the
wallet returned to SOL only, all six listeners and every read-only script run
live, five trade paths simulated with error=None.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore(deps): move the bot onto solders 0.29 and solana 0.40
Same three API moves as the cookbook commit, now in src: TxOpts is
TxOptsModel in solana.rpc.core, and spl.token's BurnParams/CloseAccountParams
come from spl.token.models. build_and_send_transaction builds a
VersionedTransaction from MessageV0 with no lookup tables, so the bot no
longer sends a legacy transaction anywhere.
verify_transaction_v1 proved the log route by stripping the logs and expecting
nothing back, which only held while solders could not read a v1 envelope. On
0.29 it can, so the check is inverted: make the envelope unreadable, keep the
logs, and the create must still arrive. A second check pins the envelope
fallback, so a decode regression is reported rather than silently absorbed by
the log route.
Offline: 19/19. Live on mainnet, funds returned:
- tools/simulate_bot_buy_path: zero-RPC buy path, err=None, 107,015 CU
- tools/simulate_v2_trades: buy_v2 95,573 CU, buy+sell 174,190 — both inside
the existing 180k/120k limits, so no retune
- tools/live_v2_round_trip: buy_v2 + sell_v2 both landed
- tools/live_listener_matrix: all four listeners detected; logs and pumpportal
ran full round trips; geyser and blocks both drew a coin on an unconfigured
quote mint and correctly refused to size a buy. Every ATA closed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore(deps): bring the rest of the dependencies to current
Every remaining dependency is now at its latest release, with the floors
raised to the versions actually tested rather than the oldest that resolves:
grpcio 1.73.1 -> 1.84.0 grpcio-tools 1.73.1 -> 1.84.0
protobuf 6.33.5 -> 7.36.2 uvloop 0.21.0 -> 0.22.1
pyyaml 6.0.2 -> 6.0.3 python-dotenv 1.2.2 -> 1.2.3
construct 2.10.70 (floor) websockets 17.1 (floor)
ruff 0.12.4 -> 0.16.8
aiohttp and base58 were already current. winloop is left at its old floor:
it is Windows-only and cannot be installed, let alone tested, here.
protobuf 7 arrives with grpcio-tools 1.84 and is the only major. The
committed geyser stubs are gencode 6.31.1 and still pass
ValidateProtobufRuntimeVersion against it; they are not regenerated, because
the committed files import each other absolutely and protoc does not emit
that, so the output would need rewriting. Noted in CLAUDE.md with the
verification date.
ruff 0.16 needs no config change. The repo-wide baseline moves 1680 -> 1713
because the rules moved, not the code; CLAUDE.md carries the new figure so it
is not misread as a regression.
Verified per dependency, not just in aggregate: all four bot configs load
under the new pyyaml/dotenv; the logs, blocks and pumpportal listeners run
under the uvloop policy bot_runner installs; both geyser paths (token
listener and the account-filtered graduation watcher) run under grpcio 1.84 /
protobuf 7. Offline 19/19, including verify_listener_cancellation for
websockets. One more live buy_v2/sell_v2 round trip on the final set, both
landed, ATA closed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(utils): redact RPC credentials from every log record
The provider endpoints carry their API key in the URL and the HTTP clients log
a full request URL at INFO, so anything that raises the root logger to INFO
prints the key. tools/cleanup_accounts.py did, four times, in a terminal.
It was already silencing httpx and httpcore by name. That stopped working the
moment solana-py 0.40 switched to httpx2: the rename took the guard with it
and nothing said so. Naming the clients is now a convenience on top of the
real control — install_secret_redaction masks the value itself, at log-record
creation, whichever logger emits it.
It redacts the *rendered* message rather than record.msg plus the string
arguments. The first version of this did the latter and still leaked: httpx2
passes its URL as a URL object, so the type check skipped the only argument
carrying the key. Confirmed by forcing httpx2 back to INFO and watching the
key print anyway. The host is kept, so a log line still says which provider
answered.
Installed from get_logger, from both logging.basicConfig sites, and from
setup_file_logging before it attaches the handler — logs/ outlives a terminal.
verify_no_rpc_credentials_logged covers all of it, including the non-str
argument. Checked against the defeated version first: it fails there and
passes here. Suite is 20/20.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs(agents): make a real bot run the verification, not the simulations
The ground rule said never to run a bot with real funds, which points at the
tools/ harnesses instead. Those call the buyer, seller and listeners directly:
they skip bot_runner, config loading, the trader loop, the exit strategy and
cleanup, so passing there says nothing about the paths only a full run reaches.
Verification is a real run, with explicit approval for that session, covering
every mode the change touches and ending with the wallet holding SOL only.
Also records why a run can look clean when nothing happened: pump_bot runs every
bots/*.yaml, three of the four committed configs ship enabled: false, and with
all of them disabled it prints nothing and exits 0. The log file is the tell.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs(utils): keep the redaction comment provider-neutral
The parameter list is what matters, not which provider uses which spelling.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>
Rewrite README.md around setup and configuration: fix the clone URL,
document the actual .env variable names, add tables for bots/*.yaml and
the learning-examples directories, and drop the empty changelog, the
2025 roadmap, and the protocol deep-dives that duplicated CLAUDE.md.
Make CLAUDE.md the single agent guide and symlink AGENTS.md to it.
AGENTS.md carried wrong env var names, a stale Python floor, and a
config key that does not exist; its safety rules move into CLAUDE.md.
Document that `uv pip install -e .` puts src/ on sys.path, so imports
are `from utils.logger import ...` rather than `from src.utils...`.
Delete .cursor/rules/, .kiro/steering/, and .windsurf/rules/ - three
byte-identical copies of rules referencing APIs that do not exist in
src/. All three tools read AGENTS.md natively.
Fix pyproject.toml:
- requires-python >=3.9 -> >=3.11; the code uses `X | None` (3.10+) and
ruff already targets py311
- drop borsh-construct and construct-typing, neither of which is
imported anywhere (construct-typing still resolves via solana)
- move grpcio-tools to the dev group; it is protoc, needed only to
regenerate the geyser_pb2 stubs, never at runtime
- move dev deps from [project.optional-dependencies] to
[dependency-groups] so `uv sync` installs ruff, making the documented
`ruff check` / `ruff format` commands actually available
Also gitignore .claude/settings.local.json, which is per-developer.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.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>
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'.