Rewards can be retrieved in a almost flash loan manner
Summary
As there are no delays neither in new lock creation nor in rewards claiming a big deposit with the shortest possible duration from an attacker can back-run the reward token transfer and instantly claim the most part of the rewards pot, without actually locking the funds for any viable duration.
Vulnerability Detail
An attacker can roll the lock with the minimal 10 minutes duration and back-run the distributeRewards() call with supplying vast capital amount with increaseLock() for the remainder of his short term lock duration, inflating the shares and immediately retrieving the rewards.
The attacker can optimize here, either calling right after distributeRewards() if there is a competition from other MEW attackers, or calling right before his lock expires if there isn't and capital lock time can be cut down to 12 seconds.
I.e. the lower curve point that will apply still does provide shares exposure, while the remainder of 10 minutes can be low enough for the cost of capital for that period to be not substantial compared to the market value of reward tokens allocated this way.
Impact
The attacker inflates the shares, obtaining the majority of rewards without any useful capital provision.
Net impact is the loss of the rewards for all the other stakers as the shares will be severely inflated for the given total reward amount.
function cumulativeRewardsOf(address _account) public view override returns (uint256) {
return ((pointsPerShare * getSharesOf(_account)).toInt256() + pointsCorrection[_account]).toUint256() / POINTS_MULTIPLIER;
}
I.e. there are no delays anywhere, as shares are issued immediately and rewards can be claimed immediately as well, while 10 minutes window is small enough so a big amount of funds can be allocated for that period only by the attacker.
depositToken.safeTransferFrom(_msgSender(), address(this), _increaseAmount);
// Multiplier should be acording the remaining time to the deposit to end
uint256 remainingDuration = uint256(userDeposit.end - block.timestamp);
+ require(remainingDuration > MIN_LOCK_DURATION);
uint256 mintAmount = _increaseAmount * getMultiplier(remainingDuration) / 1e18;
depositsOf[_receiver][_depositId].amount += _increaseAmount;
depositsOf[_receiver][_depositId].shareAmount += mintAmount;
Consider adding the delays for the rewards claiming.
Also, MIN_LOCK_DURATION can be increased as 10 minutes isn't meaningful for capital allocation purpose and the only usage of such short locks is manipulation.
hyh
high
Rewards can be retrieved in a almost flash loan manner
Summary
As there are no delays neither in new lock creation nor in rewards claiming a big deposit with the shortest possible duration from an attacker can back-run the reward token transfer and instantly claim the most part of the rewards pot, without actually locking the funds for any viable duration.
Vulnerability Detail
An attacker can roll the lock with the minimal 10 minutes duration and back-run the distributeRewards() call with supplying vast capital amount with increaseLock() for the remainder of his short term lock duration, inflating the shares and immediately retrieving the rewards.
The attacker can optimize here, either calling right after distributeRewards() if there is a competition from other MEW attackers, or calling right before his lock expires if there isn't and capital lock time can be cut down to 12 seconds.
I.e. the lower curve point that will apply still does provide shares exposure, while the remainder of 10 minutes can be low enough for the cost of capital for that period to be not substantial compared to the market value of reward tokens allocated this way.
Impact
The attacker inflates the shares, obtaining the majority of rewards without any useful capital provision.
Net impact is the loss of the rewards for all the other stakers as the shares will be severely inflated for the given total reward amount.
Code Snippet
distributeRewards() provides the funds:
https://github.com/sherlock-audit/2022-10-merit-circle/blob/main/merit-liquidity-mining/contracts/base/BasePool.sol#L95-L98
After that rewards can be withdrawn immediately:
https://github.com/sherlock-audit/2022-10-merit-circle/blob/main/merit-liquidity-mining/contracts/base/BasePool.sol#L100-L112
https://github.com/sherlock-audit/2022-10-merit-circle/blob/main/merit-liquidity-mining/contracts/base/AbstractRewards.sol#L106-L107
https://github.com/sherlock-audit/2022-10-merit-circle/blob/main/merit-liquidity-mining/contracts/base/AbstractRewards.sol#L53-L55
https://github.com/sherlock-audit/2022-10-merit-circle/blob/main/merit-liquidity-mining/contracts/base/AbstractRewards.sol#L73-L75
I.e. there are no delays anywhere, as shares are issued immediately and rewards can be claimed immediately as well, while 10 minutes window is small enough so a big amount of funds can be allocated for that period only by the attacker.
Tool used
Manual Review
Recommendation
Consider forcing min lock time in increaseLock():
https://github.com/sherlock-audit/2022-10-merit-circle/blob/main/merit-liquidity-mining/contracts/TimeLockPool.sol#L210-L218
Consider adding the delays for the rewards claiming.
Also,
MIN_LOCK_DURATION
can be increased as 10 minutes isn't meaningful for capital allocation purpose and the only usage of such short locks is manipulation.Duplicate of #106