© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2023
M. Ottina et al.Automated Market Makershttps://doi.org/10.1007/978-1-4842-8616-6_4

4. Curve Finance

Miguel Ottina1  , Peter Johannes Steffensen2 and Jesper Kristensen3
(1)
Rivadavia, Argentina
(2)
Aarhus, Denmark
(3)
New York, NY, USA
 

Curve Finance’s AMM was originally designed to facilitate trades with low price impact between stablecoins or, more generally, between assets that have the same pricelike ETH and sETH (Synthetix1 ETH). Curve’s AMM was launched in January 2020 and was initially called StableSwap. Its algorithm is more complex than those of the AMMs we have studied in the previous chapters.

In June 2021, Curve v2 was presented [12]. This new version of Curve’s AMM is called Crypto Pools, and although it is based on the previous one, it introduces a new algorithm to allow trading between nonpegged assets.2

Currently, both versions of the Curve AMM coexist. In this chapter, we will explain how the StableSwap AMM works and analyze the maths behind it. We will not cover Curve v2 in this book.

4.1 The StableSwap Invariant

The StableSwap invariant is a combination of the constant product invariant and the constant sum invariant, which are defined as

$$ prod limits_{j=1}^N{b}_j=C $$ and $$ sum limits_{j=1}^N{b}_j=D $$

respectively, where C and D are positive real numbers, N is the number of tokens in the pool, and, for each j ∈ {1, 2, …, N}, bj denotes the balance of token j in the pool. Observe that the product invariant is equivalent to Balancer’s geometric mean invariant with equally weighted tokens. On the other hand, note that the constant sum invariant gives a constant price of 1 for each pair of tokens, since if we deposit an amount b of token i and receive an amount a of token o, from the constant sum invariant, we obtain that
$$ sum limits_{j=1}^N{b}_j=left({b}_i+b
ight)+left({b}_o-a
ight)+sum limits_{egin{array}{c}j=1\ {}j
e i,oend{array}}^N{b}_j, $$

and hence, bi + bo = bi + b + bo − a, from where it follows that a = b, which implies that the effective price paid per unit of token o in terms of token i is $$ frac{b}{a}=1 $$. In particular, there is no difference between the spot price and the effective price paid by a trader; that is, there is no price impact.

As we previously mentioned, the StableSwap AMM invariant is obtained as a combination of the constant sum and constant product invariants. We will explain now how it is constructed. Let N be the number of tokens in the pool, and for j ∈ {1, 2, …, N}, let bj be a variable that represents the balance of token j in the pool. Recall that the constant sum invariant is given by
$$ sum limits_{j=1}^N{b}_j=D $$
for a certain positive number D and the constant product invariant is given by
$$ prod limits_{j=1}^N{b}_j=C $$
for a certain positive number C. In order to combine these invariants, we will establish a relation between the numbers C and D. To this end, assume for a moment that all tokens are equally priced. Hence, for all j, k ∈ {1, 2, …, N}, the spot price of token j in terms of token k is 1, and then from Equation 3.​1, we obtain that bj = bk for all j, k ∈ {1, 2, …, N}. Thus, from the constant sum formula, we obtain that $$ {b}_j=frac{D}{N} $$ for all j ∈ {1, 2, …, N}, and substituting these expressions in the constant product formula, we obtain that $$ C={left(frac{D}{N}
ight)}^N $$. Thus, our constant product invariant will be
$$ prod limits_{j=1}^N{b}_j={left(frac{D}{N}
ight)}^N. $$
The first idea to combine both invariants is to take a convex combination of both formulae; that is, for any chosen t ∈ [0, 1], we consider the expression
$$ tsum limits_{j=1}^N{b}_j+left(1-t
ight)prod limits_{j=1}^N{b}_j= tD+left(1-t
ight){left(frac{D}{N}
ight)}^N. $$

Note that when t = 0, we obtain the constant product formula, and when t = 1, we obtain the constant sum formula.

Now we will modify the previous expression, replacing the parameter t with a new parameter x so that we obtain the constant product formula when x = 0 and the constant sum formula when x tends to infinity. To this end, we need to replace the parameter t with a function g(x) such that
$$ g(0)=0kern0.24em 	extrm{and};underset{x	o +infty }{lim }g(x)=1. $$
Although many functions can do the trick, we consider the map g : [0, +∞) → ℝ given by $$ g(x)=frac{x}{x+1} $$. Note that this function g satisfies thedesired properties and that 0 ≤ g(x) ≤ 1 for all x ≥ 0. Replacing t = g(x) in the preceding expression, we obtain
$$ frac{x}{x+1}sum limits_{j=1}^N{b}_j+frac{1}{x+1}prod limits_{j=1}^N{b}_j=frac{x}{x+1}D+frac{1}{x+1}{left(frac{D}{N}
ight)}^N $$
or equivalently,
$$ xsum limits_{j=1}^N{b}_j+prod limits_{j=1}^N{b}_j= xD+{left(frac{D}{N}
ight)}^N. $$
In addition, we want the parameter x to be dimensionless. To this end, we will substitute x = χDN − 1 to obtain
$$ chi {D}^{N-1}sum limits_{j=1}^N{b}_j+prod limits_{j=1}^N{b}_j=chi {D}^N+{left(frac{D}{N}
ight)}^N. $$
Finally, we want the parameter χ to be dynamic rather than a constant chosen value. To this end, we substitute
$$ chi =Acdot frac{prod limits_{j=1}^N{b}_j}{{left(frac{D}{N}
ight)}^N}, $$
(4.1)

where A is a positive number that is called amplification coefficient. In the actual code, there is a parameter labeled A, which is a positive integer and is equal to ANN − 1. Therefore, from now on, we will assume that ANN − 1 is a positive integerand this will indeed be needed later.

The idea behind the substitution of Equation 4.1 is the following. Assuming that $$ {sum}_{j=1}^N{b}_j=D $$ (which will not hold in general as the pool balances change), by the inequality of arithmetic and geometric means,3 we have that
$$ {left(prod limits_{j=1}^N{b}_j
ight)}^{frac{1}{N}}le frac{1}{N}sum limits_{j=1}^N{b}_j=frac{D}{N}, $$
(with equality holding if and only if b1 = b2 = … = bN). Hence,
$$ prod limits_{j=1}^N{b}_jle {left(frac{D}{N}
ight)}^N, $$
and thus, χ ≤ A. On the other hand, when the balances move away of the equilibrium point b1 = b2 = … = bN, the parameter χ decreases, as we can see for the case N = 2 in Figure 4-1, where we plot the graph of the function f : [0, 2] × [0, 2] → ℝ given by
$$ fleft({b}_1,{b}_2
ight)=frac{b_1{b}_2}{{left(frac{b_1+{b}_2}{2}
ight)}^2}. $$
As we can see, the function has maximum value 1 when b1 = b2 and decreases when we move away from this line.
Figure 4-1

Graph of the function f

Making the aforementioned substitution of χ, we obtain
$$ Acdot frac{prod limits_{j=1}^N{b}_j}{{left(frac{D}{N}
ight)}^N}{D}^{N-1}sum limits_{j=1}^N{b}_j+prod limits_{j=1}^N{b}_j=Acdot frac{prod limits_{j=1}^N{b}_j}{{left(frac{D}{N}
ight)}^N}{D}^N+{left(frac{D}{N}
ight)}^N, $$
and multiplying both sides by $$ frac{D}{prod limits_{j=1}^N{b}_j} $$, we get
$$ {AN}^Nsum limits_{j=1}^N{b}_j+D={ADN}^N+frac{D^{N+1}}{N^Nprod limits_{j=1}^N{b}_j}, $$
(4.2)

