Что такое decimals у жетона на TON (USDT = 6, а не 9)
Разбираем decimals у жетона на TON: почему USDT = 6, а большинство жетонов = 9, как get_jetton_info возвращает decimals и почему это критично для балансов
Вы пишете агента, который показывает баланс USDT. Дёргаете баланс джеттон-кошелька, получаете 5000000, делите на 10^9 — и на экране появляется 0.005 USDT вместо честных 5 USDT. Транзакции проходят, RPC отвечает, get-метод возвращает число — а сумма всё равно промахнулась ровно в 1000 раз. Виновата одна цифра, которую почти все хардкодят по привычке: decimals.
Если вы подключаете ИИ-агента к TON или считаете суммы жетонов руками, decimals — это первое, что нужно понять до любых балансов и свапов. Разберём, почему у USDT decimals = 6, а не привычные для TON 9, откуда вообще берётся это число и как получить его одним вызовом, не угадывая.
decimals жетона на TON: raw-единицы против человекочитаемого числа
Блокчейн не умеет хранить дроби. Вообще. На TON, как и почти везде, любая сумма лежит в контракте как целое число — так называемые raw-единицы (минимальные неделимые единицы). Никаких 5.5 в словаре контракта нет и быть не может — есть только целое вроде 5500000.
Чтобы из этого целого получить сумму, которую видит человек, нужен масштаб. Этот масштаб и есть decimals — степень десятки, на которую сдвинуто хранимое целое относительно человекочитаемой суммы:
human = raw / 10^decimals
raw = human * 10^decimals
Аналогия простая. Деньги в вашем кошельке — в рублях, но в бухгалтерии банка всё считается в копейках, целыми числами: 100 копеек = 1 рубль, то есть decimals = 2. Хочешь показать человеку рубли — делишь копейки на 10^2 = 100. С жетонами то же самое, только степень десятки другая.
Ключевой момент: сам блокчейн не знает, где у числа «запятая». Он оперирует целыми raw-единицами. Куда ставить запятую при показе пользователю — решает клиент, читая decimals из метаданных жетона. Ошибся с decimals — запятая уехала, сумма превратилась в тыкву.
USDT decimals на TON = 6, а большинство жетонов = 9
Две цифры, которые надо запомнить:
- USDT (Tether) на TON →
decimals = 6. Значит1 USDT = 1 000 000 raw-единиц. - Большинство остальных жетонов TON →
decimals = 9. Это же значение — по умолчанию: если полеdecimalsвообще не указано в метаданных, клиент по стандарту обязан считать его равным9.
Откуда взялась девятка. Сам GRAM (бывший Toncoin, переименован в июне 2026 — сеть по-прежнему называется TON) имеет 9 знаков: 1 GRAM = 10^9 нанограммов, и «нано» здесь и есть raw-единица. Стандарт жетонов TON унаследовал это значение как дефолт, и подавляющее большинство токенов в сети живут именно с decimals = 9. Разработчики привыкают писать / 1e9 не глядя.
А USDT — гость из мира Ethereum, где у Tether исторически 6 знаков. Эмитент сохранил привычную точность и на TON. Поэтому USDT — то самое исключение, на котором спотыкается почти каждый, кто «просто захардкодил 9», — и это ровно самый ходовой жетон для платежей.
Посчитаем цену ошибки. Пусть в джеттон-кошельке лежит 5 000 000 raw-единиц USDT:
- правильно (
decimals = 6):5 000 000 / 10^6 = 5 USDT; - неправильно (
decimals = 9):5 000 000 / 10^9 = 0.005.
Промах ровно в 10^(9−6) = 1000 раз. И наоборот: если пользователь вводит «отправить 5 USDT», а вы умножаете на 10^9, вы попытаетесь двинуть в 1000 раз больше. Для платёжного бота это разница между «оплачено» и «отклонено».
Откуда берётся decimals: метаданные жетона и стандарт TEP-64
Важно понять: decimals — это не поле в коде кошелька и не константа протокола. Это часть метаданных конкретного жетона, описанных стандартом TEP-64 (Token Data Standard). Каждый жетон сам заявляет свой decimals, имя, символ, картинку.
TEP-64 разрешает хранить метаданные в трёх форматах:
- on-chain — все поля лежат в словаре (dictionary) прямо в контракте жетона; скачивать ничего не надо;
- off-chain — в контракте только ссылка (
uri), а весь JSON с полями (name,symbol,decimals,image) лежит по этому URI на веб-сервере или в IPFS; - semi-chain (гибрид) — часть полей в он-чейн-словаре, часть по
uri; клиент скачивает off-chain-контент и мержит его со значениями из словаря.
По TEP-64 правило слияния такое: если в словаре присутствует ключ uri, клиент обязан скачать off-chain-контент по ссылке и объединить его со значениями из он-чейн-словаря. Разные форматы — разная цена чтения: on-chain читается одним get-методом, off-chain требует ещё и HTTP-запроса. Хендлить эти три случая руками — отдельное удовольствие, и как раз здесь удобно, когда за вас это делает один инструмент.
Метаданные USDT: как get_jetton_info отдаёт decimals = 6
USDT на TON хранит метаданные в off-chain-формате: в контракте мастер-жетона лежит ссылка на off-chain JSON, а уже в этом JSON записаны name, symbol и, главное, decimals = 6. Чтобы честно собрать карточку жетона, клиенту нужно прочитать контракт, достать ссылку, скачать по ней JSON и разобрать поля.
Держать в голове формат TEP-64 и парсить ячейки словаря вручную не нужно: это ровно та работа, которую забирает на себя один инструмент. get_jetton_info сам сходит в контракт, разберёт метаданные и вернёт готовое decimals = 6 — а на нём уже строится вся арифметика.
Как получить decimals одним вызовом: get_jetton_info
Вместо того чтобы вручную читать словарь, распознавать формат (on/off/semi-chain), ходить по URI и мержить JSON — есть get_jetton_info. Это инструмент MCP-сервера TONNode: он принимает адрес мастер-контракта жетона и возвращает уже собранные метаданные — имя, символ, decimals и эмиссию.
TONNode — это hosted MCP-сервер для TON. MCP (Model Context Protocol) — стандарт, по которому ИИ-агенты (Claude, Cursor, ChatGPT/Codex, любой MCP-клиент) вызывают инструменты. Подключить бесплатно локально, полный набор чтения:
{
"mcpServers": {
"ton": { "command": "npx", "args": ["-y", "@tonnode/mcp"] }
}
}
Пакет @tonnode/mcp — open source (MIT), работает по нативному ADNL-протоколу TON, без HTTP-прослоек. Дальше агенту достаточно обычной просьбы на человеческом языке:
Через инструмент
get_jetton_infoузнайdecimalsжетона USDT на TON (адрес мастер-контракта такой-то) и посчитай, сколько это в человекочитаемых USDT для баланса 5 000 000 raw.
get_jetton_info вернёт decimals = 6, и вся дальнейшая арифметика строится на этом числе, а не на предположении. Тот же вызов для произвольного жетона вернёт его собственное значение (чаще 9, но проверять надо всегда). Главное правило:
Не хардкодьте
9. Читайтеdecimalsизget_jetton_infoдля каждого жетона.
Девятка сработает для большинства токенов и молча сломается на USDT — а это ровно тот жетон, где чаще всего крутятся реальные деньги. Зачем ИИ-агенту вообще отдельный MCP-доступ к TON, а не публичный RPC с лимитами, разобрано в заметке про MCP-сервер для агентов на TON.
Почему неверный decimals ломает балансы и свапы
decimals — это не косметика для показа. Он входит во все места, где сумма пересекает границу «raw ⇄ человек», и ошибка распространяется на всю цепочку.
Балансы
get_jetton_balance возвращает баланс жетона в raw-единицах (нужный джеттон-кошелёк вычисляется он-чейн, вам не надо его искать самим). Само по себе это целое число ничего не значит без decimals — показать его пользователю правильно нельзя, пока вы не поделите на 10^decimals:
raw = 5 000 000
decimals = 6 → 5 USDT ✅
decimals = 9 → 0.005 ❌ (в 1000 раз меньше)
Правильный порядок в агенте: сначала get_jetton_info → взять decimals, потом get_jetton_balance → поделить raw на 10^decimals. А как публичные лайтсерверы упираются в лимиты на таких запросах чтения — в разборе лимитов публичных лайтсерверов TON.
Свапы
get_swap_quote отдаёт твёрдую котировку DEX GRAM⇄жетон (через протокол Omniston, ликвидность STON.fi + DeDust) — и вводимую сумму, и оценку получаемого — тоже в raw-единицах жетона. Здесь неверный decimals бьёт дважды. Допустим, пользователь хочет свапнуть 10 USDT: при decimals = 6 в котировку надо передать 10 000 000 raw. Применили по ошибке 9 — запросили 10 000 000 000 raw, то есть свап на 10 000 USDT, которых на кошельке нет. Обратная ошибка при разборе ответа занизит ожидаемую сумму в 1000 раз, и пользователь решит, что курс грабительский.
Тот же принцип действует в кроссчейн-котировках и в любых транзакциях: на блокчейне всё в целых raw-единицах, и единственный мост к человекочитаемым суммам — правильный decimals.
Проверка на практике: get_jetton_balance и get_swap_quote
Соберём короткий сценарий, который агент прогоняет сам, без единой захардкоженной цифры.
Пошаговый сценарий
1. Узнать decimals. get_jetton_info(USDT master) → decimals = 6.
2. Прочитать баланс. get_jetton_balance(владелец, USDT master) → 5 000 000 (raw). Считаем: 5 000 000 / 10^6 = 5 USDT. Если бы взяли 9 — получили бы 0.005. Это ваш сигнальный тест: увидели неожиданно крошечное число у USDT — первым делом проверьте, не применили ли 9 вместо 6.
3. Запросить котировку. Хотим свапнуть 5 USDT в GRAM. В raw это 5 × 10^6 = 5 000 000 — отдаём именно эту сумму в get_swap_quote. Результат тоже придёт в raw, а GRAM — 9-знаковый, значит делим ответ на 10^9, чтобы показать пользователю.
4. Не доверяете метаданным? Можно перепроверить он-чейн напрямую. run_get_method вызывает любой read-only get-метод контракта — тем же способом можно дёрнуть get_jetton_data у мастер-контракта и убедиться, что цифры сходятся. Это гибче, но требует знания формата TEP-64 и парсинга словаря вручную; когда важна скорость и надёжность, get_jetton_info закрывает вопрос одним ответом. Чем RPC-провайдеры TON отличаются по доступу к get-методам и архиву — в честном сравнении RPC-провайдеров TON.
Промпт агенту
Промпт для всего сценария выглядит буднично:
Возьми USDT на TON. Через
get_jetton_infoпрочитайdecimals. Черезget_jetton_balanceвозьми мой баланс в raw и переведи в человекочитаемые USDT. Потом черезget_swap_quoteпосчитай котировку на свап 5 USDT в GRAM — не забудь перевести 5 USDT в raw по прочитанномуdecimals.
Ещё деталь для агентных сценариев: инструменты свапа TONNode строго некастодиальны. Сервер никогда не подписывает и не хранит ключи — build_swap_tx возвращает неподписанное TonConnect-сообщение, которое подписывает кошелёк пользователя. Даже правильный decimals не даёт серверу контроля над средствами: он только считает и формирует транзакцию. Где в свапах на TON теряются миллисекунды и задержки — в разборе скоростного трейдинга на TON.
Коротко
decimals— степень десятки между raw-единицами в блокчейне и человекочитаемой суммой:human = raw / 10^decimals.- Дефолт в TON — 9 знаков; USDT — 6 (
1 USDT = 1 000 000raw). Перепутать = промах в 1000 раз. decimalsживёт в метаданных жетона (TEP-64), а не в коде. У USDT метаданные хранятся off-chain, ноget_jetton_infoвсё равно отдаётdecimals = 6одним ответом.- Не хардкодьте
9. Читайтеdecimalsчерезget_jetton_infoи стройте на нём все балансы (get_jetton_balance) и котировки (get_swap_quote).
Возьмите бесплатный ключ Hobby (60 запросов/мин, без карты) и читайте decimals любого жетона через get_jetton_info прямо сейчас: https://tonnode.io/dashboard?plan=hobby
Дайте вашему агенту доступ к TON
16 MCP-инструментов: чтение, некастодиальные свапы, кроссчейн и кошельки. Бесплатный тариф — 60 запр/мин, карта не нужна.