Skip to main content

Debugging smart contracts

It’s a common situation when you encounter an error in your smart contract. In some cases, you will see that an unexpected exit code is returned. This can be a sign of a bug in your contract. There are several techniques to debug your contract.

Log to the console

A straightforward and effective technique. The most common values to print are transactions and get‑method results. There are helper functions that let you inspect transactions in a developer‑friendly way.
TypeScript

Dump values from a contract

Three TVM debug instructions exist: Availability depends on the language you use.
  • In Tolk, use functions from the globally available debug object.
  • In FunC, these methods are available globally in stdlib.fc.
  • In Tact, use dumpStack for DUMPSTK and dump function for the other two. Tact also prints the exact line where dump is called, so you can quickly find it in your code.

Explore TVM logs

TypeScript
There are multiple verbosity levels; two are most useful:
  • vm_logs — outputs VM logs for each transaction; includes executed instructions and occurred exceptions.
  • vm_logs_full — outputs full VM logs for each transaction; includes executed instructions with binary offsets, the current stack for each instruction, and gas used by each instruction.
Typical output for vm_logs looks like this:
The contract tries to load a 64‑bit integer (LDU 64) from the slice, but there is not enough data, so exit code 9 occurs. Inspect the same code with the vm_logs_full verbosity level. (Output is heavily truncated from the top)
Stack is printed as [bottom, ..., top], where top is the top of the stack. Here, the stack contains two values:
  • Slice being read
  • Integer (500000000)
However, the slice has only 725 bits, and 711 bits were already read, as were both references. The contract tried to read 64 bits, but there was not enough data in the slice. You should locate the load_uint(64) call that causes the issue and ensure enough bits are available or adjust the read width. TVM log limits The size of TVM debug output depends on the verbosity level: When the output exceeds its limit, it is truncated from the bottom — older entries are discarded, and only the most recent lines are kept. Logs are not rotated.

Explore the transaction tree

For simple transaction trees, print all transactions and inspect them.
TypeScript
For complex trees, use a GUI tool. Two tools are commonly used:
  • TonDevWallet trace view — requires the TonDevWallet application; does not require a custom @ton/sandbox; requires the @tondevwallet/traces package.
  • TxTracer Sandbox — requires a custom @ton/sandbox package; runs in your browser.
Also, these tools allow you to explore each transaction’s separate logs.

Debugging with TVM Retracer

Even when a contract executes successfully (exit code = 0) with no errors, its actions may not produce the expected on-chain effect. TVM Retracer lets you replay the transaction and inspect VM-level execution.

Scenarios for retracing

  • All transaction phases complete without errors, yet the expected outcome is missing.
  • An action is skipped, or a transfer does not reach its destination.
  • You need a step-by-step view of how the TVM executed your contract logic.

How to analyze a transaction

  1. Obtain the transaction hash from a blockchain explorer.
  2. Open TVM Retracer and enter the transaction hash.
  3. Review the execution:
  • Inspect Logs section for executed instructions and exceptions.
  • Examine Actions cell (C5) to review data passed between contracts.
  • Check message modes — some modes can suppress errors, causing actions to be skipped.