Uniswap v3 Maths Explained: Capital Efficiency

田少谷 Shao
10 min readJun 12, 2021

--

2/(1-(a/b)^(1/2)).

Image source: https://uniswap.org/blog/uniswap-v3/

Outline

0. Intro 
- TL;DR
1. Deduction flow
2. Liquidity unit
3. Quick comparison
4. Deduction
5. getAmount{0,1}Delta()
6. Conclusion

0. Intro

While I was writing the previous article on Uniswap v3, I mentioned the capital efficiency calculator and knew there’s Hayden’s tweet on the equation, but did not pay too much attention to it.

The benefit of the capital efficiency of v3 only exists as LPs of v2 can only provide liquidity throughout the price range (0,∞), while LPs of v3 can customise the range and thus end up with the same amount of liquidity while using far less capital!

On a boring night, the equation suddenly popped up and I thought it’s about time to figure out where this 1/(1-(a/b)^(1/4)) comes from.

TL;DR

In the end, I failed to deduct the equation but found another equally precise one to express the capital efficiency of v3:

2/(1-(a/b)^(1/2)).

The difference between the two equations is merely 1/(1+(a/b)^(1/4)), whose maximum value is about 1, which is pretty minor.

Before diving in, listening to music while studying maths is a must!

1. Deduction flow

Let’s first define our goal: compare v3’s capital efficiency to v2 (or v1).

The next question is: how?

For those familiar with v3, it is quite obvious: figure out how many x of X token and y of Y token on v2 can achieve the same amount of liquidity as providing x’ amount of X token (or y', or both x’ + y’) on v3, in a price range (a, b), for a < b.*

To find out the answer, these are the steps to go through:

  1. What is the common unit that describes the amount of liquidity provided to a Uniswap pool of all versions (v1–v3)?
  2. What’s the equivalence of x + y on v2 to x' (or y', or x’ + y’) on v3, in a range of (a,b)?
  3. The capital efficiency of v3 compared to v2 is: (x + y) / x’ times more efficient.

* Providing liquidity on v2 (or v1) always requires both tokens while on v3 it depends; can be one or both.

2. Liquidity unit

While many interesting features are introduced in v3, v3 is essentially still the AMM of previous versions, with the equation x * y = k describing the inventories of the two tokens in a pool.

The only difference is that the whole price range (0,∞) is sliced into numerous ticks and the equation x * y = k has to be updated every time the price crosses the boundary of ticks.

Thus, we can expect the measurement of liquidity of all three versions (v1-v3) to be the same.

According to the v3 whitepaper, liquidity is defined as L , for L = √x * √y = √k.

Also, in their Github, there is a function getAmount0Delta() in SqrtPriceMath.sol, whose equation is in the comment as liquidity / sqrt(lower) - liquidity / sqrt(upper).

This function is to find out that given an amount of liquidity, to make the price go up from lower to upper, what is the amount of token0 (token X in our case) going to be swapped into token1 (Y) completely.

Thus, when liquidity = 1, the equation is 1 / sqrt(lower) - 1 / sqrt(upper).

When liquidity = 1, hmm… now the question becomes: whether the liquidity in the getAmount0Delta() is the same as the definition of liquidity L = √x * √y.

The implication of the two liquidity being the same is that a minimal amount of liquidity exists when √x * √y = L = 1.

But how can we verify this assumption? Run some numbers!

When L = √x * √y = 1, we take an arbitrary range (0,2) in the tick index form, which is equal to the price range (1.0001⁰, 1.0001²) = (1, 1.00020001).

Sheet 1; assume minimal liquidity exists when √x * √y = √k = L = 1

If the price of X is to go from 1 to 1.00020001, 0.000099990001 xin the pool has to be swapped into 0.0001 y.

Does the amount 0.000099990001 of X along with the square roots of lower and upper prices (1, 1.0001) satisfy the equation of getAmount0Delta()?

1 / sqrt(lower) - 1 / sqrt(upper)
= 1 / sqrt(1) - 1 / sqrt(1.00020001)
= 1 / 1 - 1 / 1.0001
= 0.000099990001

YES.

By the way, if you’re not sure why liquidity provision, in the above case, requires only token X, check out this article for the basic mechanisms of Uniswap v3.

We have now come to our first conclusion: when √x * √y = L = 1, there’s the minimal unit of liquidity.

Some might be confused: why do we care about whether it’s minimal liquidity or not?

As the simplification of the function getAmount0Delta() demonstrates, if we can simplify liquidity / sqrt(lower) - liquidity / sqrt(upper) to 1 / sqrt(lower) - 1 / sqrt(upper), life will be easier as there is one less variable in the deduction process to be considered.

Therefore, following the example in Sheet 1, the question becomes:

  • What is the equivalent amount of X and Y tokens x + y on v2 to 0.000099990001 of X token on v3 in the price range (1, 1.00020001) when there is minimal liquidity in both v2 and v3 pools?

3. Quick comparison

If you have sharp eyes, the answer already lies in Sheet 1!

Sheet 1; add on the line numbers to make things clear.

Isn’t line 3 exactly how we provide liquidity on v2?

A liquidity provider puts 1 X token and 1 Y token into the pool, making the initial price of X equal to 1. Thus, when the price goes from 1 to 1.00020001, 0.000099990001 X, the equivalence of one unit of liquidity in the price range (1, 1.00020001), is swapped into 0.0001 Y.

With these numbers, we can make a quick comparison, assuming values below are calculated in the price of Y (ex: 1$ if Y is USDC):

  • v2: 1 X + 1 Y = 1 * price of X + 1= 1 * 1 + 1 = 2
  • v3: 0.000099990001 X * price of X = 0.000099990001 * 1 = 0.000099990001

The above is to compare that to achieve the same 1 liquidity in the tick range (0,2) as on v3, how many X and Y tokens are required on v2.

Turns out that the capital efficiency of providing liquidity in the tick range (0, 2) on v3 is 2 / 0.000099990001 = 20,002 times more efficient than providing liquidity in the price range (0,∞) on v2.

Let’s verify the equations real quick:

  • 2/(1-(a/b)^(1/2)): 2 / (1 — (1 / 1.00020001) ^ (1 / 2)) = 20,002
  • 1/(1-(a/b)^(1/4)): 1 / (1 — (1 / 1.00020001) ^ (1 / 4)) = 20,001.4999875
  • 20,002 — 20,001.4999875 = 0.5000125
  • 1/(1+(a/b)^(1/4)): 1 / (1 + (1 / 1.00020001) ^ (1 / 4)) = 0.50001249937 ~= 0.5000125

Looks like the first equation is slightly more precise than the second one!

Let’s also go through an example when liquidity is not equal to 1.

Sheet 2

Assume we provide 100 liquidity, which means k = L ^ 2 is 100² = 10000, and x and y are both multiplied by 100 compared to Sheet 1.

  • v2: 100 X + 100 Y = 100 * price of X + 100= 100 * 1 + 100 = 200
  • v3: 0.0099990001 X * price of X = 0.0099990001 * 1 = 0.0099990001

The capital efficiency is 200 / 0.0099990001, which is identical to the above one 2 / 0.000099990001 = 20,002 if we divide both the numerator and denominator by 100.

Since liquidity L is not even part of the equations, results of them remain the same as the lower and upper bounds haven’t changed.

4. Deduction

Now that we know how numbers actually work out, it’s time to turn them into mathematical symbols and express the capital efficiency comparison in equations.

Sheet 3

Sheet 3 includes everything we need for the deduction!

This is how comparisons in 3. Quick comparison look like in mathematical symbols:

  • v2: x X + y Y = x * price of X + y = x * y / x + y = 2 * y
  • v3: (1 / √a — 1 / √b) X = (1 / √a — 1 / √b) * y / x

Thus, the capital efficiency equation is 2 * y / ((1 / √a — 1 / √b) * y / x), and can be simplified as 2 * x / (1 / √a — 1 / √b).

We’re not done yet. We should try to replace x with a or b to make the equation only related to the price range, such that it’s more convenient to estimate the capital efficiency.

This requires some trick ;)

If we somehow manage to substitute the x in the numerator for x * y or √x * √y, then we can get rid of x and y, since now x * y = 1by multiplying both the numerator and denominator by √a!

