Skip to main content
Try out the RareSkills ZK AI Tutor!
Blog

get_D() and get_y() in Curve StableSwap

Last updated on Aug 20, 2025

This article shows algebraically step-by-step how the code for get_D() and get_y() are derived from the StableSwap invariant.

Given the StableSwap Invariant:

Ann∑xi+D=AnnD+Dn+1nn∏xi

There are two frequent math operations we wish to conduct with it:

  1. Compute D given fixed values for A, and the reserves x1,…,xn. Note that n, the number of coins the pool supports, is fixed at the time the pool is deployed. This is what the function get_D() does.
  2. Given D, we wish to increase the value of one of the reserves xi to a new value xi′ and figure out how much another reserve xj needs to decrease to keep the equation balanced. This is what the function get_y() does. Here “y” means xi′.

These operations are called get_D() and get_y(), respectively, in Curve StableSwap.

The objective of get_D()

In Curve V1 (StableSwap), D behaves similarly to k in Uniswap V2 — the larger D is, the more the reserves are, and the “further out” the price curve will be. D changes — and needs to be recomputed — after liquidity is added or removed, or a fee changes the pool balance. This is what the function get_D() is for. Given the current reserves of the pool, it computes D.

If a curve pool holds two tokens, x and y, the StableSwap invariant is

4A(x+y)+D=4AD+D34xy

The “amplification factor” A, for our purposes, can be treated as a constant.

The objective of get_y()

The function get_y() is used during a swap. Similar to k in Uniswap V2, D must be held constant during a swap (ignoring fees). Specifically, given a new value for x, it computes the value of y that keeps the equation balanced. Thus, it is an important subroutine for figuring out “if I put in this much token x into the pool, how much token y can be taken out?”

Curve can hold more than 2 tokens in the pool (e.g. 3pool holds USDT, USDC, and DAI). Curve identifies the coins by an index in an array. So, in this case, x and y refer to particular coins in that array. In this context, get_y() means changing the balance of a particular token x, holding the other balances constant, but allowing another token y to change in value. Then, given a particular change in x, compute how y changes to keep the invariant balanced.

The invariant for n tokens is:

Ann∑xi+D=AnnD+Dn+1nn∏xi

For simplicity, we will use S instead of summation and P instead of product in the rest of the article, so the invariant becomes:

AnnS+D=ADnn+Dn+1nnP

Where S is the sum of the balances of the tokens (x0+x1+…+xn), P is the product of the balances (x0x1...xn), and xi is the balance of token i.

In the whitepaper, S is written as ∑xi and P is written as ∏xi. The whitepaper equation is replicated below:

Ann∑xi+D=ADnn+Dn+1nn∏xi

We will use S and P instead of the sum and product notation.

We assume that the pools can hold an arbitrary number n tokens, so the formulas will reflect that. In practice, however, n must be small, otherwise the Dn+1 term is liable to overflow.

Computing D with get_D()

In get_D(), we are presented with a set of balances x_0, x_1, ..., x_n and we are to compute D.

It is not possible to algebraically solve

AnnS+D=ADnn+Dn+1nnP

for D. Instead, we need to apply Newton’s method to solve it numerically. To do so, we create a function f(D), which is 0 when the equation is balanced.

0=ADnn+Dn+1nnP−D−AnnS
0=Dn+1nnP+ADnn−D−AnnS
f(D)=Dn+1nnP+AnnD−D−AnnS

and we compute the derivative f′(D) with respect to D as:

f′(D)=(n+1)DnnnP+Ann−1

Newton’s Method Formula

We can iteratively solve for D using:

Dnext=D−f(D)f′(D)

It will be helpful to express f′(D) with D in the denominator. First we multiply the top and bottom of the left fraction defining f′(D) by D.

f′(D)=(n+1)Dn+1nnPD+Ann−1

And then combine f′(D) into a single fraction:

f′(D)=(n+1)Dn+1nnPD+(Ann−1)DDf′(D)=(n+1)Dn+1nnP+(Ann−1)DD

We can re-write Newton’s method to have a common denominator:

Dnext=D−f(D)f′(D)// Newton’s Method=Df′(D)f′(D)−f(D)f′(D)// multiply D by f′(D)f′(D)=Df′(D)−f(D)f′(D)// combine by common denominator

By substituting the f(D) and f′(D) from earlier into the re-written Newton’s method formula we get:

=D(n+1)Dn+1nnP+(Ann−1)DD−(Dn+1nnP+AnnD−D−AnnS)(n+1)Dn+1nnP+(Ann−1)DD

Since we re-arranged f′(D) to have D in the denominator, the Df′(D) term will cancel nicely:

=D(n+1)Dn+1nnP+(Ann−1)DD−(Dn+1nnP+AnnD−D−AnnS)(n+1)Dn+1nnP+(Ann−1)DD
=(n+1)Dn+1nnP+(Ann−1)D−(Dn+1nnP+AnnD−D−AnnS)(n+1)Dn+1nnP+(Ann−1)DD

Distribute all the terms to remove the parenthesis in the numerator:

=Dn+1nnP+nDn+1nnP+AnnD−D−Dn+1nnP−AnnD+D+AnnS(n+1)Dn+1nnP+(Ann−1)DD

This leads to a lot of cancellations

=Dn+1nnP+nDn+1nnP+AnnD−D−Dn+1nnP−AnnD+D+AnnS(n+1)Dn+1nnP+(Ann−1)DD
=nDn+1nnP+AnnS(n+1)Dn+1nnP+(Ann−1)DD

We multiply the numerator and denominator by D

=(AnnS+nDn+1nnP)D(Ann−1)D+(n+1)Dn+1nnP

If we define Dp as

Dp=Dn+1nnP

and substitute Dp we get

=(AnnS+nDn+1nnP)D(Ann−1)D+(n+1)Dn+1nnP
Dnext=(AnnS+Dpn)D(Ann−1)D+(n+1)Dp

Comparison to the original source code

This matches exactly what is in the Vyper code:

Screenshot of get_D() with the code annotated

The variable Dp was defined as:

D_P: uint256 = D # D_P = S

for _x in xp:
    D_P = D_P * D / (_x * N_COINS)

xp is the number of tokens, so the loop will run n times. Therefore, we have D multiplied by itself n times in the denominator

Dp=Dn+1nn∏i=1nxi

Computing y with get_y()

The idea is we force one of the xi to take on a new value (the code calls this x) and calculate the correct value for another xj (where i≠j) such that the equation stays balanced. The balance of the other tokens remains unchanged. xj is referred to as y.

Although a StableSwap pool could have multiple tokens, it is only possible to exchange two of those tokens at a time using get_y().

Again, we have the same invariant

AnnS+D=ADnn+Dn+1nnP

D, A, and n are fixed, but we will be changing two of the values in S and P

S=x0+x1+...+xnP=x0x1...xn

Therefore, we need to adjust the formula a bit, since S and P contain the values we are computing for.

  • S′ will be the sum of all the balances except the new balance of token xi that we are trying to solve for
  • P will be the product of the balances of all the tokens, except for the one we are trying to solve for.

In other words,

S=S′+yP=P′y

To stay consistent with the code, we will call the token whose new balance we are trying to compute y.

The formula then becomes

Ann(S′+y)+D=ADnn+Dn+1nnP′y

Again, we derive an f(y) which is 0 when the equation is balanced, and its derivative with respect to y

f(y)=ADnn+Dn+1nnP′y−Ann(S′+y)−Df′(y)=−Dn+1nnP′y2−Ann

Here is the formula for Newton’s method again:

ynext=y−f(y)f′(y)

After substituting f(y) and f′(y) into Newton’s method we get:

ynext=y−ADnn+Dn+1nnP′y−Ann(S′+y)−D−Dn+1nnP′y2−Ann

Take −1 out of the denominator

ynext=y−−1⋅ADnn+Dn+1nnP′y−Ann(S′+y)−DDn+1nnP′y2+Ann
ynext=y+ADnn+Dn+1nnP′y−Ann(S′+y)−DDn+1nnP′y2+Ann

Multiply y (in the box below) to have a common denominator:

ynext=y+ADnn+Dn+1nnP′y−Ann(S′+y)−DDn+1nnP′y2+Ann
ynext=yDn+1nnP′y2+AnnDn+1nnP′y2+Ann+ADnn+Dn+1nnP′y−Ann(S′+y)−DDn+1nnP′y2+Ann

Distribute y in the left term

ynext=yDn+1nnP′y2+AnnDn+1nnP′y2+Ann+ADnn+Dn+1nnP′y−Ann(S′+y)−DDn+1nnP′y2+Ann
ynext=Dn+1nnP′y+AnnyDn+1nnP′y2+Ann+ADnn+Dn+1nnP′y−AnnS′−Anny−DDn+1nnP′y2+Ann

Combine the sums with a common denominator

ynext=ADnn+2Dn+1nnP′y−AnnS′−DDn+1nnP′y2+Ann

Creating a substitution from the original invariant

It might seem like the equation cannot be simplified further, but if we revisit our original invariant

Ann(S′+y)+D=ADnn+Dn+1nnP′y

We can solve for ADnn we get

Ann(S′+y)+D=ADnn+Dn+1nnP′y
Ann(S′+y)+D−Dn+1nnP′y=ADnn
ADnn=−Dn+1nnP′y+Ann(S′+y)+D

And then if we substitute ADnn into the numerator in our latest formula for ynext, we get

ynext=ADnn+2Dn+1nnP′y−AnnS′−DDn+1nnP′y2+Ann
ynext=(−Dn+1nnP′y+Ann(S′+y)+D)+2Dn+1nnP′y−AnnS′−DDn+1nnP′y2+Ann

A lot of cancellation happens

ynext=−Dn+1nnP′y+AnnS′+Anny+D+2Dn+1nnP′y−AnnS′−DDn+1nnP′y2+Ann

And we are left with a much smaller equation

ynext=Anny+Dn+1nnP′yDn+1nnP′y2+Ann

We multiply the top and bottom by yAnn

ynext=(Anny+Dn+1nnP′y)yAnn(Dn+1nnP′y2+Ann)yAnn
ynext=y2+Dn+1nnP′AnnDn+1nnP′y1Ann+y

Going back to our invariant, we can solve for the fractional term in the denominator:

Ann(S′+y)+D=ADnn+Dn+1nnP′y
Dn+1nnP′y=Ann(S′+y)+D−ADnn

Then we can substitute that into the equation for ynext:

ynext=y2+Dn+1nnP′AnnDn+1nnP′y1Ann+y
ynext=y2+Dn+1nnP′Ann(Ann(S′+y)+D−ADnn)1Ann+y

Then we can distribute 1Annand simplify the denominator

ynext=y2+Dn+1nnP′Ann((S′+y)+DAnn−D)+y
ynext=y2+Dn+1nnP′Ann(S′+y+DAnn−D)+y

Simplify the denominator by removing the parenthesis and adding the two y together

ynext=y2+Dn+1nnP′Ann2y+S′+DAnn−D

In the original code, Curve defines additional variables:

c=Dn+1nnP′Ann
b=S′+DAnn

After substitution into the formula for ynext, we get:

ynext=y2+Dn+1nnP′Ann2y+S′+DAnn−D
ynext=y2+c2y+b−D

Comparison to the original source code

This matches the Curve code exactly, see the purple box below:

screenshot of get_y() annotated

Mismatch between Ann and Anⁿ

Rather confusingly, the Curve whitepaper uses the invariant Ann but the codebase uses Ann. That is, the codebase appears to be computing A * n * n rather than A * n ** n. The reason for this discrepancy is that the codebase stores A as Ann−1. Since n is fixed at deployment time, precomputing nn−1 allows the code to avoid computing an exponent on-chain, which is more costly operation.

Summary

The core invariant of the Curve does not allow the variables D or xi to be solved symbolically. Instead, the terms must be solved numerically.

One takeway from this exercise is that good algebraic manipulation is a very effective gas optimization technique. The Curve developers were able to compute Newton’s method formula that is much smaller than naively plugging in f and its derivative and leaving it at that.

Citations and Acknowledgements

The following resources were consulted in writing this article:

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