Core Contracts

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

Auction continues to show active state after all tokens are sold

The Auction contract fails to properly end when all tokens are sold (totalRemaining = 0). According to the documentation, the auction should conclude in one of two ways:

  1. When all ZENO tokens are purchased

  2. When the auction reaches its predetermined end time

However, the current implementation only checks for the end time in checkAuctionEnded() and does not update the auction state when all tokens are sold.

Impact

This issue leads to several incorrect behaviors:

  1. getPrice() continues to calculate and return a linearly decreasing price even though no tokens are available for purchase

  2. checkAuctionEnded() incorrectly reports the auction as still active

  3. Users may be misled about the auction status and current token price

While the buy() function prevents purchasing when totalRemaining = 0, the incorrect auction state could cause confusion and potentially impact integrating protocols that rely on accurate auction status information.

Proof of Concept

function getPrice() public view returns (uint256) {
if (block.timestamp < state.startTime) return state.startingPrice;
if (block.timestamp >= state.endTime) return state.reservePrice;
// Continues to calculate price even when totalRemaining = 0
return state.startingPrice - (
(state.startingPrice - state.reservePrice) *
(block.timestamp - state.startTime) /
(state.endTime - state.startTime)
);
}
function checkAuctionEnded() external {
// Only checks timestamp, not totalRemaining
require(block.timestamp >= state.endTime, "Auction not ended");
emit AuctionEnded(getPrice());
}

Update the buy() function to end the auction immediately when all tokens are sold:

function buy(uint256 amount) external whenActive {
require(amount <= state.totalRemaining, "Not enough ZENO remaining");
uint256 price = getPrice();
uint256 cost = price * amount;
require(usdc.transferFrom(msg.sender, businessAddress, cost), "Transfer failed");
bidAmounts[msg.sender] += amount;
state.totalRemaining -= amount;
state.lastBidTime = block.timestamp;
state.lastBidder = msg.sender;
if (state.totalRemaining == 0) {
state.endTime = block.timestamp; // End auction immediately
}
zeno.mint(msg.sender, amount);
emit ZENOPurchased(msg.sender, amount, price);
}

Also, update getPrice() to explicitly handle the case when all tokens are sold:

function getPrice() public view returns (uint256) {
if (block.timestamp < state.startTime) return state.startingPrice;
if (block.timestamp >= state.endTime || state.totalRemaining == 0) return state.reservePrice;
// ...rest of the function
}
Updates

Lead Judging Commences

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

Auction.sol's checkAuctionEnded() only verifies time-based completion, ignoring sold-out condition, contradicting documentation and preventing early auction completion signaling

Support

FAQs

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

Give us feedback!