The issue leads to incorrect calculation of concentrated rewards which results in loss of funds for the protocol. Hence the high severity.
Proof of Concept
The Incentive Mechanism docs suggests that:
the range in which a user provides liquidity must be a superset of [currentTick-10, currentTick+10] in order for them to receive incentives. If the user's range only includes part of [currentTick-10, currentTick+10], they will not receive incentives
But while claiming concentrated rewards, the above requirement doesn't hold true.
Here's the theoretical walk through of the issue:
This block of code iterates over the lowerTick +10 and upperTick -10 to get In range concentrated liquidity from timeWeightedWeeklyPositionInRangeConcLiquidity_ mapping and add that to inRangeLiquidityOfPosition variable.
As per the docs, range must of superset of lowerTick +10 and upperTick -10 and if it is not in range, timeWeightedWeeklyPositionInRangeConcLiquidity_[poolIdx][posKey][week][j] returns 0 because that tick indicated by j doesn't exist.
Also if timeWeightedWeeklyPositionInRangeConcLiquidity_[poolIdx][posKey][week][j] returns 0, it means the range only includes part of lowerTick +10 and upperTick -10 and thus ineligible for rewards for that week. But the code here adds the value even if 0 to the inRangeLiquidityOfPosition variable and rewards are calculated and added here for the ineligible week. Thus, code is not following the incentive mechanism documented by the protocol.
Tools Used
Manual Analysis
Recommended Mitigation Steps
This issue can be mitigated by modifying this block of code to this:
./LiquidityMining.sol
if (overallInRangeLiquidity > 0) {
uint256 inRangeLiquidityOfPosition;
bool isZero;
for (int24 j = lowerTick + 10; j <= upperTick - 10; ++j) {
if (timeWeightedWeeklyPositionInRangeConcLiquidity_[poolIdx][posKey][week][j] == 0 ){
isZero == true;
break; // break out of iteration as one liquidity is already 0.
} else {
inRangeLiquidityOfPosition += timeWeightedWeeklyPositionInRangeConcLiquidity_[poolIdx][posKey][week][j];
}
}
if(isZero) {
inRangeLiquidityOfPosition == 0; // If zero exists, the liquidity for that week will be 0.
}
// Percentage of this weeks overall in range liquidity that was provided by the user times the overall weekly rewards
rewardsToSend += inRangeLiquidityOfPosition * concRewardPerWeek_[poolIdx][week] / overallInRangeLiquidity;
}
Lines of code
https://github.com/code-423n4/2023-10-canto/blob/40edbe0c9558b478c84336aaad9b9626e5d99f34/canto_ambient/contracts/mixins/LiquidityMining.sol#L185
Vulnerability details
Impact
The issue leads to incorrect calculation of concentrated rewards which results in loss of funds for the protocol. Hence the high severity.
Proof of Concept
The Incentive Mechanism docs suggests that:
But while claiming concentrated rewards, the above requirement doesn't hold true.
Here's the theoretical walk through of the issue:
This block of code iterates over the
lowerTick +10
andupperTick -10
to getIn range concentrated liquidity
fromtimeWeightedWeeklyPositionInRangeConcLiquidity_
mapping and add that toinRangeLiquidityOfPosition
variable.As per the docs, range must of superset of
lowerTick +10
andupperTick -10
and if it is not in range,timeWeightedWeeklyPositionInRangeConcLiquidity_[poolIdx][posKey][week][j]
returns0
because that tick indicated byj
doesn't exist.Also if
timeWeightedWeeklyPositionInRangeConcLiquidity_[poolIdx][posKey][week][j]
returns0
, it means the range only includes part oflowerTick +10
andupperTick -10
and thus ineligible for rewards for that week. But the code here adds the value even if0
to theinRangeLiquidityOfPosition
variable and rewards are calculated and added here for the ineligible week. Thus, code is not following the incentive mechanism documented by the protocol.Tools Used
Manual Analysis
Recommended Mitigation Steps
This issue can be mitigated by modifying this block of code to this:
Assessed type
Other