Core Contracts

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

Incorrect return values in `mint & burn` in tokens result in incorrect emit values.

Vulnerability Details

The RToken::mint first projects Mint event with amountToMint before returning minted values. Then,RToken::mint returns amountToMint in place of amountScaled. This value is then returned to LendingPool::depositwhere mint amount is emitted.

RToken::mint:

@> emit Mint(caller, onBehalfOf, amountToMint, index);
...
@> return (isFirstMint, amountToMint, totalSupply(), amountScaled);

ReserveLibrary::deposit:

(bool isFirstMint, uint256 amountScaled, uint256 newTotalSupply, uint256 amountUnderlying) = IRToken(reserve.reserveRTokenAddress).mint(
address(this), // caller
depositor, // onBehalfOf
amount, // amount
reserve.liquidityIndex // index
);
@> amountMinted = amountScaled; // @audit amountScaled is actually amountToMint that was incorrectly returned
// Update the total liquidity and interest rates
updateInterestRatesAndLiquidity(reserve, rateData, amount, 0);
@> emit Deposit(depositor, amount, amountMinted);
@> return amountMinted;

LendingPool::deposit:

@> uint256 mintedAmount = ReserveLibrary.deposit(reserve, rateData, amount, msg.sender);
// Rebalance liquidity after deposit
// @ this deposits/withdraws assets from curve vault
_rebalanceLiquidity();
@> emit Deposit(msg.sender, amount, mintedAmount);

Issue 1:

The amountToMint returned & emitted is the amount that user deposited. This could be different from the actual amount minted since the amount that's minted is scaled; it is influenced by the reserve.liquidityIndex via _update function so the amounts emitted should contain balanceIncrease to align with Aave's AToken implementation which uses it for emit purpose.

The balanceIncrease calculation is missing in RToken::burn, but same issues are present there. It should use similar implementation used in Aave's AToken to align with it. It's provided in mitigation section,

The DebtToken faces similar issues which need adjustments.

Issue2:

The balanceIncrease calculations in RToken & DebtToken make use of output from balanceOf which returns actual balance, not the scaled balance. This value is then further unscaled when calculating balanceIncrease.

For clarification, in this report, scaled or normalized implies: value.rayDiv(index).

unscaled or unnormalized or actual value implies: value.rayMul(index).

For instance, here in RToken::mint

@> uint256 scaledBalance = balanceOf(onBehalfOf); // @ this is already unscaled
uint256 balanceIncrease = 0;
if (_userState[onBehalfOf].index != 0 && _userState[onBehalfOf].index < index) {
//@audit scaledBalance is being unscaled again here!
@> balanceIncrease = scaledBalance.rayMul(index) - scaledBalance.rayMul(_userState[onBehalfOf].index);
}

As per AToken implementation, balanceIncrease is calculated by using output from super.balanceOf(user) rather than from balanceOf that returns super.balanceOf(user).rayMul(index).

Recommendations

Make the following adjustments,

RToken::mint:

- uint256 scaledBalance = balanceOf(onBehalfOf); // @ this is already unscaled
+ uint256 scaledBalance = scaledBalanceOf(onBehalfOf);
- emit Mint(caller, onBehalfOf, amountToMint, index);
+ emit Mint(caller, onBehalfOf, amountToMint+balanceIncrease, index);
...
- return (isFirstMint, amountToMint, totalSupply(), amountScaled);
+ return (isFirstMint, amountToMint+balanceIncrease, totalSupply(), amountToMint);

RToken::burn : Here, balanceIncrease actually denotes deposit growth. So if protocol wants the burned amount, no need to emit Mint. The recommended mitigation aligns with Aave's AToken

- uint256 userBalance = balanceOf(user);
+ uint256 userBalance = scaledBalanceOf(user);
+ uint256 balanceIncrease = userBalance.rayMul(index) - userBalance.rayMul(_userState[user].index);
+ uint256 amountBurned = balanceIncrease > amount ? 0 : amount - balanceIncrease
+ if (balanceIncrease > amount) {
+ uint256 amountToMint = balanceIncrease - amount;
+ emit Mint(caller, onBehalfOf, amountToMint, index);
+ } else {
+ uint256 amountToBurn = amount - balanceIncrease;
- emit Burn(from, receiverOfUnderlying, amount, index);
+ emit Burn(from, receiverOfUnderlying, amountToBurn, index);
+ }
- return (amount, totalSupply(), amount);
+ return (amountBurned, totalSupply(), amount);

DebtToken::mint:

uint256 amountScaled = amount.rayDiv(index);
- uint256 scaledBalance = balanceOf(onBehalfOf); // @ this is already unscaled
+ uint256 scaledBalance = scaledBalanceOf(onBehalfOf);
- emit Transfer(address(0), onBehalfOf, amountToMint);
- emit Mint(user, onBehalfOf, amountToMint, balanceIncrease, index);
+ emit Transfer(address(0), onBehalfOf, amountScaled);
+ emit Mint(user, onBehalfOf, amountScaled, balanceIncrease, index);
- return (scaledBalance == 0, amountToMint, totalSupply());
+ return (scaledBalance == 0, amountScaled, totalSupply());

DebtToken::burn:

- uint256 scaledBalance = balanceOf(onBehalfOf); // @ this is already unscaled
+ uint256 scaledBalance = scaledBalanceOf(onBehalfOf);
...
...
uint256 balanceIncrease = 0;
if (_userState[from].index != 0 && _userState[from].index < index) {
uint256 borrowIndex = ILendingPool(_reservePool).getNormalizedDebt();
balanceIncrease = userBalance.rayMul(borrowIndex) - userBalance.rayMul(_userState[from].index);
- amount = amount;
}
...
...
+ uint256 amountBurned = balanceIncrease > amount ? 0 : amount - balanceIncrease
+ if (balanceIncrease > amount) {
+ uint256 amountToMint = balanceIncrease - amount;
+ emit Mint(user, onBehalfOf, amountToMint, balanceIncrease, index);
+ } else {
+ uint256 amountToBurn = amount - balanceIncrease;
- emit Burn(from, amountScaled, index);
+ emit Burn(from, amountToBurn, index);
+ }
...
...
- return (amount, totalSupply(), amount);
+ return (amountBurned, totalSupply(), amount);
Updates

Lead Judging Commences

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

RToken::burn returns incorrect underlying asset amount (amount instead of amountScaled), leading to wrong interest rate calculations

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

RToken::burn returns incorrect underlying asset amount (amount instead of amountScaled), leading to wrong interest rate calculations

Support

FAQs

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

Give us feedback!