Barcha maqolalar
7 daq oʻqish

TONda kiruvchi USDT-toʻlovni qanday ishonchli aniqlash

TONda USDT-toʻlovni aniqlash: get_transactions orqali jetton transfer_notification tahlili, get_jetton_info dan decimals, summani tekshirish va idempotentlik.

TONda USDTtoʻlovni aniqlashtransfer_notificationTEP-74 jettonlaridempotentlikget_transactions

TONda toʻlov qabul qilishni bir marta boʻlsa ham qurgan har bir odam bir xil tuzoqqa duch kelgan: toʻlov keldi, backend esa uni «koʻrmadi». Mijoz 50 USDT joʻnatdi, hamyon tushumni koʻrsatib turibdi, invoys esa hamon «kutilmoqda» statusida osilib yotibdi. Oʻn daqiqadan keyin — qoʻllab-quvvatlash xizmatiga gʻazabli tiket. Yana bir soatdan soʻng esa maʼlum boʻladiki, siz toʻlovni ikki marta hisobga olib qoʻygansiz, chunki poller qayta urinishda (retry) yana ishlab ketgan. TONda kiruvchi USDT-toʻlovni (incoming USDT payment) ishonchli aniqlash — bu «daqiqada bir marta balansni tekshirish» emas, balki jettonning aniq ichki xabarlarini idempotentlik va soxtalashtirishdan himoya bilan tahlil qilishdir. Buni qanday toʻgʻri bajarishni bosqichma-bosqich koʻrib chiqamiz — TONNode MCP-serverining real vositalarida, ularni lokalda bepul chaqirish mumkin.

Nega TONda USDT-toʻlovni aniqlash hamyon balansi haqida emas

Sodda yondashuv — vaqti-vaqti bilan hamyon balansini oʻqib turish va agar u oshgan boʻlsa, toʻlovni qabul qilingan deb hisoblash. Bu faqat qutidagi umumiy summaga qaray oladigan kassaga oʻxshaydi: +10 USDT keldi — lekin kimdan, qaysi hisob boʻyicha, eski buyurtma uchunmi yoki yangisi uchunmi? Agar ikkita mijoz bir vaqtda 10 tadan toʻlasa, siz buni yagona +20 boʻlib koʻrasiz, xolos.

TONda bunday yondashuv bir vaqtning oʻzida bir necha sababga koʻra qulaydi:

  • USDT — nativ tanga emas, balki jetton (TEP-74 standarti). Mablagʻ asosiy hamyonga toʻgʻridan-toʻgʻri emas, uning jetton-hamyoniga — jettonning aniq master-kontraktiga bogʻlangan alohida kontraktga keladi. Asosiy hamyonning GRAMdagi balansi esa bunda umuman oʻzgarmaydi.
  • Balans deltasi kim va nima uchun toʻlaganini aytmaydi. Balans invoysga bogʻlanishni saqlamaydi.
  • Poyga va agregatsiya. Ikki soʻrov oraligʻida bir nechta toʻlov keladi — siz alohida hodisalarni emas, umumiy deltani koʻrasiz.
  • Ikki marta hisobga olish. Pollingning qayta urinishi yoki workerning qayta ishga tushishi — va bitta toʻlov ikki marta hisoblanadi.
  • Decimals. USDTda ular 6 ta, jettonlar uchun odatiy boʻlgan 9 ta emas. Bitta notoʻgʻri boʻluvchi — va mijozning hisobiga 1000 barobar xato summa «tushadi».

Toʻgʻri yoʻl — balans bilan emas, oluvchining jetton-hamyonidagi tranzaksiyalar oqimi bilan ishlash va har bir oʻtkazma hodisasini alohida tahlil qilish. Barcha oʻqish amallari lokalda bepul mavjud:

{ "mcpServers": { "ton": { "command": "npx", "args": ["-y", "@tonnode/mcp"] } } }

Kiruvchi USDT-toʻlov ichkaridan qanday koʻrinadi: jetton internal_transfer (op 0x178d4519)

