Docs
Supported ChainsZetachainJSON-RPC APIDebug Methods

debug_traceBlockByHash - Zetachain RPC Method

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

Traces all transactions in a block on Zetachain 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 Zetachain? Build on the universal omnichain blockchain enabling cross-chain smart contracts across 50+ chains including Bitcoin with native Bitcoin support, 50+ chain interoperability via UNISON, no bridging required, and partnerships with Curve and SushiSwap.

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

When to Use This Method

debug_traceBlockByHash is essential for cross-chain dApp developers, Bitcoin DeFi builders, and teams requiring native multi-chain interoperability:

  • 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 Zetachain
  • 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 omnichain DeFi, native Bitcoin smart contracts, cross-chain asset management, and unified liquidity aggregation
  • 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-zetachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByHash",
    "params": [
      "0x1c062dc4e905e15e9c1099a63c36a1c6fd5f0a6d45334d2e6c4eec3f668bde5f",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'

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

Common Use Cases

1. Find All Reverted Transactions in a Block

Identify and analyze failed transactions on Zetachain:

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 Zetachain:

Python
import requests

def analyze_token_transfers(block_hash):
    response = requests.post('https://api-zetachain-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('0x1c062dc4e905e15e9c1099a63c36a1c6fd5f0a6d45334d2e6c4eec3f668bde5f')

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 Zetachain
-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;
  }
}