The Standard

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

Staked amount is accumulated even if TST is less thant sEURO

Summary

Whitepaper, section 2.9.2, stated that the formula should "If participant TSTStaked < sEURO then void Participant else CombinedStake = sEUROStaked * 2." However, not only the participants are not voided, but the stake has not been multiplied by 2 and added into calculation.

Vulnerability Details

In LiquidationPool::distributeAssets(), it calls LiquidationPool::getStakeTotal(), then LiquidationPool::stake(). The function includes any higher tokens without checking if participant sEURO is higher in amount and voiding them. Also, both token's asset wasn't accumulated in the calculation.

To simplify:

Alice deposited 100 sEURO and 100 TST, risking both tokens.
Bob deposited only 100 TST and 1 sEURO.
Bob will only get rewards based on 1sEURO and not the total amount of 100 TST + 1sEURO, not taken into consideration.
Bob will not get the actual rewards like Alice.

The calculation of rewards is not based on TST, governance token but it is chosen based on which token is lesser. So this means that if the user staked sEURO token that is higher than TST, the TST will be chosen as the main token.

The _portion takes into account how much the Manager asset has multiplied by the _positionStake of Bob. So the stake amount only chooses the lesser amount staked, which doesn't make any sense.

//LiquidationPool::distributeAssets()
uint256 _positionStake = stake(_position);
...
uint256 _portion = asset.amount * _positionStake / stakeTotal;
function stake(Position memory _position) private pure returns (uint256) {
return _position.TST > _position.EUROs ? _position.EUROs : _position.TST;
}
function getStakeTotal() private view returns (uint256 _stakes) {
for (uint256 i = 0; i < holders.length; i++) {
Position memory _position = positions[holders[i]];
_stakes += stake(_position);
}
}
function distributeAssets(ILiquidationPoolManager.Asset[] memory _assets, uint256 _collateralRate, uint256 _hundredPC) external payable {
consolidatePendingStakes();
//@audit stale price
(,int256 priceEurUsd,,,) = Chainlink.AggregatorV3Interface(eurUsd).latestRoundData();
uint256 stakeTotal = getStakeTotal(); //@audit not reflecting true value
uint256 burnEuros;
uint256 nativePurchased;
for (uint256 j = 0; j < holders.length; j++) {
Position memory _position = positions[holders[j]];
uint256 _positionStake = stake(_position);
if (_positionStake > 0) {
for (uint256 i = 0; i < _assets.length; i++) {
ILiquidationPoolManager.Asset memory asset = _assets[i];
//@note struct Asset { ITokenManager.Token token; uint256 amount; }
//@note struct Token { bytes32 symbol; address addr; uint8 dec; address clAddr; uint8 clDec; }
if (asset.amount > 0) {
//@audit stale price
//input chainlink address
(,int256 assetPriceUsd,,,) = Chainlink.AggregatorV3Interface(asset.token.clAddr).latestRoundData();
uint256 _portion = asset.amount * _positionStake / stakeTotal; //@audit-issue true stake amount not reflected

Impact

Risk taken by users are not accounted for leading to the unfair distribution of assets due to flaws in the formula.

Tools Used

Manual Review

Recommendations

Add in the consideration of voiding participants if criteria are not met and combine sEURO as well TST staked.

Updates

Lead Judging Commences

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

informational/invalid

thedoctor Submitter
over 1 year ago
hrishibhat Lead Judge
over 1 year ago

Support

FAQs

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