Усі статті
7 хв читання

Що таке decimals у жетона на TON (USDT = 6, а не 9)

Розбираємо decimals жетонів TON: чому в USDT 6 знаків, а в більшості жетонів 9, як get_jetton_info повертає decimals і чому це критично для балансів

decimals жетонаUSDT на TONметадані жетонаTEP-64TON MCPraw-одиниці

Ви пишете агента, який показує баланс 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 000 raw). Переплутати = промах у 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 зап/хв, картка не потрібна.