ShibaSwap v2’s concentrated liquidity system rewards liquidity providers based on their position’s effectiveness within specific price ranges and time spent in range.
Rewards are proportional to liquidity share, time in range, and the effectiveness of your position’s price range.
Concentrated Liquidity Reward Mechanisms
Fee Distribution Algorithm
Trading fees are distributed proportionally based on liquidity share and time in range:
contracts/LiquidityRewards.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0 ;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol" ;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol" ;
import "@openzeppelin/contracts/security/ReentrancyGuard.sol" ;
contract ConcentratedLiquidityRewards {
// Fee growth tracking for concentrated liquidity
struct FeeGrowthInfo {
uint256 feeGrowthGlobal0X128;
uint256 feeGrowthGlobal1X128;
uint256 feeGrowthOutside0X128;
uint256 feeGrowthOutside1X128;
uint256 feeGrowthInside0LastX128;
uint256 feeGrowthInside1LastX128;
}
// Position information for reward calculation
struct PositionInfo {
uint256 tokenId;
uint128 liquidity;
int24 tickLower;
int24 tickUpper;
uint256 feeGrowthInside0LastX128;
uint256 feeGrowthInside1LastX128;
uint128 tokensOwed0;
uint128 tokensOwed1;
}
// Calculate fees earned by a concentrated liquidity position
function calculatePositionFees (
PositionInfo memory position ,
FeeGrowthInfo memory feeGrowth
) public pure returns ( uint256 fee0 , uint256 fee1 ) {
// Calculate fee growth inside the position's tick range
uint256 feeGrowthInside0X128 = feeGrowth.feeGrowthGlobal0X128 - feeGrowth.feeGrowthOutside0X128;
uint256 feeGrowthInside1X128 = feeGrowth.feeGrowthGlobal1X128 - feeGrowth.feeGrowthOutside1X128;
// Calculate fees earned since last update
uint256 feeGrowthInside0DeltaX128 = feeGrowthInside0X128 - position.feeGrowthInside0LastX128;
uint256 feeGrowthInside1DeltaX128 = feeGrowthInside1X128 - position.feeGrowthInside1LastX128;
// Calculate actual fees earned
fee0 = (feeGrowthInside0DeltaX128 * position.liquidity) / 2 ** 128 ;
fee1 = (feeGrowthInside1DeltaX128 * position.liquidity) / 2 ** 128 ;
}
// Calculate reward multiplier based on time in range
function calculateTimeInRangeMultiplier (
uint256 timeInRange ,
uint256 totalTime
) public pure returns ( uint256 multiplier ) {
if (timeInRange >= totalTime * 90 / 100 ) {
return 2 ; // 2x for 90%+ time in range
} else if (timeInRange >= totalTime * 75 / 100 ) {
return 15 ; // 1.5x for 75%+ time in range
} else if (timeInRange >= totalTime * 50 / 100 ) {
return 12 ; // 1.2x for 50%+ time in range
} else {
return 1 ; // 1x for less than 50% time in range
}
}
// Calculate concentrated liquidity effectiveness
function calculateLiquidityEffectiveness (
uint128 liquidity ,
uint256 totalLiquidity ,
uint256 timeInRange
) public pure returns ( uint256 effectiveness ) {
// Base effectiveness is proportional to liquidity share
uint256 baseEffectiveness = (liquidity * 1e18 ) / totalLiquidity;
// Apply time in range multiplier
uint256 timeMultiplier = calculateTimeInRangeMultiplier (timeInRange, 365 days );
effectiveness = (baseEffectiveness * timeMultiplier) / 1e18 ;
}
}
Fee Collection and Distribution
Concentrated liquidity positions automatically collect fees as trades occur within their price range.
contracts/FeeCollection.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0 ;
contract FeeCollection {
// Fee collection for concentrated liquidity positions
function collectFees (
uint256 tokenId ,
uint128 amount0Max ,
uint128 amount1Max
) external returns ( uint128 amount0 , uint128 amount1 ) {
// Get position information
PositionInfo memory position = getPosition (tokenId);
// Calculate fees earned
( uint256 fee0, uint256 fee1) = calculatePositionFees (position, getCurrentFeeGrowth ());
// Limit collection to requested amounts
amount0 = uint128 (fee0 > amount0Max ? amount0Max : fee0);
amount1 = uint128 (fee1 > amount1Max ? amount1Max : fee1);
// Update position's fee tracking
updatePositionFees (tokenId, amount0, amount1);
// Transfer fees to recipient
if (amount0 > 0 ) {
transferToken0 ( msg.sender , amount0);
}
if (amount1 > 0 ) {
transferToken1 ( msg.sender , amount1);
}
}
// Calculate total fees earned by a position
function getPositionFees ( uint256 tokenId ) external view returns ( uint256 fee0 , uint256 fee1 ) {
PositionInfo memory position = getPosition (tokenId);
return calculatePositionFees (position, getCurrentFeeGrowth ());
}
// Get current fee growth for the pool
function getCurrentFeeGrowth () internal view returns ( FeeGrowthInfo memory ) {
// This would interface with the actual pool contract
// to get current fee growth values
}
}
Reward Calculation
Fees are distributed based on the following formula:
Fees = (FeeGrowthInside * Liquidity) / 2^128
Where:
FeeGrowthInside = Global fee growth - Fee growth outside position range
Liquidity = Position’s liquidity amount
2^128 = Fixed-point precision factor
Time-in-Range Multipliers
Positions earn bonus multipliers based on time spent in range:
Time in Range Multiplier Description 90%+ 2.0x Maximum bonus for consistent in-range positions 75%+ 1.5x High bonus for mostly in-range positions 50%+ 1.2x Moderate bonus for balanced positions Less than 50% 1.0x Base rate for out-of-range positions
Position Effectiveness
The effectiveness of a concentrated liquidity position is calculated as:
Effectiveness = (Liquidity Share * Time Multiplier) / Total Pool Liquidity
This rewards positions that:
Provide liquidity in high-traffic price ranges
Maintain consistent in-range status
Contribute significant liquidity relative to the pool
## Fee Growth Tracking
The fee growth tracking system ensures accurate fee distribution:
```solidity contracts/FeeGrowthTracker.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract FeeGrowthTracker {
// Global fee growth for the entire pool
uint256 public feeGrowthGlobal0X128;
uint256 public feeGrowthGlobal1X128;
// Fee growth outside specific tick ranges
mapping(int24 => uint256) public feeGrowthOutside0X128;
mapping(int24 => uint256) public feeGrowthOutside1X128;
// Update global fee growth when swaps occur
function updateFeeGrowth(uint256 fee0, uint256 fee1, uint128 liquidity) external {
if (liquidity > 0) {
feeGrowthGlobal0X128 += (fee0 * 2**128) / liquidity;
feeGrowthGlobal1X128 += (fee1 * 2**128) / liquidity;
}
}
// Calculate fee growth inside a tick range
function getFeeGrowthInside(
int24 tickLower,
int24 tickUpper,
int24 tickCurrent
) external view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) {
if (tickCurrent < tickLower) {
// Current tick is below the range
feeGrowthInside0X128 = feeGrowthOutside0X128[tickLower] - feeGrowthOutside0X128[tickUpper];
feeGrowthInside1X128 = feeGrowthOutside1X128[tickLower] - feeGrowthOutside1X128[tickUpper];
} else if (tickCurrent >= tickUpper) {
// Current tick is above the range
feeGrowthInside0X128 = feeGrowthOutside0X128[tickUpper] - feeGrowthOutside0X128[tickLower];
feeGrowthInside1X128 = feeGrowthOutside1X128[tickUpper] - feeGrowthOutside1X128[tickLower];
} else {
// Current tick is inside the range
feeGrowthInside0X128 = feeGrowthGlobal0X128 - feeGrowthOutside0X128[tickLower] - feeGrowthOutside0X128[tickUpper];
feeGrowthInside1X128 = feeGrowthGlobal1X128 - feeGrowthOutside1X128[tickLower] - feeGrowthOutside1X128[tickUpper];
}
}
}
Best Practices
Choose optimal price ranges
Concentrate liquidity around current price for maximum fee earnings. Use the ShibaSwap interface to visualize optimal price ranges and liquidity distribution.
Monitor position status
Regularly check if your position is still in range and earning fees. Out-of-range positions don’t earn fees and may suffer impermanent loss.
Collect fees regularly
Collect accumulated fees to compound your earnings. Regular fee collection helps maximize your returns from concentrated liquidity.
Optimize range width
Balance between fee earnings and impermanent loss risk. Narrower ranges earn more fees but have higher impermanent loss risk.
Concentrated Liquidity Optimization
Strategy : Position ranges around high-traffic price levels.Implementation :
Analyze historical price movements
Focus on support and resistance levels
Use multiple narrow ranges instead of one wide range
Strategy : Choose appropriate fee tiers based on volatility.Implementation :
High volatility pairs: Use 1% fee tier
Medium volatility pairs: Use 0.3% fee tier
Stable pairs: Use 0.05% fee tier
Time-in-Range Maximization
Strategy : Maximize time spent in range for bonus multipliers.Implementation :
Use wider ranges during high volatility
Rebalance positions when price moves significantly
Monitor and adjust ranges based on market conditions