چگونه اجاره انرژی TRON را با API خودکار کنیم
چرا اصلاً باید اجاره انرژی را خودکار کرد
اگر یک کیف پول داغ، یک موتور برداشت صرافی، یا یک قرارداد را اجرا میکنید که از طرف کاربران انتقالهای TRC-20 را راهاندازی میکند، این درد را از قبل میشناسید: به محض اینکه تراکنشی شلیک میشود، باید انرژی TRON آماده داشته باشید. شارژ دستی انرژی از طریق یک رابط کاربری برای بیش از چند انتقال در روز مقیاسپذیر نیست. اگر فرصت را از دست بدهید، تراکنش به جای آن 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 بازمیگردد، اما تراکنش واگذاری روی زنجیره در پسزمینه پخش میشود و معمولاً ظرف چند ثانیه ثبت میشود. یکپارچهسازی شما باید پاسخ اولیه را به عنوان تأیید پذیرفته و پرداخت شدن سفارش در نظر بگیرد، سپس endpoint جزئیات سفارش را تا زمانی که وضعیت به ENERGY_DELEGATED منتقل شود فراخوانی (poll) کند.
انتخاب مدت زمان مناسب اجاره
این API یک پارامتر period با چهار مقدار مجاز میپذیرد: 1h, 1d, 3d, 30d. قیمتها با بازار انرژی روی زنجیره شناور هستند و در طول روز تغییر میکنند، بنابراین اعداد زنده همیشه در صفحه قیمتگذاری قرار دارند. ترتیب نسبی پایدار است: 1h به ازای هر فراخوانی ارزانترین است، 30d زمانی که در میان انتقالهای متعدد از یک آدرس مستهلک شود، ارزانترین است.
برای یک سیستم رویدادمحور که در آن به ازای هر انتقال خروجی یک اجاره راهاندازی میکنید، رده 1h تقریباً همیشه انتخاب درست است. کمترین هزینه مطلق را میپردازید و انرژی ظرف چند ثانیه از زمان واگذاری مصرف میشود. ردههای 1d و طولانیتر زمانی منطقی هستند که در حال اجرای بارهای کاری دستهای قابل پیشبینی هستید، برای مثال یک کار پرداخت شبانه، و میخواهید یک بلوک بزرگ انرژی را یک بار اجاره کنید به جای فراخوانی دهها باره API.
اگر سیستم شما به طور مداوم بیش از 20 تا 30 انتقال در ساعت از یک آدرس شلیک میکند، اجاره یک بلوک 1d با اندازهای که حجم مورد انتظار شما را پوشش دهد، تمیزتر از فراخوانیهای هر انتقال است. هزینه اولیه افزایش مییابد اما سربار API و تأخیر تأیید روی زنجیره از مسیر داغ ناپدید میشوند.
احراز هویت و ساختار درخواست
endpoint اجاره در این آدرس قرار دارد:
GET https://api.tronenergyrent.com/place-energy-order
این یک درخواست GET ساده با پارامترهای کوئری است. احراز هویت از طریق پارامتر کوئری apiKey انجام میشود، که آن را پس از ثبتنام و شارژ حساب خود از داشبورد تولید میکنید. هیچ احراز هویت مبتنی بر هدر و هیچ بدنه درخواست JSON برای این endpoint وجود ندارد.
پارامترها عبارتند از:
apiKey(الزامی): کلید API شما از داشبورد.period(الزامی): مدت زمان اجاره، یکی از1h,1d,3d,30d.energyAmount(الزامی): مقدار انرژی که باید واگذار شود. حداقل15000است. برای یک انتقال استاندارد USDT به گیرندهای که از قبل USDT دارد،65000عدد امن است؛ برای اولین انتقال به یک دارنده تازه USDT، از130000استفاده کنید.destinationAddress(الزامی): آدرس TRON (base58check، باTشروع میشود) که انرژی واگذار شده را دریافت خواهد کرد.preActivateDestinationAddress(اختیاری، پیشفرض0): اگر آدرس مقصد هرگز TRX دریافت نکرده و در نتیجه روی زنجیره فعال نشده است، آن را روی1تنظیم کنید. سپس سرویس1.5 TRXاز موجودی پیشپرداخت شما را ارسال میکند تا آدرس را قبل از واگذاری انرژی فعال کند. اگر آدرس از قبل فعال است، برای جلوگیری از هزینه اضافی این مقدار را روی0بگذارید.
پاسخ همیشه با وضعیت HTTP 200 بازگردانده میشود، صرف نظر از اینکه سفارش موفق یا ناموفق بوده است. به جای وضعیت HTTP، روی فیلد status در بدنه JSON شاخهبندی کنید. یک بدنه موفق به این شکل است:
{
"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 در 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. این را پس از قرار دادن سفارش هر یک یا دو ثانیه یک بار poll کنید و فقط پس از اینکه ENERGY_DELEGATED را دیدید با انتقال TRC-20 خود ادامه دهید. در عمل این معمولاً یک یا دو poll است.
یک یکپارچهسازی کاربردی Python
اینجا یک الگوی حداقلی اما با شکل آماده برای محیط تولید است. اجاره را قرار میدهد، تا زمانی که واگذاری روی زنجیره ثبت شود poll میکند، و اگر اشتباهی پیش بیاید یک خطای واضح نشان میدهد.
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() به تنهایی هیچ چیزی درباره نتیجه واقعی به شما نمیگوید. حلقه poll دارای یک timeout سخت است تا یک سفارش گیر کرده نتواند خط لوله انتقال شما را به طور نامحدود مسدود کند. شاخهبندی روی 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 شامل موجودی فعلی و وضعیت حساب شما است. آن را به پشته نظارت خود متصل کنید و هرگز در ساعت 2 بامداد با یک موجودی تخلیه شده غافلگیر نخواهید شد. توجه داشته باشید که حداقل شارژ به موجودی tronenergyrent.com شما 10 TRX است.
مدیریت خطا در عمل
سه حالت شکست بیشتر از همه در محیط تولید پیش میآیند. کدهای خطای زیر از API واقعی میآیند و میتوانید با خیال راحت روی آنها شاخهبندی کنید.
INSUFFICIENT_BALANCE: موجودی پیشپرداخت شما برای پوشش اجاره درخواستی به علاوه هرگونه پیشفعالسازی بسیار کم است. دوباره تلاش نکنید، تلاش مجدد آن را برطرف نخواهد کرد. این را به طور خاص بگیرید و یک هشدار یا جریان شارژ راهاندازی کنید.requestIdرا به payload هشدار خود اضافه کنید.INVALID_ADDRESS:destinationAddressدر رمزگشایی base58check روی سرور شکست خورد. اگر سیستم شما آدرسها را به صورت پویا میسازد، برای مثال از ورودی کاربر، قبل از فراخوانی API آنها را به صورت محلی اعتبارسنجی کنید. کتابخانه Python به نامtronpyبرای این کارis_address()را دارد؛ رد کردن ورودی بد در سمت مشتری سریعتر از انتظار برای رفت و برگشت است.INACTIVE_DESTINATION_ADDRESS_ERROR: آدرس مقصد هرگز روی زنجیره فعال نشده است و شما از سرویس نخواستید آن را فعال کند. این را با پیششارژ کردن آدرس با مقدار کمی TRX از جای دیگر، یا با ارسالpreActivateDestinationAddress=1در فراخوانی بعدی برطرف کنید تا سرویس آن را با1.5 TRXفعال کند.
یک حالت ظریفتر: ORDER_IS_ALREADY_IN_PROGRESS میتواند بازگردد اگر چندین کارگر همزمان برای یک مقصد یکسان اجاره قرار میدهند. راهحل سمت فرآیند است، نه سمت API. یک قفل توزیعشده (Redis به خوبی کار میکند) بگیرید که با آدرس مقصد کلیدبندی شده و تا تکمیل واگذاری نگه داشته شود.
تأیید روی زنجیره
برای بیشتر یکپارچهسازیها، poll کردن /single-order-details تا ENERGY_DELEGATED کافی است. اگر میخواهید تأیید کمربند و بند شلوار داشته باشید، میتوانید پس از ثبت واگذاری مستقیماً نود کامل TRON را نیز پرسوجو کنید:
GET https://api.trongrid.io/v1/accounts/{destinationAddress}
پاسخ شامل وضعیت منابع فعلی آدرس است، از جمله انرژی واگذار شده به آن از آدرسهای خارجی. نام دقیق فیلد به نمای فعلی Stake 2.0 بستگی دارد، بنابراین هنگام اتصال این مورد به مستندات HTTP API زنده TRON مراجعه کنید. به عنوان یک بررسی نرم که به جای مسدود کردن انتقال یک هشدار ثبت میکند، یک قناری مفید برای مورد نادری است که زمانی که چیزی در پاییندست خود API اجاره اشتباه پیش میرود.
آن بررسی اضافی ساعتها اشکالزدایی را در یک باری که اجاره در API موفق به نظر میرسد اما چیز دیگری روی زنجیره اشتباه پیش میرود، نجات خواهد داد.