Back to Blog

#3 Incidente de Balancer V2: Una Inconsistencia de Redondeo Rompe el Invariante y se Propaga entre Cadenas

Code Auditing
February 10, 2026
6 min read

#3 Incidente de Balancer V2: Una Inconsistencia de Redondeo Rompe el Invariante y se Propaga Entre Cadenas

El 3 de noviembre de 2025, los Composable Stable Pools de Balancer V2, junto con varios proyectos bifurcados en múltiples cadenas, sufrieron un exploit coordinado que resultó en pérdidas totales de más de $125 millones, con aproximadamente $45 millones recuperados según se informó. La causa raíz fue la manipulación de precios habilitada por la pérdida de precisión en el cálculo del invariante, derivada de un redondeo inconsistente entre las operaciones de escalado ascendente y descendente, lo que en última instancia distorsionó la lógica de fijación de precios del BPT (Balancer Pool Token).

Este incidente se destaca como uno de los diez principales incidentes de seguridad de 2025, no solo por la magnitud de las pérdidas, sino también por la sutileza del error subyacente. Además, el exploit se propagó rápidamente a través de múltiples cadenas y afectó tanto a Balancer como a sus bifurcaciones, lo que pone de manifiesto cómo las bases de código compartidas y la infraestructura DeFi componible pueden amplificar significativamente el riesgo sistémico.

Hemos publicado un informe extenso, "Análisis en Profundidad: El Exploit de Balancer V2" [1], que proporciona un desglose técnico detallado. A continuación, presentamos una ilustración concisa del incidente.

Antecedentes

El Composable Stable Pool de Balancer V2

El componente afectado en este ataque fue el Composable Stable Pool [2] del protocolo Balancer V2. Estos pools están diseñados para activos que se espera mantengan una paridad cercana a 1:1 (o que se negocien a una tasa de cambio conocida) y permiten intercambios grandes con un impacto mínimo en el precio, mejorando así significativamente la eficiencia del capital entre activos similares o correlacionados. Cada pool tiene su propio Balancer Pool Token (BPT), que representa la participación del proveedor de liquidez en el pool, junto con los activos subyacentes correspondientes.

  • Este pool adopta Stable Math (basado en el modelo StableSwap de Curve), donde el invariante D representa el valor total virtual del pool.

  • El precio del BPT puede aproximarse como:

Price(BPT)DtotalSupplyPrice(BPT) \approx \frac{D}{totalSupply}

De la fórmula anterior, si D puede hacerse más pequeño sobre el papel (incluso sin ninguna pérdida real de fondos), el precio del BPT parecerá más barato.

batchSwap() y onSwap()

Balancer V2 proporciona la función batchSwap(), que permite intercambios de múltiples saltos dentro del Vault [3]. Existen dos tipos de intercambio determinados por un parámetro pasado a esta función:

  • GIVEN_IN ("Dado de entrada"): el llamante especifica la cantidad exacta del token de entrada, y el pool calcula la cantidad de salida correspondiente.
  • GIVEN_OUT ("Dado de salida"): el llamante especifica la cantidad de salida deseada, y el pool calcula la cantidad de entrada requerida.

Normalmente, un batchSwap() consiste en múltiples intercambios de token a token ejecutados a través de la función onSwap(). Este proceso inevitablemente involucra cálculos de cantidades vinculados al invariante D [1].

Escalado y Redondeo

Para normalizar los cálculos a través de diferentes saldos de tokens, Balancer realiza las siguientes dos operaciones:

  • Escalado ascendente: Escala los saldos y cantidades a una precisión interna unificada antes de realizar los cálculos.
  • Escalado descendente: Convierte los resultados de vuelta a su precisión nativa, aplicando redondeo direccional (por ejemplo, las cantidades de entrada generalmente se redondean hacia arriba para garantizar que el pool no cobre de menos, mientras que las cantidades de salida suelen redondearse hacia abajo).

El escalado ascendente y descendente son operaciones teóricamente emparejadas: multiplicación y división, respectivamente. Sin embargo, existe una inconsistencia en la implementación de estas dos operaciones. Específicamente, la operación de escalado descendente tiene dos variantes o direcciones: divUp y divDown. En contraste, la operación de escalado ascendente tiene solo una dirección, a saber, mulDown.

Análisis de la Vulnerabilidad

El problema subyacente surge de la operación de redondeo hacia abajo realizada durante el escalado ascendente en la función BaseGeneralPool._swapGivenOut(). En particular, _swapGivenOut() redondea incorrectamente hacia abajo swapRequest.amount a través de la función _upscale(). El valor redondeado resultante se usa posteriormente como amountOut al calcular amountIn mediante _onSwapGivenOut(). Este comportamiento contradice la práctica estándar de que el redondeo debe aplicarse de manera que beneficie al protocolo.