Kimdir sizga USDT oʻtkazganda quyidagi zanjir ishga tushadi:

  1. Joʻnatuvchining jetton-hamyoni summani hisobdan chiqaradi va sizning jetton-hamyoningizga internal_transfer internal-xabarini (op 0x178d4519) yuboradi.
  2. Sizning jetton-hamyoningiz mablagʻni hisobga oladi va — faqat joʻnatuvchi forward_ton_amount > 0 ilova qilgan boʻlsa — qoʻshimcha ravishda sizning asosiy hamyoningizga transfer_notification ichki xabarini (op 0x7362d09c) yuboradi.

Mana shu yerda eng muhim ajralish nuqtasi yashiringan. transfer_notification — «sizga jetton keldi» degan qulay bildirishnoma, ammo u asosiy hamyonga — jetton egasining hamyoniga — va faqat forward_ton_amount > 0 boʻlgandagina yuboriladi. Agar joʻnatuvchi nol qoʻysa — jetton-hamyon mablagʻni baribir hisobga oladi, lekin egasiga bildirishnoma yuborilmaydi.

Jetton-hamyonning oʻz tarixida esa tushum har doim qayd etilgan boʻladi — forward_ton_amount dan qatʼi nazar, kiruvchi internal_transfer sifatida. Shuning uchun ishonchli aniqlash oluvchining jetton-hamyoni tarixini soʻrash va aynan internal_transfer ni tahlil qilish asosida quriladi. Uning TEP-74 boʻyicha tuzilishi:

internal_transfer#178d4519
  query_id:           uint64
  amount:             (VarUInteger 16)     // jettonning raw-birliklari
  from:               MsgAddress           // TOʻLOVCHI-egasining manzili (odam)
  response_address:   MsgAddress
  forward_ton_amount: (VarUInteger 16)
  forward_payload:    (Either Cell ^Cell)  // izoh/memo

Ikkita muhim jihat:

  • from maydoni — bu haqiqiy toʻlovchining (odam-egasining) manzili, uning jetton-hamyoni manzili emas. Aynan uni joʻnatuvchi sifatida logga yozish kerak. Eʼtibor bering: bu yerda xabar darajasidagi manzil (tx.in_msg.source) — toʻlovchining jetton-hamyoni, odam-toʻlovchi esa xabar tanasidagi from maydonida koʻrsatiladi.
  • amount maydonidagi summa — raw-birliklarda, qayta hisoblashga quyida qaytamiz.

Bundan arxitektura haqidagi asosiy xulosa

Ishonchli aniqlash asosiy hamyonda notification kelishini kutish emas, balki aynan oluvchining jetton-hamyoni tarixini soʻrash asosida quriladi. Uning tarixida tushum har doim koʻrinadi, forward_ton_amount dan qatʼi nazar. Agar siz qoʻshimcha ravishda asosiy hamyonni ham tinglasangiz, u yerda transfer_notification ni (op 0x7362d09c, joʻnatuvchi — sender maydonida) ushlaysiz — ammo bu yagona haqiqat manbayi emas, faqat forward_ton_amount > 0 holati uchun bonus, xolos.

get_transactions orqali tarixni oʻqiymiz va internal_transfer ni tahlil qilamiz

Avval oluvchining jetton-hamyoni manzili kerak. Uni oflayn hisoblab chiqarmang va tikerga ishonmang — USDTning master-kontraktining oʻzidan soʻrang: USDT masterida get_wallet_address chaqiruvi (run_get_method orqali) haqiqiy masterga bogʻlangan sizning USDT-jetton-hamyoningiz manzilini deterministik tarzda qaytaradi. Soʻngra shu jetton-hamyonning tranzaksiyalarini get_transactions orqali soʻraymiz.

Polling siklidagi agentga prompt:

USDT masterida — EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs
manzilida — get_wallet_address metodi va argument sifatida
UQ...myservice egasining manzili bilan run_get_method ni chaqir.
Bizning USDT-jetton-hamyonimiz manzilini qaytar. Soʻngra
get_transactions orqali shu jetton-hamyonning soʻnggi tranzaksiyalarini
oʻqi. Har bir kiruvchi internal_transfer (op 0x178d4519) uchun query_id,
amount (raw), from maydoni (toʻlovchi manzili), forward_payload dagi
matnli izoh, shuningdek tranzaksiyaning lt va hash ini qaytar.

