# debug_traceCall - Ethereum RPC Method

Traces a call on Ethereum without creating a transaction on-chain. This is a dry-run trace - it executes the call in the EVM at a specified block and returns detailed execution traces including opcodes, internal calls, and state changes, without any on-chain side effects.

> **Why Ethereum?** Build on the world's leading smart contract platform with the largest developer ecosystem with battle-tested security, institutional adoption, and unmatched ecosystem with $166B+ TVL.

This method requires an archive node with debug APIs enabled when tracing against historical blocks. For `"latest"` or `"pending"` blocks, a full node with debug APIs may suffice. Dwellir provides archive node access for Ethereum - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceCall` is powerful for DeFi developers, NFT creators, and enterprise blockchain teams:

- **Simulating Transactions Before Sending** - Preview the full execution trace of a transaction before committing it on-chain, catching reverts and unexpected behavior before spending gas on Ethereum
- **Debugging Contract Interactions** - Step through contract execution at the opcode level to understand complex interactions, delegate calls, and proxy patterns for DeFi protocols (60% market share), NFT marketplaces, DAOs, and enterprise dApps
- **Gas Estimation With Trace Details** - Go beyond `eth_estimateGas` by seeing exactly which opcodes and internal calls consume gas, enabling targeted optimization
- **Security Analysis** - Analyze how a contract would execute a specific call, detecting reentrancy, unexpected state modifications, and access control issues

## Best Practices

- Requires archive node access when tracing against historical blocks
- Use the stateDiff tracer for storage change analysis on simulated calls
- The prestateTracer shows account state before the call executes
- The callTracer is fastest for understanding call structure

## Request Parameters

- `callObject` (`Object, required`): Transaction call object (same format as eth_call)
- `blockNumber` (`QUANTITY|TAG, required`): Block number as hex string, or tag: "earliest", "latest", "pending"
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)
- `from` (`DATA, optional`): Sender address (defaults to zero address)
- `to` (`DATA, required`): Recipient / contract address
- `gas` (`QUANTITY, optional`): Gas limit for the call
- `gasPrice` (`QUANTITY, optional`): Gas price in wei
- `maxFeePerGas` (`QUANTITY, optional`): Max fee per gas (EIP-1559)
- `maxPriorityFeePerGas` (`QUANTITY, optional`): Max priority fee per gas (EIP-1559)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Encoded function call data

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceCall",
  "params": [
    {
      "from": "0x1234567890abcdef1234567890abcdef12345678",
      "to": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
      "data": "0x70a08231000000000000000000000000d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
    },
    "latest",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `structLogs[].stack` (`Array<DATA>, required`): Stack contents (if not disabled)
- `structLogs[].storage` (`Object, required`): Storage changes (if not disabled)
- `address` (`Object, required`): State of each account touched by the call
- `address.balance` (`QUANTITY, required`): Account balance
- `address.nonce` (`QUANTITY, required`): Account nonce
- `address.code` (`DATA, required`): Contract bytecode (if contract account)
- `address.storage` (`Object, required`): Storage slots accessed

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0x1234567890abcdef1234567890abcdef12345678",
    "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "value": "0x0",
    "gas": "0x2faf080",
    "gasUsed": "0x5e1a",
    "input": "0x70a08231000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000",
    "calls": [
      {
        "type": "DELEGATECALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0xfedcba0987654321fedcba0987654321fedcba09",
        "gas": "0x2fa4060",
        "gasUsed": "0x2510",
        "input": "0x70a08231000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000"
      }
    ]
  }
}
```

## Error Responses

### Error Response (Reverted Call)

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0x1234567890abcdef1234567890abcdef12345678",
    "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "value": "0x0",
    "gas": "0x2faf080",
    "gasUsed": "0x831b",
    "input": "0xa9059cbb...",
    "output": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020...",
    "error": "execution reverted",
    "revertReason": "ERC20: transfer amount exceeds balance"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# Trace a read-only contract call with call tracer
