Tracing
Transaction tracing allows you to inspect the internal operations of smart contract executions, debug failed transactions, and analyze gas consumption patterns.
Overview​
Tracing APIs provide detailed information about:
- EVM execution steps
- Internal transactions
- State changes
- Gas consumption
- Opcode execution
- Storage modifications
Available Tracing Methods​
debug_traceTransaction​
Trace a single transaction by hash.
curl -X POST https://api-ethereum-mainnet.dwellir.com/YOUR_API_KEY \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "debug_traceTransaction",
"params": [
"0xhash",
{
"tracer": "callTracer",
"tracerConfig": {
"onlyTopCall": false
}
}
],
"id": 1
}'
debug_traceBlockByNumber​
Trace all transactions in a block.
curl -X POST https://api-ethereum-mainnet.dwellir.com/YOUR_API_KEY \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "debug_traceBlockByNumber",
"params": ["0x1234", {"tracer": "callTracer"}],
"id": 1
}'
trace_transaction​
Get trace information for a transaction (Parity/OpenEthereum style).
curl -X POST https://api-ethereum-mainnet.dwellir.com/YOUR_API_KEY \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "trace_transaction",
"params": ["0xhash"],
"id": 1
}'
Tracer Types​
1. Call Tracer​
Captures all calls made during transaction execution.
{
"tracer": "callTracer",
"tracerConfig": {
"onlyTopCall": false,
"withLog": true
}
}
Output includes:
- Call type (CALL, DELEGATECALL, STATICCALL, CREATE)
- From/to addresses
- Input/output data
- Gas used
- Error messages
2. Prestate Tracer​
Shows state before transaction execution.
{
"tracer": "prestateTracer",
"tracerConfig": {
"diffMode": true
}
}
Output includes:
- Account balances
- Contract code
- Storage values
- Nonce values
3. 4byte Tracer​
Identifies function signatures in the transaction.
{
"tracer": "4byteTracer"
}
Output:
- Map of 4-byte signatures to call counts
4. Custom JavaScript Tracer​
Create custom tracers for specific analysis.
{
"tracer": "{data: [], step: function(log) { this.data.push(log.op.toString()); }, result: function() { return this.data; }}"
}
Practical Examples​
Debugging Failed Transactions​
async function debugFailedTransaction(txHash) {
const trace = await provider.send('debug_traceTransaction', [
txHash,
{ tracer: 'callTracer' }
]);
// Find the exact failure point
function findError(call) {
if (call.error) {
return {
address: call.to,
error: call.error,
input: call.input
};
}
if (call.calls) {
for (const subcall of call.calls) {
const error = findError(subcall);
if (error) return error;
}
}
return null;
}
return findError(trace);
}
Analyzing Gas Usage​
async function analyzeGasUsage(txHash) {
const trace = await provider.send('debug_traceTransaction', [
txHash,
{ tracer: 'callTracer' }
]);
function calculateGas(call, depth = 0) {
const gasInfo = {
address: call.to,
gasUsed: parseInt(call.gasUsed, 16),
depth: depth,
calls: []
};
if (call.calls) {
for (const subcall of call.calls) {
gasInfo.calls.push(calculateGas(subcall, depth + 1));
}
}
return gasInfo;
}
return calculateGas(trace);
}
Internal Transactions​
async function getInternalTransactions(txHash) {
const trace = await provider.send('trace_transaction', [txHash]);
return trace
.filter(t => t.type === 'call' && t.action.value !== '0x0')
.map(t => ({
from: t.action.from,
to: t.action.to,
value: t.action.value,
success: t.result !== null
}));
}
Performance Considerations​
Archive Node Requirements​
Tracing requires archive nodes for historical data:
- Full state at every block
- Complete transaction history
- Higher infrastructure costs
Request Optimization​
- Cache Trace Results
const traceCache = new Map();
async function getCachedTrace(txHash) {
if (traceCache.has(txHash)) {
return traceCache.get(txHash);
}
const trace = await provider.send('debug_traceTransaction', [
txHash,
{ tracer: 'callTracer' }
]);
traceCache.set(txHash, trace);
return trace;
}
- Batch Trace Requests
async function batchTraceTransactions(txHashes) {
const requests = txHashes.map((hash, index) => ({
jsonrpc: '2.0',
method: 'debug_traceTransaction',
params: [hash, { tracer: 'callTracer' }],
id: index
}));
return await provider.send(requests);
}
- Use Appropriate Tracers
callTracer
for debuggingprestateTracer
for state analysis4byteTracer
for function identification- Custom tracers for specific needs
Error Handling​
Common tracing errors and solutions:
Missing State​
{
"error": {
"code": -32000,
"message": "missing trie node"
}
}
Solution: Use an archive node or request more recent data.
Timeout​
{
"error": {
"code": -32000,
"message": "execution timeout"
}
}
Solution: Simplify tracer or reduce block range.
Invalid Tracer​
{
"error": {
"code": -32602,
"message": "invalid tracer"
}
}
Solution: Check tracer syntax and supported tracers for the network.
Security Considerations​
When using tracing:
- Validate Input: Sanitize transaction hashes and parameters
- Rate Limit: Implement client-side rate limiting
- Access Control: Restrict tracing access in production
- Data Privacy: Traced data may contain sensitive information
Tools and Libraries​
Web3.js​
const trace = await web3.currentProvider.send({
method: 'debug_traceTransaction',
params: [txHash, { tracer: 'callTracer' }]
});
Ethers.js​
const trace = await provider.send('debug_traceTransaction', [
txHash,
{ tracer: 'callTracer' }
]);
Cast (Foundry)​
cast run --trace 0xhash --rpc-url https://api-ethereum-mainnet.dwellir.com/YOUR_API_KEY
Need help with tracing? Contact our support team.