Skip to content

fix: catch slippage and on-chain tx errors in trade functions#15

Merged
smypmsa merged 2 commits intomainfrom
fix/slippage-error-handling
Mar 18, 2026
Merged

fix: catch slippage and on-chain tx errors in trade functions#15
smypmsa merged 2 commits intomainfrom
fix/slippage-error-handling

Conversation

@smypmsa
Copy link
Member

@smypmsa smypmsa commented Mar 18, 2026

Summary

Fixes B-06 from docs/backlog.md — slippage rejection was dead code.

  • Added TransactionFailedError exception class that parses on-chain error codes from Solana meta.err objects
  • Added slippage error code constants for pump.fun bonding curve (6002, 6003, 6042) and PumpSwap AMM (6004, 6040) programs
  • Wrapped send_tx calls in buy_token, sell_token, buy_pumpswap, sell_pumpswap with structured error handling — slippage errors return {"error": "slippage"} (exit code 3), other on-chain errors return {"error": "tx_error"} (exit code 1)

Layers Touched

  • commands/ — no changes needed; existing dead code (result["error"] in ("graduated", "slippage") → exit code 3) is now live
  • core/trade.py and pumpswap.py now catch TransactionFailedError from send_tx and return structured error dicts
  • protocol/client.py adds TransactionFailedError(RuntimeError) with error code parsing; contracts.py adds slippage error code constants

Does this affect transaction construction or signing?

No. This only changes error handling AFTER send_tx — transaction construction and signing are untouched.

Test Plan

  • Unit tests: 15 new tests added (339 total), all passing
  • Surfpool: not needed (error handling is mocked at the send_tx boundary)
  • Mainnet: not needed

🤖 Generated with Claude Code

Protocol Layer & Fund-Handling Security

  • This PR touches code paths that submit real Solana transactions: RpcClient.send_tx in src/pumpfun_cli/protocol/client.py is the boundary where failures are parsed and where the new TransactionFailedError is raised. send_tx is widely used (core/wallet.py, core/trade.py, core/pumpswap.py, core/launch.py).
  • Crucially, there are no changes to transaction construction, signing, keypair handling, or instruction building. The change is strictly post-submit: when a confirmed transaction has meta.err, send_tx now raises TransactionFailedError (parsing instruction index and error_code) instead of a generic RuntimeError.
  • Therefore this is a read-only change with respect to on-chain fund movement: the transaction has already been submitted/confirmed before any new logic runs.

Architectural Layering

  • Layer responsibilities remain intact:
    • protocol/client.py: reports and parses on-chain error metadata (new TransactionFailedError).
    • protocol/contracts.py: exposes two new slippage-code constants (PUMP_SLIPPAGE_ERROR_CODES, PUMPSWAP_SLIPPAGE_ERROR_CODES).
    • core/trade.py and core/pumpswap.py: interpret TransactionFailedError and map to business-level results ({"error": "slippage", ...} or {"error": "tx_error", ...}) and drive exit codes.
  • No protocol-level responsibility creep: parsing/reporting happens in protocol layer; interpretation and exit-code policy are handled in core layer.

Security Implications for Wallets & Transactions

  • No wallet secret/key handling or signing code was modified. Wallet flows still call client.send_tx but behavior only changes when send_tx reports a confirmed on-chain error.
  • Error parsing could expose on-chain error codes to higher layers (now used to classify slippage vs other tx errors). This improves diagnostics and enables deterministic exit codes (exit code 3 for slippage).
  • There is no added RPC behavior, no additional network calls, and no new capability that can modify or re-submit transactions.

Behavioral Changes & Operational Impact

  • New semantics: when client.send_tx indicates an on-chain failure:
    • Core trade/pumpswap now catch TransactionFailedError and return structured dicts:
      • {"error": "slippage", "error_code": } → mapped to CLI exit code 3 (previously unreachable).
      • {"error": "tx_error", ...} → mapped to exit code 1.
  • Commands layer: previously-dead code paths that set exit code 3 are now reachable.
  • No changes to transaction flow for dry-run paths or pre-validation logic; only post-send handling changed.

New Constants & Tests

  • Added slippage code constants in protocol/contracts.py:
    • PUMP_SLIPPAGE_ERROR_CODES = {6002, 6003, 6042}
    • PUMPSWAP_SLIPPAGE_ERROR_CODES = {6004, 6040}
  • Added TransactionFailedError in protocol/client.py that parses meta.err strings.
  • 15 new unit tests added (total 339) covering parsing, slippage classification, exit codes, and ensuring client.close() is still called on failures; all tests pass.

Review Notes / Risk Summary

  • Risk level: low. No changes to signing, key management, or instruction creation. The primary effect is improved, structured handling of on-chain failures after submission.
  • Actionable items for reviewers: inspect TransactionFailedError regex/parser for robustness against varied Solana meta.err formats; confirm slippage code sets are complete and stable for the deployed programs you interact with.

Implements B-06 (Slippage rejection is dead code).

- Add TransactionFailedError in protocol/client.py that parses on-chain
  error codes from Solana transaction failures
- Add slippage error code constants for both pump.fun bonding curve
  (6002, 6003, 6042) and PumpSwap AMM (6004, 6040)
