Core Contracts

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

Incorrect collateral check in NFT withdrawal allows under-collateralized withdrawals

Summary

In the withdrawNFT function, the contract checks whether removing an NFT would leave the user undercollateralized by comparing the remaining collateral value to a percentage of the user’s debt. However, the check uses a percentage multiplication on the debt value (via userDebt.percentMul(liquidationThreshold)) that inadvertently lowers the effective debt threshold. This miscalculation permits withdrawals that should be disallowed, allowing users to remove NFTs even when doing so would put them under the required collateralization ratio. As a result, users may extract NFT collateral in scenarios where they should remain overcollateralized, jeopardizing the protocol’s solvency and exposing the system to potential liquidation or denial-of-service risks.

Vulnerability Details

Incorrect Collateral Validation in withdrawNFT

The function includes the following check to prevent withdrawals that would leave the user undercollateralized:

if (collateralValue - nftValue < userDebt.percentMul(liquidationThreshold)) {
revert WithdrawalWouldLeaveUserUnderCollateralized();
}
  • Intended Logic:
    The idea is to ensure that after removing the NFT, the user’s remaining collateral (collateralValue minus the value of the NFT being withdrawn) remains at least 80% of the user’s debt.

  • Issue with Calculation:*
    The expression userDebt.percentMul(liquidationThreshold) effectively scales down the user's debt. For example, if the user's debt is 950 and the liquidation threshold is 80% (expressed as 8000 in basis points, assuming a factor of 10,000), the calculation would yield a threshold value of approximately 760. This reduced threshold means that the contract permits the withdrawal if the remaining collateral is above 760—even if the actual risk level should be higher.

Example Scenario

  • User Collateral: The user holds 10 NFTs valued at 100 each, giving a total collateral of 1000.

  • NFT to Withdraw: Withdrawing one NFT worth 100 would leave the user with a remaining collateral of 900.

  • User Debt: Suppose the user's total debt is 950.

The correct approach would require the remaining collateral to be at least 80% of the debt (which would be 760 if computed correctly, but the intended calculation should prevent unsafe withdrawals). Due to the bug, the check compares 900 (remaining collateral) against a reduced threshold (approximately 760), thereby allowing the withdrawal even when it might render the user undercollateralized.

Impact

  • Undercollateralized Withdrawals:
    Users can withdraw NFTs even when doing so causes their remaining collateral to fall below safe levels. This allows users to extract collateral improperly, undermining the intended risk management.

  • Protocol Insolvency Risk:
    Permitting undercollateralized states can lead to a situation where users’ positions are not adequately secured. This increases the risk of defaults and insolvency of the reserve, as the remaining collateral would be insufficient to cover the outstanding debt.

  • Economic Exploitation:
    Malicious users may exploit this flaw to withdraw valuable NFTs from the protocol, effectively reducing the overall collateral backing the debt. This can lead to cascading liquidations or force the protocol to absorb losses.

  • Denial of Service (DoS):
    In the long run, if many users take advantage of this bug, it may lead to severe undercollateralization across the system. This could force emergency shutdowns or trigger liquidation mechanisms that prevent further withdrawals, effectively creating a DoS for users trying to access their funds.

Tools Used

Manual review

Recommendations

Correct the Collateral Check:
Adjust the withdrawal check in withdrawNFT so that it accurately reflects the collateral requirement. Instead of applying percentMul directly on userDebt, consider aligning the check with the calculation in the calculateHealthFactor function:

uint256 collateralThreshold = collateralValue.percentMul(liquidationThreshold);
// Ensure (collateralValue - nftValue) remains above the adjusted debt threshold:
if ((collateralValue - nftValue) < collateralThreshold) {
revert WithdrawalWouldLeaveUserUnderCollateralized();
}

Alternatively, rework the logic to calculate a health factor and ensure it stays above 1 after the NFT is withdrawn.

Updates

Lead Judging Commences

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

LendingPool::borrow as well as withdrawNFT() reverses collateralization check, comparing collateral < debt*0.8 instead of collateral*0.8 > debt, allowing 125% borrowing vs intended 80%

Support

FAQs

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