Este artículo muestra algebraicamente paso a paso cómo se deriva el código para get_D() y get_y() a partir del invariante de StableSwap.
Dado el invariante de StableSwap:
Hay dos operaciones matemáticas frecuentes que deseamos realizar con él:
- Calcular dados valores fijos para , y las reservas . Ten en cuenta que , el número de monedas que soporta el pool, se fija en el momento en que se despliega el pool. Esto es lo que hace la función
get_D(). - Dado , deseamos aumentar el valor de una de las reservas a un nuevo valor y averiguar cuánto necesita disminuir otra reserva para mantener la ecuación equilibrada. Esto es lo que hace la función
get_y(). Aquí “y” significa .
Estas operaciones se llaman get_D() y get_y(), respectivamente, en Curve StableSwap.
El objetivo de get_D()
En Curve V1 (StableSwap), D se comporta de manera similar a k en Uniswap V2 — cuanto mayor sea D, mayores serán las reservas y más “hacia afuera” estará la curva de precios. D cambia — y necesita ser recalculado — después de que se añade o se retira liquidez, o si una comisión cambia el balance del pool. Para esto sirve la función get_D(). Dadas las reservas actuales del pool, calcula D.
Si un pool de Curve contiene dos tokens, x e y, el invariante de StableSwap es
El “factor de amplificación” A, para nuestros propósitos, puede ser tratado como una constante.
El objetivo de get_y()
La función get_y() se utiliza durante un intercambio (swap). De manera similar a k en Uniswap V2, D debe mantenerse constante durante un swap (ignorando las comisiones). Específicamente, dado un nuevo valor para x, calcula el valor de y que mantiene la ecuación equilibrada. Por lo tanto, es una subrutina importante para averiguar “si meto esta cantidad de token x en el pool, ¿qué cantidad de token y se puede sacar?”
Curve puede contener más de 2 tokens en el pool (por ejemplo, 3pool contiene USDT, USDC y DAI). Curve identifica las monedas por un índice en un array. Así que, en este caso, x e y se refieren a monedas particulares en ese array. En este contexto, get_y() significa cambiar el balance de un token particular x, manteniendo los otros balances constantes, pero permitiendo que otro token y cambie de valor. Luego, dado un cambio particular en x, calcula cómo cambia y para mantener el invariante equilibrado.
El invariante para n tokens es:
Para mayor simplicidad, utilizaremos en lugar de sumatoria y en lugar de productoria en el resto del artículo, de modo que el invariante se convierte en:
Donde es la suma de los balances de los tokens (), es el producto de los balances (), y es el balance del token i.
En el whitepaper, se escribe como y se escribe como . La ecuación del whitepaper se replica a continuación:
Utilizaremos y en lugar de la notación de suma y producto.
Asumimos que los pools pueden contener un número arbitrario de tokens, por lo que las fórmulas reflejarán eso. En la práctica, sin embargo, debe ser pequeño, de lo contrario el término es susceptible de sufrir un overflow.
Calculando con get_D()
En get_D(), se nos presenta un conjunto de balances x_0, x_1, ..., x_n y debemos calcular D.
No es posible despejar algebraicamente
para . En su lugar, necesitamos aplicar el método de Newton para resolverlo numéricamente. Para hacerlo, creamos una función , la cual es 0 cuando la ecuación está equilibrada.
y calculamos la derivada con respecto a como:
Fórmula del Método de Newton
Podemos despejar iterativamente utilizando:
Será útil expresar con en el denominador. Primero multiplicamos la parte superior e inferior de la fracción izquierda que define por .
Y luego combinamos en una sola fracción:
Podemos reescribir el método de Newton para tener un denominador común:
Al sustituir el y anteriores en la fórmula reescrita del método de Newton obtenemos:
Dado que reorganizamos para tener en el denominador, el término se cancelará perfectamente:
Distribuimos todos los términos para eliminar los paréntesis en el numerador:
Esto conduce a muchas cancelaciones
Multiplicamos el numerador y el denominador por
Si definimos como
y sustituimos obtenemos
Comparación con el código fuente original
Esto coincide exactamente con lo que hay en el código de Vyper:

