كل المقالات
6 دقائق قراءة

رصيد USDT على محفظة TON باستدعاء واحد عبر get_jetton_balance

كيف تحصل على رصيد USDT في محفظة TON باستدعاء واحد عبر get_jetton_balance — عنوان محفظة الجيتون يُحسب على السلسلة، ولا حاجة إلى مُفهرِس.

رصيد USDT على TONget_jetton_balanceجيتونات TONMCP لشبكة TONTONNodedecimals الجيتون

محفظة الوكيل مفتوحة، وأعاد get_balance بضعة GRAM صادقة — أما USDT فصفر. مع أنك تعرف يقينًا أن العملات المستقرة قد وصلت إلى هذا العنوان. يبدو مألوفًا؟ تبدو المحفظة فارغة بينما فيها 500 USDT. ليس هذا عطلًا ولا أموالًا ضائعة — أنت ببساطة سألتَ العقد الخطأ. وهنا بالضبط تفشل معظم عمليات الربط الأولى بين وكيل الذكاء الاصطناعي وشبكة TON: الوكيل يقرأ العنوان الخطأ.

لماذا رصيد USDT ليس رصيد محفظة TON

في Ethereum اعتدتَ أن توكن ERC-20 «موجود على العنوان»: عقد التوكن يحفظ جدولًا address → balance، ولمعرفة الرصيد تسأل هذا العقد الواحد. أما في TON فالنموذج مختلف، وهذا أول مصادر الالتباس.

العملة الأصلية GRAM (Toncoin سابقًا؛ أما الشبكة فما زالت تُسمّى TON) تُخزَّن مباشرةً في العقد الذكي لمحفظتك — ويحصل عليها get_balance باستعلام واحد عن حالة الحساب. أما USDT فهو جيتون (jetton، معيار TON للتوكينات القابلة للاستبدال). ورصيد الجيتون محفوظ لا على محفظتك الأساسية، بل على عقد صغير منفصل — محفظة الجيتون (jetton wallet).

والتشبيه بسيط: محفظة TON الأساسية لديك هي أنت بوصفك شخصًا. أما محفظة جيتون USDT فهي حساب منفصل باسمك، فتحه بنك بعينه (عقد ماستر USDT). وسؤال الشخص نفسه «كم لديّ من الدولارات» لا معنى له — فالمال في الحساب لا في الجيب. ولكل ثنائي «مالك + جيتون» محفظة جيتون خاصة به: عنوان لـ USDT، وآخر لـ NOT، وثالث لأي جيتون كان.

