getCalculatedFee() performs two sequential integer divisions that both truncate toward zero. For tokens with a low WETH price or for small loan amounts, the intermediate value valueOfBorrowedToken truncates to zero, making fee also zero.
A borrower can take a flash loan of a non-trivial amount and pay no fee at all if the product amount * getPriceInWeth(token) is less than s_feePrecision (1e18), causing the first division to round down to zero.
This is independent of the double-division precision loss issue (L-03 / 10-thunder-L2.md): L-01 concerns complete fee elimination, not just reduced fees.
If amount * getPriceInWeth(token) < 1e18, then valueOfBorrowedToken = 0 and fee = 0.
Likelihood: Medium — tokens priced far below WETH (e.g. meme tokens, stablecoins on a different decimal scale, or tokens added with a very small TSwap price) create windows where small-to-medium amounts produce zero fees. The required loan size depends on the token's WETH price.
Impact: Low — the protocol loses fee revenue for those loans but LP principal is unaffected (the loan is still repaid). However, an attacker can arbitrage or manipulate state repeatedly at zero cost.
Foundry test:
Add a minimum fee floor so that any non-zero loan always incurs at least 1 wei in fees:
## Description getCalculatedFee can be as low as 0 ## Vulnerability Details Any value up to 333 for "amount" can result in 0 fee based on calculation ``` function testFuzzGetCalculatedFee() public { AssetToken asset = thunderLoan.getAssetFromToken(tokenA); uint256 calculatedFee = thunderLoan.getCalculatedFee( tokenA, 333 ); assertEq(calculatedFee ,0); console.log(calculatedFee); } ``` ## Impact Low as this amount is really small ## Recommendations A minimum fee can be used to offset the calculation, though it is not that important.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.