Skip to content

fix: align instruction builders with IDL signer/writable flags and data encoding#18

Merged
smypmsa merged 3 commits intomainfrom
fix/idl-instruction-audit
Mar 20, 2026
Merged

fix: align instruction builders with IDL signer/writable flags and data encoding#18
smypmsa merged 3 commits intomainfrom
fix/idl-instruction-audit

Conversation

@smypmsa
Copy link
Member

@smypmsa smypmsa commented Mar 20, 2026

Summary

Comprehensive audit of all instruction builders against the pump.fun and PumpSwap AMM IDL files revealed 8 discrepancies between our instruction construction and the on-chain program expectations. These bugs caused PumpSwap AMM buys to fail with error 6023 (Overflow) due to corrupted instruction data, and introduced incorrect account flags that could cause subtle issues.

Changes

  • _TRACK_VOLUME encoding: Fixed from bytes([1, 1]) (2 bytes) to bytes([1]) (1 byte). The IDL defines OptionBool as struct { bool } — a single byte, not Borsh Option<bool> (2 bytes). The extra byte corrupted on-chain instruction parsing.
  • Bonding curve buy/buy_exact_sol_in: Removed _TRACK_VOLUME entirely — mainnet transactions confirm 24-byte data (8 disc + 8 + 8, no track_volume).
  • Bonding curve sell: Removed _TRACK_VOLUME (already confirmed by mainnet).
  • PumpSwap buy/buy_exact_quote_in: Added _TRACK_VOLUME (1 byte) — mainnet shows 25-byte data.
  • create_v2: Changed MAYHEM_PROGRAM_ID from is_writable=False to is_writable=True per IDL.
  • extend_account: Changed user from is_writable=True to is_writable=False per IDL.
  • claim_cashback: Changed user from is_signer=True to is_signer=False per IDL.
  • collect_coin_creator_fee: Added missing 8th account (PUMP_AMM_PROGRAM) per IDL.
  • E2e test script: PumpSwap buy now tries multiple graduated tokens, skipping pools that hit on-chain overflow (error 6023 at buy.rs:400 — a program bug on specific high-reserve pools).

Layers Touched

  • protocol/instructions.py — All instruction builder fixes
  • scripts/mainnet-test.sh — PumpSwap pool fallback for e2e tests
  • tests/test_protocol/ — 8 new tests + updated assertions

Does this affect transaction construction or signing?

Yes — instruction data encoding and account flags are corrected to match IDL specifications. PumpSwap buy transactions now send 25 bytes (was 26), bonding curve buy sends 24 bytes (was 26), and several account writable/signer flags are corrected.

Test Plan

  • Unit tests: 368 passing (8 new tests added)
  • Mainnet e2e: PumpSwap buy+sell ✅, bonding curve buy+sell ✅, token launch ✅, launch+buy ✅

🤖 Generated with Claude Code

Protocol-Level Transaction Construction Changes

This PR modifies instruction builders in src/pumpfun_cli/protocol/instructions.py that are directly used to construct and send real Solana transactions (these builders are used by transaction paths invoked from client code — e.g., trade, pumpswap, launch flows — and exercised by the mainnet e2e script). Any change here affects on-chain funds flow and wallet/authorization behavior.

Instruction Data Encoding Changes

  • Corrected _TRACK_VOLUME encoding to a single byte (bytes([1])) per IDL OptionBool.
  • Bonding curve instructions (build_buy_instructions, build_buy_exact_sol_in_instructions, build_sell_instructions): removed trailing _TRACK_VOLUME from payloads.
    • Before: 26 bytes (8-byte discriminator + 8 + 8 + 2-byte track_volume)
    • Now: 24 bytes (8 + 8 + 8)
    • Unit tests assert sell instruction data == 24 bytes.
  • PumpSwap AMM buys (build_pumpswap_buy_instructions, build_pumpswap_buy_exact_quote_in_instructions): append a single-byte _TRACK_VOLUME.
    • Now: 25 bytes (8 + 8 + 8 + 1)
    • Unit tests assert data length == 25 and final byte == 0x01.
  • These encoding fixes address misparsed instruction data that previously caused on-chain failures (notably overflow error 6023).

Account Metadata (Signer/Writable Flags) & Account List Changes

