วิธีทำให้การเช่าพลังงาน TRON เป็นอัตโนมัติด้วย API
ทำไมต้องทำให้การเช่าพลังงานเป็นอัตโนมัติ
ถ้าคุณรันฮอตวอลเล็ต, เอนจินถอนเงินของกระดานเทรด หรือสัญญาที่ทริกเกอร์การโอน TRC-20 แทนผู้ใช้ คุณจะรู้ปัญหานี้อยู่แล้ว: คุณต้องการให้พลังงาน TRON พร้อมใช้งานในวินาทีที่ธุรกรรมจะถูกยิงออกไป การเติมพลังงานด้วยมือผ่าน UI ไม่สามารถสเกลเกินกว่าการโอนเพียงไม่กี่ครั้งต่อวันได้ ถ้าคุณพลาดจังหวะ ธุรกรรมจะเผา TRX จากที่อยู่ผู้ส่งแทน ซึ่งมักมีต้นทุนสูงกว่าพลังงานที่เช่าไว้ล่วงหน้ามาก
วิธีแก้คือเรียก API เช่าพลังงานในจังหวะที่แบ็กเอนด์ของคุณกำลังจะบรอดแคสต์ธุรกรรม เพื่อให้พลังงานถูกจัดสรรโดยโปรแกรมโดยไม่มีมนุษย์อยู่ในลูป บทความนี้จะอธิบายวิธีทำสิ่งนั้นกับ tronenergyrent.com แบบเจาะลึก: รูปแบบคำขอจริง, วงจรชีวิตของคำสั่งซื้อแบบ asynchronous, การเลือกระยะเวลาที่เหมาะสม และตัวอย่าง Python ที่ใช้งานได้จริงที่คุณสามารถนำไปใส่ในเซอร์วิสของคุณได้เลย
API ทำอะไรจริงๆ บน On-Chain
ก่อนเขียนโค้ด การเข้าใจกลไก on-chain จะช่วยให้คุณไม่ต้องดีบักแบบมืดบอด
โมเดลทรัพยากรของ TRON แบ่งการคำนวณ (พลังงาน) ออกจากขนาดธุรกรรม (bandwidth) การโอน USDT TRC-20 มาตรฐานหนึ่งครั้งใช้พลังงานประมาณ 65,000 เมื่อผู้รับมี USDT อยู่แล้ว และประมาณ 130,000 เมื่อยอดคงเหลือ USDT ของผู้รับเป็นศูนย์ การโอนเข้าครั้งแรกจะจ่ายค่าจัดเก็บสำหรับการสร้างรายการยอดคงเหลือ ซึ่งเป็นเหตุผลที่ต้นทุนขึ้นอยู่กับผู้รับทั้งหมด ไม่ใช่ผู้ส่ง การใช้ bandwidth สำหรับการโอนอยู่ที่ราว 345 ไบต์ ซึ่งเล็กพอที่บัญชีส่วนใหญ่จะครอบคลุมจากโควต้าฟรีรายวันได้
เมื่อคุณเรียก API เช่าพลังงาน เซอร์วิสจะ stake TRX ของตัวเองและมอบหมาย (delegate) พลังงานที่ได้ให้กับที่อยู่ที่คุณระบุ โดยใช้ DelegateResourceContract จาก Stake 2.0 การ delegate เป็นแบบเฉพาะที่อยู่และมีกำหนดเวลา เมื่อระยะเวลาเช่าหมดอายุ พลังงานจะถูกผู้ให้บริการเรียกคืนโดยอัตโนมัติ
รายละเอียดสำคัญข้อหนึ่ง: การเช่าเป็นแบบ asynchronous การเรียก API จะตอบกลับทันทีด้วย orderId และ state เป็น PAID_BY_USER แต่ธุรกรรม delegation แบบ on-chain จะถูกบรอดแคสต์ในเบื้องหลังและมักจะลงบล็อกภายในไม่กี่วินาที การอินทิเกรตของคุณควรปฏิบัติต่อการตอบกลับครั้งแรกว่าเป็นการยืนยันว่าคำสั่งซื้อได้รับการยอมรับและชำระเงินแล้ว จากนั้นจึง poll ที่ endpoint รายละเอียดคำสั่งซื้อจนกว่า state จะเปลี่ยนเป็น ENERGY_DELEGATED
การเลือกระยะเวลาเช่าที่เหมาะสม
API รับพารามิเตอร์ period โดยมีค่าที่อนุญาตสี่ค่า: 1h, 1d, 3d, 30d ราคาผันผวนตามตลาดพลังงาน on-chain และเปลี่ยนแปลงระหว่างวัน ดังนั้นตัวเลขสดอยู่ที่หน้าราคาเสมอ ลำดับเชิงเปรียบเทียบนั้นคงที่: 1h ถูกที่สุดต่อการเรียกหนึ่งครั้ง, 30d ถูกที่สุดเมื่อเฉลี่ยข้ามการโอนหลายครั้งจากที่อยู่เดียวกัน
สำหรับระบบที่ขับเคลื่อนด้วยอีเวนต์ที่คุณทริกเกอร์การเช่าหนึ่งครั้งต่อการโอนออกหนึ่งครั้ง ระดับ 1h มักจะเป็นตัวเลือกที่ถูกต้องเกือบทุกครั้ง คุณจ่ายต้นทุนสัมบูรณ์ต่ำสุดและพลังงานจะถูกใช้ภายในไม่กี่วินาทีหลังจากถูก delegate ระดับ 1d และยาวกว่านั้นเหมาะกับการที่คุณรันงาน batch ที่คาดเดาได้ เช่น งานจ่ายเงินช่วงกลางคืน และต้องการเช่าบล็อกพลังงานขนาดใหญ่หนึ่งครั้งแทนที่จะเรียก API หลายสิบครั้ง
ถ้าระบบของคุณยิงการโอนมากกว่า 20-30 ครั้งต่อชั่วโมงจากที่อยู่เดียวกันอย่างสม่ำเสมอ การเช่าบล็อก 1d ที่มีขนาดครอบคลุมปริมาณที่คาดการณ์ไว้นั้นสะอาดกว่าการเรียกแบบต่อการโอน ต้นทุนเริ่มต้นสูงขึ้น แต่ overhead ของ API และความหน่วงในการยืนยัน on-chain จะหายไปจาก hot path
การยืนยันตัวตนและโครงสร้างคำขอ
Endpoint สำหรับเช่าพลังงานอยู่ที่:
GET https://api.tronenergyrent.com/place-energy-order
เป็นคำขอ GET ธรรมดาพร้อม query parameter การยืนยันตัวตนคือ query parameter ชื่อ apiKey ซึ่งคุณสร้างได้จากแดชบอร์ดของคุณหลังจากลงทะเบียนและเติมเงินเข้าบัญชี ไม่มีการยืนยันตัวตนแบบ header และไม่มี request body แบบ JSON สำหรับ endpoint นี้
พารามิเตอร์มีดังนี้:
apiKey(จำเป็น): คีย์ API ของคุณจากแดชบอร์ดperiod(จำเป็น): ระยะเวลาเช่า หนึ่งในค่า1h,1d,3d,30denergyAmount(จำเป็น): จำนวนพลังงานที่จะ delegate ค่าต่ำสุดคือ15000สำหรับการโอน USDT มาตรฐานหนึ่งครั้งไปยังผู้รับที่มี USDT อยู่แล้ว65000เป็นตัวเลขที่ปลอดภัย, สำหรับการโอนครั้งแรกไปยังผู้ถือ USDT รายใหม่ ให้ใช้130000destinationAddress(จำเป็น): ที่อยู่ TRON (base58check, ขึ้นต้นด้วยT) ที่จะได้รับพลังงานที่ถูก delegatepreActivateDestinationAddress(ทางเลือก, ค่าเริ่มต้น0): ตั้งเป็น1ถ้าที่อยู่ปลายทางไม่เคยได้รับ TRX และจึงไม่ได้ถูกเปิดใช้งานบน on-chain เซอร์วิสจะส่ง1.5 TRXจากยอดเงินที่จ่ายล่วงหน้าของคุณเพื่อเปิดใช้งานที่อยู่นั้นก่อนทำการ delegate พลังงาน ถ้าที่อยู่ถูกเปิดใช้งานแล้ว ให้ค่านี้เป็น0เพื่อหลีกเลี่ยงค่าใช้จ่ายเพิ่มเติม
การตอบกลับจะถูกส่งกลับด้วย HTTP status 200 เสมอ ไม่ว่าคำสั่งซื้อจะสำเร็จหรือล้มเหลว ให้แตกเงื่อนไขจากฟิลด์ status ในเนื้อหา JSON แทน HTTP status เนื้อหาที่สำเร็จมีลักษณะดังนี้:
{
"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
}
บันทึก log requestId เสมอทั้งสองสาขา ถ้าคุณจำเป็นต้องขอให้ทีมซัพพอร์ตติดตามการเช่าเฉพาะรายการ ID นี้คือสิ่งที่พวกเขาใช้ค้นหา
วงจรชีวิตของคำสั่งซื้อ
ฟิลด์ state ใน payload จะดำเนินไปตามลำดับคงที่สั้นๆ:
PAID_BY_USER: state เริ่มต้น คำสั่งซื้อได้รับการชำระเงินจากยอดเงินของคุณแล้วแต่ยังไม่มีการกระทำใดๆ บน on-chainWAITING_DELEGATION: เซอร์วิสได้รับคำสั่งซื้อแล้วและกำลังเตรียมธุรกรรม delegationENERGY_DELEGATED: ธุรกรรม delegation ลงบล็อกบน on-chain แล้ว ที่อยู่ปลายทางตอนนี้ถือพลังงานที่เช่าและสามารถใช้กับธุรกรรมส่งออกครั้งต่อไปได้ERROR_DELEGATION: การ delegation ล้มเหลวด้วยเหตุผลบางอย่าง พบได้น้อย แต่เป็นไปได้ในช่วงที่เครือข่ายแออัดหนักCANCELLED: คำสั่งซื้อถูกยกเลิกและคืนเงินกลับเข้ายอดเงินของคุณ
ในการตรวจสอบ state ปัจจุบัน เรียก:
GET https://api.tronenergyrent.com/single-order-details?apiKey=YOUR_API_KEY&orderId=ORDER_ID
การตอบกลับใช้ envelope status / errorCode / payload เดียวกัน โดยมี state ปัจจุบันอยู่ภายใน payload ให้ poll ค่านี้ทุกๆ หนึ่งหรือสองวินาทีหลังจากวางคำสั่งซื้อ และดำเนินการโอน TRC-20 ของคุณก็ต่อเมื่อคุณเห็น ENERGY_DELEGATED เท่านั้น ในทางปฏิบัติ มักจะเป็นการ poll หนึ่งหรือสองครั้ง
การอินทิเกรต Python ที่ใช้งานได้จริง
นี่คือรูปแบบที่น้อยที่สุดแต่มีรูปทรงพร้อมใช้งานจริง โค้ดจะวางคำสั่งซื้อเช่า, poll จนกว่า delegation จะอยู่บน on-chain และแจ้งข้อผิดพลาดที่ชัดเจนถ้ามีอะไรผิดพลาด
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 status เป็น 200 เสมอ ดังนั้น resp.raise_for_status() เพียงอย่างเดียวไม่ได้บอกอะไรเกี่ยวกับผลลัพธ์จริง ลูป polling มี timeout แบบเด็ดขาด เพื่อไม่ให้คำสั่งซื้อที่ค้างไม่บล็อกไปป์ไลน์การโอนของคุณอย่างไม่มีกำหนด การแตกเงื่อนไขบน state จัดการผลลัพธ์ปลายทางสามแบบ (ENERGY_DELEGATED, ERROR_DELEGATION, CANCELLED) อย่างชัดเจน แทนที่จะพึ่งพาเงื่อนไขทั่วไปแบบ "ไม่สำเร็จ"
ในเวิร์กโฟลว์การโอนของคุณ ให้เรียก rent_energy() ก่อน แล้วจึงบรอดแคสต์การโอน TRC-20 ลำดับของการดำเนินการสำคัญ: ถ้าบรอดแคสต์ก่อน ธุรกรรมจะเผา TRX จากผู้ส่งเพราะ delegation ยังไม่ได้ลงบล็อก
การกำหนดขนาดยอดเงินที่จ่ายล่วงหน้า
ค่าเช่าจะถูกหักจากยอดเงินที่จ่ายล่วงหน้าซึ่งคุณรักษาไว้ในบัญชี tronenergyrent.com ของคุณ ตั้งค่าการแจ้งเตือนหรือการเติมเงินอัตโนมัติเมื่อยอดเงินต่ำกว่าเกณฑ์ที่กำหนด ขั้นต่ำที่สมเหตุสมผลคือยอดที่ครอบคลุมปริมาณช่วงพีคสองชั่วโมง
ในการอ่านยอดเงินปัจจุบันแบบโปรแกรม:
GET https://api.tronenergyrent.com/account-info?apiKey=YOUR_API_KEY
Payload รวมถึงยอดเงินปัจจุบันและ state ของบัญชีคุณ ต่อสายเข้ากับ stack การมอนิเตอร์ของคุณ แล้วคุณจะไม่ประหลาดใจกับยอดเงินที่หมดเกลี้ยงตอนตีสอง โปรดทราบว่าการเติมเงินขั้นต่ำเข้ายอดเงิน tronenergyrent.com ของคุณคือ 10 TRX
การจัดการข้อผิดพลาดในทางปฏิบัติ
โหมดความล้มเหลวสามแบบเกิดขึ้นบ่อยที่สุดในการใช้งานจริง รหัสข้อผิดพลาดด้านล่างมาจาก API จริงและคุณสามารถแตกเงื่อนไขจากรหัสเหล่านั้นได้อย่างปลอดภัย
INSUFFICIENT_BALANCE: ยอดเงินที่จ่ายล่วงหน้าของคุณต่ำเกินไปที่จะครอบคลุมการเช่าที่ร้องขอบวกกับการเปิดใช้งานล่วงหน้าใดๆ อย่าลองใหม่ การลองใหม่จะไม่แก้ไขปัญหา ดักจับกรณีนี้โดยเฉพาะและทริกเกอร์การแจ้งเตือนหรือโฟลว์การเติมเงิน เพิ่มrequestIdลงใน payload การแจ้งเตือนของคุณINVALID_ADDRESS:destinationAddressล้มเหลวในการ decode แบบ base58check บนเซิร์ฟเวอร์ ถ้าระบบของคุณสร้างที่อยู่แบบไดนามิก เช่นจาก input ของผู้ใช้ ให้ตรวจสอบความถูกต้องในเครื่องก่อนเรียก API ไลบรารี Pythontronpyมีis_address()สำหรับสิ่งนี้, การปฏิเสธ input ที่ไม่ดีฝั่ง client เร็วกว่าการรอ round tripINACTIVE_DESTINATION_ADDRESS_ERROR: ที่อยู่ปลายทางไม่เคยถูกเปิดใช้งานบน on-chain และคุณไม่ได้ขอให้เซอร์วิสเปิดใช้งานให้ แก้ไขโดยการเติมเงินที่อยู่ล่วงหน้าด้วย TRX จำนวนเล็กน้อยจากที่อื่น หรือโดยส่งpreActivateDestinationAddress=1ในการเรียกครั้งถัดไป เพื่อให้เซอร์วิสเปิดใช้งานให้ในราคา1.5 TRX
กรณีที่ละเอียดอ่อนกว่า: ORDER_IS_ALREADY_IN_PROGRESS อาจกลับมาถ้าคุณมีหลาย worker วางคำสั่งซื้อเช่าสำหรับปลายทางเดียวกันในเวลาเดียวกัน วิธีแก้อยู่ที่ฝั่ง process ไม่ใช่ฝั่ง API ใช้ distributed lock (Redis ใช้ได้ดี) ที่ key ด้วยที่อยู่ปลายทางและถือไว้จนกว่า delegation จะเสร็จสิ้น
การยืนยันบน On-Chain
สำหรับการอินทิเกรตส่วนใหญ่ การ poll /single-order-details จนกว่าจะได้ ENERGY_DELEGATED นั้นเพียงพอ ถ้าคุณต้องการการยืนยันแบบเข็มขัดและสายรัด คุณสามารถสอบถาม TRON full node โดยตรงหลังจาก delegation ลงบล็อกได้:
GET https://api.trongrid.io/v1/accounts/{destinationAddress}
การตอบกลับมี state ทรัพยากรปัจจุบันของที่อยู่ รวมถึงพลังงานที่ถูก delegate ให้กับที่อยู่นั้นจากที่อยู่ภายนอก ชื่อฟิลด์ที่แน่นอนขึ้นอยู่กับ view ปัจจุบันของ Stake 2.0 ดังนั้นให้ปรึกษาเอกสาร TRON HTTP API สดเมื่อต่อสายสิ่งนี้เข้า ในฐานะการตรวจสอบแบบเบาที่บันทึก log คำเตือนแทนการบล็อกการโอน มันเป็นนกขมิ้นที่มีประโยชน์สำหรับกรณีที่หายากเมื่อมีบางสิ่งผิดพลาดในส่วนปลายน้ำของ API เช่าพลังงานเอง
การตรวจสอบเพิ่มเติมนั้นจะช่วยประหยัดเวลาดีบักเป็นชั่วโมงในครั้งเดียวที่การเช่าดูเหมือนสำเร็จใน API แต่มีบางสิ่งอื่นผิดพลาดบน on-chain