Skip to main content
ClaudeWave
Slash Command65 repo starsupdated yesterday

debug-user-tx

Reproduce and debug a user-reported failing transaction against forked cluster state, mapping the failure back to source code

Install in Claude Code
Copy
mkdir -p ~/.claude/commands && curl -fsSL https://raw.githubusercontent.com/solanabr/solana-ai-kit/HEAD/.claude/commands/debug-user-tx.md -o ~/.claude/commands/debug-user-tx.md
Then start a new Claude Code session; the slash command loads automatically.

debug-user-tx.md

You are debugging a transaction that a user reports as failing. You have the project's source code locally; the goal is to reproduce the failure against forked cluster state, then map the on-chain error back to the exact line of Rust / IDL that produced it and suggest a fix.

## Related Skills

- [ext/solana-dev/skill/references/testing.md](../skills/ext/solana-dev/skill/references/testing.md) — Surfpool (mainnet fork), LiteSVM, Mollusk
- [ext/solana-dev/skill/references/programs/anchor.md](../skills/ext/solana-dev/skill/references/programs/anchor.md) — Anchor error codes, constraint failures
- [ext/solana-dev/skill/references/programs/pinocchio.md](../skills/ext/solana-dev/skill/references/programs/pinocchio.md) — Pinocchio error patterns
- [ext/solana-dev/skill/references/security.md](../skills/ext/solana-dev/skill/references/security.md) — Common failure categories

## Inputs

Collect from the user (at least one of `signature` or `instruction` is required):

| Input | Required | Notes |
|-------|----------|-------|
| `signature` | preferred | On-chain tx signature. Fastest path — fetch + replay. |
| `wallet` | optional | User's pubkey. Auto-extracted from tx if signature given. |
| `instruction` | fallback | Raw ix JSON when the tx never landed (serialized tx / accounts + data). |
| `cluster` | optional | `mainnet` / `devnet`. Default: infer from `Anchor.toml` or `.env`. |
| `rpc` | optional | Override RPC endpoint. Default: cluster default or project `.env`. |
| `program` | optional | Program ID. Auto-detected from workspace. |

If the user only pasted an error message, ask for the signature before proceeding — it's the difference between 30 seconds and guessing.

## Step 1: Detect Project Layout

```bash
echo "Detecting project..."

FRAMEWORK="unknown"
if [ -f "Anchor.toml" ]; then
    FRAMEWORK="anchor"
    echo "Anchor project detected"
elif [ -f "Cargo.toml" ] && grep -q "pinocchio" Cargo.toml 2>/dev/null; then
    FRAMEWORK="pinocchio"
    echo "Pinocchio project detected"
elif [ -f "Cargo.toml" ] && grep -q "solana-program" Cargo.toml 2>/dev/null; then
    FRAMEWORK="native"
    echo "Native Solana program detected"
else
    echo "No Solana program workspace detected. Debug will proceed RPC-only (no source mapping)."
fi

# Infer cluster
CLUSTER="${CLUSTER:-mainnet}"
if [ -f "Anchor.toml" ]; then
    DETECTED=$(grep -m1 'cluster' Anchor.toml | sed 's/.*= *//' | tr -d '"')
    [ -n "$DETECTED" ] && CLUSTER="$DETECTED"
fi
echo "Cluster: $CLUSTER"

# Collect program IDs + IDLs
ls target/idl/*.json 2>/dev/null || echo "No IDLs found at target/idl/ — run 'anchor build' for best results"
```

## Step 2: Fetch the Failing Transaction

If a signature was provided, fetch it with full detail. Use the project's RPC if configured; otherwise fall back to the cluster default.

```bash
SIG="<signature>"
RPC="${RPC:-https://api.$CLUSTER.solana.com}"

mkdir -p .claude/debug
OUT=".claude/debug/tx-${SIG:0:8}.json"

curl -s -X POST "$RPC" \
  -H "Content-Type: application/json" \
  -d "$(cat <<EOF
{
  "jsonrpc":"2.0","id":1,"method":"getTransaction",
  "params":["$SIG",{"encoding":"json","maxSupportedTransactionVersion":0,"commitment":"confirmed"}]
}
EOF
)" > "$OUT"

# Sanity check
jq -e '.result != null' "$OUT" >/dev/null || { echo "Tx not found or not yet confirmed"; exit 1; }
```

Extract from the JSON (Claude: read `$OUT` with `jq` or the Read tool):

- `meta.err` — the error object (e.g. `{"InstructionError":[1,{"Custom":6003}]}`)
- `meta.logMessages` — full program log output
- `meta.preBalances` / `meta.postBalances`
- `meta.preTokenBalances` / `meta.postTokenBalances`
- `meta.innerInstructions` — CPI tree
- `slot` — for fork replay
- `transaction.message.accountKeys` — ordered account list
- `transaction.message.instructions` — each with `programIdIndex`, `accounts`, `data`
- `transaction.message.addressTableLookups` — resolve if present

## Step 3: Identify the Failing Instruction

From `meta.err`, extract the instruction index. For `InstructionError: [N, ...]`, instruction `N` failed.

