Docs
Supported ChainsTempoJSON-RPC APIDebug Methods

debug_traceBlockByNumber - Tempo RPC Method

Trace all transactions in a block by number on Tempo. Analyze gas consumption, debug state transitions, and inspect historical block execution.

Traces all transactions in a block on Tempo identified by its block number or tag. This is the most convenient block-tracing method — pass a block number or "latest" to get full execution traces of every transaction in that block.

Why Tempo? Build on a payments-first EVM chain with deterministic settlement and stablecoin-native fees with no native gas token, fees denominated in supported USD stablecoins, and full EVM RPC compatibility.

Archive Node Required

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Tempo — ensure your plan includes debug namespace support.

When to Use This Method

debug_traceBlockByNumber is essential for payment application developers, wallet teams, stablecoin issuers, and treasury automation builders:

  • Historical Block Analysis — Trace transactions in any past block by number, enabling time-series analysis of Tempo execution patterns
  • Gas Consumption Patterns — Profile gas usage across all transactions in a block to understand network congestion and gas cost trends for merchant settlement, treasury operations, payout automation, and stablecoin-native financial applications
  • Debugging State Transitions — Inspect how every transaction in a block changed the global state, useful for verifying protocol upgrades and hard fork behavior
  • Automated Block Scanning — Iterate through block ranges by number to build analytics pipelines, detect anomalies, and index execution traces

Request Parameters

Request
blockNumberQUANTITY|TAG

Block number as hex string, or tag: "earliest", "latest", "pending"

tracerConfigObject

Tracer configuration object (see options below)

Response Body

Response
resultArray<Object>

Array of trace objects, one per transaction

result[].resultObject

Trace output (structure depends on tracer used)

result[].txHashDATA

Transaction hash for this trace

typestring

Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)

fromDATA

Sender address

toDATA

Recipient address

valueQUANTITY

Value transferred in wei

gasQUANTITY

Gas provided

gasUsedQUANTITY

Gas consumed

inputDATA

Call data

outputDATA

Return data

errorstring

Error message if the call reverted

revertReasonstring

Decoded revert reason (if available)

callsArray

Sub-calls made during execution

gasQUANTITY

Gas provided

returnValueDATA

Return value of the call

structLogsArray

Array of opcode execution steps

structLogs[].pcQUANTITY

Program counter

structLogs[].opstring

Opcode name

structLogs[].gasQUANTITY

Remaining gas

structLogs[].gasCostQUANTITY

Gas cost of this opcode

structLogs[].depthQUANTITY

Call depth

structLogs[].stackArray<DATA>

Stack contents (if not disabled)

structLogs[].storageObject

Storage changes (if not disabled)

Error Responses

Errors
Error Response-32000

Code Examples

Bash
# Trace latest block with call tracer
curl -X POST https://api-tempo-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["latest", {"tracer": "callTracer"}],
    "id": 1
  }'

# Trace specific block with prestate tracer
curl -X POST https://api-tempo-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["0xF4240", {"tracer": "prestateTracer"}],
    "id": 1
  }'

# Trace with default opcode tracer (minimal output)
curl -X POST https://api-tempo-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["latest", {"disableStorage": true, "disableStack": true}],
    "id": 1
  }'

Common Use Cases

1. Historical Gas Consumption Analysis

Profile gas usage across a range of blocks on Tempo:

JavaScript
async function analyzeGasOverRange(provider, startBlock, endBlock) {
  const blockStats = [];

  for (let block = startBlock; block <= endBlock; block++) {
    const blockHex = '0x' + block.toString(16);
    const traces = await provider.send('debug_traceBlockByNumber', [
      blockHex,
      { tracer: 'callTracer' }
    ]);

    let totalGas = 0;
    let maxGas = 0;
    let revertCount = 0;

    for (const trace of traces) {
      const gasUsed = parseInt(trace.result.gasUsed, 16);
      totalGas += gasUsed;
      maxGas = Math.max(maxGas, gasUsed);
      if (trace.result.error) revertCount++;
    }

    blockStats.push({
      block,
      txCount: traces.length,
      totalGas,
      avgGas: traces.length > 0 ? Math.round(totalGas / traces.length) : 0,
      maxGas,
      revertCount
    });

    console.log(
      `Block ${block}: ${traces.length} txs, ${totalGas} total gas, ${revertCount} reverts`
    );
  }

  return blockStats;
}

