# debug_traceCall - Tempo RPC Method

Traces a call on Tempo 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 Tempo?** Build on a payments-first EVM chain with deterministic settlement and stablecoin-native fees with no native gas token, fees denominated in supported USD stablecoins, and full EVM RPC compatibility.

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 Tempo - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceCall` is powerful for payment application developers, wallet teams, stablecoin issuers, and treasury automation builders:

- **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 Tempo
- **Debugging Contract Interactions** - Step through contract execution at the opcode level to understand complex interactions, delegate calls, and proxy patterns for merchant settlement, treasury operations, payout automation, and stablecoin-native financial applications
- **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": "0x0bd34b0a5be345c9bf7a147eb698e993511180cb",
      "data": "0x70a082310000000000000000000000000bd34b0a5be345c9bf7a147eb698e993511180cb"
    },
    "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-tempo-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceCall",
    "params": [
      {
        "to": "0x0bd34b0a5be345c9bf7a147eb698e993511180cb",
        "data": "0x70a082310000000000000000000000000bd34b0a5be345c9bf7a147eb698e993511180cb"
      },
      "latest",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'

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

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

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

// Trace a simple read-only contract call
const callTrace = await provider.send('debug_traceCall', [
  {
    to: '0x0bd34b0a5be345c9bf7a147eb698e993511180cb',
    data: '0x70a082310000000000000000000000000bd34b0a5be345c9bf7a147eb698e993511180cb'
  },
  '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: '0x0bd34b0a5be345c9bf7a147eb698e993511180cb',
    data: '0x70a082310000000000000000000000000bd34b0a5be345c9bf7a147eb698e993511180cb'
  },
  '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-tempo-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': '0x0bd34b0a5be345c9bf7a147eb698e993511180cb',
    'data': '0x70a082310000000000000000000000000bd34b0a5be345c9bf7a147eb698e993511180cb'
}

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-tempo-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":   "0x0bd34b0a5be345c9bf7a147eb698e993511180cb",
        "data": "0x70a082310000000000000000000000000bd34b0a5be345c9bf7a147eb698e993511180cb",
    }

    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-tempo-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 Tempo 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 Tempo:

```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 Tempo:

```python
import requests

def security_trace_call(call_object, block='latest'):
    response = requests.post('https://api-tempo-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': '0x0bd34b0a5be345c9bf7a147eb698e993511180cb',
    'data': '0x095ea7b3000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
})
```

## Related Methods

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