Uniswap V3 introduced a groundbreaking approach to liquidity provision by allowing users to concentrate their liquidity within specific price ranges. This innovation significantly enhances capital efficiency but also introduces complexities in calculating rewards for liquidity mining programs. Unlike the simpler models of V2, V3's reward distribution must account for both time and the asset's price position relative to a user's chosen range.
This article breaks down the core principles behind liquidity mining in Uniswap V3, explaining the mathematical models and on-chain implementations that make it work.
How Liquidity Mining Rewards Were Calculated in V2
In Uniswap V2, reward calculation was relatively straightforward. The protocol emitted rewards at a constant rate, and users earned a share proportional to their contributed liquidity over time.
Let’s define the key variables:
- $R$: The reward emission rate per second.
- $l(t)$: The value of liquidity provided by a specific user at time $t$.
- $L(t)$: The total value of all liquidity staked in the pool at time $t$.
The reward a user earned at any single moment $t$ was:
$$ R \cdot \frac{l(t)}{L(t)} $$
To calculate the total rewards earned between time $t_0$ and $t_1$, you would sum up all these instantaneous rewards:
$$ \sum_{t=t_0}^{t1} {R \cdot \frac{l(t)}{L(t)}} $$
While simple off-chain, this summation would be prohibitively expensive in gas costs to compute on-chain. To optimize, the model was refactored.
If a user's liquidity, $l$, remained constant throughout the period, the formula could be simplified and pre-computed using a cumulative sum.
We define a cumulative variable:
$$ S_l(t_i)=\sum_{t=0}^{t_i} {\frac{1}{L(t)}} $$
The rewards for the period then become a simple subtraction:
$$ R \cdot l \cdot (S_l(t_1) - S_l(t_0)) $$
This cumulative variable $S_l(t_i)$ is updated whenever a user deposits or withdraws liquidity, drastically reducing the computational overhead for calculating individual rewards.
The Challenge and Solution in Uniswap V3
Uniswap V3's key innovation is concentrated liquidity. Users provide liquidity within a custom price range $[i_{lower}, i_{upper}]$. They only earn trading fees and, by extension, liquidity mining rewards when the current price $i_c$ is within their specified range.
This means the reward calculation must be multiplied by an indicator function that is 1 when $i_{lower} \le i_c \le i_{upper}$ and 0 otherwise. The reward sum becomes:
$$ R \cdot l \cdot \sum_{t=t_1}^{t_2} \begin{cases} \frac{1}{L(t)} & \text{if } i_{lower} \le i_c \le i_{upper} \\ 0 & \text{otherwise} \end{cases} $$
This can be algebraically transformed into a form that leverages existing data structures within the Uniswap V3 protocol:
$$ R \cdot l \cdot \left( \sum_{t=t_1}^{t_2} {\frac{1}{L(t)}} - \sum_{t=t_1}^{t_2} \begin{cases} \frac{1}{L(t)} & \text{if } i_c < i_{lower} \\ 0 & \text{otherwise} \end{cases} - \sum_{t=t_1}^{t_2} \begin{cases} \frac{1}{L(t)} & \text{if } i_c \ge i_{upper} \\ 0 & \text{otherwise} \end{cases} \right) $$
The critical insight is that the terms inside the parentheses can be efficiently computed on-chain. Uniswap V3 pools already maintain a cumulative variable for fee calculation called secondsPerLiquidityInsideX128
, which tracks the time-weighted sum of 1/L(t)
but only for periods when the price was inside a given range.
The core contract exposes a function to snapshot these cumulative values for any price range:
function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)
external
view
returns (
int56 tickCumulativeInside,
uint160 secondsPerLiquidityInsideX128,
uint32 secondsInside
)
How It Works in Practice: The Staker Contract
The official UniswapV3Staker
contract utilizes this function to calculate rewards.
- Upon Staking (
t1
): When a user stakes an NFT (representing a liquidity position), the contract callssnapshotCumulativesInside
for that position's price range. It stores the initial valuesecondsPerLiquidityInsideInitialX128
and the stakedliquidity
amount. - Upon Claiming (
t2
): To calculate rewards, the contract callssnapshotCumulativesInside
again for the same range. - Reward Calculation: The difference between the final and initial cumulative values is calculated. This difference represents the integrated
secondsPerLiquidityInside
accrued specifically while the price was inside the range during the staking period. This value is then multiplied by the user's liquidity and used to compute their share of the total reward pool.
The core logic from the library function is:
secondsInsideX128 = (secondsPerLiquidityInsideX128 - secondsPerLiquidityInsideInitialX128) * liquidity;
reward = FullMath.mulDiv(totalRewardUnclaimed, secondsInsideX128, totalSecondsUnclaimedX128);
This elegant solution efficiently and fairly distributes rewards based on a user's liquidity and, crucially, the time-weighted usefulness of that liquidity as defined by its active price range. 👉 Explore more advanced DeFi strategies
Frequently Asked Questions
What is the main advantage of Uniswap V3's liquidity mining?
The main advantage is capital efficiency. Liquidity providers (LPs) can concentrate their capital within specific price ranges, potentially earning higher fees and rewards per dollar deposited compared to V2, provided the price stays within their chosen range.
How does my chosen price range affect my rewards?
You only earn liquidity mining rewards when the market price of the asset pair is within your provided liquidity range. If the price moves above or below your range, you stop accruing rewards until the price moves back inside.
Is providing liquidity in V3 riskier than in V2?
It can be. While it offers higher potential returns, V3 involves more active management. If the price moves outside your chosen range, your capital becomes idle and earns no fees or rewards, potentially leading to impermanent loss without the offsetting income.
How do I know the best price range to choose?
Choosing a range depends on your market outlook and risk tolerance. A wider range is less likely to become inactive but offers lower fee density. A narrower range can yield higher returns but carries a greater risk of the price moving beyond it. Many LPs use tools and analytics platforms to inform their decisions.
Can I change my price range after I've staked for mining?
Typically, yes, but it involves multiple steps. You would usually have to unstake your current position, withdraw it, adjust the price range of your liquidity position (which burns the old NFT and mints a new one), and then restake the new NFT in the incentive contract. This process involves gas costs.
Where can I see my accrued mining rewards?
You can check the specific staking contract interface where you deposited your NFT. Blockchain explorers and popular DeFi dashboards often integrate with these contracts, allowing you to connect your wallet and view your pending rewards for various liquidity mining programs. 👉 View real-time yield farming tools