calculateDustAmount() calculates dust amounts based on the underlying asset balance and total supply, scaled by the liquidity index (getNormalizedIncome()). But, it relies on the static reserve.liquidityIndex from LendingPool, which may be outdated if updateReserveState() hasn’t been called recently. This staleness can lead to incorrect dust calculations, potentially causing minor over-transfers of dust in transferAccruedDust(), resulting in small protocol losses or accounting discrepancies.
calculateDustAmount() is defined as:
This function computes dust as the excess of the underlying asset balance (IERC20.balanceOf(address(this))) over the scaled total supply (totalSupply().rayMul(getNormalizedIncome())), both normalized by the liquidity index (getNormalizedIncome()).
The LendingPool.getNormalizedIncome() function returns:
But, reserve.liquidityIndex is only updated when ReserveLibrary.updateReserveState() is called during state-modifying operations (like deposit(), withdraw()). If significant time passes since the last update (tracked by reserve.lastUpdateTimestamp), reserve.liquidityIndex becomes stale, failing to reflect accrued interest on the underlying asset.
In contrast, ReserveLibrary.getNormalizedIncome() ( computes a dynamic liquidity index:
calculateDustAmount() uses the static reserve.liquidityIndex instead of this dynamic value, risking an underestimation of totalRealBalance (due to missing interest) and an overestimation of dust.
This issue impacts transferAccruedDust()
If dust is overestimated due to a stale index, transferAccruedDust() may transfer more than the true excess, reducing the protocol’s liquidity.
Overestimated dust could lead to small over-transfers, depleting the protocol’s underlying asset balance. Given dust’s typically small scale (e.g., rounding errors or residual balances), losses are limited but accumulate over time or frequent transfers.
Discrepancies between reported dust and actual balances could skew internal accounting, though totalSupply() and asset balances remain correct
Manual code review of RToken.sol, LendingPool.sol, and ReserveLibrary.sol.
Modify calculateDustAmount() to use the dynamic liquidity index from ReserveLibrary.getNormalizedIncome() instead of LendingPool.getNormalizedIncome()
This will require you to expose reserve and rateData from LendingPool as public or nternal getter
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.