Язык схем TL-B
TL-B (Type Language - Binary) — это язык описания структур данных в TON, играющий ту же роль, что и ABI в Ethereum или Protobuf в gRPC. Каждое сообщение, каждая ячейка состояния, каждый блок в TON описывается схемой TL-B. Умение читать и писать TL-B схемы — обязательный навык для работы с низкоуровневыми API, парсинга транзакций и взаимодействия с системными контрактами.
В уроке о ячейках и BoC (M01-L04) мы узнали, что все данные в TON хранятся в ячейках (cells) — структурах с битами и ссылками. Но как понять, что именно записано в ячейке? Какие биты отвечают за адрес, какие — за сумму, а какие — за код операции? Ответ — TL-B (Type Language - Binary), формальный язык описания схем данных TON.
TL-B — это “система типов” всей экосистемы TON. Каждая структура ячеек, каждый формат сообщений и каждое хранилище контрактов описаны в TL-B. Понимание TL-B необходимо для чтения стандартов TEP, разбора on-chain данных и понимания протокола на глубоком уровне.
Зачем нужен TL-B
Если ячейки — это формат хранения (контейнер из битов и ссылок), то TL-B — это схема содержимого (что означает каждый бит внутри ячейки).
TL-B нужен для:
- Чтения стандартов TEP — TEP-74 (Jettons), TEP-62 (NFTs) и другие стандарты описывают свои интерфейсы именно через TL-B-схемы
- Парсинга on-chain данных — чтобы прочитать содержимое ячейки, нужно знать её TL-B-схему
- Понимания форматов сообщений — internal messages, external messages, state_init и другие протокольные структуры определены в TL-B
Связь с предыдущими уроками: в M01-L04 мы разобрали, как данные упаковываются в ячейки (cells) и сериализуются в BoC. В M02-L03 мы изучили типы сообщений (internal, external). TL-B — это язык, который формально описывает структуру всех этих данных.
Базовый синтаксис
Конструкторы
Основная единица TL-B — конструктор. Он описывает один вариант структуры данных:
constructor_name$tag field1:type1 field2:type2 = TypeName;
constructor_name— имя конструктора (для людей)$tag— двоичный тег-префикс, по которому парсер различает вариантыfield1:type1— поля с типамиTypeName— результирующий тип (после=)
Пример: простое сообщение с кодом операции и данными:
transfer$0x5fcc3d14 query_id:uint64 new_owner:MsgAddress
response_destination:MsgAddress custom_payload:(Maybe ^Cell)
forward_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell)
= InternalMsgBody;
Теги
Теги — это двоичные префиксы, позволяющие различать несколько конструкторов одного типа:
| Формат тега | Пример | Описание |
|---|---|---|
$0, $1 | _ $0 = Bool; | Один бит: 0 или 1 |
$10, $11 | a$10 = X; b$11 = X; | Два бита |
#cc | ext_message#cc ... | Hex-тег (автоматически переводится в биты) |
| (без тега) | _ field:Type = Name; | CRC32 от определения (автоматически) |
Пример: тип Bool с двумя конструкторами:
bool_false$0 = Bool;
bool_true$1 = Bool;
Парсер читает один бит: если 0 — это bool_false, если 1 — bool_true.
Встроенные типы
| Тип | Описание |
|---|---|
# | uint32 (32 бита, беззнаковое) |
## N | N-битное беззнаковое целое |
uint1..uint256 | Беззнаковые целые от 1 до 256 бит |
int1..int257 | Знаковые целые от 1 до 257 бит |
bits1..bits1023 | Битовые строки фиксированной длины |
Cell | Произвольная ячейка |
Any | Любой тип (полиморфный) |
Ссылки и вложенные ячейки
Ячейка может содержать до 1023 бит и до 4 ссылок на другие ячейки. В TL-B ссылки обозначаются оператором ^:
^TypeName — данные типа TypeName хранятся в ссылочной ячейке
Пример: структура с телом сообщения в отдельной ячейке:
message$_ info:CommonMsgInfo
init:(Maybe (Either StateInit ^StateInit))
body:(Either X ^X) = Message X;
Здесь ^StateInit означает, что StateInit хранится в отдельной ячейке (по ссылке), а не inline в текущей.
Можно также определить содержимое ссылочной ячейки inline:
^[ field1:uint32 field2:uint64 ]
Это эквивалентно созданию анонимного типа с указанными полями в отдельной ячейке.
Условные поля и ограничения
Условные поля
Поле может присутствовать только при определённом условии:
flag:(## 1) value:(flag?uint32) = ConditionalExample;
Здесь value существует в ячейке только если flag == 1. Если flag == 0, поля value нет вообще (ни одного бита).
Ограничения значений
Фигурные скобки задают ограничения:
storage$_ x:uint32 y:uint32 { x + y <= 100 } = BoundedPair;
Ограничение { x + y <= 100 } проверяется при валидации — данные считаются невалидными, если сумма превышает 100.
Параметризованные типы
TL-B поддерживает типы с параметрами — аналог дженериков:
Hashmap n X — словарь с ключами длиной n бит и значениями типа X
Пример: хранилище балансов как словарь с 256-битными адресами:
balances:(Hashmap 256 Coins)
Это означает: Hashmap, где ключ — 256 бит (адрес), значение — тип Coins.
Другие параметризованные типы:
| Тип | Описание |
|---|---|
Maybe X | Опциональный: 1 бит-флаг + X (если 1) |
Either X Y | Один из двух: 0-бит + X или 1-бит + Y |
VarUInteger n | Переменной длины uint (длина кодируется в ceil(log2(n)) битах) |
HashmapE n X | Hashmap с пустым вариантом (может быть пустым) |
Чтение реальных TL-B схем
Рассмотрим практические примеры чтения TL-B-схем, с которыми вы столкнётесь при работе с TON.
Внутреннее сообщение (Internal Message)
int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
src:MsgAddressInt dest:MsgAddressInt
value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams
created_lt:uint64 created_at:uint32 = CommonMsgInfo;
Разбор по полям:
$0 — тег: 1 бит "0" = это internal message
ihr_disabled:Bool — 1 бит: IHR отключён (обычно true)
bounce:Bool — 1 бит: сообщение bounced-capable
bounced:Bool — 1 бит: это bounced-сообщение?
src:MsgAddressInt — адрес отправителя (переменная длина)
dest:MsgAddressInt— адрес получателя (переменная длина)
value — сумма перевода
ihr_fee, fwd_fee — комиссии (Grams = VarUInteger 16)
created_lt:uint64 — логическое время создания
created_at:uint32 — UNIX timestamp создания
Jetton Transfer (TEP-74)
transfer#0f8a7ea5 query_id:uint64 amount:(VarUInteger 16)
destination:MsgAddress response_destination:MsgAddress
custom_payload:(Maybe ^Cell)
forward_ton_amount:(VarUInteger 16)
forward_payload:(Either Cell ^Cell) = InternalMsgBody;
Разбор:
#0f8a7ea5 — 32-битный op-code (hex тег)
query_id:uint64 — 64-битный ID запроса для отслеживания
amount — сколько жетонов переводится
destination — кому переводить (адрес кошелька)
response_destination — куда отправить подтверждение
custom_payload — опциональные данные (Maybe = бит-флаг + ^Cell)
forward_ton_amount — сколько TON приложить к уведомлению
forward_payload — данные для получателя
Эта TL-B схема из TEP-74 описывает сообщение Jetton Transfer. Мы подробнее разбираем архитектуру жетонов в M05-L01 (Jetton Architecture), где эта схема используется на практике.
Внешнее сообщение кошелька v4
external_message$_ signature:bits512
^[ seqno:uint32 subwallet_id:uint32 valid_until:uint32
send_mode:uint8 message_to_send:^Cell ] = ExternalBody;
Разбор:
signature:bits512 — 512 бит подписи Ed25519
^[...] — остальное в ссылочной ячейке:
seqno:uint32 — порядковый номер (защита от replay)
subwallet_id:uint32 — ID подкошелька
valid_until:uint32 — срок действия сообщения (UNIX time)
send_mode:uint8 — режим отправки
message_to_send — внутреннее сообщение для отправки
Инструменты для работы с TL-B
Для практической работы с TL-B существует несколько инструментов:
| Инструмент | Назначение | Ссылка |
|---|---|---|
| TL-B Online Editor | Интерактивный редактор TL-B схем в браузере | ton-community.github.io/tlb-editor |
| tlb-codegen | Генерация TypeScript-кода из TL-B схем | npm: @ton-community/tlb-codegen |
| tonpy | Python-библиотека с поддержкой TL-B парсинга | pip: tonpy |
tlb-codegen особенно полезен: он принимает .tlb файл и генерирует TypeScript-функции для сериализации и десериализации данных. Это позволяет работать с on-chain данными типобезопасно, не парся биты вручную.
Чтение стандартов TEP
Стандарты TEP (TON Enhancement Proposal) используют TL-B для формального описания интерфейсов. Умение читать TL-B — ключ к пониманию любого TEP.
Как TEP-74 (Jettons) использует TL-B
TEP-74 определяет стандарт жетонов через набор TL-B-схем:
- Сообщения:
transfer,transfer_notification,burn— каждое описано TL-B-конструктором с op-кодом - Get-методы: возвращаемые значения
get_wallet_dataиget_jetton_dataописаны TL-B-схемами - Хранилище: формат persistent storage мастер-контракта и кошелька определён TL-B
Алгоритм чтения TEP с TL-B:
- Найдите раздел “Message Layouts” — там TL-B-схемы всех сообщений
- Определите op-коды (hex-теги конструкторов) — по ним контракт маршрутизирует входящие сообщения
- Прочитайте поля каждого сообщения — типы, порядок, условные поля
- Проверьте get-методы — их возвращаемые значения тоже описаны TL-B
TEP-62 (NFTs)
TEP-62 аналогично описывает NFT через TL-B:
transferсообщение для передачи NFTownership_assignedуведомление нового владельца- Get-метод
get_nft_dataвозвращает данные в формате, описанном TL-B
TL-B — это не только про сообщения. Форматы хранилища контрактов (persistent data), структуры блоков и даже формат самих транзакций в TON описаны TL-B-схемами. Это буквально “язык описания всего” в экосистеме TON.
Ключевые выводы
- TL-B — это система типов TON: каждая структура данных, формат сообщений и хранилище контрактов описаны TL-B-схемами
- Ячейки — контейнер, TL-B — содержимое: Cell определяет, как данные хранятся (биты + ссылки), TL-B определяет, что эти данные означают
- Конструкторы и теги позволяют описывать несколько вариантов одного типа и различать их по битовому префиксу
- Оператор
^означает ссылочную ячейку — данные хранятся не inline, а в отдельной ячейке - TEP-стандарты (Jettons, NFTs и другие) используют TL-B для формального описания своих интерфейсов — умение читать TL-B необходимо для работы с любым стандартом
- Инструменты (tlb-codegen, online editor) упрощают практическую работу, генерируя код из TL-B-схем
Частые ошибки
- Путают TL-B с TL (Type Language из Telegram): это родственные, но разные языки; TL-B специализирован для бинарной сериализации в ячейки TON.
- Забывают указывать размер полей в битах (например, uint32 vs uint64), что приводит к неправильной десериализации данных.
- Не учитывают выравнивание и padding при чтении данных из ячеек, поскольку TL-B работает на уровне битов, а не байтов.
- Игнорируют конструкторы (constructor tags) в TL-B, которые необходимы для различения вариантов типа при десериализации.
Проверка знанийЧто означает запись ^Cell в TL-B схеме и чем она отличается от просто Cell?
Check Your Understanding
Finished the lesson?
Mark it as complete to track your progress
Войдите чтобы оценить урок