The Standard

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

Holder will not receive a fees, if he has not staked TST.

Summary

In the "LiquidationPool" contract, users stake their EUROs and/or TST tokens, and receive a fees every time a "distributeFees" function is called. This happens by increasing the user's position by a certain amount of EUROs, which is calculated in the function depending on the value of the variables _amount, tstTotal and positions[_holder].TST:

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

The value of the first is controlled by the protocol, the value of the second will never be 0 if there are TST holders. However, the value of the third depends on whether the holder's position has a TST. That is, if the user did not staked TST, then he will not receive any profit from the function, because his positions[_holder].TST will be = 0, as it is not possible to multiply/divide by 0 and the value of positions[_holder].EUROs will not change.
It also refers to pending stakes.

POC

This test shows that if the user did not stake TST, but only EUROs, then he will not receive fees. Add this test to liquidationPool.js:

it('if the user only staked EUROs, he will not receive a fees', async () => {
// Deposit EUROs only for a user
const euroStake = ethers.utils.parseEther('10000');
await EUROs.mint(user1.address, euroStake);
await EUROs.connect(user1).approve(LiquidationPool.address, euroStake);
await LiquidationPool.connect(user1).increasePosition(0, euroStake);
await fastForward(DAY);
const feeBalance = ethers.utils.parseEther('1000');
await EUROs.mint(LiquidationPoolManager.address, feeBalance);
await LiquidationPoolManager.distributeFees();
let { _position} = await LiquidationPool.position(user1.address);
expect(_position.TST).to.equal('0');
expect(_position.EUROs).to.equal(euroStake);
});

Impact

Holder will not receive a fees, if he has not staked TST.

Tools Used

Manual review, hardhat test.

Recommendations

If the project owners believe that both tokens are equivalent for staking, then it is recommended to duplicate the position growth mechanism for TST. In this case, the holder's position will grow, even if one of the tokens is missing. Also needed to calculate variables like "eurosTotal":

positions[_holder].TST += _amount * positions[_holder].EUROs / tstTotal;
pendingStakes[i].TST += _amount * pendingStakes[i].TST / tstTotal;

Otherwise, it is recommended to calculate the fees amount based on some constant value if the user should be able to use only EUROs.

Updates

Lead Judging Commences

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

no-fee-euro

informational/invalid

Support

FAQs

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