Aligned account metas with the IDL:

  • create_v2: MAYHEM_PROGRAM_ID (account index 9) is now is_writable=True.
  • extend_account: user account (index 1) is now is_signer=False and is_writable=False (read-only, non-signer).
  • claim_cashback: user account (index 0) is now is_signer=False (writable remains True).
  • collect_coin_creator_fee: added missing 8th account PUMP_AMM_PROGRAM (is_signer=False, is_writable=False).
  • Unit tests were added/updated to assert these account counts and flags.

Implication: transaction construction, required signatures, and which accounts may be mutated by on-chain programs changed; callers must build transactions with the updated signer/writable layout.

Code Paths that Send Transactions

  • These builders are used in flows that send funds on mainnet (buy/sell, token create/launch), and the mainnet e2e script executes real transactions. The PR updates both builders and the e2e script logic.

E2E Script & Operational Behavior

  • scripts/mainnet-test.sh: improved PumpSwap buy flow to iterate graduated pools (select by market_cap), retry across candidates, skip pools that fail specifically with on-chain overflow (error 6023), and classify results as PASS/FAIL/ISSUE accordingly.
  • Added higher-slippage flags and retries for bonding-curve trades and pump-swap sells.
  • New token launch flow added (creates a temp PNG and runs launch + optional buy/sell verifications).

Security & Wallet Implications

  • These changes affect transaction authorization and signing:
    • Removing is_signer for claim_cashback and extend_account means the user's signature is no longer required for those instructions — this changes the on-chain authorization model and must match the program's intended access control as defined by the IDL. It reduces the wallet involvement for these instructions; if incorrect, this could allow other parties to submit those instructions on behalf of the user.
    • Changing MAYHEM_PROGRAM_ID to writable expands which on-chain accounts the program may modify during create_v2.
    • Adding PUMP_AMM_PROGRAM to collect_coin_creator_fee alters required account inputs for that instruction.
  • Instruction data encoding mismatches (fixed here) previously caused misparsed parameters and on-chain errors; those could have led to failed transactions and unexpected on-chain behavior (now corrected and validated by unit and mainnet tests).

Testing & Validation

  • Unit tests: added 8 tests and updated existing assertions to validate data lengths and account flags (total reported: 368 passing).
  • Mainnet e2e: PumpSwap buy+sell, bonding-curve buy+sell, token launch, and launch+buy reported successful with the corrected builders.
  • The e2e script now also detects and tolerates pools that fail only with on-chain overflow (6023) by attempting alternate pools.

Architecture & Layer Violations

  • No architectural or layering violations introduced: instruction builders remain library-level code that constructs AccountMeta and Instruction objects; no new external dependencies or network calls were added to instruction-building code. The e2e script changes only affect CI/QA tooling and test flows.

Summary: This PR corrects critical instruction encoding and account metadata to match the IDL, fixes causes of on-chain parsing/overflow failures (6023) for PumpSwap buys, and updates end-to-end test behavior. Because these builders are used to construct real-value transactions, the signer/writable changes (especially removal of signer requirements) should be reviewed for intended authorization semantics against the on-chain program IDL and security expectations before release.

…ta encoding

Audited all instruction builders against pump_fun_idl.json and pump_fun_amm_idl.json.
Fixed 8 discrepancies: OptionBool encoding (1 byte not 2), removed spurious
track_volume from bonding curve buy/sell, corrected writable/signer flags on
create_v2 and extend_account, fixed claim_cashback signer flag, and added
missing PUMP_AMM_PROGRAM account to collect_coin_creator_fee. E2e mainnet
tests confirm PumpSwap buy+sell, bonding curve buy+sell, and token launch
all work correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 20, 2026

📝 Walkthrough

Walkthrough

Refactors instruction payloads (track-volume encoding moved between bonding-curve and PumpSwap builders), adjusts account signer/writable flags, updates unit tests, and enhances mainnet test script with PumpSwap candidate iteration, slippage control, and a new token-launch workflow that generates a temporary PNG and exercises buy/sell flows.

Changes

