> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sei.io/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript Tracers

> Create custom debugging logic with JavaScript tracers for advanced EVM transaction analysis on Sei. Learn performance optimization, memory management, and advanced debugging patterns.

JavaScript tracers allow you to create custom debugging logic for analyzing EVM transactions on Sei. This powerful feature enables sophisticated analysis patterns beyond what built-in tracers provide.

## Interface Overview

Every JavaScript tracer follows this structure:

```javascript theme={"dark"}
{
  "tracer": `{
    // Initialize variables
    data: [],

    // Called for each opcode execution
    step: function(log, db) {
      // Your analysis logic here
    },

    // Called when execution fails (optional)
    fault: function(log, db) {
      return {};
    },

    // Called at the end to return results
    result: function(ctx, db) {
      return {
        // Your analysis results
      };
    }
  }`
}
```

## Core Methods

### step(log, db)

Called for each EVM opcode execution. This is where your main analysis logic goes.

**Parameters:**

* `log`: Current execution context with methods like `getPC()`, `op.toString()`, `getCost()`
* `db`: Database interface for querying state

**Gas Cost**: Varies by tracer complexity

### result(ctx, db)

Called at the end of execution to return your analysis results.

**Parameters:**

* `ctx`: Execution context with `gasUsed`, transaction info
* `db`: Database interface for final queries

## Advanced Tracer Examples

### State Change Tracker

Monitor all state modifications with detailed analysis:

```javascript theme={"dark"}
{
  "tracer": `{
    stateChanges: {},

    step: function(log, db) {
      if (log.op.toString() == "SSTORE") {
        var key = log.stack.peek(0).toString(16);
        var value = log.stack.peek(1).toString(16);
        var address = log.contract.getAddress().toString();

        if (!this.stateChanges[address]) {
          this.stateChanges[address] = {};
        }

        this.stateChanges[address][key] = {
          oldValue: db.getState(address, key),
          newValue: value,
          pc: log.getPC()
        };
      }
    },

    result: function(ctx, db) {
      return {
        modifiedAccounts: Object.keys(this.stateChanges).length,
        stateChanges: this.stateChanges
      };
    }
  }`
}
```

### DeFi Transaction Analysis

Analyze complex DeFi transactions with multiple contract interactions:

```javascript theme={"dark"}
{
  "tracer": `{
    transfers: [],

    step: function(log, db) {
      // Track ERC20 Transfer events (LOG3 with Transfer signature)
      if (log.op.toString() == "LOG3") {
        var topic0 = log.stack.peek(2).toString(16);
        // ERC20 Transfer event signature
        if (topic0 == "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef") {
          this.transfers.push({
            from: log.stack.peek(3).toString(16),
            to: log.stack.peek(4).toString(16),
            value: log.memory.slice(log.stack.peek(0), log.stack.peek(1)),
            contract: log.contract.getAddress().toString()
          });
        }
      }
    },

    result: function(ctx, db) {
      return {
        totalTransfers: this.transfers.length,
        transfers: this.transfers,
        gasEfficiency: this.transfers.length > 0 ? ctx.gasUsed / this.transfers.length : 0
      };
    }
  }`
}
```

### Security Analysis Tracer

Detect suspicious patterns and potential vulnerabilities:

```javascript theme={"dark"}
{
  "tracer": `{
    suspiciousOps: [],
    externalCalls: 0,
    selfDestructs: 0,

    step: function(log, db) {
      var op = log.op.toString();

      // Track external calls
      if (op == "CALL" || op == "DELEGATECALL" || op == "STATICCALL") {
        this.externalCalls++;

        // Flag suspicious delegate calls
        if (op == "DELEGATECALL") {
          this.suspiciousOps.push({
            type: "DELEGATECALL",
            target: log.stack.peek(1).toString(16),
            pc: log.getPC()
          });
        }
      }

      // Track self-destructs
      if (op == "SELFDESTRUCT") {
        this.selfDestructs++;
        this.suspiciousOps.push({
          type: "SELFDESTRUCT",
          beneficiary: log.stack.peek(0).toString(16),
          pc: log.getPC()
        });
      }
    },

    result: function(ctx, db) {
      return {
        riskScore: this.suspiciousOps.length * 10 + this.selfDestructs * 50,
        externalCalls: this.externalCalls,
        suspiciousOperations: this.suspiciousOps,
        recommendations: this.suspiciousOps.length > 0 ?
          ["Review delegate calls", "Verify contract security"] :
          ["Transaction appears safe"]
      };
    }
  }`
}
```

### Gas Optimization Tracer

Identify expensive operations for gas optimization:

```javascript theme={"dark"}
{
  "tracer": `{
    expensive: [],
    threshold: 1000, // Gas cost threshold

    step: function(log, db) {
      var cost = log.getCost();
      if (cost > this.threshold) {
        this.expensive.push({
          pc: log.getPC(),
          op: log.op.toString(),
          cost: cost,
          gas: log.getGas()
        });
      }
    },

    result: function(ctx, db) {
      // Sort by cost descending
      this.expensive.sort((a, b) => b.cost - a.cost);

      return {
        expensiveOperations: this.expensive.slice(0, 20),
        totalExpensiveCost: this.expensive.reduce((sum, op) => sum + op.cost, 0),
        optimizationPotential: this.expensive.length > 0
      };
    }
  }`
}
```

## Performance Optimization

