Docs
Supported ChainsMantleJSON-RPC APIDebug Methods

debug_traceBlockByHash - Mantle RPC Method

Trace all transactions in a block by hash on Mantle. Investigate specific blocks, analyze execution order, and debug reverts with detailed traces.

Traces all transactions in a block on Mantle identified by its block hash. Returns detailed execution traces for every transaction, making it ideal for investigating specific blocks when you know the exact hash.

Why Mantle? Build on the world's largest ZK rollup by TVL with $2.5B+ secured and deep Bybit integration with near-instant ZK finality via OP Succinct, $6.2B treasury backing, mETH liquid staking, and 25% Bybit trading fee discounts.

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

When to Use This Method

debug_traceBlockByHash is essential for DeFi developers, liquid staking builders, and teams seeking institutional exchange integration:

  • Investigating Specific Blocks — When you have a block hash from an event, alert, or on-chain reference, trace every transaction in that exact block on Mantle
  • Analyzing Transaction Execution Order — Understand how transactions within a block interact, including cross-transaction state dependencies
  • Debugging Reverted Transactions — Find the exact opcode where transactions failed across an entire block for liquid staking (mETH $1.87B TVL), institutional DeFi via Bybit, and yield optimization strategies
  • Fork and Reorg Analysis — Use block hashes to trace transactions in specific forks, ensuring you analyze the correct chain branch

Request Parameters

Request
blockHashDATA

32-byte hash of the block to trace

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

addressObject

State of each account touched by the transaction

address.balanceQUANTITY

Account balance before execution

address.nonceQUANTITY

Account nonce before execution

address.codeDATA

Contract bytecode (if contract account)

address.storageObject

Storage slots read or written

Error Responses

Errors
Error Response-32000

Code Examples

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

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

Common Use Cases

1. Find All Reverted Transactions in a Block

Identify and analyze failed transactions on Mantle:

JavaScript
async function findReverts(provider, blockHash) {
  const traces = await provider.send('debug_traceBlockByHash', [
    blockHash,
    { tracer: 'callTracer' }
  ]);

  const reverts = [];

  for (const trace of traces) {
    if (trace.result.error) {
      reverts.push({
        txHash: trace.txHash,
        error: trace.result.error,
        revertReason: trace.result.revertReason || 'N/A',
        from: trace.result.from,
        to: trace.result.to,
        gasUsed: parseInt(trace.result.gasUsed, 16)
      });
    }

    // Also check sub-calls for internal reverts
    const internalReverts = findInternalReverts(trace.result.calls || []);
    if (internalReverts.length > 0) {
      reverts.push({
        txHash: trace.txHash,
        internalReverts,
        topLevelSuccess: !trace.result.error
      });
    }
  }

  console.log(`Found ${reverts.length} reverted transactions out of ${traces.length}`);
  for (const r of reverts) {
    console.log(`  ${r.txHash}: ${r.error || 'internal revert'}`);
  }
  return reverts;
}

function findInternalReverts(calls) {
  const reverts = [];
  for (const call of calls) {
    if (call.error) {
      reverts.push({ type: call.type, to: call.to, error: call.error });
    }
    reverts.push(...findInternalReverts(call.calls || []));
  }
  return reverts;
}

2. Analyze Token Transfer Patterns in a Block

Extract all ERC-20 transfer events from block traces on Mantle:

Python
import requests

def analyze_token_transfers(block_hash):
    response = requests.post('https://api-mantle-mainnet.n.dwellir.com/YOUR_API_KEY', json={
        'jsonrpc': '2.0',
        'method': 'debug_traceBlockByHash',
        'params': [block_hash, {'tracer': 'callTracer'}],
        'id': 1
    })
    traces = response.json()['result']

    # ERC-20 transfer(address,uint256) selector
    TRANSFER_SELECTOR = '0xa9059cbb'
    # ERC-20 transferFrom(address,address,uint256) selector
    TRANSFER_FROM_SELECTOR = '0x23b872dd'

    transfers = []

    for trace in traces:
        calls = flatten_calls(trace['result'])
        for call in calls:
            input_data = call.get('input', '')
            if input_data.startswith(TRANSFER_SELECTOR) or \
               input_data.startswith(TRANSFER_FROM_SELECTOR):
                transfers.append({
                    'tx_hash': trace['txHash'],
                    'token_contract': call['to'],
                    'from': call['from'],
                    'type': call['type'],
                    'gas_used': int(call.get('gasUsed', '0x0'), 16)
                })

    print(f'Found {len(transfers)} token transfers in block')
    # Group by token contract
    by_token = {}
    for t in transfers:
        by_token.setdefault(t['token_contract'], []).append(t)

    for token, txs in by_token.items():
        print(f'  {token}: {len(txs)} transfers')

    return transfers

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

analyze_token_transfers('0x731df9ec5eb8000dc29641dfbebb0efef7660fff0b55f64e402eec934af2c538')

3. Block Execution State Diff

Compare account states before and after block execution using the prestate tracer:

JavaScript
async function getBlockStateDiff(provider, blockHash) {
  // Get prestate — accounts state before each transaction
  const prestateTraces = await provider.send('debug_traceBlockByHash', [
    blockHash,
    { tracer: 'prestateTracer', tracerConfig: { diffMode: true } }
  ]);

  const allAddresses = new Set();
  const balanceChanges = {};

  for (const trace of prestateTraces) {
    const pre = trace.result.pre || trace.result;
    const post = trace.result.post || {};

    for (const [addr, state] of Object.entries(pre)) {
      allAddresses.add(addr);
      if (!balanceChanges[addr]) {
        balanceChanges[addr] = {
          preBal: BigInt(state.balance || '0x0'),
          postBal: BigInt((post[addr]?.balance) || state.balance || '0x0')
        };
      }
    }
  }

  console.log(`Block touched ${allAddresses.size} unique addresses`);
  for (const [addr, change] of Object.entries(balanceChanges)) {
    const diff = change.postBal - change.preBal;
    if (diff !== 0n) {
      console.log(`  ${addr}: ${diff > 0n ? '+' : ''}${diff} wei`);
    }
  }

  return balanceChanges;
}

Error Handling

Common errors and solutions:

Error CodeDescriptionSolution
-32000Block not foundVerify the block hash is correct and exists on Mantle
-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
-32002Resource unavailableNode is syncing or the requested block is not yet available
JavaScript
async function safeTraceBlockByHash(provider, blockHash, options = {}) {
  const defaultOptions = {
    tracer: 'callTracer',
    timeout: '60s',
    ...options
  };

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