كيفية أتمتة تأجير طاقة TRON عبر الـ API

2026-05-13

لماذا أتمتة تأجير الطاقة من الأساس

إذا كنت تشغّل محفظة ساخنة، أو محرّك سحب لمنصة تداول، أو عقدًا يطلق تحويلات TRC-20 نيابةً عن المستخدمين، فأنت تعرف الألم جيدًا: تحتاج إلى طاقة TRON جاهزة في اللحظة التي تنطلق فيها المعاملة. شحن الطاقة يدويًا عبر الواجهة لا يصمد بعد بضعة تحويلات في اليوم. إن فاتتك اللحظة، فستحرق المعاملة TRX من عنوان المرسل بدلًا من ذلك، وغالبًا بتكلفة أعلى بكثير من الطاقة المؤجَّرة مسبقًا.

الحل هو استدعاء واجهة التأجير في اللحظة التي يستعد فيها الخلفية الخاصة بك لبث المعاملة، حتى يتم تجهيز الطاقة برمجيًا دون أي تدخل بشري. تشرح هذه المقالة بالضبط كيفية القيام بذلك مع tronenergyrent.com: الشكل الفعلي للطلب، دورة حياة الطلب غير المتزامنة، المقايضات المعقولة في المدة، ومثال بايثون عملي يمكنك دمجه في خدمتك مباشرةً.

ما الذي تفعله الـ API فعليًا على السلسلة

قبل كتابة أي كود، من المفيد فهم آلية العمل على السلسلة حتى لا تتعامل مع تصحيح الأخطاء وأنت أعمى.

يفصل نموذج موارد TRON بين الحوسبة (الطاقة) وحجم المعاملة (bandwidth). تستهلك التحويلة القياسية من USDT TRC-20 ما يقارب 65,000 طاقة عندما يمتلك المستلم USDT بالفعل، وحوالي 130,000 عندما يكون رصيد المستلم من USDT صفرًا. التحويلة الواردة الأولى تدفع تكلفة تخزين إنشاء سجل الرصيد، ولهذا تعتمد التكلفة كليًا على المستلم وليس على المرسل. أما استهلاك bandwidth للتحويلة فحوالي 345 بايت، وهو حجم صغير بما يكفي ليغطيه معظم الحسابات من حصتهم اليومية المجانية.

عندما تستدعي واجهة التأجير، تقوم الخدمة بتجميد TRX الخاصة بها وتفوّض الطاقة الناتجة إلى العنوان الذي تحدده، باستخدام DelegateResourceContract من Stake 2.0. التفويض مخصص لعنوان معين ومحدّد بوقت. بمجرد انتهاء فترة التأجير، يستردّ المزوّد الطاقة تلقائيًا.

تفصيل مهم: التأجير غير متزامن. يعود استدعاء الـ API فورًا بمعرّف orderId وحالة PAID_BY_USER، لكن معاملة التفويض على السلسلة تُبَث في الخلفية وعادةً ما تُنفّذ خلال ثوانٍ معدودة. ينبغي للتكامل الخاص بك أن يعامل الاستجابة الأولية كتأكيد لقبول الطلب ودفعه، ثم يستعلم عن نقطة تفاصيل الطلب حتى تنتقل الحالة إلى ENERGY_DELEGATED.

اختيار مدة التأجير المناسبة

تقبل الـ API معامل period بأربع قيم مسموح بها: 1h، 1d، 3d، 30d. تتذبذب الأسعار مع سوق الطاقة على السلسلة وتتغير خلال اليوم، لذلك تظل الأرقام الحية دائمًا على صفحة الأسعار. الترتيب النسبي ثابت: 1h هو الأرخص لكل استدعاء، و 30d هو الأرخص عند توزيع التكلفة على عدد كبير من التحويلات من نفس العنوان.

بالنسبة لنظام مبني على الأحداث حيث تشغّل تأجيرًا واحدًا لكل تحويلة صادرة، فإن الفئة 1h هي الخيار الصحيح في الغالب. تدفع أدنى تكلفة مطلقة وتُستهلك الطاقة في غضون ثوانٍ من تفويضها. الفئتان 1d والفئات الأطول منطقية عندما تشغّل أحمال عمل مجمّعة قابلة للتنبؤ، مثل وظيفة دفعات ليلية، وترغب في تأجير كتلة طاقة كبيرة مرة واحدة بدلًا من استدعاء الـ API عشرات المرات.

