---
# System prepended metadata

title: Calculating Liquidity Amounts

---

# Calculating Liquidity Amounts

In Uniswap v4, when we call `modifyLiquidity` to add or remove liquidity from a pool, we need to provide a `ModifyLiquidityParams` struct. One of those struct values is `liquidityDelta`, which is used to determine how much of Token 0 and Token 1 do we need to add.

```solidity
struct ModifyLiquidityParams {
    // the lower and upper tick of the position
    int24 tickLower;
    int24 tickUpper;
    // how to modify the liquidity
    int256 liquidityDelta;
    // a value to set if you want unique liquidity positions at the same range
    bytes32 salt;
}
```

This post is a brief explainer of `liquidityDelta`, how it works, and how to calculate it.

## Black Box

We will not get into mathematical proofs, and we will trust the equations mentioned in the Uniswap whitepapers and the likes. 

From the whitepaper and the liquidity math paper, we get a few equations that are relevant to us.

First, let's define some terminology:

`L` = Liquidity
`currentTick` = Current tick of the pool 
`Sqrt P` = Square root of the current price at tick = `currentTick`

`tickLower` = Lower boundary of the tick range we want to add liquidity to
`Sqrt P_a` = Square root of the price at the lower tick boundary

`tickUpper` = Upper boundary of the tick range we want to add liquidity to
`Sqrt P_b` = Square root of the price at the upper tick boundary

`x` = Amount of token 0 to add
`y` = Amount of token 1 to add

---

The equations give us relationships between `x`, `y`, and `L`. Given one of these three values, we can compute the other two. The relevant equations are these:

### `x` and `L`

