The Standard

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

`LiquidationPool::distributeFees` has a front-running vulnerability attacker can utilize without being a user

Summary

Distribution of fees can be front-run by any attacker on-chain just by jumping on the protocol for a few seconds thereby taking as many rewards if not all rewards meant to be distributed to legitimate users who have been a part of the protocol for a long period.

Vulnerability Details

This vulnerability exists across several parts of the codebase's logic for rewarding users. The issue becomes prone to frontruns because even pending stakers who haven't been staked earn rewards just as normal users who have been staked a long time. So there's no real incentive to stake for a long time.

FILE: LiquidationPoolManager.sol
function distributeFees() public { // @audit-issue anyone can call to trigger the liquidation manager pool to distribute fees as long as _feesForPool > 0
IERC20 eurosToken = IERC20(EUROs);
uint256 _feesForPool = eurosToken.balanceOf(address(this)) * poolFeePercentage / HUNDRED_PC;
if (_feesForPool > 0) {
eurosToken.approve(pool, _feesForPool);
LiquidationPool(pool).distributeFees(_feesForPool);
}
eurosToken.transfer(protocol, eurosToken.balanceOf(address(this)));
}

As noted in the code, anyone can trigger the distributeAssets function of the LiquidationPoolManager to begin the process of the attack. A bot can be up to monitor when _feesForPool > 0 and kick off the attack then deposit a huge amount of TST and run distributeFees of the manager contract or wait for somebody to run it then front run with their deposit. A flashloan can be used for max profit. TST stakes in the pool doesnt have to be 0, just low enough for him
to get max rewards.

FILE: LiquidationPool.sol
function distributeFees(uint256 _amount) external onlyManager {
uint256 tstTotal = getTstTotal();
if (tstTotal > 0) {
IERC20(EUROs).safeTransferFrom(msg.sender, address(this), _amount);
for (uint256 i = 0; i < holders.length; i++) {
address _holder = holders[i];
positions[_holder].EUROs += _amount * positions[_holder].TST / tstTotal;
}
for (uint256 i = 0; i < pendingStakes.length; i++) {
@> pendingStakes[i].EUROs += _amount * pendingStakes[i].TST / tstTotal; // @audit-issue can be exploited with frontruns paird with `distributeFees()` of `LiquidationPoolManager.sol`
}
}
}

Since the EUROs position of pendingStakes gets included in the fee distribution, a staker who joined at least one position above the txn to distribute fees will earn rewards and just proceed to quit the system afterwards then the process repeats.

Impact

The vulnerability has already highlighted the impact but in simple terms, legitimate users will get next to nothing compared to a bot who has a massive stake and then quit the system after the realized profit. Even if the team advises users to have both EUROs and TST staked, we don't believe each user has the same buying power of funds acquired by flashloans.

Tools Used

Manual review

Recommendations

Some recommendations we have to remediate this issue:

  1. Perhaps don't let the distributeFees of the LiquidationPoolManager be accessible to everyone. This will fix one of the issue but still be prone to frontruns of the actual txn.

  2. Enforce a minimum stake period e.g. 1 day or more and remove pendingStakes from the rewards. Keep rewards to users who are actually staked.

Updates

Lead Judging Commences

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

frontrun-distrubutefees

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

frontrun-feedist-low

Support

FAQs

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