Comment automatiser la location d'énergie TRON avec l'API
Pourquoi automatiser la location d'énergie
Si vous exploitez un hot wallet, un moteur de retrait pour une plateforme d'échange ou un contrat qui déclenche des transferts TRC-20 pour le compte d'utilisateurs, vous connaissez déjà la difficulté : vous avez besoin que l'énergie TRON soit prête à la seconde où une transaction est émise. Recharger l'énergie à la main via une interface ne tient pas la charge au-delà de quelques transferts par jour. Si vous ratez la fenêtre, la transaction consommera des TRX depuis l'adresse expéditrice à la place, souvent à un coût bien supérieur à celui de l'énergie pré-louée.
La solution consiste à appeler l'API de location au moment précis où votre backend s'apprête à diffuser une transaction, de sorte que l'énergie soit provisionnée par programme sans intervention humaine. Cet article décrit exactement comment procéder avec tronenergyrent.com : la structure réelle des requêtes, le cycle de vie asynchrone des commandes, des compromis raisonnables sur la durée, et un exemple Python fonctionnel que vous pouvez intégrer directement dans votre service.
Ce que fait réellement l'API on-chain
Avant d'écrire du code, il est utile de comprendre la mécanique on-chain pour ne pas déboguer à l'aveugle.
Le modèle de ressources de TRON sépare le calcul (énergie) de la taille de transaction (bandwidth). Un transfert USDT TRC-20 standard consomme environ 65,000 d'énergie lorsque le destinataire détient déjà des USDT, et environ 130,000 lorsque le solde USDT du destinataire est nul. Le premier transfert entrant prend en charge le coût de stockage de la création de l'entrée de solde, ce qui explique pourquoi le coût dépend entièrement du destinataire, et non de l'expéditeur. La consommation de bandwidth pour un transfert est d'environ 345 octets, ce qui est suffisamment faible pour que la plupart des comptes la couvrent grâce à leur quota gratuit quotidien.
Lorsque vous appelez l'API de location, le service stake ses propres TRX et délègue l'énergie obtenue à l'adresse que vous indiquez, en utilisant DelegateResourceContract de Stake 2.0. La délégation est spécifique à une adresse et limitée dans le temps. Une fois la période de location expirée, l'énergie est récupérée automatiquement par le fournisseur.
Un détail important : la location est asynchrone. L'appel API renvoie immédiatement un orderId et un état PAID_BY_USER, mais la transaction de délégation on-chain est diffusée en arrière-plan et est généralement confirmée en quelques secondes. Votre intégration doit traiter la réponse initiale comme la confirmation que la commande a été acceptée et payée, puis interroger l'endpoint de détails de la commande jusqu'à ce que l'état passe à ENERGY_DELEGATED.
Choisir la bonne durée de location
L'API accepte un paramètre period avec quatre valeurs autorisées : 1h, 1d, 3d, 30d. Les prix évoluent au gré du marché on-chain de l'énergie et fluctuent au cours de la journée, c'est pourquoi les chiffres en temps réel sont toujours disponibles sur la page des tarifs. L'ordre relatif reste stable : 1h est le moins cher par appel, 30d est le moins cher quand on amortit le coût sur de nombreux transferts depuis la même adresse.
Pour un système événementiel où vous déclenchez une location par transfert sortant, le palier 1h est presque toujours le bon choix. Vous payez le coût absolu le plus bas et l'énergie est consommée en quelques secondes après sa délégation. Les paliers 1d et supérieurs ont du sens lorsque vous exécutez des charges de travail batch prévisibles, par exemple un job de paiement nocturne, et que vous souhaitez louer un grand bloc d'énergie une seule fois plutôt que d'appeler l'API des dizaines de fois.
Si votre système émet systématiquement plus de 20 à 30 transferts par heure depuis la même adresse, louer un bloc 1d dimensionné pour couvrir votre volume attendu est plus propre que des appels par transfert. Le coût initial augmente, mais la surcharge de l'API et la latence de confirmation on-chain disparaissent du chemin critique.
Authentification et structure de la requête
L'endpoint de location se trouve à :
GET https://api.tronenergyrent.com/place-energy-order
Il s'agit d'une simple requête GET avec des paramètres de requête. L'authentification se fait via le paramètre de requête apiKey, que vous générez depuis votre tableau de bord après vous être inscrit et avoir alimenté votre compte. Il n'y a pas d'authentification par en-tête et pas de corps JSON pour cet endpoint.
Les paramètres sont :
apiKey(obligatoire) : votre clé API depuis le tableau de bord.period(obligatoire) : durée de location, parmi1h,1d,3d,30d.energyAmount(obligatoire) : quantité d'énergie à déléguer. Le minimum est15000. Pour un transfert USDT standard vers un destinataire qui détient déjà des USDT,65000est la valeur sûre ; pour un premier transfert vers un nouveau détenteur d'USDT, utilisez130000.destinationAddress(obligatoire) : l'adresse TRON (base58check, commence parT) qui recevra l'énergie déléguée.preActivateDestinationAddress(facultatif, par défaut0) : à définir sur1si l'adresse de destination n'a jamais reçu de TRX et n'est donc pas activée on-chain. Le service envoie alors1.5 TRXdepuis votre solde prépayé pour activer l'adresse avant de déléguer l'énergie. Si l'adresse est déjà active, laissez ce paramètre à0pour éviter le coût supplémentaire.
La réponse est toujours renvoyée avec le statut HTTP 200, que la commande ait réussi ou échoué. Branchez-vous sur le champ status du corps JSON plutôt que sur le statut HTTP. Un corps de succès ressemble à ceci :
{
"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 corps d'erreur contient status: "ERROR", un errorCode lisible par machine et un errorDescription lisible par un humain :
{
"status": "ERROR",
"errorCode": "INVALID_ENERGY_AMOUNT",
"errorDescription": "energyAmount is less than 15000",
"requestId": "71431087-4",
"payload": null
}
Journalisez toujours le requestId dans les deux branches. Si vous devez un jour demander au support de tracer une location spécifique, c'est cet identifiant qu'ils utiliseront pour la rechercher.
Le cycle de vie de la commande
Le champ state dans le payload progresse selon une petite séquence fixe :
PAID_BY_USER: état initial, la commande est payée depuis votre solde mais aucune action on-chain n'a encore eu lieu.WAITING_DELEGATION: le service a pris en charge la commande et prépare la transaction de délégation.ENERGY_DELEGATED: la transaction de délégation a été confirmée on-chain. L'adresse de destination dispose maintenant de l'énergie louée et peut l'utiliser pour la prochaine transaction sortante.ERROR_DELEGATION: la délégation a échoué pour une raison quelconque. Rare, mais possible en cas de forte congestion du réseau.CANCELLED: la commande a été annulée et les fonds ont été remboursés sur votre solde.
Pour vérifier l'état actuel, appelez :
GET https://api.tronenergyrent.com/single-order-details?apiKey=YOUR_API_KEY&orderId=ORDER_ID
La réponse utilise la même enveloppe status / errorCode / payload, avec l'état state actuel à l'intérieur de payload. Interrogez cet endpoint toutes les une à deux secondes après avoir passé la commande, et ne procédez à votre transfert TRC-20 qu'après avoir vu ENERGY_DELEGATED. En pratique, cela représente généralement une ou deux interrogations.
Une intégration Python fonctionnelle
Voici un schéma minimal mais conçu pour la production. Il place la location, interroge jusqu'à ce que la délégation soit on-chain, et fait remonter une erreur claire en cas de problème.
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
Quelques points à souligner. La vérification explicite status == "SUCCESS" est importante car le statut HTTP est toujours 200, donc resp.raise_for_status() seul ne vous dit rien sur le résultat réel. La boucle d'interrogation a un timeout strict pour qu'une commande bloquée ne puisse pas paralyser votre pipeline de transferts indéfiniment. Le branchement sur state gère explicitement les trois issues terminales (ENERGY_DELEGATED, ERROR_DELEGATION, CANCELLED) plutôt que de s'appuyer sur un "non-succès" générique.
Dans votre flux de transfert, appelez rent_energy() d'abord, puis diffusez le transfert TRC-20. L'ordre des opérations est crucial : si vous diffusez d'abord, la transaction consommera des TRX depuis l'expéditeur car la délégation ne sera pas encore confirmée.
Dimensionner votre solde prépayé
Le coût de la location est déduit d'un solde prépayé que vous maintenez sur votre compte tronenergyrent.com. Mettez en place une alerte ou une recharge automatique lorsque le solde descend en dessous d'un seuil. Un seuil raisonnable correspond à ce qui couvre deux heures de volume de pointe.
Pour lire le solde actuel par programme :
GET https://api.tronenergyrent.com/account-info?apiKey=YOUR_API_KEY
Le payload inclut votre solde actuel et l'état de votre compte. Branchez cela sur votre stack de monitoring et vous ne serez jamais surpris par un solde épuisé à 2h du matin. Notez que la recharge minimale sur votre solde tronenergyrent.com est de 10 TRX.
Gestion des erreurs en pratique
Trois modes d'échec reviennent le plus souvent en production. Les codes d'erreur ci-dessous proviennent de la véritable API et vous pouvez vous brancher dessus en toute sécurité.
INSUFFICIENT_BALANCE: votre solde prépayé est trop faible pour couvrir la location demandée plus toute pré-activation éventuelle. Ne réessayez pas, réessayer n'y changera rien. Attrapez cette erreur spécifiquement et déclenchez une alerte ou un flux de recharge. Ajoutez lerequestIdau payload de votre alerte.INVALID_ADDRESS: l'destinationAddressa échoué au décodage base58check côté serveur. Si votre système construit des adresses de manière dynamique, par exemple à partir d'entrées fournies par l'utilisateur, validez-les localement avant d'appeler l'API. La bibliothèque Pythontronpyproposeis_address()pour cela ; rejeter une entrée invalide côté client est plus rapide que d'attendre l'aller-retour.INACTIVE_DESTINATION_ADDRESS_ERROR: l'adresse de destination n'a jamais été activée on-chain et vous n'avez pas demandé au service de l'activer. Corrigez en pré-finançant l'adresse avec une petite quantité de TRX depuis un autre endroit, ou en passantpreActivateDestinationAddress=1lors du prochain appel afin que le service l'active pour1.5 TRX.
Un cas plus subtil : ORDER_IS_ALREADY_IN_PROGRESS peut être renvoyé si plusieurs workers passent des locations pour la même destination au même moment. La correction se fait côté processus, pas côté API. Prenez un verrou distribué (Redis fonctionne très bien) indexé sur l'adresse de destination et maintenu jusqu'à la fin de la délégation.
Vérification on-chain
Pour la plupart des intégrations, interroger /single-order-details jusqu'à ENERGY_DELEGATED est suffisant. Si vous voulez une vérification avec ceinture et bretelles, vous pouvez également interroger directement un full node TRON après que la délégation a été confirmée :
GET https://api.trongrid.io/v1/accounts/{destinationAddress}
La réponse contient l'état actuel des ressources de l'adresse, y compris l'énergie qui lui est déléguée depuis des adresses externes. Le nom exact du champ dépend de la vue Stake 2.0 actuelle, alors consultez la documentation HTTP API TRON en vigueur lors de l'intégration. En tant que contrôle souple qui journalise un avertissement plutôt que de bloquer le transfert, c'est un canari utile pour les rares cas où quelque chose se passe mal en aval de l'API de location elle-même.
Cette vérification supplémentaire vous épargnera des heures de débogage la seule fois où la location semble réussie côté API mais où autre chose se passe mal on-chain.