curl -X POST https://api-ethereum-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceCall",
    "params": [
      {
        "to": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
        "data": "0x70a08231000000000000000000000000d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
      },
      "latest",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'

# Trace with default opcode tracer
curl -X POST https://api-ethereum-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceCall",
    "params": [
      {
        "to": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
        "data": "0x70a08231000000000000000000000000d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
      },
      "latest",
      {"disableStorage": true, "enableReturnData": true}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Interface } from 'ethers';

const provider = new JsonRpcProvider('https://api-ethereum-mainnet.n.dwellir.com/YOUR_API_KEY');

// Trace a simple read-only contract call
const callTrace = await provider.send('debug_traceCall', [
  {
    to: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
    data: '0x70a08231000000000000000000000000d8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
  },
  'latest',
  { tracer: 'callTracer' }
]);

console.log(`Call type: ${callTrace.type}`);
console.log(`Gas used: ${parseInt(callTrace.gasUsed, 16)}`);
console.log(`Sub-calls: ${(callTrace.calls || []).length}`);

if (callTrace.error) {
  console.log(`Error: ${callTrace.error}`);
  console.log(`Revert reason: ${callTrace.revertReason}`);
} else {
  console.log(`Output: ${callTrace.output}`);
}

// Trace with prestate tracer to see state access
const prestateTrace = await provider.send('debug_traceCall', [
  {
    to: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
    data: '0x70a08231000000000000000000000000d8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
  },
  'latest',
  { tracer: 'prestateTracer' }
]);

for (const [addr, state] of Object.entries(prestateTrace)) {
  console.log(`Account ${addr}:`);
  if (state.balance) console.log(`  Balance: ${state.balance}`);
  if (state.storage) console.log(`  Storage slots: ${Object.keys(state.storage).length}`);
}
```

```python
import requests

def trace_call(call_object, block='latest', tracer='callTracer', **kwargs):
    tracer_config = {'tracer': tracer, **kwargs}
    response = requests.post(
        'https://api-ethereum-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceCall',
            'params': [call_object, block, tracer_config],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f'RPC error: {result["error"]["message"]}')
    return result['result']

# Trace a read-only contract call
call_obj = {
    'to': '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
    'data': '0x70a08231000000000000000000000000d8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
}

trace = trace_call(call_obj)
gas_used = int(trace['gasUsed'], 16)
print(f'Call type: {trace["type"]}')
print(f'Gas used: {gas_used}')

if 'error' in trace:
    print(f'Error: {trace["error"]}')
else:
    print(f'Output: {trace["output"]}')

# Show sub-calls
for call in trace.get('calls', []):
    print(f'  -> {call["type"]} to {call["to"]}')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-ethereum-mainnet.n.dwellir.com/YOUR_API_KEY'))

trace = w3.provider.make_request('debug_traceCall', [
    call_obj,
    'latest',
    {'tracer': 'callTracer'}
])
print(f'Result: {trace["result"]["type"]}')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type CallTrace struct {
    Type         string      `json:"type"`
    From         string      `json:"from"`
    To           string      `json:"to"`
    Value        string      `json:"value"`
    Gas          string      `json:"gas"`
    GasUsed      string      `json:"gasUsed"`
    Input        string      `json:"input"`
    Output       string      `json:"output"`
    Error        string      `json:"error,omitempty"`
    RevertReason string      `json:"revertReason,omitempty"`
    Calls        []CallTrace `json:"calls,omitempty"`
}

func main() {
    callObj := map[string]string{
        "to":   "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
        "data": "0x70a08231000000000000000000000000d8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
    }

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceCall",
        "params": []interface{}{
            callObj,
            "latest",
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-ethereum-mainnet.n.dwellir.com/YOUR_API_KEY",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result CallTrace `json:"result"`
    }
    json.Unmarshal(data, &response)

    trace := response.Result
    gasUsed, _ := strconv.ParseInt(trace.GasUsed[2:], 16, 64)

    fmt.Printf("Type: %s\n", trace.Type)
    fmt.Printf("Gas used: %d\n", gasUsed)

    if trace.Error != "" {
        fmt.Printf("Error: %s\n", trace.Error)
        fmt.Printf("Revert reason: %s\n", trace.RevertReason)
    } else {
        fmt.Printf("Output: %s\n", trace.Output)
    }

    // Print sub-calls
    for _, call := range trace.Calls {
        subGas, _ := strconv.ParseInt(call.GasUsed[2:], 16, 64)
        fmt.Printf("  -> %s to %s (%d gas)\n", call.Type, call.To, subGas)
    }
}
```

## Common Use Cases

### 1. Pre-Flight Transaction Simulation

Test a transaction before sending it on Ethereum to catch reverts and estimate costs:

```javascript
async function simulateTransaction(provider, txParams) {
  // Use callTracer to see the full call tree
  const trace = await provider.send('debug_traceCall', [
    {
      from: txParams.from,
      to: txParams.to,
      data: txParams.data,
      value: txParams.value || '0x0',
      gas: txParams.gasLimit || '0x1e8480' // 2M gas default
    },
    'latest',
    { tracer: 'callTracer' }
  ]);

  const gasUsed = parseInt(trace.gasUsed, 16);

  if (trace.error) {
    console.error('Transaction would revert!');
    console.error(`  Error: ${trace.error}`);
    console.error(`  Reason: ${trace.revertReason || 'unknown'}`);
    console.error(`  Gas wasted: ${gasUsed}`);
    return { success: false, error: trace.error, revertReason: trace.revertReason, gasUsed };
  }

  // Analyze internal calls for unexpected behavior
  const allCalls = flattenCalls(trace);
  const delegateCalls = allCalls.filter(c => c.type === 'DELEGATECALL');
  const creates = allCalls.filter(c => c.type === 'CREATE' || c.type === 'CREATE2');

  console.log('Simulation results:');
  console.log(`  Gas used: ${gasUsed}`);
  console.log(`  Internal calls: ${allCalls.length}`);
  console.log(`  Delegate calls: ${delegateCalls.length}`);
  console.log(`  Contract creations: ${creates.length}`);

  return { success: true, gasUsed, trace };
}

function flattenCalls(trace) {
  const calls = [trace];
  for (const sub of trace.calls || []) {
    calls.push(...flattenCalls(sub));
  }
  return calls;
}
```

### 2. Gas Optimization Analysis

Identify the most expensive opcodes in a contract call on Ethereum:

```javascript
async function analyzeGasHotspots(provider, callObj) {
  // Use default opcode tracer for step-by-step gas analysis
  const trace = await provider.send('debug_traceCall', [
    callObj,
    'latest',
    { disableStorage: false, enableReturnData: true }
  ]);

  const opcodeGas = {};

  for (const log of trace.structLogs) {
    if (!opcodeGas[log.op]) {
      opcodeGas[log.op] = { count: 0, totalGas: 0 };
    }
    opcodeGas[log.op].count++;
    opcodeGas[log.op].totalGas += log.gasCost;
  }

  // Sort by total gas cost
  const sorted = Object.entries(opcodeGas)
    .map(([op, stats]) => ({ op, ...stats, avgGas: Math.round(stats.totalGas / stats.count) }))
    .sort((a, b) => b.totalGas - a.totalGas);

  console.log('Gas hotspots:');
  console.log('Op'.padEnd(15), 'Count'.padStart(8), 'Total Gas'.padStart(12), 'Avg Gas'.padStart(10));
  for (const entry of sorted.slice(0, 10)) {
    console.log(
      entry.op.padEnd(15),
      String(entry.count).padStart(8),
      String(entry.totalGas).padStart(12),
      String(entry.avgGas).padStart(10)
    );
  }

  // Identify SSTORE/SLOAD hotspots (most expensive storage operations)
  const storageOps = trace.structLogs.filter(
    log => log.op === 'SSTORE' || log.op === 'SLOAD'
  );
  console.log(`\nStorage operations: ${storageOps.length} (${storageOps.filter(s => s.op === 'SSTORE').length} writes)`);

  return { opcodeGas: sorted, totalSteps: trace.structLogs.length, totalGas: trace.gas };
}
```

### 3. Security Analysis of Contract Interactions

Detect potentially dangerous patterns when calling a contract on Ethereum:

```python
import requests

def security_trace_call(call_object, block='latest'):
    response = requests.post('https://api-ethereum-mainnet.n.dwellir.com/YOUR_API_KEY', json={
        'jsonrpc': '2.0',
        'method': 'debug_traceCall',
        'params': [call_object, block, {'tracer': 'callTracer'}],
        'id': 1
    })
    trace = response.json()['result']

    warnings = []
    all_calls = flatten_calls(trace)

    for call in all_calls:
        # Detect unexpected delegate calls
        if call['type'] == 'DELEGATECALL':
            warnings.append(f'DELEGATECALL to {call["to"]} - could modify caller storage')

        # Detect value transfers to unexpected addresses
        value = int(call.get('value', '0x0'), 16)
        if value > 0 and call['to'] != call_object.get('to', '').lower():
            warnings.append(
                f'Value transfer of {value} wei to unexpected address {call["to"]}'
            )

        # Detect selfdestruct (CALL with no input to EOA after value)
        if call.get('error'):
            warnings.append(f'Internal revert at {call["to"]}: {call["error"]}')

    if trace.get('error'):
        print(f'TOP-LEVEL REVERT: {trace["error"]}')
        if trace.get('revertReason'):
            print(f'  Reason: {trace["revertReason"]}')
    else:
        gas_used = int(trace['gasUsed'], 16)
        print(f'Call succeeded: {gas_used} gas used')

    if warnings:
        print(f'\nSecurity warnings ({len(warnings)}):')
        for w in warnings:
            print(f'  - {w}')
    else:
        print('No security warnings detected')

    return {'success': not trace.get('error'), 'warnings': warnings}

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls

# Example: analyze a token approval
security_trace_call({
    'from': '0x1234567890abcdef1234567890abcdef12345678',
    'to': '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
    'data': '0x095ea7b3000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
})
```

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/ethereum/eth_call) - Execute a call without trace (returns only the result, not execution details)
- [`debug_traceTransaction`](https://www.dwellir.com/docs/ethereum/debug_traceTransaction) - Trace an already-executed transaction by hash
- [`eth_estimateGas`](https://www.dwellir.com/docs/ethereum/eth_estimateGas) - Estimate gas for a call (without trace details)
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/ethereum/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/ethereum/debug_traceBlockByHash) - Trace all transactions in a block by hash
