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 →
Esta página es operativa: proporciona las fórmulas, convenciones de punto fijo y procedimientos utilizados por el programa CLMM. Para entender el razonamiento detrás de la curva de liquidez concentrada en sí — por qué
L = sqrt(x · y) importa — consulta algorithms/clmm-math. Esta página asume que ya has leído esa sección.Representación de sqrt-price
CLMM almacena el precio comosqrt_price_x64 — la raíz cuadrada del precio token1-por-token0, como un número de punto fijo Q64.64:
donde p = token1_amount / token0_amount. Trabajar en sqrt en lugar de p linealiza las matemáticas del swap (los deltas de montos de tokens se vuelven lineales en Δsqrt_price), y el punto fijo x64 mantiene la precisión a través de swaps multi-tick.
La conversión tick ↔ sqrt-price se precomputa mediante una aproximación logarítmica bit-por-bit:
implementada como una exponenciación basada en búsqueda en tick_math::get_sqrt_price_at_tick.
Liquidez como unidad canónica
Dentro de un rango[sqrt_a, sqrt_b] (con sqrt_a < sqrt_b) una posición de liquidez L se mapea a montos de tokens de la siguiente manera. Sea sqrt_c = sqrt_price_x64 el precio actual del pool.
Las tres identidades provienen del invariante
x = L / sqrt_p, y = L · sqrt_p que la liquidez concentrada satisface dentro de un rango.
Los integradores típicamente quieren lo inverso: dado un depósito de amount0 / amount1, calcula el máximo L que cabe en el rango. El método LiquidityMath.getLiquidityFromTokenAmounts del SDK hace esto. La fórmula para el caso dentro del rango:
Cualquiera de los dos lados que sea restrictivo determina la proporción realmente consumida; el otro lado puede tener sobrante.
Paso de swap de un solo tick
Un swap procede en pasos. Cada paso ya sea (a) consume toda la entrada disponible dentro del rango de tick actual sin cruzar un tick, o (b) mueve el precio exactamente al siguiente tick inicializado. Dado el estado actual(sqrt_c, L) y un swap hacia abajo (token0 entra, token1 sale, sqrt_price disminuye — el precio se cotiza como token1/token0, así que agregar token0 lo baja), el siguiente tick inicializado por debajo se encuentra en sqrt_t < sqrt_c. Dentro de este micro-intervalo la relación entre entrada y precio es:
y
Un swap con token1 de entrada es la imagen especular: sqrt_price sube hacia el siguiente tick por encima, y las dos fórmulas intercambian cuál extremo se resta. El programa codifica la dirección como zero_for_one (true cuando el mint de entrada es token_mint_0), y un swap zero_for_one se limita hacia MIN_SQRT_PRICE_X64.
El programa hace una de dos cosas:
-
¿Cabe toda la entrada? Si la entrada restante (después de comisión) es menor que
Δamount0para alcanzarsqrt_t, resuelve el nuevosqrt_c'exactamente: (para un swap exacto-entradatoken0 → token1). El swap se completa en este paso sin cruzar un tick. -
¿La entrada excede
Δamount0? Establecesqrt_c' = sqrt_t, cruza el tick (aplicaliquidity_net), decrementa la entrada restante porΔamount0, incrementa la salida porΔamount1, y repite.
token1 → token0, precio bajando), las fórmulas tienen sqrt_c y sqrt_t intercambiados y la inversión en el otro lugar.
La implementación completa en Rust vive en raydium-clmm/programs/amm/src/libraries/swap_math.rs. La lógica allí coincide uno-a-uno con SwapMath.computeSwapStep de Uniswap v3.
Comisiones en cada paso
Las comisiones comerciales se toman de la cantidad de entrada en cada paso, la misma convención que CPMM:L_i que se mantuvo dentro del rango durante este swap posteriormente leerá L_i · Δfee_growth_global / 2^{64} tokens adeudados.
Las porciones de protocolo y fondo se acumulan en PoolState.protocol_fees_token_{0,1} y PoolState.fund_fees_token_{0,1} respectivamente, idéntico a CPMM. Se barren mediante CollectProtocolFee / CollectFundFee.
Crecimiento de comisiones fuera y dentro
La parte complicada de la contabilidad de comisiones de CLMM: una posición gana comisiones solo mientras el precio del pool está dentro de su rango. El pool rastrea comisiones acumulativas globalmente; la posición necesita saber las comisiones acumulativas mientras está dentro de su rango específico. La solución es un acumulador basado en ticks. Cada tick almacena:- Si el precio del pool está por encima de este tick (
tick_current >= this_tick),fee_growth_outside = fee_growth_global. (Todo lo ganado hasta ahora está “fuera” — es decir, por debajo — de este tick, relativo al precio actual.) - Si no,
fee_growth_outside = 0.
fee_growth_outside de ese tick:
El invariante que esto preserva: para cualquier tick t, fee_growth_outside(t) es igual a las comisiones que se acumularon mientras tick_current estaba en el lado opuesto de t.
Crecimiento de comisiones dentro de un rango [tick_lower, tick_upper] se deriva entonces:
Qué almacena una posición y qué lee
UnPersonalPositionState almacena fee_growth_inside_0_last_x64 y fee_growth_inside_1_last_x64: los valores de fee_growth_inside en la última vez que se tocó la posición.
En cualquier toque posterior (aumentar, disminuir, cobrar), el programa:
- Calcula el
fee_growth_inside_{0,1}_x64actual usando la fórmula anterior. - Calcula
Δ = fee_growth_inside_now − fee_growth_inside_last(resta modular en u128). - Suma
Δ × position.liquidity / 2^{64}atokens_fees_owed_{0,1}. - Actualiza
fee_growth_inside_lastal nuevo valor.
CollectFees / DecreaseLiquidity, contra tokens_fees_owed.
Recompensas
Cada uno de los hasta 3 flujos de recompensas del pool utiliza la misma maquinaria de crecimiento-dentro, en su propio acumuladorreward_growth_global_x64. En el momento de emisión:
— las emisiones se escalan inversamente con la liquidez activa, así que un pool más denso paga a cada posición proporcionalmente menos por segundo, pero sobre más posiciones en total. La recompensa adeudada por posición es
y se paga mediante DecreaseLiquidity / DecreaseLiquidityV2 — no hay instrucción independiente de cobro de recompensas. Consulta products/clmm/fees.
Ejemplo trabajado: swap de entrada exacta
Supongamos:tick_spacing = 60sqrt_price_x64 = 1 × 2^{64}— precio = 1.0, así quetick_current = 0.- Liquidez activa
L = 1_000_000 × 2^{64}. - Siguiente tick inicializado por debajo:
t = −60(sqrt_price_b ≈0.997005 × 2^{64}). Un swap con token0 de entrada se mueve hacia abajo, así que el tick por encima es irrelevante aquí. - Tasa de comisión comercial: 500 (0.05%).
SwapBaseInput entrada exacta 1,000 token0.
Paso 1 — comisiones:
999 < 3004.4, así que toda la entrada cabe sin cruzar el tick.
Paso 3 — nuevo precio:
sqrt_c' cae ligeramente por debajo de sqrt_c, que es la dirección correcta: un swap token0 → token1 agrega token0 al pool y por lo tanto baja el precio token1/token0.
Paso 4 — monto de salida:
trade_fee_rate × protocol_fee_rate / 1e6 (y similar para fondo); la porción LP fluye hacia fee_growth_global_0_x64.
Coincidencia de órdenes limitadas durante swap
Cuando un paso de swap cruza un tick que contiene órdenes limitadas abiertas, esas órdenes consumen entrada de swap antes de que lo haga la curva LP, al precio exacto del tick. La coincidencia es FIFO dentro del tick por cohorteorder_phase.
Estado por cohorte en TickState
orders_amount y heredan el siguiente order_phase; no pueden llenarse hasta que la cohorte anterior se consume completamente.
Paso de coincidencia
Pseudo-código para la coincidencia que ocurre en cada cruce de tick durante un swap:SettleLimitOrder (o DecreaseLimitOrder). El pool simplemente rastrea cuánto de la cohorte ahora está lleno mediante unfilled_ratio_x64. Cada LimitOrderState almacena su propia instantánea (order_phase, unfilled_ratio_x64) en el momento de apertura, así que la liquidación se reduce a:
Interacción con la curva LP
En un paso de swap, la coincidencia de órdenes limitadas ocurre en el tick (ceroΔsqrt_price); el consumo de la curva LP ocurre entre ticks. El orden es por lo tanto:
- Cruza el tick
t_cross(aplica el cambio de LPliquidity_netprimero, ya que así es como Uniswap-V3 lo hace). - Llena cualquier orden limitada sentada en
t_cross. - Continúa a lo largo de la curva LP al siguiente tick inicializado o al agotamiento de
swap_input.
Derivación de comisión dinámica
PoolState.dynamic_fee_info lleva el estado de volatilidad. Cada paso de swap calcula la tasa de comisión por paso como:
donde:
- —
DYNAMIC_FEE_CONTROL_DENOMINATOR - —
VOLATILITY_ACCUMULATOR_SCALE vol_acces el acumulador por swap después de la regla de actualización a continuacióntick_spacinges dePoolState.tick_spacing
Actualización del acumulador
Se aplican dos reglas en cada swap, en orden: Decaimiento. El piso de referencia decae basado en el tiempo desde la última actualización: Acumular. El nuevo acumulador es la referencia más la distancia de tick recorrida desde el índice de referencia anterior:tick_spacing_index_reference () está en unidades de tick-spacing, no ticks crudos: .
Por qué parabólico en distancia de tick
Elevar al cuadrado el acumulador significa que la comisión sube como el cuadrado de cuán lejos ha caminado el precio desde su punto de referencia. Empíricamente esto coincide con el escalado de varianza del precio bajo presión de caminata aleatoria: una excursión de tick 2× implica 4× la volatilidad implícita, así que cobra 4× el recargo. El parámetrodynamic_fee_control calibra el nivel absoluto.
La ventana filter_period previene pequeñas oscilaciones sub-segundo (por ejemplo, bots MEV sándwich) de inflar el acumulador. La ventana decay_period previene que un único pico pasado cobre comisiones indefinidamente después de que el mercado se haya calmado.
Robustez numérica
- Todos los productos intermedios pasan por aritmética de forma
u128ou256. CLMM usa ayudantesU128Sqrty patronesFullMath::mulDivdirectamente portados de Uniswap v3. - El redondeo de división se elige por paso para hacer cumplir el invariante
k' ≥ klocalmente.SwapBaseInputredondea la salida hacia abajo;SwapBaseOutputredondea la entrada hacia arriba. - Los cruces de tick que dejan
PoolState.liquidityen cero están permitidos (el precio puede atravesar un “agujero de liquidez”) pero el swap simplemente avanza al siguiente tick inicializado sin consumir entrada, sin cobrar comisión. - Guardia de desbordamiento:
sqrt_price_x64se mantiene en el rango inclusivo[MIN_SQRT_PRICE_X64, MAX_SQRT_PRICE_X64]correspondiente a[MIN_TICK, MAX_TICK]. Un swap que empujaría más allá de cualquiera de los límites revierte conSqrtPriceLimitOverflow.
Dónde ir a continuación
products/clmm/ticks-and-positionspara cómo el mapa de ticks participa en el recorrido.products/clmm/feespara el lado de comisión/recompensa de las matemáticas en detalle.algorithms/clmm-mathpara las derivaciones detrás deL = sqrt(x · y)y las fórmulas de rango-vs-liquidez.
raydium-io/raydium-clmm—libraries/swap_math.rs,libraries/tick_math.rs- “Uniswap v3 Core” whitepaper, §6–7