Cohort / File(s) Summary
Protocol Instruction Builders
src/pumpfun_cli/protocol/instructions.py
Changed _TRACK_VOLUME to bytes([1]); removed track-volume from bonding-curve buy/sell payloads and appended it to PumpSwap buy payloads. Adjusted account metas: MAYHEM_PROGRAM_ID writable in build_create_instructions; user not signer/writable in build_extend_account_instruction; user not signer in build_claim_cashback_instruction; added PUMP_AMM_PROGRAM account to build_collect_coin_creator_fee_instruction.
Unit Tests — Protocol
tests/test_protocol/test_instructions.py, tests/test_protocol/test_pumpswap_instructions.py, tests/test_protocol/test_token_types.py, tests/test_protocol/test_extras_instructions.py
Updated and added tests reflecting new data lengths: sell instruction data = 24 bytes (track-volume removed), PumpSwap buy payloads include trailing track-volume (25 bytes, last byte == 1). Adjusted account assertions: claim-cashback user no longer signer, collect-coin-creator-fee expects 8 accounts including PUMP_AMM_PROGRAM, and create_v2 asserts MAYHEM_PROGRAM_ID writable.
Mainnet Test Script / Integration Flow
scripts/mainnet-test.sh
Added --slippage 50 to bonding-curve buy/sell calls. Replaced single AMM buy attempt with candidate iteration over graduated pump_swap_pool mints, treating 6023 errors as skip, stopping on other failures, and retrying sell once on failure. Added Group 6b token-launch flow: generate temporary PNG (PIL), pumpfun --json launch (optionally --image), --json launch --buy 0.001, then sell all with single retry; persist output and cleanup temp image.

Sequence Diagram(s)

sequenceDiagram
    actor CLI as pumpfun-cli
    participant BC as Bonding Curve
    participant AMM as PumpSwap AMM
    rect rgba(100,150,200,0.5)
    Note over CLI,BC: Bonding-curve trades use --slippage 50 and exclude track-volume
    CLI->>BC: buy (slippage 50)
    BC-->>CLI: Success / Fail
    CLI->>BC: sell (slippage 50)
    BC-->>CLI: Success / Fail
    end
    rect rgba(150,200,100,0.5)
    Note over CLI,AMM: PumpSwap AMM candidate iteration (track-volume appended)
    loop for each graduated mint
        CLI->>AMM: buy --force-amm --slippage 50
        AMM-->>CLI: Error 6023?
        alt Error 6023
            CLI->>CLI: continue to next candidate
        else Other error
            CLI->>CLI: record FAIL, stop
        else Success
            CLI->>AMM: wait 5s then sell all --slippage 50
            AMM-->>CLI: Success / Fail
            alt Sell fails
                CLI->>AMM: retry sell after 5s
            end
            CLI->>CLI: record PASS, exit loop
        end
    end
    end
