Skip to main content
Esta página fue traducida automáticamente por IA. La versión en inglés es la fuente autorizada.Ver versión en inglés →

Por qué existen los ticks

La liquidez de CLMM se concentra en rangos de precio. Para hacer que los rangos sean manejables en cadena, los precios se cuantizan en ticks enteros, donde cada tick es un múltiplo constante del anterior: price(i)=1.0001i\text{price}(i) = 1.0001^{\,i} Un tick corresponde a un movimiento de precio del 0.01%, o aproximadamente 1 punto base. El mapeo es: MIN_TICK y MAX_TICK se eligen de modo que sqrt_price_x64 quepa en un u128 en ambos extremos. Cada pool garantiza que tick_lower >= MIN_TICK y tick_upper <= MAX_TICK. En la práctica, la interfaz web limita el rango a algo mucho más estrecho para evitar que los usuarios bloqueen liquidez en ticks inaccesibles.

Espaciado de ticks

El AmmConfig de un pool fija un espaciado de ticks — los únicos ticks que una posición puede usar como puntos finales. Si tick_spacing = 60, solo los ticks …, −120, −60, 0, 60, 120, … son válidos. Un intento de abrir una posición con punto final 31 revierte con InvalidTickIndex. Espaciados publicados comunes: Cuanto más grueso sea el espaciado, menos tick arrays hay que inicializar, más barato es abrir una posición amplia, y más borroso es el límite de precio. Los pares volátiles típicamente viven en niveles de espaciado 120; los estables viven en niveles de espaciado 1.

Tick arrays

El pool no almacena estado por tick en cuentas separadas. En su lugar, TICK_ARRAY_SIZE ticks adyacentes (60 en el CLMM actual de Raydium) se empaquetan en un único TickArrayState. El primer tick del array es su start_tick_index, y cubre exactamente TICK_ARRAY_SIZE * tick_spacing unidades de tick entero. Para tick_spacing = 60 y TICK_ARRAY_SIZE = 60:
  • Cada tick array abarca 60 × 60 = 3600 ticks enteros.
  • start_tick_index es un múltiplo de 3600: …, -7200, -3600, 0, 3600, 7200, ….
Un punto final de posición t = 2040 con tick_spacing = 60 vive en el tick array con start_tick_index = 0. Un punto final de posición t = 4200 vive en el array con start_tick_index = 3600.

Cuándo se crea un array

Un tick array es perezoso: la primera posición que referencia cualquier tick dentro de él inicializa el array, pagando la renta. Los swaps no inicializan tick arrays — los saltan usando el bitmap. El flujo de apertura de posición del SDK inspecciona el rango elegido, calcula la lista de tick arrays que toca, y añade instrucciones init_tick_array en la misma transacción que OpenPosition si falta alguno.

Los tick arrays no se cierran

Una vez que un tick array ha sido inicializado, persiste durante la vida del pool. El programa no expone una ruta para cerrar un tick array, incluso después de que initialized_tick_count vuelva a cero. No hay recuperación de renta para tick arrays; la renta pagada por la primera posición que toca un array se bloquea en esa cuenta permanentemente. Este es un compromiso deliberado: reutilizar un tick array existente es gratis para cada posición posterior, por lo que un pool muy negociado solo paga el costo de renta una vez por slot (pool, start_tick_index) independientemente de la rotación.

El bitmap

Encontrar “el siguiente tick inicializado a la izquierda/derecha del tick actual” tiene que ser rápido — un swap puede cruzar muchos ticks. El pool almacena un bitmap de 1 bit por tick array en línea en PoolState para el rango ±1,024 arrays alrededor del tick 0. Fuera de ese rango (posiciones de rango completo, configuraciones exóticas), TickArrayBitmapExtension proporciona el desbordamiento. Un swap camina el bitmap: lowest_set_bit_above(tick_current_array_index) da el siguiente array con un tick inicializado en el lado hacia el que el swap está cruzando. Dentro de ese array, un escaneo de bits similar localiza el siguiente tick inicializado.

liquidity_gross y liquidity_net

