Language
Guides
Debugging

Debugging Tact Contracts

Tact has a first-class support for Jest and @tact-lang/emulator for contract tests and debugging. Prefered type of tests are the one with inline snapwshots. They are easy to write and easy to debug.

For quick start, it is better to use tact-template that has everything built in.

Printing out debug data

Tact has a built-in dump function that is similar to the one in Fun that allows you to print out data in your contract. It is very useful for debugging.

To make dump work you need to enable feature debug in tact.conf.json.

Example:

dump("Hello World");
dump(123 + 444);
dump(a != null);

Using @tact-lang/emulator

Ton Emulator allows you to have a small virtual blockchain in your nodejs code. This library is built specifically for testing smart contracts in unit tests.

import { ContractSystem } from '@tact-lang/emulator';
import { sample_Contract } from './output/sample_Contract';
 
//
// Init System
//
 
// Contract System is a virtual environment that emulates the TON blockchain
const system = await ContractSystem.create();
 
// Treasure is a contract that has 1m of TONs and is a handy entry point for your smart contracts
let treasure = await system.treasure('name of treasure');
 
//
// Open contract
//
 
// Contract itself
let contract = system.open(sample_Contract.fromInit(treasure.address));
 
// This object would track all transactions in this contract
let tracker = system.track(contract.address);
 
// This object would track all logs
let logger = system.log(contract.address);
 
//
// Sending a message
//
 
// First we enqueue a messages. NOTE: You can enqueue multiple messages in row
await contract.send(treasure, { value: toNano(1) }, { $$type: "Deploy", queryId: 0n });
await contract.send(treasure, { value: toNano(1) }, { $$type: "Increment" });
 
// Run system until there are no more messages
await system.run();
 
//
// Collecting results
//
 
console.log(track.collect()); // Prints out all transactions in contract
console.log(logger.collect()); // Prints out all logs for each transaction
 
//
// Invoking get methods
//
 
let counter = await contract.getCounter();
console.log(counter); // Prints out counter value
 

Snapshot testing with jest

One of the most powerful features of Jest is the ability to write snapshot tests. Snapshot tests are a great way to test your contracts.

Initial setup

Example of a minimal test file:

import { ContractSystem } from '@tact-lang/emulator';
import { sample_Contract } from './output/sample_Contract';
 
describe("contract", () => {
    it("should deploy correctly", async () => {
 
        // Init test
        const system = await ContractSystem.create();
        const treasure = await system.treasure('my treasure');
        const contract = system.open(sample_Contract.fromInit(treasure.address));
        const tracker = system.track(contract.address);
 
        // Send a message
        await contract.send(treasure, { value: toNano(1) }, { $$type: "Deploy", queryId: 0n });
        await system.run();
 
        // Testing output
        expect(tracker.collect()).toMatchInlineSnapshot();
    });
});

Generating snapshots

After running yarn jest command, the line with toMatchInlineSnapshot of the test will be automagically updated with a snapshot of the output.

// ...
        expect(tracker.collect()).toMatchInlineSnapshot(`
            [
              {
                "$seq": 0,
                "events": [
                  {
                    "$type": "deploy",
                  },
                  {
                    "$type": "received",
                    "message": {
                      "body": {
                        "cell": "x{946A98B60000000000000000}",
                        "type": "cell",
                      },
                      "bounce": true,
                      "from": "kQAI-3FJVc_ywSuY4vq0bYrzR7S4Och4y7bTU_i5yLOB3A6P",
                      "to": "kQBrSAP2y7QIUw4_1q0qciHfqdFmOYR9CC1oinn7kyWWRuoV",
                      "type": "internal",
                      "value": 1000000000n,
                    },
                  },
                  {
                    "$type": "processed",
                    "gasUsed": 6077n,
                  },
                  {
                    "$type": "sent",
                    "messages": [
                      {
                        "body": {
                          "cell": "x{AFF90F570000000000000000}",
                          "type": "cell",
                        },
                        "bounce": true,
                        "from": "kQBrSAP2y7QIUw4_1q0qciHfqdFmOYR9CC1oinn7kyWWRuoV",
                        "to": "kQAI-3FJVc_ywSuY4vq0bYrzR7S4Och4y7bTU_i5yLOB3A6P",
                        "type": "internal",
                        "value": 992727000n,
                      },
                    ],
                  },
                ],
              },
            ]
        `);
// ...

Updating snapshots

When you change your contract, your snapshots will be outdated. For example, gas usage or addresses was changed. To update them, you need to run yarn jest -u command.