Core Contracts

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

Incorrect Decimal Scaling in Redemption Leads to Massive Overpayment Loss

Summary

After fixing the decimal mismatch in Auction.buy(), users now correctly pay 100 USDC for 1 full ZENO token (1e18 units) as I have separately reported. However, a new critical issue arises in ZENO.redeem(), where the redemption process does not properly scale USDC transfers based on its 6 decimal precision.

As a result:

  • A user who redeems 1 full ZENO (1e18 units) receives 1e18 USDC (1,000,000,000,000,000,000 USDC) instead of 100 USDC.

  • This incorrect calculation drains all USDC from the contract and results in an exponential inflation bug, leading to potential financial exploitation.

This flaw undoes the original fix in Auction.buy(), as it creates an inverse problem: instead of overpaying for 1 wei of ZENO, users now get massively over-rewarded when redeeming 1 full ZENO.

Vulnerability Details

Root Cause: No Decimal Conversion in redeem()

In ZENO.sol, the redeem() function executes:

ZENO.sol#L61-L62

_burn(msg.sender, amount);
USDC.safeTransfer(msg.sender, amount);
  • amount is denominated in ZENO’s 18 decimals.

  • USDC has only 6 decimals, so directly transferring amount leads to a 1e12 (trillion) times overpayment.

  • If a user redeems 1 full ZENO (1e18 units), they receive:

    USDC.safeTransfer(msg.sender, 1e18);
    • 1e18 USDC is completely invalid since USDC uses only 6 decimals.

    • Expected value: 100000000 USDC (100 USDC).

    • Actual value transferred: 1e18 USDC (1 trillion times more than expected).

Example Exploit Scenario

  1. User buys 1 ZENO for 100 USDC.

  2. They redeem 1 ZENO and receive 1e18 USDC (1 trillion USDC).

  3. They withdraw excess USDC from the contract.

  4. The entire protocol collapses due to infinite minting of USDC.

This bug turns every ZENO token into a direct, unlimited money printer, allowing attackers to drain all liquidity from the contract. And, of course, the function would revert given the likelihood the contract will have insufficient balance to make a transfer. But the avid users would rush into redeeming the moment isRedeemable() == true and input a fraction of their balanceOf() as redeeming amount to drain the contract balance.

Impact

  • Infinite Inflation: Users receive 1 trillion times more USDC than expected.

  • Protocol Collapse: Any user can exploit this to drain all funds instantly.

  • DeFi Attack Vector: Attackers can repeatedly redeem ZENO and withdraw excess USDC, destroying the entire ecosystem.

  • Regulatory & Legal Risk: A bug this severe could be classified as financial fraud if exploited on a live mainnet.

Tools Used

Manual

Recommendations

Consider implementing the following refactoring:

ZENO.sol#L61-L62

+ uint256 usdcAmount = amount / (10 ** 12); // Convert from 18 to 6 decimals
_burn(msg.sender, amount);
+ require(USDC.balanceOf(address(this)) >= usdcAmount, "Not enough USDC");
- USDC.safeTransfer(msg.sender, amount);
+ USDC.safeTransfer(msg.sender, usdcAmount);
Updates

Lead Judging Commences

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

Decimal precision mismatch between ZENO token (18 decimals) and USDC (6 decimals) not accounted for in redemption, causing calculation errors and incorrect payments

Support

FAQs

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

Give us feedback!