# [H-#] Fee Calculation Uses 18-Decimal WETH Precision — Non-18-Decimal Token Pools Suffer Catastrophic Exchange Rate Inflation
## Summary
The `getCalculatedFee` function calculates operational flash loan fees with an absolute precision of 18 decimals based on its underlying WETH oracle configuration. When this 18-decimal fee is passed directly into `AssetToken.updateExchangeRate`, the contract interprets it as a raw token unit quantity. For asset pools using tokens with fewer than 18 decimals (such as USDC or USDT with 6 decimals), this raw value injection scales the exchange rate exponentially. This causes subsequent user deposits to mint zero shares and permanently ruins the asset pool.
## Vulnerability Details
In `ThunderLoan.sol`, the fee calculation triggers `OracleUpgradeable.getPriceInWeth`, returning an 18-decimal formatted valuation. The calculated fee is directly routed into the pool state:
```solidity
assetToken.updateExchangeRate(fee);
```
Inside `AssetToken.sol`, this parameter updates pool share values via the following formula:
```solidity
newExchangeRate = s_exchangeRate * (totalSupply() + fee) / totalSupply();
```
Because the `fee` variable is evaluated assuming an 18-decimal base scale, passing it to an asset pool tracking a 6-decimal token scales the calculation by a factor of $10^{12}$ ($10^{18-6}$). The math adds an astronomical scale value to the true `totalSupply()`. The resulting bloated exchange rate value drives the asset-to-share conversion factor so high that typical user deposit amounts completely round down to zero, locking out future depositors.
## Impact
**High.** Any newly launched asset pool built around tokens using anything other than 18 decimals suffers immediate, catastrophic exchange rate corruption on its very first flash loan. The pool becomes permanently insolvent, new shares cannot be minted, and current liquidity providers are blocked from executing safe token redemptions.
## Proof of Concept
Add the following test case to your suite to confirm the decimal-scaling issue:
```solidity
function test_PoC9_UnitMismatch_Non18Decimals() public {
ERC20Mock usdc = new ERC20Mock("USDC", "USDC", 6);
MockPoolFactory poolFactory = new MockPoolFactory();
poolFactory.createPool(address(usdc));
ThunderLoan tl = ThunderLoan(address(new ERC1967Proxy(address(new ThunderLoan()), "")));
tl.initialize(address(poolFactory));
tl.setAllowedToken(IERC20(address(usdc)), true);
AssetToken at = tl.getAssetFromToken(IERC20(address(usdc)));
usdc.mint(lp, 1_000_000e6);
vm.startPrank(lp);
usdc.approve(address(tl), 1_000_000e6);
tl.deposit(IERC20(address(usdc)), 1_000_000e6);
vm.stopPrank();
uint256 exchangeRateBefore = at.getExchangeRate();
FlashLoanReceiver receiver = new FlashLoanReceiver(address(tl), address(usdc));
usdc.mint(address(receiver), 3e17);
vm.startPrank(user);
tl.flashloan(address(receiver), IERC20(address(usdc)), 1000e6, "");
vm.stopPrank();
uint256 exchangeRateAfter = at.getExchangeRate();
assertGt(exchangeRateAfter, exchangeRateBefore * 1000);
usdc.mint(lp2, 1_000_000e6);
vm.startPrank(lp2);
usdc.approve(address(tl), 1_000_000e6);
uint256 sharesMinted = tl.deposit(IERC20(address(usdc)), 1_000_000e6);
assertEq(sharesMinted, 0);
vm.stopPrank();
}
```
## Tools Used
* Manual Code Review
## Recommendations
Normalize the computed fee amount downward based on the decimals of the underlying asset before passing it to `updateExchangeRate`:
```solidity
uint256 fee = getCalculatedFee(token, amount);
uint256 tokenDecimals = ERC20(address(token)).decimals();
if (tokenDecimals < 18) {
fee = fee / (10 ** (18 - tokenDecimals));
}
assetToken.updateExchangeRate(fee);
```