Core Contracts

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

Incorrect parameter handling in debt repayment and finalize liquidation functions leads to wrong asset transfer amount

Summary

In the LendingPool contract, both _repay and finalizeLiquidation functions incorrectly handle the return values from DebtToken's burn function, specifically interchanging amountScaled and amountBurned. This causes the wrong amount of underlying asset (crvUSD) to be transferred to the lending pool during debt repayment and liquidation processes.

Vulnerability Details

The issue stems from the mishandling of return values from the DebtToken's burn function in two critical operations: debt repayment and liquidation.

Let's look at how DebtToken's burn function works:

function burn(
address from,
uint256 amount,
uint256 index
) external override onlyReservePool returns (uint256, uint256, uint256, uint256) {
// ... other logic ...
uint256 amountScaled = amount.rayDiv(index);
_burn(from, amount.toUint128());
emit Burn(from, amountScaled, index);
// @audit plz note this order of parameters
return (amount, totalSupply(), amountScaled, balanceIncrease);
}

The burn function returns four values in this order:

  1. amount: The amount of DebtTokens (amount burned)

  2. totalSupply: The new total supply after burning

  3. amountScaled: The scaled amount (amount/index)

  4. balanceIncrease: Any increase in balance due to interest

However, in the LendingPool's _repay function:

(
uint256 amountScaled, // @audit Should be 'amountBurned'
uint256 newTotalSupply,
uint256 amountBurned, // @audit Should be 'amountScaled'
uint256 balanceIncrease
) = IDebtToken(reserve.reserveDebtTokenAddress).burn(
onBehalfOf,
amount,
reserve.usageIndex
);
// @audit Transfer based on wrong amount
IERC20(reserve.reserveAssetAddress).safeTransferFrom(
msg.sender,
reserve.reserveRTokenAddress,
amountScaled // @audit Using wrong value
);

The same issue exists in finalizeLiquidation:

function finalizeLiquidation(address userAddress) external nonReentrant onlyStabilityPool {
// ...
(
uint256 amountScaled, // @audit Should be 'amountBurned'
uint256 newTotalSupply,
uint256 amountBurned, // Should be 'amountScaled'
uint256 balanceIncrease
) = IDebtToken(reserve.reserveDebtTokenAddress).burn(
userAddress,
userDebt,
reserve.usageIndex
);

The issue arises because:

  1. The return values are incorrectly mapped - amountScaled is capturing the original amount, while amountBurned is capturing the scaled amount

  2. The asset transfer uses amountScaled (which actually contains the original amount) instead of the properly scaled amount

  3. This leads to incorrect asset transfers during both repayment and liquidation processes

Impact

  • Incorrect amount of assets being transferred during debt repayments and liquidations

  • Protocol accounting becomes inaccurate

  • Liquidations may transfer wrong amounts, affecting both the protocol and users

Tools Used

Manual Review

Recommendations

Correct the parameter interchange in both _repay and finalizeLiquidation functions:

(
uint256 amountBurned, // fixed
uint256 newTotalSupply,
uint256 amountScaled, // fixed
uint256 balanceIncrease
) = IDebtToken(reserve.reserveDebtTokenAddress).burn(
onBehalfOf,
amount,
reserve.usageIndex
);
IERC20(reserve.reserveAssetAddress).safeTransferFrom(
msg.sender,
reserve.reserveRTokenAddress,
amountScaled
);
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

DebtToken::burn calculates balanceIncrease (interest) but never applies it, allowing borrowers to repay loans without paying accrued interest

Interest IS applied through the balanceOf() mechanism. The separate balanceIncrease calculation is redundant/wrong. Users pay full debt including interest via userBalance capping.

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

DebtToken::burn calculates balanceIncrease (interest) but never applies it, allowing borrowers to repay loans without paying accrued interest

Interest IS applied through the balanceOf() mechanism. The separate balanceIncrease calculation is redundant/wrong. Users pay full debt including interest via userBalance capping.

Support

FAQs

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

Give us feedback!