# Debugging smart contracts with Blueprint (https://docs-dmpho5eos-ton-core-docs.vercel.app/llms/contract-dev/blueprint/debug/content.md)



<Callout type="note">
  [Acton](/llms/contract-dev/acton/content.md) is the recommended tool for new smart contract projects. Blueprint remains supported for existing projects.
</Callout>

<Callout>
  All examples from this article are available on [GitHub](https://github.com/ton-org/docs-examples/tree/main/guidebook/debug).
</Callout>

Errors in smart contracts can produce an [exit code](/llms/tvm/exit-codes/content.md), often indicating a bug in the contract. Use debugging methods to locate and fix the issue.

## Log to the console [#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.

{/* NB! There are two imports from @ton/test-utils, so that it's easier for a user to copy the one they need. */}

```ts title="TypeScript"
import '@ton/test-utils';
import { toNano } from '@ton/core';
import { Blockchain } from '@ton/sandbox';
import { Test } from './output/sample_Test';
import { findTransaction } from '@ton/test-utils';
import { flattenTransaction } from '@ton/test-utils';

const setup = async () => {
    const blockchain = await Blockchain.create();
    const owner = await blockchain.treasury('deployer');
    const contract = blockchain.openContract(
        await Test.fromInit(),
    );
    const deployResult = await contract.send(
        owner.getSender(),
        { value: toNano(0.5), bounce: true },
        null,
    );
    return { blockchain, owner, contract, deployResult };
};

it('should deploy correctly', async () => {
    const { contract, deployResult } = await setup();

    const txToInspect = findTransaction(
        deployResult.transactions,
        {
            to: contract.address,
            deploy: true,
        },
    );
    if (txToInspect === undefined) {
        throw new Error('Requested tx was not found.');
    }
    // User-friendly output
    console.log(flattenTransaction(txToInspect));
    // Verbose output
    console.log(txToInspect);
});
```

## Dump values from a contract [#dump-values-from-a-contract]

There are three TVM debug [instructions](/llms/tvm/instructions/content.md): `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`.

<Callout type="caution">
  Debug instructions consume gas and affect gas measurement. Remove them before measuring gas or deploying to production.
</Callout>

## Explore TVM logs [#explore-tvm-logs]

```ts title="TypeScript"
const blockchain = await Blockchain.create();
blockchain.verbosity.vmLogs = "vm_logs";
```

Of all [verbosity levels](/llms/contract-dev/testing/reference/content.md), 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:

```text
...
execute SWAP
execute PUSHCONT x30
execute IFJMP
execute LDU 64
handling exception code 9: cell underflow
default exception handler, terminating vm with exit code 9
```

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](/llms/tvm/exit-codes/content.md).

Inspect the same code with the `vm_logs_full` verbosity level. The output is heavily truncated at the top.

```text
...
execute PUSHCONT x30
gas remaining: 999018
stack: [ 500000000 CS{Cell{02b168008d0d4580cd8f09522be7c0390a7a632bda4a99291c435b767c95367ebe78e9af0023d36bc5f97853f4c898f868f95b035ae8f555a321d0ffce8d9f6165e2252d7a9077359400060e9fc800000000003d0902d1b85b3919} bits: 711..711; refs: 2..2} 0 Cont{vmc_std} ]
code cell hash: F9EAC82B7999AEEF696D592FE2469B9069FB05ED35C92213D7EE516F45AB97CA offset: 344
execute IFJMP
gas remaining: 999000
stack: [ 500000000 CS{Cell{02b168008d0d4580cd8f09522be7c0390a7a632bda4a99291c435b767c95367ebe78e9af0023d36bc5f97853f4c898f868f95b035ae8f555a321d0ffce8d9f6165e2252d7a9077359400060e9fc800000000003d0902d1b85b3919} bits: 711..725; refs: 2..2} ]
code cell hash: F9EAC82B7999AEEF696D592FE2469B9069FB05ED35C92213D7EE516F45AB97CA offset: 352
execute LDU 64
handling exception code 9: cell underflow
default exception handler, terminating vm with exit code 9
```

