In the ZENO contract, when users trade in their ZENO tokens for USDC after the maturity date, they get way too much USDC more than they should because the contract doesn’t adjust for the difference in how ZENO and USDC count their decimals.
The ZENO contract inherits from OpenZeppelin’s ERC20 implementation, which assigns it 18 decimals by default, meaning 1 ZENO token is represented as 10^18 ZENO wei. In contrast, USDC, a standard ERC-20 token, uses 6 decimals, so 1 USDC token is 10^6 USDC wei. This difference is normal in blockchain tokens, but the ZENO contract doesn’t account for it when giving back USDC.
In the redeem function, the contract takes the ZENO amount a user wants to cash in, burns those ZENO tokens, and sends the same number of units in USDC.
The line USDC.safeTransfer(msg.sender, amount) uses the same amount burned from ZENO and sends it as USDC. The protocol says ZENO is a “zero-coupon bond fully backed by USDC,” and users should “convert their ZENO tokens back into USDC” at maturity. That sounds like 1 ZENO should equal 1 USDC, but the code doesn’t do that.
The mint function just creates ZENO based on what the auction tells it, with no sign of scaling:
The first user to redeem could take all the USDC in the contract, leaving nothing for others. For 1 ZENO, they’d get trillions of USDC, far more than the contract could hold.
Once the USDC runs out, redemptions fail, and the whole bond system stops working, hurting the real estate financing goal.
Manual Review
Change the USDC transfer to match 1 ZENO token to 1 USDC token. Add this line in redeem and redeemAll
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.