Skip to content

feat: add --cashback flag to token launch + fix create_v2 accounts#14

Merged
smypmsa merged 2 commits intomainfrom
feat/launch-cashback-flag
Mar 18, 2026
Merged

feat: add --cashback flag to token launch + fix create_v2 accounts#14
smypmsa merged 2 commits intomainfrom
feat/launch-cashback-flag

Conversation

@smypmsa
Copy link
Member

@smypmsa smypmsa commented Mar 18, 2026

Summary

  • Wire is_cashback_enabled (OptionBool) through all three layers (commands/core/protocol/) for the create_v2 instruction, exposed as --cashback CLI flag
  • Fix pre-existing bug: mayhem accounts were conditionally included in build_create_instructions only when is_mayhem=True, but the IDL requires them always — this caused all create_v2 calls to fail on-chain with AccountNotEnoughKeys
  • Serialize is_cashback_enabled as a single Borsh byte after is_mayhem_mode in instruction data

Test plan

  • 324 unit tests pass (uv run pytest tests/ -q)
  • 3 new protocol tests: cashback false, cashback true, mayhem+cashback byte encoding
  • 2 new core tests: parameter passthrough verification
  • 1 new CLI smoke test: --cashback flag in help output
  • 8 new surfpool integration tests — full mayhem × cashback × initial_buy matrix, all verified on-chain:
    • test_launch_default / test_launch_cashback / test_launch_mayhem / test_launch_mayhem_cashback
    • test_launch_default_with_buy / test_launch_cashback_with_buy / test_launch_mayhem_with_buy / test_launch_mayhem_cashback_with_buy

🤖 Generated with Claude Code

Protocol Layer & Fund Handling

This PR directly modifies build_create_instructions, which constructs the create_v2 instruction sent to the pump.fun program to launch tokens with real SOL. The changes affect transaction construction in two ways:

Critical Bug Fix (Protocol)

  • Mayhem accounts (MAYHEM_PROGRAM_ID, MAYHEM_GLOBAL_PARAMS, MAYHEM_SOL_VAULT, mayhem_state, mayhem_token_vault) were previously only included in the account list when is_mayhem=True, but the on-chain IDL requires them unconditionally. This omission caused "AccountNotEnoughKeys" failures when creating tokens without the mayhem flag enabled.
  • This PR fixes the bug by unconditionally including all mayhem accounts in create_accounts (lines in protocol/instructions.py show accounts 10-14 are now always present).
  • MAYHEM_SOL_VAULT is correctly marked is_writable=True, consistent with it being a vault account that receives/manages funds.

New Cashback Serialization

  • The is_cashback boolean flag is serialized as a single Borsh byte immediately after is_mayhem in the instruction data using struct.pack("<?", is_cashback).
  • This format change is critical: instruction data serialization must match the on-chain program's deserialization logic exactly, or transactions will fail or funds could be misallocated.

Transaction Construction Changes

The instruction data format now includes:

discriminators["create_v2"] + strings(name/symbol/uri) + creator_pubkey + 
[is_mayhem byte] + [is_cashback byte]

This affects all create_v2 transactions sent via client.send_tx() in launch_token() (core/launch.py, line 114).

Architectural Flow

The change properly threads the --cashback flag through the system:

  • CLI (commands/launch.py): Accepts --cashback typer option
  • Core (core/launch.py): Receives is_cashback parameter, passes to protocol layer, includes in response dict
  • Protocol (protocol/instructions.py): Serializes into instruction data bytes

No violations of abstraction layers—each layer has appropriate responsibility.

Testing & Verification

  • 8 surfpool integration tests verify the full matrix of (is_mayhem × is_cashback × initial_buy), with metadata upload mocked but transactions executed on-chain
  • 3 protocol unit tests validate the byte encoding for cashback false/true and mayhem+cashback combinations
  • Tests confirm account count equals 16 and mayhem accounts are present in all scenarios
  • Integration tests verify tokens are created and queryable on-chain

