Core Contracts

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

Decimal precision mismatch in NFT minting causes incorrect pricing

Summary

The mint function in RAACNFT contract fails to handle decimal precision differences between the oracle-provided house prices and the payment token (USDC), leading to incorrect price calculations during NFT minting.

Vulnerability Details

In RAACNFT.sol, prices are compared directly without decimal adjustment:

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);
// ... rest of the function
}

Stable coins can have different decimals. I.e: USDC/USDT 6 decimals, DAI 18 decimals, etc.

The oracle basically set the returned value into tokenToHousePrice.

function setHousePrice(
uint256 _tokenId,
uint256 _amount
) external onlyOracle {
tokenToHousePrice[_tokenId] = _amount;
// ...
}

The key issues are:

  • Direct comparison between 18 decimal and 6 decimal numbers

  • No decimal normalization before price comparison

  • Incorrect transfer amount calculation

  • Potential for massive overpayment or underpayment

Impact

Users can pay exponentially more, i.e: 1000000000000x (10^12) than intended for minting NFTs due to decimal precision mismatch. This could lead to significant financial losses as house prices are substantial values.

Tools Used

Manual Review

Recommendations

Normalize the token amount before pulling the tokens from the user.

/// @notice Normalizes token amounts between different decimal bases
/// @param amount The amount to normalize
/// @param decimalsFrom The decimal places of the input amount
/// @param decimalsTo The target decimal places
/// @return The normalized amount
function _normalizeDecimals(
uint256 amount,
uint8 decimalsFrom,
uint8 decimalsTo
) internal pure returns (uint256) {
if (decimalsFrom == decimalsTo) {
return amount;
}
if (decimalsFrom < decimalsTo) {
return amount * (10 ** (decimalsTo - decimalsFrom));
}
return amount / (10 ** (decimalsFrom - decimalsTo));
}
function mint(uint256 _tokenId, uint256 _amount) public override {
uint256 price = raac_hp.tokenToHousePrice(_tokenId);
if(price == 0) { revert RAACNFT__HousePrice(); }
// Normalize user input amount to 18 decimals for comparison
uint256 normalizedAmount = _normalizeDecimals(
_amount,
token.decimals(),
18 // Oracle decimals
);
if(price > normalizedAmount) { revert RAACNFT__InsufficientFundsMint(); }
...
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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