Files
gh-tenequm-claude-plugins-f…/skills/skill/references/debugging.md
2025-11-30 09:01:14 +08:00

5.1 KiB

Debugging Workflows

Foundry debugging tools for finding and fixing smart contract issues.

Verbosity Levels

forge test                 # Summary only
forge test -v              # Show passing test names
forge test -vv             # Print logs for all tests
forge test -vvv            # Traces for failing tests
forge test -vvvv           # Traces for all tests + setup
forge test -vvvvv          # All traces + storage changes

Use:

  • -vv: Quick check of console.log output
  • -vvv: First step when test fails
  • -vvvv: Full debugging with all traces

Understanding Traces

Trace Format

[24661] TestContract::testFunction()
├─ [2262] Target::read()
│   └─ ← 0
├─ [20398] Target::write(42)
│   └─ ← ()
└─ ← ()
  • [gas]: Gas consumed by call and nested calls
  • Green: Successful calls
  • Red: Reverting calls
  • Blue: Cheatcode calls
  • Cyan: Emitted logs
  • Yellow: Contract deployments

Common Trace Errors

Error Meaning
OOG Out of gas
Revert Transaction reverted
InvalidFEOpcode Unknown opcode (0xFE)
NotActivated EVM feature not available

Console Logging

Basic Usage

import {console} from "forge-std/console.sol";

function test_Debug() public {
    console.log("Value:", value);
    console.log("Address:", msg.sender);
    console.log("Balance:", address(this).balance);
}

Format Specifiers

console.log("String: %s", "hello");
console.log("Decimal: %d", 123);
console.log("Hex: %x", 255);

// Multiple arguments (up to 4)
console.log("From %s to %s: %d", from, to, amount);

// Type-specific
console.logBytes32(hash);
console.logAddress(token);
console.logBool(success);

Debugging Pattern

function test_Transfer() public {
    console.log("=== Transfer ===");
    console.log("From:", from);
    console.log("To:", to);
    console.log("Amount:", amount);

    token.transfer(to, amount);

    console.log("Balance after:", token.balanceOf(to));
}

Breakpoints

Set breakpoints in code, jump to them in debugger:

function test_Complex() public {
    vm.breakpoint("start");
    uint256 x = calculate();

    vm.breakpoint("middle");
    process(x);

    vm.breakpoint("end");
}

In debugger, press ' + letter to jump (e.g., 'a for first breakpoint).

Interactive Debugger

Starting

# Debug specific test
forge test --debug --match-test "test_Function"

# Debug script
forge script script/Deploy.s.sol --debug

# Debug transaction from chain
cast run --debug 0x123...

Debugger Layout

Four quadrants:

  1. Top-left: EVM opcodes (current instruction highlighted)
  2. Top-right: Stack state
  3. Bottom-left: Solidity source code
  4. Bottom-right: Memory contents

Navigation Keys

Movement:

  • j/k: Step forward/backward
  • g/G: Go to beginning/end
  • c/C: Next/previous CALL instruction
  • 'a-z: Jump to breakpoint

Display:

  • t: Toggle stack labels
  • m: Toggle memory as UTF8
  • h: Help
  • q: Quit

Debugging Workflow

# 1. Test fails
forge test --match-test "test_Deposit"

# 2. See what failed
forge test -vvv --match-test "test_Deposit"

# 3. Add console.log for quick debugging
# OR use interactive debugger
forge test --debug --match-test "test_Deposit"

# 4. Step through with j/k, watch stack with t

Gas Profiling

Gas Reports

forge test --gas-report

Output:

│ Function    │ min   │ avg   │ median │ max   │ calls │
├─────────────┼───────┼───────┼────────┼───────┼───────┤
│ transfer    │ 2900  │ 5234  │ 5200   │ 8901  │ 145   │
│ balanceOf   │ 596   │ 596   │ 596    │ 596   │ 234   │

Gas Snapshots

forge snapshot              # Create snapshot
forge snapshot --diff       # Compare to previous
forge snapshot --check      # Fail if changed

Inline Measurement

function test_GasUsage() public {
    uint256 gasBefore = gasleft();

    contract.operation();

    uint256 gasUsed = gasBefore - gasleft();
    console.log("Gas used:", gasUsed);
}

Common Error Patterns

Assertion Failure

AssertionError: a == b
  Expected: 1000000
  Actual: 500000

Debug: Run with -vvv to trace calculation.

Revert Without Message

Error: reverted

Debug:

  1. Run with -vvvv for full trace
  2. Find red (reverting) call in trace
  3. Add console.log before suspect operations

Out of Gas

Error: OutOfGas

Fix: Reduce loop iterations or split into batches.

Custom Error

vm.expectRevert(abi.encodeWithSelector(
    InsufficientBalance.selector,
    1000,  // have
    2000   // need
));
token.transfer(recipient, 2000);

Best Practices

  1. Start simple: Use console.log before --debug
  2. Isolate tests: Test one thing per test function
  3. Use descriptive logs: Log state at each step
  4. Check assumptions: Verify preconditions
  5. Save traces: forge test -vvvv > trace.txt