2. Automated Block Scanner for Contract Interactions

Scan blocks for interactions with a specific contract on Tempo:

Python
import requests

def scan_blocks_for_contract(start_block, end_block, target_contract):
    target = target_contract.lower()
    interactions = []

    for block_num in range(start_block, end_block + 1):
        block_hex = hex(block_num)
        response = requests.post('https://api-tempo-mainnet.n.dwellir.com/YOUR_API_KEY', json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByNumber',
            'params': [block_hex, {'tracer': 'callTracer'}],
            'id': 1
        })
        traces = response.json()['result']

        for trace in traces:
            calls = flatten_calls(trace['result'])
            for call in calls:
                if call.get('to', '').lower() == target:
                    interactions.append({
                        'block': block_num,
                        'tx_hash': trace['txHash'],
                        'call_type': call['type'],
                        'from': call['from'],
                        'input': call['input'][:10],  # function selector
                        'gas_used': int(call.get('gasUsed', '0x0'), 16)
                    })

    print(f'Found {len(interactions)} interactions with {target_contract}')
    for i in interactions:
        print(f'  Block {i["block"]}: {i["tx_hash"]} [{i["call_type"]}] selector={i["input"]}')

    return interactions

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls

3. Debugging State Transitions After Protocol Upgrades

Compare block execution before and after a hard fork or protocol upgrade:

JavaScript
async function compareBlockExecution(provider, forkBlock) {
  const preFork = '0x' + (forkBlock - 1).toString(16);
  const postFork = '0x' + forkBlock.toString(16);

  const [preTraces, postTraces] = await Promise.all([
    provider.send('debug_traceBlockByNumber', [
      preFork,
      { tracer: 'callTracer' }
    ]),
    provider.send('debug_traceBlockByNumber', [
      postFork,
      { tracer: 'callTracer' }
    ])
  ]);

  console.log(`Pre-fork block ${forkBlock - 1}: ${preTraces.length} txs`);
  console.log(`Post-fork block ${forkBlock}: ${postTraces.length} txs`);

  // Analyze opcode-level differences for the first transaction in each
  const [preOpcodes, postOpcodes] = await Promise.all([
    provider.send('debug_traceBlockByNumber', [
      preFork,
      { disableStorage: true, enableReturnData: true }
    ]),
    provider.send('debug_traceBlockByNumber', [
      postFork,
      { disableStorage: true, enableReturnData: true }
    ])
  ]);

  // Check for new opcodes introduced after the fork
  const preOps = new Set();
  const postOps = new Set();

  for (const trace of preOpcodes) {
    for (const log of trace.result.structLogs || []) {
      preOps.add(log.op);
    }
  }

  for (const trace of postOpcodes) {
    for (const log of trace.result.structLogs || []) {
      postOps.add(log.op);
    }
  }

  const newOps = [...postOps].filter(op => !preOps.has(op));
  if (newOps.length > 0) {
    console.log('New opcodes observed after fork:', newOps);
  }
}

Error Handling

Common errors and solutions:

Error CodeDescriptionSolution
-32000Block not foundVerify the block number exists on Tempo — it may be in the future
-32000Missing trie nodeNode is not an archive node or state has been pruned — use an archive endpoint
-32601Method not foundDebug namespace is not enabled on the node
-32603Internal errorBlock may be too large to trace — increase timeout or use disableStorage
-32005Rate limit exceededReduce request frequency or upgrade your plan
-32602Invalid paramsCheck that the block number is a valid hex string or tag
JavaScript
async function safeTraceBlockByNumber(provider, blockNumber, options = {}) {
  const defaultOptions = {
    tracer: 'callTracer',
    timeout: '60s',
    ...options
  };

  try {
    return await provider.send('debug_traceBlockByNumber', [blockNumber, defaultOptions]);
  } catch (error) {
    if (error.code === -32601) {
      throw new Error('debug_traceBlockByNumber not available — ensure archive node with debug API');
    }
    if (error.code === -32000 && error.message?.includes('not found')) {
      throw new Error(`Block ${blockNumber} not found on Tempo`);
    }
    if (error.message?.includes('timeout')) {
      console.warn('Trace timed out — retrying with top-level calls only');
      return await provider.send('debug_traceBlockByNumber', [
        blockNumber,
        { tracer: 'callTracer', tracerConfig: { onlyTopCall: true } }
      ]);
    }
    throw error;
  }
}