Thunder Loan

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

[H-03] `deposit()` Used as Repayment — Fee Payment Bypassed + LP Tokens Stolen

[M-03] Unused Return Value of functionCall() — Receiver Failure Silently Accepted

Scope

  • ThunderLoan.sol

Description

The flashloan() function calls receiverAddress.functionCall() to invoke executeOperation(), which returns bool. The return value is discarded. If a receiver signals failure by returning false, ThunderLoan treats it as successful.

// ThunderLoan.sol — flashloan()
@> receiverAddress.functionCall( // Return value IGNORED
abi.encodeWithSignature(
"executeOperation(address,uint256,uint256,address,bytes)",
address(token), amount, fee, msg.sender, params
)
);

Risk

Likelihood: Medium

  • Occurs when a receiver returns false to signal an error condition during the flash loan callback.

Impact: Medium

  • Failed flash loan operations are treated as successful, potentially causing unexpected state changes.

Severity: Medium

Proof of Concept

A receiver that encounters an internal error and returns false (as per the IFlashLoanReceiver interface convention) is treated identically to one returning true. The flash loan proceeds, the balance check is attempted, and if it passes, the loan completes despite the receiver indicating failure.

contract FailingReceiver is IFlashLoanReceiver {
function executeOperation(
address token, uint256 amount, uint256 fee,
address, bytes calldata
) external returns (bool) {
// Signal failure
IERC20(token).transfer(msg.sender, amount + fee); // Repay anyway
@> return false; // "I failed" — but ThunderLoan doesn't care
}
}
function test_false_return_accepted() public {
// This should revert but doesn't — balance check passes because tokens were transferred
thunderLoan.flashloan(address(failingReceiver), tokenA, 100e18, "");
// Flash loan completed despite receiver returning false
}

Recommended Mitigation

Decode and verify the return value:

- receiverAddress.functionCall(
+ bytes memory result = receiverAddress.functionCall(
abi.encodeWithSignature("executeOperation(address,uint256,uint256,address,bytes)", ...)
);
+ require(abi.decode(result, (bool)), "Flash loan callback failed");
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!