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
32-byte hash of the block to trace
Tracer configuration object (see options below)
Response Body
Array of trace objects, one per transaction
Trace output (structure depends on tracer used)
Transaction hash for this trace
Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
Sender address
Recipient address
Value transferred in wei
Gas provided
Gas consumed
Call data
Return data
Error message if the call reverted
Decoded revert reason (if available)
Sub-calls made during execution
State of each account touched by the transaction
Account balance before execution
Account nonce before execution
Contract bytecode (if contract account)
Storage slots read or written
Error Responses
Code Examples
# 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:
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:
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:
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 Code | Description | Solution |
|---|---|---|
| -32000 | Block not found | Verify the block hash is correct and exists on Zetachain |
| -32000 | Missing trie node | Node is not an archive node or state has been pruned — use an archive endpoint |
| -32601 | Method not found | Debug namespace is not enabled on the node |
| -32603 | Internal error | Block may be too large to trace — increase timeout or use disableStorage |
| -32005 | Rate limit exceeded | Reduce request frequency or upgrade your plan |
| -32002 | Resource unavailable | Node is syncing or the requested block is not yet available |
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;
}
}Related Methods
debug_traceBlock— Trace all transactions using RLP-encoded block datadebug_traceBlockByNumber— Trace all transactions in a block by numberdebug_traceTransaction— Trace a single transaction by hashdebug_traceCall— Trace a call without creating a transactioneth_getBlockByHash— Get block details by hash (without traces)
debug_traceBlock
Trace all transactions in a block on Zetachain using a serialized block payload. Essential for block-level debugging, gas profiling, and MEV analysis.
debug_traceBlockByNumber
Trace all transactions in a block by number on Zetachain. Analyze gas consumption, debug state transitions, and inspect historical block execution.