Cómo automatizar el alquiler de energía TRON con la API

2026-05-13

Por qué automatizar el alquiler de energía

Si gestionas una hot wallet, un motor de retiros de un exchange o un contrato que dispara transferencias TRC-20 en nombre de los usuarios, ya conoces el dolor: necesitas tener energía TRON lista en el mismo segundo en que se lanza una transacción. Recargar energía a mano desde una interfaz no escala más allá de un puñado de transferencias al día. Si pierdes la ventana, la transacción quema TRX desde la dirección emisora, a menudo a un coste mucho mayor que la energía alquilada con antelación.

La solución es llamar a la API de alquiler en el momento en que tu backend está a punto de difundir una transacción, de modo que la energía se aprovisione de forma programática sin intervención humana. Este artículo explica exactamente cómo hacerlo contra tronenergyrent.com: la estructura real de la solicitud, el ciclo de vida asíncrono del pedido, las compensaciones razonables de duración y un ejemplo Python funcional que puedes incorporar a tu servicio.

Qué hace la API realmente en la cadena

Antes de escribir código, conviene entender la mecánica on-chain para no depurar a ciegas.

El modelo de recursos de TRON separa el cómputo (energía) del tamaño de la transacción (bandwidth). Una transferencia estándar de USDT TRC-20 consume aproximadamente 65,000 de energía cuando el destinatario ya posee USDT, y unos 130,000 cuando el saldo de USDT del destinatario es cero. La primera transferencia entrante paga el coste de almacenamiento de crear la entrada de saldo, por eso el coste depende enteramente del destinatario, no del emisor. El uso de bandwidth para una transferencia ronda los 345 bytes, lo bastante reducido como para que la mayoría de cuentas lo cubran con su asignación diaria gratuita.

Cuando llamas a la API de alquiler, el servicio bloquea sus propios TRX y delega la energía resultante a la dirección que especifiques, usando DelegateResourceContract de Stake 2.0. La delegación es específica para cada dirección y tiene una duración limitada. Una vez expira el periodo de alquiler, la energía la recupera el proveedor automáticamente.

Un detalle importante: el alquiler es asíncrono. La llamada a la API devuelve inmediatamente un orderId y un estado de PAID_BY_USER, pero la transacción de delegación on-chain se difunde en segundo plano y normalmente se confirma en pocos segundos. Tu integración debería tratar la respuesta inicial como confirmación de que el pedido se aceptó y pagó, y luego consultar el endpoint de detalles del pedido hasta que el estado pase a ENERGY_DELEGATED.

Elegir la duración de alquiler adecuada

La API acepta un parámetro period con cuatro valores permitidos: 1h, 1d, 3d, 30d. Los precios fluctúan con el mercado de energía on-chain y varían durante el día, así que los números en vivo siempre están en la página de precios. El orden relativo es estable: 1h es el más barato por llamada, 30d es el más barato cuando se amortiza entre muchas transferencias desde la misma dirección.

Para un sistema basado en eventos donde disparas un alquiler por cada transferencia saliente, el plan 1h es casi siempre la elección correcta. Pagas el coste absoluto más bajo y la energía se consume en cuestión de segundos tras ser delegada. Los planes 1d y superiores tienen sentido cuando ejecutas cargas de trabajo en lotes predecibles, por ejemplo un proceso nocturno de pagos, y quieres alquilar un bloque grande de energía una sola vez en lugar de llamar a la API decenas de veces.

Si tu sistema dispara consistentemente más de 20-30 transferencias por hora desde la misma dirección, alquilar un bloque 1d dimensionado para cubrir tu volumen esperado es más limpio que las llamadas por transferencia. El coste inicial sube, pero la sobrecarga de la API y la latencia de confirmación on-chain desaparecen de la ruta crítica.

Autenticación y estructura de la solicitud

El endpoint de alquiler está en:

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

Es una solicitud GET simple con parámetros de consulta. La autenticación es el parámetro de consulta apiKey, que generas desde tu panel tras registrarte y financiar tu cuenta. No hay autenticación por cabecera ni cuerpo JSON de solicitud para este endpoint.

Los parámetros son:

  • apiKey (obligatorio): tu clave API desde el panel.
  • period (obligatorio): duración del alquiler, uno de 1h, 1d, 3d, 30d.
  • energyAmount (obligatorio): cuánta energía delegar. El mínimo es 15000. Para una transferencia estándar de USDT a un destinatario que ya posee USDT, 65000 es el número seguro; para una primera transferencia a un nuevo poseedor de USDT, usa 130000.
  • destinationAddress (obligatorio): la dirección TRON (base58check, empieza por T) que recibirá la energía delegada.
  • preActivateDestinationAddress (opcional, por defecto 0): ponlo a 1 si la dirección de destino nunca ha recibido TRX y por tanto no está activada on-chain. El servicio entonces envía 1.5 TRX desde tu saldo prepago para activar la dirección antes de delegar la energía. Si la dirección ya está activa, déjalo en 0 para evitar el coste adicional.

La respuesta siempre se devuelve con estado HTTP 200, independientemente de si el pedido tuvo éxito o falló. Ramifica según el campo status del cuerpo JSON en lugar del estado HTTP. Un cuerpo de éxito tiene este aspecto:

{
  "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"
  }
}

Un cuerpo de error tiene status: "ERROR", un errorCode legible por máquina y un errorDescription legible por humanos:

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

