Core Contracts

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

User receives no interest from RToken

Summary

RTokenis an interest bearing token, as such, it should appreciate in value vs the reserve asset crvUSD. When a user deposits crvUSDthey are minted rTokenand that token should be interest bearing and the interest accumulated, able to be redeemed by the user. The interest is calculated using the liquidityIndex.

But the user never receives this interest, whenever they withdraw their crvUSD back and burn rTokens, they get exactly the amount of rTokens that they burn, meaning they never receive any interest.

Vulnerability Details

The user withdraws crvUSD by burning rTokens through this process:

  1. call LendingPool::withdraw- specifying the amount of reserveTokento withdraw

  2. ReservePool::withdraw-> RToken::burn

The issue is in RToken::burn

side note : in ReservePool::withdrawthere is documentation that shows that the amount of rTokens burned should be the scaled amount needed to burn. Also, the return value from calling mintin the RTokencontract is expecting the scaled amount of rTokens burned.

* @return amountScaled The scaled amount of RTokens burned.
(uint256 burnedScaledAmount, uint256 newTotalSupply, uint256 amountUnderlying) = IRToken(reserve.reserveRTokenAddress).burn(
recipient, // from
recipient, // receiverOfUnderlying
amount, // amount
reserve.liquidityIndex // index

In RToken::burn

  • amountis the user requested amount of reserveAssetto receive

  • amountis used (the same value) for the amount to burn of rTokensand the amount of reserveAssettransferred to the user.

  • Because of this, the user receives 0 interest that they have earned from holding rToken.

* @return A tuple containing:
* - uint256: The amount of scaled tokens burned
* - uint256: The new total supply after burning
* - uint256: The amount of underlying asset transferred
function burn(
address from,
address receiverOfUnderlying,
uint256 amount,
uint256 index
) external override onlyReservePool returns (uint256, uint256, uint256) {
uint256 amountScaled = amount.rayMul(index);
_userState[from].index = index.toUint128();
_burn(from, amount.toUint128());
if (receiverOfUnderlying != address(this)) {
IERC20(_assetAddress).safeTransfer(receiverOfUnderlying, amount);
}
emit Burn(from, receiverOfUnderlying, amount, index);
return (amount, totalSupply(), amount);
}

The amount of rTokento be burned should be scaled by the index.

This scaled amount will represent the appreciation in value of rTokenand the interest the user has earned.

Less rTokenshoud be burned for the amount of reserveAssetthat the user receives because the rTokenshould have appreciated in value and that would represent the interest the user earned.

There is clear documentation that the protocol is expecting to burn the scaled amount of rTokensbut that is never done.

Impact

The user receives no interest that has been generated from depositing the reserveAsset and receiving the minted rTokens.

This removes any incentive for anyone to deposit into the protocol to receive rToken as they will get no reward for it, leaving the protocol unnattractive and having no reason for a user to deposit and provide liquidity to the protocol.

Tools Used

manual review

Recommendations

Scale the amount of rTokensto burn for the amountof reserveAsset being withdrawn. Ensuring the user receives the interest that they have accumulated.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

RToken::burn transfers original deposit amount (amount) to users instead of amount plus interest (amountScaled), causing loss of all accrued interest on withdrawals

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

RToken::burn transfers original deposit amount (amount) to users instead of amount plus interest (amountScaled), causing loss of all accrued interest on withdrawals

Support

FAQs

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