Core Contracts

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

Scaled amount should be minted not amount

Summary

RToken::mint should mint the amountScaled as opposed to the amount. This doesn't work with the interest accrual mechanism.

Vulnerability Details

The mint function mints the raw amount instead of the scaled amount:

// Current implementation
_mint(onBehalfOf, amountToMint.toUint128());
// Aave's correct implementation
_mint(onBehalfOf, amountScaled.toUint128());

Example scenario:

User A deposits 100 USDC when index = 1.0

Receives 100 RTokens

User B deposits 100 USDC when index = 1.1

Should receive ~90.91 RTokens (100/1.1)

Currently receives 100 RTokens

Result: User B's tokens represent more value at deposit, despite the same deposit time, because the value of their RTokens will be 100 * 1.1 = 110, but in reality, the function should mint the scaled amout so that when scaled up by the interest, the RTokens equal the amount being minted at whatever time they were minted.

Also, just to reitorate how clearly this is wrong, RToken::balanceOf perfectly verifies this finding.

function balanceOf(address account) public view override(ERC20, IERC20) returns (uint256) {
uint256 scaledBalance = super.balanceOf(account);
return scaledBalance.rayMul(ILendingPool(_reservePool).getNormalizedIncome());
}

As you can see, when balanceOf is done, it is the users token balance scaled by the liquidity index.

Therefore, going back to the original example, User A now rightfully has 110 tokens due to interest accrual, yet User B also has 110 tokens, but they haven't deposited for nearly as long as A. This mechanism is unfair and breaks basic logic.

Impact

Favours later users over previous, a user can use the instant increase in token value upon deposit to withdraw an amount greater than the amount they initially inputted into the protocol.

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();
--> if(amount > userBalance){
amount = userBalance;
}
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);

This is because balanceOf considers the interest accrual. So a user could mint tokens (which is a process that doesn't consider interest accrual), than have a greater balance immediately (due to interest accrual), then use their inflated balance to withdraw more tokens than they deposited. So a user could extract all the underlying assets if liquidityIndex > 1.

Tools Used

Manual review

Recommendations

Mint the scaled amount

Updates

Lead Judging Commences

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

DebtToken::mint miscalculates debt by applying interest twice, inflating borrow amounts and risking premature liquidations

Support

FAQs

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