إذا كان نظامك يطلق باستمرار أكثر من 20-30 تحويلة في الساعة من نفس العنوان، فإن استئجار كتلة 1d بحجم يغطي توقعاتك أنظف من الاستدعاءات لكل تحويلة. ترتفع التكلفة الأولية، لكن يختفي حِمل الـ API وزمن التأكيد على السلسلة من المسار الساخن.

المصادقة وبنية الطلب

تقع نقطة نهاية التأجير على:

GET https://api.tronenergyrent.com/place-energy-order

إنها طلب GET بسيط مع معاملات استعلام. المصادقة تتم عبر معامل الاستعلام apiKey، الذي تُولّده من لوحة التحكم بعد التسجيل وتمويل حسابك. لا توجد مصادقة عبر الترويسات ولا جسم طلب JSON لهذه النقطة.

المعاملات هي:

  • apiKey (مطلوب): مفتاح الـ API الخاص بك من لوحة التحكم.
  • period (مطلوب): مدة التأجير، واحدة من 1h، 1d، 3d، 30d.
  • energyAmount (مطلوب): مقدار الطاقة المراد تفويضها. الحد الأدنى هو 15000. لتحويلة USDT قياسية واحدة إلى مستلم يمتلك USDT بالفعل، الرقم الآمن هو 65000؛ لأول تحويلة إلى حامل USDT جديد، استخدم 130000.
  • destinationAddress (مطلوب): عنوان TRON (بصيغة base58check، يبدأ بـ T) الذي سيستقبل الطاقة المفوَّضة.
  • preActivateDestinationAddress (اختياري، الافتراضي 0): اضبطه على 1 إذا كان عنوان الوجهة لم يستقبل أبدًا أي TRX وبالتالي لم يُفعَّل على السلسلة. عندئذ ترسل الخدمة 1.5 TRX من رصيدك المدفوع مسبقًا لتفعيل العنوان قبل تفويض الطاقة. إذا كان العنوان مفعّلًا بالفعل، اتركه عند 0 لتجنّب التكلفة الإضافية.

تُعاد الاستجابة دائمًا بحالة HTTP 200، بغض النظر عن نجاح الطلب أو فشله. تفرّع على حقل status في جسم JSON بدلًا من حالة HTTP. يبدو جسم النجاح هكذا:

{
  "status": "SUCCESS",
  "errorCode": null,
  "errorDescription": null,
  "requestId": "2651eacd-2428",
  "payload": {
    "orderId": "128de799-501e-44b2-8d6f-1fa825c2deed",
    "totalPriceSun": 5662800,
    "totalPriceTrx": 5.6628,
    "state": "PAID_BY_USER"
  }
}

أما جسم الخطأ فيحتوي على status: "ERROR"، و errorCode قابل للقراءة آليًا، و errorDescription قابل للقراءة بشريًا:

{
  "status": "ERROR",
  "errorCode": "INVALID_ENERGY_AMOUNT",
  "errorDescription": "energyAmount is less than 15000",
  "requestId": "71431087-4",
  "payload": null
}

سجّل دائمًا requestId في كلا الفرعين. إذا احتجت يومًا إلى أن تطلب من الدعم تتبّع عملية تأجير محددة، فهذا هو المعرّف الذي يبحثون به.

دورة حياة الطلب

يتقدم حقل state داخل الحمولة عبر سلسلة ثابتة قصيرة:

  • PAID_BY_USER: الحالة الأولية، تم دفع الطلب من رصيدك ولكن لم يحدث أي إجراء على السلسلة بعد.
  • WAITING_DELEGATION: التقطت الخدمة الطلب وتقوم بتجهيز معاملة التفويض.
  • ENERGY_DELEGATED: تم تنفيذ معاملة التفويض على السلسلة. يحوز عنوان الوجهة الآن الطاقة المؤجَّرة ويمكنه إنفاقها على المعاملة الصادرة التالية.
  • ERROR_DELEGATION: فشل التفويض لسبب ما. نادر، لكنه ممكن في أوقات الازدحام الشديد على الشبكة.
  • CANCELLED: تم إلغاء الطلب وردّت الأموال إلى رصيدك.

للتحقق من الحالة الحالية، استدعِ:

GET https://api.tronenergyrent.com/single-order-details?apiKey=YOUR_API_KEY&orderId=ORDER_ID

تستخدم الاستجابة نفس مظروف status / errorCode / payload، مع state الحالية داخل payload. استعلم عن هذا كل ثانية أو ثانيتين بعد تقديم الطلب، وتابع مع تحويلة TRC-20 الخاصة بك فقط بعد أن ترى ENERGY_DELEGATED. في الممارسة العملية، عادةً ما يكون استعلامًا أو اثنين.

تكامل بايثون عملي

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

import logging
import time
import requests

API_BASE = "https://api.tronenergyrent.com"
API_KEY = "your_api_key_here"
ENERGY_FOR_TRANSFER = 65_000   # use 130_000 if recipient has zero USDT balance
HTTP_TIMEOUT_SECS = 10
POLL_INTERVAL_SECS = 1
POLL_TIMEOUT_SECS = 30


def place_order(destination_address: str, period: str = "1h",
                energy_amount: int = ENERGY_FOR_TRANSFER) -> dict:
    resp = requests.get(
        f"{API_BASE}/place-energy-order",
        params={
            "apiKey": API_KEY,
            "period": period,
            "energyAmount": energy_amount,
            "destinationAddress": destination_address,
            "preActivateDestinationAddress": 0,
        },
        timeout=HTTP_TIMEOUT_SECS,
    )
    resp.raise_for_status()
    body = resp.json()
    if body.get("status") != "SUCCESS":
        raise RuntimeError(
            f"place-energy-order failed: {body.get('errorCode')} "
            f"({body.get('errorDescription')}) requestId={body.get('requestId')}"
        )
    return body["payload"]


def wait_for_delegation(order_id: str) -> dict:
    deadline = time.monotonic() + POLL_TIMEOUT_SECS
    while time.monotonic() < deadline:
        resp = requests.get(
            f"{API_BASE}/single-order-details",
            params={"apiKey": API_KEY, "orderId": order_id},
            timeout=HTTP_TIMEOUT_SECS,
        )
        resp.raise_for_status()
        body = resp.json()
        if body.get("status") != "SUCCESS":
            raise RuntimeError(
                f"single-order-details failed: {body.get('errorCode')} "
                f"({body.get('errorDescription')})"
            )
        state = body["payload"]["state"]
        if state == "ENERGY_DELEGATED":
            return body["payload"]
        if state == "ERROR_DELEGATION":
            raise RuntimeError(f"Delegation failed for order {order_id}")
        if state == "CANCELLED":
            raise RuntimeError(f"Order {order_id} was cancelled")
        time.sleep(POLL_INTERVAL_SECS)
    raise TimeoutError(f"Order {order_id} did not reach ENERGY_DELEGATED in {POLL_TIMEOUT_SECS}s")


def rent_energy(destination_address: str) -> dict:
    placed = place_order(destination_address)
    logging.info(
        "Order placed: id=%s priceSun=%s priceTrx=%s",
        placed["orderId"], placed["totalPriceSun"], placed["totalPriceTrx"],
    )
    delegated = wait_for_delegation(placed["orderId"])
    logging.info("Order delegated: id=%s", delegated["orderId"])
    return delegated

بعض النقاط الجديرة بالإبراز. الفحص الصريح لـ status == "SUCCESS" مهم لأن حالة HTTP هي دائمًا 200، لذلك فإن resp.raise_for_status() وحدها لا تخبرك بشيء عن النتيجة الفعلية. حلقة الاستعلام لديها مهلة صلبة حتى لا يستطيع طلب عالق أن يحجب خط أنابيب التحويل خاصتك إلى ما لا نهاية. التفرّع على state يعالج النتائج النهائية الثلاث (ENERGY_DELEGATED، ERROR_DELEGATION، CANCELLED) صراحةً بدلًا من الاعتماد على "عدم النجاح" العام.

في سير عمل التحويل الخاص بك، استدعِ rent_energy() أولًا، ثم ابثّ تحويلة TRC-20. ترتيب العمليات مهم: إذا بثثت أولًا، فستحرق المعاملة TRX من المرسل لأن التفويض لم يتم تنفيذه بعد.

تحديد حجم رصيدك المدفوع مسبقًا

تُخصم تكلفة التأجير من رصيد مدفوع مسبقًا تحتفظ به في حسابك على tronenergyrent.com. أعدّ تنبيهًا أو شحنًا تلقائيًا عندما ينخفض الرصيد إلى ما دون عتبة معينة. الحد الأدنى المعقول هو ما يغطي ساعتين من حجم الذروة.

لقراءة الرصيد الحالي برمجيًا:

GET https://api.tronenergyrent.com/account-info?apiKey=YOUR_API_KEY

تتضمن الحمولة رصيدك الحالي وحالة الحساب. اربط ذلك بمنظومة المراقبة الخاصة بك ولن تفاجأ أبدًا برصيد منضوب في الساعة الثانية صباحًا. لاحظ أن الحد الأدنى لشحن رصيدك على tronenergyrent.com هو 10 TRX.

التعامل مع الأخطاء في الممارسة العملية

هناك ثلاث حالات فشل تظهر بأكبر تكرار في الإنتاج. رموز الأخطاء أدناه تأتي من الـ API الفعلية ويمكنك التفرّع عليها بأمان.

  1. INSUFFICIENT_BALANCE: رصيدك المدفوع مسبقًا منخفض جدًا لتغطية التأجير المطلوب بالإضافة إلى أي تفعيل مسبق. لا تعد المحاولة، إعادة المحاولة لن تصلح ذلك. التقط هذا الخطأ تحديدًا وأطلق تدفق تنبيه أو شحن. أضف requestId إلى حمولة التنبيه.
  2. INVALID_ADDRESS: فشل destinationAddress في فك ترميز base58check على الخادم. إذا كان نظامك يُنشئ العناوين ديناميكيًا، مثلًا من إدخال يقدّمه المستخدم، فتحقق منها محليًا قبل استدعاء الـ API. تحتوي مكتبة tronpy في بايثون على is_address() لهذا الغرض؛ رفض الإدخال السيء من جانب العميل أسرع من انتظار رحلة الذهاب والإياب.
  3. INACTIVE_DESTINATION_ADDRESS_ERROR: لم يتم تفعيل عنوان الوجهة على السلسلة من قبل، ولم تطلب من الخدمة تفعيله. أصلح ذلك إما بتمويل العنوان مسبقًا بمبلغ صغير من TRX من مكان آخر، أو بتمرير preActivateDestinationAddress=1 في الاستدعاء التالي لتفعّله الخدمة مقابل 1.5 TRX.

حالة أكثر دقة: قد يعود ORDER_IS_ALREADY_IN_PROGRESS إذا كان لديك عدة عمّال يقدّمون تأجيرات لنفس الوجهة في الوقت ذاته. الإصلاح يتم من جانب العملية، لا من جانب الـ API. استخدم قفلًا موزّعًا (يعمل Redis جيدًا) مرتبطًا بعنوان الوجهة ومحفوظًا حتى يكتمل التفويض.

التحقق على السلسلة

بالنسبة لمعظم التكاملات، يكفي الاستعلام عن /single-order-details حتى ظهور ENERGY_DELEGATED. إذا أردت تحققًا بحزام وأشرطة معًا، يمكنك أيضًا الاستعلام مباشرة من عقدة TRON الكاملة بعد تنفيذ التفويض:

GET https://api.trongrid.io/v1/accounts/{destinationAddress}

تحتوي الاستجابة على حالة الموارد الحالية للعنوان، بما في ذلك الطاقة المفوَّضة إليه من عناوين خارجية. اسم الحقل الدقيق يعتمد على عرض Stake 2.0 الحالي، لذا راجع مستندات HTTP API الحية الخاصة بـ TRON عند توصيل هذا. كفحص ناعم يسجل تحذيرًا بدلًا من حجب التحويل، فهو مؤشر تحذيري مفيد للحالة النادرة التي يحدث فيها خطأ ما بعد الـ API نفسها.

هذا الفحص الإضافي سيوفر عليك ساعات من تصحيح الأخطاء في المرة الواحدة التي يبدو فيها التأجير ناجحًا في الـ API بينما يحدث خطأ آخر على السلسلة.

هل تريد توفير رسوم معاملات TRON؟ تحقق من أسعار الطاقة الآن. تقدير السعر
العودة إلى المدونة