Core Contracts

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

`LendingPool::borrow` is increasing the `user.scaledDebtBalance`by the wrong amount

Summary

The LendingPool keeps an internal representation of the user scaled debt balance, which is updated during borrows and repays.
The issue is that that value forgets to factor in the debt increase due to the debt index.

Vulnerability details

In DebtToken::mint, the user debt balance is increased by the new amount borrowed + the increase due to the borrowing rate (index):

File: contracts/core/tokens/DebtToken.sol
136: function mint(
137: address user,
138: address onBehalfOf,
139: uint256 amount,
140: uint256 index
...:
...: //* ----------------- some code ----------------- *//
...:
153: uint256 balanceIncrease = 0;
154: if (_userState[onBehalfOf].index != 0 && _userState[onBehalfOf].index < index) {
155: balanceIncrease = scaledBalance.rayMul(index) - scaledBalance.rayMul(_userState[onBehalfOf].index); <@(1) compute increase in debt due to index
156: }
157:
158: _userState[onBehalfOf].index = index.toUint128();
159:
160: uint256 amountToMint = amount + balanceIncrease; <@(2) add the balance increase to the debt to mint
161:
162: _mint(onBehalfOf, amountToMint.toUint128()); <@(3) mint it
163:
164: emit Transfer(address(0), onBehalfOf, amountToMint);
165: emit Mint(user, onBehalfOf, amountToMint, balanceIncrease, index);
166:
167: return (scaledBalance == 0, amountToMint, totalSupply()); <@(4) returns `amountToMint`
168: }

But then in LendingPool::borrow, the userScaledDebtBalance is only increased by scaledAmount which do not contain the debt increase due to the borrow index:

File: contracts/core/pools/LendingPool/LendingPool.sol
325: function borrow(uint256 amount) external nonReentrant whenNotPaused onlyValidAmount(amount) {
...:
...: //* ----------------- some code ----------------- *//
...:
347:
348: // Update user's scaled debt balance
349: uint256 scaledAmount = amount.rayDiv(reserve.usageIndex);
350:
351:
352: // Mint DebtTokens to the user (scaled amount) <@ calls(1) `DebtToken::mint`
353: (bool isFirstMint, uint256 amountMinted, uint256 newTotalSupply) = IDebtToken(reserve.reserveDebtTokenAddress).mint(msg.sender, msg.sender, amount, reserve.usageIndex);
354:
355: // Transfer borrowed amount to user
356: IRToken(reserve.reserveRTokenAddress).transferAsset(msg.sender, amount);
357:
358: user.scaledDebtBalance += scaledAmount; <@(2) increase user debt by borrowed amount rather than `amountMinted`

Impact

Wrong user debt value stored in LedingPool

Recommended Mitigation Steps

function borrow(uint256 amount) external nonReentrant whenNotPaused onlyValidAmount(amount) {
//* ----------------- some code ----------------- *//
// Mint DebtTokens to the user (scaled amount)
(bool isFirstMint, uint256 amountMinted, uint256 newTotalSupply) = IDebtToken(reserve.reserveDebtTokenAddress).mint(msg.sender, msg.sender, amount, reserve.usageIndex);
// Transfer borrowed amount to user
IRToken(reserve.reserveRTokenAddress).transferAsset(msg.sender, amount);
+ uint256 scaledAmountMinted = amountMinted.rayDiv(reserve.usageIndex)
- user.scaledDebtBalance += scaledAmount;
+ user.scaledDebtBalance += scaledAmountMinted;
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.