Ethereum: de historia a labs

Artículo 42 de 48

88%

completado al llegar aquí

Sistemas distribuidosNota de investigación14 min de lectura

Uniswap v3: liquidez concentrada y ticks

Uniswap v3 con números: rangos de precio, ticks de 1.0001, liquidez L, cuánto capital ahorra un rango y por qué el LP asume más riesgo por dólar.

Ruta Ethereum: 0/48

Prerequisitos recomendados

Si alguno de estos conceptos todavía está medio nebuloso, empieza por aquí. Te ahorra tropiezos y un par de cejas fruncidas.

En un pool v2 de DAI/USDC, dos monedas estables que casi siempre valen lo mismo, el capital se reparte sobre todos los precios posibles, de cero a infinito. La documentación de Uniswap estima que ese par usa alrededor del 0,50 % del capital disponible para operar entre 0,99 y 1,01, que es donde ocurre casi todo el volumen. El 99,5 % restante espera precios que casi nunca llegan.

Uniswap v3 deja que cada proveedor de liquidez (LP) elija el rango donde su capital trabaja. Dentro de ese rango, el pool se comporta como si tuviera mucha más liquidez; fuera de él, la posición no opera ni cobra comisiones. El capítulo anterior explicó la curva x · y = k; aquí veremos cómo v3 la recorta y qué cuesta ese recorte.

Brújula de lectura

Actor, evidencia y estado

Antes de avanzar, ubica quién actúa, qué prueba queda y qué parte del sistema cambia.

Actor

LP con rango propio

Cada posición elige tick inferior y superior; el resultado depende de esa decisión.

Evidencia

slot0, ticks y posición NFT

sqrtPriceX96 y tick actual en slot0; la posición se identifica como un NFT ERC-721.

Estado

Liquidez activa L

Solo la liquidez de los rangos que contienen el precio actual participa en un swap.

Preguntas guía

  • ¿Qué pasa con una posición cuando el precio sale de su rango?
  • ¿Por qué el precio se guarda como raíz cuadrada y en una cuadrícula de ticks?
  • ¿Cuánto capital ahorra un rango estrecho para ofrecer la misma profundidad?
  • Si el capital es menor, ¿el riesgo por unidad de capital también es menor?

Del rango infinito al rango elegido

En v2, la liquidez se distribuye de forma uniforme sobre el intervalo de precios (0, ∞). En v3, el LP asigna su capital a un intervalo finito, y esa asignación se llama posición. Un LP puede tener muchas posiciones en el mismo pool, cada una con su rango.

La liquidez de una posición es activa mientras el precio está dentro de su rango. A medida que el precio se mueve en una dirección, los traders se llevan uno de los tokens y la posición acumula el otro, hasta que, en el borde, queda formada por un solo activo. Si el precio sale del rango, la posición deja de cobrar comisiones; si vuelve a entrar, se reactiva.

Hay una diferencia de representación con v2. Allí, la participación del LP es un ERC-20 fungible. En v3, cada posición tiene sus propios límites y se representa como un NFT ERC-721 a través del contrato NonfungiblePositionManager; en v4 la gestión pasa a un PositionManager que usa ERC-6909 para su contabilidad interna. Para un equipo de custodia, eso significa que una posición v3 se custodia como un NFT, no como un saldo.

Ticks: la cuadrícula de precios

El espacio de precios de v3 está dividido en ticks. El tick i corresponde al precio 1,0001^i, así que subir un tick sube el precio un 0,01 %. La biblioteca TickMath calcula sqrt(1,0001^tick) como número de punto fijo Q64.96 (64 bits enteros y 96 fraccionarios) y acota los ticks entre MIN_TICK = -887272 y MAX_TICK = 887272, derivados del logaritmo en base 1,0001 de 2^-128 y 2^128.