Cada tick inicializado almacena dos valores de liquidez:
  • liquidity_gross — la suma de L sobre todas las posiciones que referencian este tick como punto final. Cuando liquidity_gross llega a cero, el tick se desinicia y puede ser removido del bitmap.
  • liquidity_net — el cambio firmado a la liquidity a nivel de pool cuando el precio cruza este tick moviéndose hacia arriba (de izquierda a derecha en el espacio de ticks). Si este tick es el límite inferior de una posición con tamaño L, contribuye +L; si es el límite superior de esa posición, contribuye −L.
Ejemplo trabajado: dos posiciones en el mismo pool.
  • Posición A: tick_lower = -120, tick_upper = 0, liquidez L_A = 100.
  • Posición B: tick_lower = -60, tick_upper = 60, liquidez L_B = 50.
Estado tick por tick: liquidity a nivel de pool para diferentes valores de tick_current:
  • tick_current = -180: liquidity = 0 (antes de cualquier posición)
  • tick_current = -90: liquidity = 100 (dentro de A solamente)
  • tick_current = -30: liquidity = 150 (dentro de A y B)
  • tick_current = 30: liquidity = 50 (dentro de B solamente)
  • tick_current = 90: liquidity = 0 (pasado ambas)
En cada cruce de tick durante un swap, el programa suma liquidity_net (posiblemente negativo) a PoolState.liquidity. Este es el mecanismo exacto de Uniswap-v3.

Posiciones como NFTs

Una posición CLMM de Raydium es un NFT. Abrir una posición acuña un nuevo mint completamente nuevo con suministro 1 en la billetera del llamador, y la autoridad del mint es el programa CLMM. El programa vincula la propiedad de la posición a quienquiera que tenga un saldo en un ATA de ese mint en el momento de CPI. Consecuencias:
  • Las posiciones normalmente son transferibles. Una billetera puede vender o airdrops una posición transfiriendo el NFT. El nuevo titular puede entonces llamar a CollectRewards, IncreaseLiquidity, etc. La excepción es una posición congelada bajo la ruta de emisor restringido a continuación.
  • Las posiciones son direccionables fuera de CLMM. Los mercados y billeteras muestran posiciones como otros NFTs. El SDK establece un name/symbol razonable en los metadatos del mint.
  • El PDA de una posición se deriva del mint del NFT. Puedes encontrar el PersonalPositionState sin saber quién lo sostiene actualmente.

Posiciones de emisor restringido

Cada mint de NFT de posición creado después de la actualización 2026-08 registra su pool_state de CLMM como autoridad de congelación. Esto no significa que cada nueva posición esté congelada. Para pools ordinarios y cada posición que no coincida, la cuenta de token NFT permanece descongelada y transferible. El PDA del pool no puede firmar fuera del programa CLMM, y CLMM no expone ninguna instrucción de congelación de propósito general. La congelación requiere ambas condiciones:
  1. La posición se abre a través de OpenPositionV2 u OpenPositionWithToken22Nft.
  2. Al menos un mint de bóveda de pool lleva una autoridad de congelación de la lista de emisor restringido codificada del programa.
Solo cuando ambas condiciones se cumplen, CLMM congela la cuenta NFT de posición recién creada inmediatamente después de acuñar. OpenPosition V1 no aplica este filtro. Consulta reference/program-addresses para la lista actual. Una posición congelada:
  • No puede transferir su NFT a otra cuenta de token.
  • No puede cambiar el propietario de la cuenta de token NFT.
  • Aún puede aumentar o disminuir liquidez y cobrar comisiones o recompensas cuando el propietario registrado firma.
  • Aún puede cerrarse. ClosePosition usa el PDA del pool para descongelar la cuenta NFT, luego quema el NFT y cierra las cuentas de posición en la misma instrucción.
Las posiciones existentes no se migran ni se congelan retroactivamente. Los mints de NFT de posición creados antes de la actualización retienen su configuración de autoridad de congelación anterior.
Un cliente que cierra una posición congelada debe añadir el pool_state de la posición como la primera cuenta restante a ClosePosition. La lista de cuentas IDL declarada no cambia, por lo que los clientes más antiguos pueden abrir una posición congelada con éxito pero luego fallar al cerrarla con AccountLack. Actualiza el constructor de cierre antes de soportar estos pools.