which is the StableSwap invariant [11].

In Figure 4-2, we compare the StableSwap invariant for three different values of the amplification coefficient A with the constant product and constant sum invariants in a pool with two tokens. Observe that at the equilibrium point, the price of token X in terms of token Y is 1, since the slope of the curve is –1. In addition, near the equilibrium point, the curve looks like a constant sum invariant curve, and hence, the price impact is very low, and the price is approximately 1. But if the balances of the tokens deviate from the equilibrium point, the price changes, and the curve looks more like a constant product invariant one.
Figure 4-2

Comparison between the StableSwap invariant, the constant product invariant, and the constant sum invariant

4.2 Mathematical Preliminaries

In this subsection, we will analyze several mathematical arguments that are used within the StableSwap AMM. We will also prove some results that will be needed later in order to explain the mathematical foundations of this AMM.

Consider a StableSwap liquidity pool consisting of N tokens. For each j ∈ {1, 2, …, N}, let Bj be the balance of token j in the pool. Let D0 be the value of the pool parameter D. Recall that D0 > 0. Let o ∈ {1, 2, …, N}. We will prove that the balance of token o is uniquely determined by D0 and the balances Bj, j ≠ o.

For simplicity, let y = Bo. From the invariant Equation 4.2, we know that
$$ {AN}^Nsum limits_{j=1}^N{B}_j+{D}_0={AD}_0{N}^N+frac{D_0^{N+1}}{N^Nprod limits_{j=1}^N{B}_j}, $$
that is,
$$ {AN}^Ny+{AN}^Nsum limits_{egin{array}{c}j=1\ {}j
e oend{array}}^N{B}_j+{D}_0-{AD}_0{N}^N=frac{D_0^{N+1}}{N^Nyprod limits_{egin{array}{c}j=1\ {}j
e oend{array}}^N{B}_j}. $$
Multiplying both sides of the equation by $$ frac{y}{AN^N} $$ we get
$$ {y}^2+left(sum limits_{egin{array}{c}j=1\ {}j
e oend{array}}^N{B}_j+frac{D_0}{AN^N}-{D}_0
ight)y=frac{D_0^{N+1}}{AN^{2N}prod limits_{egin{array}{c}j=1\ {}j
e oend{array}}^N{B}_j}. $$
Let
$$ {displaystyle egin{array}{llll}S& =sum limits_{egin{array}{c}j=1\ {}j
e oend{array}}^N{B}_j,& P& =prod limits_{egin{array}{c}j=1\ {}j
e oend{array}}^N{B}_j,\ {}b& =S+frac{D_0}{AN^N},	extrm{and}& c& =frac{D_0^{N+1}}{AN^{2N}P}.end{array}} $$
With this notations, the previous equation can be written as
$$ {y}^2+left(b-{D}_0
ight)y-c=0, $$
(4.3)

and the value of y can be obtained through solving the previous quadratic equation. Observe that if y1 and y2 are the solutions of the previous equation, then y1y2 =  − c, and since c > 0, we obtain that y1y2 < 0. This means that one of the solutions of the previous equation is positive and the other is negative. Thus, the value of y that we are looking for is well defined since it is the only positive solution to the previous equation. Therefore, we have proved that the balance of token o is uniquely determined by D0 and the balances Bj, j ≠ 0.

The previous proof allows us to give the following definition.

Definition 4.1. Consider a StableSwap liquidity pool that has N tokens. Let o ∈ {1, 2, …, N}. For each j ∈ {1, 2, …, N} − {o}, let Bj be the balance of token j in the pool. Let D0 be the value of the pool parameter D. We define
$$ mathcal{Y}left(o,{B}_1,dots, {B}_{o-1},{B}_{o+1},dots, {B}_N,{D}_0
ight) $$

as the only positive number that satisfies Equation 4.3. That is, the number $$ mathcal{Y}left(o,{B}_1,dots, {B}_{o-1},{B}_{o+1},dots, {B}_N,{D}_0
ight) $$ is the balance of token o if the balance of token j is Bj for all j ∈ {1, 2, …, N} − {o} and the value of the pool parameter D is D0.

We will analyze now how $$ mathcal{Y}left(o,{B}_1,dots, {B}_{o-1},{B}_{o+1},dots, {B}_N,{D}_0
ight) $$ changes when we modify the balances Bj, j ≠ o, or the value of the pool parameter D.

