Core Contracts

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

Inability to Handle NFTs in Stability Pool After Liquidation

Summary

The finalizeLiquidation function in the LendingPool contract transfers NFTs to the Stability Pool during liquidation. However, the StabilityPool.sol contract lacks functionality to manage or transfer out these NFTs, leading to potential asset lockup and protocol inefficiencies.


Vulnerability Details

Explanation

The finalizeLiquidation function is designed to handle the liquidation process for a user who has defaulted on their debt. During this process, liquidateBorrower is called from the StabilityPool contract, which calls finalizeLiquidation, the user's NFTs are transferred to the Stability Pool as part of the collateral recovery mechanism. However, the Stability Pool contract does not have any functionality to manage or transfer these NFTs after they are received. This creates a critical issue where NFTs are effectively locked in the Stability Pool, rendering them inaccessible and unusable.

Root Cause in the Contract Function

In the finalizeLiquidation function, the following code transfers NFTs to the Stability Pool:

for (uint256 i = 0; i < user.nftTokenIds.length; i++) {
uint256 tokenId = user.nftTokenIds[i];
user.depositedNFTs[tokenId] = false;
raacNFT.transferFrom(address(this), stabilityPool, tokenId);
}

While this code successfully transfers the NFTs to the Stability Pool, the Stability Pool contract does not implement any mechanism to:

  1. Store or track the received NFTs.

  2. Transfer or redistribute the NFTs to other users or back to the protocol.

  3. Sell or liquidate the NFTs to recover value for the protocol.

This oversight results in NFTs being permanently locked in the Stability Pool, leading to:

  • Loss of Value: The protocol cannot recover or utilize the value of the locked NFTs.

  • Inefficient Asset Management: The Stability Pool becomes a dead end for NFTs, reducing the overall efficiency of the liquidation process.


Proof of Concept

Scenario Example

  1. User Defaults: A user defaults on their debt, triggering the liquidation process.

  2. NFTs Transferred to Stability Pool: The StabilityPool::liquidateBorrower is called, which then calls LendingPool::finalizeLiquidation function that transfers the user's NFTs to the Stability Pool.

  3. NFTs Locked: The Stability Pool has no functionality to manage or transfer the NFTs, causing them to be permanently locked.

Code

The vulnerability is demonstrated in the finalizeLiquidation function:

function finalizeLiquidation(address userAddress) external nonReentrant onlyStabilityPool {
// ... (other logic)
// Transfer NFTs to Stability Pool
for (uint256 i = 0; i < user.nftTokenIds.length; i++) {
uint256 tokenId = user.nftTokenIds[i];
user.depositedNFTs[tokenId] = false;
raacNFT.transferFrom(address(this), stabilityPool, tokenId);
}
delete user.nftTokenIds;
// ... (other logic)
}

This code transfers NFTs to the Stability Pool but does not address how the Stability Pool will handle them afterward.


Impact

  • Asset Lockup: NFTs transferred to the Stability Pool are permanently locked, leading to a loss of value for the protocol.

  • Financial Loss: The protocol cannot recover the value of locked NFTs, leading to potential financial losses.


Tools Used

  • Manual Code Review: The vulnerability was identified through a manual review of the finalizeLiquidation function and the StabilityPool.sol contract.

  • Solidity: The smart contract language used to write the LendingPool and StabilityPool contracts.


Recommendations

Implement NFT Management in Stability Pool:

  • Add functionality to the Stability Pool contract to store, track, and manage NFTs received during liquidation.

  • Implement functions to transfer or redistribute NFTs to other users, or sell or back to the protocol.

Example:

function transferNFT(address to, uint256 tokenId) external onlyManagerOrOwner nonReentrant whenNotPaused {
raacNFT.transferFrom(address(this), to, tokenId);
}
Updates

Lead Judging Commences

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

Liquidated RAACNFTs are sent to the StabilityPool by LendingPool::finalizeLiquidation where they get stuck

Support

FAQs

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