Wire is_cashback_enabled (OptionBool) through all three layers for the
create_v2 instruction, and fix a pre-existing bug where mayhem accounts
were conditionally included — the IDL requires them always.

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

coderabbitai bot commented Mar 18, 2026

📝 Walkthrough

Walkthrough

Adds a cashback flag to the token launch flow: --cashback on the CLI, propagated through core launch logic into the protocol instruction (encode as is_cashback), and includes mayhem-related accounts unconditionally. Tests added to validate propagation and instruction encoding.

Changes

Cohort / File(s) Summary
CLI Command Layer
src/pumpfun_cli/commands/launch.py
Added --cashback option, pass cashback to launch_token, render Cashback: enabled on success when present.
Core Launch Logic
src/pumpfun_cli/core/launch.py
Added is_cashback: bool = False parameter, forward into build_create_instructions, include "is_cashback" in return payload.
Protocol Instructions
src/pumpfun_cli/protocol/instructions.py
Added is_cashback param and packed it into create_v2 instruction data; mayhem-related accounts (MAYHEM_PROGRAM_ID, MAYHEM_GLOBAL_PARAMS, MAYHEM_SOL_VAULT, mayhem_state, mayhem_token_vault) are now included unconditionally in account list.
CLI & Command Tests
tests/test_commands/test_smoke.py
Added test to ensure --cashback appears in launch --help.
Core Launch Tests
tests/test_core/test_launch.py
Added tests asserting is_cashback is passed to build_create_instructions and returned by launch_token (both true/false cases).
Protocol Instruction Tests
tests/test_protocol/test_instructions.py
Added tests verifying create_v2 encoding for cashback false (0x00) and true (0x01), and combinations with mayhem; updated expected account count.
Integration Tests
tests/test_surfpool/test_launch.py
Added integration tests covering combinations of is_mayhem, is_cashback, and initial_buy_sol, validating token creation and flags.

Sequence Diagram(s)

sequenceDiagram
    rect rgba(235,245,255,0.5)
    participant User
    participant CLI
    participant Core as Core.launch
    participant Protocol as build_create_instructions
    participant RPC as RpcClient/Blockchain
    end

    User->>CLI: run "launch --cashback ..."
    CLI->>Core: launch_token(..., is_cashback=true)
    Core->>Protocol: build_create_instructions(..., is_cashback=true)
    Protocol->>RPC: submit create_v2 instruction (includes is_cashback flag, mayhem accounts)
    RPC->>Blockchain: send tx
    Blockchain-->>RPC: tx signature / confirmation
    RPC-->>Core: return signature/result
    Core-->>CLI: return result {..., "is_cashback": true}
    CLI-->>User: display success and "Cashback: enabled"
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed Title follows conventional commits format (feat:) and clearly summarizes the main changes: adding cashback flag and fixing create_v2 accounts.
Description check ✅ Passed Description covers all required template sections: summary with issue linkage, layers touched (commands/core/protocol), transaction construction impact, and comprehensive test plan with specifics.
Docstring Coverage ✅ Passed Docstring coverage is 91.67% which is sufficient. The required threshold is 80.00%.
No Print() Calls ✅ Passed No bare print() calls found in modified Python files. Command file uses error(), render(), and typer.echo() for output; core files have no print statements.
Layer Separation ✅ Passed All command files in src/pumpfun_cli/commands/ have no direct imports from pumpfun_cli.protocol. The cashback parameter properly flows through command → core → protocol layers, maintaining architectural separation.
Rpcclient Cleanup ✅ Passed RpcClient instantiation in src/pumpfun_cli/core/launch.py properly wraps all operations in try...finally block with await client.close() cleanup.

✏️ 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 feat/launch-cashback-flag
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/launch-cashback-flag
📝 Coding Plan
  • Generate coding plan for human review comments

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: 5

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

