Core Contracts

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

The `cost` calculation in `Auction.buy()` is wrongly implemented

Summary

The cost calculation in Auction.buy() is wrongly implemented,
as it multiplies the amount of Zeno tokens the user wants to buy without precition deviding.

Vulnerability Details

Let's take state.reservePrice and state.startingPrice from the test folder Integration.test.js.
https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/test/unit/Zeno/Integration.test.js#L81

startingPrice = ethers.parseUnits("100", 6); // 100 USDC
reservePrice = ethers.parseUnits("10", 6); // 10 USDC

consider a scenario when a user wants to buy 10 Zeno tokens:

  1. the user calls buy(10).

  2. then the input gets multiplied to 100e6 making the cost equal to 1000e6.

  3. the user get minted ten Zeno token for 1000 USDC.

this will lead to a user buying Zenos for a high value, then burning them for the corresponding amount of USDC.
see the redeemAll() description:

Function Signature: function redeemAll()/

Description: This method is used to redeem ZENO tokens for USDC after the bond's maturity date./

The ZENO tokens are burnt during this process, effectively removing them from circulation./

This function ensures that users can get their USDC back once the bond matures./

Usage Scenario: After the bond matures, users call this method to exchange their ZENO tokens back for USDC./

The method would burn the ZENO tokens and transfer the corresponding amount of USDC to the user./

Impact

This will break the Zeno Bond functionality leading to a loss of funds for the users.

Tools Used

Manual Review.

Recommendations

implement the cost calculation as follow:

function buy(uint256 amount) external whenActive {
require(amount <= state.totalRemaining, "Not enough ZENO remaining");
uint256 price = getPrice();
+++ uint256 cost = price * amount / 100;
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);
emit ZENOPurchased(msg.sender, amount, price);
}
Updates

Lead Judging Commences

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

ZENO.sol implements fixed 1:1 redemption with USDC regardless of auction purchase price, breaking zero-coupon bond economics and causing user funds to be permanently lost

Support

FAQs

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

Give us feedback!