Thunder Loan

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

The deposit function updates the exchange rate before receiving the tokens, allowing value to be stolen from the protocol

Root + Impact

The deposit function updates the exchange rate before actually transferring the funds from the user, allowing attackers to exploit the inflated exchange rate during a transaction and steal value from liquidity providers.

Description

Normally, a user deposits tokens, and the protocol mints AssetTokens and records the exchange rate based on the deposited amount.

However, the specific issue is that the protocol updates the exchange rate (inflating it with theoretical fees) BEFORE executing the actual token transfer using safeTransferFrom. Because the contract believes the funds have already been deposited and increases the pool’s value prematurely, an attacker can exploit this window during a flash loan.

function deposit(IERC20 token, uint256 amount) external revertIfZero(amount) revertIfNotAllowedToken(token) {
// ...
assetToken.mint(msg.sender, mintAmount);
uint256 calculatedFee = getCalculatedFee(token, amount);
@> assetToken.updateExchangeRate(calculatedFee); // Exchange rate goes up here
@> token.safeTransferFrom(msg.sender, address(assetToken), amount); // But tokens are actually transferred AFTER!
}
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);
}

Risk

Likelihood:

  • High. Any user can interact with the public deposit function.

  • High. An attacker can easily sequence this interaction inside a malicious smart contract during a flash loan.

Impact:

  • High. The exchange rate is artificially inflated.

  • High. Legitimate liquidity providers lose value because the attacker can mint AssetTokens at a manipulated rate and immediately withdraw them to steal funds.

Proof of Concept

function testExchangeRateUpdatesBeforeTransfer() public {
// 1. Normal user deposits 1000 tokens
vm.prank(user);
tokenA.approve(address(thunderLoan), 1000e18);
vm.prank(user);
thunderLoan.deposit(tokenA, 1000e18);
uint256 exchangeRateBefore = assetToken.getExchangeRate();
// 2. Attacker deposits, exploiting the order of operations
vm.prank(attacker);
tokenA.approve(address(thunderLoan), 500e18);
vm.prank(attacker);
thunderLoan.deposit(tokenA, 500e18);
uint256 exchangeRateAfter = assetToken.getExchangeRate();
// 3. The exchange rate went up even before the attacker's tokens were safely held
assert(exchangeRateAfter > exchangeRateBefore);
}

Recommended Mitigation

Ensure tokens have been successfully transferred to the protocol first. Only after the receipt of funds has been confirmed should AssetTokens be minted and the exchange rate updated.
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);
+ assetToken.mint(msg.sender, mintAmount);
+ assetToken.updateExchangeRate(calculatedFee);
}
Updates

Lead Judging Commences

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