Core Contracts

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

RAACNft::mint() sent ERC20 after mint are locked forever on the contract

Summary

The RAACTNFT.sol allows users to mint NFT by sending an ERC20 to the contract while minting, and get a NFT that represent a house property.

The issue is that once this is done by calling mint the sent ERC20 are locked forever in the contract.

token.safeTransferFrom(msg.sender, address(this), _amount); //ERC20 is sent to the contract

In the RAAC protocol, we can see in several places that the main token that will be used is crvUSD. This token can be locked in a vault and generate yields for the protocol. This mechanism is not doable with locked tokens on the contract, and they are indeed lost forever as there is no way to retrieve them. Contract is not upgradable.

Vulnerability Details

  1. RAAC Team deployed RAACNft.sol (crvUSD is set as payment token) and and RAACHousePrices

  2. Oracle is called and some houses (tokenId) have a set price in RAACHousePrices

  3. Users start to buy houses by calling RAACNft::mint(tokenId, amount), transferring crvUSD to RAACNft contract.

  4. RAAC teams want to retrieve those crvUSD, but cannot as there is no function for that

Impact

All ERC20 sent to RAACNFT contract while minting will be locked forever. RAAC team cannot get it back to repay previous owners of those houses or the mortgage they process to own those houses and sell them on-chain using NFT.
Risks of insolvency of the protocol/company, rendering those NFT worthless and incurring legal implications for NFT owner vs bank or previous home owner.

Tools Used

Manual

Recommendations

There should be a function to retrieve funds on RAACNFT, only callable by ADMIN/OWNER

function retrieveAllTokens(address token) external onlyOwner {
uint256 contractBalance = IERC20(token).balanceOf(address(this));
if (contractBalance > 0) {
bool success = IERC20(token).safeTransfer(msg.sender, contractBalance); //or another address
if (!success) revert TransferFailed();
}
}

OR change the mint function to send tokens to a treasury address instead of RAACToken :

function mint(uint256 _tokenId, uint256 _amount) public override {
uint256 price = raac_hp.tokenToHousePrice(_tokenId);
if(price == 0) { revert RAACNFT__HousePrice(); }
if(price > _amount) { revert RAACNFT__InsufficientFundsMint(); }
// transfer erc20 from user to contract - requires pre-approval from user
- token.safeTransferFrom(msg.sender, address(this), _amount);
+ token.safeTransferFrom(msg.sender, address(treasury), price); //it's possible to directly send the proper price
// mint tokenId to user
_safeMint(msg.sender, _tokenId);
- // If user approved more than necessary, refund the difference -> this is not ideal, 2 transfers for same result
- if (_amount > price) {
- uint256 refundAmount = _amount - price;
- token.safeTransfer(msg.sender, refundAmount);
- }
emit NFTMinted(msg.sender, _tokenId, price);
}
Updates

Lead Judging Commences

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

RAACNFT collects payment for NFT minting but lacks withdrawal functionality, permanently locking all tokens in the contract

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

RAACNFT collects payment for NFT minting but lacks withdrawal functionality, permanently locking all tokens in the contract

Support

FAQs

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

Give us feedback!