Bitta tranzaksiyani qayta ishlash psevdokodi:

for (const tx of txs) {
  const body = tx.in_msg?.decoded;          // kiruvchi xabarning tahlil qilingan tanasi
  if (body?.op !== 0x178d4519) continue;    // internal_transfer emas — oʻtkazib yuboramiz

  const rawAmount = BigInt(body.amount);    // raw-birliklar, odam oʻqiydigan koʻrinishda EMAS
  const payer     = body.from;              // toʻlovchi-egasining manzili
  const memo      = parseComment(body.forward_payload);
  const key       = `${tx.lt}:${tx.hash}`;  // dedup uchun identifikator
  // …tekshirish va hisobga olish quyida
}

Asosiy akkaunt holatini get_account_state orqali bir marta tekshirib koʻrish ham foydali (u faolmi, soʻnggi tranzaksiya qachon boʻlgan) — bu poller «tirik»mi va ortda qolib ketmaganmi, shuni tushunishga yordam beradi.

Hamma narsani decimals hal qiladi: get_jetton_info va raw-summani qayta hisoblash (USDT = 6, 9 emas)

Oʻtkazmadagi amount maydoni — jettonning raw-birliklarida, verguli boʻlmagan butun son. Uni odam oʻqiydigan summaga aylantirish uchun decimals kerak. Aynan shu yerda eng koʻp integratsiya buziladi.

Jettonlarning koʻpchiligida decimals = 9. TONdagi USDT (Tether) uchun esa — decimals = 6. Yaʼni:

1 USDT = 1 000 000 raw   (10^6, 10^9 emas)

Agar odat boʻyicha USDTning raw-summasini 10^9 ga boʻlsangiz, mijozning 50 USDTlik toʻlovi sizda 0.05 ga aylanadi — 1000 barobar kam. Teskari yoʻnalishdagi xato ham xuddi shunday osonlik bilan 1000 barobar koʻp summani hisobga olib qoʻyadi. Boʻluvchini hardkod qilmang — decimals ni get_jetton_info orqali on-chain metamaʼlumotlardan oling:

const info = await getJettonInfo(USDT_MASTER); // name, symbol, decimals, emissiya
const decimals = info.decimals;                // USDT uchun = 6
const human = Number(rawAmount) / 10 ** decimals; // 50000000 → 50.0

decimals ni keshda tiker boʻyicha emas, master manzili boʻyicha saqlang: tiker soxtalashtiriladi, decimals ni esa aniq kontraktga bogʻlash kerak. Qoida oddiy: decimals har doim get_jetton_info dan olinadi, summa esa ekranga chiqarilgunga qadar faqat BigInt/raw koʻrinishida saqlanadi.

Kelib chiqishi, summa va izohni tekshiramiz — soxta jettonlarni ajratib tashlaymiz

Endi xavfsizlik uchun eng muhim qismi. Quyidagilar — ularsiz hisobga olishning umuman iloji yoʻq boʻlgan tekshiruvlar.

1. Master-kontrakt — faqat haqiqiy USDT. Har kim «USDT» nomi va «USD₮» ramzi bilan jetton chiqarib, sizga 1000 «USDT» lik oʻtkazma yuborishi mumkin. Agar toʻlovni tiker boʻyicha moslashtirsangiz — sizni besh daqiqada aldashadi. Haqiqiy Tetherga yagona ishonchli bogʻlanish — haqiqiy USDT master-kontrakti sizning egalik manzilingiz uchun hisoblab bergan jetton-hamyon bilan ishlash:

Tether USD₮ master: EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs

Aynan shuning uchun jetton-hamyon manzilini shu masterdagi get_wallet_address on-chain chaqiruvidan olamiz (yuqoriga qarang), tarixni esa faqat shu — boshqa hech qanday — kontraktdan soʻraymiz. Unga tushgan har qanday internal_transfer kafolatli ravishda oʻsha masterning qonuniy jetton-hamyonlaridan biridan keladi: soxta «USDT» boshqa masterning hamyonlarida yashaydi va sizning manzilingizgacha shunchaki yetib kelmaydi. Shuning uchun bu arxitekturada tx.in_msg.source ni biror narsa bilan solishtirish shart emas — himoya kuzatuv nuqtasini toʻgʻri tanlashning oʻzi bilan taʼminlanadi. Va shuni hisobga oling: parse_address — bu faqat EQ/UQ/raw formatlarining oflayn konvertatsiyasi, u jetton-hamyon manzilini hisoblamaydi; buning uchun aynan get_wallet_address on-chain chaqiruvi kerak. Uni solishtirishdan oldin manzillarni normallashtirish uchun qoʻllash qulay — shunda bitta manzilning turli koʻrinishlarini har xil deb hisoblab yubormaysiz.

