Как читать квитанцию транзакции TRON: потраченная энергия, плата за энергию, пропускная способность и результат
Страница транзакции в обозревателе блоков показывает перевод USDT как слово, комиссию и несколько цифр ресурсов. Нода возвращает квитанцию той же транзакции методом gettransactioninfobyid, с именем у каждой цифры: energy_usage, energy_usage_total, origin_energy_usage, energy_penalty_total, energy_fee, net_usage, net_fee и result. energy_fee — это TRX, сожжённые за энергию, которой на аккаунте не было, по 100 sun за единицу, net_fee — TRX, сожжённые за байты, а fee верхнего уровня на каждой квитанции в этом посте — их сумма. У нас хранятся квитанции основной сети для каждого случая, который стоит читать: перевод на арендованной энергии, перевод, оплаченный в TRX, вызовы, где часть энергии оплатил контракт, и переводы, остановившиеся на OUT_OF_ENERGY или REVERT.
Отсутствующее поле означает ноль
Квитанция опускает каждое поле, значение которого ноль. У перевода, не сжигавшего TRX за энергию, нет energy_fee, а у того, чьи байты ничего не стоили, нет net_fee. Суммы — energy_fee, net_fee и fee верхнего уровня — выражены в sun, миллионных долях TRX.
Один перевод на арендованной энергии, один — оплаченный в TRX
Оба перевода ниже потребили 130,285 единиц — цифру для получателя без баланса USDT. Первый наш: 2 сентября 2026 года мы арендовали на JustLend DAO энергию для второго кошелька и отправили с него один USDT (во сколько обошёлся тот заказ). Второй — чужого отправителя, от 6 октября 2026 года, без энергии на аккаунте. Мы прочитали его из списка транзакций отправителя, который выписывает те же поля плоско рядом с транзакцией, нули включительно, и опускает energy_penalty_total.
| Поле | Арендованная энергия, 2 сентября 2026 года | Без энергии, 6 октября 2026 года |
|---|---|---|
Результат: receipt.result или contractRet в списке | SUCCESS | SUCCESS |
energy_usage | 130,285 | 0 |
energy_fee, sun | отсутствует | 13,028,500 |
energy_usage_total | 130,285 | 130,285 |
energy_penalty_total | 100,635 | нет в списке |
net_usage, байты | отсутствует | 0 |
net_fee, sun | 345,000 | 345,000 |
fee, sun | 345,000 | 13,373,500 |
| Комиссия в TRX | 0.345 TRX | 13.3735 TRX |
energy_usage_total — то, что потребил вызов. energy_usage — часть, которую отправитель оплатил энергией, уже бывшей у него, застейканной или делегированной, а energy_fee — остальное, купленное сжиганием TRX по 100 sun за единицу. На нашем переводе первые два равны, а третьего нет, так что каждую единицу оплатила делегация. У другого отправителя энергии не было, и 13,028,500 sun ушли на 130,285 единиц — ровно по 100 sun на каждую.
Строки пропускной способности совпадают. Ни один из аккаунтов не покрыл байты собственной пропускной способностью, поэтому каждый заплатил 345,000 sun: 345 байт по 1,000 sun за байт — это ставка из параметров сети, прочитанных нами 2 сентября 2026 года.
Когда часть энергии оплачивает контракт
Создатель контракта может взять на себя часть энергии каждого вызова в него. Тогда в квитанции есть origin_energy_usage — часть создателя. Остальное вызывающий оплачивает так же, как оплачивается перевод: сначала собственной энергией, потом сжиганием. Рынок энергии JustLend DAO задаёт долю вызывающего в одну десятую (consume_user_resource_percent равен 10), и на всех 83 его квитанциях в нашей выборке часть вызывающего равна energy_usage плюс одна сотая от energy_fee.
| Поле, 2 сентября 2026 года | Наш вызов аренды | Вызов возврата другого арендатора |
|---|---|---|
energy_usage | 8,429 | 24 |
energy_fee, sun | отсутствует | 739,000 |
origin_energy_usage | 75,861 | 66,721 |
energy_usage_total | 84,290 | 74,135 |
| Часть вызывающего, итого минус origin | 8,429 | 7,414 |
net_usage, байты | 383 | отсутствует |
net_fee, sun | отсутствует | 378,000 |
fee, sun | отсутствует | 1,117,000 |
У второго вызывающего было 24 единицы собственной энергии, и он сжёг 739,000 sun за остальные 7,390. У нашего вызова аренды нет fee, хотя он отправил контракту предоплату: эти деньги идут как значение вызова, а fee считает только то, что взяла сеть (куда ушла предоплата). Ни одна из квитанций USDT в этом посте не несёт origin_energy_usage: на каждой собственная энергия отправителя и её сжигание складываются в весь итог.
Отсутствующее origin_energy_usage означает, что создатель не заплатил ничего, а вызывающий оплатил весь вызов. Это самый дорогой случай, и выглядит он ровно как поле, которое никто не заполнил. Доля создателя берётся из собственной энергии создателя, и только пока она остаётся. Во второй половине того же дня, менее чем за три часа до нашей аренды, вызов аренды другого арендатора на том же рынке не нёс origin_energy_usage и оплатил все 52,672 единиц сам, 51,492 из них — сжиганием.
Пропускная способность: net_usage против net_fee
Пропускная способность меряется в байтах подписанной транзакции (энергия и пропускная способность рядом). net_usage считает байты, которые аккаунт покрыл собственной пропускной способностью — бесплатной суточной нормой или стейком под пропускную способность. net_fee — то, что он заплатил в TRX за байты, которые покрыть не смог. В каждой квитанции из имеющихся у нас — либо одно, либо другое.
Наш заказ на JustLend показывает оба. У вызова аренды выше net_usage равен 383, а net_fee нет: его покрыла бесплатная пропускная способность кошелька. У нашего собственного вызова возврата с того же кошелька через 144 секунды, которого нет в таблице выше, нет net_usage, а net_fee равен 378,000 sun. Бесплатная норма — 600 в сутки по getFreeNetLimit сети, и того, что от неё оставила аренда, на возврат не хватило. Из 83 квитанций JustLend в нашей выборке 29 заплатили за свои байты, каждая по 1,000 sun за байт.
Что решает слово результата
Слово встречается в квитанции дважды. Неудавшаяся квитанция несёт result верхнего уровня FAILED, что бы ни пошло не так, и resMessage в hex. Причина этажом ниже, в receipt.result: SUCCESS, OUT_OF_ENERGY или REVERT. У успешной квитанции result верхнего уровня нет вовсе, поэтому скрипт, читающий только внешнее поле, сваливает каждый сбой под одно имя.
Три записи с одного кошелька от 6 октября 2026 года показывают, как возникает OUT_OF_ENERGY. Это один перевод, подписанный трижды, прочитанный из списка транзакций, потому что fee_limit принадлежит подписанной транзакции. Третий — оплаченный в TRX перевод из первой таблицы.
| 6 октября 2026 года | 15:49 UTC | 16:24 UTC | 16:26 UTC |
|---|---|---|---|
fee_limit, sun | 1,100,000 | не задан | 13,046,000 |
Результат, contractRet | OUT_OF_ENERGY | OUT_OF_ENERGY | SUCCESS |
energy_usage_total | 11,000 | 0 | 130,285 |
energy_fee, sun | 1,100,000 | 0 | 13,028,500 |
net_usage, байты | 344 | 0 | 0 |
net_fee, sun | 0 | 339,000 | 345,000 |
| Комиссия в TRX | 1.1 TRX | 0.339 TRX | 13.3735 TRX |
Первая попытка остановилась ровно на 11,000 единиц — это её fee_limit, делённый на 100 sun: нода ограничивает энергию вызова величиной fee_limit, делённой на цену энергии, сколько бы ни держал аккаунт. Вторая не несла fee_limit, а это считается нулём, поэтому потратить она не могла вовсе никакой энергии. В её квитанции нет полей энергии, а её resMessage расшифровывается как «Not enough energy for 'PUSH1' operation executing: curInvokeEnergyLimit[0], …». Три минуты спустя тот же аккаунт оплатил третью попытку, так что пуст он не был. Делегированная энергия подпадает под тот же предел, поэтому наш API и отклоняет перевод в mode B, подписанный без достаточного fee_limit, с fee_limit_too_low, до того как мы покупаем какую-либо энергию.
Два других перевода USDT из тех же нескольких минут показывают оба слова отказа, у каждого на верхнем уровне FAILED.
| Поле, 6 октября 2026 года | Кончилась энергия | Отклонён контрактом |
|---|---|---|
receipt.result | OUT_OF_ENERGY | REVERT |
energy_usage | 44,165 | 8,624 |
energy_fee, sun | 1,383,400 | отсутствует |
energy_usage_total | 57,999 | 8,624 |
energy_penalty_total | 25,905 | 6,640 |
net_fee, sun | 345,000 | 345,000 |
| Комиссия в TRX | 1.7284 TRX | 0.345 TRX |
У первого отправителя было 44,165 единиц собственной энергии, он сжёг TRX ещё на 13,834 и остановился на 57,999 — это всё, что могли купить его энергия и TRX вместе. Его подписанный fee_limit допускал 350,000 единиц, так что на этот раз пределом был аккаунт. Во втором перевод отклонил контракт: resMessage читается «REVERT opcode executed», а USDT при откате причины не называет. Наш справочник API называет две причины: отправитель, у которого уже нет этой суммы, и отправитель, замороженный эмитентом токена (отказы, с которыми встречаются службы выплат).
Только квитанция SUCCESS фиксирует движение токенов: в её log лежит событие Transfer токена, как у нашего арендованного перевода. У квитанций OUT_OF_ENERGY и REVERT лога нет, потому что токены не двигались. Комиссия при этом всё равно потрачена, ведь всё, что вызов потребил до остановки, остаётся оплаченным (почему TRX уходят после OUT_OF_ENERGY).
Когда мы отправляем в сеть подписанный клиентом перевод в mode B, квитанция заодно решает, кто платит за энергию. Квитанция OUT_OF_ENERGY, потратившая меньше, чем нужно переводу, — 64,285 единиц для адреса с USDT или 130,285 для адреса без него, — означает, что нашей энергии на адресе в момент вызова не было. Позиция закрывается как expired, и мы берём за неё плату — если только наши записи не показывают, что продавец забрал делегацию до блока этого перевода или что её потратил другой ваш перевод с того же адреса. Тогда нехватка наша — как и в том случае, когда квитанция потратила не меньше требуемого и всё равно остановилась, потому что наша оценка оказалась занижена: позиция закрывается как failed, и мы возвращаем резерв. Одна из имеющихся у нас квитанций потребила 108,047 единиц и всё равно остановилась: на переводе, который мы рассчитали под получателя с USDT, эта нехватка была бы нашей. За REVERT мы тоже берём плату.
Штраф входит в итог
На нашем арендованном переводе итог — 130,285 единиц, кошелёк списал со своей делегации ровно 130,285, и 100,635 из этих единиц несут метку штрафа. Неудавшиеся переводы согласуются: энергия с аккаунта плюс сожжённая энергия дают итог, штраф включительно.
Значения, которые у нас есть: 100,635 на завершённом переводе на адрес без USDT, 25,905 на двух переводах, у которых кончилась энергия, и 6,640 на том, который отклонил контракт. Dry-run перевода на ноде сообщает соответствующий energy_penalty: 100,635 рядом с 130,285 для получателя без USDT и 49,635 рядом с 64,285 для того, у кого он есть. В параметрах сети, прочитанных нами 2 сентября 2026 года, динамическая энергия включена (getAllowDynamicEnergy равен 1), и название подходит штрафу, растущему вместе с более дорогим получателем. Арифметику ноды мы с этими параметрами не сверяли, поэтому за что именно берётся штраф, здесь по-прежнему не измерено.
Как проверить аренду по её квитанции
После аренды энергии два поля квитанции перевода говорят, справилась ли аренда со своей задачей: energy_fee отсутствует, а energy_usage плюс любой origin_energy_usage равен energy_usage_total. energy_fee на переводе, под который вы арендовали энергию, означает, что часть его энергии всё же куплена за TRX. Делегация была меньше вызова, не успела дойти к моменту вызова, уже была отозвана или раньше была потрачена другим переводом с того же кошелька (как делегация начинается и кончается). Сам аккаунт можно прочитать до отправки (как безопасно арендовать энергию).
Отдайте нам подписанный перевод в mode B — и квитанцию читаем мы, позиция за позицией: все причины, с которыми может закрыться позиция, — в справочнике API →