Proposition 4.1. Consider a StableSwap liquidity pool of N tokens. Let o ∈ {1, 2, …, N}. For each j ∈ {1, 2, …, N} − {o}, let Bj and $$ {B}_j^{hbox{'}} $$ be positive real numbers. Let
$$ 	extbf{B}=left({B}_1,dots, {B}_{o-1},{B}_{o+1},dots, {B}_N
ight) $$
and let
$$ {	extbf{B}}^{hbox{'}}=left({B}_1^{hbox{'}},dots, {B}_{o-1}^{hbox{'}},{B}_{o+1}^{hbox{'}},dots, {B}_N^{hbox{'}}
ight). $$
Let D0 and $$ {D}_0^{hbox{'}} $$ be positive real numbers. If $$ {D}_0ge {D}_0^{hbox{'}} $$ and $$ {B}_jle {B}_j^{hbox{'}} $$ for all j ∈ {1, 2, …, N} − {o}, then
$$ mathcal{Y}left(o,	extbf{B},{D}_0
ight)ge mathcal{Y}left(o,{	extbf{B}}^{hbox{'}},{D}_0^{hbox{'}}
ight). $$
If, in addition, any of the inequalities $$ {B}_jle {B}_j^{hbox{'}},j
e o $$, or $$ {D}_0ge {D}_0^{hbox{'}} $$ is strict, then
$$ mathcal{Y}left(o,	extbf{B},{D}_0
ight)&gt;mathcal{Y}left(o,{	extbf{B}}^{hbox{'}},{D}_0^{hbox{'}}
ight). $$
Proof. Let
$$ y=mathcal{Y}left(o,	extbf{B},{D}_0
ight) $$
and let
$$ {y}^{hbox{'}}=mathcal{Y}left(o,{	extbf{B}}^{hbox{'}},{D}_0^{hbox{'}}
ight). $$
Let
$$ Skern0.5em =sum limits_{egin{array}{c}j=1\ {}j
e oend{array}}^N{B}_j,kern0.5em {S}^{hbox{'}}kern0.5em =sum limits_{egin{array}{c}j=1\ {}j
e oend{array}}^N{B}_j^{hbox{'}}, $$
$$ Pkern0.5em =prod limits_{egin{array}{c}j=1\ {}j
e oend{array}}^N{B}_j,kern0.5em {P}^{hbox{'}}kern0.5em =prod limits_{egin{array}{c}j=1\ {}j
e oend{array}}^N{B}_j^{hbox{'}}, $$
$$ bkern0.5em =S+frac{D_0}{AN^N},kern0.5em {b}^{hbox{'}}kern0.5em ={S}^{hbox{'}}+frac{D_0^{hbox{'}}}{AN^N}, $$
$$ ckern0.5em =frac{D_0^{N+1}}{AN^{2N}P},	extrm{and}kern0.5em {c}^{hbox{'}}kern0.5em =frac{{left({D}_0^{hbox{'}}
ight)}^{N+1}}{AN^{2N}{P}^{hbox{'}}}. $$
Recall that y2 + (b − D0)y − c = 0 and $$ {left({y}^{hbox{'}}
ight)}^2+left({b}^{hbox{'}}-{D}_0^{hbox{'}}
ight){y}^{hbox{'}}-{c}^{hbox{'}}=0 $$ and that y > 0 and y´ > 0. Hence,
$$ y+left(b-{D}_0
ight)=frac{c}{y}kern0.24em 	extrm{and};{y}^{hbox{'}}+left({b}^{hbox{'}}-{D}_0^{hbox{'}}
ight)=frac{c^{hbox{'}}}{y^{hbox{'}}}. $$
Suppose that $$ {B}_jle {B}_j^{hbox{'}} $$ for all j ∈ {1, 2, …, N} − {o} and D0$$ {D}_0^{hbox{'}} $$. Then S ≤ Ś and P ≤ P´. And since $$ {D}_0ge {D}_0^{hbox{'}} $$, we obtain that c ≥ c´. On the other hand, since ANN − 1 is a positive integer and N ≥ 2, it follows that $$ frac{1}{AN^N}-1&lt;0 $$. Thus,
$$ b-{D}_0=S+frac{D_0}{AN^N}-{D}_0=S+{D}_0left(frac{1}{AN^N}-1
ight) $$
$$ le {S}^{hbox{'}}+{D}_0^{hbox{'}}left(frac{1}{AN^N}-1
ight)={S}^{hbox{'}}+frac{D_0^{hbox{'}}}{AN^N}-{D}_0^{hbox{'}} $$
$$ ={b}^{hbox{'}}-{D}_0^{hbox{'}} $$
Suppose that y < yʹ. Then
$$ frac{c}{y}&gt;frac{c^{hbox{'}}}{y^{hbox{'}}}={y}^{hbox{'}}+left({b}^{hbox{'}}-{D}_0^{hbox{'}}
ight)&gt;y+left(b-{D}_0
ight)=frac{c}{y} $$

which entails a contradiction. Thus, y ≥ yʹ.

In addition, if any of the inequalities $$ {B}_jle {B}_j^{hbox{'}},j
e o $$, or D0$$ {D}_0^{hbox{'}} $$ is strict, then c > cʹ. Suppose that y ≤ yʹ. Then
$$ frac{c}{y}&gt;frac{c^{hbox{'}}}{y^{hbox{'}}}={y}^{hbox{'}}+left({b}^{hbox{'}}-{D}_0^{hbox{'}}
ight)ge y+left(b-{D}_0
ight)=frac{c}{y} $$

which entails a contradiction. Thus, y > yʹ.        □

4.2.1 Finding the Parameter D

After each transaction, the pool parameter D is updated based on the current pool state. Given a liquidity pool with N tokens and balances B1, B2, …, BN, the parameter D can be found by solving the following equation:
$$ frac{D^{N+1}}{N^Nprod limits_{j=1}^N{B}_j}+Dleft({AN}^N-1
ight)-{AN}^Nsum limits_{j=1}^N{B}_j=0 $$
(4.4)

which follows from the invariant Equation 4.2.

Note that in order for the value D to be well defined, we need the previous equation to have a unique positive solution. We will prove now that this is indeed the case. Let g : ℝ → ℝ be defined by
$$ g(x)=frac{1}{N^Nprod limits_{j=1}^N{B}_j}{x}^{N+1}+left({AN}^N-1
ight)x-{AN}^Nsum limits_{j=1}^N{B}_j. $$
Note that g is a polynomial of degree N + 1 (and hence a continuous function) and that ANN − 1 > 0 since ANN − 1 is a positive integer and N ≥ 2. On the other hand, the derivative of g is
$$ {g}^{hbox{'}}(x)=frac{N+1}{N^Nprod limits_{j=1}^N{B}_j}{x}^N+left({AN}^N-1
ight). $$
Observe that gʹ(x) > 0 for all x ≥ 0 since N, B1, B2, …, BN and ANN − 1 are positive numbers. Thus, g is a strictly increasing function in the interval [0, +∞). It follows that g has at most one root in that interval. In addition, since
$$ g(0)=-{AN}^Nsum limits_{j=1}^N{B}_j&lt;0kern1em 	extrm{and}kern1em underset{x	o +infty }{lim }g(x)=+infty $$

(because the leading coefficient of g is a positive number), we obtain that g has at least one root in the interval (0, +∞). Therefore, g has exactly one positive root, and thus, the parameter D is well defined.

Now, in order to effectively compute the value of the parameter D, the smart contract applies the Newton’s Method4 to the function g. Explicitly, let d0 > 0 be an initial approximate value for the parameter D. For example, we can take
$$ {d}_0=sum limits_{j=1}^N{B}_j, $$
where B1, B2, …, BN are the balances of the tokens of the pool. Now let
$$ {d}_1={d}_0-frac{gleft({d}_0
ight)}{g^{hbox{'}}left({d}_0
ight)}. $$
Then, for each n ∈ ℕ, let
$$ {d}_{n+1}={d}_n-frac{gleft({d}_n
ight)}{g^{hbox{'}}left({d}_n
ight)}. $$
(4.5)

The process ends when the desired precision has been obtained, that is, when the difference between two consecutive terms, dk and dk + 1, is smaller than a certain number.

This process can be found in the get D function of the smart contract of the StableSwap AMM,5 as we will see now. For simplicity, let
$$ alpha ={AN}^N-1kern1em 	extrm{and}kern1em eta =frac{1}{N^Nprod limits_{j=1}^N{B}_j}. $$
Note that
$$ {d}_{n+1}={d}_n-frac{gleft({d}_n
ight)}{g^{hbox{'}}left({d}_n
ight)}={d}_n-frac{eta {d}_n^{N+1}+alpha {d}_n-{AN}^Nsum limits_{j=1}^N{B}_j}{left(N+1
ight)eta {d}_n^N+alpha } $$
$$ =frac{left(N+1
ight)eta {d}_n^{N+1}+alpha {d}_n-eta {d}_n^{N+1}-alpha {d}_n+{AN}^Nsum limits_{j=1}^N{B}_j}{left(N+1
ight)eta {d}_n^N+alpha } $$
$$ =frac{Neta {d}_n^{N+1}+{AN}^Nsum limits_{j=1}^N{B}_j}{left(N+1
ight)eta {d}_n^N+alpha }=frac{left( Neta {d}_n^{N+1}+{AN}^Nsum limits_{j=1}^N{B}_j
ight){d}_n}{left(N+1
ight)eta {d}_n^{N+1}+alpha {d}_n}. $$
This formula can be found in the code shown in Listing 4-1, where the translation between the variables of the code and our notation is the following:
$$ {displaystyle egin{array}{rcl}{	exttt{N}}_{hbox{-}}	exttt{COINS}&amp; =&amp; N,\ {}kern1.2em 	exttt{Ann}&amp; =&amp; {AN}^N,\ {}kern1.92em 	exttt{S}&amp; =&amp; sum limits_{j=1}^N{B}_j,\ {}kern1.92em 	exttt{D}&amp; =&amp; {d}_n,\ {}kern1.32em {	exttt{D}}_{hbox{-}}	exttt{P}&amp; =&amp; eta {d}_n^{N+1}=frac{d_n^{N+1}}{N^Nprod limits_{j=1}^N{B}_j}end{array}} $$
Note also that d0 = S.
def get_D (xp: uint256[N_COINS], amp: uint256) ->
↪ uint256:
    S: uint256 =0
    for _x in xp:
        S += _x
    if S == 0:
        return 0
    Dprev: uint256 = 0
    D: uint256 = S
    Ann: uint256 = amp * N_COINS
    for _i in range(255):
        D_P: uint256 = D
        for _x in xp:
            D_P = D_P * D / (_x * N_COINS) # If
            ↪  division by 0, this will be borked:
            ↪  only withdrawal will work. And that is
            ↪  good
        Dprev = D
        D = (Ann * S + D_P * N_COINS) * D / ((Ann - 1)
        ↪  * D + (N_COINS + 1) * D_P)
        # Equality with the precision of 1
        if D > Dprev:
            if D - Dprev <= 1:
                break
        else:
            if Dprev - D <= 1:
                break
    return D
Listing 4-1

get_D function of the smart contract of StableSwap

4.3 Trading Formulae

Consider a StableSwap liquidity pool of N tokens and no fees and let B1, B2, …, BN be the balances of the tokens in the pool. Let D0 be the value of the pool parameter D. Let i, o ∈ {1, 2, …, N}. As in the previous chapters, we want to compute the amount Ao of token o that a trader will receive when depositing a certain amount Ai of token i. For each j ∈ {1, 2, …, N}, let $$ {B}_j^{hbox{'}} $$ be the balance of token j after the trade. Clearly, $$ {B}_o^{hbox{'}}={B}_o-{A}_o,{B}_i^{hbox{'}}={B}_i+{A}_i $$ and $$ {B}_j^{hbox{'}}={B}_j $$ for all j ∈ {1, 2, …, N} − {i, o}. For simplicity, let $$ y={B}_o^{hbox{'}} $$. Note that Ao = Bo − y. We will compute y. From the previous section, we know that the value of y can be obtained by solving the quadratic Equation 4.3 and is given by $$ y=mathcal{Y}left(o,{B}_1^{hbox{'}},dots, {B}_{o-1}^{hbox{'}},{B}_{o+1}^{hbox{'}},dots, {B}_N^{hbox{'}},{D}_0
ight) $$ following Definition 4.1.

In the code of the StableSwap AMM, instead of solving the quadratic equation, a different approach is taken: the value of y is found using Newton’s Method, as we can see in the get_y function shown in Listing 4-2.
def get_y(i: int128, j: int128, x: uint256, xp_:
↪  uint256[N_COINS]) -> uint256:
    # x in the input is converted to the same
    ↪  price/precision
    assert i != j        # dev: same coin
    assert j >= 0        # dev: j below zero
    assert j < N_COINS   # dev: j above N_COINS
    # should be unreachable, but good for safety
    assert i >= 0
    assert i < N_COINS
    amp: uint256 = self._A()
    D: uint256 = self.get_D(xp_, amp)
    c: uint256 = D
    S_: uint256 = 0
    Ann: uint256 = amp * N_COINS
    _x: uint256 = 0
    for _i in range(N_COINS):
        if _i == i:
            _x = x
        elif _i != j:
            _x = xp_[_i]
        else:
            continue
        S_ += _x
        c = c * D / (_x * N_COINS)
c = c * D / (Ann * N_COINS)
b: uint256 = S_ + D / Ann # - D
y_prev: uint256 = 0
y: uint256 = D
for _i in range(255):
    y_prev = y
    y = (y*y + c) / (2 * y + b - D)
    # Equality with the precision of 1
    if y > y_prev:
        if y - y_prev <= 1:
            break
    else:
        if y_prev - y <= 1:
            break
return y
Listing 4-2

get_y function of the smart contract of StableSwap

4.3.1 Taking Fee into Consideration

Unlike Uniswap and Balancer, the pool fee for the StableSwap AMM is charged on the output token. In consequence, with the previous notations, after finding the balance y of token o using Equation 4.3, the amount Ao of token o that the trader receives is
$$ {A}_o=left({B}_o-y
ight)cdot left(1-phi 
ight) $$
where ϕ is the pool fee. Note that the trader is charged a fee of ϕ(Bo − y). In addition, a fraction ɷ of this charged fee is generally reserved for the protocol as an administrative fee and not deposited into the pool. And since, if there had been no fees, the balance of token o after the trade would have been equal to y, the balance of token o is updated in the following way:
$$ {B}_o^{hbox{'}}=y+left({B}_o-y
ight)phi -omega left({B}_o-y
ight)phi =y+left({B}_o-y
ight)phi left(1-omega 
ight). $$
Observe that
$$ {B}_o^{hbox{'}}=y+left({B}_o-y
ight)phi -omega left({B}_o-y
ight)phi $$
$$ ={B}_o-left({B}_o-y
ight)+left({B}_o-y
ight)phi -omega left({B}_o-y
ight)phi $$
$$ ={B}_o-left({B}_o-y
ight)left(1-phi 
ight)-omega left({B}_o-y
ight)phi $$
$$ ={B}_o-{A}_o-omega left({B}_o-y
ight)phi . $$

This last formula is the one that appears in the code of the StableSwap AMM, as we can see in Listing 4-3.

On the other hand, the updated balance of token i is clearly
$$ {B}_i^{hbox{'}}={B}_i+{A}_i. $$
def exchange(i: int128, j: int128, dx: uint256, min_dy:
↪  uint256):
    assert not self.is_killed # dev: is killed
    rates: uint256[N_COINS] = RATES
    old_balances: uint256[N_COINS] = self.balances
    xp: uint256[N_COINS] = self._xp_mem(old_balances)
    # Handling an unexpected charge of a fee on
    ↪  transfer (USDT, PAXG)
    dx_w_fee: uint256 = dx
    input_coin: address = self.coins[i]
    if i == FEE_INDEX:
        dx_w_fee = ERC20(input_coin).balanceOf(self)
    # "safeTransferFrom" which works for ERC20s which
    ↪  return bool or not
    _response: Bytes[32] = raw_call(
        input_coin,
        concat(
            ↪  method_id("transferFrom(address,address,uint256)")
            convert(msg.sender, bytes32),
            convert(self, bytes32),
            convert(dx, bytes32),
        ),
    max_outsize=32,
) # dev: failed transfer
if len(_response) > 0:
    assert convert(_response, bool) # dev: failed
    ↪  transfer
if i == FEE_INDEX:
    dx_w_fee = ERC20(input_coin).balanceOf(self) -
    ↪  dx_w_fee
x: uint256 = xp[i] + dx_w_fee * rates[i] /
↪  PRECISION
y: uint256 = self.get_y(i, j, x, xp)
dy: uint256 = xp[j] - y - 1 # -1 just in case
↪  there were some rounding errors
dy_fee: uint256 = dy * self.fee / FEE_DENOMINATOR
# Convert all to real units
dy = (dy - dy_fee) * PRECISION / rates[j]
assert dy >= min_dy, "Exchange resulted in fewer
↪  coins than expected"
dy_admin_fee: uint256 = dy_fee * self.admin_fee /
↪  FEE_DENOMINATOR
dy_admin_fee = dy_admin_fee * PRECISION / rates[j]
# Change balances exactly in same way as we change
↪  actual ERC20 coin amounts
self.balances[i] = old_balances[i] + dx_w_fee
# When rounding errors happen, we undercharge admin
↪  fee in favor of LP
self.balances[j] = old_balances[j] - dy -
↪  dy_admin_fee
# "safeTransfer" which works for ERC20s which
↪  return bool or not
_response = raw_call(
    self.coins[j],
    concat(
        method_id("transfer(address,uint256)"),
        convert(msg.sender, bytes32),
        convert(dy, bytes32),
    ),
    max_outsize=32,
)  # dev: failed transfer
if len(_response) > 0:
    assert convert(_response, bool) # dev: failed
    ↪  transfer
log TokenExchange(msg.sender, i, dx, j, dy)
Listing 4-3

exchange function of the smart contract of StableSwap

Example 4.1. Consider a StableSwap pool having three tokens, USDC, DAI, and USDT, with the following balances:
 

Token 1

Token 2

Token 3

 

USDC

DAI

USDT

Bj

100,000

120,000

80,000

Suppose that the fee of the pool is 0.03%, that the pool administrative fee is 50%, and that the value of the amplification coefficient A is 500.

Applying the algorithm described by Equation 4.5, we obtain that the value of D is approximately 299,999. Observe that it is very close to the sum B1 + B2 + B3 of the balances of each token, which is 100,000+120,000+80,000=300,000.

Suppose that a trader wants to obtain USDC depositing 10,000 USDT. In order to compute the amount of USDC that the trader receives, we first compute the value of y from Equation 4.3, which represents the balance of USDC in the pool after the deposit of 10,000 USDT, keeping constant the value of D and assuming that there are no fees. Solving the quadratic equation, we obtain that y ≈ 89,999.71. Hence, the amount Ao of USDC that the trader receives is
$$ {displaystyle egin{array}{ll}{A}_o&amp; =left({B}_o-y
ight)cdot left(1-phi 
ight)=left(100,000-89,999.71
ight)cdot 0.9997\ {}&amp; approx 9,997.29end{array}} $$
The updated balance of USDC in the pool is given by
$$ {B}_o^{hbox{'}}={B}_o-{A}_o-omega left({B}_o-y
ight)phi $$
$$ approx 100,000-9,997.29-0.5cdot left(100,000-89,999.71
ight)cdot 0.0003 $$
$$ approx 90,001.21 $$

Observe that the balance of USDC in the pool has decreased by approximately 100,000 - 90,001.21 = 9,998.79, and since just 9,997.29 USDC are given to the trader, we obtain that the difference between those amounts1.5 USDChas been charged as an administrative fee to the liquidity providers, since an additional amount of 1.5 USDC has been removed from the pool.

Note also that if there had been no fees, the trader would have received approximately 100,000 – 89,999.71 = 10,000.29 USDC (this is 3 USDC more than the amount they received when fees were considered), and thus, the balance of USDC in the pool would have been 100,000 - 10,000.29 = 89,999.71 (which is represented by y). Therefore, the pool now has 90,001.21 - 89,999.71 = 1.5 USDC more than it would have had if there were no fees. As we can see, the trader has been charged a fee of 3 USDC, and from this amount, 1.5 USDC goes into the pool, and the other 1.5 USDC is reserved separately as an administrative fee of the protocol.

Finally, note that the balances of the tokens in the pool after the trade are as follows:
 

USDC

DAI

USDT

Bj

90,001.21

120,000

90,000

Also, after the trade, the parameter D is recalculated with the algorithm given by Equation 4.5, and we have that the new value for the parameter D is approximately 300,000.57.

4.4 All-Asset Deposit

In a similar way as with Uniswap and Balancer, liquidity providers can deposit assets into a StableSwap pool in order to earn trading fees. In exchange for their deposit, they receive StableSwap LP tokens, which represent ownership of the assets contained in the pool. During the pool trading activity, the fees from swaps accrue into the pool, resulting in added value for the StableSwap LP tokens. In this section, we will describe how the all-asset deposit works in the StableSwap AMM.

Consider a StableSwap AMM with N tokens, and suppose that a liquidity provider wants to deposit amounts x1, x2, …, xN of tokens 1, 2, …, N, respectively. Let B1, B2, …, BN be the pool balances before the deposit. Since the pool needs to remain balanced, the amounts x1, x2, …, xN must satisfy that
$$ frac{x_i}{B_i}=frac{x_j}{B_j} $$

for all i, j ∈ {1, 2, …, N}.

Let M be the amount of existent StableSwap LP tokens before the deposit and let $$ q=frac{x_1}{B_1} $$ be the share that the new liquidity provider adds to the pool. Hence, they will receive a share q of the amount of existent StableSwap LP tokens; that is, they will receive an amount qM of StableSwap LP tokens. Note also that for all j ∈ {1, 2, …, N},
$$ frac{x_j}{B_j}=frac{x_1}{B_1}=q, $$

and thus, xj = qBj for all j ∈ {1, 2, …, N}. Hence, in order to receive a share q of the existent StableSwap LP tokens, the liquidity provider has to deposit, for each j ∈ {1, 2, …, N}, an amount qBj of token j.

We will prove now that the number of newly minted StableSwap LP tokens is exactly reflected in the change of the StableSwap pool parameter D. Concretely, assume that the new liquidity provider deposits a share q of each token. Let D0 and D1 be the values of the pool parameter D before and after the deposit, respectively. Let B1, B2, …, BN be the balances of the tokens in the pool before the deposit, and let $$ {B}_1^{hbox{'}},{B}_2^{hbox{'}},dots, {B}_N^{hbox{'}} $$ be the balances of the tokens in the pool after the deposit. Note that $$ {B}_j^{hbox{'}}={B}_jleft(1+q
ight) $$ for all j ∈ {1, 2, …, N}. From Equation 4.4, we know that D0 and D1 satisfy
$$ frac{D_0^{N+1}}{N^Nprod limits_{j=1}^N{B}_j}+{D}_0left({AN}^N-1
ight)-{AN}^Nsum limits_{j=1}^N{B}_j=0 $$
and
$$ frac{D_1^{N+1}}{N^Nprod limits_{j=1}^N{B}_j^{hbox{'}}}+{D}_1left({AN}^N-1
ight)-{AN}^Nsum limits_{j=1}^N{B}_j^{hbox{'}}=0. $$
Hence,
$$ 0=frac{D_1^{N+1}}{N^Nprod limits_{j=1}^N{B}_j^{hbox{'}}}+{D}_1left({AN}^N-1
ight)-{AN}^Nsum limits_{j=1}^N{B}_j^{hbox{'}} $$
$$ =frac{D_1^{N+1}}{N^Nprod limits_{j=1}^Nleft(left(1+q
ight){B}_j
ight)}+{D}_1left({AN}^N-1
ight)-{AN}^Nsum limits_{j=1}^Nleft(left(1+q
ight){B}_j
ight) $$
$$ =left(1+q
ight)left(frac{{left(frac{D_1}{1+q}
ight)}^{N+1}}{N^Nprod limits_{j=1}^N{B}_j}+frac{D_1}{1+q}left({AN}^N-1
ight)-{AN}^Nsum limits_{j=1}^N{B}_j
ight). $$
Let g : ℝ → ℝ be defined by
$$ g(x)=frac{x^{N+1}}{N^Nprod limits_{j=1}^N{B}_j}+left({AN}^N-1
ight)x-{AN}^Nsum limits_{j=1}^N{B}_j. $$

Note that both D0 and $$ frac{D_1}{1+q} $$ are positive solutions of the equation g(x) = 0, and hence, $$ frac{D_1}{1+q}={D}_0 $$ since the map g has exactly one positive root (see subsection 4.2.1). Thus, D1 = (1 + q)D0.

We summarize the previous result in the following table:
 

Before deposit

After deposit

Balance of token j

Bj

(1 + q)Bj

Value of parameter D

D0

(1 + q)D0

Example 4.2. Consider a StableSwap pool with tokens USDT, USDC, and DAI. Suppose that the balances of each of these tokens are given by the following table:
 

USDT

USDC

DAI

Bj

100,000

101,000

95,000

Suppose, in addition, that the current number of StableSwap LP tokens in circulation is 10,000.

If a liquidity provider wants to perform an all-asset deposit of 1% of the pool size, they will need to deposit the following amounts of each of the tokens of the pool:
$$ {displaystyle egin{array}{ll}	extrm{USDT}:&amp; 100,000cdot 0.01=1000\ {}	extrm{USDC}:&amp; 100,100cdot 0.01=1010\ {} DAI:&amp; 95,000cdot 0.01=950.end{array}} $$

In addition, they will receive an amount of 10,000.0.01 = 100 StableSwap LP tokens.

4.5 All-Asset Withdrawal

We will now analyze the case in which a liquidity provider redeems StableSwap LP tokens. Suppose that the liquidity provider owns an amount m of StableSwap LP tokens, and let M be the amount of StableSwap LP tokens in circulation. Let $$ sigma =frac{m}{M} $$; that is, σ is the share of the pool that the liquidity provider owns. Let B1, B2, …, BN be the balances of the tokens in the pool. Clearly, in return for the amount m of StableSwap LP tokens, the liquidity provider will receive, for each j ∈ {1, 2, …, N}, an amount σBj of token j.

Note also that for all j ∈ {1, 2, …, N}, the balance of token j in the pool after the all-asset withdrawal is Bj − σBj = (1 − σ)Bj. As we did in the case of the all-asset deposit, we will prove that the parameter D changes in the same proportion as the balances of the tokens of the pool. That is, we will prove that if the value of the parameter D is D0 before the all-asset withdrawal, then its value after the withdrawal is (1 − σ)D0.

We proceed in a similar way as we did in the previous section. Let D1 be the value of the parameter D after the withdrawal. From Equation 4.4, we know that D0 and D1 satisfy
$$ frac{D_0^{N+1}}{N^Nprod limits_{j=1}^N{B}_j}+{D}_0left({AN}^N-1
ight)-{AN}^Nsum limits_{j=1}^N{B}_j=0 $$
and
$$ frac{D_1^{N+1}}{N^Nprod limits_{j=1}^Nleft(left(1-sigma 
ight){B}_j
ight)}+{D}_1left({AN}^N-1
ight)-{AN}^Nsum limits_{j=1}^Nleft(left(1-sigma 
ight){B}_j
ight)=0 $$
Hence,
$$ 0=frac{D_1^{N+1}}{N^Nprod limits_{j=1}^Nleft(left(1-sigma 
ight){B}_j
ight)}+{D}_1left({AN}^N-1
ight)-{AN}^Nsum limits_{j=1}^Nleft(left(1-sigma 
ight){B}_j
ight) $$
$$ =left(1-sigma 
ight)left(frac{{left(frac{D_1}{1-sigma}
ight)}^{N+1}}{N^Nprod limits_{j=1}^N{B}_j}+frac{D_1}{1-sigma}left({AN}^N-1
ight)-{AN}^Nsum limits_{j=1}^N{B}_j
ight) $$
Let g : ℝ → ℝ be defined by
$$ g(x)=frac{x^{N+1}}{N^Nprod limits_{j=1}^N{B}_j}+left({AN}^N-1
ight)x-{AN}^Nsum limits_{j=1}^N{B}_j. $$

Note that both D0 and $$ frac{D_1}{1-sigma } $$ are positive solutions of the equation g(x) = 0, and hence, $$ frac{D_1}{1-sigma }={D}_0 $$ since the function g has exactly one positive root (see subsection 4.2.1). Thus, D1 = (1 − σ)D0.

We summarize the previous observations in the following table:
 

Before withdrawal

After withdrawal

Balance of token j

Bj

(1 − σ)Bj

Value of parameter D

D0

(1 − σ)D0

Observe that the parameter D plays the same role as the parameter V in Balancer (and as the parameter L in Uniswap v2), meaning that for liquidity deposits (or withdrawals), the amount of tokens minted (or burned) is reflected in the change of the parameter D.

Example 4.3. Consider a StableSwap pool with tokens USDT, USDC, and DAI, and suppose that the corresponding balances are given by the following table:
 

USDT

USDC

DAI

Bj

50,000

50,100

49,500

Assume that the number of StableSwap LP tokens in circulation is 100,000 and that a liquidity provider owns 100 StableSwap LP tokens. Hence, the corresponding share is $$ sigma =frac{100}{100,000}=0.001 $$; that is, the liquidity provider owns 0.1% of the pool. If they decide to redeem their StableSwap LP tokens by performing an all-asset withdrawal, they will obtain the following amounts of each token:
$$ {displaystyle egin{array}{ll}	extrm{USDT}:&amp; 50,000cdot 0.001=50,\ {}	extrm{USDC}:&amp; 50,100cdot 0.001=50.1,\ {} DAI:&amp; 49,500cdot 0.001=49.5end{array}} $$

4.6 Single-Asset Withdrawal

We will now tackle the single-asset withdrawal feature of Curve Finance. Suppose that we have a StableSwap pool that has N tokens with balances B1, B2, …, BN and that the pool has a nonzero fee ϕ. Assume that a liquidity provider owns a share σ of all the StableSwap LP tokens and that instead of redeeming a proportion σ of each of the tokens of the pool, they want to obtain all their share in terms of just one token of the pool, say, token o. Let D0 be the value of the parameter D before the withdrawal. If we were to perform an all-asset withdrawal, the value of the parameter D after the withdrawal would be (1 − σ)D0, as we saw in the previous section. Thus, it makes sense to use this value of D to calculate the amount of token o that will be left after the single-asset withdrawal since the parameter D also reflects the liquidity of the pool in a certain sense.

Let y be the amount of token o after the withdrawal taking into account only the previous considerations. Since for each j∈ {1, 2, …, N} − {o} the amount of token j in the pool does not change after the single-asset withdrawal, applying the invariant Equation 4.2, we obtain that
$$ frac{{left(left(1-sigma 
ight){D}_0
ight)}^{N+1}}{N^Nyprod limits_{egin{array}{c}j=1\ {}j
e oend{array}}^N{B}_j}+left(1-sigma 
ight){D}_0left({AN}^N-1
ight)-{AN}^Nleft(y+sum limits_{egin{array}{c}j=1\ {}j
e oend{array}}^N{B}_j
ight)=0 $$
Proceeding as in Section 4.2, we obtain that
$$ {y}^2+left(sum limits_{egin{array}{c}j=1\ {}j
e oend{array}}^N{B}_j+frac{left(1-sigma 
ight){D}_0}{AN^N}-left(1-sigma 
ight){D}_0
ight)y-frac{{left(left(1-sigma 
ight){D}_0
ight)}^{N+1}}{AN^{2N}prod limits_{egin{array}{c}j=1\ {}j
e oend{array}}^N{B}_j}=0. $$
Hence, we can obtain the value of y by solving the previous quadratic equation. Applying Definition 4.1, observe that
$$ y=mathcal{Y}left(o,{B}_1,dots, {B}_{o-1},{B}_{o+1},dots, {B}_N,left(1-sigma 
ight){D}_0
ight) $$
We mention that it is not important to write the explicit value of y in terms of the other variables. We just want to emphasize that the value of y can be computed if the values of B1, B2, …, BN and the parameter D are known. We summarize both pool states in the following table:
 

Before withdrawal

After withdrawal

Balance of token o

Bo

y

Balance of token j, with j ≠ o

Bj

Bj

Value of parameter D

D0

(1 − σ)D0

Therefore, if there were no fees, the liquidity provider would have to receive an amount Bo − y of token o (note that y < Bo by Proposition 4.1). However, since the liquidity provider receives only one token, the proportions of the tokens in the pool are modified, and thus, several spot prices also change, as occurred with the Balancer AMM. Thus, a fee has to be charged, in a similar way as when a trade is performed.

In order to charge a fair fee, we will compare the amounts of tokens in the pool after an all-asset withdrawal (AAW) and after the situation of a single-asset withdrawal (SAW) considered before.
 

After AAW

After SAW

Balance of token o

(1 − σ)Bo

y

Balance of token j, with j ≠ o

(1 − σ)Bj

Bj

Value of parameter D

(1 − σ)D0

(1 − σ)D0

It will be reasonable to charge the fee on the difference between the balances of the tokens in both situations, since a direct single-asset withdrawal should be equivalent to an all-asset withdrawal followed by the corresponding trades, that is, trading, for each j∈ {1, 2, …, N} − {o}, the amount σBj of token j received in the all-asset withdrawal so as to obtain token o. Hence, for each j∈ {1, 2, …, N} − {o}, the fee on token j should be ϕσBj (note that here we are considering charging fees on the way in). Therefore, if we consider the situation of an all-asset withdrawal followed by the corresponding trades, for each j ∈ {1, 2, …, N} − {o}, the balance of token j in the pool after the trades will be (1 − σ)Bj + σBj = Bj as expected. However, in order to obtain the amount of token o that the liquidity provider should receive, we have to compute the balance of token o with respect to the balances of all the other tokens considering that the liquidity provider deposits an amount σBj − ϕσBj of token j, due to the fees that are charged. In addition, the value of the parameter D to be considered is (1 − σ)D0, that is, the same value as after the all-asset withdrawal, since in a trade, the value of D is preserved in order to compute the amount of tokens that should be given to the trader (as if there were no fees) and then when the fees are charged, the value of parameter D is updated. Observe that a similar situation occurs in Uniswap v2 and in Balancer’s AMMs.

This said, we have to consider the following state of the pool:
 

State

Balance of token j, with j ≠ o

Bj − ϕσBj

Balance of token o

yʹ

Value of parameter D

(1 − σ)D0

where the amount yʹ has to be computed from the values of the balances of tokens j, with j ≠ o and the value of the parameter D by means of the quadratic Equation 4.3. Equivalently,
$$ {y}^{hbox{'}}=mathcal{Y}left(o,{B}_1^{hbox{'}},dots, {B}_{o-1}^{hbox{'}},{B}_{o+1}^{hbox{'}},dots, {B}_N^{hbox{'}},left(1-sigma 
ight){D}_0
ight) $$

where for each $$ jin left{1,2,dots, N
ight}-left{o
ight},{B}_j^{hbox{'}}={B}_j-phi sigma {B}_j $$.

In consequence, the amount of token o that has to be given to the liquidity provider should be Bo − yʹ. However, the StableSwap AMM also charges a fee on token o corresponding to the difference of balances of token o shown in the second table of this section. This fee is ϕ((1 − σ)Bo − y) (note that y < (1 − σ)Bo by Proposition 4.1). In conclusion, the amount of token o that is given to the liquidity provider is
$$ {B}_o-{y}^{hbox{'}}-phi left(left(1-sigma 
ight){B}_o-y
ight). $$
(4.6)
This formula can be seen in the code of the StableSwap AMM that is shown in Listing 4-4, with the following translation between the variables of the code and our notation:
$$ {displaystyle egin{array}{ll}kern1.08em 	exttt{i}&amp; =o,\ {}kern0.84em 	exttt{D}	exttt{0}&amp; ={D}_0,\ {}kern0.84em 	exttt{D}	exttt{1}&amp; =left(1-sigma 
ight){D}_0,\ {}	exttt{new}\_	exttt{y}&amp; =y,\ {};	exttt{xp}left[	exttt{j}
ight]&amp; ={B}_j.end{array}} $$
Note also that y= self.get_y_D(amp, i, xp_reduced, D1).
def _calc_withdraw_one_coin(_token_amount: uint256, i:
↪  int128) -> (uint256, uint256):
    # First, need to calculate
    # * Get current D
    # * Solve Eqn against y_i for D - _token_amount
    amp: uint256 = self._A()
    _fee: uint256 = self.fee * N_COINS / (4 * (N_COINS
    ↪  - 1))
    precisions: uint256[N_COINS] = PRECISION_MUL
    total_supply: uint256 = self.token.totalSupply()
    xp: uint256[N_COINS] = self._xp()
    D0: uint256 = self.get_D(xp, amp)
    D1: uint256 = D0 - _token_amount * D0 /
    ↪  total_supply
    xp_reduced: uint256[N_COINS] = xp
    new_y: uint256 = self.get_y_D(amp, i, xp, D1)
    dy_0: uint256 = (xp[i] - new_y) / precisions[i] #
    ↪  w/o fees
    for j in range(N_COINS):
        dx_expected: uint256 = 0
        if j == i:
            dx_expected = xp[j] * D1 / D0 - new_y
        else:
            dx_expected = xp[j] - xp[j] * D1 / D0
        xp_reduced[j] -= _fee * dx_expected /
        ↪  FEE_DENOMINATOR
    dy: uint256 = xp_reduced[i] - self.get_y_D(amp, i,
    ↪  xp_reduced, D1)
    dy = (dy - 1) / precisions[i] # Withdraw less to
    ↪  account for rounding errors
    return dy, dy_0 - dy
Listing 4-4

Function used for the single-asset withdrawal in the smart contract of StableSwap

In the following example, we will compare the following two possibilities for a liquidity provider who wants to withdraw their position and obtain only one token.
  • Performing a single-asset withdrawal

  • Performing an all-asset withdrawal followed by some trades to obtain the token they want

Example 4.4. Consider a StableSwap pool having three tokens, USDT, USDC, and DAI, with the following balances:
 

Token 1

Token 2

Token 3

 

USDC

DAI

USDT

Bj

1,800,000

1,400,000

800,000

Suppose that the fee of the pool is 0.03%, the administrative pool fee is 50%, and the value of the amplification coefficient A is 500.

We now apply the algorithm given by Equation 4.5 to compute the value of the parameter D at the state of the pool given by the previous table, obtaining D ≈ 3,999,947.93. This value will be called D0.

Assume that a liquidity provider holds a share of 1% of the pool and that they want to withdraw their position solely in USDC. We will consider the two possibilities described previously.
  • Suppose first that the liquidity provider decides to perform a single-asset withdrawal. By Equation 4.6, the amount of USDC that the liquidity provider receives is Bo − yʹ − ϕ((1 − σ)Bo − y), where Bo is the balance of USDC in the pool, σ = 0.01, and where y and yʹ are the amounts of USDC that correspond to the following states of the pool:

 

State A

State B

Balance of USDC

y

yʹ

Balance of DAI

1,400,000

(1 − 0.0003 0.01)⋅1,400,000 = 1,399,995.8

Balance of USDT

800,000

(1 − 0.0003 ⋅ 0.01) ⋅ 800,000 =799,997.6

Value of parameter

0.99D0

0.99D0

or in other words,
$$ y=mathcal{Y}left(1,1400000,800000,0.99{D}_0
ight) $$
and
$$ {y}^{hbox{'}}=mathcal{Y}left(1,1399995.8,799997.6,0.99{D}_0
ight). $$
Solving the quadratic Equation 4.3, we obtain that y ≈ 1,759,997.36 and yʹ ≈ 1,760,003.96. Thus,
$$ {B}_o-{y}^{hbox{'}}-phi left(left(1-sigma 
ight){B}_o-y
ight) $$
$$ approx 1,800,000-1,760,003.96-0.0003left(0.99cdot 1,800,000-1,759,997.36
ight) $$
$$ approx 39,989.44 $$
Hence, the liquidity provider will receive approximately 39,989.44 USDC if they decide to perform a single-asset withdrawal in USDC of their position.
  • Suppose now that the liquidity provider decides to perform an all-asset withdrawal and then trade the received amounts of DAI and USDT to obtain more USDC. Clearly, they receive 18,000 USDC, 14,000 DAI, and 8,000 USDT from the all-asset withdrawal, and after it, the updated balances of the pool are as follows:

USDC

DAI

USDT

1,782,000

1,386,000

792,000

and the new value of the parameter D is 0.99D0 ≈ 3,959,948.45.

Now, the liquidity provider trades 14,000 DAI for USDC. The amount $$ {A}_o^{(1)} $$ of USDC that they receive is
$$ {A}_o^{(1)}=left({B}_o-y
ight)cdot left(1-phi 
ight), $$
where the value of y is obtained from Equation 4.3, or more precisely,
$$ y=mathcal{Y}left(1,1400000,792000,0.99{D}_0
ight)approx 1,767,999.25. $$
Hence,
$$ {A}_o^{(1)}=left({B}_o-y
ight)cdot left(1-phi 
ight)approx left(1,782,000-1,767,999.25
ight)cdot 0.9997 $$
$$ approx 13,996.54, $$
that is, the liquidity provider receives 13,996.54 USDC more. On the other hand, the updated balance of USDC in the pool is given by
$$ {B}_o^{hbox{'}}={B}_o-{A}_o^{(1)}-omega left({B}_o-y
ight)phi $$
$$ approx 1,782,000-13,996.54-0.5cdot left(1,782,000-1,767,999.25
ight)cdot 0.0003 $$
$$ approx 1,768,001.36, $$

and the updated balances of DAI and USDT are 1,400,000 and 792,000, respectively. Also, the new value of the parameter D, which is obtained by applying the algorithm given by Equation 4.5, is D1 ≈ 3,959,950.55.

Finally, the liquidity provider trades 8,000 USDT for USDC. The amount $$ {A}_o^{(2)} $$ of USDC that they receive is
$$ {A}_o^{(2)}=left({B}_o^{hbox{'}}-y
ight)cdot left(1-phi 
ight), $$
where $$ y=mathcal{Y}left(1,1400000,800000,{D}_1
ight)approx 1,759,999.46 $$. Thus,
$$ {A}_o^{(2)}=left({B}_o^{hbox{'}}-y
ight)cdot left(1-phi 
ight) $$
$$ approx left(1,768,001.36-1,759,999.46
ight)cdot 0.9997 $$
$$ approx 7,999.49 $$

that is, the liquidity provider receives 7,999.49 USDC more.

Therefore, the liquidity provider has received a total amount of approximately
$$ 18,000+13,996.54+7,999.49=39,996.03;	extrm{USDC} $$

which is a bit more than the 39,989.44 USDC they obtain performing a single-asset withdrawal.

4.7 StableSwap LP Token Swap

As we did in the previous chapters, consider two different (Uniswap, Balancer, or StableSwap) pools, P1 and P2, respectively. Let LP1 and LP2 be the LP tokens of pools P1 and P2, respectively. We want to compute the fair swap price between LP1 and LP2. To this end, we will calculate the total value of each of the pools in terms of a chosen token Z, and then we will compute the value of tokens LP1 and LP2 in terms of token Z so as to find the fair swap price.

Firstly, we need to make the following observation regarding StableSwap liquidity pools. Let P be a StableSwap liquidity pool that has N tokens Y1, Y2, …, YN with balances B1, B2, …, BN, respectively. Let Z be any token, and for each j ∈ {1, 2, …, N}, let Pj be the price of token Yj in terms of token Z.6 Then, the value of the whole pool P in terms of token Z is
$$ sum limits_{j=1}^N{B}_j{P}_j. $$
Hence, if the circulating supply of StableSwap LP tokens of pool P is M, the value of each token is
$$ frac{1}{M}sum limits_{j=1}^N{B}_j{P}_j. $$
Now, for j ∈ {1, 2}, let Vj(Z) be the value of each LP token of pool Pj in terms of token Z, which can be computed as in the previous paragraph for StableSwap pools and as in the previous chapters for Uniswap v2 or Balancer pools. Let P be the price of one LP1 token in terms of LP2 tokens. Hence, V1(Z) = P ⋅ V2(Z), and thus,
$$ P=frac{V_1(Z)}{V_2(Z)}. $$

We will now give two examples where we will compute the fair swap price between StableSwap LP tokens and LP tokens of different pools.

Example 4.5. Suppose that Alice is a liquidity provider of a Balancer pool and Bob is a liquidity provider of a StableSwap pool. Assume that Alice’s pool and Bob’s pool are described by the data given in the following table:
 

Alice

Bob

Pool

Balancer

StableSwap

Supply of pool tokens

10,000

20,000

Token X

ETH

USDC

Token Y

BTC

USDT

Balance of token X

1,300

1,000,000

Balance of token Y

25

1,000,500

Weight of token X

0.8

Weight of token Y

0.2

We want to compute the fair swap price between Alice’s and Bob’s LP tokens. To this end, we will use USDC as the token Z of the previous argument. Suppose that the market prices of ETH and USDT in terms of USDC are 4,000 USDC/ETH and 0.9995 USDC/USDT. Then, the value of each Balancer BPT token is
$$ frac{1}{10,000}cdot frac{1,300}{0.8}cdot 4,000=650	extrm{USDC} $$
and the value of each StableSwap LP token is
$$ frac{1}{20,000}cdot left(1,000,000+1,000,500cdot 0.9995
ight)approx 100	extrm{USDC}. $$

Thus, the price of each of Alice’s tokens in terms of Bob’s tokens is $$ frac{650}{100}=6.5 $$, which means that if Alice gives Bob 1 of her tokens from the Balancer pool, she will get 6.5 of Bob’s tokens from the StableSwap pool in return.

Example 4.6. Suppose that Alice is a liquidity provider of a Uniswap v2 pool and Bob is a liquidity provider of a StableSwap pool. Assume that Alice’s pool and Bob’s pool are described by the data given in the following table:
 

Alice

Bob

Pool

Uniswap v2

StableSwap

Supply of pool tokens

20,000

10,000

Token X

ETH

USDC

Token Y

BTC

USDT

Balance of token X

1,300

1,000,000

Balance of token Y

100

1,000,500

Again, we want to compute the fair swap price between Alice’s and Bob’s tokens. As in the previous example, we will use USDC as token Z, and we will assume that the market prices of ETH and USDT in terms of USDC are 4,000 USDC/ETH and 0.9995 USDC/USDT. Then, the value of each Uniswap v2 LP token is
$$ frac{1}{20,000}cdot left(2cdot 1,300
ight)cdot 4,000=520	extrm{USDC} $$
and the value of each StableSwap LP token is
$$ frac{1}{10,000}cdot left(1,000,000+1,000,500cdot 0.9995
ight)approx 200	extrm{USDC}. $$

Thus, the price of each of Alice’s tokens in terms of Bob’s tokens is $$ frac{520}{200}=2.6 $$; that is, if Alice gives Bob 1 of her Uniswap LP tokens, she will get 2.6 of Bob’s StableSwap LP tokens in return.

4.8 Summary

In this chapter, we explained how the invariant formula for the StableSwap AMM is obtained and how trades are performed. We also showed how liquidity can be deposited and withdrawn and gave a detailed description of the single-asset withdrawal feature.

In the next chapter, we will study the Uniswap v3 AMM, which introduces concentrated liquidity. As we shall see, the design of the Uniswap v3 AMM is very different from those of the AMMs that we have described before.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.182.97