Posiciones Token-2022

CLMM puede acuñar un NFT de posición bajo Token SPL clásico a través de OpenPositionV2, o bajo Token-2022 a través de OpenPositionWithToken22Nft. Ambas rutas V2 inspeccionan los mints de bóveda del pool y aplican la misma regla de congelación de emisor restringido. OpenPosition V1 es la ruta clásica de token heredada y no puede servir un pool con mints de bóveda Token-2022. La compatibilidad de billetera y mercado difiere; la interfaz de Raydium rastrea ambos programas de NFT.

Reglas de rango permitido

En el momento de OpenPosition, el programa garantiza:
  1. tick_lower < tick_upper.
  2. tick_lower % tick_spacing == 0 y tick_upper % tick_spacing == 0.
  3. MIN_TICK <= tick_lower y tick_upper <= MAX_TICK.
  4. El llamador ha suministrado los tick arrays que contienen tick_lower y tick_upper — ya inicializados o a través de un init_tick_array en la misma transacción.
  5. La cuenta de extensión de bitmap, si esta posición se extiende al rango de extensión.
Si alguna verificación falla, la instrucción revierte con InvalidTickIndex, NotApproved, o InsufficientLiquidity dependiendo de qué restricción. Consulta reference/error-codes.

”En rango” vs “fuera de rango”

Una posición está en rango cuando tick_lower <= tick_current < tick_upper. Solo las posiciones en rango contribuyen a PoolState.liquidity y por lo tanto solo ellas ganan comisiones de swap. Una posición fuera de rango:
  • Sostiene el 100% de un token (el que su rango ha pasado). Específicamente, si tick_current < tick_lower, la posición sostiene solo token1 (ya ha sido “vendida” por el precio moviéndose); si tick_current >= tick_upper, sostiene solo token0.
  • No gana comisiones de swap.
  • continúa acumulando recompensas si los flujos de recompensa del pool emiten a liquidez fuera de rango — pero el comportamiento predeterminado de Raydium es “emitir solo a en rango”, coincidiendo con la convención de Uniswap v3. Consulta products/clmm/fees.
Los LPs que gestionan posiciones CLMM gastan la mayoría de su atención manteniendo posiciones en rango mientras el precio se mueve.

Trampas comunes de integración

  • Puntos finales fuera de espaciado. El código que calcula un tick a partir de un precio objetivo debe ajustarse a un múltiplo de tick_spacing antes de pasarlo a OpenPosition. Los ayudantes del SDK (TickUtils.getTickWithPriceAndTickspacing) hacen esto; las matemáticas caseras a menudo no.
  • Tick arrays faltantes. Abrir una posición amplia puede requerir inicializar varios tick arrays; olvidar pasarlos como cuentas escribibles revierte. El openPositionFromBase del SDK te devuelve la lista.
  • Tick obsoleto después de un swap. tick_current puede cruzar muchos ticks en un swap. Si tu UX muestra un “tick actual” de una llamada RPC y luego abre una posición en una posterior, la posición relativa vs el precio en vivo puede estar desviada por docenas de ticks. Vuelve a obtener justo antes de firmar.
  • NFTs de posición con metadatos adicionales. Si construyes una billetera que reconoce posiciones de Raydium, usa el PDA de posición / datos del programa y no un campo de metadatos codificado. Los nuevos mints de posición usan el PDA del pool como autoridad de mint y congelación en la creación; la autoridad de mint se elimina después de que se acuña el único NFT.
  • Asumir que cada posición es transferible. Lee el estado isFrozen de la cuenta de token NFT antes de mostrar acciones de transferencia, mercado, depósito en garantía, o Burn & Earn.

Dónde ir a continuación

  • Matemáticas — el paso de swap y la derivación de crecimiento de comisiones en la que participan los límites de ticks.
  • Cuentas — los diseños de TickArrayState y PositionState.
  • Comisiones y recompensas — cómo la condición de en-rango controla la acumulación de comisiones.
  • algorithms/clmm-math — la derivación compartida de las fórmulas de liquidez concentrada.
Fuentes: