Thunder Loan

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

Incorrect Exchange-Rate Update Formula (Unit Mismatch + Potential Insolvency)

Incorrect Exchange-Rate Update Formula (Unit Mismatch + Potential Insolvency)

##Description:
AssetToken.updateExchangeRate(uint256 fee) computes:

newExchangeRate = s_exchangeRate * (totalSupply() + fee) / totalSupply()

This mixes different units: totalSupply() is in AssetToken units, while fee is collected in underlying token units. Once s_exchangeRate deviates from 1e18, the formula becomes wrong (it effectively scales the fee by s_exchangeRate), which can inflate the exchange rate beyond what the contract’s underlying balance can actually support.

##Impact:

Overstated exchange rate → AssetTokens become redeemable for more underlying than exists.

This can lead to protocol insolvency (LPs withdrawing more underlying than the pool holds), or forced reverts / stuck withdrawals depending on how redemption is implemented in the wider system.

##Proof of Concept:
Assume:

totalSupply = 1e18 (1 AssetToken)

s_exchangeRate = 2e18 (1 AssetToken = 2 underlying)

fee = 1e18 (1 underlying fee earned)

Correct accounting:
Underlying backing before fee = totalSupply * s_exchangeRate / 1e18 = 1e18 * 2e18 / 1e18 = 2e18 underlying
After fee, backing = 2e18 + 1e18 = 3e18 underlying
So correct newExchangeRate should be:
(backing * 1e18) / totalSupply = (3e18 * 1e18) / 1e18 = 3e18

Current code result:
newExchangeRate = 2e18 * (1e18 + 1e18) / 1e18 = 4e18

The exchange rate is inflated to 4 instead of 3, over-crediting LP claims by 33% in this example.

##Recommended Mitigation:
Update the exchange rate using consistent units (underlying-based accounting), e.g.:

Compute total underlying backing and add fee, then derive rate:

underlyingBacking = totalSupply * s_exchangeRate / 1e18
newExchangeRate = (underlyingBacking + fee) * 1e18 / totalSupply
Equivalently (and cheaper):
newExchangeRate = s_exchangeRate + (fee * 1e18) / totalSupply
Also consider guarding totalSupply() == 0 to avoid division-by-zero if this function can be called before any minting.
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 23 hours 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!