Skip to main content

debug_traceBlockByNumber

Traces all transactions in a block specified by block number. This is a convenience method that combines block lookup with tracing.

Premium Method

This method requires enhanced API access. Please contact support for access to debug methods.

When to Use This Method​

debug_traceBlockByNumber is ideal for:

  • Historical Analysis - Trace blocks from specific heights
  • Sequential Processing - Analyze blocks in order
  • Monitoring - Track blocks as they're produced
  • Research - Study blockchain behavior at specific points

Parameters​

  1. Block Number - QUANTITY|TAG

    • Block number as hex
    • Or tags: "latest", "earliest", "pending"
  2. Tracer Configuration - Object

    • tracer - Type of tracer ("callTracer", "prestateTracer")
    • tracerConfig - Additional options
{
"jsonrpc": "2.0",
"method": "debug_traceBlockByNumber",
"params": [
"0x1234", // or "latest"
{
"tracer": "callTracer",
"tracerConfig": {
"onlyTopCall": false
}
}
],
"id": 1
}

Returns​

Array of transaction traces for all transactions in the block.

Implementation Examples​

import { JsonRpcProvider, toBeHex } from 'ethers';

const provider = new JsonRpcProvider('https://api-unichain-mainnet.n.dwellir.com/YOUR_API_KEY');

