Первый контракт на FunC: структура файла

Соберёшь минимальный FunC-контракт: load/save data, recv_internal, get-методы с method_id.

Структура FunC-контракта

FunC не прячет storage и сообщения за синтаксисом языка: ты сам парсишь cell, пишешь recv_internal и помечаешь get-методы через method_id. Это тот же Counter, что в статье про Tact — только с явным контролем байткода.


Пишем Counter на FunC

func (fallback highlight)
;; FunC (ton-community/func) — синтаксис совместим с Blueprint @ton/blueprint ^0.27
;; Файл: contracts/counter.fc
#include "imports/stdlib.fc";

const op::increase = "op::increase"c;

global int ctx_id;
global int ctx_counter;

() load_data() impure {
    slice ds = get_data().begin_parse();
    ctx_id = ds~load_uint(32);
    ctx_counter = ds~load_uint(32);
    ds.end_parse();
}

() save_data() impure {
    set_data(
        begin_cell()
            .store_uint(ctx_id, 32)
            .store_uint(ctx_counter, 32)
            .end_cell()
    );
}

() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
    if (in_msg_body.slice_empty?()) {
        return ();
    }

    slice cs = in_msg_full.begin_parse();
    int flags = cs~load_uint(4);
    if (flags & 1) { ;; bounced
        return ();
    }

    load_data();

    int op = in_msg_body~load_uint(32);
    int query_id = in_msg_body~load_uint(64); ;; стандартный заголовок TL-B сообщений

    if (op == op::increase) {
        int increase_by = in_msg_body~load_uint(32);
        ctx_counter += increase_by;
        save_data();
        return ();
    }

    throw(0xffff); ;; unknown op
}

int get_counter() method_id {
    load_data();
    return ctx_counter;
}

int get_id() method_id {
    load_data();
    return ctx_id;
}

Как это работает

  • #include "imports/stdlib.fc" — стандартная библиотека FunC (ячейки, слайсы, отправка).
  • "op::increase"c — crc32-хэш строки как opcode; альтернатива — руками задать 0x....
  • get_data / set_data — единственное persistent storage контракта (одна корневая cell).
  • recv_internal — вход для внутренних сообщений; пустое тело обычно игнорируют (деплой).
  • flags & 1 — бит bounce: отскочившее сообщение не должно повторно менять логику, если нет отдельной обработки.
  • method_id — регистрирует get-метод для off-chain вызова по имени/method_id.

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

Забыл save_data() после изменения → в storage останется старое значение, хотя в транзакции счётчик «увеличился» в памяти.

Не вызвал end_parse() → лишние биты в storage/теле пройдут незамеченными до странных багов позже.

Смешал порядок полей в load_data и save_data → молча прочитаешь мусор; всегда держи одну схему рядом.


Что дальше

Материалы gramdocs.tech носят образовательный характер и не являются финансовой, юридической или инвестиционной рекомендацией. Работа с блокчейном TON и токеном Gram связана с рисками потери средств. Правовая информация