Uniswap v4 logra liquidez programable mediante Hooks, pero debido a defectos en la codificación de permisos y control de acceso, se ha convertido en un blanco frecuente de ataques; se debe estar alerta al riesgo de mal uso de Hooks asíncronos y lógica contable.Autor y fuente del artículo: Beosin
Tras el lanzamiento en mainnet de Uniswap v4, el mecanismo Hook se ha convertido en una de las innovaciones más destacadas en DeFi. La plataforma de lanzamiento de memecoins en la cadena Base, Flaunch, utiliza Hook para implementar un precio fijo de preventa y un mecanismo de liquidación automática en el lanzamiento; el protocolo de liquidez Bunni v2 emplea Hook para construir modelos de liquidez programable y de remortgaje; este año, tokens como SATO, uPEG (Unipeg) y Slonks, centrados en el uso de Hook, también lograron aumentos de decenas de veces en un corto período.
Mientras la ecosistema de Hook florece, los ataques que explotan defectos en la implementación de Hook también están aumentando significativamente. Este artículo comenzará con el mecanismo de Hook de Uniswap v4 y analizará paso a paso su pila de llamadas principal, ayudando a los proyectos a comprender las posibles vulnerabilidades que pueden existir.
Seguridad del Hook de Uniswap v4
1. Introducción
El cambio arquitectónico más significativo de Uniswap v4 respecto a v3 es la introducción del mecanismo Hook: permite a los desarrolladores adjuntar contratos personalizados a los eventos del ciclo de vida del pool de liquidez, inyectando lógica arbitraria en puntos como swap, agregar o retirar liquidez e inicialización.
Los principales cambios en la v4 son los siguientes:
Patrón Singleton: El estado de todos los pools es gestionado centralmente por un solo contrato PoolManager, ya no se despliegan contratos independientes para cada pool.
- Contabilidad flash: Los cambios de saldo intermedios durante el proceso de operación solo se registran en el almacenamiento transitorio y se liquidan de forma unificada al finalizar la operación.
Mecanismo Hook: Cada pool puede vincularse a un contrato Hook, y PoolManager llamará a dicho contrato en puntos clave (beforeInitialize, beforeSwap, afterAddLiquidity, etc.)
- Hook no se puede reemplazar: una vez que se completa la inicialización del pool, la dirección Hook vinculada se fija permanentemente (la dirección Hook vinculada al pool no se puede modificar, pero si el contrato Hook mismo es actualizable depende de su implementación).
Durante la fase v3, los desarrolladores solo necesitaban confiar en el protocolo Uniswap; en la fase v4, la seguridad de cada pool depende del Hook al que está vinculado. Los Hooks transforman el AMM de un primitivo financiero fijo en una infraestructura financiera programable, pero el modelo de seguridad se fragmentó desde el “nivel de protocolo” al “nivel de pool”.
2. Arquitectura Hook 2.1 PoolManager y modelo unlock/callback
El contrato principal de la versión v4 es el PoolManager singleton. Cualquier operación que modifique el estado del pool (intercambio, agregar o eliminar liquidez) debe llamar primero a PoolManager.unlock() para obtener un permiso de devolución de llamada único, y luego completar la acción específica dentro de unlockCallback(). Al finalizar todo el proceso, PoolManager verifica que el libro mayor esté equilibrado:
Cuando NonzeroDeltaCount != 0, se revertirá directamente; esta es la restricción fundamental del flash accounting v4. Cualquier Hook puede temporariamente desequilibrar la contabilidad durante su ejecución, pero debe saldar su cuenta antes del final de la transacción; de lo contrario, toda la transacción se revertirá.
Cada pool está identificado de forma única por la estructura PoolKey, que incluye el campo hooks:
PoolId se calcula mediante keccak256(PoolKey), por lo que direcciones de hooks diferentes generan pools distintos. Esto también significa que PoolManager no verifica si una dirección de hook ha sido utilizada previamente para otro pool; el mismo contrato de hook puede estar vinculado simultáneamente a múltiples pools.
2.2 Codificación de bits de permiso Hook en la dirección
Un diseño contraintuitivo de la v4 es que los permisos del Hook no los determina una variable interna del contrato, sino la dirección de despliegue del contrato Hook.
PoolManager determina si se debe llamar a un Hook en un punto específico del ciclo de vida verificando los 14 bits menos significativos de la dirección Hook:
Por ejemplo, BEFORE_SWAP_FLAG = 1 << 7. Si el bit 7 de la dirección del Hook es 1, PoolManager llamará a beforeSwap() del Hook antes del intercambio; de lo contrario, incluso si el contrato del Hook implementa beforeSwap(), nunca será llamado por PoolManager.
Esto significa que, al implementar Hook, se debe calcular la dirección mediante CREATE2 + salt, construyendo una dirección cuyos bits bajos coincidan exactamente con los permisos objetivo. Uniswap proporciona oficialmente la herramienta HookMiner para este propósito:
Cuando hay una incoherencia entre los bits de permiso y la implementación de la función, se generan dos tipos de problemas:
(1) Se implementó una función hook, pero la dirección no está codificada con los bits de permiso correspondientes: PoolManager nunca llamará a esta función, por lo que la lógica es equivalente a inexistente.
(2) La dirección codifica un bit de permiso, pero el hook no implementa la función correspondiente: PoolManager puede experimentar un revert durante la devolución de llamada, lo que provoca un DOS o falla en la validación del valor devuelto, impidiendo la ejecución de la operación relacionada.
Esto también constituye una barrera natural para la actualización de Hook: si Hook se puede actualizar a través de un proxy, la dirección de despliegue no cambia durante la actualización, por lo que solo se puede modificar la implementación de las funciones Hook existentes, pero no se pueden agregar nuevos tipos de Hook. Para reservar capacidad de expansión futura, es necesario prealocar todos los bits de permiso posibles en el momento del despliegue inicial.
2.3 BaseHook y una trampa de control de acceso ampliamente ignorada
El contrato abstracto BaseHook proporcionado por la periferia de Uniswap v4 permite a los desarrolladores heredarlo para implementar Hooks personalizados. Una función importante de BaseHook es proporcionar el modificador onlyPoolManager a la función unlockCallback():
Pero—aquí existe una trampa de diseño muy fácil de pasar por alto—la versión inicial de BaseHook solo agregó onlyPoolManager a unlockCallback, sin ninguna protección para otras funciones de retorno de hook (beforeSwap, afterSwap, beforeAddLiquidity, etc.). El control de acceso a estas funciones debe ser agregado explícitamente por el desarrollador del hook.
3. Lectura del código del ciclo de vida del gancho
Por ejemplo, con un intercambio de entrada exacta, a continuación se analiza la pila de llamadas completa desde que el usuario inicia la transacción hasta su liquidación.
3.1 Inicialización del pool y enlace de Hook
Cualquiera puede llamar a PoolManager.initialize() para crear un nuevo pool:
isValidHookAddress solo verifica la compatibilidad entre los bits de permisos de la dirección y el campo fee, pero no verifica si el Hook ya se está utilizando en otro pool ni si el Hook "acepta" este PoolKey. Si el Hook no incluye lógica de lista blanca o vinculación a un solo pool en beforeInitialize, cualquier persona puede construir un nuevo pool que utilice el mismo Hook pero con cualquier par de tokens, y activar todos los callbacks posteriores del Hook.
3.2 beforeSwap y BeforeSwapDelta
La entrada del proceso de swap es PoolManager.swap(), que llama a Hooks.beforeSwap() antes de ejecutar la lógica central de swap:
El valor devuelto de beforeSwap es una tupla de tres elementos (bytes4, BeforeSwapDelta, uint24):
- bytes4: debe ser igual a IHooks.beforeSwap.selector, de lo contrario, PoolManager revertirá directamente
- BeforeSwapDelta: El hook ajusta el delta para el token especificado y el token no especificado en este swap
- uint24: valor de cobertura de la tarifa LP dinámica (solo aplica cuando el pool tiene activada la tarifa dinámica)
BeforeSwapDelta es un alias de int256, donde los 128 bits superiores son el delta del token especificado (es decir, el token cuya cantidad el usuario especificó) y los 128 bits inferiores son el delta del token no especificado:
Tenga en cuenta que la semántica de BeforeSwapDelta es que el Hook debe devolver un valor positivo al cobrar tarifas y un valor negativo al reembolsar tokens. Los desarrolladores fácilmente pueden invertir el signo; además, la correspondencia entre specified y unspecified depende del signo de params.zeroForOne y amountSpecified; un pequeño error en la implementación puede provocar un desplazamiento de tokens.
PoolManager sumará directamente el specifiedDelta devuelto por beforeSwap a amountToSwap:
Esta línea implica un significado clave: el Hook puede interceptar la cantidad de swap. Cuando hookDeltaSpecified es igual a -params.amountSpecified, amountToSwap se establece directamente en cero, lo que equivale a que el Hook asume completamente este swap: esto se conoce como Async Hook o Custom Curve Hook.
Async Hook es uno de los patrones de diseño más riesgosos en la versión 4: en esencia, reemplaza la lógica de intercambio de Uniswap con su propia lógica de Hook. Si el Hook tiene vulnerabilidades o es malicioso por naturaleza, los fondos del usuario ya no estarán protegidos por la lógica de fijación de precios nativa de Uniswap, sino que dependerán principalmente de la corrección de la implementación del Hook.
3.3 Liquidación de delta y NonzeroDeltaCount
El delta devuelto por beforeSwap y afterSwap no activa inmediatamente una transferencia, sino que se registra en el libro mayor interno de PoolManager:
Cada vez que el delta acumulado de un token cambia de cero a distinto de cero, NonzeroDeltaCount se incrementa; cuando vuelve a cero, se decrementa. Como se menciona en 2.1, al finalizar unlock(), si NonzeroDeltaCount != 0, toda la transacción se revierte.
Hook equilibra su delta mediante dos acciones: settle() (transferencia a PoolManager) y take() (retiro de PoolManager):
La semántica de seguridad que ofrece este mecanismo es clara: todos deben finalmente cerrar sus cuentas. Sin embargo, solo garantiza la “conservación de cuentas”, no la “corrección de cuentas”. Si Hook devuelve un delta maliciosamente construido en beforeSwap, PoolManager registrará fielmente ese delta; siempre que finalmente se salde, la transacción se considerará exitosa, incluso si esto implica que Hook pueda falsificar el estado del negocio para hacer que el sistema crea erróneamente que el atacante posee ciertos derechos sobre activos, y PoolManager no pueda detectar este tipo de errores a nivel de negocio.
Anteriormente, el incidente de seguridad de Cork Protocol se debió a una vulnerabilidad en su Hook, y antes del ataque ya había sido auditado por cuatro empresas de auditoría. Tras el incidente, al realizar un análisis posterior, descubrimos:
Tres de las cuatro auditorías no incluyen el contrato CorkHook.
Solo una auditoría de CorkHook identificó algunos problemas en el código y presentó sugerencias de mejora, pero no abordó completamente los problemas de control de acceso.
Otra firma de auditoría recomendó explícitamente en su informe: "un seguimiento interesante sería demostrar los invariantes para las funciones CorkHook que son invocadas por diferentes componentes verificados dentro del alcance de este compromiso". Esta recomendación, desde una perspectiva de revisión posterior, tiene un enfoque muy específico.
Esto revela una nueva ceguera en la auditoría en la era de los v4 Hooks: el crecimiento exponencial de la complejidad del protocolo hace que la definición del alcance sea en sí misma una decisión de seguridad. La cadena de interacciones entre el Hook y otros contratos del protocolo es muy larga; auditar solo el contrato del Hook no es suficiente para detectar problemas de composición entre contratos; por otro lado, auditar los contratos circundantes mientras se excluye el Hook del alcance deja sin detectar la mayor superficie de ataque de la era v4.
5. Reflexión
Al comparar el mecanismo del protocolo y el repaso del ataque Cork, se pueden identificar varios puntos clave del modelo de seguridad v4 Hook:
(1) Si la función de devolución de llamada Hook depende del contexto de llamada proporcionado por PoolManager, debe limitarse explícitamente para que solo pueda ser llamada por PoolManager. BaseHook no realizará esto por el desarrollador; este es el mayor riesgo de diseño que entra en conflicto con la experiencia general de auditoría de contratos en la versión 4.
(2) La relación de vinculación entre Hook y el pool no está restringida por PoolManager. Los desarrolladores deben implementar manualmente una lista blanca de pools o una vinculación de un solo pool en beforeInitialize.
(3) Los bits de permiso de la dirección Hook deben coincidir estrictamente con la implementación de la función. La dirección calculada debe incluir anticipadamente todos los bits de permiso que podrían ampliarse en el futuro.
(4) El hook Async / Custom Curve es esencialmente una implementación completamente personalizada de swap. No tiene ninguna protección a nivel del protocolo Uniswap y debe ser auditado según los estándares de un contrato financiero completamente autónomo.
(5) La "conservación" en la contabilidad de delta no equivale a "corrección". NonzeroDeltaCount == 0 solo garantiza que el libro mayor esté equilibrado al final, pero no que su contenido no haya sido manipulado maliciosamente.
(6) La confusión de tipos de tokens entre mercados es una nueva superficie de ataque en la era v4. Cuando los protocolos permiten a los usuarios crear mercados, es obligatorio realizar una validación semántica de los tokens, no solo confiar en la verificación de la interfaz.
Cada Hook es un dominio de confianza independiente, y la seguridad de cada pool está determinada por el Hook asociado. Por lo tanto, la complejidad de la auditoría de seguridad de los Hooks ya no es “auditar un solo código”, sino “auditar un subprotocolo completo”: este cambio implica una actualización metodológica tanto para los equipos del proyecto como para los auditores.
Referencias
(1) Cork Protocol. “May 28 2025 Exploit Post-Mortem.” 2025-06-04. https://www.cork.tech/blog/post-mortem
(2) OWASP Top 10 de Seguridad de Contratos Inteligentes 2026, SC01: Vulnerabilidades de Control de Acceso. https://scs.owasp.org/sctop10/SC01-AccessControlVulnerabilities/
(3) Whitepaper de Uniswap v4. https://app.uniswap.org/whitepaper-v4.pdf
(4) Uniswap v4-core. https://github.com/Uniswap/v4-core
(5) Uniswap v4-periphery. https://github.com/Uniswap/v4-periphery
Beosin, una de las primeras empresas globales de seguridad blockchain en dedicarse a la verificación formal, ofrece un ecosistema integral centrado en “seguridad + cumplimiento”. Tiene sedes en más de 10 países y regiones, y sus servicios abarcan auditorías de seguridad de código antes del lanzamiento de proyectos, monitoreo y bloqueo de riesgos de seguridad durante la operación, recuperación de activos robados, anti-lavado de dinero (AML) para activos virtuales y evaluaciones de cumplimiento según los requisitos regulatorios locales, todo como parte de un servicio integral de cumplimiento y seguridad blockchain. ¡Contáctenos dejando un mensaje en el cuadro de comentarios de nuestro canal oficial!