- Wrap send_tx calls in buy_token, sell_token, buy_pumpswap, sell_pumpswap
  with structured error handling returning {"error": "slippage"} or
  {"error": "tx_error"}
- The existing dead code in commands/trade.py (exit code 3 for slippage)
  is now live
- Add 15 new unit tests covering all error paths

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

coderabbitai bot commented Mar 18, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d480b141-fc37-4d89-a9c9-5b83c0cbeb2d

📥 Commits

Reviewing files that changed from the base of the PR and between 04367a3 and d538dd7.

📒 Files selected for processing (1)
  • .gitignore

📝 Walkthrough

Walkthrough

Adds a TransactionFailedError exception and uses it to standardize on-chain transaction failures. buy/sell flows in trade and pumpswap catch this exception and return structured error dictionaries classifying failures as "slippage" or "tx_error" using new slippage code sets.

Changes

Cohort / File(s) Summary
Protocol layer
src/pumpfun_cli/protocol/client.py, src/pumpfun_cli/protocol/contracts.py
Add TransactionFailedError with regex parsing of error strings; add PUMP_SLIPPAGE_ERROR_CODES and PUMPSWAP_SLIPPAGE_ERROR_CODES constants.
Core error handling
src/pumpfun_cli/core/trade.py, src/pumpfun_cli/core/pumpswap.py
Wrap client.send_tx in try/except for TransactionFailedError; add _handle_tx_error to map codes to `{error: "slippage"
Configuration
.gitignore
Add ignore pattern: .claude/worktrees/.
Tests
tests/test_protocol/test_client.py, tests/test_core/test_trade.py, tests/test_core/test_pumpswap.py, tests/test_commands/test_trade_cmd.py
New/updated tests covering TransactionFailedError parsing, slippage vs non-slippage classification, exit codes, send_tx forwarding, dry-run and balance checks.

Sequence Diagram(s)

sequenceDiagram
    participant App as App (buy/sell)
    participant Client as RpcClient
    participant Chain as On-chain
    participant Handler as _handle_tx_error
    participant Result as Result Dict

    App->>Client: send_tx(...)
    Client->>Chain: execute transaction
    Chain-->>Client: returns error string
    Client-->>App: raise TransactionFailedError(error_str)

    App->>Handler: _handle_tx_error(exc, slippage_codes)
    Handler->>Handler: parse error_code from exc
    alt error_code in slippage_codes
        Handler-->>Result: {error: "slippage", error_code: N}
    else
        Handler-->>Result: {error: "tx_error", error_code: N}
    end

    Result-->>App: return structured error
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 (fix:) and accurately describes the main change: catching slippage and on-chain transaction errors in trade functions.
Description check ✅ Passed Description includes all required sections: clear summary with issue reference (B-06), layers touched (commands/core/protocol), transaction construction impact statement, and test plan details.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
No Print() Calls ✅ Passed All modified Python files in src/ (pumpswap.py, trade.py, client.py, contracts.py) were scanned. No bare print() calls detected. Error handling uses structured dictionaries and exceptions.
Layer Separation ✅ Passed Layer separation constraint satisfied. All eight command files contain zero direct imports from pumpfun_cli.protocol; commands layer correctly imports only from pumpfun_cli.core.
Rpcclient Cleanup ✅ Passed No new RpcClient instantiations in modified core functions; cleanup responsibility remains with callers.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/slippage-error-handling
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/slippage-error-handling
📝 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: 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 @.gitignore:
- Line 12: Remove the "idl/" ignore entry so versioned IDL files are tracked;
specifically delete the `idl/` line from .gitignore and ensure
`idl/pump_fun_idl.json` (and any other IDL files) are committed, because
`src/pumpfun_cli/protocol/idl_parser.py` loads that IDL at runtime and
instruction builders rely on those discriminators.

In `@src/pumpfun_cli/protocol/contracts.py`:
- Around line 63-65: The PUMPSWAP slippage mapping PUMPSWAP_SLIPPAGE_ERROR_CODES
currently contains non-slippage codes (6004, 6040); open idl/pump_fun_idl.json,
find the PumpSwap error definitions and replace the set in
PUMPSWAP_SLIPPAGE_ERROR_CODES with only the actual slippage error code integers
from that IDL (keep the constant name and type), and then update the
assertions/expected outcomes in tests/test_core/test_pumpswap.py so they expect
non-slippage errors for the removed codes (and slippage for the added ones) to
prevent misclassification and wrong exit code 3 responses.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f4fca2fc-93b4-41b5-9b5e-95729f4cdfb2

📥 Commits

Reviewing files that changed from the base of the PR and between b74b635 and 04367a3.

📒 Files selected for processing (9)
  • .gitignore
  • src/pumpfun_cli/core/pumpswap.py
  • src/pumpfun_cli/core/trade.py
  • src/pumpfun_cli/protocol/client.py
  • src/pumpfun_cli/protocol/contracts.py
  • tests/test_commands/test_trade_cmd.py
  • tests/test_core/test_pumpswap.py
  • tests/test_core/test_trade.py
  • tests/test_protocol/test_client.py

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@smypmsa smypmsa merged commit bef50b1 into main Mar 18, 2026
14 checks passed
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