Refactor high-complexity React components in Dify frontend. Use when `pnpm analyze-component...
npx skills add nicofains1/evm-tx-debugger
Or install specific skill: npx add-skill https://github.com/nicofains1/evm-tx-debugger
# Description
Debug failed EVM transactions efficiently using Foundry's cast CLI. Use this skill when analyzing why a blockchain transaction reverted, failed, or produced unexpected results. Covers swaps, NFT mints, bridges, lending protocols, Permit2, and more across 12+ networks.
# SKILL.md
name: evm-tx-debugger
description: Debug failed EVM transactions efficiently using Foundry's cast CLI. Use this skill when analyzing why a blockchain transaction reverted, failed, or produced unexpected results. Covers swaps, NFT mints, bridges, lending protocols, Permit2, and more across 12+ networks.
license: MIT
metadata:
author: nicofains1
version: "1.0.0"
EVM Transaction Debugger
A systematic, efficient approach to diagnose why EVM transactions fail. Uses Foundry's cast CLI and block explorer APIs to identify root causes in seconds, not minutes.
When to Apply
Use this skill when:
- A user reports a failed/reverted transaction
- Investigating why a swap, mint, bridge, or DeFi transaction failed
- Debugging Permit2 signature issues
- Analyzing slippage, deadline, or allowance problems
- Any on-chain transaction that didn't behave as expected
Golden Rules
- NEVER use
cast run- It simulates the entire transaction and takes minutes. Use targeted commands instead. - Always simulate at block N-1 - Use
--block <blockNumber - 1>to reproduce the exact state. - Check the obvious first - 80% of failures are: insufficient balance, slippage, expired deadline, or revoked approval.
- No state changes = early revert - If no token transfers occurred, the tx failed in validation.
- Gas used == Gas limit = out of gas - If these are equal, the transaction ran out of gas.
Quick Reference
| Step | Command | Purpose |
|---|---|---|
| 1 | cast receipt <hash> |
Confirm failure, check gas |
| 2 | cast tx <hash> |
Get tx params |
| 3 | cast 4byte <selector> |
Decode function name |
| 4 | Blockscout API | Decoded params, state changes |
| 5 | cast call --block N-1 |
Reproduce error |
| 6 | Context checks | Validate prices, nonces, balances |
Total time: 10-30 seconds (vs 2-5 minutes with cast run)
Step-by-Step Process
Step 1: Confirm Failure and Check Gas
cast receipt <TX_HASH> --rpc-url <RPC_URL>
Key fields:
| Field | What to Look For |
|---|---|
status |
0 = failed, 1 = success |
gasUsed |
If equal to gasLimit → out of gas |
logs |
Empty [] = failed before emitting events |
# One-liner
cast receipt <TX_HASH> --rpc-url <RPC_URL> --json | jq '{status, gasUsed, gasLimit: .gas}'
Step 2: Get Transaction Data
cast tx <TX_HASH> --rpc-url <RPC_URL>
Key fields: from, to, input (first 4 bytes = selector), value, blockNumber
cast tx <TX_HASH> --rpc-url <RPC_URL> --json | jq '{from, to, value, blockNumber, input: .input[0:10]}'
Step 3: Decode the Function
cast 4byte <SELECTOR>
Common selectors:
| Selector | Function | Protocol |
|---|---|---|
0x3593564c |
execute |
Uniswap Universal Router |
0x5ae401dc |
multicall |
Uniswap V3 |
0x12aa3caf |
swap |
1inch v5 |
0x095ea7b3 |
approve |
ERC20 |
0x23b872dd |
transferFrom |
ERC20/ERC721 |
Complex calldata? If the function is
execute,multicall, or similar batch operations, see the ABI Decoder section below.
Step 4: Get Decoded Parameters
# Get decoded transaction details
curl -s "https://<NETWORK>.blockscout.com/api/v2/transactions/<TX_HASH>" | \
jq '{status, result, revert_reason, method: .decoded_input.method_call, params: .decoded_input.parameters}'
# Check state changes
curl -s "https://<NETWORK>.blockscout.com/api/v2/transactions/<TX_HASH>/state-changes" | \
jq '.items[] | {address: .address.hash, type, change, token: .token.symbol}'
If only gas was spent (no token transfers): Transaction failed in validation phase.
Step 5: Reproduce the Error
cast call <TO_ADDRESS> \
--data "<FULL_INPUT_DATA>" \
--from <FROM_ADDRESS> \
--value <VALUE> \
--block $((BLOCK_NUMBER - 1)) \
--rpc-url <RPC_URL> 2>&1
Step 6: Context-Specific Validation
Token Balance:
cast call <TOKEN> "balanceOf(address)(uint256)" <USER> --block $((BLOCK - 1)) --rpc-url <RPC>
Allowance:
cast call <TOKEN> "allowance(address,address)(uint256)" <OWNER> <SPENDER> --block $((BLOCK - 1)) --rpc-url <RPC>
ETH Balance:
cast balance <ADDRESS> --block $((BLOCK - 1)) --rpc-url <RPC>
Common Failure Patterns
1. Insufficient Slippage (Swaps)
Symptoms: Swap reverts, no token transfers, error mentions "slippage" or "min amount"
Diagnosis:
# Compare minAmountOut with actual market quote using Uniswap V3 Quoter
cast call <QUOTER> \
"quoteExactInputSingle((address,address,uint256,uint24,uint160))(uint256,uint160,uint32,uint256)" \
"(<TOKEN_IN>,<TOKEN_OUT>,<AMOUNT_IN>,<FEE>,0)" \
--block $((BLOCK - 1)) --rpc-url <RPC>
Root cause: Market moved. The minAmountOut was higher than what the market could provide.
2. Expired Deadline
Symptoms: Transaction mined but reverts, error mentions "deadline"
Diagnosis:
cast block <BLOCK> --rpc-url <RPC> --json | jq '.timestamp'
# Compare with deadline parameter: deadline > timestamp = OK
3. Insufficient Balance
Diagnosis:
cast call <TOKEN> "balanceOf(address)(uint256)" <USER> --block $((BLOCK - 1)) --rpc-url <RPC>
4. Insufficient Allowance
Diagnosis:
cast call <TOKEN> "allowance(address,address)(uint256)" <OWNER> <SPENDER> --block $((BLOCK - 1)) --rpc-url <RPC>
5. Permit2 Nonce Already Used
Diagnosis:
PERMIT2="0x000000000022D473030F116dDEE9F6B43aC78BA3"
WORD_POS=$((NONCE >> 8))
cast call $PERMIT2 "nonceBitmap(address,uint256)(uint256)" <USER> $WORD_POS --block $((BLOCK - 1)) --rpc-url <RPC>
# Result of 0 = nonce NOT used
6. Out of Gas
Diagnosis:
cast receipt <TX> --rpc-url <RPC> --json | jq 'if .gasUsed == .gas then "OUT OF GAS" else "Gas OK" end'
7. Contract Paused
Diagnosis:
cast call <CONTRACT> "paused()(bool)" --block $((BLOCK - 1)) --rpc-url <RPC>
ABI Decoder
For complex calldata (multicalls, aggregator routes, batch operations), decode nested calls:
Uniswap Universal Router
# Commands byte mapping:
# 0x00 = V3_SWAP_EXACT_IN, 0x01 = V3_SWAP_EXACT_OUT
# 0x08 = V2_SWAP_EXACT_IN, 0x0b = WRAP_ETH, 0x0c = UNWRAP_ETH
cast pretty-calldata <CALLDATA>
cast calldata-decode "execute(bytes,bytes[],uint256)" <CALLDATA>
Multicall
cast calldata-decode "multicall(uint256,bytes[])" <CALLDATA>
# Then decode each bytes element
1inch / Paraswap
cast calldata-decode "swap(address,(address,address,address,address,uint256,uint256,uint256),bytes,bytes)" <CALLDATA>
Permit2 Batch
cast calldata-decode "permitTransferFrom(((address,uint256),uint256,uint256),(address,uint256)[],address,bytes)" <CALLDATA>
Decode Custom Errors
ERROR_DATA="0x..."
SELECTOR="${ERROR_DATA:0:10}"
cast 4byte $SELECTOR
cast 4byte-decode $SELECTOR "${ERROR_DATA:10}"
Network Reference
Blockscout Domains
| Network | Domain |
|---|---|
| Ethereum | eth.blockscout.com |
| Base | base.blockscout.com |
| Optimism | optimism.blockscout.com |
| Arbitrum | arbitrum.blockscout.com |
| Polygon | polygon.blockscout.com |
| Gnosis | gnosis.blockscout.com |
| Scroll | scroll.blockscout.com |
| zkSync Era | zksync.blockscout.com |
| Linea | linea.blockscout.com |
| Blast | blast.blockscout.com |
Public RPCs
| Network | RPC URL | Chain ID |
|---|---|---|
| Ethereum | https://eth.llamarpc.com |
1 |
| Base | https://mainnet.base.org |
8453 |
| Optimism | https://mainnet.optimism.io |
10 |
| Arbitrum | https://arb1.arbitrum.io/rpc |
42161 |
| Polygon | https://polygon-rpc.com |
137 |
| BSC | https://bsc-dataseed.binance.org |
56 |
| Avalanche | https://api.avax.network/ext/bc/C/rpc |
43114 |
Key Contract Addresses
Permit2 (all chains): 0x000000000022D473030F116dDEE9F6B43aC78BA3
Uniswap V3 Quoter:
- Ethereum/Optimism/Arbitrum/Polygon: 0x61fFE014bA17989E743c5F6cB21bF9697530B21e
- Base: 0x3d4e44Eb1374240CE5F1B871ab261CD16335B76a
Output Template
## Transaction Debug Summary
**TX Hash:** `0x...`
**Network:** [Network]
**Block:** [block number]
**Status:** FAILED
### Transaction Details
- **From:** `0x...`
- **To:** `0x...` ([Contract Name])
- **Function:** `functionName(params)`
- **Value:** [X ETH]
### Root Cause
[One clear sentence explaining why the transaction failed]
### Evidence
1. [Specific data point with values]
2. [Comparison showing the mismatch]
### Recommendation
[How to avoid this failure in the future]
### Commands Used
```bash
[List exact commands for reproducibility]
---
## Installation
```bash
# Install Foundry
curl -L https://foundry.paradigm.xyz | bash
foundryup
# Install jq
brew install jq # macOS
sudo apt install jq # Ubuntu
License
MIT - Use freely, attribution appreciated.
# README.md
EVM Transaction Debugger
Debug failed EVM transactions efficiently using Foundry's cast CLI.
Installation
npx skills add nicofains1/evm-tx-debugger
What This Skill Does
Provides AI agents with a systematic 6-step process to diagnose why blockchain transactions fail:
- Confirm failure - Check receipt status and gas usage
- Get tx data - Extract from, to, calldata, value
- Decode function - Identify what was called
- Get decoded params - Use Blockscout API for full decoding
- Reproduce error - Simulate at previous block
- Context checks - Validate balances, allowances, prices, deadlines
Common Failures Covered
- Insufficient slippage (swaps)
- Expired deadline
- Insufficient balance
- Insufficient allowance
- Permit2 nonce already used
- Out of gas
- Contract paused
- Access control issues
Networks Supported
Ethereum, Base, Optimism, Arbitrum, Polygon, BSC, Avalanche, Gnosis, Scroll, zkSync Era, Linea, Blast
Example Usage
Give your AI agent a failed transaction:
Debug this failed transaction:
TX: 0xabc123...
Network: Base
The agent will follow the skill's process and return a structured debug summary with root cause and evidence.
Requirements
- Foundry (
castCLI) jqfor JSON parsing
License
MIT
Contributing
Found a new failure pattern? PRs welcome!
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.