![image](https://hackmd.io/_uploads/HyOhZvHYA.png)
which also implies,
![image](https://hackmd.io/_uploads/BJyJzDHF0.png)

### `y` and `L`
![image](https://hackmd.io/_uploads/ByKxGvrY0.png)
![image](https://hackmd.io/_uploads/B1mZMwrKA.png)

> NOTE: `Sqrt P_a` and `Sqrt P_b` are not always equivalent to the square root of the price at `tickLower` and `tickUpper`. Sometimes we treat `Sqrt P` (price at current tick) as the lower or upper tick boundary when adding liquidity. Details about when that happens and why are mentioned later on as we look at different cases.

---

So, as long as we know one of these values, we can calculate the other two.

The `liquidityDelta` we need to provide as part of `ModifyLiquidityParams` is the `L` value we can calculate with these equations.

So, for example, if we have an ETH/USDC pool where 1 ETH = 3500 USDC currently, that gives us a `currentTick` and `Sqrt P`. 

Let's say you have 1 ETH you want to add as liquidity to the pool, i.e. you know the value of `x`. Using this, you can calculate `L` and `y` to have the value needed to be passed to the function call, and also how much of USDC you need to add (`y`)

Similarly, if you have 1000 USDC and want to figure out how much ETH you need to add liquidity, the same idea works and you can calculate `x` and `L` given `y`.

## Types of Liquidity Additions

When you wish to add liquidity to a pool, there are three cases that are possible. As an example, let's use an ETH/USDC pool with 1 ETH = 3500 USDC as the current price to demonstrate.

Case #1 - You want to add liquidity in a price range completely "above" the current price. E.g. adding liquidity to the price range of 1 ETH = 4000 to 1 ETH = 5000 USDC.

Case #2 - You want to add liquidity in a price range that "includes" the current price. E.g. adding liquidity to the price range of 1 ETH = 3000 to 1 ETH = 4000 USDC.

Case #3 - You want to add liquidity in a price range completely "below" the current price. E.g. adding liquidity to the price range of 1 ETH = 2000 to 1 ETH = 3000 USDC.

---

The interesting thing to note of these three cases is that in Cases (1) and (3), you actually only need to add liquidity in a single token. Specifically, for Case (1) you only need to provide `ETH`, and for Case (3) you only need to provide `USDC`.

You can calculate this mathematically with the above equations, and you'll find that either `x` or `y` is 0 depending on which case you're talking about.

If you want to think about it intuitively, think of it like this.

Imagine you wish to add liquidity ABOVE the current price, from 1 ETH = 4000 to 5000 USDC. When does that price range become active? It becomes active as ETH becomes more and more expensive, and the `currentTick` value goes up in the pool.

As ETH is becoming more expensive, what does the pool need from LPs? It needs ETH! ETH becoming more expensive means people are buying up ETH from the pool - and the pool wants the LPs to supply ETH to the pool. It doesn't want USDC, it has enough of that, and is getting more. As a result, if you're adding liquidity completely above the current price range, you'll only need to provide `ETH`. More generally, you need to provide liquidity only in Token 0 if adding liquidity such that `currentTick < tickLower`.

On the flip side, if you're adding below the current price range, it only needs USDC from you since that's what is being bought up by swappers - the pool already has lots of ETH and doesn't need more from you. So you only need to supply USDC. More generally, you only need to provide liquidity in Token 1 if adding liquidity such that `currentTick > tickUpper`.

---

Case (2), which is the more general and common case - adding liquidity such that your price range includes the current price - is a more interesting calculation.

To understand that, let us first understand how the equations are working.

If you look at the equations above, you'll see you can calculate L using either `x` or `y` along with other tick/price related values which need to be given always (gotten from the tick range and the current tick).

For cases (1) and (3), calculating `L` is straightforward since one of `x` or `y` will always be 0, so you just calculate `L` using the other equation.

For case (2) however, there are two ways to calculate `L`. One using `x` and one using `y`.

Therefore, we can define:

`L_x` = liquidity calculated using `x` in the equation
`L_y` = liquidity calculated using `y` in the equation

In these cases, when calculating `L_x`, we use `currentTick` as the lower boundary, and `tickUpper` as the upper boundary. Therefore, we calculate this in the price range `Sqrt P` to `Sqrt P_b`. 

For `L_y`, we use `tickLower` as the lower boundary, and `currentTick` as the upper boundary, i.e. `Sqrt P_a` to `Sqrt P`.

Conceptually, the way to think about it is that we need Token 0 as the price increases since there is Token 0 buy demand, and need Token 1 as the price decreases since there is Token 1 buy demand. If we treat the current tick/currentp rice as the midpoint, on the "left" section of the curve we provide Token 1 (`y`) and provide Token 0 (`x`) on the "right" section of the curve.

So what we need to do is we calculate both `L_x` and `L_y`, and pick the smaller of the two.

Why the smaller? 

Because if we pick the larger one, the user will need to supply additional tokens of the opposite asset. 

For example, if `L_x` > `L_y`, and we pick `L_x` as our `liquidityDelta` parameter but the user only has enough `x` and `y` tokens to cover liquidity equal to `L_y`, they will not be able to supply the liquidity.

However, if we pick the smaller one, the user should have the tokens to cover that liquidity. If they don't even have that much, they couldn't add liquidity regardless of which one we chose then.

## Which equation to use, and when?

There are different cases for different things. We'll talk about this from the perspective of a hook developer, and from the perspective of the end user you are building for.

From a hook developer's standpoint, when it comes to writing tests for example - `modifyLiquidity` requires us to pass in `liquidityDelta` i.e. the `L` value. As such, we always need to know `L` up front whenever we need to call `modifyLiquidity` (in tests or in our hook or elsewhere).

Now, you can either hardcode `L` (in tests) and use it to figure out `x` and `y`, or you can can calculate `L` based on a fixed amount of `x` or `y` you want to add as liquidity.

On the other hand, for the end user - they definitely want nothing to do with `L`. They always just want to specify either `x` or `y`, and have you calculate how much of the other token they need, and then you pass it to the contract as `L`.

Now, you can either code up those equations yourself, use the Uniswap SDK for JavaScript, or use the `LiquidityAmounts` helper utility file we have in Solidity.

`LiquidityAmounts.sol` is particularly what I'll talk about here today - the other two options you can Google those. 

`LiquidityAmounts.sol` is present in `v4-core/test/utils/LiquidityAmounts.sol`. It has 2 functions of importance to us:

- `getAmountsForLiquidity`
- and, `getLiquidityForAmounts`

`getAmountsForLiquidity` takes in `L` and returns `x` and `y` (`amount0` and `amount1`)

`getLiquidityForAmounts` takes in `x` and `y` and returns `L`

It also has four more functions for intermediate calculations:

- `getLiquidityForAmount0` takes in `x` and calculates `L_x`
- `getLiquidityForAmount1` takes in `y` and calculates `L_y`
- `getAmount0ForLiquidity` takes in `L` and calculates `x`
- `getAmount1ForLiquidity` takes in `L` and calculates `y`

## Usage

If you look at the `PointsHook.t.sol` file from "Building your first hook" lesson - https://github.com/haardikk21/points-hook/blob/main/test/PointsHook.t.sol

In this case, in both our tests, we're hardcoding `liquidityDelta` to be `1 ether` when adding liquidity.

Using that, we are calculating `amount0Delta` and `amount1Delta`

```solidity
(uint256 amount0Delta, uint256 amount1Delta) = LiquidityAmounts
    .getAmountsForLiquidity(
        SQRT_PRICE_1_1,
        sqrtPriceAtTickLower,
        sqrtPriceAtTickUpper,
        1 ether
    );
```

The reason we're calculating these is because we need `amount0Delta`, since that is in `ETH`, and we need to specify it as the `value` for the `modifyLiquidity` call to pass ETH to the payable function.

`amount1Delta` is unspecified since we gave infinite approval to the router contract before, so it will just do `transferFrom` on that directly.

> NOTE: When passing in the `value`, we are doing `amount0Delta + 1`. This is because `LiquidityAmounts` will round-down, which can sometimes lead to mismatched calculated amounts due to floating point maths. Adding 1 and passing in a small amount of additional ETH will prevent that.

---

Imagine if we wanted to modify this test file to not hardcode the `liquidityDelta`, and instead just say we want to add `0.003 ETH` as liquidity and whatever amount of `TOKEN` that implies. 

We can do that by hardcoding the ETH amount to add, using it to calculate `L`, and then using that to calculate `y`.

```solidity
uint160 sqrtPriceAtTickLower = TickMath.getSqrtPriceAtTick(-60);
uint160 sqrtPriceAtTickUpper = TickMath.getSqrtPriceAtTick(60);

uint256 ethToAdd = 0.003 ether;
uint128 liquidityDelta = LiquidityAmounts.getLiquidityForAmount0(
    SQRT_PRICE_1_1,
    sqrtPriceAtTickUpper,
    ethToAdd
);
uint256 tokenToAdd = LiquidityAmounts.getAmount1ForLiquidity(
    sqrtPriceAtTickLower,
    SQRT_PRICE_1_1,
    liquidityDelta
);

modifyLiquidityRouter.modifyLiquidity{value: ethToAdd}(
    key,
    IPoolManager.ModifyLiquidityParams({
        tickLower: -60,
        tickUpper: 60,
        liquidityDelta: int256(uint256(liquidityDelta)),
        salt: bytes32(0)
    }),
    hookData
);
```
This will work the same way, but here we didn't hardcode `L`.

