Як автоматизувати оренду енергії TRON через API

2026-05-13

Навіщо взагалі автоматизувати оренду енергії

Якщо ви тримаєте гарячий гаманець, рушій виведень для біржі або контракт, який ініціює перекази TRC-20 від імені користувачів, ви вже знаєте біль: енергія TRON має бути готова в ту саму секунду, коли спрацьовує транзакція. Поповнювати енергію руками через UI не масштабується далі кількох переказів на день. Пропустите момент, і транзакція спалить TRX з адреси-відправника, часто за значно вищою ціною, ніж заздалегідь орендована енергія.

Рішення: викликати API оренди в той момент, коли ваш бекенд збирається транслювати транзакцію, щоб енергія надавалася програмно без участі людини. У цій статті ми крок за кроком розберемо, як саме це зробити з tronenergyrent.com: реальна структура запиту, асинхронний життєвий цикл замовлення, розумні компроміси за тривалістю та робочий приклад на Python, який можна вставити у ваш сервіс.

Що насправді робить API в мережі

Перш ніж писати код, корисно зрозуміти механіку в мережі, щоб не дебажити наосліп.

Модель ресурсів TRON розділяє обчислення (енергію) та розмір транзакції (bandwidth). Стандартний переказ USDT TRC-20 споживає приблизно 65,000 енергії, коли отримувач уже має USDT, і близько 130,000, коли баланс USDT в отримувача нульовий. Перший вхідний переказ оплачує вартість зберігання запису балансу, тому вартість залежить виключно від отримувача, а не від відправника. Використання bandwidth для переказу становить близько 345 байтів, цього достатньо мало, щоб більшість акаунтів покривали його щоденним безкоштовним лімітом.

Коли ви викликаєте API оренди, сервіс стейкає власні 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-запит з параметрами в query-рядку. Автентифікація це параметр apiKey в query, який ви генеруєте у своєму особистому кабінеті після реєстрації та поповнення акаунту. Автентифікації через заголовки немає, тіла 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 в обох гілках. Якщо вам колись доведеться звернутися в підтримку, щоб простежити конкретну оренду, саме за цим ID вони шукатимуть.

Життєвий цикл замовлення

Поле state в payload проходить невелику фіксовану послідовність:

  • 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. На практиці це зазвичай одне-два опитування.

Робоча інтеграція на Python

Ось мінімальний, але продакшн-орієнтований шаблон. Він розміщує оренду, опитує до моменту, коли делегування потрапить у блок, і виводить чітку помилку, якщо щось іде не так.

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

Payload містить ваш поточний баланс і стан акаунту. Підключіть це до вашого стеку моніторингу, і вас ніколи не здивує вичерпаний баланс о другій ночі. Зверніть увагу, що мінімальне поповнення вашого балансу tronenergyrent.com становить 10 TRX.

Обробка помилок на практиці

Три режими відмови виникають у продакшні найчастіше. Коди помилок нижче приходять з реального API, на них можна безпечно розгалужуватися.

  1. INSUFFICIENT_BALANCE: ваш передплачений баланс надто низький, щоб покрити запитану оренду плюс будь-яку преактивацію. Не повторюйте запит, повторні спроби це не виправлять. Ловіть конкретно цю помилку та запускайте сповіщення або потік поповнення. Додавайте requestId у payload сповіщення.
  2. INVALID_ADDRESS: destinationAddress не пройшла декодування base58check на сервері. Якщо ваша система формує адреси динамічно, наприклад з введення користувача, валідуйте їх локально перед викликом API. У Python-бібліотеці tronpy для цього є is_address(); відхиляти некоректний ввід на стороні клієнта швидше, ніж чекати round trip.
  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? Перевірте ціни на енергію зараз. Розрахувати ціну
Назад до блогу