Skip to main content
Errors in smart contracts can produce an exit code, often indicating a bug in the contract. Use debugging methods to locate and fix the issue.

Log to the console

Most commonly used to print common values: transactions and get-method results.
  • Use findTransaction() to find a transaction by its properties.
  • Use flattenTransaction() to inspect transactions in a more human-readable format.
TypeScript

Dump values from a contract

There are three TVM debug instructions: DUMPSTK, STRDUMP, and DUMP. These instructions are wrapped in functions with different names in each language:
  • Tolk: Functions on a global debug object.
  • FunC: Global functions from stdlib.fc.
  • Tact: dumpStack for DUMPSTK and the dump function for the other two. Tact also prints the exact line where dump is called, so it can quickly be found in the code.

Explore TVM logs

TypeScript
Of all verbosity levels, the two are the 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 attempts to load a 64-bit integer from the slice using LDU 64. Since there is not enough data, execution stops with exit code 9. Inspect the same code with the vm_logs_full verbosity level. The output is heavily truncated at the top.
Stack is printed as [bottom, ..., top], where top is the top of the stack. Here, the stack contains two values:
  • Top: the slice from which data is being read — CS{Cell{...} bits: 711..725; refs: 2..2}
  • Bottom: an integer value — 500000000
However, the slice contains only 725 bits, of which 711 bits and both references have already been read. The contract attempted to read 64 more bits, but the slice did not contain enough remaining data. In FunC, locate the load_uint(64) call causing 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 trace

For traces that are not too large, print all transactions and inspect them.
TypeScript
For large traces, 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 the browser.
Also, these tools allow to explore logs of each transaction.

Debugging with TVM Retracer

Even when a contract executes successfully (exit code = 0) with no errors, the actions may not produce the expected on-chain result. TVM Retracer replays the transaction and displays VM-level execution in detail.

Scenarios for retracing

  • All execution phases complete without errors, yet the expected outcome is missing.
  • An action is skipped, or a transfer does not reach its destination.
  • A step-by-step view of how the TVM executes contract logic is required, i.e. to trace a bug in a high-level smart-contract language compiler.

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.