# debug_traceBlockByNumber - Plasma RPC Method

Traces all transactions in a block on Plasma 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 Plasma?** Build on an EVM Layer 1 for stablecoin payments with PlasmaBFT consensus and Reth execution with 1 second blocks, XPL gas, full EVM JSON-RPC, and debug plus txpool on Dwellir.

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 Plasma - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlockByNumber` is essential for payment app developers, wallet teams, and teams shipping Solidity contracts for dollar transfers:

- **Historical Block Analysis** - Trace transactions in any past block by number, enabling time-series analysis of Plasma execution patterns
- **Gas Consumption Patterns** - Profile gas usage across all transactions in a block to understand network congestion and gas cost trends for stablecoin transfers, payment apps, and wallets that need 1 second confirmation
- **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

## Best Practices

- Requires archive node access; not available on standard full nodes
- Use the callTracer for faster execution when full opcode detail is not needed
- A full trace of a dense block can be hundreds of megabytes in size
- Paginate results and process traces in batches for large blocks

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number as hex string, or tag: "earliest", "latest", "pending"
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlockByNumber",
  "params": [
    "latest",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `structLogs[].stack` (`Array<DATA>, required`): Stack contents (if not disabled)
- `structLogs[].storage` (`Object, required`): Storage changes (if not disabled)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "DELEGATECALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x9c40",
            "input": "0xa9059cbb...",
            "output": "0x0000000000000000000000000000000000000000000000000000000000000001"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "block #999999999 not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# Trace latest block with call tracer
curl -X POST https://api-plasma-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-plasma-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-plasma-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
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

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

// Trace latest block with call tracer
const callTraces = await provider.send('debug_traceBlockByNumber', [
  'latest',
  { tracer: 'callTracer' }
]);

console.log(`Latest block has ${callTraces.length} transactions`);

for (const trace of callTraces) {
  const gasUsed = parseInt(trace.result.gasUsed, 16);
  const status = trace.result.error ? 'REVERTED' : 'OK';
  console.log(`  ${trace.txHash}: ${gasUsed} gas [${status}]`);

  // Print sub-calls
  if (trace.result.calls) {
    for (const call of trace.result.calls) {
      console.log(`    -> ${call.type} to ${call.to}`);
    }
  }
}

// Trace a specific historical block
const blockNum = '0xF4240'; // block 1,000,000
const historicalTraces = await provider.send('debug_traceBlockByNumber', [
  blockNum,
  { tracer: 'callTracer' }
]);
console.log(`Block 1000000 had ${historicalTraces.length} transactions`);

// Trace with prestate tracer for state analysis
const prestateTraces = await provider.send('debug_traceBlockByNumber', [
  'latest',
  { tracer: 'prestateTracer' }
]);

for (const trace of prestateTraces) {
  const addresses = Object.keys(trace.result);
  console.log(`Tx ${trace.txHash} touched ${addresses.length} accounts`);
}
```

```python
import requests

def trace_block_by_number(block_number, tracer='callTracer', **kwargs):
    tracer_config = {'tracer': tracer, **kwargs}
    response = requests.post(
        'https://api-plasma-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByNumber',
            'params': [block_number, tracer_config],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f'RPC error: {result["error"]["message"]}')
    return result['result']

# Trace latest block
traces = trace_block_by_number('latest')
print(f'Latest block: {len(traces)} transactions')

for trace in traces:
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    has_error = 'error' in result
    print(f'  {trace["txHash"]}: {gas_used} gas {"[REVERTED]" if has_error else ""}')

# Trace specific block
traces = trace_block_by_number('0xF4240')
print(f'Block 1000000: {len(traces)} transactions')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-plasma-mainnet.n.dwellir.com/YOUR_API_KEY'))

block_number = w3.eth.block_number
traces = w3.provider.make_request('debug_traceBlockByNumber', [
    hex(block_number),
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions in block {block_number}')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type TraceResult struct {
    TxHash string    `json:"txHash"`
    Result CallTrace `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Error   string      `json:"error,omitempty"`
    Calls   []CallTrace `json:"calls,omitempty"`
}

func traceBlockByNumber(blockNumber string) ([]TraceResult, error) {
    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlockByNumber",
        "params": []interface{}{
            blockNumber,
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-plasma-mainnet.n.dwellir.com/YOUR_API_KEY",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    if err := json.Unmarshal(data, &response); err != nil {
        return nil, err
    }

    return response.Result, nil
}

func main() {
    traces, err := traceBlockByNumber("latest")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Latest block: %d transactions\n", len(traces))
    for _, trace := range traces {
        gasUsed, _ := strconv.ParseInt(trace.Result.GasUsed[2:], 16, 64)
        status := "OK"
        if trace.Result.Error != "" {
            status = "REVERTED"
        }
        fmt.Printf("  %s: %d gas [%s]\n", trace.TxHash, gasUsed, status)
    }
}
```

## Common Use Cases

### 1. Historical Gas Consumption Analysis

Profile gas usage across a range of blocks on Plasma:

```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 Plasma:

```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-plasma-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);
  }
}
```

## Related Methods

- [`debug_traceBlock`](https://www.dwellir.com/docs/plasma/debug_traceBlock) - Trace all transactions using RLP-encoded block data
- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/plasma/debug_traceBlockByHash) - Trace all transactions in a block by hash
- [`debug_traceTransaction`](https://www.dwellir.com/docs/plasma/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/plasma/debug_traceCall) - Trace a call without creating a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/plasma/eth_getBlockByNumber) - Get block details by number (without traces)
