144 lines
5.7 KiB
Markdown
144 lines
5.7 KiB
Markdown
|
|
# Uniswap V3 Math - Critical Learnings
|
||
|
|
|
||
|
|
## Token Ordering and Price Representation
|
||
|
|
|
||
|
|
### Price Direction Reference Table
|
||
|
|
|
||
|
|
| Scenario | token0 | token1 | Price Represents | Lower Tick → Higher Tick |
|
||
|
|
|----------|--------|--------|------------------|---------------------------|
|
||
|
|
| **token0isETH = true** | ETH | KRAIKEN | ETH per KRAIKEN | KRAIKEN cheap → expensive |
|
||
|
|
| **token0isETH = false** | KRAIKEN | ETH | KRAIKEN per ETH | ETH cheap → expensive |
|
||
|
|
|
||
|
|
### Understanding "Above" and "Below"
|
||
|
|
|
||
|
|
**Critical distinction**: "Price" in Uniswap V3 always refers to token1's price in units of token0.
|
||
|
|
|
||
|
|
**When token0isETH = true (ETH is token0, KRAIKEN is token1):**
|
||
|
|
- Price = KRAIKEN price in ETH (how much ETH to buy 1 KRAIKEN)
|
||
|
|
- Higher tick = Higher KRAIKEN price in ETH
|
||
|
|
- Lower tick = Lower KRAIKEN price in ETH
|
||
|
|
- "Price moved up" = KRAIKEN became more expensive = ETH became cheaper
|
||
|
|
- "Price moved down" = KRAIKEN became cheaper = ETH became more expensive
|
||
|
|
|
||
|
|
**When token0isETH = false (KRAIKEN is token0, ETH is token1):**
|
||
|
|
- Price = ETH price in KRAIKEN (how much KRAIKEN to buy 1 ETH)
|
||
|
|
- Higher tick = Higher ETH price in KRAIKEN
|
||
|
|
- Lower tick = Lower ETH price in KRAIKEN
|
||
|
|
- "Price moved up" = ETH became more expensive = KRAIKEN became cheaper
|
||
|
|
- "Price moved down" = ETH became cheaper = KRAIKEN became more expensive
|
||
|
|
|
||
|
|
This determines token composition:
|
||
|
|
|
||
|
|
| Current Price vs Position | Position Contains | Why |
|
||
|
|
|--------------------------|-------------------|-----|
|
||
|
|
| Below range (tick < tickLower) | 100% token1 | Token0 is too expensive to hold |
|
||
|
|
| Within range (tickLower ≤ tick ≤ tickUpper) | Both tokens | Active liquidity range |
|
||
|
|
| Above range (tick > tickUpper) | 100% token0 | Token1 is too expensive to hold |
|
||
|
|
|
||
|
|
## Liquidity vs Token Amounts
|
||
|
|
|
||
|
|
**Key Insight**: Liquidity (L) is a mathematical constant representing capital efficiency, NOT token count.
|
||
|
|
|
||
|
|
1. **Liquidity is invariant**: The liquidity value L doesn't change when price moves
|
||
|
|
2. **Token amounts are variable**: Depend on liquidity L, price range, and current price location
|
||
|
|
3. **Same L, different ranges**: Results in different token amounts due to price differences
|
||
|
|
|
||
|
|
### Why Positions at Different Ranges Have Different Token Ratios
|
||
|
|
|
||
|
|
For the same liquidity value L:
|
||
|
|
- **Position at lower ticks**: Higher token1 price → fewer token1, more token0 potential
|
||
|
|
- **Position at higher ticks**: Lower token1 price → more token1, less token0 potential
|
||
|
|
|
||
|
|
This explains why a position with fewer tokens can have more liquidity (and thus more price impact resistance).
|
||
|
|
|
||
|
|
### Liquidity Per Tick - The Critical Metric
|
||
|
|
|
||
|
|
When comparing positions of different widths, always normalize to liquidity per tick:
|
||
|
|
|
||
|
|
```solidity
|
||
|
|
liquidityPerTick = totalLiquidity / (tickUpper - tickLower)
|
||
|
|
```
|
||
|
|
|
||
|
|
The discovery position maintains its target liquidity density through width adjustment:
|
||
|
|
|
||
|
|
```solidity
|
||
|
|
// Ensure discovery has X times more liquidity per tick than anchor
|
||
|
|
discoveryLiquidity = anchorLiquidity * multiplier * discoveryWidth / anchorWidth
|
||
|
|
```
|
||
|
|
|
||
|
|
## Key Takeaways
|
||
|
|
|
||
|
|
1. **Liquidity ≠ Token Count**: Higher liquidity can mean fewer tokens at different price ranges
|
||
|
|
2. **Price Range Matters**: Token composition depends on where positions sit relative to current price
|
||
|
|
3. **Normalize for Width**: Always compare liquidity per tick when positions have different widths
|
||
|
|
4. **Token0 Ordering is Critical**: Determines which direction is "up" or "down" in price
|
||
|
|
|
||
|
|
## Critical Formulas for Out-of-Range Positions
|
||
|
|
|
||
|
|
### When Position is Below Current Price (currentTick < tickLower)
|
||
|
|
|
||
|
|
The position holds only token0:
|
||
|
|
```
|
||
|
|
amount0 = L * (sqrt(priceUpper) - sqrt(priceLower)) / (sqrt(priceUpper) * sqrt(priceLower))
|
||
|
|
```
|
||
|
|
|
||
|
|
In Solidity/JavaScript with Q96 notation:
|
||
|
|
```javascript
|
||
|
|
amount0 = liquidity * (sqrtRatioBX96 - sqrtRatioAX96) / sqrtRatioAX96 * Q96 / sqrtRatioBX96
|
||
|
|
```
|
||
|
|
|
||
|
|
### When Position is Above Current Price (currentTick > tickUpper)
|
||
|
|
|
||
|
|
The position holds only token1:
|
||
|
|
```
|
||
|
|
amount1 = L * (sqrt(priceUpper) - sqrt(priceLower))
|
||
|
|
```
|
||
|
|
|
||
|
|
In Solidity/JavaScript with Q96 notation:
|
||
|
|
```javascript
|
||
|
|
amount1 = liquidity * (sqrtRatioBX96 - sqrtRatioAX96) / Q96
|
||
|
|
```
|
||
|
|
|
||
|
|
### Critical Insight: ETH Position Creation
|
||
|
|
|
||
|
|
When creating a position above current price with ETH in a token0isWeth pool:
|
||
|
|
1. ETH is used as token1 (via getLiquidityForAmount1)
|
||
|
|
2. The position will hold KRAIKEN when in range
|
||
|
|
3. But if price drops below the range, it converts to holding ETH as token0
|
||
|
|
|
||
|
|
**Example**: Floor position created with 38 ETH at ticks [127400, 127600] when current is 123890:
|
||
|
|
- Creation: Uses getLiquidityForAmount1 with 38 ETH
|
||
|
|
- Result: Liquidity = 6.48e18
|
||
|
|
- Current holdings: 38 ETH (as token0, since price is below range)
|
||
|
|
|
||
|
|
## Common Confusion Points
|
||
|
|
|
||
|
|
### 1. Price Direction vs ETH Value
|
||
|
|
|
||
|
|
When token0isWeth = true:
|
||
|
|
- Higher tick = Higher price = More KRAIKEN per ETH = ETH is MORE valuable
|
||
|
|
- Lower tick = Lower price = Less KRAIKEN per ETH = ETH is LESS valuable
|
||
|
|
|
||
|
|
This is counterintuitive because "price goes up" means the denominated asset (ETH) becomes more valuable.
|
||
|
|
|
||
|
|
### 2. Position Token Holdings
|
||
|
|
|
||
|
|
A position's token composition depends ONLY on:
|
||
|
|
- Current price location relative to the position range
|
||
|
|
- NOT on how the position was created
|
||
|
|
- NOT on which token was used to mint
|
||
|
|
|
||
|
|
### 3. The Floor Position "Below Current Price" Confusion
|
||
|
|
|
||
|
|
In KRAIKEN's three-position strategy:
|
||
|
|
- Floor position is placed where ETH is MORE valuable (higher ticks when token0isWeth)
|
||
|
|
- This is "below" current price from an ETH value perspective
|
||
|
|
- But "above" current tick numerically
|
||
|
|
- Selling KRAIKEN for ETH moves price TOWARD the floor position
|
||
|
|
|
||
|
|
### 4. Liquidity Calculation Direction
|
||
|
|
|
||
|
|
When minting a position out of range:
|
||
|
|
- Use getLiquidityForAmount0 if providing token0
|
||
|
|
- Use getLiquidityForAmount1 if providing token1
|
||
|
|
- The same liquidity value will require different token amounts depending on which token you provide
|