// Block range analyzer
class BlockRangeAnalyzer {
constructor(provider) {
this.provider = provider;
}

async traceBlockByNumber(blockNumber, config = {}) {
const blockHex = typeof blockNumber === 'string' ?
blockNumber : toBeHex(blockNumber);

const traces = await this.provider.send('debug_traceBlockByNumber', [
blockHex,
{
tracer: config.tracer || 'callTracer',
tracerConfig: config.tracerConfig || {
onlyTopCall: false
}
}
]);

return this.processBlockTraces(traces, blockNumber);
}

async traceBlockRange(startBlock, endBlock, config = {}) {
const results = [];

for (let block = startBlock; block <= endBlock; block++) {
console.log(`Tracing block ${block}...`);

try {
const traces = await this.traceBlockByNumber(block, config);
results.push({
blockNumber: block,
success: true,
data: traces
});
} catch (error) {
results.push({
blockNumber: block,
success: false,
error: error.message
});
}

// Rate limiting
await new Promise(resolve => setTimeout(resolve, 100));
}

return this.analyzeRange(results);
}

processBlockTraces(traces, blockNumber) {
const analysis = {
blockNumber: blockNumber,
transactionCount: traces.length,
successfulTxs: 0,
failedTxs: 0,
totalGasUsed: 0,
contractCalls: [],
uniqueContracts: new Set(),
errorTypes: {}
};

traces.forEach((trace, index) => {
const result = trace.result || {};
const gasUsed = parseInt(result.gasUsed || '0x0', 16);

analysis.totalGasUsed += gasUsed;

if (result.error) {
analysis.failedTxs++;
analysis.errorTypes[result.error] =
(analysis.errorTypes[result.error] || 0) + 1;
} else {
analysis.successfulTxs++;
}

// Track contract interactions
if (result.to) {
analysis.uniqueContracts.add(result.to);
analysis.contractCalls.push({
index: index,
to: result.to,
type: result.type,
gasUsed: gasUsed
});
}

// Process sub-calls
if (result.calls) {
this.processSubCalls(result.calls, analysis);
}
});

analysis.uniqueContracts = Array.from(analysis.uniqueContracts);
analysis.successRate = (
(analysis.successfulTxs / analysis.transactionCount) * 100
).toFixed(2) + '%';

return analysis;
}

processSubCalls(calls, analysis) {
for (const call of calls) {
if (call.to) {
analysis.uniqueContracts.add(call.to);
}
if (call.calls) {
this.processSubCalls(call.calls, analysis);
}
}
}

analyzeRange(results) {
const summary = {
blocksAnalyzed: results.length,
successfulBlocks: results.filter(r => r.success).length,
failedBlocks: results.filter(r => !r.success).length,
totalTransactions: 0,
totalGasUsed: 0,
averageTransactionsPerBlock: 0,
averageGasPerBlock: 0,
mostActiveBlock: null,
leastActiveBlock: null
};

const successfulResults = results.filter(r => r.success);

if (successfulResults.length === 0) {
return summary;
}

// Calculate totals
successfulResults.forEach(result => {
summary.totalTransactions += result.data.transactionCount;
summary.totalGasUsed += result.data.totalGasUsed;
});

// Calculate averages
summary.averageTransactionsPerBlock =
summary.totalTransactions / successfulResults.length;
summary.averageGasPerBlock =
summary.totalGasUsed / successfulResults.length;

// Find extremes
const sorted = successfulResults.sort((a, b) =>
b.data.transactionCount - a.data.transactionCount
);

summary.mostActiveBlock = {
number: sorted[0].data.blockNumber,
transactions: sorted[0].data.transactionCount
};

summary.leastActiveBlock = {
number: sorted[sorted.length - 1].data.blockNumber,
transactions: sorted[sorted.length - 1].data.transactionCount
};

return summary;
}

async findSpecificPattern(startBlock, endBlock, pattern) {
const matches = [];

for (let block = startBlock; block <= endBlock; block++) {
const traces = await this.traceBlockByNumber(block);

// Check for pattern
if (pattern.type === 'high_gas') {
const highGasTxs = traces.contractCalls.filter(
call => call.gasUsed > pattern.threshold
);

if (highGasTxs.length > 0) {
matches.push({
block: block,
transactions: highGasTxs
});
}
} else if (pattern.type === 'failed_calls') {
if (traces.failedTxs > 0) {
matches.push({
block: block,
failedCount: traces.failedTxs,
errors: traces.errorTypes
});
}
} else if (pattern.type === 'contract_activity') {
const activity = traces.contractCalls.filter(
call => call.to === pattern.contract
);

if (activity.length > 0) {
matches.push({
block: block,
calls: activity.length,
totalGas: activity.reduce((sum, call) =>
sum + call.gasUsed, 0
)
});
}
}
}

return matches;
}

async compareBlocks(blockNumbers) {
const comparison = {
blocks: [],
metrics: {
gasUsage: [],
transactionCount: [],
successRate: [],
uniqueContracts: []
}
};

for (const blockNum of blockNumbers) {
const analysis = await this.traceBlockByNumber(blockNum);

comparison.blocks.push(analysis);
comparison.metrics.gasUsage.push({
block: blockNum,
gas: analysis.totalGasUsed
});
comparison.metrics.transactionCount.push({
block: blockNum,
count: analysis.transactionCount
});
comparison.metrics.successRate.push({
block: blockNum,
rate: analysis.successRate
});
comparison.metrics.uniqueContracts.push({
block: blockNum,
count: analysis.uniqueContracts.length
});
}

// Calculate statistics
comparison.statistics = {
avgGas: comparison.metrics.gasUsage.reduce(
(sum, m) => sum + m.gas, 0
) / blockNumbers.length,
avgTransactions: comparison.metrics.transactionCount.reduce(
(sum, m) => sum + m.count, 0
) / blockNumbers.length,
gasVariance: this.calculateVariance(
comparison.metrics.gasUsage.map(m => m.gas)
)
};

return comparison;
}

calculateVariance(values) {
const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
const squaredDiffs = values.map(val => Math.pow(val - mean, 2));
return squaredDiffs.reduce((sum, val) => sum + val, 0) / values.length;
}
}

