The Standard

The Standard
DeFiHardhat
20,000 USDC
View results
Submission Details
Severity: medium
Valid

User Will loose their pending stakes if they decrease their positions completely before consolidation of the pending stakes

Summary

When Holders want to remove their staked tokens, they will make a call to LendingPool::decreasePosition, and remove their tokens, and if the user removes their tokens completely, their position will be deleted, but the problem that arises with that is that any pending stakes that the holder has that has not being consolidated will be lost

Vulnerability Details

When Users call decreasePosition it checks if they have a pending stake that has not been consolidated, and it adds it to the position, but as you will see below it does not return that the user still has an unmatured pending stake left, it just proceeds

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) {
more code.....
}

As see above if the date has not yet being 1 day it just skips the if statement

Now we will look at the decreasePosition function below to understand how this vulnerability will happen

function decreasePosition(uint256 _tstVal, uint256 _eurosVal) external {
consolidatePendingStakes();
ILiquidationPoolManager(manager).distributeFees();
require(_tstVal <= positions[msg.sender].TST && _eurosVal <= positions[msg.sender].EUROs, "invalid-decr-amount");
if (_tstVal > 0) {
IERC20(TST).safeTransfer(msg.sender, _tstVal);
positions[msg.sender].TST -= _tstVal;
}
if (_eurosVal > 0) {
IERC20(EUROs).safeTransfer(msg.sender, _eurosVal);
positions[msg.sender].EUROs -= _eurosVal;
}
if (empty(positions[msg.sender])) deletePosition(positions[msg.sender]);
}

As described Before the Holder will remove their whole position, and now as the code snippet shows, if the position is empty the position is deleted which means that the holder position is no longer stored on the contract

But The User still has a pending stake that has not been added to their position, but the position of the holder is now deleted which means that when consolidatePendingStake is called by any action, it will results into loss of funds of the staked tokens for the Holder as shown the snippet below

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

As shown Above once the stake is matured it tries to add the stakes to the deleted positions, but the position does not exists anymore, which means that no effect of the addition will be recorded on the state of the contract, Then after this the pending stake will be deleted which means loss of record for the holder which will result in the loss of funds

Impact

Loss of Funds For users that still has pending stakes left, after emptying their positions using decreasePosition

Tools Used

Manual Review

Recommendations

The Following addition should be added to the decreasePosition function

if (empty(positions[msg.sender])) deletePosition(positions[msg.sender]);
//@audit In this if statement a check should also be added to know if the users has a pending stake still recorded, then if that is true then the deletePosition method should not execute
Updates

Lead Judging Commences

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

pendingstake-dos

inzinko Submitter
over 1 year ago
hrishibhat Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

deletePosition-issye

Support

FAQs

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