Inline comments:
In `@src/pumpfun_cli/commands/launch.py`:
- Around line 68-69: The new ad-hoc typer.echo call should be removed and the
cashback information routed through the existing render(...) path: update the
data structure passed to render (the variable `result` or the response object
used by `render(...)` in launch.py) to include a "cashback" or "is_cashback"
field (e.g., set result["cashback"] = "enabled" or a boolean) and then call
render(result, json_mode) as usual; remove the standalone typer.echo(" 
Cashback: enabled") so TTY/JSON output remains centralized via the render(...)
function.

In `@src/pumpfun_cli/protocol/instructions.py`:
- Around line 324-330: The AccountMeta for MAYHEM_PROGRAM_ID in the create_v2
instruction block is incorrectly marked writable; update the
AccountMeta(pubkey=MAYHEM_PROGRAM_ID, is_signer=False, is_writable=True) to set
is_writable=False to match the IDL/read-only account definition for create_v2
and ensure account meta consistency with the create_v2 instruction.

In `@tests/test_protocol/test_instructions.py`:
- Around line 86-151: The tests only check trailing bytes but must also lock the
instruction account layout to prevent AccountNotEnoughKeys regressions: in each
test that calls build_create_instructions assert that the returned create_ix has
the expected 16 accounts (e.g., assert len(create_ix.keys) == 16) and, for the
mayhem variants, assert the presence/expected position of the mayhem-specific
account (referencing the known mayhem PDA symbol/name used by
build_create_instructions, e.g., MAYHEM_PDA or MAYHEM_ACCOUNT) in
create_ix.keys; add equivalent presence/position checks for any other mandatory
accounts to ensure the full 16-account layout is enforced.

In `@tests/test_surfpool/test_launch.py`:
- Around line 31-231: Tests under tests/test_surfpool/test_launch.py are making
real RPC calls via surfpool_rpc using launch_token and get_token_info, which
violates the no-network rule; refactor these to use mocked RPC responses or mark
them as integration-only: replace direct surfpool_rpc network usage in
test_launch_* functions with fixtures/mocks that stub launch_token and
get_token_info (or inject a mocked rpc client), or move the entire file/tests to
an integration test suite with a distinct marker (e.g., pytest.mark.integration)
so they are excluded from default runs while keeping unit tests in tests/**
fully mocked.
- Around line 31-231: Replace the eight near-identical tests
(test_launch_default, test_launch_cashback, test_launch_mayhem,
test_launch_mayhem_cashback, test_launch_default_with_buy,
test_launch_cashback_with_buy, test_launch_mayhem_with_buy,
test_launch_mayhem_cashback_with_buy) with a single parametrized
pytest.mark.parametrize test that iterates over combinations of is_mayhem
(bool), is_cashback (bool), and initial_buy_sol (None or 0.001); call
launch_token with those params (preserving the `@_UPLOAD_PATCH` decorator,
surfpool_rpc/funded_keypair/test_keystore/test_password fixtures and using
launch_token and get_token_info), assert the same expectations conditionally
(check result["is_cashback"], result["initial_buy_sol"] when provided,
result["signature"], and token info["graduated"] where applicable) to eliminate
duplication while keeping test behavior identical.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 605be9f5-9cc4-4383-b06e-560703c0c8ec

📥 Commits

Reviewing files that changed from the base of the PR and between ed5e22f and ad25194.

📒 Files selected for processing (7)
  • src/pumpfun_cli/commands/launch.py
  • src/pumpfun_cli/core/launch.py
  • src/pumpfun_cli/protocol/instructions.py
  • tests/test_commands/test_smoke.py
  • tests/test_core/test_launch.py
  • tests/test_protocol/test_instructions.py
  • tests/test_surfpool/test_launch.py

… parametrize surfpool

- Set MAYHEM_PROGRAM_ID to is_writable=False in create_v2 (read-only per IDL)
- Add account count + position assertions to create_v2 instruction tests
- Parametrize 8 surfpool launch tests into single matrix test

Co-Authored-By: Claude Opus 4.6 (1M context) <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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pumpfun_cli/protocol/instructions.py (1)

281-291: 🧹 Nitpick | 🔵 Trivial

Consider using keyword-only parameters for protocol-layer safety.

The function signature currently has adjacent bool parameters (is_mayhem, is_cashback) with defaults. While no existing positional call sites risk misbinding, using *, after uri would prevent future positional argument confusion in this critical funds-handling layer:

Suggested improvement
 def build_create_instructions(
     idl: IDLParser,
     mint: Pubkey,
     user: Pubkey,
     name: str,
     symbol: str,
     uri: str,
+    *,
     is_mayhem: bool = False,
     is_cashback: bool = False,
     token_program: Pubkey = TOKEN_2022_PROGRAM,
 ) -> list[Instruction]:

This is a defensive best practice that makes intent explicit without requiring any call-site changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pumpfun_cli/protocol/instructions.py` around lines 281 - 291, The
function build_create_instructions should make the optional flags and
token_program keyword-only to avoid accidental positional misbinding: change the
signature so that parameters after uri (is_mayhem, is_cashback, token_program)
are declared as keyword-only (e.g., add a * after the uri parameter) so callers
must name is_mayhem, is_cashback, and token_program explicitly; update any
internal references to those names if needed but no call-site changes should be
required.
♻️ Duplicate comments (1)
tests/test_surfpool/test_launch.py (1)

61-80: ⚠️ Potential issue | 🟠 Major

Live RPC calls in tests/** violate test policy.

This test still performs real network calls (launch_token, get_token_info) via surfpool_rpc. Please move it to an integration-only suite/marker excluded from default tests/**, or mock RPC responses in this path.

As per coding guidelines, tests/**: “Tests must use mocks for RPC calls, no real network.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_surfpool/test_launch.py` around lines 61 - 80, This test performs
real RPC calls via surfpool_rpc using launch_token and get_token_info; either
convert it to an integration-only test (add an integration marker/rename so it
is excluded from default tests) or replace the external calls with mocks;
specifically, mock the launch_token and get_token_info calls (or the underlying
RPC client used by them) to return the expected result dicts and signatures, and
ensure assertions remain the same while removing any network I/O in the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/pumpfun_cli/protocol/instructions.py`:
- Around line 281-291: The function build_create_instructions should make the
optional flags and token_program keyword-only to avoid accidental positional
misbinding: change the signature so that parameters after uri (is_mayhem,
is_cashback, token_program) are declared as keyword-only (e.g., add a * after
the uri parameter) so callers must name is_mayhem, is_cashback, and
token_program explicitly; update any internal references to those names if
needed but no call-site changes should be required.

---

Duplicate comments:
In `@tests/test_surfpool/test_launch.py`:
- Around line 61-80: This test performs real RPC calls via surfpool_rpc using
launch_token and get_token_info; either convert it to an integration-only test
(add an integration marker/rename so it is excluded from default tests) or
replace the external calls with mocks; specifically, mock the launch_token and
get_token_info calls (or the underlying RPC client used by them) to return the
expected result dicts and signatures, and ensure assertions remain the same
while removing any network I/O in the test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8bf5abe7-8527-4cda-82bf-9464fa2b8ad9

📥 Commits

Reviewing files that changed from the base of the PR and between ad25194 and 57a2040.

📒 Files selected for processing (3)
  • src/pumpfun_cli/protocol/instructions.py
  • tests/test_protocol/test_instructions.py
  • tests/test_surfpool/test_launch.py

@smypmsa
Copy link
Member Author

smypmsa commented Mar 18, 2026

@coderabbitai resolve

@smypmsa smypmsa merged commit b74b635 into main Mar 18, 2026
22 checks passed
@smypmsa smypmsa deleted the feat/launch-cashback-flag branch March 18, 2026 17:54
@coderabbitai
Copy link

coderabbitai bot commented Mar 18, 2026

✅ Actions performed

Comments resolved and changes approved.

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