Core Contracts

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

Users can receive incorrect refund amounts during NFT minting, even when the house price oracle provides valid data.

Summary

RAACNFT's mint function has a refund calculation issue where users can receive incorrect refund amounts. When users provide more funds than the house price, the refund mechanism fails to properly validate the arithmetic, potentially leading to economic imbalances in the protocol.

From the RAACNFT.sol contract, the mint function includes a refund mechanism as follows.

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);
}

The contract calculates refunds using simple subtraction (_amount - price) without considering edge cases. But this way the user's final balance doesn't always match the expected balance after refund.

Expected

  • If amount > price: User should receive (amount - price) as refund

  • If amount ≤ price: User should pay exactly the price

The root cause stems from the refund calculation not being properly bounded. While the contract checks if price > amount, it doesn't validate the refund arithmetic against the user's initial balance.

Vulnerability Details

The protocol's minting mechanism includes a clever refund system, if you send more funds than the house price, you get the difference back. However, there's an interesting edge case in this process.

When a user wants to mint an RAAC NFT, they call the mint function with two parameters:

  • tokenId: representing the specific property

  • amount: the funds they're sending

The contract then:

  1. Checks the house price from the oracle

  2. Verifies the sent amount is sufficient

  3. Processes the payment

  4. Handles any refund

You see how the refund calculation uses simple subtraction: function mint

function mint(uint256 _tokenId, uint256 _amount) public override {
// S1: Price Check
uint256 price = raac_hp.tokenToHousePrice(_tokenId);
// Validates house exists in oracle ✓
if(price == 0) { revert RAACNFT__HousePrice(); }
// Validates sufficient payment ✓
if(price > _amount) { revert RAACNFT__InsufficientFundsMint(); }
// S2: Payment Processing
// ! Critical Point HERE: Transfers full _amount, not just price
token.safeTransferFrom(msg.sender, address(this), _amount);
// S3: NFT Minting
_safeMint(msg.sender, _tokenId);
// S4: Refund Logic - Vulnerability Surface
if (_amount > price) {
// ! Vulnerability HERE: No bounds checking on refund calculation
uint256 refundAmount = _amount - price;
// ! Potential issue HERE: Refunds full difference without validation
token.safeTransfer(msg.sender, refundAmount);
}
// S5: Event Emission
emit NFTMinted(msg.sender, _tokenId, price);
}

You see the issue is in assuming this straightforward calculation will always produce the correct economic outcome. However the final balance doesn't always match what we'd expect. In certain scenarios, users could receive incorrect refund amounts, disrupting the protocol's economic assumptions about house price payments.

mathint expectedBalance = amount > price ?
to_mathint(balanceBefore) - to_mathint(price) :
to_mathint(balanceBefore) - to_mathint(amount);

Impact

if (amount > price) {
uint256 refundAmount = amount - price;
token.safeTransfer(msg.sender, refundAmount);
}

When users mint an NFT representing a $500,000 house by sending 600,000 RAAC tokens, the refund mechanism could potentially return incorrect amounts, disrupting the protocol's careful economic balance.

mathint expectedBalance = amount > price ?
to_mathint(balanceBefore) - to_mathint(price) :
to_mathint(balanceBefore) - to_mathint(amount);

Like a real estate escrow system where the excess deposit isn't properly reconciled. This vulnerability directly impacts RAAC's core value proposition of maintaining precise 1:1 relationships between on-chain tokens and real-world property values.

Tools Used

manual

Recommendations

We need to put in some solid checks before we process any refunds. That means making sure all stay within safe limits and that there’s enough balance in the account to cover what’s going out.

function mint(uint256 _tokenId, uint256 _amount) public override {
// S1: Price and Balance Validation
uint256 price = raac_hp.tokenToHousePrice(_tokenId);
if(price == 0) { revert RAACNFT__HousePrice(); }
// Check user's actual token balance first
uint256 userBalance = token.balanceOf(msg.sender);
require(_amount <= userBalance, "Amount exceeds balance");
if(price > _amount) { revert RAACNFT__InsufficientFundsMint(); }
// Optimized Payment Processing
// Transfer exact price amount instead of full amount
token.safeTransferFrom(msg.sender, address(this), price);
// NFT Minting
_safeMint(msg.sender, _tokenId);
// Enhanced Refund Logic
if (_amount > price) {
// Safe refund calculation with bounds check
uint256 refundAmount = _amount - price;
require(refundAmount <= _amount, "Invalid refund amount");
require(refundAmount == _amount - price, "Refund calculation error");
// Verify contract has sufficient balance for refund
require(token.balanceOf(address(this)) >= refundAmount, "Insufficient contract balance");
token.safeTransfer(msg.sender, refundAmount);
}
emit NFTMinted(msg.sender, _tokenId, price);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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

Give us feedback!