The Standard

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

User able to get sEURO token without depositing enough tokens

Summary

In the example section 2.8.2 & 2.9.2 of the whitepaper:

Distribution%=(Participant'ssEURO+Participant'sTST)/(TotalsEURO+TotalTST)

It is intended to have a combined stake of both tokens, which to simplify assumes that both token's values would be the same, thus combining them via multiplying by 2. However in the contract implementation, only the TST token is taken into consideration:

Distribution%=(Participant'sTST)/(TotalTST)

According to the whitepaper section 2.8.1 Liquidation Pool, it is stated the the sEURO and TST tokens staked by participants must have equal proportions of 1:1. This means that if you were to increase the position of TST tokens by 1, sEURO must increase by 1. However, in the function, users can choose to increase their position regardless of the ratio. This will cause the pegging system to fail and the unfair distribution of token since TST is not "pegged".
Whitepaper

Vulnerability Details

The governance token TST, is intended to peg 1:1 with sEURO. The user can increasePosition with 100 TST tokens, and 1 sEURO token. This means the liquidation pool TST is not worth anything in terms of value with sEURO since they are not equal in amount and thus not equal in actual value. The user can choose to use the other 99 or any sEURO tokens for any reasons in any other protocol, without investing in the liquidation pool.

function getTstTotal() private view returns (uint256 _tst) {
for (uint256 i = 0; i < holders.length; i++) {
_tst += positions[holders[i]].TST;
}
for (uint256 i = 0; i < pendingStakes.length; i++) {
_tst += pendingStakes[i].TST;
}
}
function increasePosition(uint256 _tstVal, uint256 _eurosVal) external { //@audit allow non equal stake
require(_tstVal > 0 || _eurosVal > 0);
consolidatePendingStakes();
ILiquidationPoolManager(manager).distributeFees();
if (_tstVal > 0) IERC20(TST).safeTransferFrom(msg.sender, address(this), _tstVal);
if (_eurosVal > 0) IERC20(EUROs).safeTransferFrom(msg.sender, address(this), _eurosVal);
pendingStakes.push(PendingStake(msg.sender, block.timestamp, _tstVal, _eurosVal));
addUniqueHolder(msg.sender);
}
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];
// still able to get liquidate amount even though, you only staked 100 TST, and euros not in
positions[_holder].EUROs += _amount * positions[_holder].TST / tstTotal; //@audit Eligible for rewards bug
}
for (uint256 i = 0; i < pendingStakes.length; i++) {
pendingStakes[i].EUROs += _amount * pendingStakes[i].TST / tstTotal; //@audit Eligible for rewards bug
}
}
}

Impact

User can get sEURO token as rewards even though they did not deposit enough TST and sEURO in the pool.

Tools Used

Manual Review

Recommendations

Ensure that token amount deposited is of equal amount, and include the actual formula:

Distribution%=(Participant'ssEURO+Participant'sTST)/(TotalsEURO+TotalTST)
Updates

Lead Judging Commences

hrishibhat Lead Judge almost 2 years ago
Submission Judgement Published
Invalidated
Reason: Design choice
Assigned finding tags:

informational/invalid

thedoctor Submitter
almost 2 years ago
hrishibhat Lead Judge
over 1 year ago

Support

FAQs

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