Una quema de tokens activada por una venta dejó casi vacío el lado de TENDRIL de un pool de liquidez en BNB Chain el 4 de noviembre de 2026, preparando el terreno para un intercambio que retiró casi 4,42 millones de USDT. La salida neta del pool en la transacción fue menor —555.670,538752928908475139 USDT— porque antes habían entrado cantidades sustanciales de USDT en esa misma transacción.
Conclusiones clave
El controlador de TENDRIL retiró del pool el doble del importe de la venta original.
El pool registró casi nada de TENDRIL antes de recibir los tokens restantes del vendedor.
La renuncia a la propiedad no deshabilitó el permiso del controlador para quemar tokens.
El resultado final del swap y la salida neta de la transacción miden cantidades distintas.
Cómo la venta activó la quema de tokens desencadenada por la venta
La venta invocó a un controlador antes de que los TENDRIL restantes del vendedor llegaran al pool. A las 16:02:45 UTC, una llamada transferFrom de un router inició la venta de 39,053,180,124.060957132414461269 TENDRIL, lo que activó el impuesto del token y la lógica del controlador.
Las transferencias al pool principal estaban sujetas a un impuesto de venta del 30 %, excepto cuando las realizaba una de las dos direcciones configuradas, _poolControl y _markControl. El contrato ejecutor no cumplía los requisitos para ninguna de las dos exenciones. Por lo tanto, el token envió primero 11,715,954,037.218287139724338380 TENDRIL a _poolControl.
A continuación, el token invocó la función sellToken del controlador con el importe original de la venta, no solo con el impuesto. Esa llamada se completó antes de que el token acreditara al pool el 70 % restante.
El rastro histórico de llamadas muestra la secuencia: transferFrom, sellToken, controlburn y sync, seguidos del swap final. El rastro de opcodes del controlador confirma que multiplicó por dos el importe original de la venta, lo que produjo exactamente la cantidad que después se retiró del pool.
Controlburn retiró tokens del pool sin reducir la oferta total
La función controlburn transfirió TENDRIL del saldo del pool a una dirección muerta. No redujo la oferta total registrada del token ni quemó tokens de proveedores de liquidez.
Su comprobación de permisos exigía que el caller coincidiera con _poolControl. Una vez autorizado, utilizó _basicTransfer para restar tokens del saldo del pool y acreditar la dirección muerta. No hubo ninguna autorización del pool ni retirada de liquidez: el contrato del token controlaba sus propios registros de saldo.
El registro 28 documenta la retirada de 78,106,360,248.121914264828922538 TENDRIL. Quedaron 6,678 unidades sin decimales en el pool. Con los 18 decimales de TENDRIL, el remanente era de apenas 0.000000000000006678 TENDRIL.
La quema no retiró USDT al mismo tiempo. Desapareció casi todo el saldo de TENDRIL, mientras que los USDT del pool siguieron disponibles para el siguiente intercambio.
La renuncia a la propiedad no bloqueó esta vía. En el bloque 131653426, inmediatamente antes del incidente, owner() devolvió la dirección cero. Pero controlburn comprobaba el controlador configurado, no owner(), por lo que ese permiso independiente seguía vigente. La función no estaba abierta sin restricciones al público: cualquier caller arbitrario habría fallado la comprobación de autorización.
El reajuste de las reservas permitió retirar casi todos los USDT
El controlador llamó a sync() inmediatamente después de la quema, registrando el saldo agotado del token antes de que llegaran los tokens de venta restantes. El registro 29 fijó las reservas en 4,421,023.357469283419586501 USDT y 0.000000000000006678 TENDRIL.
Solo después entraron al pool 27,337,226,086.842669992690122889 TENDRIL. El swap final transfirió 4,421,023.357469283419586499 USDT, dejando 0.000000000000000002 USDT.
La aritmética del producto constante explica la magnitud de ese resultado. A modo de ejemplo, dejando de lado las comisiones y el redondeo, el resultado en USDT se calcula multiplicando la reserva de USDT por la cantidad de tokens de entrada y dividiendo ese resultado entre la reserva de tokens más la cantidad de tokens de entrada. Con una reserva de tokens registrada casi igual a cero, la entrada domina el denominador y el resultado se aproxima a la reserva total de USDT.
Ese retiro final no fue el importe neto extraído del pool durante la transacción. Las salidas totales de USDT alcanzaron 5,117,517.824288279504509510, mientras que las entradas sumaron 4,561,847.285535350596034371. Al restarlas, se obtiene una salida neta de 555,670.538752928908475139 USDT.
El código del contrato y los rastros históricos establecen la secuencia
La quema de tokens activada por la venta se reconstruyó utilizando el código fuente verificado por el explorador, llamadas históricas, registros de eventos y un rastro de opcodes. La recompilación del token con Solidity v0.8.24+commit.e11b9ed9, con la optimización desactivada, coincidió con sus bytes ejecutables de runtime en el bloque 131653426, excluyendo los metadatos del compilador.
El argumento de quema decodificado coincidió exactamente con la transferencia del pool a la dirección muerta. La aritmética de enteros también hizo cuadrar el impuesto, los tokens de venta restantes y los saldos de reserva. La oferta total se mantuvo sin cambios entre los bloques inmediatamente anterior y posterior a la transacción.
La transacción exitosa del incidente apareció en el bloque 131653427. Las comprobaciones del recibo coincidieron a través de dos endpoints públicos de RPC de BNB Chain, y los flujos de USDT a nivel de dirección cuadraron exactamente al volver a comprobarse el 6 de noviembre de 2026.
Artículo elaborado con la ayuda de inteligencia artificial y revisado por el equipo editorial.
