Thunder Loan

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

[I-02] Event Ordering — `FlashLoan` Event Emitted Before External Calls

[I-02] Event Ordering — FlashLoan Event Emitted Before External Calls

Scope

  • ThunderLoan.sol

Description

The FlashLoan event is emitted before the external call to receiverAddress.functionCall() and before the repayment balance check. In the checks-effects-interactions pattern, events should ideally be emitted after all state changes and external interactions have completed and been verified. Emitting the event early means it logs a flash loan that may ultimately revert (if repayment fails), which can confuse off-chain log parsers.

// ThunderLoan.sol — flashloan()
assetToken.updateExchangeRate(fee);
@> emit FlashLoan(receiverAddress, token, amount, fee, params); // Emitted BEFORE callback
s_currentlyFlashLoaning[token] = true;
assetToken.transferUnderlyingTo(receiverAddress, amount);
receiverAddress.functionCall(...); // External call AFTER event
// ... balance check ...

Risk

Likelihood: Low

  • The event ordering only matters for off-chain consumers processing events from reverted transactions (which is uncommon but possible in some indexer configurations).

Impact: Low

  • Off-chain systems may temporarily record flash loans that ultimately reverted, though most indexers handle reverts correctly.

Severity: Informational

Proof of Concept

A flash loan that fails the repayment check still emits the FlashLoan event in the transaction trace (even though the transaction reverts). An aggressive log parser that processes events before checking transaction status would record a phantom flash loan.

function test_event_emitted_before_revert() public {
vm.recordLogs();
vm.expectRevert(); // Repayment will fail
thunderLoan.flashloan(address(nonPayingReceiver), tokenA, 100e18, "");
// FlashLoan event was emitted in the reverted trace
}

Recommended Mitigation

Move the FlashLoan event emission to after the repayment balance check. This ensures the event only appears in successful (non-reverted) transactions and the logged data reflects the final verified state.

function flashloan(...) external {
// ...
- emit FlashLoan(receiverAddress, token, amount, fee, params);
s_currentlyFlashLoaning[token] = true;
assetToken.transferUnderlyingTo(receiverAddress, amount);
receiverAddress.functionCall(...);
uint256 endingBalance = token.balanceOf(address(assetToken));
if (endingBalance < startingBalance + fee) { revert; }
+ emit FlashLoan(receiverAddress, token, amount, fee, params);
s_currentlyFlashLoaning[token] = false;
}
Updates

Lead Judging Commences

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