Skip to content

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
}