The calculateDustAmount() function in RToken incorrectly mixes scaled and unscaled values when calculating excess tokens (dust). which make the function transferAccruedDust of the lendingPool effectively broken
The vulnerability lies in the calculateDustAmount() function's incorrect handling of scaled values in RToken. This function is designed to identify excess tokens (dust) by comparing the contract's actual token balance against what it should hold based on the total supply. However, the implementation fundamentally mishandles the scaling mechanics.
First, let's look at how totalSupply() is implemented in RToken:
This shows that totalSupply() already returns an unscaled value (real token amount) by multiplying the scaled balance by the liquidity index.
However, in calculateDustAmount(), the function incorrectly handles scaling:
The function makes two critical errors:
Unnecessary Normalization of Contract Balance:
Takes the real token balance and divides it by liquidity index: balanceOf(address(this)).rayDiv(getNormalizedIncome())
This incorrectly converts real tokens into a normalized (scaled) form
Double Scaling of Total Supply:
Uses totalSupply() which already returns unscaled amount
Then multiplies it again by liquidity index: totalSupply().rayMul(_liquidityIndex)
This results in double-scaling the total supply
Contract actual balance = 200 tokens
Total supply = 150 tokens (already in real token terms)
Liquidity index = 1.5 RAY
The function performs:
contractBalance = 200/1.5 = 133.34 (incorrectly scaling down real balance)
totalRealBalance = 150 * 1.5 = 225 (double scaling)
Compares: 133.34 > 225 (false)
This results in the function reporting no dust, when in reality there is 50 tokens excees
The dust recovery mechanism in the protocol becomes non-functional
Excess tokens become permanently locked in the contract
Protocol owners cannot recover legitimate dust amounts
Manual Review
Foundry
Remove the double scaling by comparing values in the same scale. Compare raw balance with scaled total supply:
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.