// Real-time block monitor
class BlockMonitor {
constructor(provider) {
this.provider = provider;
this.analyzer = new BlockRangeAnalyzer(provider);
this.monitoring = false;
}

async startMonitoring(callback, config = {}) {
this.monitoring = true;

this.provider.on('block', async (blockNumber) => {
if (!this.monitoring) return;

try {
const analysis = await this.analyzer.traceBlockByNumber(
blockNumber,
config
);

// Check for anomalies
const anomalies = this.detectAnomalies(analysis);

callback({
blockNumber: blockNumber,
analysis: analysis,
anomalies: anomalies,
timestamp: Date.now()
});
} catch (error) {
console.error(`Failed to trace block ${blockNumber}:`, error);
}
});
}

stopMonitoring() {
this.monitoring = false;
this.provider.removeAllListeners('block');
}

detectAnomalies(analysis) {
const anomalies = [];

// High failure rate
if (analysis.failedTxs / analysis.transactionCount > 0.1) {
anomalies.push({
type: 'high_failure_rate',
value: analysis.successRate,
severity: 'warning'
});
}

// Unusual gas usage
const avgGasPerTx = analysis.totalGasUsed / analysis.transactionCount;
if (avgGasPerTx > 500000) {
anomalies.push({
type: 'high_gas_usage',
value: avgGasPerTx,
severity: 'info'
});
}

// Too many unique contracts
if (analysis.uniqueContracts.length > 50) {
anomalies.push({
type: 'high_contract_diversity',
value: analysis.uniqueContracts.length,
severity: 'info'
});
}

return anomalies;
}
}

// Usage examples
const analyzer = new BlockRangeAnalyzer(provider);

// Trace specific block
const blockAnalysis = await analyzer.traceBlockByNumber(12345678);
console.log('Block analysis:', blockAnalysis);

// Trace block range
const rangeAnalysis = await analyzer.traceBlockRange(12345678, 12345688);
console.log('Range summary:', rangeAnalysis);

// Find patterns
const highGasBlocks = await analyzer.findSpecificPattern(
12345678,
12345688,
{ type: 'high_gas', threshold: 1000000 }
);
console.log('High gas blocks:', highGasBlocks);

// Compare blocks
const comparison = await analyzer.compareBlocks([
12345678, 12345688, 12345698
]);
console.log('Block comparison:', comparison);

// Monitor new blocks
const monitor = new BlockMonitor(provider);
monitor.startMonitoring((data) => {
console.log(`Block ${data.blockNumber}:`);
console.log(' Transactions:', data.analysis.transactionCount);
console.log(' Anomalies:', data.anomalies);
});

Common Use Cases​

1. Historical Block Analysis​

// Analyze historical blocks for research
async function analyzeHistoricalBlocks(blockNumbers) {
const analyzer = new BlockRangeAnalyzer(provider);
const results = {
blocks: [],
trends: {},
outliers: []
};

for (const blockNum of blockNumbers) {
const analysis = await analyzer.traceBlockByNumber(blockNum);
results.blocks.push(analysis);

// Identify outliers
if (analysis.transactionCount > 400 ||
analysis.failedTxs > 10 ||
analysis.totalGasUsed > 15000000) {
results.outliers.push({
block: blockNum,
reason: 'Unusual activity',
metrics: {
transactions: analysis.transactionCount,
failures: analysis.failedTxs,
gas: analysis.totalGasUsed
}
});
}
}

// Calculate trends
results.trends = {
avgTransactions: results.blocks.reduce(
(sum, b) => sum + b.transactionCount, 0
) / results.blocks.length,
avgGas: results.blocks.reduce(
(sum, b) => sum + b.totalGasUsed, 0
) / results.blocks.length,
failureRate: results.blocks.reduce(
(sum, b) => sum + (b.failedTxs / b.transactionCount), 0
) / results.blocks.length * 100
};

return results;
}

2. Network Health Monitoring​

