Перейти к содержанию
Learning Platform
Глоссарий Troubleshooting
Урок 05.04 · 25 мин
Средний
TolkСинтаксисSmart ContractsTVM

Синтаксис Tolk

Синтаксис Tolk спроектирован так, чтобы быть знакомым разработчикам из мира TypeScript, Swift и Kotlin, при этом сохраняя полный доступ к возможностям TVM. Знание синтаксических конструкций Tolk позволяет быстро писать и читать контракты среднего уровня, не погружаясь в низкоуровневые особенности FunC. Это особенно важно для проектов, которым нужен более тонкий контроль над газом, чем предоставляет Tact, но без сложности FunC.

В этом уроке мы разберём синтаксис Tolk во всех деталях и напишем полноценный смарт-контракт. Tolk сочетает знакомый синтаксис с полным доступом к возможностям TVM.


Объявление функций

Tolk предлагает несколько видов функций:

// Обычная функция
fun add(a: int, b: int): int {
    return a + b;
}

// Get-метод (доступен для внешних вызовов)
get fun counter(): int {
    return getContractData().beginParse().loadUint(32);
}

// Asm-функция (встраивание TVM-инструкций)
asm fun myCustomOp(x: int, y: int): int {
    ADD
}

Точка входа контракта

Главная функция контракта — onInternalMessage:

fun onInternalMessage(myBalance: int, msgValue: int, msgFull: cell, msgBody: slice) {
    // Обработка входящего сообщения
}

Для внешних сообщений:

fun onExternalMessage(inMsg: slice) {
    // Обработка внешнего сообщения
    acceptMessage();
}

Переменные и типы

val и var

val x = 42;            // Неизменяемая, тип int выведен автоматически
var counter = 0;        // Изменяемая
counter += 1;           // OK

val name: slice = cs.loadBits(256);  // Явное указание типа

Система типов

ТипОписаниеПример
int257-битное целое числоval x: int = 42;
boolЛогическое значениеval flag: bool = true;
cellЯчейка TVMval c: cell = beginCell().endCell();
sliceУказатель чтенияval s: slice = c.beginParse();
builderКонструктор ячейкиval b: builder = beginCell();
tupleУпорядоченная коллекцияval t: tuple = createEmptyTuple();
voidОтсутствие значенияДля функций без возврата
// Вывод типов -- компилятор определяет тип автоматически
val balance = 1000000000;              // int
val data = getContractData();          // cell
val cs = data.beginParse();            // slice
val flag = true;                       // bool

Управление потоком

Условия

if (balance > minBalance) {
    sendCoins(recipient, amount);
} else {
    throw 101;  // Недостаточно средств
}

Циклы

// while
var i = 0;
while (i < 10) {
    // ...
    i += 1;
}

// do-while
var attempts = 0;
do {
    attempts += 1;
    // Попытка операции...
} while (!success && attempts < 3);

// repeat -- фиксированное количество итераций
repeat (64) {
    // Выполнится ровно 64 раза
}

Обработка ошибок

// throw -- бросить исключение с кодом
throw 401;                          // Безусловно

// assert -- проверка условия
assert(senderAddr == ownerAddr, 401);   // Если false, throw 401

// try-catch -- перехват исключений
try {
    val result = riskyOperation();
} catch (exitCode) {
    // exitCode -- числовой код ошибки TVM
    handleError(exitCode);
}

Операции с Cell

Tolk использует точечный синтаксис для всех операций с ячейками:

Создание ячейки (Builder)

val msg = beginCell()
    .storeUint(0x18, 6)           // Флаги сообщения
    .storeSlice(destAddr)         // Адрес получателя
    .storeCoins(amount)           // Сумма
    .storeUint(0, 107)            // Служебные поля
    .storeSlice(body)             // Тело сообщения
    .endCell();

Чтение ячейки (Slice)

