Uma queima de tokens acionada por uma venda quase esvaziou o lado do TENDRIL de um pool de liquidez da BNB Chain em 4 de novembro de 2026, preparando o terreno para um swap que retirou quase 4,42 milhões de USDT. A saída líquida do pool na transação foi menor — 555.670,538752928908475139 USDT — porque quantidades substanciais de USDT haviam entrado antes na mesma transação.

Principais conclusões

  • O controlador do TENDRIL retirou do pool o dobro do valor original da venda.

  • O pool registrou quase nenhum TENDRIL antes de receber os tokens restantes do vendedor.

  • A renúncia à propriedade não desativou a permissão do controlador para queimar tokens.

  • O resultado final do swap e a saída líquida da transação representam valores diferentes.

Como a venda acionou a queima de tokens

A venda invocou um controlador antes que o TENDRIL restante do vendedor chegasse ao pool. Às 16:02:45 UTC, uma chamada transferFrom de um roteador iniciou a venda de 39,053,180,124.060957132414461269 TENDRIL, acionando o imposto do token e a lógica do controlador.

As transferências para o pool principal estavam sujeitas a um imposto de venda de 30%, exceto quando enviadas por um dos dois endereços configurados, _poolControl e _markControl. O contrato que executou a transação não se qualificava para nenhuma das isenções. Portanto, o token enviou primeiro 11,715,954,037.218287139724338380 TENDRIL para _poolControl.

Em seguida, o token invocou a função sellToken do controlador com o valor original da venda — não apenas o imposto. Essa chamada foi concluída antes de o token creditar os 70% restantes ao pool.

O rastreamento histórico das chamadas mostra a sequência: transferFrom, sellToken, controlburn e sync, seguidas pelo swap final. O rastreamento de opcodes do controlador confirma que ele multiplicou o valor original da venda por dois, produzindo exatamente o valor posteriormente removido do pool.

Controlburn removeu tokens do pool sem reduzir a oferta total

A função controlburn transferiu TENDRIL do saldo do pool para um endereço morto. Ela não reduziu a oferta total registrada do token nem queimou tokens de provedor de liquidez.

A verificação de permissão exigia que o chamador correspondesse a _poolControl. Após a autorização, a função usava _basicTransfer para subtrair tokens do saldo do pool e creditá-los ao endereço morto. Não houve envolvimento de autorização do pool nem retirada de liquidez: o contrato do token controlava seus próprios registros de saldo.

O Log 28 registra a remoção de 78,106,360,248.121914264828922538 TENDRIL. Isso deixou 6,678 unidades brutas no pool. Com 18 casas decimais de TENDRIL, o restante era de apenas 0.000000000000006678 TENDRIL.

A queima não removeu USDT ao mesmo tempo. Quase todo o saldo de TENDRIL desapareceu, enquanto o USDT do pool permaneceu disponível para a troca seguinte.

A renúncia à propriedade não bloqueou esse caminho. No bloco 131653426, imediatamente antes do incidente, owner() retornou o endereço zero. Mas controlburn verificava o controlador configurado, e não owner(), então essa permissão separada continuava em vigor. A função não era irrestritamente acessível ao público; uma chamada arbitrária falharia na verificação de autorização.

A redefinição das reservas permitiu retirar quase todo o USDT

Imediatamente após a queima, o controlador chamou sync(), registrando o saldo reduzido de tokens antes da chegada dos tokens restantes da venda. O Log 29 definiu as reservas em 4,421,023.357469283419586501 USDT e 0.000000000000006678 TENDRIL.

Só depois disso, 27,337,226,086.842669992690122889 TENDRIL entrou no pool. O swap final transferiu 4,421,023.357469283419586499 USDT para fora, deixando 0.000000000000000002 USDT.

A aritmética do produto constante explica a dimensão desse resultado. Para fins de exemplo, desconsiderando taxas e arredondamentos, o valor de USDT recebido pode ser calculado multiplicando-se a reserva de USDT pela quantidade de tokens inserida e, em seguida, dividindo-se o resultado pela soma da reserva de tokens com a quantidade de tokens inserida. Com uma reserva de tokens registrada quase igual a zero, a quantidade inserida domina o denominador e o resultado se aproxima da reserva total de USDT.

Essa retirada final não correspondeu ao valor líquido extraído do pool ao longo da transação. As saídas totais de USDT chegaram a 5,117,517.824288279504509510, enquanto as entradas somaram 4,561,847.285535350596034371. Subtraindo-as, obtém-se a saída líquida de 555,670.538752928908475139 USDT.

O código do contrato e os rastros históricos estabelecem a sequência

A queima de tokens acionada pela venda foi reconstruída com base no código-fonte verificado pelo explorador, em chamadas históricas, logs de eventos e um rastreamento de opcodes. A recompilação do token com Solidity v0.8.24+commit.e11b9ed9, com a otimização desativada, correspondeu aos bytes executáveis em tempo de execução no bloco 131653426, excluindo os metadados do compilador.

O argumento decodificado da queima correspondeu exatamente à transferência do pool para o endereço morto. A aritmética com inteiros também reconciliou o imposto, os tokens restantes da venda e os saldos das reservas. A oferta total permaneceu inalterada entre os blocos imediatamente anterior e posterior à transação.

A transação bem-sucedida relacionada ao incidente apareceu no bloco 131653427. As verificações do recibo em dois endpoints públicos de RPC da BNB Chain coincidiram, e os fluxos de USDT por endereço foram reconciliados exatamente na nova verificação feita em 6 de novembro de 2026.

Artigo produzido com a assistência de inteligência artificial e revisado pela equipe editorial.