debug_traceCall
https://api.triport.io/v1/polygonSimulate a single Polygon call against a historical or pending state and return its full Geth-style execution trace — without broadcasting anything to the network.
debug_traceCall executes a transaction-shaped call object against the EVM at a
chosen block, exactly like eth_call, but instead of returning only the
final return data it returns the complete tree of execution frames the call
produced. Because nothing is signed or broadcast, you can use it to dry-run a
swap, simulate a potential MEV opportunity, or reproduce a revert and inspect
every internal call along the way.
This is the Geth-style tracer. It is the lighter, faster counterpart to the
Parity-style trace_call: it returns a single nested call
object ({type, from, to, gas, gasUsed, value, input, output, calls[], error?})
rather than a flat array of trace/stateDiff/vmTrace records. The output
schemas differ — if you index Polygon execution with both, your decoder must
handle each shape separately.
Typical uses:
- Pre-confirmation analysis — simulate a candidate transaction and read the full execution path (including internal token transfers) before deciding to send it.
- Revert debugging — when an
eth_callfails opaquely, trace it to see which sub-call reverted and with whaterror. - Forensics — re-run a call against a past block to reconstruct behavior.
Both debug_* trace methods on Polygon belong to the premium polygon_trace
category and require Pro tier. debug_traceCall is the cheapest of them —
single-call traces are small and fast, unlike full-block traces such as
debug_traceBlockByNumber.
Parameters
Positional params array, [callObject, blockParameter, traceConfig?]:
callObjectobjectrequiredeth_call transaction object).blockParameterstringrequired"0x4470eb3"), a 32-byte block hash, or a tag ("latest", "pending", "earliest").traceConfigobjectoptional{"tracer": "callTracer"}. With callTracer you get the nested call tree shown below; omit it for the default opcode-level (struct-log) trace.callObject fieldsobjectfromstringoptionaltostringoptionalgasstringoptionalgasPricestringoptionalmaxFeePerGas / maxPriorityFeePerGas.valuestringoptionaldatastringoptionalResponse
A call that reverts comes back with an error field (and usually no output):
{
"type": "CALL",
"from": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f1",
"to": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619",
"gas": "0x2dc6c0",
"gasUsed": "0x1860",
"input": "0x...",
"error": "execution reverted"
}typestringCALL, STATICCALL, DELEGATECALL, CREATE, CREATE2, SELFDESTRUCT.fromstringtostringvaluestringgasstringgasUsedstringinputstringoutputstringcallsarrayerrorstring"execution reverted", "out of gas").Errors
| Code | Meaning | When it happens |
|---|---|---|
-32002 | tier_insufficient | Your key is below Pro tier (or lacks the polygon_trace scope). The error data includes current_tier, required_tier, and an upgrade_url. |
-32003 | rate_limited | You exceeded your tier's RPS for polygon_trace (Pro 3 / Business 10). The data carries limit_rps, burst_capacity, and retry_after_sec. |
-32005 | unauthorized | Missing or invalid Authorization header. |
-32601 | method_unknown | Method not enabled for this key/chain. |
-32602 | invalid params | Malformed callObject, bad block parameter, or unknown tracer. |
All errors use the shared JSON-RPC envelope — see errors.md
for the full structure and the data fields above.
Examples
JavaScript (fetch)
const res = await fetch("https://api.triport.io/v1/polygon", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.TRIPORT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "debug_traceCall",
params: [
{
from: "0xddf252ad1be2c89b69c2b068fc378daa952ba7f1",
to: "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",
data: "0x70a08231000000000000000000000000ddf252ad1be2c89b69c2b068fc378daa952ba7f1",
},
"latest",
{ tracer: "callTracer" },
],
}),
});
const { result } = await res.json();
console.log(result.type, result.gasUsed, result.calls?.length ?? 0);TypeScript SDK (@triport/sdk)
import { Triport } from "@triport/sdk";
const tp = new Triport({ apiKey: process.env.TRIPORT_API_KEY! });
const trace = await tp.polygon.rpc("debug_traceCall", [
{
from: "0xddf252ad1be2c89b69c2b068fc378daa952ba7f1",
to: "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",
data: "0x70a08231000000000000000000000000ddf252ad1be2c89b69c2b068fc378daa952ba7f1",
},
"latest",
{ tracer: "callTracer" },
]);
console.log(trace.gasUsed, trace.error ?? "ok");Python (triport-sdk)
import os
from triport import Triport
tp = Triport(api_key=os.environ["TRIPORT_API_KEY"])
trace = tp.polygon.rpc(
"debug_traceCall",
[
{
"from": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f1",
"to": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",
"data": "0x70a08231000000000000000000000000ddf252ad1be2c89b69c2b068fc378daa952ba7f1",
},
"latest",
{"tracer": "callTracer"},
],
)
print(trace["gasUsed"], trace.get("error", "ok"))Notes
- Geth vs Parity output.
debug_traceCallreturns a nested call tree;trace_callreturns flat Paritytracerecords. They describe the same execution but with different schemas — pick one as your primary and adapt your decoder accordingly. Polygon's Bor execution may also render certain frame types slightly differently from vanilla Geth (e.g.CALLvsDELEGATECALLboundaries), so handle both when reconciling traces. - Stay under your RPS. Single-call traces are light and fast, but the
polygon_tracecategory is rate-limited to 3 rps (Pro) / 10 rps (Business). For high-volume simulation, batch your logic per call rather than fanning out many concurrentdebug_traceCallrequests. - Heavier relatives. To trace an already-mined transaction use a
transaction-trace method; to trace an entire block use
debug_traceBlockByNumber— both share the same Pro-tierpolygon_tracelimits. - Block selection matters. Tracing against
"latest"reflects current state; pass a specific hex block number to reproduce historical behavior.