OUT_OF_ENERGY: o erro da TRON que gasta a taxa e não envia nada
Uma transferência de USDT sai, a carteira mostra uma taxa cobrada, e o destinatário não recebeu nada. A transação está num bloco — não foi rejeitada e nenhum nó estava fora do ar — e onde uma transferência concluída diz SUCCESS, esta diz OUT_OF_ENERGY. Na TRON esse erro quer dizer exatamente o que está escrito: a chamada ficou sem energia no meio do caminho e parou. Aqui está o mecanismo, os três jeitos de uma rodada de pagamentos cair nele, e as correções, da mais rápida à que de fato custa menos.
O que o resultado realmente diz
Enviar USDT é uma chamada a um contrato inteligente, e a execução de contrato na TRON é medida em energia em vez de cobrada como taxa fixa (o que é energia). A energia do próprio remetente é gasta primeiro. Quando ela não cobre a chamada, a rede não para aí — ela compra o resto queimando o TRX do remetente pela taxa do protocolo de 100 sun por unidade, mas só até onde o fee_limit da própria transação permite. Esse campo não é uma taxa que alguém cobra. É um teto de quanto do TRX do remetente esta chamada tem permissão para queimar.
Chegue ao teto com a chamada inacabada e a execução para. Toda mudança de estado que ela tinha feito é desfeita, então token nenhum se move, e a rede escreve o veredicto dela na transação, no lugar em que uma chamada concluída traz SUCCESS. Esse veredicto é a palavra que um explorador mostra ao lado de uma transação que falhou, e o campo que um nó devolve como contractRet. Nada na transação deixou de chegar à rede. Ela chegou à rede e foi executada até o dinheiro para isso acabar.
O TRX é gasto de qualquer jeito
O desfazimento desfaz a transferência. Ele não desfaz a queima: energia consumida é energia paga, e o remetente fica com o prejuízo de tudo o que a tentativa queimou no caminho até o teto, sem token nenhum movido, e com um hash de transação que um script de conciliação vai alegremente arquivar como pagamento.
O que faz da repetição reflexa a jogada cara. A mesma transferência com o mesmo limite gasta o mesmo TRX pelo mesmo resultado, e um laço de pagamentos que repete as falhas dele sozinho pode fazer isso várias vezes antes de um humano ler a string de resultado. A maior parte das falhas que uma mesa de pagamentos encontra custa dinheiro em silêncio. Esta custa dinheiro em voz alta e ainda assim passa batido, porque a carteira informa uma taxa e o dinheiro não está em lugar nenhum.
Por que um limite que funcionava ontem para de funcionar
O destinatário mudou e o limite não. Este é o primeiro que uma rodada de pagamentos encontra. Uma transferência para um endereço que já tem USDT leva cerca de 65,000 unidades de energia; para um endereço que nunca teve, a transferência também precisa criar a conta de token do destinatário, e isso dá cerca de 131,000 — o dobro do apetite para a mesma quantia de dinheiro movida. Queimar pela primeira custa 6.5 TRX pela taxa do protocolo, e pela segunda, 13.1 TRX. Um teto dimensionado para o caso comum fica curto pela metade na primeira vez que um cliente novo é pago, e nada no lote parece diferente: é uma propriedade do destinatário, não da transferência.
A energia estava lá e não está mais. Uma delegação é uma quantidade por um prazo, não uma assinatura. O que a primeira transferência gasta não está lá para a segunda, e quando o prazo termina o resto volta para o dono. Uma carteira que já enviou uma vez na mesma janela tem menos do que o número anunciado da delegação sugere, e é a transferência mais tardia que trava — o que, de fora, parece a mesma transferência falhando ao acaso.
O limite é zero. Zero é o valor certo quando a energia é certa, e no mode B, onde nós entregamos a energia e transmitimos a transação, fee_limit é o que pedimos que você deixe sem definir: um limite ali não seria nada além de uma permissão permanente para queimar o TRX do remetente se algo desse errado. Também é uma barreira rígida. Permissão zero quer dizer que uma chamada curta por uma única unidade trava em vez de pagar por ela, então um limite deixado em zero é uma decisão que só vale enquanto a energia valer.
Ver isso antes de a transação ser assinada
A pergunta por trás do primeiro gatilho — quanta energia este destinatário custa — dá para responder antes de qualquer coisa ser assinada, com duas chamadas que não reservam nada e não cobram nada.
GET /v1/address-check responde por um endereço: activated, holds_usdt, blacklisted, is_contract, e o expected_kind que decorre deles. holds_usdt é o que define o tamanho da chamada.
curl -s -H "Authorization: Bearer $KEY" \ "https://api.nrg.market/v1/address-check?address=TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9" # expected_kind é "single", "double" ou "custom"
POST /v1/estimate responde por um lote — até 500 destinatários numa chamada, cada um com um kind, as energy_units por trás dele e quaisquer warnings. single é a transferência comum e double a que abre uma conta de token. custom é o item que regra de bolso nenhuma cobre: o destinatário acaba sendo um contrato com lógica de transfer própria, então nenhum dos dois números padrão se aplica e energy_units vem de um dry-run da chamada de verdade. São esses os itens de que dimensionar um limite um a um, e o aviso contract_recipient ao lado deles é muitas vezes o primeiro sinal de que um endereço de pagamento não é carteira nenhuma.
As correções, da mais fácil em diante
Suba o limite. Um campo, nenhuma infraestrutura, e a parada some: a chamada termina e a rede pega o que precisa. O custo não some junto. Um limite é permissão para queimar, então um limite generoso é uma queima generosa — a transferência que travava agora se completa a 13.1 TRX. Certo como piso embaixo de uma rodada de pagamentos, errado como plano.
Ponha energia no remetente. Aí não sobra nada para o limite permitir. Energia vem de fazer stake de TRX, o que significa travar um saldo de trabalho e ficar de olho num orçamento de recurso, ou de alugar uma delegação pelos poucos minutos de que a transferência precisa (quanto isso custa agora). De um jeito ou de outro a chamada é paga em energia e queima nenhuma acontece.
Envie dentro da janela. Energia alugada fica na carteira por um prazo, e a resposta de um pedido traz send_before — o instante em que a janela de entrega fecha, três segundos antes de o prazo comprado acabar. Fique de olho nesse campo aparecendo, e não na palavra ready: o pedido é cumprido pela entrega de energia, então completed vem logo em seguida. Enviar depois do send_before põe a transferência na rede quando a energia já pode ter voltado para casa, que é exatamente a falta de que este texto trata.
Entregue a transação assinada. O mode B tira a janela do seu lado por completo: você posta uma transferência já assinada, nós a seguramos, e a transmitimos assim que a energia é confirmada on-chain. Se uma transação que nós transmitimos ainda assim chegar a um bloco sem executar, a posição fecha em failed com transaction_reverted e a reserva inteira é liberada — essa causa, como toda outra da lista, nunca é cobrada. Leia o nome pelo que ele é: transaction_reverted é a nossa palavra para “chegou a um bloco e não executou”, qualquer que tenha sido a palavra da própria rede para a recusa, então um OUT_OF_ENERGY e um REVERT do próprio contrato chegam sob o mesmo status. O TRX que uma transação dessas queimou no caminho até esse veredicto é do remetente e não nosso, o que é o argumento a favor da estimativa no começo desta seção e não de um laço de repetição no fim dela.