Por eso el estado principal del pool, en la estructura slot0, guarda sqrtPriceX96 (la raíz cuadrada del precio en Q64.96) y el tick actual. Trabajar con la raíz del precio simplifica las fórmulas de cantidades, como veremos enseguida. Un ejemplo: un precio de 2.000 unidades de token1 por unidad de token0, en unidades mínimas de cada token, cae cerca del tick 76.012, porque log(2000) / log(1,0001) ≈ 76.012,8.

No todos los ticks pueden ser bordes de posición. La fábrica asocia cada nivel de comisión con un espaciado de ticks: 0,05 % con 10, 0,30 % con 60 y 1 % con 200, fijados en su constructor. La documentación de comisiones agrega que la gobernanza puede habilitar niveles nuevos y que en diciembre de 2021 se añadió el de 0,01 %.

Cruzar un tick inicializado tiene costo. Cuando un swap llega al borde de un rango, el contrato debe activar o desactivar la liquidez que empieza o termina ahí, y esa operación aumenta el gas de la transacción que la cruza. Un espaciado menor da más granularidad a los LPs y puede encarecer los swaps que atraviesan muchos bordes.

Invariante dentro de un rango: la liquidez L

En v2, con reservas x e y y precio P = y / x, se puede definir L = sqrt(x · y). Despejando, x = L / sqrt(P) e y = L · sqrt(P). v3 reutiliza esa L pero la aplica solo a un intervalo [Pa, Pb]. La biblioteca SqrtPriceMath calcula las cantidades de cada token entre dos precios con estas fórmulas, que conviene leer como "cuánto token hace falta para recorrer ese tramo de la curva":

Texto
amount0 entre sqrt(Pa) y sqrt(Pb) = L · (1/sqrt(Pa) − 1/sqrt(Pb))
amount1 entre sqrt(Pa) y sqrt(Pb) = L · (sqrt(Pb) − sqrt(Pa))

Con el precio actual P dentro del rango, la posición contiene L · (1/sqrt(P) − 1/sqrt(Pb)) de token0 y L · (sqrt(P) − sqrt(Pa)) de token1. Si P está por debajo de Pa, solo tiene token0; si está por encima de Pb, solo token1.

Ahora el cálculo que justifica todo el diseño. Supón token0 = ETH, token1 = USDC, precio 2.000 y un rango de 1.800 a 2.200 (±10 %). Para aportar 1 ETH hace falta L ≈ 960,98, y con esa L la posición necesita unos 2.205,40 USDC: en total, unos 4.205 USDC de valor. Una posición de rango completo con la misma L necesitaría L / sqrt(2000) ≈ 21,49 ETH y L · sqrt(2000) ≈ 42.976 USDC, unos 85.952 USDC de valor. Para la misma profundidad cerca de 2.000, el rango estrecho usa unas 20 veces menos capital.

Figura 1

Una posición v3 entre dos ticks

Fuentes:

[6]
Una posición v3 entre dos ticks
Cómo leer esta figura

Un eje horizontal de precio con marcas de ticks. Un rango sombreado va de Pa igual a 1800 a Pb igual a 2200, y una línea vertical marca el precio actual P igual a 2000. A la izquierda del rango, una caja indica que la posición es solo token0 por L por la diferencia entre uno sobre raíz de Pa y uno sobre raíz de Pb, unos 2,16 ETH, y no cobra comisiones. Dentro del rango, una caja indica que la liquidez está activa, contiene ambos tokens, en el ejemplo 1 ETH y unos 2205 USDC, y cobra comisiones. A la derecha, una caja indica que la posición es solo token1, unos 4303 USDC, y no cobra comisiones. Debajo, una nota compara el capital: unos 4205 USDC en el rango contra unos 85952 USDC en rango completo para la misma liquidez L.

Visual original de TrautsLab. La composición de la posición depende de dónde está el precio respecto del rango: por debajo es solo token0, por encima es solo token1 y solo dentro del rango la liquidez está activa y cobra comisiones.

Traza de ejecución: un swap que cruza ticks

