API năng lượng TRON thực sự làm gì
Một API năng lượng TRON bán đúng một thứ, và thứ đó không phải là mức giảm giá cho một khoản phí. Tài khoản của bạn cần năng lượng để chạy hợp đồng USDT, và khi không có, mạng lưới lấy TRX thay vào đó, 100 sun cho mỗi đơn vị. Một API năng lượng thu xếp để một tài khoản khác — một tài khoản đã stake TRX — cho mượn năng lượng của nó tới địa chỉ sắp ký lần chuyển của bạn, trong vài phút. Mọi thứ bên dưới là hình dạng của các yêu cầu dựng quanh đúng một sự việc trên chuỗi ấy.
Trên chuỗi đó là một lần ủy quyền, và bạn không ký gì cả
Tài khoản cho mượn ký một lệnh ủy quyền nêu tên địa chỉ của bạn. Địa chỉ của bạn không ký gì, không phê duyệt gì và không trao lại gì: mạng lưới nâng hạn mức năng lượng của nó lên, và toàn bộ sự việc chỉ có thế (năng lượng là gì). Chính vì vậy một API năng lượng hỏi một địa chỉ chứ không bao giờ hỏi một khóa — chữ ký duy nhất trong cả thu xếp này thuộc về phía bên kia.
Luồng nhỏ nhất mà chạy được
URL gốc là https://api.nrg.market/v1 và khóa đi trong đúng một header, Authorization: Bearer nrg_live_…. Nó chỉ hiện ra một lần lúc được cấp và có thể ghim vào một danh sách IP. Hãy gọi nó từ máy chủ của bạn: các endpoint cần xác thực không nhận bất kỳ origin trình duyệt nào, vì một khóa đã lên tới trang là một khóa ai cũng có. Còn một URL gốc thứ hai, trên một tên miền con sandbox của nrg.market — vẫn bề mặt /v1 ấy nhưng chạy trên tiền thử và một chuỗi mô phỏng, với khóa nrg_test_… riêng và trang quản lý riêng, và nó được mô tả ở docs.nrg.market.
Hãy bắt đầu bằng POST /v1/estimate. Nó không giữ gì và không tính tiền gì, nhận tối đa 500 người nhận trong một lệnh gọi, và trả về đúng những gì một đơn hàng sẽ tính ra.
curl -s https://api.nrg.market/v1/estimate \
-H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d '{"duration_s":300,
"transfers":[{"from":"TQ5NMqJjW8sBGqcpvUcXhbXm3jGnyDCmwK",
"to":"TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9"}]}'
{"items":[{"to":"TN3W…","kind":"single","energy_units":65000,"warnings":[]}],
"zone":"day","duration_s":300}
# đã lược bớt — câu trả lời còn mang price_trx cho từng mục, total_trx và burn_cost_trx
kind là single với người nhận đã giữ USDT, double với người nhận chưa giữ và cần tạo tài khoản token trước, và custom với một hợp đồng, được tính giá từ một lần chạy thử chứ không từ con số tiêu chuẩn nào trong hai con số kia. from là tùy chọn và không làm giá xê dịch; nêu nó ra thì bạn mua thêm được hai câu trả lời về người gửi — inactive_sender và blacklisted_sender nằm trong warnings — trước khi một đơn hàng ở chế độ bạn đang dùng có thể từ chối vì một trong hai.
Rồi tới đơn hàng. Mode a là chế độ bạn tự phát lần chuyển lên mạng.
curl -s https://api.nrg.market/v1/orders \
-H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-H "Idempotency-Key: 8f3c1e02-6b47-4a1d-9d5c-0b2e7c4a1f90" \
-d '{"mode":"a","client_ref":"invoice-1042",
"transfers":[{"from":"TQ5NMqJjW8sBGqcpvUcXhbXm3jGnyDCmwK",
"to":"TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9"}]}'
Câu trả lời là 202 kèm hai header đáng đọc chứ không nên tự ráp lấy: Location, chính URL của đơn hàng, và Retry-After, số giây chờ trước lần hỏi đầu tiên và giữa các lần hỏi. Phần thân là đơn hàng, thường với status ở funded và send_before vẫn còn null — tiền đã được giữ lại và việc mua đã bắt đầu.
send_before xuất hiện khi năng lượng được xác nhận trên chuỗi và đơn hàng đạt ready. Đó là một cửa sổ khoảng bốn phút, tính từ lần ủy quyền được xác nhận cuối cùng, nên mọi mục trong một lô cùng chung một mốc hết hạn; hãy đọc trường đó chứ đừng mặc định bốn phút. Cũng hãy canh trường đó chứ đừng canh chữ ready: một đơn hàng ở mode A hoàn tất bằng việc giao năng lượng chứ không bằng lần chuyển của bạn, nên completed có thể đến trước khi bạn gửi bất cứ thứ gì.
Có hai lời từ chối đáng nối dây sẵn trước đơn hàng thật đầu tiên: 402 insufficient_balance, được quyết trước khi bất kỳ đồng nào dịch chuyển và mang theo required_trx cùng available_trx, và 422 invalid_address với details.reason đặt ở inactive_wallet — ví gửi chưa từng được kích hoạt, nên không có tài khoản nào để ủy quyền tới (những hỏng hóc đứng sau hai lời này).
Khóa idempotency
POST /v1/orders đòi một Idempotency-Key, và khóa ấy gắn với đúng chuỗi byte thô của phần thân chứ không với JSON đã được phân tích. Cùng những byte đó thì trả lại đúng đơn hàng ban đầu; một phần thân khác dưới cùng một khóa là 409 idempotency_conflict chứ không phải một đơn hàng thứ hai; một yêu cầu bị khâu kiểm tra chặn lại thì không tiêu tốn khóa. Ở cỡ một lô, đó chính là thứ biến một lần hết giờ mạng thành một câu hỏi có câu trả lời.
Các header hạn mức nói gì với bạn
Có ba rổ được đếm cho mỗi khóa API: đơn hàng 120 lần một phút, các yêu cầu tra cứu 600, và GET /v1/address-check 60, vì mỗi lần trượt bộ đệm của nó là một câu hỏi gửi tới chuỗi. Mọi câu trả lời từ một rổ gắn với khóa, kể cả những câu thành công, đều mang RateLimit-Limit, RateLimit-Remaining và RateLimit-Reset, nên một client có thể chậm lại trước lời từ chối chứ không phải sau nó; vượt hạn mức thì là 429 rate_limited kèm Retry-After. Còn một 429 nữa mang nghĩa gần như ngược lại — too_many_auth_failures, ở đó chờ đợi không giúp được gì vì chính tấm thẻ đang bị từ chối. Hãy rẽ nhánh theo code, đừng bao giờ theo message.
Webhooks, để vòng lặp không phải là hỏi lại
Hãy đăng ký một điểm nhận bằng POST /v1/webhooks: url phải là https, phải phân giải ra một host công khai và không mang theo thông tin đăng nhập. Bí mật dùng để ký được trả về đúng một lần, lúc tạo, và mỗi tài khoản giữ tối đa năm điểm nhận đang bật. Hãy đăng ký đúng những gì luồng của bạn cần — order.ready, order.completed, order.partially_completed, order.failed, cộng thêm order.item.sent và order.item.expired theo từng mục. Mỗi lần gửi đều mang X-NRG-Timestamp, X-NRG-Event-Id và X-NRG-Signature, một HMAC-SHA256 trên dấu thời gian, một dấu chấm và phần thân thô: hãy kiểm tra đối chiếu với chính những byte bạn nhận được chứ không phải với JSON bạn mã hóa lại, và khử trùng lặp theo event_id. POST /v1/webhooks/{webhook_id}/test đẩy một lần gửi thật, có ký, đi đúng con đường thật và báo lại điểm nhận của bạn đã trả lời gì, hoàn toàn miễn phí.
Mode B: bạn ký, chúng tôi phát lên mạng
Mode b thay from và to bằng signed_tx, tức giao dịch đã ký ở dạng hex, và chúng tôi phát nó đi ngay khi năng lượng đáp xuống — không ai phải ngồi canh một cửa sổ bốn phút. Những gì được nhận là hẹp một cách có chủ ý: một lệnh gọi transfer của hợp đồng USDT và không gì khác, một chữ ký của chủ sở hữu, không kèm TRX, và đa chữ ký thì bị từ chối, vì bộ khóa của một quyền nằm trên chuỗi và có thể đổi mà chúng tôi không hay biết. Giao dịch phải còn ba phút hiệu lực vào lúc đơn hàng được tạo, và hạn của chính nó làm cửa sổ ngắn lại — send_before không bao giờ chạy quá mốc đó trừ đi một khoảng dự phòng để phát đi. Không kịp chuyển tiếp nó thì mục ấy đóng lại ở failed với broadcast_window_missed, phần giữ lại trả về nguyên vẹn.
Mode C: năng lượng mà không nêu người nhận
Mode c nhận from và kind và không có người nhận nào cả — dành cho một chiếc ví bạn muốn sẵn sàng từ trước khi cái lô kia tồn tại. kind là single hoặc double, và chọn đúng là việc của bạn: không có người nhận thì chẳng có gì để đọc ra khối lượng, nên single ở chỗ lẽ ra phải là double sẽ để lần chuyển thiếu hụt và phần chênh lệch đốt TRX. custom cố ý không được cung cấp ở đây, vì nó là kết quả của một lần chạy thử với một người nhận là hợp đồng cụ thể. Các mục mang to: null, không bao giờ tới sent, và thành expired khi cửa sổ đóng: kỳ hạn thuê đã hết, đó là cái kết bình thường chứ không phải một hỏng hóc — và vẫn bị tính tiền, vì năng lượng đã được giao.
Thứ không bao giờ có trong yêu cầu
Không có trường nào cho một khóa riêng ở bất cứ đâu trong bản hợp đồng, ở bất cứ chế độ nào. Mode A không bao giờ nhìn thấy giao dịch của bạn. Mode B nhìn thấy một giao dịch bạn đã ký sẵn, thứ mà chúng tôi không sửa nổi một byte nếu không phá vỡ chữ ký ấy; nó được lưu ở dạng mã hóa và bị xóa bảy ngày sau khi đơn hàng đạt trạng thái cuối. Mode C không có giao dịch nào để nhìn. Thứ được đưa cho API là các địa chỉ (phần còn lại của những gì chúng tôi giữ và không giữ).
Những phần không cần khóa nào
Các endpoint tra cứu và bản hợp đồng hoàn toàn không cần khóa, và bị giới hạn theo IP chứ không theo khóa. GET /v1/tariff trả lời giá của một đơn vị năng lượng tính bằng sun ngay lúc này, khung giá đang chạy, next_change_at và một lịch tham chiếu — đó là thứ trang bảng giá đọc ở mỗi lần tải. GET /v1/market trả lời những gì các nhà cung cấp chúng tôi theo dõi tự công bố, mỗi con số đều đóng dấu thời điểm nó được đọc, cùng giá của chính chúng tôi xếp hạng trong đó, và đó là trang thị trường. Hãy trích dẫn endpoint chứ đừng trích một con số: giá của chúng tôi được tính lại vài phút một lần.
Bản hợp đồng nằm ở https://docs.nrg.market/openapi.yaml, và giống hệt từng byte là tài liệu ở https://api.nrg.market/v1/contract.yaml — đáng biết, vì host tài liệu nằm sau một lớp biên chặn một số client tự động trước khi yêu cầu tới được chỗ chúng tôi. ETag ở đó là SHA-256 của phần thân, nên If-None-Match nhận về 304 khi không có gì thay đổi. Hãy sinh client của bạn từ chính tệp đó: trang này là một bản tóm tắt, còn tệp kia mới là thứ chúng tôi bị ràng buộc.