والخبر الجيد: عنوان محفظة الجيتون هذه ليس عشوائيًا. فهو يُشتق اشتقاقًا حتميًا من شيئين:

  • عنوان المالك (محفظة TON العادية لديك، EQ…/UQ…
  • عنوان عقد ماستر الجيتون (jetton master — وهو لـ USDT عقد واحد ثابت).

والخبر السيئ: لكي تحسب هذا العنوان حسابًا صادقًا، عليك أن تذهب إلى عقد الماستر وتستدعي get-method الخاص به، ثم تقرأ حالة محفظة الجيتون. وهذه يدويًا بضع خطوات، وعليها بالضبط تتعثّر عمليات الربط.

get_jetton_balance: استدعاء واحد بدل المُفهرِس

عادةً تُحلّ هذه المهمة بإحدى طريقتين، وكلتاهما غير مريحة:

  • مُفهرِس خاص بك. تُشغّل عقدة، وتفهرس تحويلات الجيتون في قاعدة بيانات، وتُبقيها محدَّثة. مكلف وهشّ من أجل رقم واحد، والبيانات تتأخر دائمًا قليلًا عن السلسلة.
  • واجهة HTTP عامة. تصطدم سريعًا بالحدود: فبلا مفتاح يكون المعدّل نحو طلب واحد في الثانية، وعند التجاوز تحصل على HTTP 429 Too Many Requests الصادق. زد على ذلك أنك تعتمد على فهرسة غيرك وعلى عمقها.

وأداة get_jetton_balance من TONNode تزيل المشكلتين معًا. أنت تمرّر إليها:

  • عنوان المالك — محفظة TON العادية للمستخدم (EQ…/UQ…
  • معرّف الجيتون — USDT مثلًا.

وبعدها يقوم الخادم بكل العمل بنفسه على السلسلة:

  1. يستدعي get-method في عقد ماستر الجيتون، وهو يعيد — بحسب عنوان المالك — عنوانَ محفظة الجيتون الخاصة به (ذلك الاشتقاق الحتمي بعينه)؛
  2. يقرأ الرصيد من محفظة الجيتون هذه ويعيده إليك.

لا مُفهرِس خارجي، ولا قاعدة بيانات، ولا خروج عن التزامن — قراءة مباشرة لحالة الشبكة عبر get-methods الخاصة بالعقود فحسب.

وTONNode خادم MCP مُستضاف لشبكة TON. أما MCP (Model Context Protocol) فهو المعيار الذي يستدعي بموجبه وكلاء الذكاء الاصطناعي (Claude وCursor وChatGPT/Codex وأي عميل MCP) الأدوات الخارجية. أي أن get_jetton_balance ليس سطرًا في كودك، بل أداة يستدعيها الوكيل بنفسه حين يسأل المستخدم عن الرصيد. وتحت الغطاء تعمل حزمة @tonnode/mcp (مفتوحة المصدر، MIT) عبر بروتوكول ADNL الأصلي في TON، دون طبقات HTTP وسيطة — فالوكيل يتحدث إلى الشبكة مباشرةً، لا عبر بوابة REST أخرى.

مثال: مطالبة للوكيل وما الذي يعود في الردّ

الربط محلي، بلا مفتاح وبلا بطاقة. أضف إلى إعدادات عميل MCP (Claude Desktop، Cursor، أي عميل متوافق مع MCP):

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

وبعدها يكفي الوكيلَ طلبٌ عادي بلغة البشر:

كم من USDT في المحفظة UQAbc…xyz؟ أعد القيمة مقروءةً بشريًا.

سيختار الوكيل أداة get_jetton_balance بنفسه، ويمرّر عنوان المالك وجيتون USDT، فيعيد الخادم الرصيد — لكن لا بـ«الدولارات» المألوفة، بل بـالوحدات الخام (أصغر وحدات الجيتون غير القابلة للتجزئة). وإلى جانب الرصيد نفسه يأتي في الردّ عنوانُ محفظة الجيتون الذي حسبه الخادم على السلسلة — وهو مفيد إن أردت لاحقًا أن تتابع تحويلات هذا العقد تحديدًا. أما الرصيد نفسه في مثالنا فقيمة على هذه الشاكلة: 12500000.

لكن 12500000 ليست 12.5 مليون USDT. وهنا يبدأ الفخّ الثاني الذي يسهل الوقوع فيه.

الوحدات الخام وdecimals: لماذا لـ USDT قيمة 6 لا 9

البلوكتشين لا يعمل بالكسور. فكل المبالغ محفوظة بـوحدات خام صحيحة، أما القيمة «البشرية» فتُستخرج بالقسمة على 10^decimals، حيث decimals خاصية للجيتون بعينه. الأمر أشبه بالسنتات والدولارات: على المستوى الأدنى كل شيء بالسنتات، وdecimals يقول أين توضع الفاصلة.

والفارق الجوهري في TON: الجيتونات المختلفة لها أعداد decimals مختلفة.

  • لـ GRAM الأصلي ولـمعظم جيتونات TON decimals = 9.
  • أما لـUSDT على TON فـdecimals = 6.

لذلك تعني القيمة الخام الواحدة نفسها مبالغ مختلفة تمامًا. لنعِد حساب مثالنا حسابًا صحيحًا:

raw      = 12500000
decimals = 6            // لـ USDT تحديدًا
human = 12500000 / 10^6 = 12500000 / 1_000_000 = 12.5 USDT

أي 12.5 USDT، لا 12.5 مليون. ولو قسمتَ بحكم العادة على 10^9 (كما تفعل مع جيتون عادي) لحصلت على 0.0125 — خطأ بمقدار ألف ضعف. الرقم نفسه — وفارق ثلاث مراتب. وهكذا بالضبط تضيع الأموال في عمليات الربط الساذجة: مقسومٌ عليه 10^9 مكتوبٌ ثابتًا في الكود ومُطبَّقٌ على كل شيء بلا تمييز. لذلك لا تكتب decimals ثابتة في الكود أبدًا — خذها من البيانات الوصفية للجيتون نفسه.

get_jetton_info: من أين تأخذ decimals والبيانات الوصفية للجيتون

ولئلّا تُخمّن، هناك أداة مرافقة — get_jetton_info. تقرأ عقد ماستر الجيتون وتعطي بياناته الوصفية:

  • الاسم (name)؛
  • الرمز (symbol)؛
  • decimals — ذلك الرقم الذي يُحسب به المقسوم عليه؛
  • إجمالي المعروض (total supply).

والسيناريو الموثوق للوكيل استدعاءان: إن كان الجيتون غير معروف، فأولًا get_jetton_info لمعرفة decimals، ثم get_jetton_balance لأخذ الرصيد الخام، وعندها فقط القسمة raw / 10^decimals للعرض.

1) get_jetton_info(USDT)            -> decimals = 6
2) get_jetton_balance(owner, USDT)  -> raw = 12500000
3) human = 12500000 / 10^6          -> 12.5 USDT

ويمكن صياغة المطالبة بحيث يلحم الوكيل هذه الخطوات بنفسه:

خذ decimals لـ USDT عبر get_jetton_info، ثم رصيد المحفظة UQAbc…xyz عبر get_jetton_balance، وحوّل الخام إلى رقم مقروء بشريًا.

وهذا الترتيب إلزامي إن كنت تعمل مع جيتونات اعتباطية لا مع USDT وحده: فمع توكن غير معروف لا تعرف مسبقًا أعدد decimals فيه 6 أم 9. أما لـ USDT فـdecimals = 6 ثابتة، لكن عادة سحبها من get_jetton_info ستنقذك عند أول جيتون غير قياسي. والأسلوب نفسه يقوم عليه كشف مدفوعات USDT الواردة على TON — وهناك تكون decimals حاسمة كي لا تخلط بين 1 USDT وغبار مجهري.

