MorpheusAI

MorpheusAI
Foundry
22,500 USDC
View results
Submission Details
Severity: low
Valid

No `pool_.payoutStart` timestamp protection in `Distribution::editPool` can put funds at risk

Summary

Lack of pool_.payoutStart protection in Distribution::editPool can lead to wrong reward calculation for users.
Users will get either smaller rewards or at the worst case a pool getting drained.

Vulnerability Details

The Distribution contract has two functions for pool modeling - createPool and editPool. Both are callable only by the owner.
In the cratePool function there is a check that requires pool.payoutStart to be greater than the current timestamp.

function createPool(Pool calldata pool_) public onlyOwner {
@> require(pool_.payoutStart > block.timestamp, "DS: invalid payout start value");
_validatePool(pool_);
...

However this check is missing in editPool.
It is noted in the CodeHawks docs that "Issues related to incorrect input by admins, call order mistakes, and assumptions breaking due to admin actions." , are invalid, but also that these rules are not concrete and may vary in certain scenarios. In the current case this will lead to users getting less or more rewards depending in which direction pool.payoutStart is moved.
It is not an issue that can be bypassed easy once it happens - for example just redeploying a contract. Between the time of the issue taking place and being fixed (calling editPool again) someone could have gotten less rewards or the pool drained.

Note that this will not be the case if the current payoutStart > block.timestamp and is moved in the future, however this would not be good for users that have already staked.

Proof Of Concept

1.
Current Time: pool.payoutStart
Bob stakes 10 tokens
2.
Current Time: pool.payoutStart + 10 days
Admin: edits pool and sets payoutStart to X + 10 days
3.
Bob withdraws his stake not getting any rewards due to the changed payout start

In the reverse scenario the protocol will lose funds. In the worst case scenario (pool.payoutStart = 0) the pool would be drained of rewards.

Impact

The likelihood would be small/medium, but the impact on the protocol is High

Tools Used

Manual Review

Recommendations

To escape this scenario, would highly recommend adding a require that disables the pool.payoutStart from being changed

function editPool(uint256 poolId_, Pool calldata pool_) external onlyOwner poolExists(poolId_) {
_validatePool(pool_);
require(pools[poolId_].isPublic == pool_.isPublic, "DS: invalid pool type");
+ require(pools[poolId_].payoutStart == pool_.payoutStart , "DS: invalid payout start value");
PoolData storage poolData = poolsData[poolId_];
uint256 currentPoolRate_ = _getCurrentPoolRate(poolId_);
// Update pool data
poolData.rate = currentPoolRate_;
poolData.lastUpdate = uint128(block.timestamp);
pools[poolId_] = pool_;
emit PoolEdited(poolId_, pool_);
}

Or only changing it if the payout has not yet started and it would be moved sooner but not less than the current timestamp, in which case nor users or protocol lose from the change

Updates

Lead Judging Commences

inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

`editPool` function doesn't do the payoutStart check

inallhonesty Lead Judge
over 1 year ago
yotov721OLD Submitter
over 1 year ago
inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

`editPool` function doesn't do the payoutStart check

Support

FAQs

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