// Monitor network health over time
async function monitorNetworkHealth(duration = 100) {
const monitor = new BlockMonitor(provider);
const healthMetrics = {
blocks: [],
alerts: [],
summary: {}
};

const latestBlock = await provider.getBlockNumber();
const startBlock = latestBlock - duration;

for (let block = startBlock; block <= latestBlock; block++) {
const analysis = await analyzer.traceBlockByNumber(block);

const health = {
block: block,
score: calculateHealthScore(analysis),
issues: []
};

// Check for issues
if (analysis.failedTxs > 5) {
health.issues.push('High failure rate');
}

if (analysis.totalGasUsed > 12000000) {
health.issues.push('High gas usage');
}

if (analysis.errorTypes['out of gas']) {
health.issues.push('Out of gas errors detected');
}

healthMetrics.blocks.push(health);

if (health.issues.length > 0) {
healthMetrics.alerts.push({
block: block,
issues: health.issues,
severity: health.score < 50 ? 'critical' : 'warning'
});
}
}

// Generate summary
healthMetrics.summary = {
averageHealth: healthMetrics.blocks.reduce(
(sum, b) => sum + b.score, 0
) / healthMetrics.blocks.length,
criticalBlocks: healthMetrics.alerts.filter(
a => a.severity === 'critical'
).length,
warningBlocks: healthMetrics.alerts.filter(
a => a.severity === 'warning'
).length
};

return healthMetrics;
}

function calculateHealthScore(analysis) {
let score = 100;

// Deduct for failures
score -= (analysis.failedTxs / analysis.transactionCount) * 50;

// Deduct for high gas
if (analysis.totalGasUsed > 15000000) score -= 20;
else if (analysis.totalGasUsed > 12000000) score -= 10;

// Deduct for errors
Object.keys(analysis.errorTypes).forEach(error => {
score -= 5;
});

return Math.max(0, score);
}

3. Smart Contract Activity Timeline​

// Build activity timeline for specific contract
async function buildContractTimeline(contractAddress, startBlock, endBlock) {
const timeline = {
contract: contractAddress,
blockRange: { start: startBlock, end: endBlock },
activity: [],
statistics: {
totalCalls: 0,
totalGasUsed: 0,
uniqueCallers: new Set(),
peakActivity: null
}
};

for (let block = startBlock; block <= endBlock; block++) {
const analysis = await analyzer.traceBlockByNumber(block);

const contractActivity = analysis.contractCalls.filter(
call => call.to === contractAddress
);

if (contractActivity.length > 0) {
const blockActivity = {
block: block,
calls: contractActivity.length,
gasUsed: contractActivity.reduce(
(sum, call) => sum + call.gasUsed, 0
),
callers: []
};

// Get unique callers for this block
// Would need to fetch full traces for caller info

timeline.activity.push(blockActivity);
timeline.statistics.totalCalls += contractActivity.length;
timeline.statistics.totalGasUsed += blockActivity.gasUsed;

if (!timeline.statistics.peakActivity ||
blockActivity.calls > timeline.statistics.peakActivity.calls) {
timeline.statistics.peakActivity = blockActivity;
}
}
}

return timeline;
}

Error Handling​

Error TypeDescriptionSolution
Invalid block numberBlock doesn't existVerify block number
TimeoutLarge block trace timeoutUse onlyTopCall option
Rate limitToo many requestsImplement rate limiting
async function safeTraceByNumber(blockNumber, retries = 3) {
for (let attempt = 0; attempt < retries; attempt++) {
try {
const result = await provider.send('debug_traceBlockByNumber', [
toBeHex(blockNumber),
{
tracer: 'callTracer',
tracerConfig: {
onlyTopCall: attempt > 0, // Simplify on retry
timeout: `${10 + attempt * 10}s` // Increase timeout
}
}
]);

return result;

} catch (error) {
if (error.message.includes('not found')) {
throw new Error(`Block ${blockNumber} not found`);
}

if (attempt === retries - 1) {
throw error;
}

// Exponential backoff
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, attempt) * 1000)
);
}
}
}

Need help? Contact our support team for debug method access or check the Unichain documentation.