Core Contracts

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

Severe Overpayment in Auction Due to Decimal Mismatch

Summary

The buy() function in the Auction.sol contract contains a critical decimal mismatch between ZENO (18 decimals) and USDC (6 decimals). This causes buyers to massively overpay for their purchases. Specifically, when a user calls buy(1), they are only receiving 1 wei of ZENO (10⁻¹⁸ ZENO) while still paying 100 USDC.

A major concern is that the test cases passed despite this issue because the test inputted amount = 1, which was interpreted as 1 wei rather than 1 full ZENO token. Since the contract successfully minted 1 wei, the test did not catch the massive overpayment issue, leading to a dangerously misleading outcome.

Vulnerability Details

Root Cause

The issue arises from the incorrect cost calculation in buy():

Auction.sol#L87

uint256 cost = price * amount;

  • price is denominated in USDC’s 6-decimal format (e.g., 100 USDC = 100000000 as reflected in the test).

  • amount is denominated in ZENO’s 18-decimal format as in OZ's default decimals().

  • Because amount = 1 in the test, the contract assumes 1 wei of ZENO (10⁻¹⁸ ZENO tokens).

  • The cost calculation does not account for the decimal difference between ZENO (18 decimals) and USDC (6 decimals).

How the Erroneous Test Happens

  1. Auction StartsgetPrice() returns 100 USDC (100000000 in 6 decimals).

  2. User calls buy(1)

    await auction1.connect(addr1).buy(1);

    • The amount parameter is 1 wei of ZENO (10⁻¹⁸ ZENO tokens).

  3. Cost has earlier been incorrectly calculated:

    cost = price * amount; // 100000000 * 1 = 100 USDC

    • User pays 100 USDC but only receives 1 wei of ZENO (10⁻¹⁸ ZENO).

  4. Test still passes because zeno.mint(msg.sender, amount) mints 1 wei of ZENO, which matches the expected balance of 1.

Impact

This is a severe financial risk because:

  • Users massively overpay for ZENO tokens.

  • Trust in the auction system is compromised as users unknowingly pay 100 USDC for a near-zero amount of ZENO.

  • The test cases falsely validated correctness because they inputted amount = 1, which meant 1 wei of ZENO instead of 1 full token.

  • Potential for silent exploitation since no error occurs, and users may not notice the issue.

Tools Used

Manual

Recommendations

1️⃣ Fix the Decimal Mismatch in buy()

Modify the cost calculation to properly account for ZENO’s 18 decimals and USDC’s 6 decimals:

Auction.sol#L87

- uint256 cost = price * amount;
+ uint256 cost = (price * amount) / (10 ** 18);

✅ This ensures cost is correctly calculated in USDC’s 6-decimal format.

2️⃣ Improve Test Cases to Catch This Flaw

The test cases must validate both:

  1. Correct USDC cost calculation

    const expectedCost = price.mul(ethers.parseUnits("1", 18)).div(ethers.parseUnits("1", 18));
    expect(actualCost).to.equal(expectedCost);

    ✅ Ensures that the price reflects full ZENO token units, not wei-level amounts.

  2. Correct user balance in ZENO tokens

    expect(await zeno1.balanceOf(addr1.address)).to.equal(ethers.parseUnits("1", 18));

    ✅ Ensures the buyer actually receives 1 full ZENO token, not just 1 wei.

Updates

Lead Judging Commences

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

Auction.sol's buy() function multiplies ZENO amount (18 decimals) by price (6 decimals) without normalization, causing users to pay 1 trillion times the intended USDC amount

Support

FAQs

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

Give us feedback!