val cs = data.beginParse();
val op = cs.loadUint(32);           // Operation code
val queryId = cs.loadUint(64);      // Query ID
val addr = cs.loadAddress();        // Адрес
val coins = cs.loadCoins();         // Монеты (VarUInt16)
val ref = cs.loadRef();             // Ссылка на другую ячейку

Проверки

if (cs.isEndOfSlice()) {
    // Все данные прочитаны
}

val bitsLeft = cs.bitsLeft();       // Оставшиеся биты
val refsLeft = cs.refsLeft();       // Оставшиеся ссылки

Строковые литералы

Tolk поддерживает строковые литералы для удобной работы с данными:

// Строковый литерал как slice
val greeting = "Hello, TON!";

// Хранение строки в ячейке
val cell = beginCell()
    .storeSlice("increment")
    .endCell();

Полный пример: Counter контракт

Объединим все элементы в полноценный контракт:

// Counter -- простой контракт со счётчиком
// Хранит: counter (uint32)
// Операции: increment (op=1), decrement (op=2), set (op=3)

const OP_INCREMENT = 1;
const OP_DECREMENT = 2;
const OP_SET = 3;

fun loadCounter(): int {
    val ds = getContractData().beginParse();
    return ds.loadUint(32);
}

fun saveCounter(value: int) {
    setContractData(
        beginCell().storeUint(value, 32).endCell()
    );
}

fun onInternalMessage(myBalance: int, msgValue: int, msgFull: cell, msgBody: slice) {
    if (msgBody.isEndOfSlice()) {
        return;  // Пустое сообщение -- просто получение TON
    }

    val op = msgBody.loadUint(32);
    val queryId = msgBody.loadUint(64);

    if (op == OP_INCREMENT) {
        val current = loadCounter();
        saveCounter(current + 1);
        return;
    }

    if (op == OP_DECREMENT) {
        val current = loadCounter();
        assert(current > 0, 400);  // Счётчик не может быть отрицательным
        saveCounter(current - 1);
        return;
    }

    if (op == OP_SET) {
        val newValue = msgBody.loadUint(32);
        saveCounter(newValue);
        return;
    }

    throw 0xffff;  // Неизвестная операция
}

get fun counter(): int {
    return loadCounter();
}

get fun version(): int {
    return 1;
}
TIP

Tolk код можно скомпилировать прямо в браузере с помощью встроенного редактора на платформе. Попробуйте изменить контракт и посмотреть результат компиляции.

Частые ошибки

  1. Путают синтаксис Tolk и Tact: хотя оба языка имеют высокоуровневый синтаксис, они компилируются по-разному и имеют разные абстракции.
  2. Забывают о точке с запятой в конце выражений: в отличие от Tact, Tolk требует явного завершения statements.
  3. Не используют типизацию: Tolk поддерживает опциональную типизацию, и её отсутствие лишает вас проверок на этапе компиляции.
  4. Копируют паттерны FunC вместо использования идиоматичного синтаксиса Tolk — например, используют тильда-методы вместо нового синтаксиса.

Проверка знанийKnowledge check
In Tolk, how does the `fun` keyword combined with type annotations (e.g., `fun add(a: int, b: int): int`) differ from FunC's function declaration style, and what practical advantage does this provide?
ОтветAnswer
FunC uses C-style declarations where the return type precedes the function name (`int add(int a, int b)`), while Tolk places the return type after a colon at the end. This makes Tolk functions self-documenting and easier to scan -- you immediately see the name and parameters, with the return type as an explicit annotation rather than a prefix that can be confused with variable declarations.

Итоги

  • Функции объявляются через fun, get-методы через get fun
  • val — неизменяемая переменная, var — изменяемая
  • Типы: int, bool, cell, slice, builder, tuple
  • Управление потоком: if/else, while, do/while, repeat
  • Cell-операции используют точечный синтаксис: cs.loadUint(32) вместо cs~load_uint(32)
  • throw и assert — механизмы обработки ошибок
  • try/catch — перехват исключений TVM

В следующем уроке мы разберём оптимизацию газа — критически важный навык для production-контрактов на TON.

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 6