feat(trading): zero-RPC extreme-fast buys and listener race hardening (#170) (#183)

Extreme-fast mode submits with no RPC calls between detection and buy
when the listener parsed the on-chain CreateEvent (geyser/logs/blocks):
TokenInfo.state_from_event carries the canonical creator, mayhem/cashback
flags and quote_mint, so the pre-buy curve refresh is skipped. The geyser
listener now delegates to the event-first parser instead of inlining
instruction decoding, which also catches create_v2 transactions that omit
the trailing is_cashback_enabled byte.

Listeners without event data (pumpportal) keep the refresh, hardened per
issue #170: bonding curve derived from the mint instead of the payload's
bondingCurveKey, curve and mint read in one slot-consistent
getMultipleAccounts (correcting the guessed token program), and an
unreadable curve now skips the token within trade.curve_refresh_budget
instead of submitting a buy built from guessed accounts.

Machine-checked by verify_extreme_fast_zero_rpc.py and
verify_pumpportal_buy_path.py; validated live via simulate_bot_buy_path
on both geyser and pumpportal paths.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Anton Sauchyk
2026-07-29 18:29:03 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent bc59a2257d
commit 7bc6f25508
19 changed files with 1239 additions and 86 deletions
+29 -1
View File
@@ -90,13 +90,41 @@ Logs land in `logs/{bot_name}_{timestamp}.log`.
The YAML files are commented inline. The sections that matter most:
- **`trade`** — `buy_amount` (in SOL), slippage, `exit_strategy` (`time_based`, `tp_sl`, `manual`), and `extreme_fast_mode`, which skips the bonding-curve price check and buys a fixed token amount instead. Faster, less precise.
- **`trade`** — `buy_amount` (in SOL), slippage, `exit_strategy` (`time_based`, `tp_sl`, `manual`), and `extreme_fast_mode`, which skips the bonding-curve price check and buys a fixed token amount instead. Faster, less precise. See [Extreme fast mode](#extreme-fast-mode-zero-rpc-buys) for the zero-RPC behavior and its two knobs, `trust_create_event` and `curve_refresh_budget`.
- **`priority_fees`** — fixed or dynamic. Dynamic costs an extra RPC call, which slows the buy.
- **`filters`** — `listener_type`, `max_token_age`, name/creator matching, `marry_mode` (buy only, never sell), `yolo_mode` (trade continuously).
- **`retries`** — attempts and the wait windows around creation, buy, and the next token.
- **`cleanup`** — when to close leftover token accounts: `disabled`, `on_fail`, `after_sell`, `post_session`.
- **`node.max_rps`** — cap requests per second to match your provider's plan.
### Extreme fast mode: zero-RPC buys
With `extreme_fast_mode: true` the bot buys a fixed token amount
(`extreme_fast_token_amount`) instead of fetching the curve price first. For
tokens detected through the on-chain **CreateEvent** — the `geyser`, `logs`
and `blocks` listeners — the buy is built entirely from the event: the
canonical creator, mayhem/cashback flags and quote mint are all in it, so
**no RPC call happens between detecting the token and submitting the buy**.
That is the point of the mode; a single account read costs ~4050 ms even on
a good endpoint, a tenth of a slot.
The `pumpportal` listener can't do this — its payload carries none of those
fields — so it performs one batched account read (bonding curve + mint in a
single slot-consistent `getMultipleAccounts`) before buying. If the curve
isn't readable within `trade.curve_refresh_budget` seconds (default 2.0),
the token is **skipped**: a buy built from guessed accounts reverts on-chain
with `NotAuthorized` (6000) or `ConstraintSeeds` (2006) and still costs the
fee. The same skip applies to any token whose event data was incomplete.
`trade.trust_create_event: false` turns the zero-RPC path off and forces the
pre-buy read for every listener — the safe fallback if pump.fun changes what
the CreateEvent carries.
Machine checks: `learning-examples/verify_extreme_fast_zero_rpc.py` (the
zero-RPC contract per listener) and
`learning-examples/verify_pumpportal_buy_path.py` (the refresh/skip path).
Neither moves funds.
### Non-SOL quote assets
pump.fun supports quote assets other than SOL, USDC first. Amounts are in that mint's own whole units, so `usdc: 1.0` is one USDC and is **not** comparable to `buy_amount`: