Работа с ячейками вручную в FunC

Соберёшь и разберёшь cell в FunC: биты, ссылки, builder/slice и типичные ловушки end_parse.

Ячейки в FunC

Всё в TON — cells: код, данные, сообщения. FunC даёт прямой доступ: begin_cell / store_* пишут builder, begin_parse / load_* читают slice. Лимит одной cell — 1023 бита и до 4 ссылок на другие cells.


Упаковка заголовка и payload

func (fallback highlight)
;; FunC + stdlib из Blueprint
#include "imports/stdlib.fc";

;; Схема:
;; header: op:uint32 query_id:uint64
;; payload: amount:coins memo_hash:uint256
;; ref0: metadata cell

cell pack_transfer(int op, int query_id, int amount, int memo_hash, cell metadata) impure {
    return begin_cell()
        .store_uint(op, 32)
        .store_uint(query_id, 64)
        .store_coins(amount)
        .store_uint(memo_hash, 256)
        .store_ref(metadata)
        .end_cell();
}

(int, int, int, int, cell) unpack_transfer(slice s) impure {
    int op = s~load_uint(32);
    int query_id = s~load_uint(64);
    int amount = s~load_coins();
    int memo_hash = s~load_uint(256);
    cell metadata = s~load_ref();
    s.end_parse(); ;; не оставляй «хвост» непрочитанным
    return (op, query_id, amount, memo_hash, metadata);
}

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

    var (op, query_id, amount, memo_hash, metadata) = unpack_transfer(in_msg_body);
    ;; дальше — бизнес-логика; metadata.begin_parse() при необходимости
    throw_unless(100, amount > 0);
    throw_unless(101, op == 0x12345678);

    set_data(
        begin_cell()
            .store_uint(query_id, 64)
            .store_uint(memo_hash, 256)
            .store_ref(metadata)
            .end_cell()
    );
}

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

  • store_coins / load_coins — VarUInteger для nanotons; не путай с фиксированным store_uint(..., 64).
  • store_ref / load_ref — выносит большие данные во вторую cell, когда не влезаешь в 1023 бита.
  • end_parse() — страховка от частично прочитанного slice (лишние биты = ошибка, а не молчаливый успех).
  • Порядок store_* должен один в один совпадать с load_* у всех участников протокола.
  • in_msg_full и in_msg_body — разные cells: флаги/адрес отправителя в full, полезная нагрузка в body.

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

Положил 5+ ref в одну cell → TVM отвергнет; дроби дерево ячеек.

Считал slice_bits / глубину «на глаз» → превышаешь лимит на этапе end_cell или при отправке сообщения.

Смешал load_uint(256) и load_msg_addr() для адреса → адрес в TON — не «просто 256 бит», парси msg address API stdlib.


Что дальше

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