The Standard

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

Block LiquidationPool by providing too much pending stake

Summary

Block increasePosition, decreasePosition or distributeAssets by having too much pending stake.

Vulnerability Details

When increasing the position, we add a pending stake object in the pendingStakes variables. A deadline of 1 day is defined to take into consideration the pending stake and to delete it from the pending list. This verification is done when we are calling some functions as increasePosition, decreasePosition or distributeAssets. Each time, we iterate over all the list element, to verify if we can delete the item or not. The main issue is when we have too much pending stakes waiting inside the pendingStakes. This lead to a significant increase of the gas used which can block users to interact with it as it could be impossible to be execute on chain.

Impact

Each time we add a new item in the pendingStakes by using for instance increasePosition() function, we increase the gas price that the user will have to use leading to a potential block of the request. This can block the access to increasePosition, decreasePosition or distributeAssets functions.

For instance, we have reproduce a small code that show the impact of increasing the gas:

describe('Block pool', async () => {
it('block pool by too much increase', async () => {
const balance = ethers.utils.parseEther('10000');
await TST.mint(user1.address, balance);
await EUROs.mint(user1.address, balance);
await TST.approve(LiquidationPool.address, balance);
await EUROs.approve(LiquidationPool.address, balance);
for (let i = 0; i < 100; i++) {
await LiquidationPool.increasePosition(1, 1);
}
await fastForward(DAY);
let tx = await LiquidationPool.increasePosition(1, 1);
let receipt = await tx.wait();
console.log("Tx price: ", receipt.gasUsed)
});
});
  • For 10 iterations : 397 956 gas

  • For 100 iterations: 11 089 498 gas

=> We have multiple by 27.

An attacker can decide to add small liquidity just to block the functions to be use.

Tools Used

Manual review / Hardhat.

Recommendations

Have the possibility to run consolidatePendingStakes in a batch to avoid too much treatment when calling increasePosition, decreasePosition or distributeAssets functions.

Also, avoid iteration on all the pendingStakes variable when not necessary. Each time we add a new item in the list, it is order by timestamps. So we can break the loop when we do not match the deadline. See:

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--;
+ } else {
+ break;
+ }
}
}
Updates

Lead Judging Commences

hrishibhat Lead Judge almost 2 years ago
Submission Judgement Published
Validated
Assigned finding tags:

pendingstake-dos

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

pendingstake-high

Support

FAQs

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