Por lo tanto, para un pool dado (wstETH/rETH/cbETH), el amountIn calculado subestima la entrada real requerida. Esto permite a un usuario intercambiar una cantidad menor de un activo subyacente (por ejemplo, wstETH) por otro (por ejemplo, cbETH), disminuyendo así el invariante D como resultado de la reducción de la liquidez efectiva. En consecuencia, el precio del BPT correspondiente (wstETH/rETH/cbETH) se deflacta artificialmente, ya que el precio del BPT = D / totalSupply.

Análisis del Ataque

El atacante ejecutó un ataque en dos etapas, probablemente para minimizar el riesgo de detección:

  • En la primera etapa, el exploit principal se realizó dentro de una sola transacción, sin generar ninguna ganancia inmediata.
  • En la segunda etapa, el atacante obtuvo ganancias retirando activos en una transacción separada.

La primera etapa puede dividirse a su vez en dos fases: cálculo de parámetros e intercambio por lotes. A continuación, ilustramos estas fases utilizando una transacción de ataque de ejemplo (TX) en Arbitrum.

La Fase de Cálculo de Parámetros

En esta fase, el atacante combinó cálculos fuera de la cadena con simulaciones en cadena para ajustar con precisión los parámetros de cada salto en la siguiente fase (intercambio por lotes), basándose en el estado actual del Composable Stable Pool (incluidos los factores de escala, el coeficiente de amplificación, la tasa del BPT, las comisiones de intercambio y otros parámetros). El atacante también desplegó un contrato auxiliar para asistir con estos cálculos, lo que puede haber tenido como objetivo reducir la exposición al front-running. Consulte [1] para más detalles.

La Fase de Intercambio por Lotes

Luego, la operación batchSwap() puede desglosarse en tres pasos:

Paso 1: El atacante intercambia BPT (wstETH/rETH/cbETH) por activos subyacentes para ajustar con precisión el saldo de un token (cbETH) al límite de un umbral de redondeo (cantidad = 9). Esto establece las condiciones para la pérdida de precisión en el siguiente paso.

Paso 2: El atacante luego intercambia entre otro activo subyacente (wstETH) y cbETH usando una cantidad diseñada (= 8). Debido al redondeo hacia abajo al escalar las cantidades de tokens, el Δx calculado se vuelve ligeramente menor (de 8.918 a 8), lo que lleva a un Δy subestimado y, por lo tanto, a un invariante menor (D del modelo StableSwap de Curve). Dado que el precio del BPT = D / totalSupply, el precio del BPT se deflacta artificialmente.

Paso 3: El atacante realiza el intercambio inverso de los activos subyacentes de vuelta a BPT, restaurando el equilibrio mientras se beneficia del precio deflactado del BPT.

Resumen

Este incidente involucró una serie coordinada de transacciones de exploit dirigidas a los Composable Stable Pools de Balancer V2 y múltiples despliegues bifurcados en diferentes cadenas, resultando en pérdidas sustanciales. Después del primer exploit, las transacciones de imitación emergieron rápidamente, mostrando con qué velocidad un patrón de ataque puede propagarse una vez expuesto.

Lecciones clave:

  • Manejo de Redondeo y Precisión: Cada operación de escalado y precisión sobre cantidades de tokens debe redondearse en la dirección que beneficie al protocolo. Una sola inconsistencia entre _upscale() (solo redondeo hacia abajo) y las operaciones de escalado descendente (redondeo direccional) fue suficiente para crear una distorsión de precios explotable.
  • Carrera Armamentista en Seguridad: El atacante dividió la manipulación y la extracción de ganancias en transacciones separadas para evadir la detección. Los sistemas de detección deben correlacionar transacciones relacionadas, no solo marcar las individuales.
  • Seguridad Operacional: Una vez que el patrón del exploit fue público, los imitadores lo replicaron en cadenas en cuestión de minutos. Los protocolos que comparten una base de código necesitan monitoreo coordinado y capacidades de pausa rápida entre cadenas.

Referencia

  1. https://blocksec.com/blog/in-depth-analysis-the-balancer-v2-exploit

  2. https://docs-v2.balancer.fi/concepts/pools/composable-stable.html

  3. https://docs-v2.balancer.fi/reference/swaps/batch-swaps.html


Acerca de BlockSec

BlockSec es un proveedor integral de seguridad blockchain y cumplimiento de criptomonedas. Desarrollamos productos y servicios que ayudan a los clientes a realizar auditorías de código (incluyendo contratos inteligentes, blockchain y billeteras), interceptar ataques en tiempo real, analizar incidentes, rastrear fondos ilícitos y cumplir con las obligaciones AML/CFT, a lo largo del ciclo de vida completo de protocolos y plataformas.

BlockSec ha publicado múltiples artículos de seguridad blockchain en conferencias prestigiosas, ha reportado varios ataques de día cero en aplicaciones DeFi, ha bloqueado múltiples hackeos para rescatar más de 20 millones de dólares y ha asegurado miles de millones en criptomonedas.

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit