Beginner FriendlyFoundryDeFiOracle
100 EXP
View results
Submission Details
Severity: high
Invalid

Transfer of less than amount in `ThunderLoan::flashLoan` and `ThunderLoanUpgraded::flashLoan`

Summary

Some tokens (e.g., USDC) contain a special case for amount == type(uint256).max in their transfer functions that results in only the user's balance being transferred. This may cause issues with systems that transfer a user-supplied amount to their contract and then credit the user with the same value in storage (e.g., Vault-type systems) without checking the amount that has actually been transferred.

Vulnerability Details

The special case for amount == type(uint256).max in the transfer function of some tokens, like USDC, can indeed cause issues with systems that transfer a user-supplied amount to their contract and then credit the user with the same value in storage without checking the amount that has actually been transferred. This is because when amount == type(uint256).max, the token's transfer function will transfer the maximum possible amount of tokens, which could be more or less than the user-supplied amount.

Impact

The ThunderLoan::flashLoan function calls the getCalculatedFee() function and then transfers the calculated fee to the assetToken contract. If the token contract has a special case for amount == type(uint256).max, it could transfer more or less tokens than the calculated fee, which would lead to incorrect balances in the assetToken contract and in the user's account. This is also valid for the ThunderLoanUpgraded::flashLoan function.

Tools Used

Manual Review, VS Code

Recommendations

To prevent this issue, you should modify the flashLoan() function in contracts ThunderLoan.sol and ThunderLoanUpgraded.sol to check the amount of tokens that has actually been transferred after calling the getCalculatedFee() function. You can do this by calling the balanceOf function of the token contract after the transfer, and then comparing this amount with the calculated fee.

function flashloan(address receiverAddress, IERC20 token, uint256 amount, bytes calldata params) external {
// ...
uint256 fee = getCalculatedFee(token, amount);
uint256 initialBalance = token.balanceOf(address(assetToken));
assetToken.transferUnderlyingTo(receiverAddress, amount);
uint256 finalBalance = token.balanceOf(address(assetToken));
require(finalBalance - initialBalance == fee, "Incorrect transfer amount");
// ...
}

In this modified flashLoan() function, initialBalance is the balance of the assetToken contract before the transfer, and finalBalance is the balance after the transfer. The require statement checks that the difference between finalBalance and initialBalance is equal to the calculated fee, which ensures that the correct amount of tokens has been transferred.

Updates

Lead Judging Commences

0xnevi Lead Judge
about 2 years ago
0xnevi Lead Judge about 2 years ago
Submission Judgement Published
Invalidated
Reason: Other

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.