Що таке 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 зап/хв, картка не потрібна.