code-423n4 / 2023-10-canto-findings

0 stars 1 forks source link

Incorrect calculation of concentrated rewards #203

Closed c4-submissions closed 1 year ago

c4-submissions commented 1 year ago

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:

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:

  1. 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.

  2. 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.

  3. 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;
            }

Assessed type

Other

c4-pre-sort commented 1 year ago

141345 marked the issue as duplicate of #113

c4-pre-sort commented 1 year ago

141345 marked the issue as sufficient quality report

c4-judge commented 1 year ago

dmvt marked the issue as unsatisfactory: Invalid