Summary
The contractBalance should represent the balance of underlying assets held by the RToken contract. However, it is incorrectly divided by the index, leading to an inaccurate calculation of the excess amount and resulting in locked underlying assets.
Vulnerability Details
The RToken.calculateDustAmount() function miscalculates the contractBalance.
Since contractBalance refers to the actual underlying assets held by the RToken contract. However, it is unjustifiably divided by the liquidity index of the LendingPool.
Consequently, the calculated excess amount is lower than it should be, causing some underlying assets to be locked.
function calculateDustAmount() public view returns (uint256) {
319 uint256 contractBalance = IERC20(_assetAddress).balanceOf(address(this)).rayDiv(ILendingPool(_reservePool).getNormalizedIncome());
uint256 currentTotalSupply = totalSupply();
uint256 totalRealBalance = currentTotalSupply.rayMul(ILendingPool(_reservePool).getNormalizedIncome());
return contractBalance <= totalRealBalance ? 0 : contractBalance - totalRealBalance;
}
Impact
Some underlying assets may be locked.
Tools Used
Manual review
Recommendations
Do not divide by the liquidity index.
function calculateDustAmount() public view returns (uint256) {
// Calculate the actual balance of the underlying asset held by this contract
- uint256 contractBalance = IERC20(_assetAddress).balanceOf(address(this)).rayDiv(ILendingPool(_reservePool).getNormalizedIncome());
+ uint256 contractBalance = IERC20(_assetAddress).balanceOf(address(this));
// Calculate the total real obligations to the token holders
uint256 currentTotalSupply = totalSupply();
// Calculate the total real balance equivalent to the total supply
uint256 totalRealBalance = currentTotalSupply.rayMul(ILendingPool(_reservePool).getNormalizedIncome());
// All balance, that is not tied to rToken are dust (can be donated or is the rest of exponential vs linear)
return contractBalance <= totalRealBalance ? 0 : contractBalance - totalRealBalance;
}