2 * x * √a / ((1 / √a — 1 / √b) * √a)
= 2 * x * (√y / √x) / (1 - √a / √b)
= 2 * √x * √y / (1 - √a / √b)
= 2 * 1 / (1 - √a / √b)
= 2 / (1 - (a / b) ^ (1 / 2))

Voila! This is how we get the equation 2/(1-(a/b)^(1/2))!

The above deduction showcases the scenario when providing liquidity in only token X. Does it also apply to when providing liquidity in both tokens X and Y, or in token Y only?

Firstly, liquidity provision with both tokens on v3 happens only when the current price is between the lower and upper prices of the user’s choice:

  • lower ≤ current ≤ upper

However, this is no different from splitting the above one liquidity provision into two, with each of the lower and upper prices as:

  1. (lower, current): lower price = lower, upper price = current
  2. (current, upper): lower price = current, upper price = upper

Thus, if we can prove that the equation 2/(1-(a/b)^(1/2)) also fits the scenario of liquidity provision in token Y only, then we’re all done!

As we use the equation of getAmount0Delta() in the previous example, we’ll now need the equation of getAmount1Delta(): liquidity * (sqrt(upper) — sqrt(lower)), which is sqrt(upper) — sqrt(lower) when liquidity = 1.

Sheet 4

Note that the lower price is still a and upper price b, while their locations in Sheet 4 and Sheet 3 are the opposite.

Comparison:

  • v2: x X + y Y = x * y / x + y = 2 * y = 2 * x * b
  • v3: √b — √a Y = √b — √a

Thus, the equation is 2 * x * b / (√b — √a).

This time, similarly, we divide both the numerator and denominator by √b!

(2 * x * b / √b) / ((√b — √a) / √b)
= 2 * x * √b / (1 - √a / √b)
= 2 * x * (√y / √x) / (1 - √a / √b)
= 2 * √x * √y / (1 - √a / √b)
= 2 * 1 / (1 - √a / √b)
= 2 / (1 - (a / b) ^ (1 / 2))

Still the same equation, great!

5. getAmount{0,1}Delta()

We’ve come a long way, so why not go a bit further by also figuring out where the equations of getAmount0Delta() and getAmount1Delta() come from?

getAmount0Delta()

Sheet 5

Sheet 5 is only different from Sheet 3 as getAmount0Delta() is now an unknown variable d.

I’ve put down all the obvious equivalent relationships between variables x, y, a, b, and we’re still missing a few ones to complete the puzzle!

  • x * y = 1 -> y = 1 / x -> a = y / x = 1 / (x ^ 2) -> √a = 1 / x -> x = 1 / √a
  • b = 1 / ((x — d) ^ 2) -> √b = 1 / (x — d) -> √b * (x — d) = 1

Note that we don’t consider the negative solution to the dimension descending as prices a and b should be positive.

Now, substitute x for 1 / √a in the equation that contains d:

  • √b * (1 / √a — d) = 1 -> 1 / √a — d = 1 / √b -> d = 1 / √a — 1 / √b

getAmount1Delta()

Sheet 6

Similar to Sheet 4, the locations of a and b in Sheet 6 are also the opposite of Sheet 3 & 5.

  • x * y = 1 -> x = 1 / y-> b = y / x = y ^ 2 -> √b = y
  • a = (y — d) ^ 2 -> √a = y — d-> √a = √b — d->d = √b - √a

Lastly, let’s go through a final check when liquidity L != 1, using the case of Sheet 6.

Sheet 7
  • x * y = L ^ 2-> x = L ^ 2 / y-> b = y / x = y ^ 2 / L ^ 2-> √b = y / L -> √b * L = y
  • a = (y — d) ^ 2 / (L ^ 2)-> √a = (y — d) / L-> √a * L = √b * L — d->d = L * (√b - √a) = liquidity * (sqrt(upper) — sqrt(lower))

6. Conclusion

Though I personally enjoy the whole process of deducting the equation, this article ends up being too detailed than it should, and probably too lengthy for maths experts :(

Anyway, hope this will come in handy for those unfamiliar with the maths of Uniswap v3!

As usual, leave a comment down below if you have any doubt or find an error. Stay tuned, as this won’t be the last one of the Uniswap v3 series!

--

--