Core Contracts

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

Incorrect collateral liquidated in `LiquidationFinalized` event due to `getUserCollateralValue` call after NFT deletion

Summary

In contracts::core::pools::LendingPool::LendingPool.sol, the finalizeLiquidation function emits the LiquidationFinalized event, including the user's collateral liquidated, after deleting user.nftTokenIds. This results in getUserCollateralValue(userAddress) returning 0, misleading off-chain services relying on event emission.

Vulnerability Details

In finalizeLiquidation, the function processes liquidation by transferring NFTs, updating debt balances, and emitting the LiquidationFinalized event. The last parameter of this event is collateralLiquidated, which is the amount of collateral liquidated. Therefore, the function calls getUserCollateralValue(userAddress), which iterates over user.nftTokenIds to compute collateral value. However, since getUserCollateralValue(userAddress) is called after user.nftTokenIds has been deleted, the function returns 0 instead of the actual collateral value.

In contracts/core/pools/LendingPool/LendingPool.sol#L511-L535:

isUnderLiquidation[userAddress] = false;
liquidationStartTime[userAddress] = 0;
// 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; // <-- Causes incorrect event data
.
.
.
// Call getUserCollateralValue after deleting NFT data
emit LiquidationFinalized(stabilityPool, userAddress, userDebt, getUserCollateralValue(userAddress));

Since getUserCollateralValue retrieves NFT prices by iterating user.nftTokenIds, an empty array results in a 0 collateral value in the emitted event, leading to inaccurate data.

In contracts/core/pools/LendingPool/LendingPool.sol#L561-L572:

function getUserCollateralValue(address userAddress) public view returns (uint256) {
UserData storage user = userData[userAddress];
uint256 totalValue = 0;
@> for (uint256 i = 0; i < user.nftTokenIds.length; i++) {
uint256 tokenId = user.nftTokenIds[i];
uint256 price = getNFTPrice(tokenId);
totalValue += price;
}
return totalValue;
}

Impact

Frontend or other off-chain services may display incorrect values, potentially misleading users.

Tools Used

Manual Review

Recommendations

Capture the user's collateral value before deleting user.nftTokenIds:

// 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);
}
+ uint256 collateralValue = getUserCollateralValue(userAddress);
delete user.nftTokenIds; // Delete after storing collateral value
.
.
.
- emit LiquidationFinalized(stabilityPool, userAddress, userDebt, getUserCollateralValue(userAddress));
+ emit LiquidationFinalized(stabilityPool, userAddress, userDebt, collateralValue);
Updates

Lead Judging Commences

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

LendingPool::finalizeLiquidation emits 0 collateralLiquidated because it deletes the info required to compute it

Support

FAQs

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