Jovial Teal Butterfly
High
Precision loss in BondToken::getIndexedUserAmount
function due to division by SHARES_DECIMALS
in every iteration.
BondToken::getIndexedUserAmount
is used to get shares owned by an user. In the function the shares is calculated as -
https://github.com/sherlock-audit/2024-12-plaza-finance/blob/14a962c52a8f4731bbe4655a2f6d0d85e144c7c2/plaza-evm/src/BondToken.sol#L195
for (uint256 i = userPool.lastUpdatedPeriod; i < period; i++) {
shares += (balance * globalPool.previousPoolAmounts[i].sharesPerToken).toBaseUnit(SHARES_DECIMALS);
}
the toBaseUnit does following -
function toBaseUnit(uint256 amount, uint8 decimals) internal pure returns (uint256) {
return amount / (10 ** decimals);
}
But this approach can lead to, precision loss, as the division is done for each iteration instead of whole.
for example consider a loop of 2 iteration only -
1st iteration
balance = 105555 globalPool.previousPoolAmounts[0].sharesPerToken = 2000
shares = (109999*2000)/ 10**6 = 2199
2nd iteration
balance = 105555 globalPool.previousPoolAmounts[1].sharesPerToken = 4000
shares = 2199 + (109999*4000)/ 10**6 = 2199 + 4399 = 6598
If the final product is divide by the shares then the precession loss will be comparatively less.
i.e.
[(109999*2000) + (109999*4000)]/10**6 = 6599
It can be clearly seen there is loss of 1 unit.
As for the above the amount taken are very small and only of 2 iterations, but in real life BOND_ETH has 18 decimals and the length of loop could be very large, in that case the precision loss could be pretty high.
Another analogy -
current architecture - 1.7/1 +1.4/1 = 2
if divided the whole - [1.7 + 1.4]/1 = 3
Leading to loss by 1 uint. This analogy could also be applied.
Division of all elements by SHARES_DECIMALS
for each iteration of for loop.
No response
No response
No response
- Can lead to less number of shares calculation for user.
- The share of an user is stored in a mapping
userAssets[user]
. - In
Distributer::claim
the shares of an user is fetched via the function
uint256 shares = bondToken.getIndexedUserAmount(msg.sender, balance, currentPeriod)
.normalizeAmount(bondToken.decimals(), IERC20(couponToken).safeDecimals());
- as the updated
userAssets[user]
is less as it should be due to precession, the user will claim less reward.
No response
Avoid dividing for each iteration of loop, instead divide the fincal product by SHARES_DECIMAL
something like -
for (uint256 i = userPool.lastUpdatedPeriod; i < period; i++) {
shares += (balance * globalPool.previousPoolAmounts[i].sharesPerToken);
}
return shares.toBaseUnit(SHARES_DECIMALS);