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​
-
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-base-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-base-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? Contact our support team for debug method access or check the Base documentation.