<Callout type="tip">
  To investigate the error in more detail, examine the TVM source code for the `LDU` instruction.
  Sometimes several instructions are implemented within a single `exec_*` method. For example, [`LDU`](/llms/tvm/instructions/content.md) (`load_uint`), [`LDI`](/llms/tvm/instructions/content.md) (`load_int`) and it's preload versions (`preload_uint`and`preload_int`).

  Check how `LDU` [is implemented](https://github.com/ton-blockchain/ton/blob/34823b1ea378edbe3bc59f3bcc48126480a0b768/crypto/vm/cellops.cpp#L981).
</Callout>

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](/llms/foundations/serialization/cells/content.md) 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 [#tvm-log-limits]

The size of TVM debug output depends on the verbosity level:

| Level | Setting                                                                        | Max size                   |
| ----- | :----------------------------------------------------------------------------- | :------------------------- |
| 0     | `none`                                                                         | 256 bytes &#x2A;(default)* |
| 1–4   | `vm_logs` <br /> `vm_logs_location` <br /> `vm_logs_gas` <br /> `vm_logs_full` | 1 MB                       |
| 5     | `vm_logs_verbose`                                                              | 32 MB                      |

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 [#explore-the-trace]

For traces that are not too large, print all transactions and inspect them.

```ts title="TypeScript"
const result = await contract.send(
    owner.getSender(),
    { value: toNano(0.5), bounce: true },
    null,
);
for (const tx of result.transactions) {
    console.log(flattenTransaction(tx));
}
```

For large traces, use a GUI tool. Two tools are commonly used:

* [TonDevWallet trace view](https://github.com/TonDevWallet/TonDevWallet) — requires the TonDevWallet application; does not require a custom `@ton/sandbox`; requires the `@tondevwallet/traces` package.
* [TxTracer Sandbox](/llms/tvm/tools/txtracer/content.md) — requires a custom `@ton/sandbox` package; runs in the browser.

Also, these tools allow to explore logs of each transaction.

## Inspect BoC returned by APIs [#inspect-boc-returned-by-apis]

When interacting with the blockchain using APIs, responses may include serialized cells in [BoC (bag of cells)](/llms/foundations/serialization/boc/content.md), TON's standard cell serialization format.

In API responses, BoC is typically represented as a base64-encoded string, which is not human-readable. Example API response:

```json
{
  "stack": [
    {
      "type": "cell",
      "value": "te6cckEBAQEAFwAAKSioyuboQNrK5ubCzspA0txAxsrY2Whv0fw="
    }
  ]
}
```

The `"value"` field contains a base64-encoded BoC string representing a serialized cell. To decode and inspect its structure, use [`ton-cell-abi-viewer`](https://ton-cell-abi-viewer.vercel.app/). After decoding:

```json
{
  "success": true,
  "value": {
    "kind": "Message",
    "len": 20,
    "text": "Test message in cell"
  }
}
```

### How to use [#how-to-use]

1. Copy the base64 BoC string or hex, if applicable, from the API response.
2. Paste it into the [`ton-cell-abi-viewer`](https://ton-cell-abi-viewer.vercel.app/).
3. Provide the TL-B schema for the cell, if available.
4. Inspect the decoded structure.

<Callout type="note">
  Without an explicit TL-B schema, the tool attempts to recognize common cell patterns automatically. Since BoC represents raw binary data, some cells may not be fully recognizable or may be parsed incorrectly.
</Callout>

Use this tool when API responses contain serialized cells that require structural inspection during debugging.

## Debug with TVM Retracer [#debug-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](/llms/tvm/tools/retracer/content.md) replays the transaction and displays VM-level execution in detail.

### Scenarios for retracing [#scenarios-for-retracing]

* All [execution phases](/llms/foundations/phases/content.md) 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 [#how-to-analyze-a-transaction]

1. Obtain the transaction hash from a [blockchain explorer](/llms/ecosystem/explorers/overview/content.md).
2. Open [TVM Retracer](/llms/tvm/tools/retracer/content.md) and enter the transaction hash.
3. Review the execution:
   * Inspect **Logs section** for executed instructions and exceptions.
   * Examine &#x2A;*Actions cell (C5)** to review data passed between contracts.
   * Check **message modes** — some modes can suppress errors, causing actions to be skipped.
