traceCall
Analyze transaction execution with detailed traces.
Usage
example.ts
import { client } from './config'
const trace = await client.traceCall({
from: '0x742d35Cc6634C0532925a3b844Bc9e7595f06e8c',
to: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
data: '0xa9059cbb0000000000000000000000001234567890123456789012345678901234567890000000000000000000000000000000000000000000000000000000000000000a'
}, {
callTracer: true
})Call Hierarchy Analysis
const trace = await client.traceCall({
from: '0x742d35Cc6634C0532925a3b844Bc9e7595f06e8c',
to: '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D', // Uniswap Router
data: '0x38ed1739...', // swapExactTokensForTokens
value: '0x0'
}, {
callTracer: true,
prestateTracer: true
})
// Analyze call tree
if (trace.callTracer) {
console.log('Main call:', trace.callTracer.type)
console.log('Gas used:', trace.callTracer.gasUsed)
console.log('Sub-calls:', trace.callTracer.calls?.length || 0)
}State Changes Tracking
const trace = await client.traceCall({
from: '0x742d35Cc6634C0532925a3b844Bc9e7595f06e8c',
to: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
data: '0xa9059cbb...'
}, {
prestateTracer: true
})
// Check state changes
if (trace.prestateTracer) {
Object.entries(trace.prestateTracer).forEach(([address, state]) => {
console.log(`Account ${address}:`)
if ('balance' in state) console.log(` Balance: ${state.balance}`)
if ('nonce' in state) console.log(` Nonce: ${state.nonce}`)
})
}Builder Pattern
For complex traces with multiple tracers.
const trace = await client.trace()
.call({
from: '0x742d35Cc6634C0532925a3b844Bc9e7595f06e8c',
to: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
data: '0xa9059cbb...'
})
.withCallTracer({ onlyTopCall: false, withLogs: true })
.withPrestateTracer({ diffMode: true })
.with4ByteTracer()
.withStructLogger({ disableMemory: true })
.atBlock('latest')
.execute()
// Access all trace results
console.log('Has call trace:', !!trace.callTracer)
console.log('Has state trace:', !!trace.prestateTracer)
console.log('Has struct logs:', !!trace.structLogger)
console.log('Has 4byte data:', !!trace['4byteTracer'])Returns
ExtendedTracerResponse
The trace results with all enabled tracers.
Parameters
call
- Type:
TransactionCall
The transaction call to trace.
const trace = await client.traceCall({
from: '0x742d35Cc6634C0532925a3b844Bc9e7595f06e8c',
to: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
data: '0x095ea7b3...'
})options.block (optional)
- Type:
string | number - Default:
'latest'
The block to trace against.
const trace = await client.traceCall(
{ /* transaction */ },
{ block: '0x1234567' }
)options.callTracer (optional)
- Type:
boolean - Default:
false
Enable call hierarchy tracing.
const trace = await client.traceCall(
{ /* transaction */ },
{ callTracer: true }
)options.prestateTracer (optional)
- Type:
boolean - Default:
false
Enable state change tracing.
const trace = await client.traceCall(
{ /* transaction */ },
{ prestateTracer: true }
)options.structLogger (optional)
- Type:
boolean - Default:
false
Enable opcode-level tracing.
const trace = await client.traceCall(
{ /* transaction */ },
{ structLogger: true }
)options.fourByteTracer (optional)
- Type:
boolean - Default:
false
Enable function signature tracking.
const trace = await client.traceCall(
{ /* transaction */ },
{ fourByteTracer: true }
)Trace Transaction
Trace an executed transaction by hash.
const trace = await client.traceTransaction(
'0xbc4a51bbcbe7550446c151d0d53ee14d5318188e2af1726e28a481b075fc7b4c',
{
callTracer: true,
prestateTracer: true,
structLogger: true,
fourByteTracer: true
}
)
// Access transaction receipt
if (trace.receipt) {
console.log('Status:', trace.receipt.status)
console.log('Gas used:', trace.receipt.gasUsed)
console.log('Logs:', trace.receipt.logsCount)
}Trace Multiple Calls
Trace multiple calls with cumulative state changes.
const traces = await client.traceCallMany(
[
{
transactions: [{
from: '0x742d35Cc6634C0532925a3b844Bc9e7595f06e8c',
to: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
data: '0x095ea7b3...' // approve
}]
},
{
transactions: [{
from: '0x742d35Cc6634C0532925a3b844Bc9e7595f06e8c',
to: '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D',
data: '0x38ed1739...' // swap
}]
}
],
{
stateContext: {
blockNumber: '0x1234567',
transactionIndex: -1
},
callTracer: true
}
)
traces.forEach((trace, i) => {
console.log(`Bundle ${i + 1}:`, trace[0]?.callTracer?.gasUsed)
})Tracer Configurations
Call Tracer
const trace = await client.trace()
.call({ /* transaction */ })
.withCallTracer({
onlyTopCall: false, // Include internal calls
withLogs: true // Include event logs
})
.execute()
// Analyze call hierarchy
function analyzeCallTree(call: CallFrame, depth = 0) {
console.log(' '.repeat(depth) + `${call.type} to ${call.to}`)
console.log(' '.repeat(depth) + `Gas: ${call.gasUsed}/${call.gas}`)
call.calls?.forEach(subcall => analyzeCallTree(subcall, depth + 1))
}
if (trace.callTracer) {
analyzeCallTree(trace.callTracer)
}Prestate Tracer
const trace = await client.trace()
.call({ /* transaction */ })
.withPrestateTracer({
diffMode: true, // Show before/after
disableCode: false, // Include bytecode
disableStorage: false // Include storage
})
.execute()
// Analyze state changes
if (trace.prestateTracer && 'pre' in trace.prestateTracer) {
const { pre, post } = trace.prestateTracer
Object.keys(post).forEach(address => {
if (!pre[address]) {
console.log(`New account: ${address}`)
} else {
// Compare balances
const preBalance = BigInt(pre[address].balance || '0')
const postBalance = BigInt(post[address].balance || '0')
if (preBalance !== postBalance) {
console.log(`Balance change for ${address}:`)
console.log(` ${preBalance} -> ${postBalance}`)
}
}
})
}Struct Logger
const trace = await client.trace()
.call({ /* transaction */ })
.withStructLogger({
cleanStructLogs: true,
disableMemory: true, // Save memory
disableReturnData: false,
disableStack: false,
disableStorage: false
})
.execute()
// Analyze opcodes
if (trace.structLogger?.structLogs) {
const storageOps = trace.structLogger.structLogs.filter(
log => log.op === 'SSTORE' || log.op === 'SLOAD'
)
console.log(`Storage operations: ${storageOps.length}`)
storageOps.forEach(op => {
console.log(`${op.op} at pc=${op.pc}, gas=${op.gas}`)
})
}State Overrides
Modify state before tracing.
const trace = await client.trace()
.call({
from: '0x742d35Cc6634C0532925a3b844Bc9e7595f06e8c',
to: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
data: '0xa9059cbb...'
})
.withStateOverrides({
'0x742d35Cc6634C0532925a3b844Bc9e7595f06e8c': {
balance: '0x1bc16d674ec80000', // 2 ETH
nonce: 10
}
})
.withCallTracer()
.execute()Types
interface ExtendedTracerResponse {
callTracer?: CallTraceResponse | null
prestateTracer?: PrestateTraceResponse | null
structLogger?: StructLogResponse | null
'4byteTracer'?: FourByteResponse | null
receipt?: TransactionReceiptInfo | null
// Helper methods
hasCallTrace(): boolean
hasPrestateTrace(): boolean
hasStructLogs(): boolean
has4ByteTrace(): boolean
getGasUsed(): bigint
getErrors(): string[]
}
interface CallFrame {
type: 'CALL' | 'DELEGATECALL' | 'STATICCALL' | 'CREATE' | 'CREATE2'
from: string
to?: string
value?: string
gas: string
gasUsed: string
input: string
output?: string
error?: string
revertReason?: string
calls?: CallFrame[]
logs?: LogEntry[]
}
interface StructLog {
pc: number
op: string
gas: number
gasCost: number
depth: number
stack?: string[]
memory?: string[]
storage?: Record<string, string>
refund?: number
error?: string
}