Simulation and traces

Find out what a transaction will do before it does it — including a sequence of them.

One call

The Simulator page runs a call and shows the call tree, gas and a decoded revert reason, without committing anything. It can pretend about state — a balance, a storage slot, code that was never deployed — for that run only. Over RPC it is debug_traceCall:

params
[{ "from": "0x…", "to": "0x…", "data": "0x…" }, "latest", {
  "tracer": "structLogger",
  "tracerConfig": { "opcodes": "relevant", "limit": 2000 },
  "stateOverrides": { "0xYou": { "balance": "0x56BC75E2D63100000" } }
}]

Opcode steps are opt-in, because one swap is tens of thousands of them. opcodes picks a set — relevant, storage, calls, logs, memory — before the limit applies.

A sequence: forkstate_simulateBundle

Several transactions, in order, each on top of what the one before it did — and none of it kept. An approve and a swap simulated separately answer a different question; as a bundle they answer the real one.

params
[{
  "transactions": [
    { "from": "0xYou", "to": "0xToken",  "data": "0x095ea7b3…" },
    { "from": "0xYou", "to": "0xRouter", "data": "0x38ed1739…" }
  ],
  "overrides": { "0xToken": { "stateDiff": { "0x…": "0x…" } } },
  "trace": true,
  "diff": true
}]
  • A transaction that reverts is rolled back on its own; the rest still run, so you hear about all of them.
  • Each result has status, gas, return value, decoded revert reason, logs, and — if asked — the call tree and storage changes.
  • Up to 64 transactions per bundle. The Simulator page has a Bundle tab for the same thing.

Traces of real transactions

debug_traceTransaction returns the call tree and state diff recorded when a transaction ran on the testnet. The console's transaction page shows the same, and its debugger steps through the opcodes.

Every simulation runs in the block a transaction sent now would land in — the same block.timestamp and block.number the real one will see.