El pool guarda la liquidez activa total en una variable y, para cada tick inicializado, un liquidityNet: cuánto cambia la liquidez activa al cruzar ese tick de izquierda a derecha. Durante un swap, el contrato consume la liquidez del intervalo actual hasta llegar al siguiente tick inicializado; si todavía queda cantidad por intercambiar, llama a ticks.cross, suma liquidityNet (con el signo invertido si el precio baja) y continúa con la nueva liquidez.

Las comisiones tampoco se reparten por posición en cada swap, porque sería imposible recorrer miles de posiciones. El pool acumula feeGrowthGlobal0X128 y feeGrowthGlobal1X128, la comisión cobrada por unidad de liquidez, y cada tick guarda cuánto creció "fuera" de él. Con eso, cualquier posición calcula cuánto creció dentro de su rango restando lo de abajo y lo de arriba. Es contabilidad perezosa: el costo se paga cuando el LP consulta o retira, no en cada swap.

Riesgo operativo: más capital eficiente, más riesgo por dólar

El ejemplo de las 20 veces tiene una contracara que pocas veces se muestra. Mientras el precio está dentro del rango, la posición de rango completo con la misma L equivale a la posición concentrada más una cesta fija de tokens: L / sqrt(Pb) de token0 y L · sqrt(Pa) de token1, que no cambian mientras el precio no salga del rango. Esa cesta se comporta como tokens guardados, así que no aporta pérdida frente a mantener. Por tanto, la pérdida frente a mantener es la misma en términos absolutos para las dos posiciones.

Pero la posición concentrada puso unas 20 veces menos capital, así que, medida como porcentaje de lo invertido, su pérdida frente a mantener los tokens también es unas 20 veces mayor. Las comisiones se multiplican igual, y por eso la decisión depende de la volatilidad y del volumen, no solo del APR.

El estudio de loss-versus-rebalancing descompone el retorno del LP en exposición al mercado más comisiones menos pérdidas frente a arbitrajistas. Concentrar liquidez amplifica ambos lados de esa resta para el mismo capital.

Fuera de rango, la posición queda entera en el activo que perdió valor relativo y no cobra nada. Esa propiedad permite las range orders: aportar un solo activo en un rango por encima o por debajo del precio actual se parece a una orden límite. La documentación advierte la diferencia clave: si el precio cruza todo el rango y luego vuelve antes de que el LP retire, la orden queda deshecha, así que hay que vigilarla o delegar el retiro.

v3 también mejora el oráculo. Cada pool guarda observaciones con un acumulador tickCumulative, y cualquiera puede pagar para ampliar el historial hasta 65.535 observaciones. Dividir la diferencia de dos acumuladores por el tiempo da un tick medio aritmético, que corresponde a un precio medio geométrico. La documentación aclara que v4 ya no incluye oráculo integrado.

Decisión del LP

Elegir el ancho del rango

Recomendado

Rango ancho

Menos capital eficiente, pero sale de rango con menos frecuencia.

Ideal para
Pares volátiles sin gestión activa.
Ojo con
Cobra menos comisiones por dólar aportado.
Precaución

Rango estrecho con tesis

Mucha profundidad por dólar, con límites y rebalanceo planificados.

Ideal para
Pares estables o gestión con monitoreo y presupuesto de gas.
Ojo con
La pérdida frente a mantener crece en la misma proporción que la eficiencia.
Riesgo

Rango mínimo persiguiendo APR

Maximiza comisiones estimadas con tolerancia casi nula al movimiento.

Ideal para
Detectar simulaciones optimistas.
Ojo con
Tiempo fuera de rango, gas de rebalanceo y LVR suelen superar las comisiones.
Eficiencia de capital y mantenimiento empujan en direcciones opuestas.

Lab propuesto: medir capital y composición de una posición

