En capítulos anteriores, derivamos fórmulas para calcular las reservas reales de los tokens X e Y entre dos precios en un segmento. El protocolo utiliza estas fórmulas cuando los usuarios añaden o retiran liquidez, así como durante los swaps.
En este capítulo, mostramos dónde se encuentran estas fórmulas en el código base. Dado que el protocolo almacena las raíces cuadradas de los precios en el formato Q64.96, las fórmulas deben ajustarse para usar este formato.
Calculando las cantidades y en el código base
El código base contiene funciones auxiliares para calcular las cantidades de los tokens X e Y entre dos precios a liquidez constante. Estas funciones están definidas en la biblioteca SqrtPriceMath.sol con los siguientes nombres:
getAmount0Delta: Calcula las reservas reales en X entre dos precios para una liquidez dada.getAmount1Delta: Calcula las reservas reales en Y entre dos precios para una liquidez dada.
Estas fórmulas para las reservas reales en realidad dependen de las raíces cuadradas de los precios, no de los precios en sí. Podemos recuperar el precio a partir de su raíz cuadrada elevando el valor al cuadrado, pero esto solo se hace off-chain; no es necesario on-chain.
Por brevedad, continuaremos refiriéndonos a los precios, pero el lector debe tener en cuenta que el protocolo solo usa las raíces cuadradas de los precios. Por ejemplo, si el protocolo necesita calcular la cantidad de reservas reales entre los precios 100 y 200, llamará a estas funciones con los argumentos y , ambos en formato Q64.96, en lugar de 100 y 200.
Las funciones getAmount0Delta y getAmount1Delta
La biblioteca SqrtPriceMath tiene dos funciones llamadas getAmount0Delta, una de las cuales tiene la siguiente firma, la otra se discutirá en un capítulo posterior.

Recibe dos raíces cuadradas de precios en formato Q64.96 (sqrtRatioAX96 y sqrtRatioBX96), la liquidez y un booleano (roundUp) que especifica si la cantidad final debe redondearse hacia arriba o hacia abajo.
La cantidad se redondea hacia abajo cuando representa un pago del protocolo al usuario (como en los swaps o al retirar liquidez) y se redondea hacia arriba cuando representa un depósito del usuario al protocolo (como en los swaps o al proveer liquidez). El redondeo siempre favorece al protocolo y perjudica al usuario.
Esta función calcula y devuelve la cantidad de reservas reales en tokens X entre sqrtRatioAX96 y sqrtRatioBX96 para una liquidez dada , asumiendo que la liquidez en este segmento está completamente en el token X.
El protocolo también tiene dos funciones getAmount1Delta, una de las cuales tiene una firma muy similar a getAmount0Delta, la otra se discutirá en un capítulo posterior. Su propósito es calcular la cantidad de reservas reales en tokens Y entre sqrtRatioAX96 y sqrtRatioBX96, para una liquidez dada , asumiendo que la liquidez en este segmento está completamente en el token Y.

A esta función se le aplica el mismo comportamiento de redondeo que a getAmount0Delta.
La herramienta interactiva a continuación calcula lo mismo que getAmount0Delta y getAmount1Delta para un intervalo de precios, dada una liquidez. Para usarla, mueva los controles deslizantes azul y morado para ajustar sqrtRatioAX96 y sqrtRatioBX96, y el control deslizante naranja para ajustar la liquidez. Los botones de radio amount0/amount1 indican qué función debe ejecutarse. Para redondear el resultado hacia arriba, seleccione la casilla roundUp.
Tenga en cuenta que estas funciones asumen que, entre dos puntos de precio, la liquidez es constante y solo consta de una única reserva.
Tenemos que conocer esa información de antemano antes de usar estas funciones porque no tienen conocimiento de la distribución de la liquidez a lo largo de todo el rango de precios posibles.
La función getAmount0Delta
La función getAmount0Delta calcula la siguiente fórmula, la cual derivamos en el capítulo sobre reservas reales:
pero usando la raíz cuadrada de los precios en formato Q64.96, dada por sqrtRatioAX96 y sqrtRatioBX96.
Dado que la relación entre y sqrtRatioX96 es
podemos sustituir esta relación en la fórmula para de la siguiente manera
El último paso no es estrictamente necesario desde el punto de vista matemático, pero en el código donde la división introduce un error de redondeo, aumenta la precisión ya que realizamos solo una división en lugar de dos.
Esta fórmula se puede encontrar en las anotaciones de la función en el código base (sin el )

