debug_traceBlockByNumber - Zetachain RPC Method
Trace all transactions in a block by number on Zetachain. Analyze gas consumption, debug state transitions, and inspect historical block execution.
Traces all transactions in a block on Zetachain 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 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_traceBlockByNumber is essential for cross-chain dApp developers, Bitcoin DeFi builders, and teams requiring native multi-chain interoperability:
- Historical Block Analysis — Trace transactions in any past block by number, enabling time-series analysis of Zetachain execution patterns
- Gas Consumption Patterns — Profile gas usage across all transactions in a block to understand network congestion and gas cost trends for omnichain DeFi, native Bitcoin smart contracts, cross-chain asset management, and unified liquidity aggregation
- 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
Request Parameters
Block number as hex string, or tag: "earliest", "latest", "pending"
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
Gas provided
Return value of the call
Array of opcode execution steps
Program counter
Opcode name
Remaining gas
Gas cost of this opcode
Call depth
Stack contents (if not disabled)
Storage changes (if not disabled)
Error Responses
Code Examples
# Trace latest block 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_traceBlockByNumber",
"params": ["latest", {"tracer": "callTracer"}],
"id": 1
}'
# Trace specific block 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_traceBlockByNumber",
"params": ["0xF4240", {"tracer": "prestateTracer"}],
"id": 1
}'
# Trace with default opcode tracer (minimal output)
curl -X POST https://api-zetachain-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
}'Common Use Cases
1. Historical Gas Consumption Analysis
Profile gas usage across a range of blocks on Zetachain:
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 Zetachain:
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-zetachain-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 calls3. Debugging State Transitions After Protocol Upgrades
Compare block execution before and after a hard fork or protocol upgrade:
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);
}
}Error Handling
Common errors and solutions:
| Error Code | Description | Solution |
|---|---|---|
| -32000 | Block not found | Verify the block number exists on Zetachain — it may be in the future |
| -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 |
| -32602 | Invalid params | Check that the block number is a valid hex string or tag |
async function safeTraceBlockByNumber(provider, blockNumber, options = {}) {
const defaultOptions = {
tracer: 'callTracer',
timeout: '60s',
...options
};
try {
return await provider.send('debug_traceBlockByNumber', [blockNumber, defaultOptions]);
} catch (error) {
if (error.code === -32601) {
throw new Error('debug_traceBlockByNumber not available — ensure archive node with debug API');
}
if (error.code === -32000 && error.message?.includes('not found')) {
throw new Error(`Block ${blockNumber} not found on Zetachain`);
}
if (error.message?.includes('timeout')) {
console.warn('Trace timed out — retrying with top-level calls only');
return await provider.send('debug_traceBlockByNumber', [
blockNumber,
{ tracer: 'callTracer', tracerConfig: { onlyTopCall: true } }
]);
}
throw error;
}
}Related Methods
debug_traceBlock— Trace all transactions using RLP-encoded block datadebug_traceBlockByHash— Trace all transactions in a block by hashdebug_traceTransaction— Trace a single transaction by hashdebug_traceCall— Trace a call without creating a transactioneth_getBlockByNumber— Get block details by number (without traces)
debug_traceBlockByHash
Trace all transactions in a block by hash on Zetachain. Investigate specific blocks, analyze execution order, and debug reverts with detailed traces.
debug_traceCall
Trace a call without creating a transaction on Zetachain. Simulate transactions, debug contract interactions, and analyze gas usage with full execution traces.