Con un fork local de mainnet o un despliegue de v3-core en Anvil, el lab crea dos posiciones con la misma L: una de rango completo y otra entre 1.800 y 2.200. La validación observable es comparar los tokens que exige cada mint con los valores calculados arriba (salvo redondeo y decimales de cada token), mover el precio con swaps hasta salir del rango y comprobar que la posición estrecha queda con un solo token y deja de acumular comisiones. Como teardown basta detener Anvil.

Conexiones dentro de la serie

Referencias

Bibliografía académica

Formato IEEENumerado para ingeniería, sistemas y protocolos.
  1. [1]

    Uniswap Labs, "Concentrated Liquidity," Uniswap Docs (repositorio Uniswap/docs).

    Rangos de precio, liquidez activa, ticks de 0,01 % y costo de cruzar ticks.

  2. [2]

    Uniswap Labs, "How Uniswap Works," Uniswap Docs (repositorio Uniswap/docs).

    Representación de posiciones: ERC-20 en v2, NFT ERC-721 en v3, ERC-6909 en v4.

  3. [3]

    Uniswap, "TickMath.sol," Uniswap/v3-core, GitHub.

    source codeID: UNISWAP-V3-TICKMATHAbrir fuente (abre en otra pestaña)

    Precio por tick de 1,0001, formato Q64.96 y límites MIN_TICK y MAX_TICK.

  4. [4]

    Uniswap, "UniswapV3Factory.sol," Uniswap/v3-core, GitHub.

    source codeID: UNISWAP-V3-FACTORYAbrir fuente (abre en otra pestaña)

    Niveles de comisión iniciales y espaciado de ticks; enableFeeAmount para añadir niveles.

  5. [5]

    Uniswap Labs, "Fees," Uniswap Docs, archivo de conceptos de protocolo (repositorio Uniswap/docs).

    Niveles de comisión de v3 y alta del nivel de 0,01 % por gobernanza en diciembre de 2021.

  6. [6]

    Uniswap, "SqrtPriceMath.sol," Uniswap/v3-core, GitHub.

    source codeID: UNISWAP-V3-SQRTPRICEMATHAbrir fuente (abre en otra pestaña)

    getAmount0Delta y getAmount1Delta a partir de la liquidez y la raíz del precio.

  7. [7]

    Uniswap, "UniswapV3Pool.sol," Uniswap/v3-core, GitHub.

    source codeID: UNISWAP-V3-POOLAbrir fuente (abre en otra pestaña)

    slot0 con sqrtPriceX96 y tick, cruce de ticks con liquidityNet y acumuladores de comisiones.

  8. [8]

    J. Milionis, C. C. Moallemi, T. Roughgarden y A. L. Zhang, "Automated Market Making and Loss-Versus-Rebalancing," arXiv:2208.06046, 2022.

    Retorno del LP: exposición al mercado más comisiones menos pérdidas frente a arbitrajistas (LVR).

  9. [9]

    Uniswap Labs, "Range Orders," Uniswap Docs, archivo de conceptos de protocolo (repositorio Uniswap/docs).

    docsID: UNISWAP-DOCS-RANGE-ORDERSAbrir fuente (abre en otra pestaña)

    Órdenes por rango con un solo activo y el riesgo de que el precio vuelva a cruzar el rango.

  10. [10]

    Uniswap Labs, "Oracle," Uniswap Docs, archivo de conceptos de protocolo (repositorio Uniswap/docs).

    docsID: UNISWAP-DOCS-ORACLEAbrir fuente (abre en otra pestaña)

    Observaciones con tickCumulative, máximo de 65.535 y media geométrica; v4 sin oráculo integrado.

Fuentes primarias, papers seminales y especificaciones técnicas citadas en esta investigación.

Rutas vecinas

Si este tema te abrió hambre, sigue por aquí.

Series y labs que comparten área o dependen de esta ruta. Pocas opciones, para no convertir la curiosidad en menú infinito.

Sigue leyendo

Ideas conectadas con este artículo

Una ruta corta para continuar sin abrir veinte pestañas y perder el hilo.

Los comentarios se cargan al final de la lectura para no meter peso antes de tiempo.