Core Contracts

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

ZENO Token Redemption State Inconsistency After Maturity

Summary

The ZENO token redemption mechanism carry a critical state inconsistency where the total supply fails to decrease properly when tokens are redeemed after maturity. This creates a mismatch between actual circulating supply and recorded total supply, potentially enabling double-redemption attacks.

function redeem(uint amount) external nonReentrant {
// Proper validation checks
if (!isRedeemable()) {
revert BondNotRedeemable();
}
if (amount == 0) {
revert ZeroAmount();
}
uint256 totalAmount = balanceOf(msg.sender);
if (amount > totalAmount) {
revert InsufficientBalance();
}
// State updates happen in incorrect order
totalZENORedeemed += amount;
_burn(msg.sender, amount); // <<: _burn() updates user balance but totalSupply tracking becomes inconsistent
// Potential reentrancy risk - state updates after external call
USDC.safeTransfer(msg.sender, amount);
}

The key issues are:

  1. State variable updates occur in a non-optimal order

  2. Reliance on _burn() for supply management creates inconsistency with totalZENORedeemed

  3. External call after state modifications (though protected by nonReentrant)

Let's say a user with 500 ZENO tokens initiates redemption after maturity. The contract burns their tokens through _burn(), but fails to decrease totalSupply. This creates an accounting discrepancy where the sum of all balances no longer equals the total supply.

Vulnerability Details

The redeem() function updates totalZENORedeemed but relies solely on _burn() for supply management, creating a mismatch between tracking variables. While user balances decrease correctly during redemption, the total supply remains unchanged. shows:

balanceBefore = 1000
totalSupplyBefore = 1000
redeem(500)
balanceAfter = 500
totalSupplyAfter = 1000 // Should be 500

A user with 500 ZENO tokens initiates redemption after maturity. The contract burns their tokens through _burn(), but fails to decrease totalSupply. This creates an accounting discrepancy where the sum of all balances no longer equals the total supply.

Let's walk through how this plays out:

Imagine Alice holds 500 ZENO tokens from the initial 1000 token supply. After maturity, she redeems her tokens for USDC. The contract burns her tokens, reducing her balance to 0, but the total supply remains stuck at 1000. This mismatch opens up a critical vulnerability, the contract still "thinks" there are 1000 tokens in circulation when only 500 remain.

The root lies in the redeem() function:

function redeem(uint256 amount) external {
totalZENORedeemed += amount;
_burn(msg.sender, amount);
// The contract tracks redemptions but doesn't adjust total supply
USDC.safeTransfer(msg.sender, amount);
}

This is analogous to a bank deducting money from your account but not updating their total deposits ledger. The consequence? The protocol's accounting system becomes increasingly inaccurate with each redemption, potentially allowing more tokens to be redeemed than should exist.

Impact

The redemption mechanism fails to properly decrease total supply while burning user tokens, creating an inconsistency between total supply and actual circulating tokens. This could lead to incorrect protocol accounting and potential double-redemption attacks.

Recommendations

function redeem(uint256 amount) external {
totalZENORedeemed += amount;
_burn(msg.sender, amount);
_totalSupply -= amount; // Keep all supply tracking in sync
USDC.safeTransfer(msg.sender, amount);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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