Registra siempre el requestId en ambas ramas. Si alguna vez necesitas pedir a soporte que rastree un alquiler concreto, ese identificador es el que ellos buscan.

El ciclo de vida del pedido

El campo state en el payload progresa a través de una pequeña secuencia fija:

  • PAID_BY_USER: estado inicial, el pedido está pagado desde tu saldo pero aún no ha ocurrido ninguna acción on-chain.
  • WAITING_DELEGATION: el servicio ha recogido el pedido y está preparando la transacción de delegación.
  • ENERGY_DELEGATED: la transacción de delegación se ha confirmado on-chain. La dirección de destino ya posee la energía alquilada y puede gastarla en la siguiente transacción saliente.
  • ERROR_DELEGATION: la delegación falló por alguna razón. Raro, pero posible durante una fuerte congestión de la red.
  • CANCELLED: el pedido se canceló y los fondos se reembolsaron a tu saldo.

Para comprobar el estado actual, llama a:

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

La respuesta usa el mismo envoltorio status / errorCode / payload, con el state actual dentro de payload. Consulta esto cada segundo o dos tras realizar el pedido y procede con tu transferencia TRC-20 solo después de ver ENERGY_DELEGATED. En la práctica suelen ser una o dos consultas.

Una integración Python funcional

Aquí tienes un patrón mínimo pero con forma de producción. Realiza el alquiler, consulta hasta que la delegación esté on-chain y emite un error claro si algo sale mal.

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

Conviene destacar algunas cosas. La comprobación explícita status == "SUCCESS" es importante porque el estado HTTP siempre es 200, así que resp.raise_for_status() por sí solo no te dice nada sobre el resultado real. El bucle de consulta tiene un tiempo límite estricto para que un pedido atascado no pueda bloquear tu canal de transferencias indefinidamente. La ramificación según state maneja los tres resultados terminales (ENERGY_DELEGATED, ERROR_DELEGATION, CANCELLED) de forma explícita en lugar de basarse en un genérico "no éxito".

En tu flujo de transferencia, llama primero a rent_energy() y luego difunde la transferencia TRC-20. El orden de las operaciones importa: si difundes primero, la transacción quemará TRX del emisor porque la delegación todavía no se ha confirmado.

Dimensionar tu saldo prepago

El coste del alquiler se descuenta de un saldo prepago que mantienes en tu cuenta de tronenergyrent.com. Configura una alerta o una recarga automática cuando el saldo caiga por debajo de un umbral. Un mínimo razonable es lo que cubra dos horas de volumen pico.

Para leer el saldo actual de forma programática:

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

El payload incluye tu saldo actual y el estado de la cuenta. Conéctalo a tu pila de monitorización y nunca te sorprenderá un saldo agotado a las 2 de la madrugada. Ten en cuenta que la recarga mínima de tu saldo de tronenergyrent.com es de 10 TRX.

Manejo de errores en la práctica

Tres modos de fallo aparecen con mayor frecuencia en producción. Los códigos de error que siguen vienen de la API real y puedes ramificar sobre ellos con seguridad.

  1. INSUFFICIENT_BALANCE: tu saldo prepago es demasiado bajo para cubrir el alquiler solicitado más cualquier preactivación. No reintentes, reintentar no lo arreglará. Captúralo de forma específica y dispara una alerta o un flujo de recarga. Añade el requestId al payload de la alerta.
  2. INVALID_ADDRESS: la destinationAddress falló la decodificación base58check en el servidor. Si tu sistema construye direcciones dinámicamente, por ejemplo a partir de entradas suministradas por el usuario, valídalas localmente antes de llamar a la API. La librería Python tronpy tiene is_address() para esto; rechazar entradas incorrectas en el cliente es más rápido que esperar la ida y vuelta.
  3. INACTIVE_DESTINATION_ADDRESS_ERROR: la dirección de destino nunca ha sido activada on-chain y no pediste al servicio que la activara. Arréglalo prefinanciando la dirección con una pequeña cantidad de TRX desde otro lugar, o pasando preActivateDestinationAddress=1 en la siguiente llamada para que el servicio la active por 1.5 TRX.

Un caso más sutil: ORDER_IS_ALREADY_IN_PROGRESS puede devolverse si tienes varios workers realizando alquileres para el mismo destino al mismo tiempo. La solución está del lado del proceso, no de la API. Toma un bloqueo distribuido (Redis funciona bien) con clave en la dirección de destino y mantenido hasta que se complete la delegación.

Verificación on-chain

Para la mayoría de integraciones, consultar /single-order-details hasta ENERGY_DELEGATED es suficiente. Si quieres una verificación con cinturón y tirantes, también puedes consultar directamente el nodo completo de TRON una vez que la delegación se haya confirmado:

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

La respuesta contiene el estado actual de recursos de la dirección, incluida la energía delegada hacia ella desde direcciones externas. El nombre exacto del campo depende de la vista actual de Stake 2.0, así que consulta la documentación en vivo de la API HTTP de TRON al integrarlo. Como verificación blanda que registra una advertencia en lugar de bloquear la transferencia, es un canario útil para el caso raro en que algo sale mal aguas abajo de la propia API de alquiler.

Esa verificación adicional te ahorrará horas de depuración la única vez que el alquiler parezca exitoso en la API pero algo más salga mal on-chain.

¿Quiere ahorrar en comisiones TRON? Consulte los precios de energía ahora. Estimación de Precio
Volver al Blog