Thunder Loan

AI First Flight #7
Beginner FriendlyFoundryDeFiOracle
EXP
View results
Submission Details
Severity: low
Valid

Double division in getCalculatedFee() causes near-zero fees for low-priced tokens due to intermediate precision loss

Root + Impact

Description

  • getCalculatedFee computes the flash loan fee in two steps, each dividing by s_feePrecision (1e18). This divide-before-multiply pattern causes significant intermediate truncation.

  • For tokens with low WETH-denominated prices, the first division truncates the intermediate value to near zero, making the final fee effectively zero regardless of the borrowed amount. Borrowers of low-priced tokens receive free capital, paying no fee to LPs.

function getCalculatedFee(IERC20 token, uint256 amount) public view returns (uint256 fee) {
@> uint256 valueOfBorrowedToken = (amount * getPriceInWeth(address(token))) / s_feePrecision;
// First division: intermediate result truncated for low-price tokens
@> fee = (valueOfBorrowedToken * s_flashLoanFee) / s_feePrecision;
// Second division: compounds truncation
}
// Expanded formula:
// fee = ((amount * price) / 1e18 * 3e15) / 1e18
// = (amount * price * 3e15) / 1e36
//
// For price = 1e6 (very cheap token), amount = 1e18:
// fee = (1e18 * 1e6 * 3e15) / 1e36 = 3e39 / 1e36 = 3000 wei ≈ 0

Risk

Likelihood:

  • Any allowed token with a WETH price below approximately 1e12 per token unit triggers near-zero fees. Many legitimate DeFi tokens fall in this price range.

  • The truncation is deterministic — it occurs on every flash loan for affected tokens without exception.

Impact:

  • Flash loans of low-priced tokens provide free capital to borrowers, yielding zero income for LPs holding that token's AssetTokens.

  • The fee calculation silently breaks for an entire class of tokens without any revert or error — the loan proceeds normally with a zero fee.

Proof of Concept

Place this test in test/ and run forge test --match-test test_nearZeroFeeForLowPriceToken. The test demonstrates that very low-price tokens produce a fee that rounds down to zero due to integer division, allowing free flash loans of those assets.

function test_nearZeroFeeForLowPriceToken() public {
// Token worth 1e6 wei of WETH (very cheap)
mockOracle.setPrice(address(cheapToken), 1e6);
// Flash loan of 1e18 tokens
uint256 fee = thunderLoan.getCalculatedFee(cheapToken, 1e18);
// fee = (1e18 * 1e6 * 3e15) / 1e36 = 3000 wei
assertEq(fee, 3000); // essentially zero — no meaningful LP yield
// Borrower takes a massive flash loan for 3000 wei fee
}

Recommended Mitigation

Add a minimum fee floor (e.g. fee = max(fee, MIN_FEE)) so that even low-priced tokens incur a non-zero borrowing cost, preventing free flash loans.

function getCalculatedFee(IERC20 token, uint256 amount) public view returns (uint256 fee) {
- uint256 valueOfBorrowedToken = (amount * getPriceInWeth(address(token))) / s_feePrecision;
- fee = (valueOfBorrowedToken * s_flashLoanFee) / s_feePrecision;
+ uint256 price = getPriceInWeth(address(token));
+ // Multiply first, divide once at end — eliminates intermediate truncation
+ fee = (amount * price * s_flashLoanFee) / (s_feePrecision * s_feePrecision);
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 7 hours ago
Submission Judgement Published
Validated
Assigned finding tags:

[L-03] Mathematic Operations Handled Without Precision in getCalculatedFee() Function in ThunderLoan.sol

## Description In a manual review of the ThunderLoan.sol contract, it was discovered that the mathematical operations within the getCalculatedFee() function do not handle precision appropriately. Specifically, the calculations in this function could lead to precision loss when processing fees. This issue is of low priority but may impact the accuracy of fee calculations. ## Vulnerability Details The identified problem revolves around the handling of mathematical operations in the getCalculatedFee() function. The code snippet below is the source of concern: ``` uint256 valueOfBorrowedToken = (amount * getPriceInWeth(address(token))) / s_feePrecision; fee = (valueOfBorrowedToken * s_flashLoanFee) / s_feePrecision; ``` The above code, as currently structured, may lead to precision loss during the fee calculation process, potentially causing accumulated fees to be lower than expected. ## Impact This issue is assessed as low impact. While the contract continues to operate correctly, the precision loss during fee calculations could affect the final fee amounts. This discrepancy may result in fees that are marginally different from the expected values. ## Recommendations To mitigate the risk of precision loss during fee calculations, it is recommended to handle mathematical operations differently within the getCalculatedFee() function. One of the following actions should be taken: Change the order of operations to perform multiplication before division. This reordering can help maintain precision. Utilize a specialized library, such as math.sol, designed to handle mathematical operations without precision loss. By implementing one of these recommendations, the accuracy of fee calculations can be improved, ensuring that fees align more closely with expected values.

Support

FAQs

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

Give us feedback!