Core Contracts

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

User unable to withdraw when withdrawing all of their balance

Summary

In RToken, the burnfunction is responsible for facilitating the withdraw of a user by burning their rTokensin exchange for crvUSD/ reserveToken.

But when a user tries to withdraw their entire balance, it will fail because the burnamount is never scaled while their balance of reserveTokenincreases. So the user will not have enough rTokensbalance to burn and it will revert.

The amountused for both burning rTokens and transferring reserveToken is the scaled total balance the user has which is a larger number than the actual balance becasue it represents the increase in value from interest that has accumulated. So the user wont have enough rTokensin their actual balance to burn.

Vulnerability Details

  1. The user wants to withdraw all of their balance, so they input a number they think represents the total balance they have plus the interest they earned. IF this number is larger than their actual balance the amountwill be set to userBalance. That is the scaled balance which includes the increase in value from interest accumulation.

  2. The userBalancerepresents the total value of reserveTokenthat their balance of rTokennow represents becasue of the accrual of interest. So this number is larger than their actual balance of rToken.

  3. _burnattempts to burn the scaled amount userBalancewhich is greater than the actual balance of rToken-> and it fails.

  4. The transfer of reserveTokennever happens becasue of the failed burn.

function burn(
address from,
address receiverOfUnderlying,
uint256 amount,
uint256 index
) external override onlyReservePool returns (uint256, uint256, uint256) {
if (amount == 0) {
return (0, totalSupply(), 0);
}
uint256 userBalance = balanceOf(from);
_userState[from].index = index.toUint128();
/
// This passes so `amount` is now the scaled balance
if(amount > userBalance){
amount = userBalance;
}
uint256 amountScaled = amount.rayMul(index);
_userState[from].index = index.toUint128();
// FAILS: attempts to burn the scaled balance, which is greater than the actual balance the user ahs of rTokens
_burn(from, amount.toUint128());
// Tokens never withdrawn / sent to user
if (receiverOfUnderlying != address(this)) {
IERC20(_assetAddress).safeTransfer(receiverOfUnderlying, amount);
}

The scaled userBalanceis the balance of reserveTokensthe user should receive because it represents the total value + the interest they have earned. This value is correct as the user should receive their total balance and the interest they have accrued.

The problem is because the amount burned in _burnis never scaled to represent the appreciated value of the rToken. The amount of rTokensburned should be less because it has gotten stronger, as such, the user should burn their total actual balance of rTokens-> and receive the scaled userBalance.

That would function as intended which rewards the user with their earned interest, while burning all of their rTokens, completing a full withdraw.

Impact

User unable to withdraw their total balance, and will have to withdraw piece by piece or not be able to get all of their balance back.

Tools Used

manual review

Recommendations

Scale the amount of rTokensto be burned, to represent the growth in strength, which will allow the correct amount of rTokens to be burnt and the user to burn all of their rTokensto redeem all of their balance.

Updates

Lead Judging Commences

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

RToken::burn incorrectly burns amount (asset units) instead of amountScaled (token units), breaking token economics and interest-accrual mechanism

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

RToken::burn incorrectly burns amount (asset units) instead of amountScaled (token units), breaking token economics and interest-accrual mechanism

Support

FAQs

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