2. Invoys bilan moslashtirish uchun izoh (memo). Matnli izoh forward_payload da standart formatda saqlanadi: op prefiksi 0x00000000 (4 ta nol bayt) + UTF-8 satr. Aynan u orqali kiruvchi oʻtkazmani muayyan buyurtma bilan bogʻlaysiz.

3. Summa va toʻlovchi. Qayta hisoblangan summani invoys boʻyicha kutilgani bilan solishtiring; kerak boʻlsa, tarix va antifrod uchun from (toʻlovchi manzili) ni qayd qilib qoʻying.

// 1. Kelib chiqishi kafolatlangan: biz haqiqiy USDT masterining
//    get_wallet_address chaqiruvi qaytargan AYNAN oʻsha jetton-hamyon
//    tarixini oʻqiymiz — demak, bu yerdagi har qanday oʻtkazma haqiqiy.

// 2. Izoh → invoys
const invoice = invoices.get(memo);
if (!invoice) continue;

// 3. Summa mos keladimi?
if (human < invoice.expectedAmount) markUnderpaid(invoice);

// 4. Toʻlovchi — loglar/antifrod uchun
log({ payer, memo, human, lt: tx.lt, hash: tx.hash });

Idempotentlik: toʻlovni ikki marta hisobga olmaslik uchun lt + hash boʻyicha dedup

Polling har doim oʻzi bilan oʻzi kesishadi: jadval boʻyicha ishga tushadi, ishdan chiqadi, qayta urinadi, soʻrov oynalari bir-birining ustiga tushadi. Dedupsiz siz ertami-kechmi bitta toʻlovni ikki marta hisobga olasiz.

TONda har bir tranzaksiya logical time (lt) + hash juftligi bilan yagona tarzda identifikatsiya qilinadi. Bu — sizning tabiiy idempotentlik kalitingiz:

const key = `${tx.lt}:${tx.hash}`;
if (await seen.has(key)) continue;   // allaqachon qayta ishlangan — chiqamiz
await creditInvoice(invoice, human); // hisobga olish
await seen.add(key);                 // oʻsha DB tranzaksiyasida qayd qilamiz

Kalit sifatida oʻtkazmadagi query_id ni ham ishlatish mumkin, ammo (lt, hash) jetton-hamyonning har qanday kiruvchi tranzaksiyasi uchun, jumladan forward_ton_amount = 0 holati uchun ham ishlaydi. Qayta ishlangan kalitlarni bazada doimiy saqlab boring va hisobga olishni yagonalik tekshiruvi bilan bitta DB tranzaksiyasida bajaring — shunda parallel workerlar dublikat yaratmaydi.

Vaqti-vaqti bilan rekonsiliatsiya qilib turish kerak: har N daqiqada jetton-hamyonning haqiqiy balansini (get_jetton_balance orqali — u manzilni on-chain hisoblaydi va balansni bitta chaqiruvda qaytaradi) hisobga olingan barcha toʻlovlar summasi bilan solishtiring. Nomuvofiqlik — qayerdadir oʻtkazma oʻtkazib yuborilgani yoki ikki marta hisoblangani haqidagi signal; u hatto bitta polling-sikl nosozlik bergan holatni ham ochib beradi.

Yuk ostida barqaror throughput: ommaviy limitlar oʻrniga oʻz kalitingiz

Toʻlov pollingi — bu doimiy, tekis soʻrovlar oqimi: N ta hamyon × soʻrov chastotasi. Va aynan shu yerda ommaviy infratuzilma tor joyga aylanadi.

