Core Contracts

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

inaccurate transfer in `RToken::transferAccruedDust` function

Summary

This is due to the improper calculations in the RToken::calculateDustAmount function

Vulnerability Details

function transferAccruedDust(
address recipient,
uint256 amount
) external onlyReservePool {
if (recipient == address(0)) revert InvalidAddress();
@> uint256 poolDustBalance = calculateDustAmount();
if (poolDustBalance == 0) revert NoDust();
// Cap the transfer amount to the actual dust balance
uint256 transferAmount = (amount < poolDustBalance)
? amount
: poolDustBalance;
// Transfer the amount to the recipient
IERC20(_assetAddress).safeTransfer(recipient, transferAmount);
emit DustTransferred(recipient, transferAmount);
}

As seen here, the transferAccruedDust function depends on the calculateDustAmount to properly calculate the dust amount of asset token. As we take a look at the function, we find this:

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());
// 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;
}

The function scales asset amount as well as the total supply of RTokens. This is useless and even leads to improper calculation at the end, since the totalSupply function returns the total supply of RTokens denominated in assets and scaling both values again will lead to calculation that is way off the actual amount of dust accrued

Impact

This leads to improper calculations every time when ILendingPool(_reservePool).getNormalizedIncome() is different than 1 Ray and may even lead to users being unable it withdraw funds in some very extreme conditions where everybody withdraw their funds from the protocol

Tools Used

Manual Review

Recommendations

Don't call the rayMul on totalRealBalance as it is already denominated in asset token as seen here:

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

and don't call rayDiv on the total balance of assets, since it is denominated in assets.

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 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.

Give us feedback!