El hook y el anti-snipe
Un hook de Uniswap v4 es un contrato al que el PoolManager llama en momentos precisos de
un swap. La implementación actual de StockFunHook usa seis permisos: beforeInitialize,
beforeAddLiquidity, beforeSwap, afterSwap y los dos permisos de delta que le permiten
llevarse una parte. Desde el 2026-10-02, su dirección lleva los 14 permisos v4; los
callbacks que no usa se dejan pasar.
La dirección codifica los permisos
v4 lee los bits bajos de la dirección de un hook para saber cuándo llamarlo. Por tanto, la dirección no se elige: se mina mediante CREATE2 hasta dar con un valor cuyos bits coincidan con los permisos declarados.
Desde el 2026-10-02, la dirección minada es la de un proxy, con los 14 bits de permiso activados. Un upgrade sustituye la implementación que hay detrás de esa dirección sin moverla, así que nunca hace falta volver a minarla, y una implementación posterior puede usar cualquier callback. La actual deja pasar los callbacks que no usa: devuelven su selector y, donde se espera un delta, cero. La inicialización del proxy comprueba que su dirección lleva todos los bits.
Consecuencia práctica: la dirección minada depende del bytecode del proxy y de los
argumentos de su constructor, que llevan la dirección de la implementación. Para un nuevo
despliegue hay que volver a minarla. Por eso también foundry.toml fija
bytecode_hash = "none": sin eso, la dirección minada ya no coincide con el contrato
desplegado.
El permiso beforeAddLiquidity, añadido el 2026-10-01, cambió los propios bits: la
dirección se volvió a minar, y cualquier despliegue anterior a esa fecha queda obsoleto. El
proxy del 2026-10-02 los volvió a cambiar: cualquier despliegue anterior a esa fecha
también queda obsoleto.
El cobro de la comisión
En una compra, la comisión se cobra en beforeSwap, sobre el ETH entrante, antes de
que se produzca el swap. El hook retira su parte del PoolManager y devuelve un delta que
se la carga al comprador. El router de StockFun y el lock de liquidez ingresan el ETH del
comprador en el PoolManager antes del swap, así que sus compras nunca recurren al ETH que
el PoolManager ya contiene.
Cualquier otro router v4 paga la misma tasa de impuesto. Pero uno que liquida el ETH del
comprador después del swap, como hace el V4Router de v4-periphery con su codificación
por defecto, ve cómo el impuesto se cobra sobre el ETH que el PoolManager ya contiene, y
su compra falla cuando el impuesto es mayor. Eso puede ocurrir en un PoolManager que
contenga poco ETH, el de una testnet, por ejemplo. Un integrador debería ingresar el ETH
del comprador antes del swap, como hace el router oficial. Las ventas no se ven afectadas.
El pipeline de seguridad del 2026-10-01 documentó esta limitación. Cobrar en su lugar el
impuesto como claims del PoolManager la levantaría para todos los routers, pero haría
que el 2 % de la tesorería pasara de pagarse durante el trade a acreditarse y pagarse más
tarde: el 2026-10-05 el owner decidió mantener el impuesto tal como se cobra hoy.
En una venta, se cobra en afterSwap, sobre el ETH saliente, una vez conocido el
monto.
En ambos casos, el hook reparte de inmediato: tesorería, creador, equipo, buyback. Solo la línea de la tesorería sale durante el trade, enviada al vault del mercado. Las otras tres líneas se acreditan en el hook y se pagan fuera de cualquier trade.
Desde el 2026-10-05, un vault que rechaza la línea de la tesorería ya no hace fallar el
trade: el hook guarda el monto como adeudado a ese vault (treasuryOwed, evento
TreasuryOwed), y todas las compras y ventas siguen. Cualquiera paga la deuda a ese vault
con payTreasury, que falla y conserva la deuda mientras el vault siga rechazándola; el
keeper lo intenta en cada ciclo. El excedente del anti-snipe sigue la misma regla. Hasta
entonces, el envío fallaba de forma explícita: un vault que no podía recibir, tras un
upgrade defectuoso por ejemplo, hacía fallar todos los trades de su mercado, ventas
incluidas.
La parte del creador es de tipo pull: se acumula en el hook, mercado por mercado, y el
creador la reclama cuando quiere, con una transacción por mercado (claimCreatorFees).
Desde el 2026-10-05, un creador que no puede recibir ETH por sí mismo, un contrato sin
forma de recibirlo, reclama hacia otra dirección (claimCreatorFeesTo); solo el creador
puede hacerlo.
Desde el 2026-10-01, las partes del equipo y del buyback se acreditan de la misma forma, un
saldo cada una, y se pagan con claimTeamFees() y claimBuybackFees(). Cualquiera puede
llamarlas; solo pagan a las wallets del equipo y del buyback que la factory designa en el
momento de la reclamación. El BuybackBurner retira él mismo su saldo al inicio de cada
burn. Una wallet que rechaza ETH solo retrasa su propio pago: su reclamación falla y el
saldo se queda en el hook.
Hasta entonces, esas dos partes se enviaban durante el trade, con fallback a un escrow
cuando el envío fallaba. La auditoría de seguridad del 2026-09-29 mostró que una wallet que
aceptaba el envío y luego llamaba al PoolManager podía detener el trading en todos los
pools. El escrow ya no existe.
Lo que el hook guarda entre trades se le debe a alguien: los saldos de los creadores, los
del equipo y del buyback, y las deudas con los vaults. El ETH que le envía cualquiera que
no sea el PoolManager se cuenta aparte, como extraviado (strayEth). Desde el
2026-10-05 el owner de StockFun puede sacar lo extraviado (rescue): como máximo ese
monto de ETH, y cualquier token, ya que el hook nunca guarda ninguno. Nada de lo que el
hook debe puede salir por esa vía. El ETH forzado sin llamada no se cuenta, y espera a un
upgrade.
Solo el lock añade liquidez
Desde el 2026-10-01, beforeAddLiquidity rechaza cualquier aportación de liquidez a un
pool de StockFun, salvo la del lock de liquidez.
Una posición colocada justo al lado del precio actual actúa como una orden límite: el swap de un trader la cruza y la convierte, y el trader paga el impuesto, mientras que el propietario de la posición la añade y la retira sin pagar nunca el 5 %, ni, en los diez primeros bloques, el impuesto anti-snipe. La auditoría de seguridad del 2026-09-29 lo reprodujo. Los pools no cobran comisión de LP por defecto, así que ningún uso legítimo necesita una posición de terceros.
Los cobros de comisiones del propio lock, con un delta de liquidez igual a cero, pasan por el camino de retirada de liquidez, que la implementación actual deja pasar: no se ven afectados. Tampoco la recuperación del modo de cierre, que saca las posiciones por el mismo camino: consulta El lanzamiento en dos posiciones.
Anti-snipe decreciente
Un pool v4 está operativo desde el momento en que se inicializa. Sin protección, los primeros bloques tras la creación se los llevarían los bots. Desde el 2026-09-28, esos bloques pagan un impuesto más alto, que baja en cada bloque, tanto en compras como en ventas. Con los ajustes por defecto:
| Bloque desde el lanzamiento | Impuesto |
|---|---|
| 1 | 80 % |
| 2 a 10 | 72, 64, 56, 48, 40, 32, 24, 16, 8 % |
| 11+ | Normal, 5 % |
Un bot que compra en el bloque de apertura paga un 80 % de golpe: el snipe sale a pérdida.
Tres cosas merecen una explicación.
La compra de lanzamiento del creador no necesita ninguna comprobación de identidad. El
único swap que realiza el LiquidityLock es la compra opcional del creador, dentro de su
propio callback de creación. Así que «quien llama es el lock» implica «estamos dentro de
la transacción de creación», y esa compra paga el 5 % normal.
El excedente tiene su propio destino. El 5 % normal mantiene su reparto habitual. La
parte por encima va a la tesorería del mercado y, por tanto, a los holders en el próximo
airdrop. En el mercado de $STOCKFUN va al saldo del equipo en el hook, que se paga con
claimTeamFees().
La whitelist necesita identidad, y esa es la parte sutil. El hook ve el router, no al
comprador. Por eso el router de StockFun lleva la dirección de quien lo llama en los datos
del hook, y el hook confía en ese campo solo si quien llama es el router oficial, el
que registra la factory y que el owner puede cambiar en cualquier momento (aceptado el
2026-09-28). En ningún punto de este protocolo se usa tx.origin.
Limitación documentada: una dirección de la whitelist que enrute a través de un agregador de terceros durante los diez primeros bloques no queda exenta.
La whitelist
Fijada por el creador del mercado. Las direcciones que figuran en ella pagan el 5 % normal durante los bloques anti-snipe. Hasta el 2026-09-28, la lista estaba en manos del owner y la compartían todos los mercados, precisamente para que no pudiera convertirse en una ventaja para insiders; ahora un creador puede eximir sus propias wallets. Validado el 2026-09-28: la lista se fija en la transacción de creación, es pública, inmutable y tiene un tope de 20 direcciones por defecto.
El mercado de $STOCKFUN
El impuesto decreciente también se aplica. $STOCKFUN no tiene creador externo: su
excedente va a las comisiones reclamables del equipo, y su whitelist la fija el owner en
el lanzamiento.
Los ajustes
Desde el 2026-10-05, cada cifra de esta página es un ajuste del owner de StockFun, que se
cambia en el hook con setTaxSettings: el impuesto, un 5 % por defecto; sus líneas,
2 / 2 / 0,5 en un mercado lanzado y 2 / 2,5 en $STOCKFUN, y el buyback se lleva el
resto; el impuesto de apertura del anti-snipe, un 80 %; su bajada por bloque, 8 puntos;
los bloques que dura, el de creación incluido, 10; y la whitelist más larga, 20
direcciones.
Un cambio se aplica desde el siguiente swap, en todos los pools, incluido un pool que siga en sus bloques anti-snipe: el decrecimiento se calcula con los ajustes vigentes, desde el bloque de lanzamiento del pool. Sean cuales sean los ajustes, el anti-snipe nunca cobra menos que el impuesto. El setter rechaza una tasa superior al 100 % y un esquema cuyas líneas superen el impuesto. El tope de la whitelist se comprueba al crear un mercado: una lista ya fijada se queda como está.
Los upgrades
Desde el 2026-10-02, el owner de StockFun puede hacer un upgrade del hook, con efecto
inmediato. El impuesto, su reparto, el anti-snipe y las whitelists que describe esta página
son los de la implementación actual. Un upgrade conserva la dirección del hook y debe
conservar el mismo PoolManager; la factory, que el hook lee y que decide quién puede
hacerle un upgrade, está fijada en la implementación.