Core Contracts

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

Incorrect RToken amount minted as lenders deposit leads to protocol insolvency

Summary

The LendingPool's deposit function mints RTokens using the raw deposit amount instead of the scaled amount adjusted by the liquidity index. This causes users to receive more RTokens than they should, leading to protocol insolvency as users can withdraw more underlying assets than they originally deposited.

Vulnerability Details

The issue lies in the core deposit flow where RTokens are minted to represent users' deposits. The protocol is designed to use a liquidity index to track the accumulation of interest over time, similar to Aave's aToken mechanism.

Before I explain the issue more, here is a short recap on the lender flows;

deposit: LendingPool.deposit(asset) -> ReserveLibrary.deposit -> RToken.mint -> lender has rTokens

withdraw: LendingPool.withdraw(rTokens) -> ReserveLibrary.withdraw -> RToken.burn -> lender has asset

When a user deposits crvUSD, the amount should be scaled down by dividing it by the current liquidity index before minting RTokens. This ensures that as the liquidity index increases over time (representing earned interest), the same number of RTokens will be worth more underlying crvUSD.

However, in the current implementation in RToken::mint:

function mint(
address caller,
address onBehalfOf,
uint256 amountToMint,
uint256 index
) external override onlyReservePool returns (bool, uint256, uint256, uint256) {
// ...
uint256 amountScaled = amountToMint.rayDiv(index);
// ...
// @audit Incorrect - Minting raw amount instead of scaled amount
_mint(onBehalfOf, amountToMint.toUint128());
// ...
}

The protocol calculates the correct scaled amount but then incorrectly uses the unscaled amountToMint when minting RTokens.

To understand why this is problematic:

  1. The liquidity index starts at RAY (1e27) and increases over time

  2. If a user deposits 100 crvUSD when the liquidity index is 1.1 * RAY:

    • Correct scaled amount: 100 / 1.1 ≈ 90.90 RTokens

    • Current implementation: Mints 100 RTokens

  3. Later when the user withdraws and the liquidity index is 1.2 * RAY:

    • Correct: 90.90 * 1.2 = 109.08 crvUSD (representing principal + interest)

    • Current: 100 * 1.2 = 120 crvUSD (incorrectly high amount)

On the other hand when withdrawing, RToken::burn is called which uses balanceOf function which correctly scales up the balance using the current liquidity index, This means users will be able to withdraw more crvUSD than they should be entitled to, as their incorrectly high RToken balance gets scaled up by the liquidity index.

PoC

  1. Alice deposits 100 crvUSD when liquidity index = 1.1 * RAY

  2. Protocol incorrectly mints 100 RTokens instead of 90.90 RTokens

  3. Time passes, liquidity index increases to 1.2 * RAY

  4. Alice withdraws with 100 RTokens

  5. Alice receives 120 crvUSD instead of the correct 109.08 crvUSD

  6. Protocol loses 10.92 crvUSD to Alice due to incorrect minting

Impact

Protocol becomes insolvent as users can withdraw more assets than they deposited plus legitimate interest.

Tools Used

Manual code review

Recommendations

Correct the minting in RToken to use the scaled amount:

_mint(onBehalfOf, amountScaled.toUint128());
Updates

Lead Judging Commences

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