The Standard

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

Stakers receive reduced rewards due to rounding issue in `LiquidationPool::distributeAssets` function

Summary

The LiquidationPool contract is responsible for managing staking positions and distributing rewards based on the staked assets. The LiquidationPool::distributeAssets function is a key component that allocates rewards to stakers proportionally to their stake in the pool. But stakers may receive less than their fair share of rewards, potentially due to rounding errors inherent in the Solidity programming language's handling of integer division.

Vulnerability Details

Solidity performs integer division, which truncates the decimal part of the quotient, potentially leading to a loss of precision in calculations. In the LiquidationPool::distributeAssets the _portion is calculated as follows:

uint256 _portion = asset.amount * _positionStake / stakeTotal;

But this calculation can result in truncated values, causing some stakers to receive slightly fewer rewards.

Impact

Let's consider the following scenario:
The amount of a given asset is 100. Thera are 3 holders and their positions are respectively: 1, 2 and 3.
The _portion amount for the first holder will be: 100 * 1 / 6 which is 16,666666667 but in Solidity is rounding down to 16.
The _portion amount for the second holder will be: 100 * 2 / 6 which is 33,333333333 but in Solidity is rounding down to 33.
The _portion amount for the third holder will be: 100 * 3 / 6 which is 50.

The first two holders will receive less than their fair share of rewards.

Also, the LiquidationPool::distributeAssets function does not appear to handle any remainder that might be left after all rewards have been distributed.

Additionally, if costInEuros is greater than _position.EUROs the _portion will be calculated as the following:

if (costInEuros > _position.EUROs) {
@> _portion = _portion * _position.EUROs / costInEuros;
costInEuros = _position.EUROs;
}

In that case multiplication of the result of division will be executed. Also, if costInEuros is greater than _portion * position.EUROs, _portion will be 0.

Tools Used

Manual Review

Recommendations

You can track the amount of the distributed assets and the last holder can receive the remaining undistributed assets.
Or you can track the remainder of rewards that are not distributed due to rounding and this remainder can be added to the reward pool for the next distribution cycle.

Updates

Lead Judging Commences

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

precision

Support

FAQs

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