Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: medium
Valid

`transferAccruedDust()` doesn't function as expected

Summary

The RToken.transferAccruedDust() function is supposed to transfer dust amounts of the underlying token to certain recipient. It uses calculateDustAmount() to get the amount of dust to transfer, but the calculation is incorrect.

Vulnerability Details

function calculateDustAmount() public view returns (uint256) {
uint256 contractBalance = IERC20(_assetAddress).balanceOf(address(this)).rayDiv(ILendingPool(_reservePool).getNormalizedIncome());
uint256 currentTotalSupply = totalSupply();
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;
}

RToken.sol#317

Looking at this function we can see that the contract balance of crvUSD is unnecessarily divided by the liquidity index, which makes it lower than expected.

After that, we can see that totalSupply() is multiplied by the liquidity index, but if we take a closer look inside totalSupply() we can see that this value is already multiplied by the liquidtyIndex.

function totalSupply() public view override(ERC20, IERC20) returns (uint256) {
return super.totalSupply().rayMul(ILendingPool(_reservePool).getNormalizedIncome());
}

RToken.sol#203

Impact

Inside calculateDustAmount() totalRealBalance is higher than expected and contractBalance is lower than expected, which makes the function return wrong values.

Due to the fact that contractBalance is lower than expected, the function will return 0 even if there is dust to transfer. This will make the transferAccruedDust() function revert.

Recommendation

Update the calculateDustAmount() function the following way:

function calculateDustAmount() public view returns (uint256) {
- uint256 contractBalance = IERC20(_assetAddress).balanceOf(address(this)).rayDiv(ILendingPool(_reservePool).getNormalizedIncome());
+ uint256 contractBalance = IERC20(_assetAddress).balanceOf(address(this));
uint256 currentTotalSupply = totalSupply();
- 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;
+ return contractBalance <= currentTotalSupply ? 0 : contractBalance - currentTotalSupply;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

RToken::calculateDustAmount incorrectly applies liquidity index, severely under-reporting dust amounts and permanently trapping crvUSD in contract

Support

FAQs

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