MorpheusAI

MorpheusAI
Foundry
22,500 USDC
View results
Submission Details
Severity: high
Invalid

Making startTime_ equal to payoutStart_ disturbs whole accounting

Vulnerability Details

LinearDistributionIntervalDecreaselibrary has getPeriodReward which checks pool startTime should not be less than the reward payoutStart time.

// 'startTime_' can't be less than 'payoutStart_'
if (startTime_ < payoutStart_) {
startTime_ = payoutStart_;
}

However in the condition startTime_ is assigned the value of payoutStart_ which disturb the whole reward accounting if startTime_ input is passed less than the time payoutStart_; leading to loss of rewards for stakers.

In Line#33 when startTime_ is set to payoutStart_ they are then substracted from each other at L#48 which will result in 0 as both have same value.

The resultant value of timePassedBefore_ will be zero which will bypass the condition at L#49 if ((timePassedBefore_ / interval_) == ((endTime_ - payoutStart_) / interval_)) , as it is no met.

The code then comes at L#57 which calls the _calculatePartPeriodReward function where on line#110 calculation will be carried out uint256 intervalsPassed_ = (startTime_ - payoutStart_) / interval_;
As, both the startTime_ and payoutStart_ will have same value it will result in 0 which will lead to zero value outcome when division is carried out with interval_.
The result of decreaseRewardAmount_ will also result in zero as the value of intervalsPassed_ is zero.

The value of intervalFullReward_ at L#115 will remain unchanged, as the value of decreaseRewardAmount will be zero due to calculation uint256 intervalFullReward_ = initialAmount_ - decreaseRewardAmount_;.
As the value of toEnd_ is true it will go to L#119 which will have result in same value as the value of interval_ as the intervalsPassed_ value is zero which is added to 1 resulting in one.

intervalPart_ = interval_ * (intervalsPassed_ + 1) + payoutStart_ - startTime_;

In the above arithmetic operation the interval_ is multiplied by one as intervalsPassed have a value of zero.
interval_ will have result in same value and later added with payoutStart_ - startTime_ which as well have value equal to zero as both have same value due to condition at line #33.
When the code reaches L#124 the value of will be same which will return value equal to 0.

After the calling of _calculatePartPeriodReward at line#57 the code will then proceed to L#66, that calls the _calculateFullPeriodReward.

_calculateFullPeriodReward function at L#131 will have same issues of calculation as above define as value of timePassedBefore_ will be zero due to condition at line#33.
At line#141, the _divideCeil function is called which will also return 0 as can be checked from this simple test.

function test_divideCeil(uint256 a_, uint256 b_) public {
a_ = 0;
b_= 10;
uint c = (a_ + b_ - 1) / b_;
console2.log(c, 'this is result');
assertEq(c, 1);
}
----------------------------------------------------------------------------------------------------------------------------------------------------
[FAIL. Reason: assertion failed; counterexample: calldata=0xc6d4a74a0000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000 args=[0, 0]] test_divideCeil(uint256,uint256) (runs: 0, μ: 0, ~: 0)
Logs:
0 this is result
Error: a == b not satisfied [uint]
Left: 0
Right: 1
Test result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 61.91ms
Ran 1 test suites: 0 tests passed, 1 failed, 0 skipped (1 total tests)
Failing tests:
Encountered 1 failing test in test/price.t.sol:CounterTest
[FAIL. Reason: assertion failed; counterexample: calldata=0xc6d4a74a0000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000 args=[0, 0]] test_divideCeil(uint256,uint256) (runs: 0, μ: 0, ~: 0)
Encountered a total of 1 failing tests, 0 tests succeeded

This will result in value at L#143 to be equal to 0. Due to zero value the variable initialReward_ will remain unchanged.
At line#153 the result will be lower value as intervalsPassedBefore_ will be equal to zero that leads to loss of precision. Afterwards at L#158 the return value will be less reward token than the staker is eligible for leading to loss of rewards.

When the call to _calculateFullPeriodReward complete at line 66 the code proceed to line#75.

At line 75 _calculatePartPeriodReward function is again called with bool value false which will repeat the same process carried out at L#57 with exception to going to L#121 after L#115.

At line 121 the result value will be zero as the value of intervalsPassed_ will be already zero and the whole calculation will result in same zero as well.
The next code executed will be return value at line#128 which will have a resultant zero due to intervalPart_ part being zero.

After completion of _calculatePartPeriodReward function call at line #75 the code will proceed to line#84 which will return the value of above variables that will be less than expected as firstPeriodReward_, thirdPeriodReward_ will return zero and secondPeriodReward_ have less reward tokens.

Impact

The impact will be on several functions in Distribution.sol mainly the claim functions as it call _getCurrentPoolRate(_) inside it.
_getCurrentPoolRate(_) function calls getPeriodReward method which returns the value from getPeriodReward method of LinearDistributionIntervalDecrease Library.
claim is the main function that will be used by stakers to claim their reward token.

Recommendations

The recommendation is made to simply does not allowing value of startTime_ to be less than payoutStart_ and putting a sanity check that validates value of startTime_ is always greater than payoutStart_.

// 'startTime_' can't be less than 'payoutStart_'
- if (startTime_ < payoutStart_) {
- startTime_ = payoutStart_;
- }
+ require (startTime_ > payoutStart_, startTime_ can't be less than payoutStart);
Updates

Lead Judging Commences

inallhonesty Lead Judge
over 1 year ago
inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.