Global configdagi ommaviy liteserverlar — umumiy va limitlangan: yuk ostida not ready deb javob beradi yoki ADNL-taymautga ketadi. Kalitsiz ommaviy HTTP-API lar (toncenter, tonapi.io) sekundiga taxminan 1 ta soʻrovni koʻtaradi va limitdan oshganda halol HTTP 429 «Too Many Requests» qaytaradi. Eʼtibor bering: limitning haqiqiy kodi — aynan 429, TON-hamjamiyat boʻylab yurgan mem «228» emas, u umuman API kodi emas. Har necha soniyada tarixni soʻrab turadigan kassa uchun bu limitlar — oʻtkazib yuborilgan va kechikkan hisobga olishlar demakdir. Oʻz kaliti bor provayderni qanday tanlash — alohida tahlilda.

Toʻlovlarni aniqlash uchun kerak boʻladigan barcha oʻqish vositalari — get_transactions, run_get_method, get_jetton_balance, get_jetton_info, parse_address, get_account_statelokalda bepul mavjud: npx -y @tonnode/mcp. @tonnode/mcp paketi — open source (MIT) va TONning nativ ADNL-protokoli orqali, HTTP-qatlamlarsiz ishlaydi. Polling prodga oʻtganda va kafolatlangan throughput kerak boʻlganda, oʻz kalitli hosted-endpoint ulanadi:

{
  "mcpServers": {
    "ton": {
      "type": "http",
      "url": "https://mcp.tonnode.io/mcp",
      "headers": { "Authorization": "Bearer tn_live_…" }
    }
  }
}

Tariflar faqat daqiqadagi soʻrovlar limiti bilan farqlanadi — 16 ta vositaning barchasi har birida mavjud:

  • Hobby — abadiy bepul, 60 soʻrov/daq. Kalit tizimga kirgandan soʻng darhol beriladi, kartasiz.
  • Pro — $29/oy, 300 soʻrov/daq.
  • Scale — $199/oy, 1200 soʻrov/daq.

Toʻlov pollingini boshlash uchun bepul 60 soʻrov/daq 429 xatosiz barqaror soʻrov yuborib turish uchun yetarli. Agar shu asosda toʻlovlarni oʻzi tahlil qiladigan AI-agent qurayotgan boʻlsangiz — bu qanday tashkil etilgani TONdagi AI-agentlar uchun MCP boʻyicha qoʻllanmada koʻrib chiqilgan.

Bepul Hobby kalitidan boshlang — toʻlov pollingi uchun 60 req/min, kartasiz: tonnode.io/dashboard?plan=hobby. Yuk oshganda va bitta worker yetmay qolganda — Pro va Scale limitlarini solishtiring.


TONda USDTni ishonchli aniqlash boʻyicha chek-list:

  1. Asosiy hamyon balansini emas, oluvchining jetton-hamyoni tarixini get_transactions orqali soʻrang.
  2. Bu jetton-hamyonning manzilini haqiqiy USD₮ masterida (EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs) get_wallet_address (run_get_method) on-chain chaqiruvidan oling — shunda unga kelgan har qanday oʻtkazma kafolatli ravishda haqiqiy USDT boʻladi.
  3. Op 0x178d4519 bilan kiruvchi internal_transfer larni qidiring — aynan ular forward_ton_amount dan qatʼi nazar jetton-hamyon tarixida har doim qayd etilgan boʻladi. transfer_notification (0x7362d09c) esa asosiy hamyonga va faqat forward_ton_amount > 0 boʻlgandagina ketadi.
  4. Toʻlovchi manzilini from maydonidan, summani esa amount (raw) dan oling.
  5. decimals ni get_jetton_info dan oling. USDT = 6, 9 emas.
  6. Invoysni forward_payload dagi izoh boʻyicha moslashtiring (op 0x00000000 + UTF-8).
  7. (lt, hash) boʻyicha dedup — qatʼiy bir marta hisobga oling.
  8. Barqaror throughputni ommaviy limitlar bilan emas, oʻz kalitingiz bilan ushlab turing.

Agentingizga TONga yoʻl oching

16 ta MCP-vosita: oʻqish, nokastodial svoplar, krosscheyn va hamyonlar. Bepul tarif — 60 soʻrov/daq, karta kerak emas.