debug_traceBlock
Returns execution traces for all transactions in a block. Useful for analyzing block-wide patterns, MEV activities, and transaction interactions.
Premium Method
This method requires enhanced API access. Please contact support for access to debug methods.
When to Use This Method​
debug_traceBlock
is essential for:
- Block Analysis - Understand all transactions in a block
- MEV Detection - Identify sandwich attacks and arbitrage
- Gas Analysis - Compare gas usage across transactions
- State Analysis - Track state changes throughout a block
Parameters​
-
Block Identifier -
DATA
- RLP-encoded block or block hash
-
Tracer Configuration -
Object
tracer
- Type of tracer to usetracerConfig
- Configuration optionsonlyTopCall
- Only trace top-level callstimeout
- Execution timeout per transaction
{
"jsonrpc": "2.0",
"method": "debug_traceBlock",
"params": [
"0xf90217...", // RLP-encoded block
{
"tracer": "callTracer",
"tracerConfig": {
"onlyTopCall": false
}
}
],
"id": 1
}
Returns​
Array of transaction traces, each containing:
- Transaction trace data (same as debug_traceTransaction)
- Transaction index in block
- Transaction hash
Implementation Examples​
- JavaScript
- Python
import { JsonRpcProvider } from 'ethers';
const provider = new JsonRpcProvider('https://api-unichain-mainnet.n.dwellir.com/YOUR_API_KEY');
// Block-wide transaction analyzer
class BlockAnalyzer {
constructor(provider) {
this.provider = provider;
}
async traceBlock(blockNumber, options = {}) {
try {
// Get block data
const block = await this.provider.getBlock(blockNumber);
if (!block) {
throw new Error('Block not found');
}
// Trace all transactions
const traces = await this.provider.send('debug_traceBlock', [
block.hash,
{
tracer: options.tracer || 'callTracer',
tracerConfig: options.tracerConfig || {
onlyTopCall: false,
timeout: '10s'
}
}
]);
return this.analyzeBlockTraces(traces, block);
} catch (error) {
throw new Error(`Failed to trace block: ${error.message}`);
}
}
analyzeBlockTraces(traces, block) {
const analysis = {
blockNumber: block.number,
blockHash: block.hash,
timestamp: block.timestamp,
transactionCount: traces.length,
totalGasUsed: 0,
transactions: [],
patterns: {
mev: [],
highGas: [],
failed: [],
contracts: new Set()
}
};
// Analyze each transaction
traces.forEach((trace, index) => {
const tx = block.transactions[index];
const gasUsed = parseInt(trace.result?.gasUsed || '0x0', 16);
analysis.totalGasUsed += gasUsed;
const txAnalysis = {
index: index,
hash: tx,
from: trace.result?.from,
to: trace.result?.to,
gasUsed: gasUsed,
success: !trace.result?.error,
callCount: this.countCalls(trace.result),
depth: this.getMaxDepth(trace.result)
};
analysis.transactions.push(txAnalysis);
// Detect patterns
if (gasUsed > 1000000) {
analysis.patterns.highGas.push(index);
}
if (trace.result?.error) {
analysis.patterns.failed.push(index);
}
if (trace.result?.to) {
analysis.patterns.contracts.add(trace.result.to);
}
});
// Detect MEV patterns
analysis.patterns.mev = this.detectMEVPatterns(traces);
analysis.patterns.contracts = Array.from(analysis.patterns.contracts);
return analysis;
}
countCalls(trace) {
if (!trace || !trace.calls) return 1;
let count = 1;
for (const call of trace.calls) {
count += this.countCalls(call);
}
return count;
}
getMaxDepth(trace, currentDepth = 0) {
if (!trace || !trace.calls || trace.calls.length === 0) {
return currentDepth;
}
let maxDepth = currentDepth;
for (const call of trace.calls) {
const depth = this.getMaxDepth(call, currentDepth + 1);
maxDepth = Math.max(maxDepth, depth);
}
return maxDepth;
}
detectMEVPatterns(traces) {
const patterns = [];
// Look for sandwich attacks
for (let i = 1; i < traces.length - 1; i++) {
const prev = traces[i - 1].result;
const curr = traces[i].result;
const next = traces[i + 1].result;
// Simple sandwich detection
if (prev && curr && next) {
if (prev.from === next.from &&
prev.to === next.to &&
prev.from !== curr.from) {
patterns.push({
type: 'potential_sandwich',
transactions: [i - 1, i, i + 1],
attacker: prev.from,
victim: curr.from,
target: prev.to
});
}
}
}
// Look for arbitrage
const addressActivity = {};
traces.forEach((trace, index) => {
const from = trace.result?.from;
if (from) {
if (!addressActivity[from]) {
addressActivity[from] = [];
}
addressActivity[from].push(index);
}
});
// Check for multiple transactions from same address
for (const [address, indices] of Object.entries(addressActivity)) {
if (indices.length >= 2) {
// Check if transactions are sequential or close
const sequential = indices.every((val, i, arr) =>
i === 0 || val === arr[i - 1] + 1
);
if (sequential || indices[indices.length - 1] - indices[0] <= 5) {
patterns.push({
type: 'potential_arbitrage',
transactions: indices,
address: address
});
}
}
}
return patterns;
}
async compareTransactionGas(blockNumber) {
const traces = await this.traceBlock(blockNumber);
// Sort by gas usage
const sorted = traces.transactions
.sort((a, b) => b.gasUsed - a.gasUsed)
.map(tx => ({
hash: tx.hash,
gasUsed: tx.gasUsed,
percentage: ((tx.gasUsed / traces.totalGasUsed) * 100).toFixed(2),
efficiency: tx.success ? 'success' : 'failed'
}));
return {
blockNumber: traces.blockNumber,
totalGas: traces.totalGasUsed,
averageGas: Math.floor(traces.totalGasUsed / traces.transactionCount),
mostExpensive: sorted.slice(0, 5),
leastExpensive: sorted.slice(-5).reverse(),
gasDistribution: this.calculateGasDistribution(traces.transactions)
};
}
calculateGasDistribution(transactions) {
const ranges = {
'under_50k': 0,
'50k_100k': 0,
'100k_500k': 0,
'500k_1m': 0,
'over_1m': 0
};
transactions.forEach(tx => {
const gas = tx.gasUsed;
if (gas < 50000) ranges['under_50k']++;
else if (gas < 100000) ranges['50k_100k']++;
else if (gas < 500000) ranges['100k_500k']++;
else if (gas < 1000000) ranges['500k_1m']++;
else ranges['over_1m']++;
});
return ranges;
}
async analyzeBlockInteractions(blockNumber) {
const traces = await this.traceBlock(blockNumber);
// Build interaction graph
const interactions = new Map();
traces.transactions.forEach(tx => {
if (!tx.from || !tx.to) return;
const key = `${tx.from}->${tx.to}`;
if (!interactions.has(key)) {
interactions.set(key, {
from: tx.from,
to: tx.to,
count: 0,
transactions: [],
totalGas: 0
});
}
const interaction = interactions.get(key);
interaction.count++;
interaction.transactions.push(tx.index);
interaction.totalGas += tx.gasUsed;
});
// Find most active contracts
const contractActivity = {};
traces.transactions.forEach(tx => {
if (tx.to) {
if (!contractActivity[tx.to]) {
contractActivity[tx.to] = {
address: tx.to,
interactions: 0,
gasUsed: 0,
uniqueCallers: new Set()
};
}
contractActivity[tx.to].interactions++;
contractActivity[tx.to].gasUsed += tx.gasUsed;
if (tx.from) {
contractActivity[tx.to].uniqueCallers.add(tx.from);
}
}
});
// Convert sets to counts
Object.values(contractActivity).forEach(activity => {
activity.uniqueCallers = activity.uniqueCallers.size;
});
return {
blockNumber: traces.blockNumber,
uniqueInteractions: interactions.size,
interactions: Array.from(interactions.values())
.sort((a, b) => b.count - a.count)
.slice(0, 10),
mostActiveContracts: Object.values(contractActivity)
.sort((a, b) => b.interactions - a.interactions)
.slice(0, 10)
};
}
}
// MEV detector
class MEVDetector {
constructor(provider) {
this.provider = provider;
this.analyzer = new BlockAnalyzer(provider);
}
async detectMEV(blockNumber) {
const block = await this.provider.getBlock(blockNumber, true);
const traces = await this.analyzer.traceBlock(blockNumber);
const mevOpportunities = {
sandwiches: [],
arbitrage: [],
liquidations: [],
suspiciousPatterns: []
};
// Analyze transaction ordering
for (let i = 0; i < traces.transactions.length; i++) {
const tx = traces.transactions[i];
const receipt = await this.provider.getTransactionReceipt(tx.hash);
// Check for DEX interactions
const isDEX = this.isDEXInteraction(receipt.logs);
if (isDEX && i > 0 && i < traces.transactions.length - 1) {
// Check for sandwich pattern
const prevTx = traces.transactions[i - 1];
const nextTx = traces.transactions[i + 1];
if (this.isSandwichPattern(prevTx, tx, nextTx)) {
mevOpportunities.sandwiches.push({
victim: tx.hash,
frontrun: prevTx.hash,
backrun: nextTx.hash,
victimGas: tx.gasUsed,
attackerProfit: this.estimateProfit(prevTx, nextTx)
});
}
}
// Check for arbitrage
if (this.isArbitragePattern(tx, traces.transactions.slice(i, i + 5))) {
mevOpportunities.arbitrage.push({
transactions: traces.transactions.slice(i, i + 5).map(t => t.hash),
estimatedProfit: 'Unknown'
});
}
}
return mevOpportunities;
}
isDEXInteraction(logs) {
// Check for common DEX event signatures
const dexSignatures = [
'0xd78ad95f', // Swap
'0x1c411e9a', // Sync
'0xdccd412f' // Mint
];
return logs.some(log =>
dexSignatures.some(sig =>
log.topics[0]?.startsWith(sig)
)
);
}
isSandwichPattern(prevTx, victimTx, nextTx) {
return prevTx.from === nextTx.from &&
prevTx.to === victimTx.to &&
nextTx.to === victimTx.to &&
prevTx.from !== victimTx.from;
}
isArbitragePattern(tx, nextTransactions) {
// Simple check: same sender, multiple DEX interactions
const sender = tx.from;
const dexInteractions = nextTransactions.filter(t =>
t.from === sender && t.to !== sender
);
return dexInteractions.length >= 2;
}
estimateProfit(frontTx, backTx) {
// Simplified profit estimation
return 'Requires detailed token analysis';
}
}
// Usage examples
const analyzer = new BlockAnalyzer(provider);
// Analyze entire block
const blockAnalysis = await analyzer.traceBlock(12345678);
console.log('Block transactions:', blockAnalysis.transactionCount);
console.log('Total gas used:', blockAnalysis.totalGasUsed);
console.log('MEV patterns detected:', blockAnalysis.patterns.mev);
// Compare gas usage
const gasComparison = await analyzer.compareTransactionGas(12345678);
console.log('Most expensive tx:', gasComparison.mostExpensive[0]);
// Analyze interactions
const interactions = await analyzer.analyzeBlockInteractions(12345678);
console.log('Most active contract:', interactions.mostActiveContracts[0]);
// Detect MEV
const mevDetector = new MEVDetector(provider);
const mevResults = await mevDetector.detectMEV(12345678);
console.log('Sandwich attacks found:', mevResults.sandwiches.length);
from web3 import Web3
import json
from typing import Dict, List, Any, Optional
from collections import defaultdict
w3 = Web3(Web3.HTTPProvider('https://api-unichain-mainnet.n.dwellir.com/YOUR_API_KEY'))
class BlockTracer:
"""Trace and analyze all transactions in a block"""
def __init__(self, w3_instance):
self.w3 = w3_instance
def trace_block(
self,
block_identifier: int,
tracer: str = 'callTracer',
config: Optional[Dict] = None
) -> Dict[str, Any]:
"""Trace all transactions in a block"""
# Get block
block = self.w3.eth.get_block(block_identifier, full_transactions=True)
if not block:
return {'error': 'Block not found'}
tracer_config = {
'tracer': tracer,
'tracerConfig': config or {'onlyTopCall': False}
}
try:
# Trace block
traces = self.w3.provider.make_request(
'debug_traceBlock',
[block['hash'].hex(), tracer_config]
)
return self._analyze_block_traces(
traces['result'],
block
)
except Exception as e:
return {'error': str(e)}
def _analyze_block_traces(
self,
traces: List[Dict],
block: Dict
) -> Dict[str, Any]:
"""Analyze block traces"""
analysis = {
'block_number': block['number'],
'block_hash': block['hash'].hex(),
'timestamp': block['timestamp'],
'transaction_count': len(traces),
'total_gas_used': 0,
'transactions': [],
'patterns': {
'mev': [],
'high_gas': [],
'failed': [],
'contracts': set()
},
'statistics': {}
}
# Analyze each transaction
for i, trace in enumerate(traces):
tx = block['transactions'][i]
result = trace.get('result', {})
gas_used = int(result.get('gasUsed', '0x0'), 16)
analysis['total_gas_used'] += gas_used
tx_analysis = {
'index': i,
'hash': tx['hash'].hex(),
'from': result.get('from'),
'to': result.get('to'),
'value': result.get('value', '0x0'),
'gas_used': gas_used,
'success': 'error' not in result,
'error': result.get('error'),
'call_count': self._count_calls(result),
'max_depth': self._get_max_depth(result)
}
analysis['transactions'].append(tx_analysis)
# Detect patterns
if gas_used > 1000000:
analysis['patterns']['high_gas'].append(i)
if not tx_analysis['success']:
analysis['patterns']['failed'].append(i)
if result.get('to'):
analysis['patterns']['contracts'].add(result['to'])
# Detect MEV patterns
analysis['patterns']['mev'] = self._detect_mev_patterns(
analysis['transactions']
)
# Convert set to list
analysis['patterns']['contracts'] = list(
analysis['patterns']['contracts']
)
# Calculate statistics
analysis['statistics'] = self._calculate_statistics(
analysis['transactions']
)
return analysis
def _count_calls(self, trace: Dict) -> int:
"""Count total calls in trace"""
if not trace or 'calls' not in trace:
return 1
count = 1
for call in trace['calls']:
count += self._count_calls(call)
return count
def _get_max_depth(self, trace: Dict, depth: int = 0) -> int:
"""Get maximum call depth"""
if not trace or 'calls' not in trace:
return depth
max_depth = depth
for call in trace['calls']:
call_depth = self._get_max_depth(call, depth + 1)
max_depth = max(max_depth, call_depth)
return max_depth
def _detect_mev_patterns(
self,
transactions: List[Dict]
) -> List[Dict[str, Any]]:
"""Detect potential MEV patterns"""
patterns = []
# Look for sandwich attacks
for i in range(1, len(transactions) - 1):
prev = transactions[i - 1]
curr = transactions[i]
next = transactions[i + 1]
# Simple sandwich detection
if (prev['from'] == next['from'] and
prev['to'] == next['to'] and
prev['from'] != curr['from']):
patterns.append({
'type': 'potential_sandwich',
'transactions': [
prev['hash'],
curr['hash'],
next['hash']
],
'attacker': prev['from'],
'victim': curr['from'],
'target': prev['to']
})
# Look for arbitrage
address_activity = defaultdict(list)
for tx in transactions:
if tx['from']:
address_activity[tx['from']].append(tx['index'])
for address, indices in address_activity.items():
if len(indices) >= 2:
# Check if transactions are close together
if indices[-1] - indices[0] <= 5:
patterns.append({
'type': 'potential_arbitrage',
'address': address,
'transaction_indices': indices
})
return patterns
def _calculate_statistics(
self,
transactions: List[Dict]
) -> Dict[str, Any]:
"""Calculate block statistics"""
if not transactions:
return {}
gas_values = [tx['gas_used'] for tx in transactions]
return {
'average_gas': sum(gas_values) / len(gas_values),
'median_gas': sorted(gas_values)[len(gas_values) // 2],
'min_gas': min(gas_values),
'max_gas': max(gas_values),
'failed_rate': (
len([tx for tx in transactions if not tx['success']]) /
len(transactions) * 100
),
'unique_senders': len(set(
tx['from'] for tx in transactions if tx['from']
)),
'unique_receivers': len(set(
tx['to'] for tx in transactions if tx['to']
))
}
def compare_gas_usage(
self,
block_number: int
) -> Dict[str, Any]:
"""Compare gas usage across transactions"""
analysis = self.trace_block(block_number)
if 'error' in analysis:
return analysis
# Sort by gas usage
sorted_txs = sorted(
analysis['transactions'],
key=lambda x: x['gas_used'],
reverse=True
)
# Calculate gas distribution
distribution = {
'under_50k': 0,
'50k_100k': 0,
'100k_500k': 0,
'500k_1m': 0,
'over_1m': 0
}
for tx in analysis['transactions']:
gas = tx['gas_used']
if gas < 50000:
distribution['under_50k'] += 1
elif gas < 100000:
distribution['50k_100k'] += 1
elif gas < 500000:
distribution['100k_500k'] += 1
elif gas < 1000000:
distribution['500k_1m'] += 1
else:
distribution['over_1m'] += 1
return {
'block_number': analysis['block_number'],
'total_gas': analysis['total_gas_used'],
'average_gas': analysis['statistics']['average_gas'],
'most_expensive': [
{
'hash': tx['hash'],
'gas': tx['gas_used'],
'percentage': round(
tx['gas_used'] / analysis['total_gas_used'] * 100, 2
)
}
for tx in sorted_txs[:5]
],
'distribution': distribution
}
def analyze_block_interactions(
self,
block_number: int
) -> Dict[str, Any]:
"""Analyze contract interactions in block"""
analysis = self.trace_block(block_number)
if 'error' in analysis:
return analysis
# Build interaction graph
interactions = defaultdict(lambda: {
'count': 0,
'transactions': [],
'total_gas': 0
})
# Track contract activity
contract_activity = defaultdict(lambda: {
'interactions': 0,
'gas_used': 0,
'unique_callers': set()
})
for tx in analysis['transactions']:
if tx['from'] and tx['to']:
# Track interaction
key = f"{tx['from']}->{tx['to']}"
interactions[key]['count'] += 1
interactions[key]['transactions'].append(tx['hash'])
interactions[key]['total_gas'] += tx['gas_used']
# Track contract activity
contract_activity[tx['to']]['interactions'] += 1
contract_activity[tx['to']]['gas_used'] += tx['gas_used']
contract_activity[tx['to']]['unique_callers'].add(tx['from'])
# Process results
top_interactions = sorted(
[
{
'pair': key,
**value
}
for key, value in interactions.items()
],
key=lambda x: x['count'],
reverse=True
)[:10]
top_contracts = sorted(
[
{
'address': addr,
'interactions': data['interactions'],
'gas_used': data['gas_used'],
'unique_callers': len(data['unique_callers'])
}
for addr, data in contract_activity.items()
],
key=lambda x: x['interactions'],
reverse=True
)[:10]
return {
'block_number': analysis['block_number'],
'unique_interactions': len(interactions),
'top_interactions': top_interactions,
'most_active_contracts': top_contracts
}
class MEVAnalyzer:
"""Analyze blocks for MEV activity"""
def __init__(self, w3_instance):
self.w3 = w3_instance
self.tracer = BlockTracer(w3_instance)
def detect_sandwich_attacks(
self,
block_number: int
) -> List[Dict[str, Any]]:
"""Detect potential sandwich attacks"""
analysis = self.tracer.trace_block(block_number)
if 'error' in analysis:
return []
sandwiches = []
# Look for sandwich patterns
for pattern in analysis['patterns']['mev']:
if pattern['type'] == 'potential_sandwich':
# Get more details about the sandwich
sandwich = {
'block': block_number,
'attacker': pattern['attacker'],
'victim': pattern['victim'],
'target_contract': pattern['target'],
'transactions': pattern['transactions']
}
# Try to estimate profit (simplified)
sandwich['estimated_profit'] = 'Requires token analysis'
sandwiches.append(sandwich)
return sandwiches
def analyze_arbitrage(
self,
block_number: int
) -> List[Dict[str, Any]]:
"""Detect arbitrage opportunities"""
analysis = self.tracer.trace_block(block_number)
if 'error' in analysis:
return []
arbitrage_ops = []
for pattern in analysis['patterns']['mev']:
if pattern['type'] == 'potential_arbitrage':
arb = {
'block': block_number,
'arbitrageur': pattern['address'],
'transaction_count': len(pattern['transaction_indices']),
'transactions': [
analysis['transactions'][i]['hash']
for i in pattern['transaction_indices']
]
}
arbitrage_ops.append(arb)
return arbitrage_ops
# Usage examples
tracer = BlockTracer(w3)
# Trace entire block
block_analysis = tracer.trace_block(12345678)
print(f"Block {block_analysis['block_number']}:")
print(f" Transactions: {block_analysis['transaction_count']}")
print(f" Total gas: {block_analysis['total_gas_used']:,}")
print(f" Failed txs: {len(block_analysis['patterns']['failed'])}")
# Compare gas usage
gas_comparison = tracer.compare_gas_usage(12345678)
print("\nGas usage distribution:")
for range_name, count in gas_comparison['distribution'].items():
print(f" {range_name}: {count} transactions")
# Analyze interactions
interactions = tracer.analyze_block_interactions(12345678)
print("\nTop contract interactions:")
for contract in interactions['most_active_contracts'][:3]:
print(f" {contract['address'][:10]}...")
print(f" Interactions: {contract['interactions']}")
print(f" Unique callers: {contract['unique_callers']}")
# MEV detection
mev_analyzer = MEVAnalyzer(w3)
sandwiches = mev_analyzer.detect_sandwich_attacks(12345678)
print(f"\nPotential sandwich attacks: {len(sandwiches)}")
arbitrage = mev_analyzer.analyze_arbitrage(12345678)
print(f"Potential arbitrage: {len(arbitrage)}")
Common Use Cases​
1. Block-Wide Gas Analysis​
// Analyze gas patterns across a block
async function analyzeBlockGasPatterns(blockNumber) {
const analyzer = new BlockAnalyzer(provider);
const analysis = await analyzer.compareTransactionGas(blockNumber);
// Identify optimization opportunities
const recommendations = [];
// Check for inefficient gas usage
if (analysis.gasDistribution.over_1m > 0) {
recommendations.push({
issue: 'Very high gas transactions detected',
count: analysis.gasDistribution.over_1m,
suggestion: 'Review complex operations for optimization'
});
}
// Check average vs median
const avgGas = analysis.averageGas;
const medianGas = Math.floor(
analysis.totalGas / analysis.mostExpensive.length
);
if (avgGas > medianGas * 2) {
recommendations.push({
issue: 'Gas usage highly skewed',
suggestion: 'A few transactions consuming disproportionate gas'
});
}
return {
blockNumber: blockNumber,
insights: recommendations,
topGasConsumers: analysis.mostExpensive
};
}
2. MEV Impact Assessment​
// Assess MEV impact on regular users
async function assessMEVImpact(blockNumber) {
const mevDetector = new MEVDetector(provider);
const mevResults = await mevDetector.detectMEV(blockNumber);
const impact = {
sandwichVictims: mevResults.sandwiches.length,
estimatedLosses: 0,
affectedUsers: new Set(),
mevTransactions: [],
regularTransactions: []
};
// Calculate impact
for (const sandwich of mevResults.sandwiches) {
impact.affectedUsers.add(
sandwich.victim.substring(0, 42)
);
// Add to MEV transactions
impact.mevTransactions.push(sandwich.frontrun);
impact.mevTransactions.push(sandwich.backrun);
}
// Get all transactions in block
const block = await provider.getBlock(blockNumber, true);
// Separate MEV from regular transactions
for (const tx of block.transactions) {
if (!impact.mevTransactions.includes(tx.hash)) {
impact.regularTransactions.push(tx.hash);
}
}
return {
blockNumber: blockNumber,
mevRatio: (
impact.mevTransactions.length /
block.transactions.length * 100
).toFixed(2) + '%',
victimsCount: impact.affectedUsers.size,
regularUsersAffected: impact.sandwichVictims
};
}
3. State Change Timeline​
// Build timeline of state changes in block
async function buildStateTimeline(blockNumber) {
const analyzer = new BlockAnalyzer(provider);
const traces = await analyzer.traceBlock(blockNumber);
const timeline = [];
for (const tx of traces.transactions) {
timeline.push({
index: tx.index,
timestamp: traces.timestamp + tx.index, // Approximate
from: tx.from,
to: tx.to,
type: tx.success ? 'success' : 'failed',
gasUsed: tx.gasUsed,
impact: tx.callCount > 5 ? 'high' :
tx.callCount > 2 ? 'medium' : 'low'
});
}
return {
blockNumber: blockNumber,
blockTime: traces.timestamp,
events: timeline,
summary: {
totalTransactions: timeline.length,
successRate: (
timeline.filter(e => e.type === 'success').length /
timeline.length * 100
).toFixed(2) + '%'
}
};
}
Error Handling​
Error Type | Description | Solution |
---|---|---|
Block not found | Invalid block number | Verify block exists |
Timeout | Block too large to trace | Reduce timeout or use pagination |
Memory limit | Too many transactions | Process in batches |
async function safeBlockTrace(blockNumber, batchSize = 10) {
try {
// Try full block trace
return await provider.send('debug_traceBlock', [
blockNumber,
{
tracer: 'callTracer',
tracerConfig: {
onlyTopCall: false,
timeout: '30s'
}
}
]);
} catch (error) {
if (error.message.includes('timeout')) {
// Fall back to batched processing
const block = await provider.getBlock(blockNumber);
const traces = [];
for (let i = 0; i < block.transactions.length; i += batchSize) {
const batch = block.transactions.slice(i, i + batchSize);
for (const txHash of batch) {
const trace = await provider.send('debug_traceTransaction', [
txHash,
{
tracer: 'callTracer',
tracerConfig: { onlyTopCall: true }
}
]);
traces.push(trace);
}
}
return traces;
}
throw error;
}
}
Need help? Contact our support team for debug method access or check the Unichain documentation.