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 is available on paid plans. Upgrade your plan here to start using it.
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
-
Block Number -
QUANTITY|TAG
- Block number as hex
- Or tags: "latest", "earliest", "pending"
-
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
- JavaScript
- Python
import { JsonRpcProvider, toBeHex } from 'ethers';
const provider = new JsonRpcProvider('https://api-ethereum-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);
});
from web3 import Web3
from typing import Dict, List, Any, Optional
import asyncio
from datetime import datetime
w3 = Web3(Web3.HTTPProvider('https://api-ethereum-mainnet.n.dwellir.com/YOUR_API_KEY'))
class BlockNumberTracer:
"""Trace blocks by number for analysis"""
def __init__(self, w3_instance):
self.w3 = w3_instance
def trace_block_by_number(
self,
block_number: int,
tracer: str = 'callTracer',
config: Optional[Dict] = None
) -> Dict[str, Any]:
"""Trace a block by its number"""
# Convert to hex if needed
if isinstance(block_number, int):
block_hex = hex(block_number)
else:
block_hex = block_number
tracer_config = {
'tracer': tracer,
'tracerConfig': config or {'onlyTopCall': False}
}
try:
traces = self.w3.provider.make_request(
'debug_traceBlockByNumber',
[block_hex, tracer_config]
)
return self._analyze_traces(traces['result'], block_number)
except Exception as e:
return {
'error': str(e),
'block_number': block_number
}
def _analyze_traces(
self,
traces: List[Dict],
block_number: int
) -> Dict[str, Any]:
"""Analyze block traces"""
analysis = {
'block_number': block_number,
'transaction_count': len(traces),
'successful_txs': 0,
'failed_txs': 0,
'total_gas_used': 0,
'contract_calls': [],
'unique_contracts': set(),
'error_types': {},
'call_types': {}
}
for i, trace in enumerate(traces):
result = trace.get('result', {})
gas_used = int(result.get('gasUsed', '0x0'), 16)
analysis['total_gas_used'] += gas_used
# Track success/failure
if result.get('error'):
analysis['failed_txs'] += 1
error = result['error']
analysis['error_types'][error] = \
analysis['error_types'].get(error, 0) + 1
else:
analysis['successful_txs'] += 1
# Track call types
call_type = result.get('type', 'UNKNOWN')
analysis['call_types'][call_type] = \
analysis['call_types'].get(call_type, 0) + 1
# Track contracts
if result.get('to'):
analysis['unique_contracts'].add(result['to'])
analysis['contract_calls'].append({
'index': i,
'to': result['to'],
'type': call_type,
'gas_used': gas_used,
'success': 'error' not in result
})
# Process nested calls
if 'calls' in result:
self._process_nested_calls(
result['calls'],
analysis
)
# Convert set to list
analysis['unique_contracts'] = list(analysis['unique_contracts'])
# Calculate metrics
if analysis['transaction_count'] > 0:
analysis['success_rate'] = (
analysis['successful_txs'] /
analysis['transaction_count'] * 100
)
analysis['average_gas'] = (
analysis['total_gas_used'] /
analysis['transaction_count']
)
else:
analysis['success_rate'] = 0
analysis['average_gas'] = 0
return analysis
def _process_nested_calls(
self,
calls: List[Dict],
analysis: Dict
):
"""Process nested calls recursively"""
for call in calls:
if call.get('to'):
analysis['unique_contracts'].add(call['to'])
if 'calls' in call:
self._process_nested_calls(call['calls'], analysis)
def trace_block_range(
self,
start_block: int,
end_block: int,
config: Optional[Dict] = None
) -> Dict[str, Any]:
"""Trace a range of blocks"""
results = []
for block_num in range(start_block, end_block + 1):
print(f"Tracing block {block_num}...")
analysis = self.trace_block_by_number(block_num, config=config)
results.append(analysis)
# Rate limiting
time.sleep(0.1)
return self._summarize_range(results)
def _summarize_range(
self,
results: List[Dict]
) -> Dict[str, Any]:
"""Summarize analysis of block range"""
successful_blocks = [r for r in results if 'error' not in r]
if not successful_blocks:
return {
'error': 'No blocks successfully traced',
'failed_blocks': len(results)
}
summary = {
'blocks_analyzed': len(results),
'successful_blocks': len(successful_blocks),
'failed_blocks': len(results) - len(successful_blocks),
'total_transactions': sum(
b['transaction_count'] for b in successful_blocks
),
'total_gas_used': sum(
b['total_gas_used'] for b in successful_blocks
),
'average_transactions_per_block': 0,
'average_gas_per_block': 0,
'block_details': []
}
if successful_blocks:
summary['average_transactions_per_block'] = (
summary['total_transactions'] / len(successful_blocks)
)
summary['average_gas_per_block'] = (
summary['total_gas_used'] / len(successful_blocks)
)
# Add block details
for block in successful_blocks:
summary['block_details'].append({
'block': block['block_number'],
'transactions': block['transaction_count'],
'gas': block['total_gas_used'],
'success_rate': block['success_rate']
})
return summary
def find_anomalous_blocks(
self,
start_block: int,
end_block: int,
threshold: Dict[str, Any]
) -> List[Dict[str, Any]]:
"""Find blocks with anomalous behavior"""
anomalies = []
for block_num in range(start_block, end_block + 1):
analysis = self.trace_block_by_number(block_num)
if 'error' in analysis:
continue
block_anomalies = []
# Check failure rate
if analysis['success_rate'] < threshold.get('min_success_rate', 90):
block_anomalies.append({
'type': 'high_failure_rate',
'value': analysis['success_rate'],
'threshold': threshold['min_success_rate']
})
# Check gas usage
if analysis['average_gas'] > threshold.get('max_avg_gas', 500000):
block_anomalies.append({
'type': 'high_gas_usage',
'value': analysis['average_gas'],
'threshold': threshold['max_avg_gas']
})
# Check transaction count
if analysis['transaction_count'] > threshold.get('max_txs', 500):
block_anomalies.append({
'type': 'high_transaction_count',
'value': analysis['transaction_count'],
'threshold': threshold['max_txs']
})
if block_anomalies:
anomalies.append({
'block': block_num,
'anomalies': block_anomalies,
'summary': analysis
})
return anomalies
class BlockPatternDetector:
"""Detect patterns across blocks"""
def __init__(self, w3_instance):
self.w3 = w3_instance
self.tracer = BlockNumberTracer(w3_instance)
def detect_activity_patterns(
self,
start_block: int,
end_block: int
) -> Dict[str, Any]:
"""Detect activity patterns across blocks"""
patterns = {
'time_range': {
'start': start_block,
'end': end_block
},
'activity_levels': [],
'peak_blocks': [],
'quiet_blocks': [],
'contract_frequency': {}
}
for block_num in range(start_block, end_block + 1):
analysis = self.tracer.trace_block_by_number(block_num)
if 'error' in analysis:
continue
# Track activity level
patterns['activity_levels'].append({
'block': block_num,
'transactions': analysis['transaction_count'],
'gas': analysis['total_gas_used']
})
# Track contract usage
for contract in analysis['unique_contracts']:
if contract not in patterns['contract_frequency']:
patterns['contract_frequency'][contract] = 0
patterns['contract_frequency'][contract] += 1
# Identify peaks and quiet periods
if patterns['activity_levels']:
sorted_by_txs = sorted(
patterns['activity_levels'],
key=lambda x: x['transactions'],
reverse=True
)
patterns['peak_blocks'] = sorted_by_txs[:5]
patterns['quiet_blocks'] = sorted_by_txs[-5:]
# Sort contracts by frequency
patterns['most_active_contracts'] = sorted(
patterns['contract_frequency'].items(),
key=lambda x: x[1],
reverse=True
)[:10]
return patterns
def compare_time_periods(
self,
period1_start: int,
period1_end: int,
period2_start: int,
period2_end: int
) -> Dict[str, Any]:
"""Compare two time periods"""
# Analyze both periods
period1 = self.tracer.trace_block_range(period1_start, period1_end)
period2 = self.tracer.trace_block_range(period2_start, period2_end)
comparison = {
'period1': {
'range': f"{period1_start}-{period1_end}",
'stats': period1
},
'period2': {
'range': f"{period2_start}-{period2_end}",
'stats': period2
},
'changes': {}
}
# Calculate changes
if period1['total_transactions'] > 0:
comparison['changes']['transaction_change'] = (
(period2['total_transactions'] - period1['total_transactions']) /
period1['total_transactions'] * 100
)
if period1['total_gas_used'] > 0:
comparison['changes']['gas_change'] = (
(period2['total_gas_used'] - period1['total_gas_used']) /
period1['total_gas_used'] * 100
)
comparison['changes']['avg_tx_change'] = (
period2['average_transactions_per_block'] -
period1['average_transactions_per_block']
)
return comparison
# Usage examples
tracer = BlockNumberTracer(w3)
# Trace single block
block_analysis = tracer.trace_block_by_number(12345678)
print(f"Block {block_analysis['block_number']}:")
print(f" Transactions: {block_analysis['transaction_count']}")
print(f" Success rate: {block_analysis['success_rate']:.2f}%")
print(f" Total gas: {block_analysis['total_gas_used']:,}")
# Trace block range
range_summary = tracer.trace_block_range(12345678, 12345688)
print(f"\nRange analysis:")
print(f" Blocks: {range_summary['blocks_analyzed']}")
print(f" Total transactions: {range_summary['total_transactions']}")
print(f" Average per block: {range_summary['average_transactions_per_block']:.2f}")
# Find anomalies
anomalies = tracer.find_anomalous_blocks(
12345678,
12345688,
{
'min_success_rate': 95,
'max_avg_gas': 300000,
'max_txs': 400
}
)
print(f"\nAnomalous blocks found: {len(anomalies)}")
# Detect patterns
detector = BlockPatternDetector(w3)
patterns = detector.detect_activity_patterns(12345678, 12345688)
print(f"\nMost active contracts:")
for contract, frequency in patterns['most_active_contracts'][:5]:
print(f" {contract[:10]}...: {frequency} blocks")
# Compare periods
comparison = detector.compare_time_periods(
12345678, 12345688, # Period 1
12345788, 12345798 # Period 2
)
print(f"\nPeriod comparison:")
print(f" Transaction change: {comparison['changes']['transaction_change']:.2f}%")
print(f" Gas change: {comparison['changes']['gas_change']:.2f}%")
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 Type | Description | Solution |
---|---|---|
Invalid block number | Block doesn't exist | Verify block number |
Timeout | Large block trace timeout | Use onlyTopCall option |
Rate limit | Too many requests | Implement 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? Upgrade your plan here to access debug methods or check the Ethereum documentation.