The Standard

The Standard
DeFiHardhat
20,000 USDC
View results
Submission Details
Severity: low
Invalid

Holders will get credited their pending stakes multiple times if they call `LendingPool::position` first before the maturity of their pending stakes

Summary

The positon method gets the users pending stakes and updates state of the contract by adding the stakes to the position, without checking if the the stakes are matured yet resulting in two thing

  • By passing of the 1 day waiting time

  • Double State update of the same stake amounts

Vulnerability Details

The positions Function as shown below, gets the users pending stakes and adds them to the position

function position(address _holder) external view returns(Position memory _position, Reward[] memory _rewards) {
_position = positions[_holder];
(uint256 _pendingTST, uint256 _pendingEUROs) = holderPendingStakes(_holder);
_position.EUROs += _pendingEUROs;
_position.TST += _pendingTST;
if (_position.TST > 0) _position.EUROs += IERC20(EUROs).balanceOf(manager) * _position.TST / getTstTotal();
_rewards = findRewards(_holder);
}

This Leads to a situation where the stakes have not being consolidated as shown in the holderPendingStakes method it just retrieves the pending stakes

function holderPendingStakes(address _holder) private view returns (uint256 _pendingTST, uint256 _pendingEUROs) {
for (uint256 i = 0; i < pendingStakes.length; i++) {
PendingStake memory _pendingStake = pendingStakes[i];
if (_pendingStake.holder == _holder) {
_pendingTST += _pendingStake.TST;
_pendingEUROs += _pendingStake.EUROs;
}
}
}

When the time passes and the pending stake is now consolidated as shown in the consolidatePendingStakes below, it does not check if the stakes have been added before it just checks for the time and adds the pending stakes to the positions

function consolidatePendingStakes() private {
uint256 deadline = block.timestamp - 1 days;
for (int256 i = 0; uint256(i) < pendingStakes.length; i++) {
PendingStake memory _stake = pendingStakes[uint256(i)];
if (_stake.createdAt < deadline) {
positions[_stake.holder].holder = _stake.holder;
positions[_stake.holder].TST += _stake.TST;
positions[_stake.holder].EUROs += _stake.EUROs;
deletePendingStake(uint256(i));
// pause iterating on loop because there has been a deletion. "next" item has same index
i--;
}
}
}

Impact

  • A User can bypass the 24 hour waiting period due to the position function not checking for the maturity of the pending stake

  • When the consolidatePendingStakes is called due to other actions, it adds the same pending stakes to the position, which means double stakes are added to the same position

Tools Used

Manual Review

Recommendations

Prevent the position function from updating the state of unmatured stakes to the holders position.

Updates

Lead Judging Commences

hrishibhat Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

informational/invalid

Support

FAQs

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