Summary
Transferring wrong amount of USDC in ZENO::redeem
and redeemAll
functions, the user lost almost all the USDC that was used to buy ZENO tokens earlier.
Vulnerability Details
The redeem
and redeemAll
functions allow user to burn an amount of ZENO tokens in exchange for USDC. But the amount of USDC transferred to users is equal to the amount of ZENO tokens burned.
ZENO.sol#L61-L62 and ZENO.sol#L72-L73:
_burn(msg.sender, amount);
USDC.safeTransfer(msg.sender, amount);
This amount is not multiplied by the price of ZENO tokens in USDC.
Compared to the cost (USDC amount) the user spent to buy ZENO tokens in Aution.sol::buy
function: Auction.sol#L84-L97
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);
emit ZENOPurchased(msg.sender, amount, price);
}
Unless the price is 1, the user will receive a much smaller amount of USDC than the original purchase amount.
Impact
The user lost almost all the USDC that was used to buy ZENO tokens earlier.
Tools Used
Manual review
Recommendations
Take the number of ZENO tokens multiplied by its price before transferring to the user.
In reddem
:
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();
}
+ uint256 amountToTransfer = amount * USDC.balanceOf(address(this)) / totalSupply();
totalZENORedeemed += amount;
_burn(msg.sender, amount);
- USDC.safeTransfer(msg.sender, amount);
+ USDC.safeTransfer(msg.sender, amountToTransfer);
}
And in redeemAll
:
function redeemAll() external nonReentrant {
if (!isRedeemable()) {
revert BondNotRedeemable();
}
uint256 amount = balanceOf(msg.sender);
+ uint256 amountToTransfer = amount * USDC.balanceOf(address(this)) / totalSupply();
totalZENORedeemed += amount;
_burn(msg.sender, amount);
- USDC.safeTransfer(msg.sender, amount);
+ USDC.safeTransfer(msg.sender, amountToTransfer);
}