Documentation Index
Fetch the complete documentation index at: https://seilabs.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
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:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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: JavaScript tracers can significantly slow down tracing. Optimize your tracer code and use timeouts for production use.
Memory-Efficient Patterns
// ✅ 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
{
"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
{
"tracer": "your-javascript-tracer",
"tracerConfig": {
"timeout": "30s",
"enableMemory": false,
"enableStack": false,
"enableStorage": false,
"enableReturnData": true
}
}
Production Settings
{
"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
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
// 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
- Start Simple: Begin with basic tracers before adding complexity
- Limit Data Collection: Only collect necessary information
- Use Efficient Data Structures: Prefer counters over arrays when possible
- Set Appropriate Timeouts: Balance thoroughness with performance
- Test Incrementally: Validate tracers on simple transactions first
- Handle Errors Gracefully: Include error handling in your tracers
Common Use Cases
Gas Profiling
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;
}
}`;
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 };
}
}`;
Need Help? If you encounter issues with JavaScript tracers, check out our
Troubleshooting Guide for common solutions and debugging tips.