Core Contracts

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

NFTs permanently locked in StabilityPool after liquidation due to missing approve/transfer functionality

Summary

After successful liquidation, NFTs are transferred to the StabilityPool contract which lacks any functionality to transfer them out, resulting in permanently locked NFTs.

Vulnerability Details

When liquidating a borrower, by calling liquidateBorrower() function, the LendingPool::finalizeLiquidation function is called, which performs some additional actions to close the user's position, and also transfers his NFTs to the StabilityPool:

// 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);
}

However, the StabilityPool contract has no functionality to transfer out or approve other addresses to transfer the NFTs once they are received. This means that after liquidation, the NFTs become permanently locked in the contract.

Proof of Concept

  1. User deposits NFTs as collateral in LendingPool

  2. User's position becomes undercollateralized and liquidation is initiated

  3. After grace period expires, StabilityPool calls LendingPool::finalizeLiquidation

  4. NFTs are transferred to StabilityPool

  5. NFTs are now permanently locked as StabilityPool has no functions to transfer or approve them

Impact

All NFTs that are transferred to the StabilityPool after liquidation become permanently locked with no way to recover them. This represents a complete loss of value for those assets.

Recommendations

  1. Add NFT transfer functionality to StabilityPool:

function transferNFT(uint256 tokenId, address to) external onlyOwner {
raacNFT.transferFrom(address(this), to, tokenId);
}
  1. Add NFT approval functionality:

function approveNFT(address operator, uint256 tokenId) external onlyOwner {
raacNFT.approve(operator, tokenId);
}
Updates

Lead Judging Commences

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