Core Contracts

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

_safeMint is in the wrong position, there is a risk of reentrancy vulnerability

Summary

_safeMint is in the wrong position, there is a risk of reentrancy vulnerability.

Vulnerability Details

https://github.com/Cyfrin/2025-02-raac/blob/main/contracts/core/tokens/RAACNFT.sol#L32-L50

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);
// mint tokenId to user
_safeMint(msg.sender, _tokenId);
// If user approved more than necessary, refund the difference
if (_amount > price) {
uint256 refundAmount = _amount - price;
token.safeTransfer(msg.sender, refundAmount);
}
emit NFTMinted(msg.sender, _tokenId, price);
}
function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
_mint(to, tokenId);
_checkOnERC721Received(address(0), to, tokenId, data);
}

From the _checkOnERC721Received() function, when calling the IERC721Receiver(to).onERC721Received function, the unknown address to is called. At this time, if to is a contract address, we call the mint() function of the attacked contract again in the onERC721Received() function of the contract to complete the reentrancy.

Impact

Without a mechanism to prevent reentrancy attacks, a malicious contract could call the mint function multiple times, causing additional NFTs to be minted or user funds to be improperly processed.

Tools Used

Manual review

Recommendations

All conditions should be checked and status updated before interacting with external contracts:

_safeMint is placed at the bottom of the mint function

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);
// If user approved more than necessary, refund the difference
if (_amount > price) {
uint256 refundAmount = _amount - price;
token.safeTransfer(msg.sender, refundAmount);
}
// mint tokenId to user
_safeMint(msg.sender, _tokenId);
emit NFTMinted(msg.sender, _tokenId, price);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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