Core Contracts

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

Incorrect Minting of ZENO Tokens Will Cause Users To Lose Funds

Summary

Bidders will always get much less USDC(or other ERC20) than they should because of the incorrect mint amount passed when minting ZENO tokens, this will cause all sensible user redemptions to revert.

Vulnerability Details

ZENO tokens are primarily acquired through RAAC Auctions. During these auctions, users deposit their USDC in exchange for a certain amount of ZENO bonds until the auction concludes. They problem however lies in the buy function:

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); // @audit amount minted instead of cost
emit ZENOPurchased(msg.sender, amount, price);
}

As shown with the @audit tag, the function uses the amount of bonds requested to be bought as the value for minting ZENO instead of the cost, which is the amount of USDC transferred from the user. Futhermore, when the maturity date expires and users want to convert their ZENO tokens back to USDC,

function redeem(uint amount) external nonReentrant {
if (!isRedeemable()) {
revert BondNotRedeemable();
}
if (amount == 0) {
revert ZeroAmount();
}
uint256 totalAmount = balanceOf(msg.sender);
if (amount > totalAmount) {
revert InsufficientBalance();
}
totalZENORedeemed += amount;
_burn(msg.sender, amount);
USDC.safeTransfer(msg.sender, amount);

the redeem function compares the amount to withdraw with the users balance and reverts if the user has insufficent balance and transfers the same amount to the user, this will cause the user to only be able to withdraw dust amounts equivalent to the amount of ZENO bonds bought and not the actual USDC value, a similar situation occurs in the redeemAll function.

function redeemAll() external nonReentrant {
if (!isRedeemable()) {
revert BondNotRedeemable();
}
uint256 amount = balanceOf(msg.sender);
totalZENORedeemed += amount;
_burn(msg.sender, amount);
USDC.safeTransfer(msg.sender, amount);
}

Impact

Users will not be able to withdraw their funds as the value of ZENO minted is much less than intended

Tools Used

Manual Review

Recommendations

Change the value of ZENO tokens minted from amount to cost

/**
Bid on the ZENO auction
User will be able to buy ZENO tokens in exchange for USDC
*/
function buy(uint256 amount) external whenActive { // @audit Users can wait till last minute before bidding
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;
```diff
+zeno.mint(msg.sender, cost);
-zeno.mint(msg.sender, amount); // @audit amount minted instead of cost
```
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!