<Warning>**Performance**: JavaScript tracers can significantly slow down tracing. Optimize your tracer code and use timeouts for production use.</Warning>

### Memory-Efficient Patterns

```javascript theme={"dark"}
// ✅ Efficient - Use summary data
{
  "tracer": `{
    summary: {
      totalGas: 0,
      operationCounts: {},
      maxStackDepth: 0
    },

    step: function(log, db) {
      // Update counters instead of storing arrays
      this.summary.totalGas += log.getCost();

      var op = log.op.toString();
      this.summary.operationCounts[op] = (this.summary.operationCounts[op] || 0) + 1;

      var stackDepth = log.stack.length();
      if (stackDepth > this.summary.maxStackDepth) {
        this.summary.maxStackDepth = stackDepth;
      }
    },

    result: function(ctx, db) {
      return this.summary;
    }
  }`
}
```

### Selective Operation Tracking

```javascript theme={"dark"}
{
  "tracer": `{
    data: [],
    counter: 0,
    maxEntries: 1000, // Limit data collection

    step: function(log, db) {
      // Only track specific operations
      var op = log.op.toString();
      if ((op == "SSTORE" || op == "SLOAD") && this.counter < this.maxEntries) {
        this.data.push([
          log.getPC(),
          op,
          log.getCost()
        ]);
        this.counter++;
      }
    },

    result: function(ctx, db) {
      return {
        operations: this.data,
        truncated: this.counter >= this.maxEntries
      };
    }
  }`
}
```

## Configuration Options

### Basic Configuration

```json theme={"dark"}
{
  "tracer": "your-javascript-tracer",
  "tracerConfig": {
    "timeout": "30s",
    "enableMemory": false,
    "enableStack": false,
    "enableStorage": false,
    "enableReturnData": true
  }
}
```

### Production Settings

```json theme={"dark"}
{
  "tracerConfig": {
    "timeout": "60s", // Longer timeout for complex traces
    "enableMemory": false, // Disable to save memory
    "enableStack": false, // Disable to save memory
    "enableStorage": true, // Enable only if needed
    "enableReturnData": true // Usually safe to enable
  }
}
```

## Practical Integration Examples

### Smart Contract Debugger

```solidity theme={"dark"}
contract TransactionAnalyzer {
    struct TraceResult {
        uint256 gasUsed;
        uint256 operationCount;
        bool hasErrors;
    }

    event TransactionTraced(
        bytes32 indexed txHash,
        uint256 gasUsed,
        uint256 operationCount
    );

    function analyzeTransaction(
        bytes32 txHash,
        string memory tracerCode
    ) external {
        // Off-chain: Use the tracer to analyze the transaction
        // On-chain: Process and store the results

        // This would typically be called by an oracle or off-chain service
        // that performs the tracing and submits results
    }
}
```

### DeFi Analytics Dashboard

```javascript theme={"dark"}
// Off-chain analytics service
class DeFiAnalytics {
  async analyzeSwap(txHash) {
    const swapTracer = `{
            swaps: [],
            
            step: function(log, db) {
                if (log.op.toString() == "LOG3") {
                    // Detect swap events
                    var topic0 = log.stack.peek(2).toString(16);
                    if (topic0 == "swap_event_signature") {
                        this.swaps.push({
                            contract: log.contract.getAddress().toString(),
                            // ... extract swap data
                        });
                    }
                }
            },
            
            result: function(ctx, db) {
                return { swaps: this.swaps };
            }
        }`;

    const result = await this.traceTransaction(txHash, swapTracer);
    return this.processSwapData(result);
  }
}
```

## Best Practices

1. **Start Simple**: Begin with basic tracers before adding complexity
2. **Limit Data Collection**: Only collect necessary information
3. **Use Efficient Data Structures**: Prefer counters over arrays when possible
4. **Set Appropriate Timeouts**: Balance thoroughness with performance
5. **Test Incrementally**: Validate tracers on simple transactions first
6. **Handle Errors Gracefully**: Include error handling in your tracers

## Common Use Cases

### Gas Profiling

```javascript theme={"dark"}
const gasProfiler = `{
  profile: {},
  
  step: function(log, db) {
    var op = log.op.toString();
    var cost = log.getCost();
    
    if (!this.profile[op]) {
      this.profile[op] = { count: 0, totalCost: 0 };
    }
    
    this.profile[op].count++;
    this.profile[op].totalCost += cost;
  },
  
  result: function(ctx, db) {
    // Calculate averages
    for (var op in this.profile) {
      this.profile[op].avgCost = this.profile[op].totalCost / this.profile[op].count;
    }
    return this.profile;
  }
}`;
```

### Event Extraction

```javascript theme={"dark"}
const eventExtractor = `{
  events: [],
  
  step: function(log, db) {
    var op = log.op.toString();
    if (op.startsWith("LOG")) {
      this.events.push({
        address: log.contract.getAddress().toString(),
        topics: this.extractTopics(log),
        data: this.extractData(log)
      });
    }
  },
  
  extractTopics: function(log) {
    // Implementation depends on log structure
    return [];
  },
  
  result: function(ctx, db) {
    return { events: this.events };
  }
}`;
```

<Info>**Need Help?** If you encounter issues with JavaScript tracers, check out our [Troubleshooting Guide](/evm/tracing/troubleshooting) for common solutions and debugging tips.</Info>
