Commit Graph

62 Commits

Author SHA1 Message Date
Anton Sauchyk 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>
2026-07-29 15:26:26 +02:00
Anton Sauchyk 5c1fb7eb28 docs: fix README callout placement, flag walkthrough as lagging
Move the "Also by Chainstack" callout back to its original position
just below the walkthrough link, in its original blockquote style, so
it stays visible without scrolling. The previous README rewrite had
relocated it to the bottom of the file.

Note that the linked walkthrough doc lags behind the code, and point
readers at the README for setup and configuration instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 14:09:47 +02:00
Anton Sauchyk 7727015548 docs: consolidate agent docs, rewrite README, prune deps (#179)
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>
2026-07-29 14:07:17 +02:00
Anton Sauchyk 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
Anton Sauchyk cc85daaeb3 chore(pumpswap): post-2026-04-28 cutover + cashback layout (#168)
* chore(pumpswap+docs): post-2026-04-28-cutover PumpSwap unlock + IDL docs

Drops the `INCLUDE_BREAKING_FEE_ACCOUNTS = False` gate in the PumpSwap
learning examples so the +2 breaking-fee accounts (fee recipient
readonly + its quote-mint ATA mutable) are always appended after
`pool-v2`. Mainnet pump-amm rejects this format pre-cutover (verified
6023 Overflow), so this PR is intentionally **draft until 2026-04-28
16:00 UTC** when the cutover happens; mark it ready-for-review and live-
test then.

Also captures the protocol gotchas we learned during this migration:

- README: a new "2026-04-28 program upgrade" section + an explicit note
  that the vendored IDL is incomplete (missing `bonding-curve-v2` and
  `pool-v2` PDAs that the on-chain program actually requires) with
  pointers to cross-check against on-chain txs.
- CLAUDE.md: a new "Pump.fun protocol notes" subsection summarising
  the same gotchas plus the BC/Pool/CreateEvent layout details and the
  extreme_fast_mode gotcha.

Open question (call out at review time, resolve post-cutover):
- BREAKING_FEE_RECIPIENT.md shows PumpSwap cashback account counts of
  27 buy / 26 sell vs 26 / 24 non-cashback — the extra cashback account
  seed/position isn't documented. Need to sample a real successful
  cashback PumpSwap tx after cutover and add the branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(pumpswap): wire cashback layout for post-2026-04-28 program upgrade

Cashback PumpSwap pools require extra writable accounts inserted before
pool-v2. Identified layouts by sampling on-chain post-cutover txs:

- buy: insert user_volume_accumulator_quote_ata (27 accounts vs 26)
  ref tx 4JaWdExj…fvjK
- sell: insert user_volume_accumulator_quote_ata + user_volume_accumulator
  (26 accounts vs 24)
  ref tx 4ei1cJV7…NP3

Detect via pool account byte 244 (is_cashback_coin). Adds the standalone
sample_cashback_pumpswap.py used to reverse-engineer the layouts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 18:20:53 +02:00
Anton Sauchyk 156cf508a0 chore(idl): refresh pump.fun IDLs from upstream
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>
2026-04-27 16:14:46 +02:00
Anton Sauchyk 7864504c84 docs(idl): vendor upstream pump.fun IDLs for reference
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>
2026-04-27 16:11:19 +02:00
smypmsa 02692793a9 docs: add cli and skill links 2026-03-20 18:56:23 +00:00
smypmsa 73fe95efe4 docs: mention OpenClaw in pumpclaw ecosystem note
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 18:52:56 +00:00
smypmsa f8eb5eb8a0 docs: add ecosystem links to pumpfun-cli and pumpclaw
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 18:51:54 +00:00
Alex Kuligowski 40ad32b8d0 feat(core): add RPC rate limiting and retry handling (#154)
* 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>
2026-02-17 10:02:11 +01:00
Anton 7377e1757e docs: add info about geyser plugin 2025-10-26 14:27:35 +01:00
Anton 093331874b docs(readme): tiny typo 2025-08-11 08:55:13 +02:00
Ake 1a934f6ffd Update README.md 2025-08-11 14:37:16 +08:00
smypmsa 83a7f9e4ad docs(readme): add letsbonk, update commands 2025-08-05 15:18:22 +00:00
Anton 4c3089b70f docs: clarify installation steps 2025-07-14 21:21:04 +02:00
Anton 277f52987a docs: mint feature done 2025-06-29 21:35:08 +02:00
Anton d493d6668b docs: mark exist strategies done 2025-06-17 15:14:05 +02:00
Ake 8e2441b0d9 A quick change on the article name 2025-05-10 12:03:34 +08:00
Anton cc5b768e6d docs: update README.md 2025-05-06 11:30:46 +02:00
Anton ebbc399e0b docs: update README.md 2025-05-06 11:07:24 +02:00
Anton 83b1db2b42 docs: refactored code merged into main 2025-04-30 13:55:38 +02:00
Anton 157f833fb4 Merge pull request #94 from chainstacklabs/refactored/main-v2
Promoting refactored bot to the main branch
2025-04-30 11:51:09 +00:00
Anton eacb2554da docs: Geyser listener completed 2025-04-15 09:06:19 +02:00
Anton 188f43f4cd docs: Geyser listener completed 2025-04-15 09:05:31 +02:00
Anton 5e0e587ca0 docs: done reviewing base64 2025-04-02 22:44:17 +02:00
Anton 62102ddbb7 docs: done reviewing base64 2025-04-02 22:43:16 +02:00
Anton 6c23dba5f4 docs: migration listening done 2025-03-31 10:39:01 +02:00
Anton 355cd6bdb1 docs: add review & optimize json, jsonParsed, base64 2025-03-31 10:37:45 +02:00
Anton 4b224879dd docs: migration listening done 2025-03-31 10:27:47 +02:00
Ake 4dad20719a Update README.md 2025-03-30 07:14:10 +08:00
Anton 72ac4c526f docs: ATA closing done 2025-03-28 19:03:30 +01:00
Anton 1f55adeeea docs: ATA closing done 2025-03-28 09:30:16 +01:00
Anton 1b9c3c5b4b docs: sync readme with main 2025-03-25 14:39:31 +01:00
Ake 528ddf33a7 Change Raydium to PumpSwap 2025-03-24 14:57:07 +08:00
Anton 6544e4bd36 Update README.md 2025-03-21 15:23:05 +01:00
Ake 91e73569f0 Update README.md 2025-03-21 12:10:51 +08:00
Anton e6236a9a86 docs: update README.md 2025-03-19 08:13:07 +01:00
Anton 669f8e8807 docs: minor fix in readme 2025-03-17 17:29:00 +01:00
Anton d3985d6bde docs: added uv instructions 2025-03-17 17:27:43 +01:00
Anton 396e9bce1d docs: update README.md 2025-03-15 09:23:15 +01:00
Anton 4c4b9a270b updated readme 2025-03-07 22:11:21 +01:00
smypmsa 4cda58265a updated readme 2025-03-07 21:09:41 +00:00
Ake 9d66f9e7d4 Add roadmap 2025-03-06 08:39:25 +08:00
Ake faba3d8306 Add logsSubcribe doc 2025-02-21 07:36:38 +07:00
Ake 093459d280 Update README.md 2025-01-22 15:13:32 +07:00
Ake f10781ae4a Update README.md 2025-01-22 15:13:02 +07:00
Ake 6fe4d47336 Update README.md 2025-01-10 09:07:42 +07:00
Ake 15ca1a6692 Update README.md 2025-01-09 09:07:01 +07:00
Ake 37f3c038a4 Update README.md 2025-01-07 08:23:22 +07:00