```bash
FAILING_IX=$(jq -r '.result.meta.err.InstructionError[0]' "$OUT")
FAILING_PROGRAM=$(jq -r --argjson i "$FAILING_IX" \
  '.result.transaction.message.accountKeys[.result.transaction.message.instructions[$i].programIdIndex]' \
  "$OUT")
echo "Failing instruction #$FAILING_IX → program $FAILING_PROGRAM"
```

> **Note**: Index 0 is almost always `ComputeBudget` (`setComputeUnitLimit` / `setComputeUnitPrice`). The real failure is usually index ≥ 1. Trust `meta.err.InstructionError[0]`, not position.

> **Address Table Lookups**: If `transaction.message.addressTableLookups` is non-empty, extend the account list before indexing: `accountKeys ++ meta.loadedAddresses.writable ++ meta.loadedAddresses.readonly`. Otherwise you'll resolve the wrong program for CPI-heavy txs.

Compare `$FAILING_PROGRAM` against the project's program IDs (from `declare_id!` or `Anchor.toml`). If it matches, source mapping is possible. If not (e.g. Jupiter, Token Program), the failure is inside a CPI target — note this and proceed with log-based diagnosis.

### Decode the instruction discriminator

`instructions[N].data` is **base58-encoded** when fetched with `encoding: "json"` (what Step 2 uses). Decode before slicing.

For the project's own program:

- **Anchor**: first 8 bytes of decoded data = handler discriminator.
  - Anchor ≥ 0.30: match directly against `target/idl/<program>.json` → `instructions[].discriminator`.
  - Anchor < 0.30: IDL has no `discriminator` field. Compute it: `sha256("global:<handler_name>")[0..8]`, then match.
- **Pinocchio / native**: typically first 1 byte. Match against the `match` arm in `process_instruction` (grep `src/lib.rs` or `src/entrypoint.rs`).

Record the handler name.

## Step 4: Map the Error to Source

**Fast path — scan `meta.logMessages` first.** Anchor emits one of these formats depending on how the error was raised:

```
# From err!() / return Err(ErrorCode::X.into()) — includes sou
anchor-engineerSubagent

Anchor framework specialist for rapid Solana program development. Use for building programs with Anchor macros, IDL generation, account validation, and standardized patterns. Prioritizes developer experience while maintaining security.\\n\\nUse when: Building new programs quickly, team projects needing standardization, projects requiring IDL for client generation, or when developer experience is prioritized over maximum CU optimization.

defi-engineerSubagent

DeFi integration specialist for composing with Solana protocols including Jupiter, Drift, Kamino, Raydium, Orca, Meteora, Marginfi, and Sanctum. Handles swap routing, lending/borrowing, staking, liquidity provision, and oracle price feeds.\n\nUse when: Integrating DeFi protocols, building swap interfaces, implementing lending/borrowing, setting up yield strategies, working with Pyth/Switchboard oracles, or composing multi-protocol transactions.

devops-engineerSubagent

CI/CD, infrastructure, and deployment specialist for Solana projects. Handles GitHub Actions, Docker, monitoring, RPC management, and Cloudflare Workers edge deployment.\n\nUse when: Setting up CI/CD pipelines, containerizing Solana validators or programs, configuring monitoring and alerting, managing RPC infrastructure, deploying edge workers, or automating build and deploy workflows.

game-architectSubagent

Senior Solana game architect for game system design, Unity/C# architecture, on-chain game state, player progression, NFT integration, and PlaySolana ecosystem. Use for high-level game design decisions, architecture reviews, and planning complex game systems.\n\nUse when: Designing new Solana games from scratch, planning game state on-chain, Unity project architecture, integrating with PlaySolana/PSG1, or deciding between implementation approaches.

mobile-engineerSubagent

React Native and Expo specialist for building Solana mobile dApps. Handles mobile wallet adapter integration, transaction signing UX, deep linking, and mobile-specific performance optimization.\n\nUse when: Building React Native or Expo mobile apps with Solana integration, implementing mobile wallet adapter flows, setting up deep links for transaction signing, or optimizing mobile dApp performance.

pinocchio-engineerSubagent

CU optimization specialist using Pinocchio framework. Use for performance-critical programs requiring 80-95% CU reduction vs Anchor. Specializes in zero-copy access, manual validation, and minimal binary size.\\n\\nUse when: CU limits are being hit, transaction costs are significant at scale, binary size must be minimized, or maximum throughput is required.

rust-backend-engineerSubagent

Rust backend specialist for building async services that interact with Solana blockchain. Builds APIs, indexing services, and off-chain processing using Axum, Tokio, and modern async patterns.\n\nUse when: Building REST/WebSocket APIs for Solana dApps, implementing transaction indexers, creating webhook services, or any Rust backend that interacts with Solana.

solana-architectSubagent

Senior Solana program architect for system design, account structures, PDA schemes, token economics, and cross-program composability. Use for high-level design decisions, architecture reviews, and planning complex multi-program systems.\n\nUse when: Designing new programs from scratch, planning account structures, optimizing PDA schemes, reviewing architecture for security, or deciding between implementation approaches.