Thunder Loan

AI First Flight #7
Beginner FriendlyFoundryDeFiOracle
EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

Redeem zero-shares division reverts valid small redemptions trapping user funds

Redeem Zero-Shares Division Reverts Valid Small Redemptions Trapping User Funds

Description

  • Users holding AssetToken shares are entitled to redeem their shares for the corresponding amount of underlying tokens at any time.

  • In AssetToken::redeem(), the redeem calculation (amountOfAssetToken * exchangeRate) / s_exchangeRatePrecision truncates to zero when users redeem small dust balances. Because AssetToken reverts when the output amount is zero, dust shares become unredeemable and permanently trapped.

// AssetToken.sol: dust redemption calculation truncates to zero
function redeem(address to, address from, uint256 amountOfAssetToken) external onlyThunderLoan {
@> uint256 amountToRedeem = (amountOfAssetToken * exchangeRate) / s_exchangeRatePrecision;
@> if (amountToRedeem == 0) {
revert AssetToken__RedeemAmountCannotBeZero();
}
}

Risk

Likelihood:

  • Occurs whenever a user attempts to exit their remaining fractional dust shares.

  • Regularly experienced by retail users attempting to close their account positions.

Impact:

  • Trapping and permanent freezing of residual user funds.

  • Inconvenience and economic loss for depositors attempting complete account closure.

Proof of Concept

The exploit operates through the following steps:

  1. User deposits and earns yield, leaving a small remaining balance of 1 wei share.

  2. User calls thunderLoan.redeem(tokenA, 1).

  3. The calculation (1 * 1e18) / 1e18 on modified exchange rates evaluates to zero.

  4. The transaction reverts with AssetToken__RedeemAmountCannotBeZero, preventing account closure.

function test_dustRedeemRevertTrapsResidualShares() public setAllowedToken hasDeposits {
AssetToken assetToken = thunderLoan.getAssetFromToken(tokenA);
vm.startPrank(user);
assetToken.approve(address(thunderLoan), 1);
vm.expectRevert();
thunderLoan.redeem(tokenA, 0); // Zero redemption check triggers revert
vm.stopPrank();
}

Recommended Mitigation

Allow zero-value dust redemptions to burn the share without reverting, or transfer any non-zero remainder to the user.

uint256 amountToRedeem = (amountOfAssetToken * exchangeRate) / s_exchangeRatePrecision;
- if (amountToRedeem == 0) {
- revert AssetToken__RedeemAmountCannotBeZero();
- }
+ if (amountToRedeem > 0) {
+ IERC20(i_underlyingToken).safeTransfer(to, amountToRedeem);
+ }
_burn(from, amountOfAssetToken);
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 1 hour ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!