La variable se definió como:
D_P: uint256 = D # D_P = S
for _x in xp:
D_P = D_P * D / (_x * N_COINS)
xp es el número de tokens, por lo que el bucle se ejecutará n veces. Por lo tanto, tenemos a multiplicado por sí mismo n veces en el denominador
Calculando y con get_y()
La idea es que forzamos a uno de los a tomar un nuevo valor (el código llama a esto x) y calculamos el valor correcto para otro (donde de tal manera que la ecuación se mantenga equilibrada. El balance de los otros tokens permanece inalterado. A se le denomina .
Aunque un pool de StableSwap podría tener múltiples tokens, solo es posible intercambiar dos de esos tokens a la vez usando get_y().
De nuevo, tenemos el mismo invariante
, , y son fijos, pero estaremos cambiando dos de los valores en y
Por lo tanto, necesitamos ajustar la fórmula un poco, ya que y contienen los valores que estamos calculando.
- será la suma de todos los balances excepto el nuevo balance del token que estamos intentando despejar
- será el producto de los balances de todos los tokens, excepto el del que estamos intentando despejar.
En otras palabras,
Para mantener la consistencia con el código, llamaremos al token cuyo nuevo balance estamos intentando calcular.
Entonces la fórmula se convierte en
De nuevo, derivamos un que es 0 cuando la ecuación está equilibrada, y su derivada con respecto a y
Aquí está la fórmula del método de Newton de nuevo:
Después de sustituir y en el método de Newton obtenemos:
Sacamos el del denominador
Multiplicamos (en la caja a continuación) para tener un denominador común:
Distribuimos en el término izquierdo
Combinamos las sumas con un denominador común
Creando una sustitución a partir del invariante original
Podría parecer que la ecuación no se puede simplificar más, pero si revisamos nuestro invariante original
Podemos despejar y obtenemos
Y luego, si sustituimos en el numerador de nuestra última fórmula para , obtenemos
Ocurren muchas cancelaciones
Y nos quedamos con una ecuación mucho más pequeña
Multiplicamos la parte superior e inferior por
Volviendo a nuestro invariante, podemos despejar el término fraccionario en el denominador:
Luego podemos sustituir eso en la ecuación para ynext:
Luego podemos distribuir y simplificar el denominador
Simplificamos el denominador eliminando los paréntesis y sumando las dos juntas
En el código original, Curve define variables adicionales:
Después de la sustitución en la fórmula para , obtenemos:
Comparación con el código fuente original
Esto coincide exactamente con el código de Curve, como se ve en el recuadro morado a continuación:

Discrepancia entre Ann y Anⁿ
De manera un tanto confusa, el whitepaper de Curve usa el invariante pero la base de código usa Ann. Es decir, la base de código parece estar calculando A * n * n en lugar de A * n ** n. La razón de esta discrepancia es que la base de código almacena como . Dado que es fijo en el momento del despliegue, precalcular permite que el código evite calcular un exponente on-chain, lo cual es una operación más costosa.
Resumen
El invariante central de Curve no permite que las variables o se despejen de manera simbólica. En su lugar, los términos deben resolverse numéricamente.
Una de las lecciones de este ejercicio es que una buena manipulación algebraica es una técnica muy efectiva de optimización de gas. Los desarrolladores de Curve fueron capaces de calcular la fórmula del método de Newton que es mucho más reducida que simplemente insertar y su derivada y dejarlo así.
Citas y Agradecimientos
Los siguientes recursos fueron consultados al escribir este artículo:
StableSwap - efficient mechanism for Stablecoin liquidity, Michael Egorov, https://resources.curve.fi/pdf/curve-stableswap.pdf
Understanding the Curve AMM, Part -1: StableSwap Invariant, Atul Agarwal https://atulagarwal.dev/posts/curveamm/stableswap/
Curve Finance Discord, “chanho”
https://discord.com/channels/729808684359876718/729812922649542758/1126630568004698132
Curve - Code Explaind - get_y() | DeFi, Smart Contract Programmer https://www.youtube.com/watch?v=jAhKbxoeskQ