Core Contracts

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

Incorrect Payment Handling in RAACNFT Minting Allows Token Loss

Summary

The RAACNFT minting system contains vulnerability in its payment handling mechanism. When users mint NFTs representing real estate assets, the contract fails to properly validate final token balances after processing payments and refunds. This creates a potential discrepancy between the house price and actual tokens retained.

// RAACNFT.sol
function mint(uint256 _tokenId, uint256 _amount) public override {
// Price check exists but no validation of final contract balance
uint256 price = raac_hp.tokenToHousePrice(_tokenId);
if(price == 0) { revert RAACNFT__HousePrice(); }
if(price > _amount) { revert RAACNFT__InsufficientFundsMint(); }
// No tracking of initial balance before transfers
token.safeTransferFrom(msg.sender, address(this), _amount);
_safeMint(msg.sender, _tokenId);
// Refund logic lacks final balance verification
if (_amount > price) {
uint256 refundAmount = _amount - price;
token.safeTransfer(msg.sender, refundAmount);
}
// Missing validation that contract retained exactly 'price' tokens
emit NFTMinted(msg.sender, _tokenId, price);
}
// RToken.sol
function mint(
address caller,
address onBehalfOf,
uint256 amountToMint,
uint256 index
) external override onlyReservePool returns (bool, uint256, uint256, uint256) {
// Zero amount check exists but no maximum cap
if (amountToMint == 0) {
return (false, 0, 0, 0);
}
// Potential precision loss in rayDiv calculation
uint256 amountScaled = amountToMint.rayDiv(index);
if (amountScaled == 0) revert InvalidAmount();
// Balance tracking with index updates
uint256 scaledBalance = balanceOf(onBehalfOf);
bool isFirstMint = scaledBalance == 0;
uint256 balanceIncrease = 0;
// Complex index-based balance calculation
if (_userState[onBehalfOf].index != 0 && _userState[onBehalfOf].index < index) {
balanceIncrease = scaledBalance.rayMul(index) - scaledBalance.rayMul(_userState[onBehalfOf].index);
}
_userState[onBehalfOf].index = index.toUint128();
// Potential overflow in uint128 cast
_mint(onBehalfOf, amountToMint.toUint128());
return (isFirstMint, amountToMint, totalSupply(), amountScaled);
}

This vulnerability affects the fundamental tokenization mechanism of real estate assets. Incorrect token handling could lead to NFTs backed by incorrect collateral amounts, breaking the protocol's economic model.

Vulnerability Details

The vulnerability centers on the mint() function in RAACNFT.sol where payment validation occurs:

token.safeTransferFrom(msg.sender, address(this), _amount);
_safeMint(msg.sender, _tokenId);
if (_amount > price) {
uint256 refundAmount = _amount - price;
token.safeTransfer(msg.sender, refundAmount);
}

We can see the contract lacks atomic validation that exactly price tokens remain after the operation completes.

Attack Flow

  1. Attacker calls mint() with an amount larger than the house price

  2. Contract transfers full amount from user

  3. Contract mints NFT

  4. During refund, contract fails to verify final balance matches house price

  5. Potential token loss occurs due to incorrect balance retention

The mint function processes transfers and refunds without ensuring the contract retains exactly the house price amount. This breaks the core invariant that each NFT must be backed by its precise house price in tokens.

Think of the RAACNFT minting system like a real estate escrow process. When buying a house, the exact purchase price must be transferred no more, no less. However, the current implementation is like an escrow agent who doesn't verify the final balance after processing refunds.

Let's say A user attempts to mint an NFT representing a $500,000 house. They approve 600,000 RAAC tokens, expecting a 100,000 token refund. The contract accepts the payment, mints the NFT, and attempts the refund but never validates that exactly 500,000 tokens remain locked as collateral.

Technical Deep Dive The mint() function in RAACNFT.sol handles three critical operations

// First, collect full payment
token.safeTransferFrom(msg.sender, address(this), _amount);
// Second, mint the NFT
_safeMint(msg.sender, _tokenId);
// Finally, process any refund
if (_amount > price) {
token.safeTransfer(msg.sender, _amount - price);
}

Just as a bank reconciles its ledger after every transaction, the contract should verify its final token balance matches the house price exactly. Instead, it assumes the transfers worked correctly without verification.

We can see this isn't just about incorrect balances, it breaks the fundamental premise of tokenized real estate. Each RAACNFT must be backed by exactly its corresponding house value in RAAC tokens. Any discrepancy undermines the entire protocol's economic model and could affect lending, liquidations, and price discovery.

Impact

The contract fails to properly validate that the contract retains exactly the house price after refunding excess payment. This could lead to:

  • Incorrect token balance retention

  • Potential loss of funds during refund process

mint(tokenId, amount) ->
token.safeTransferFrom(user, contract, amount) ->
_safeMint(user, tokenId) ->
token.safeTransfer(user, refundAmount)

The key issue is in the payment handling

// Potential issue with payment amount validation
if (_amount > price) {
uint256 refundAmount = _amount - price;
token.safeTransfer(msg.sender, refundAmount);
}

Recommendations

The solution requires adding balance validation that acts like a final escrow check

function mint(uint256 _tokenId, uint256 _amount) public {
uint256 price = raac_hp.tokenToHousePrice(_tokenId);
uint256 initialBalance = token.balanceOf(address(this));
token.safeTransferFrom(msg.sender, address(this), _amount);
_safeMint(msg.sender, _tokenId);
if (_amount > price) {
token.safeTransfer(msg.sender, _amount - price);
}
require(token.balanceOf(address(this)) == initialBalance + price,
"Final balance must match house price");
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 2 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.