y está implementada en la función getAmount0Delta:

Los recuadros naranja y verde en la imagen anterior corresponden a las partes naranja y verde de la fórmula que acabamos de derivar, las cuales conforman el numerador. El recuadro azul contiene la expresión completa, con los términos del numerador multiplicados y divididos por sqrtRatioAX96 y sqrtRatioBX96.
La función mulDiv multiplica los dos primeros argumentos y divide el producto por el tercero. La función mulDivRoundingUp hace lo mismo, redondeando hacia arriba el resultado final.
El divRoundingUp realiza una división entera que se redondea hacia arriba.
Como se explicó, el parámetro roundUp indica si la cantidad final debe redondearse hacia arriba o no.
Las funciones getAmount1Delta
El objetivo de esta función es calcular la siguiente fórmula
pero en términos de sqrtRatioAX96 y sqrtRatioBX96 en lugar de y .
Recuerde que la relación entre y sqrtRatioX96 es
entonces podemos usar esta relación en la fórmula para como
Una vez más, reorganizamos la fórmula para tener solo una división, lo que aumenta la precisión y coincide con lo que se encuentra en el código base.
La imagen a continuación muestra la función getAmount1Delta .

Funciones getAmount0Delta y getAmount1Delta en Python
En esta sección, escribiremos las funciones getAmount0Delta y getAmount1Delta en Python, las cuales se usarán para calcular las reservas reales de un segmento.
Por simplicidad, no nos preocuparemos por los errores de redondeo en el script.
Se pueden escribir de la siguiente manera:
import math
def getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, liquidity, roundUp):
numerator1 = liquidity*2**96
numerator2 = sqrtRatioBX96 - sqrtRatioAX96
if roundUp:
return math.ceil(numerator1*numerator2 / (sqrtRatioBX96*sqrtRatioAX96))
else:
return math.floor(numerator1*numerator2 / (sqrtRatioBX96*sqrtRatioAX96))
def getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, liquidity, roundUp):
if roundUp:
return math.ceil(liquidity*(sqrtRatioBX96 - sqrtRatioAX96)/2**96)
else:
return math.floor(liquidity*(sqrtRatioBX96 - sqrtRatioAX96)/2**96)
Digamos que queremos calcular las reservas reales entre los ticks -10 y 10, con el precio actual exactamente en el tick cero, como se ilustra a continuación. Las reservas reales en tokens Y se encuentran entre el tick inferior (-10) y el precio actual (0), mientras que las reservas reales en tokens X se encuentran entre el precio actual (0) y el tick superior (10).

Podemos usar las funciones getAmount0Delta y getAmount1Delta para obtener estos valores.
Para determinar si redondear hacia arriba o hacia abajo, debemos considerar el propósito del cálculo. Si el cálculo es para proveer liquidez, el resultado se redondeará hacia arriba, y viceversa para retirar liquidez. Hagamos estos cálculos al proveer liquidez, donde se requiere redondear hacia arriba.
Los cálculos y los valores resultantes se muestran en el código a continuación.
def getSqrtRatioAtTick(i):
return math.sqrt(1.0001 ** i) * 2**96;
current_price = getSqrtRatioAtTick(0)
upper_price = getSqrtRatioAtTick(10)
lower_price = getSqrtRatioAtTick(-10)
print(getAmount0Delta(current_price, upper_price, 1000000000, True)) # 499851
print(getAmount1Delta(lower_price, current_price, 1000000000, True)) # 499851
Ejercicio: Calcule las reservas reales entre los ticks -10 y 20 en los siguientes casos:
- El precio actual está en el tick -20,
- El precio actual está en el tick 0,
- El precio actual está en el tick 20.
Conclusión
- El protocolo proporciona funciones para calcular las reservas reales entre dos precios arbitrarios, y una liquidez constante dada entre ellos. Estas funciones son
getAmount0Delta(para tokens X) ygetAmount1Delta(para tokens Y). - Estas funciones reciben los precios como raíces cuadradas en formato Q64.96, en variables llamadas
sqrtRatioAX96ysqrtRatioBX96. - La cantidad calculada de reservas reales se redondea hacia arriba si representa una cantidad que el protocolo debe recibir, y se redondea hacia abajo si representa una cantidad que el protocolo debe pagar.
Este artículo es parte de una serie sobre Uniswap V3.