Thunder Loan

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

deposit() invokes TSwap oracle creating an unexpected dependency that blocks deposits when oracle is unavailable

Root + Impact

Description

  • LP deposits are a core protocol function with no inherent dependency on price data. A user depositing tokens is adding liquidity — there is no reason to consult an oracle during this operation.

  • deposit() calls getCalculatedFee(token, amount) which internally calls the TSwap pool for the token's WETH price. If the TSwap pool for the token does not exist, has zero liquidity, or reverts for any reason, all LP deposits for that token are blocked — a DoS on core functionality caused by an unrelated oracle dependency.

function deposit(IERC20 token, uint256 amount) external revertIfZero(amount) revertIfNotAllowedToken(token) {
AssetToken assetToken = s_tokenToAssetToken[token];
uint256 exchangeRate = assetToken.getExchangeRate();
uint256 mintAmount = (amount * assetToken.EXCHANGE_RATE_PRECISION()) / exchangeRate;
emit Deposit(msg.sender, token, amount);
assetToken.mint(msg.sender, mintAmount);
@> uint256 calculatedFee = getCalculatedFee(token, amount); // oracle call during deposit
@> assetToken.updateExchangeRate(calculatedFee);
token.safeTransferFrom(msg.sender, address(assetToken), amount);
}
function getPriceInWeth(address token) public view returns (uint256) {
@> address swapPoolOfToken = IPoolFactory(s_poolFactory).getPool(token);
@> return ITSwapPool(swapPoolOfToken).getPriceOfOnePoolTokenInWeth();
// Reverts if pool doesn't exist (address(0)) or has zero liquidity
}

Risk

Likelihood:

  • A newly allowed token may not yet have a TSwap pool, causing all deposits for that token to revert from the moment it is enabled.

  • TSwap pool outages or insufficient liquidity — transient conditions outside ThunderLoan's control — propagate directly into deposit failures.

Impact:

  • LP capital cannot enter the protocol for any token whose TSwap pool is unavailable, paused, or not yet created — blocking protocol growth during new token onboarding.

  • The oracle failure DoS is silent: the deposit transaction reverts with a TSwap error rather than a ThunderLoan error, making the root cause non-obvious to users.

Proof of Concept

Place this test in test/ and run forge test --match-test test_depositRevertsWhenNoTSwapPool. The test demonstrates that deposit() reverts with a generic error when a token has no TSwap pool yet, blocking users from depositing newly allowlisted tokens until a pool is created.

function test_depositRevertsWhenNoTSwapPool() public {
// Allow a new token that has no TSwap pool yet
IERC20 newToken = new MockERC20("New", "NEW", 18);
vm.prank(owner);
thunderLoan.setAllowedToken(newToken, true);
// LP tries to deposit — reverts due to oracle call on non-existent pool
vm.deal(alice, 1000e18);
deal(address(newToken), alice, 1000e18);
vm.startPrank(alice);
newToken.approve(address(thunderLoan), 1000e18);
vm.expectRevert(); // TSwap reverts — pool doesn't exist
thunderLoan.deposit(newToken, 1000e18);
}

Recommended Mitigation

Check whether a TSwap pool exists for the token before calling getExchangeRate() and skip the oracle call (using a fallback rate of 1:1) if no pool has been deployed yet.

function deposit(IERC20 token, uint256 amount) external revertIfZero(amount) revertIfNotAllowedToken(token) {
AssetToken assetToken = s_tokenToAssetToken[token];
uint256 exchangeRate = assetToken.getExchangeRate();
uint256 mintAmount = (amount * assetToken.EXCHANGE_RATE_PRECISION()) / exchangeRate;
emit Deposit(msg.sender, token, amount);
assetToken.mint(msg.sender, mintAmount);
- uint256 calculatedFee = getCalculatedFee(token, amount);
- assetToken.updateExchangeRate(calculatedFee);
token.safeTransferFrom(msg.sender, address(assetToken), amount);
}
Updates

Lead Judging Commences

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