Core Contracts

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

Auction Price Calculation Fails to Reach Reserve Price Due to Integer Division Truncation

Summary

The Auction contract's getPrice() function uses integer arithmetic for its price calculation, which leads to truncation errors. As a result, if the difference between the starting price and the reserve price is not perfectly divisible by the duration of the auction, the computed price will never exactly reach the reserve price until the auction's end, violating the intended auction design.

Vulnerability Details

The price calculation in the getPrice() function is as follows:

function getPrice() public view returns (uint256) {
if (block.timestamp < state.startTime) return state.startingPrice;
if (block.timestamp >= state.endTime) return state.reservePrice;
return state.startingPrice - (
(state.startingPrice - state.reservePrice) *
(block.timestamp - state.startTime) /
(state.endTime - state.startTime)
);
}
  • The multiplication and division operations are performed using integer arithmetic. If the value (state.startingPrice - state.reservePrice) is not perfectly divisible by (state.endTime - state.startTime), the division will truncate the fractional part.

  • For example, if state.startingPrice = 100, state.reservePrice = 10 (difference = 90), and the duration is 100 seconds, then at block.timestamp = state.startTime + 99 seconds:

    • The calculation is:
      (90 * 99) / 100 = floor(8910 / 100) = 89.

    • Thus, the price becomes 100 - 89 = 11, which is above the reserve price of 10.

  • This truncation error means that the auction price will remain slightly above the reserve price until the very last moment (or may never exactly equal it under certain conditions), undermining the auction's design to gradually decrease to the reserve price.

Example

  • Parameters:

    • state.startingPrice = 100 USDC

    • state.reservePrice = 10 USDC

    • Auction duration: 100 seconds

  • Calculation at 99 seconds after start:

    • Price difference = 100 - 10 = 90 USDC

    • Time elapsed = 99 seconds

    • Computed decrement = (90 * 99) / 100 = 8910 / 100 = floor(89.10) = 89 USDC

    • Resulting Price: 100 - 89 = 11 USDC

    • Observation: The price should ideally reach 10 USDC at auction end, but due to truncation it remains at 11 USDC.

Impact

  • The auction may not reach the intended reserve price, potentially resulting in higher-than-expected prices and unfair outcomes.

  • Buyers expecting the price to drop to the reserve price may end up paying more, impacting market fairness and efficiency.

Tools Used

Manual Review

Recommendations

Implement fixed-point arithmetic to maintain precision in the price calculation. For example, use a library like PRBMath or Solidity's built-in fixed-point math techniques to perform the division without truncation, ensuring that the computed price accurately decreases to the reserve price as intended.

Updates

Lead Judging Commences

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

Auction.sol's whenActive modifier prevents bidding at endTime when price reaches reservePrice, contradicting documentation and preventing purchases at the intended floor price

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

Auction.sol's whenActive modifier prevents bidding at endTime when price reaches reservePrice, contradicting documentation and preventing purchases at the intended floor price

Support

FAQs

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

Give us feedback!