parse_address: التحقق من العنوان دون اتصال قبل الطلب

وثمة سبب شائع آخر للرصيد «الصفري» — عنوان مالك مشوَّه. فالمستخدمون يرسلون العناوين بصيغ مختلفة: EQ… (bounceable)، وUQ… (non-bounceable)، وraw (0:…). وقبل السؤال عن الرصيد، من المفيد توحيد العنوان عبر parse_address — فهو يعمل دون اتصال (بلا أي طلب إلى الشبكة)، ويأخذ العنوان بأي من الصيغ، ويحوّله إلى الصيغ الثلاث كلها (EQ/UQ/raw)، ويقول إن كان صحيحًا أصلًا.

هذا رخيص وفوري ويزيل صنفًا كاملًا من أخطاء «الرصيد 0 لأن العنوان ليس هو الصحيح» — خصوصًا إن جاء العنوان من مصدر غير موثوق، أو من إدخال المستخدم، أو من محادثة.

كيف تربط: مجانًا محليًا أو مُستضافًا

ثلاث أدوات — parse_address وget_jetton_info وget_jetton_balance — تعطي جوابًا كاملًا صادقًا عن سؤال «كم من USDT في المحفظة» دون مُفهرِس خارجي واحد.

مجانًا محليًا

حزمة @tonnode/mcp مفتوحة المصدر (MIT)، وموجودة على npm وGitHub. والإعداد العام نفسه من المثال أعلاه (npx -y @tonnode/mcp) يعطي مجموعة أدوات القراءة الكاملة، بما فيها get_jetton_balance وget_jetton_info وparse_address — ولا حاجة إلى مفتاح منفصل.

تُعيد تشغيل العميل — فيبدأ الوكيل بقراءة أرصدة الجيتونات عبر ADNL. أما لماذا يحتاج الوكيل أصلًا إلى خادم MCP مخصّص، لا إلى بوابة عامة محدودة، فقد تناولناه في مقال عن MCP لوكلاء الذكاء الاصطناعي على TON. وأين ينتهي الإعداد العام المجاني ولماذا — في تحليل حدود اللايت-سيرفرات العامة في TON.

مُستضاف — حين تحتاج إلى سعة تمرير أعلى

حين لا يعود المعدّل المحلي كافيًا (بوتات، خدمات خلفية، حِمل إنتاجي)، تنتقل إلى نقطة النهاية المُستضافة بمفتاحك الخاص. ومجموعة الأدوات واحدة في كل مكان — كلها الـ16، بما فيها كتلة القراءة؛ ولا تختلف الخطط إلا في سعة التمرير المضمونة:

  • Hobby — مجانًا إلى الأبد، 60 طلبًا/دقيقة؛
  • Pro — 29 دولارًا/شهر، 300 طلب/دقيقة؛
  • Scale — 199 دولارًا/شهر، 1200 طلب/دقيقة.

ولا يختلف الربط المُستضاف إلا في أنك توجّه طلباتك إلى نقطة نهاية مشتركة بمفتاحك الخاص:

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

إن كنت تصنع لوحة معلومات، أو بوتًا لفحص الأرصدة، أو وكيلًا يستطلع العناوين كثيرًا — فخذ مفتاحًا كي لا تصطدم بحدود الواجهات العامة، ولا يفاجئك 429 في أسوأ لحظة.

باختصار

  • USDT على TON جيتون، والرصيد موجود في محفظة جيتون منفصلة لا على العنوان الأساسي.
  • get_jetton_balance يحسب بنفسه عنوان محفظة الجيتون على السلسلة ويقرأ الرصيد — فلا حاجة إلى مُفهرِس ولا إلى قاعدة بيانات خاصة بك.
  • الرصيد يأتي بوحدات خام؛ وللعرض اقسمه على 10^decimals.
  • لـ USDT قيمة decimals = 6 (المقسوم عليه 1_000_000)، ولمعظم الجيتونات 9. لا تكتبها ثابتة في الكود — خذها من get_jetton_info.
  • ويمكنك اختبار المسار كله مجانًا: npx -y @tonnode/mcp، الإعداد العام، مجموعة القراءة الكاملة.

مفتاح Hobby المجاني — 60 طلبًا/دقيقة، بلا بطاقة — يُصدَر فور تسجيل الدخول: احصل على المفتاح. وهو يكفي لتشغيل parse_address → get_jetton_info → get_jetton_balance على محفظة حقيقية، والتأكد من أنّ 12500000 وحدة خام تتحول إلى 12.5 USDT بالضبط — لا إلى 12.5 مليون ولا إلى 0.0125.

امنح وكيلك الوصول إلى TON

16 أداة MCP: قراءة ومبادلات غير وصائية وعبر السلاسل ومحافظ. الباقة المجانية — 60 طلب/دقيقة، بلا بطاقة.