Loading
sequenceDiagram
    actor CLI as pumpfun-cli
    participant PIL as PIL (Image Gen)
    participant Launch as pumpfun launch
    participant BC as Bonding Curve
    rect rgba(200,150,100,0.5)
    Note over CLI,Launch: Group 6b token launch workflow
    CLI->>PIL: generate temp PNG
    PIL-->>CLI: temp_image_path
    CLI->>Launch: launch --json [--image temp_image_path]
    Launch-->>CLI: JSON output (mint)
    CLI->>Launch: launch --json --buy 0.001
    Launch-->>CLI: Success / Fail (mint)
    alt buy success
        CLI->>BC: sell all --slippage 50
        BC-->>CLI: Success / Fail
        alt sell fails
            CLI->>BC: retry sell after delay
        end
    end
    CLI->>CLI: persist $LAST_OUTPUT_FILE
    CLI->>PIL: remove temp image
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed Title follows conventional commits format and accurately describes the main change: aligning instruction builders with IDL specifications.
Description check ✅ Passed Description comprehensively covers summary, layers touched, transaction impact, and test plan with clear documentation of 8 IDL discrepancies and their fixes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
No Print() Calls ✅ Passed All modified Python files in src/ contain no bare print() calls. The instruction builder changes in instructions.py follow the requirement.
Layer Separation ✅ Passed All 8 command modules import exclusively from core layer, never directly from protocol layer, maintaining proper architectural boundary and dependency isolation.
Rpcclient Cleanup ✅ Passed No new RpcClient instantiations found in modified files. Changes limited to instruction builders and tests without network client creation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/idl-instruction-audit
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/idl-instruction-audit

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/mainnet-test.sh`:
- Around line 375-378: The script currently treats the launch as a PASS based
only on LAUNCH_EXIT and then later extracts LAUNCHED_MINT from LAUNCH_OUT;
change the logic in the launch handling (references: LAUNCH_EXIT, LAUNCH_OUT,
LAUNCHED_MINT, and record) to validate the parsed JSON/mint before recording
success: after computing LAUNCHED_MINT, check it is non-empty/valid JSON; if
valid, call record "Launch" "launch" "PASS" and continue, otherwise call record
"Launch" "launch" "FAIL" (and log LAUNCH_OUT or the JSON error) and fail the
step so downstream --buy/--sell logic does not assume a mint; apply the same
validation to the other launch branch that sets LAUNCHED_MINT.
- Around line 316-357: The code incorrectly records "All graduated pools hit
error 6023" when a non-6023 AMM failure occurred because AMM_BUY_OK stays false
and AMM_MINT stays empty; fix by introducing a failure marker (e.g.
AMM_NON_OVERFLOW_FAIL) or set AMM_BUY_OK to a distinct value when you hit the
non-6023 branch inside the buy loop (the branch that calls record "PumpSwap"
"buy --force-amm --confirm" "FAIL"), then update the final conditional that
currently checks [[ "$AMM_BUY_OK" == "false" ]] && [[ -z "$AMM_MINT" ]] to also
ensure no non-overflow failure marker is set before recording the 6023 ISSUE;
this ensures the existing record call for non-6023 failures remains
authoritative and prevents misreporting 6023.

In `@src/pumpfun_cli/protocol/instructions.py`:
- Around line 366-369: The instruction definition for extend_account incorrectly
marks the user account as a signer; update the AccountMeta for variable user in
the accounts list inside extend_account to is_signer=False (it is currently
True) to match the IDL, and also flip the corresponding assertion in
tests/test_protocol/test_instructions.py from True to False so the unit test
expects user not to be a signer; locate the accounts list where
AccountMeta(pubkey=user, ...) is created and the matching test assertion to
change both in tandem.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1cd7f6d8-7048-4cca-b896-8520b7663c7b

📥 Commits

Reviewing files that changed from the base of the PR and between de48381 and 4606a07.

📒 Files selected for processing (6)
  • scripts/mainnet-test.sh
  • src/pumpfun_cli/protocol/instructions.py
  • tests/test_protocol/test_extras_instructions.py
  • tests/test_protocol/test_instructions.py
  • tests/test_protocol/test_pumpswap_instructions.py
  • tests/test_protocol/test_token_types.py

- extend_account: fix user account is_signer=True → False per IDL;
  update matching test assertion to expect is_signer=False
- mainnet-test.sh: introduce AMM_OVERFLOW_ONLY flag so non-6023 AMM
  failures are not misclassified as "All graduated pools hit error 6023"
- mainnet-test.sh: validate parsed mint from --json launch output before
  recording PASS; fall back to FAIL with "missing mint" message for both
  launch and launch --buy branches

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/mainnet-test.sh`:
- Line 374: The command capturing LAUNCH_OUT intentionally leaves $LAUNCH_IMG
unquoted which triggers ShellCheck SC2086 and is fragile for paths with spaces;
update the invocation of `uv run pumpfun ... $LAUNCH_IMG` to build the argument
list safely (e.g., construct an array or conditionally append the `--image`
argument) so that when LAUNCH_IMG is non-empty it is passed as a single argument
and when empty nothing is added; adjust the code that sets or uses LAUNCH_OUT
(the `uv run pumpfun` call) and apply the same change to the other occurrence
noted around line 394.
- Around line 370-372: The script currently creates /tmp/e2e_test_token.png
using a Python PIL one-liner and silences failures (the python3 -c "from PIL
import Image; ..." call), causing LAUNCH_IMG to remain empty with no
explanation; update the script to either document the PIL dependency in the
script header and usage comments and/or add a runtime guard: attempt to import
PIL (or run the existing python3 -c import) and if it fails echo a clear warning
like "PIL (Pillow) not installed; continuing without launch image" before
leaving LAUNCH_IMG empty, otherwise create the image and set LAUNCH_IMG="--image
/tmp/e2e_test_token.png" — modify the python3 invocation and the LAUNCH_IMG
logic in the snippet that sets LAUNCH_IMG to implement this check and message.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 942cc52c-9cbf-4100-80c0-48dcbad8fa4e

📥 Commits

Reviewing files that changed from the base of the PR and between 4606a07 and 7ca71f0.

📒 Files selected for processing (3)
  • scripts/mainnet-test.sh
  • src/pumpfun_cli/protocol/instructions.py
  • tests/test_protocol/test_instructions.py

@smypmsa smypmsa merged commit bfc74d1 into main Mar 20, 2026
3 checks passed
@smypmsa smypmsa deleted the fix/idl-instruction-audit branch March 20, 2026 18:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant