Core Contracts

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

Incorrect decimal handling of ZENO token will break USDC backing of ZENO bond

Summary

ZENO is an ERC-20 utility token intricately linked to USDC that does not pay periodic interest, specifically designed for the acquisition of RAAC bonds. It functions similarly to a zero-coupon bond, fully backed by USDC. ZENO tokens come with a predetermined maturity date, upon which holders can redeem their tokens for the equivalent amount of USDC.

However, ZENO decimals are incorrectly handled in current implementation.

Vulnerability Details

OpenZeppelin's ERC20 library has default 18 decimals

function decimals() public view virtual returns (uint8) {
return 18;
}

However, ZENO contract is not overriding this method.

According to Auction.buy and integration test script, Zeno is expected to have 0 decimals:

Auction.buy

require(amount <= state.totalRemaining, "Not enough ZENO remaining");
uint256 price = getPrice();
@> uint256 cost = price * amount; // @audit doesn't take decimals into consideration
require(usdc.transferFrom(msg.sender, businessAddress, cost), "Transfer failed");

Integration.test.js

let amountToBuy = 1;

However, according to ZENO.redeem, ZENO has same token decimals with 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);
}

So the current codebase is messing up with ZENO token decimals:

Impact

  • Considering zeno price is 1 USD, users cannot buy Zeno token via Auction.buy because no one can afford 1e18 USDC

  • When users redeem ZENO, they will receive dust amount of USDC. 1M ZENO bonds will be redeemable with 1 USD

Tools Used

Manual Review

Recommendations

  • Override OZ's decimals function and return 0

  • Handle decimals in ZENO.redeem and Auction.buy methods

Updates

Lead Judging Commences

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

Decimal precision mismatch between ZENO token (18 decimals) and USDC (6 decimals) not accounted for in redemption, causing calculation errors and incorrect payments

Auction.sol's buy() function multiplies ZENO amount (18 decimals) by price (6 decimals) without normalization, causing users to pay 1 trillion times the intended USDC amount

Support

FAQs

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