Core Contracts

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

Wrong calculation in `Auction::buy` function leads to incorrect token pricing

Summary

The Auction contract contains a critical vulnerability in its buy function that fails to properly scale decimal places between USDC (6 decimals) and ZENO tokens (18 decimals). Due to this incorrect scaling, the contract calculates a massively inflated USDC price by multiplying unscaled values. As a result, users attempting to purchase ZENO tokens will face a Denial of Service (DoS) condition since the required USDC amount will be astronomically high, causing all transactions to revert due to insufficient USDC balance.

Vulnerability Details

In the Auction contract's buy function, a critical calculation error occurs when determining the USDC cost for ZENO tokens:

function buy(uint256 amount) external whenActive {
require(amount <= state.totalRemaining, "Not enough ZENO remaining");
uint256 price = getPrice();
uint256 cost = price * amount; // Vulnerable line
require(usdc.transferFrom(msg.sender, businessAddress, cost), "Transfer failed");
...
}

The vulnerability stems from the following:

  1. The amount parameter represents ZENO tokens in their base units (18 decimals)

    • 1 ZENO = 1,000,000,000,000,000,000 (1e18) base units

  2. The getPrice() function returns the price in USDC base units (6 decimals)

    • 1 USDC = 1,000,000 (1e6) base units

  3. When calculating cost = price * amount, both numbers are multiplied directly without decimal adjustment:

    • For example, buying 1 ZENO at a price of 1 USDC:

    • cost = 1,000,000 * 1,000,000,000,000,000,000

    • cost = 1,000,000,000,000,000,000,000,000 (1e24)

    • This means the user needs to pay 1 quintillion USDC instead of 1 USDC

This calculation error inflates the required USDC amount by a factor of 10^18, making it impossible for any user to have sufficient USDC balance to complete the purchase, effectively creating a DoS condition.

Impact

  1. Complete denial of service for token purchases as the inflated USDC amount required will most likely always exceed any user's balance

  2. The auction becomes non-functional, blocking all token distribution through this contract

PoC

Paste the following code in remix:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
// The functions in this contract correctly represent how the cost is calculated in the `Auction::buy` function
contract IncorrectCostCalculationVulnerability {
// An example price with 6 decimals, because USDC has 6 decimals
uint256 public examplePrice = 10e6;
// How the `Auction"::buy` works which in incorrecct. User passes and amount scaled by 18 decimals becuse the ZENO token has 18 decimals. Gets returned a cost with 18 decimals (incorrect).
// As an example you can pass: 5e18. The return value will be 50e18
function buy(uint256 amount) public view returns (uint256) {
// The cost is not divided by 1e18 which leads to a huge cost
uint256 cost = amount * examplePrice;
return cost;
}
// This is how the function should be. User again passes an amount scaled by 18 decimals. Gets returned a cost with 6 decimals which is correct!
// As an example you can pass: 5e18. The return value will be 50e6.
function correctBuy(uint256 amount) public view returns (uint256) {
// The cost is divided by 1e18 which calculates the cost correctly
uint256 cost = (amount * examplePrice) / 1e18;
return cost;
}
}

Tools Used

Manual Review

Recommendations

Change the cost calculation as shown:

function buy(uint256 amount) external whenActive {
require(amount <= state.totalRemaining, "Not enough ZENO remaining");
uint256 price = getPrice();
- uint256 cost = price * amount;
+ uint256 cost = (price * amount) / 1e18; // Scale down by 18 decimals
require(usdc.transferFrom(msg.sender, businessAddress, cost), "Transfer failed");
...
}
Updates

Lead Judging Commences

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