Core Contracts

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

Repeated NFT Liquidation Allows IndexToken Inflation

Summary

The liquidateNFT function in the NFTLiquidator contract allows multiple liquidations of the same NFT tokenId, each time re-minting IndexToken for the same debt. This unguarded re-entry inflates the IndexToken supply and undermines the auction system.

Vulnerability Details

The code snippet never checks whether an NFT was already liquidated. Each call to liquidateNFT(tokenId, debt) executes the same steps: transferring the NFT to the contract, updating its tokenData, and minting new IndexToken to the stabilityPool.

This design breaks the expected guarantee a tokenId can only be liquidated once. If an attacker or compromised stabilityPool calls liquidateNFT repeatedly for the same tokenId, it triggers unlimited inflation of IndexToken and overwrites any existing auction data for that NFT. This vulnerability depends on unbounded re-calls, where the malicious input is simply a second or third liquidation request for a token already seized.

Impact

I've rated this as a Medium because it leads to economic damage by inflating IndexToken and corrupting the liquidation mechanism. An attacker can exploit it to acquire repeated tokens for the same debt and break the protocol’s reliability in handling NFT collateral. The likelihood is Medium if the stabilityPool can freely invoke liquidateNFT for arbitrary tokenIds. Repeated calls require no special conditions besides reusing the same tokenId.

Tools Used

Manual Review

Recommendations

Add a one-time check to ensure each NFT tokenId is only liquidated once:

function liquidateNFT(uint256 tokenId, uint256 debt) external {
require(!_alreadyLiquidated[tokenId], "NFT already liquidated");
_alreadyLiquidated[tokenId] = true;
nftContract.transferFrom(msg.sender, address(this), tokenId);
tokenData[tokenId] = TokenData({
debt: debt,
auctionEndTime: block.timestamp + 3 days,
highestBid: 0,
highestBidder: address(0)
});
indexToken.mint(stabilityPool, debt);
emit NFTLiquidated(tokenId, debt);
emit AuctionStarted(tokenId, debt, tokenData[tokenId].auctionEndTime);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Out of scope

Support

FAQs

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