Book
Debugging

Debugging Tact Contracts

Tact has first-class support for Jest and @tact-lang/emulator for contract tests and debugging. The preferred type of test is the one with inline snapshots. They are easy to write and easy to debug.

For a 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 FunC that allows you to print out data in your contract. It is very useful for debugging.

To make dump work you need to enable the 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 Node.js 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 messages. NOTE: You can enqueue multiple messages in a row
await contract.send(treasure, { value: toNano(1) }, { $$type: "Deploy", queryId: 0n });
await contract.send(treasure, { value: toNano(1) }, { $$type: "Increment" });
 
// Run the 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 the yarn jest command, the line with toMatchInlineSnapshot of the test will be automatically 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 were changed. To update them, you need to run the yarn jest -u command.