mirror of
https://github.com/chainstacklabs/pumpfun-bonkfun-bot.git
synced 2026-11-02 06:16:10 +00:00
buy_v2 takes an exact token count and caps the quote cost. The trader passed the slippage-reduced floor meant for exact-in programs as that count, so every pump.fun buy received token_amount * (1 - slippage). Slippage now goes on the cost ceiling only. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
7.7 KiB
7.7 KiB
Regression verifiers
The verifiers
Every fix ships with an offline verifier under tests/regression/. Each script's
docstring carries the bug it guards and the checks it runs — read that before
touching the code it covers, and run the ones your change reaches. None move
funds. uv run tests/regression/run_all.py runs the whole set, or name
individual scripts for a subset.
| Script | Checks |
|---|---|
verify_v2_account_layout.py |
buy_v2/sell_v2 account layouts, PDA/ATA derivations, encoding — against idl/pump_fun_idl.json |
verify_curve_account_sizes.py |
125/151/256-byte curves all decode, and nothing filters on account length |
verify_create_v2_optional_args.py |
omitted trailing option-typed create_v2 args decode as unset; mandatory args still fail |
verify_create_v2_launch_args.py |
the launch path derives a holder-reward coin's curve creator, reaches all four trailing-arg wire forms, and sizes the opening buy from Global |
verify_atomic_launch_v1.py |
the launch is one v1 transaction holding create and buy, fits 4096 bytes where v0 could not hold it, sends no ComputeBudget instructions, and states the priority fee as a lamport total |
verify_quote_asset_selection.py |
both quote registries are consulted, each admitted mint prices from its own opening reserve, an unlisted mint is refused, and a paused mint is rejected up front |
verify_transaction_v1.py |
every reader asks maxSupportedTransactionVersion: 1; a v1 create_v2 is detected from logs alone with the envelope unreadable, and from the envelope alone with the logs stripped; the same two routes over geyser, plus the inline v1 budget |
verify_shreds_listener.py |
pre-execution creates decode from the instruction alone: user at create_v2 account 5, a holder-reward creator derived as PDA(["holder-rewards", mint]), truncated trailing args decoding as not-holder-reward, lookup-table accounts resolved, and nothing reading a meta the stream has no field for |
verify_block_null_guard.py |
a blockSubscribe frame with value.block: null is skipped, not logged as an error |
verify_logs_listener_skips_failed_tx.py |
a logsNotification carrying an err yields no coin, so a create whose transaction reverted is not bought; a successful one is still detected, and geyser still excludes failed transactions server-side |
verify_creator_migration_from_logs.py |
a fee-sharing coin's creator comes from MigrateBondingCurveCreatorEvent in the same logs, not from CreateEvent.creator, so creator_vault matches the curve and the buy does not revert ConstraintSeeds; ordinary creates unchanged, state_from_event kept, and neither event is honoured unless pump.fun's own invocation emitted it |
verify_ping_loop_close_is_quiet.py |
a WebSocket closing normally ends the ping loop quietly in every listener, while an unexpected failure is still logged |
verify_listener_cancellation.py |
a cancelled WebSocket listener stops, even when websockets reports cancellation as AssertionError |
verify_create_log_gate.py |
the create marker is matched as a whole log line, so a foreign Create* instruction is not a coin and a real create sharing its transaction with one is still detected |
verify_extreme_fast_zero_rpc.py |
zero RPC calls between detection and submission for CreateEvent-sourced tokens |
verify_curve_refresh_buy_path.py |
a TokenInfo without state_from_event refreshes first: an unreadable curve skips the buy, and curve + mint are read in one slot-consistent batch that corrects the token program |
verify_cleanup_survives_shutdown.py |
shutdown cleanup runs to completion through the cancellation that triggered it, bounded, and a cleanup that hangs or raises still lets the process exit |
verify_token_queue_shutdown.py |
cancelling the token queue processor ends it without raising, and task_done() stays balanced on the paths that took an item |
verify_pumpfun_buy_token_amount.py |
a pump.fun buy_v2 asks for the whole sized token count with slippage on the cost ceiling, in extreme_fast_mode and on the regular path |
verify_buy_result_not_lost.py |
a landed buy is never reported failed, and a reverted one never reported landed |
verify_tx_status_checks.py |
every path reads meta.err; --live replays known reverted signatures |
verify_tp_sl_exit_price.py |
the tp/sl exit prices off the trigger price, and a reverted sell is retried, bounded |
verify_time_based_exit_retry.py |
the default time_based exit retries a reverted sell instead of stranding the position |
verify_time_exit_without_price.py |
max_hold_time still fires when every price read fails |
verify_exit_sell_confirmation.py |
an exit sell is retried only when retrying is provably safe |
verify_rpc_deadline.py |
post_rpc bounds wall time, not just attempts (virtual clock) |
verify_quote_decimals_resolved.py |
no trade path prices a coin before resolving its quote mint's decimals; nothing in cookbook/ or src/ falls back to a literal decimal count; both unit helpers raise for an unresolved mint; the two pre-seeded quote tables carry the same mints; the buy clears its quote gate before it sizes |
verify_migration_event_discriminator.py |
the migration decoder rejects any payload that is not the wrapper program's CreatePoolEvent, including a foreign one long enough for the schema |
verify_cookbook_arguments.py |
every cookbook script takes its input as a command-line argument |
verify_documentation_links.py |
no known-dead URL is back; --live fetches every one and fails on 4xx/5xx |
verify_no_rpc_credentials_logged.py |
credentials masked in every log record, including a URL passed as a non-str argument, and every site that installs a root handler installs the redaction first |
verify_prices_without_hardcoded_sol_unit.py |
nothing in core/, trading/ or platforms/ scales an amount by LAMPORTS_PER_SOL |
verify_pumpswap_account_layout.py |
pump-amm's pool-v2 account is gated on coin_creator, the buyback pair stays last, and base-token decimals are resolved rather than assumed; --live re-reads the authorized recipients from GlobalConfig |
verify_launchlab_trades.py |
against real StonkFun trades, the LaunchLab curve math reproduces each buy and sell to the unit — fee rate read from the configs, transfer fee from the mint; price comes from virtual plus real reserves; the swap names the on-chain 18 accounts with fee vaults keyed by the pool's quote mint; an exact-in buy gets the configured amount unpadded; buy and sell floors are net of curve fee and transfer fee; single-token mode waits past a coin its filters refuse instead of exiting on it |
verify_launchlab_launch_parsing.py |
legacy, v0 and v1 StonkFun launches parse from every account slot over blockSubscribe and geyser alike, lookup-table accounts included; the creator slot wins over the payer; a router-issued launch is found; letsbonk and StonkFun each keep to their own platform configs; LaunchLab listeners subscribe on platform configs, pump.fun on its program |
verify_cleanup_harvests_withheld_fees.py |
cleanup harvests withheld Token-2022 transfer fees into the mint before closing a token account, and sends a bare close when nothing is withheld |
Two mainnet simulations, also no funds moved:
uv run tools/simulate_v2_trades.py <MINT> # buy_v2/sell_v2 for one coin, reports CU
uv run tools/simulate_bot_buy_path.py # the bot's whole buy path, fresh coin
After any pump.fun program upgrade run verify_v2_account_layout,
verify_curve_account_sizes and both simulations, then retune
get_buy_compute_unit_limit / get_sell_compute_unit_limit in
platforms/pumpfun/instruction_builder.py from the reported unitsConsumed.