Core Contracts

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

Lack of incentive in ZENO Auction

Summary

The current auction contract implements a linear price reduction model, it lacks dynamic price decay rate adjustment, making it vulnerable to exploitation by low-demand buyers and poor price optimization based on actual market conditions.

Vulnerability Details

Below is a snippet of the getPricefunction in Auction.solwhich calculates the price of Zeno:

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 price is calculated as a simple linear formula which drops at a constant rate over time, regardless of demand. Usually, in a dutch auction, price should decay exponentially (following an e^(-λt) curve) to ensure that early buyers are incentivized to act quickly. Normally, if demand is high, price should stay higher longer (slower decay), if demand is low, price should drop faster to encourage participation. In this function, the price drops at the same rate regardless of how many tokens are sold or the total supply of the tokens.

Impact

Lack of incentive in the auction, a big buyer that intends to purchase a lot of ZENO may choose to just wait until the last moment to purchase at the lowest price. As a result, the auction fails to optimize pricing based on market demand, leading to potential revenue loss for the protocol.

Tools Used

Manual review

Recommendations

Add lambda state variable:

uint256 lambda;

Use exponential price decay instead of liner reduction:

function getPrice() public view returns (uint256) {
if (block.timestamp < state.startTime) return state.startingPrice;
if (block.timestamp >= state.endTime) return state.reservePrice;
uint256 elapsed = block.timestamp - state.startTime;
uint256 price = (state.startingPrice * 1e18) / (1 + lambda * elapsed) /1e18;
// Ensure price doesn’t go below reserve price
return price > state.reservePrice ? price : state.reservePrice;
}

This ensures that price drops more sharply early and slows down later, preventing buyers from waiting too long.

Implement lambda adjustment based on demand:

function adjustLambda() internal {
uint256 demandFactor = ((state.totalAllocated - state.totalRemaining) * 1e18) / state.totalAllocated;
lambda = lambda + (lambda * demandFactor / 10);
}

If demand is high → lambda decreases, keeping price higher for longer.

If demand is low → lambda increases, making price drop faster to attract buyers.

Adjust the lambda on the buyfunction:

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;
zeno.mint(msg.sender, amount);
// Only adjust lambda when at least 10% of total tokens have been bought
if ( ((state.totalAllocated - state.totalRemaining) * 100) / state.totalAllocated >= 10) {
adjustLambda();
}
emit ZENOPurchased(msg.sender, amount, price);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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