Docs
Supported ChainsZetachainJSON-RPC APIDebug Methods

debug_traceBlock - Zetachain RPC Method

Trace all transactions in a block on Zetachain using a serialized block payload. Essential for block-level debugging, gas profiling, and MEV analysis.

Traces all transactions in a block on Zetachain by accepting a serialized block payload. Returns detailed execution traces for every transaction in the block, including opcode-level steps, gas consumption, and internal calls.

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_traceBlock is valuable for cross-chain dApp developers, Bitcoin DeFi builders, and teams requiring native multi-chain interoperability:

  • Block-Level Debugging — Trace every transaction in a block simultaneously when you have the serialized block payload, useful for offline analysis or replaying captured block data
  • Gas Profiling Across Transactions — Measure gas consumption per opcode across all transactions in a block to identify expensive patterns on Zetachain
  • MEV Analysis — Analyze transaction ordering, sandwich attacks, and arbitrage patterns by tracing full block execution for omnichain DeFi, native Bitcoin smart contracts, cross-chain asset management, and unified liquidity aggregation
  • Protocol Research — Replay historical blocks from RLP data to study state transitions and EVM behavior

Request Parameters

Request
blockPayloadDATA

Serialized block payload as a hex string

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

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

typestring

Call type (CALL, DELEGATECALL, STATICCALL, CREATE)

fromDATA

Sender address

toDATA

Recipient address

valueQUANTITY

Value transferred in wei

gasQUANTITY

Gas provided

gasUsedQUANTITY

Gas consumed

inputDATA

Call data

outputDATA

Return data

callsArray

Sub-calls made during execution

Error Responses

Errors
Error Response-32000

Code Examples

Bash
curl -X POST https://api-zetachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlock",
    "params": [
      "0xf90217a0...SERIALIZED_BLOCK_PAYLOAD",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'

Common Use Cases

1. Block-Level Gas Profiling

Analyze gas consumption across all transactions in a block on Zetachain:

JavaScript
async function profileBlockGas(provider, blockRlp) {
  const traces = await provider.send('debug_traceBlock', [
    blockRlp,
    { tracer: 'callTracer' }
  ]);

  let totalGas = 0;
  const txGas = [];

  for (const trace of traces) {
    const gasUsed = parseInt(trace.result.gasUsed, 16);
    totalGas += gasUsed;
    txGas.push({
      txHash: trace.txHash,
      gasUsed,
      type: trace.result.type,
      hasSubCalls: (trace.result.calls || []).length > 0
    });
  }

  // Sort by gas usage
  txGas.sort((a, b) => b.gasUsed - a.gasUsed);

  console.log(`Block total gas: ${totalGas}`);
  console.log('Top gas consumers:');
  for (const tx of txGas.slice(0, 5)) {
    const pct = ((tx.gasUsed / totalGas) * 100).toFixed(1);
    console.log(`  ${tx.txHash}: ${tx.gasUsed} gas (${pct}%)`);
  }

  return { totalGas, txGas };
}

2. MEV Detection and Analysis

Detect sandwich attacks and arbitrage in Zetachain blocks:

JavaScript
async function detectMEVPatterns(provider, blockRlp) {
  const traces = await provider.send('debug_traceBlock', [
    blockRlp,
    { tracer: 'callTracer' }
  ]);

  const dexInteractions = [];

  for (let i = 0; i < traces.length; i++) {
    const trace = traces[i];
    const calls = flattenCalls(trace.result);

    for (const call of calls) {
      // Detect swap-like function selectors (e.g., Uniswap swapExactTokensForTokens)
      if (call.input && call.input.startsWith('0x38ed1739')) {
        dexInteractions.push({
          index: i,
          txHash: trace.txHash,
          to: call.to,
          type: 'swap'
        });
      }
    }
  }

  // Check for sandwich patterns (swap-X-swap by same sender)
  for (let i = 0; i < dexInteractions.length - 2; i++) {
    const first = dexInteractions[i];
    const last = dexInteractions[i + 2];
    if (first.txHash !== last.txHash &&
        traces[first.index].result.from === traces[last.index].result.from) {
      console.log(`Potential sandwich: tx ${first.index} and ${last.index}`);
    }
  }

  return dexInteractions;
}

function flattenCalls(trace) {
  const calls = [trace];
  for (const sub of trace.calls || []) {
    calls.push(...flattenCalls(sub));
  }
  return calls;
}

3. Comparing Block Execution Across Clients

Verify consistent execution by tracing the same block RLP on different clients:

Python
import requests

def trace_on_endpoint(endpoint, block_rlp):
    response = requests.post(endpoint, json={
        'jsonrpc': '2.0',
        'method': 'debug_traceBlock',
        'params': [block_rlp, {'tracer': 'callTracer'}],
        'id': 1
    })
    return response.json()['result']

# Compare traces from two different endpoints
block_rlp = '0xf90217a0...'
traces_a = trace_on_endpoint('https://api-zetachain-mainnet.n.dwellir.com/YOUR_API_KEY', block_rlp)
traces_b = trace_on_endpoint('https://other-endpoint.example.com', block_rlp)

# Verify same number of traces
assert len(traces_a) == len(traces_b), 'Transaction count mismatch'

# Compare gas usage per transaction
for i, (a, b) in enumerate(zip(traces_a, traces_b)):
    gas_a = int(a['result']['gasUsed'], 16)
    gas_b = int(b['result']['gasUsed'], 16)
    if gas_a != gas_b:
        print(f'Gas mismatch at tx {i}: {gas_a} vs {gas_b}')
    else:
        print(f'Tx {i}: {gas_a} gas (consistent)')

Error Handling

Common errors and solutions:

Error CodeDescriptionSolution
-32000Invalid RLP dataVerify the block RLP encoding is correct and complete
-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 — try increasing timeout or use disableStorage
-32005Rate limit exceededReduce request frequency or upgrade your plan
-32002Resource not foundThe block data could not be resolved — check the RLP encoding
JavaScript
async function safeTraceBlock(provider, blockRlp, options = {}) {
  const defaultOptions = {
    tracer: 'callTracer',
    timeout: '60s',
    ...options
  };

  try {
    return await provider.send('debug_traceBlock', [blockRlp, defaultOptions]);
  } catch (error) {
    if (error.code === -32601) {
      throw new Error('debug_traceBlock not available — ensure archive node with debug API');
    }
    if (error.message?.includes('timeout')) {
      console.warn('Trace timed out — retrying with reduced detail');
      return await provider.send('debug_traceBlock', [
        blockRlp,
        { ...defaultOptions, disableStorage: true, disableStack: true }
      ]);
    }
    throw error;
  }
}