Thunder Loan

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

The IThunderLoan interface does not expose most of the functions implemented in the ThunderLoan contract.

Root + Impact

Description

  • The IThunderLoan interface should reflect all the ThunderLoan contract public and external functions.

  • The IThunderLoan interface only exposes the repay() function, but the actual ThunderLoan contract implements several other critical functions that are not reflected in the interface. Plus repay() function on Interface accept address, when repay() function in Contract accept IERC20 interface

interface IThunderLoan {
@> function repay(address token, uint256 amount) external;
// @audit-issue : lack of functions
}

Risk

Likelihood:

  • Everytime some other contract try to call a function from thunderLoan that is not repay()

Impact:

  • Any protocol, receiver, or third-party contract that integrates ThunderLoan using IThunderLoan cannot access core functionality as flashLoan, deposit or redeem through the interface .

Proof of Concept

I write a test with a flashLoan receiver contract but the compilation fails because IThunderLoan doesn't support Flashloan()

contract POCtest is Test {
ThunderLoan thunderLoanImplementation;
MockPoolFactory mockPoolFactory;
ERC1967Proxy proxy;
ThunderLoan thunderLoan;
ERC20Mock weth;
ERC20Mock tokenA;
ERC20Mock tokenB;
ERC20Mock6Decimals tokenWith6Decimals;
AssetToken assetToken6Decimals;
AssetToken assetTokenA;
AssetToken assetTokenB;
address depositer = makeAddr("depositer");
address flahLoanReceiver = makeAddr("flashLoanReceiver");
address flashLoanAttacker = makeAddr("flashLoanAttacker");
function setUp() public virtual {
thunderLoan = new ThunderLoan();
mockPoolFactory = new MockPoolFactory();
weth = new ERC20Mock();
tokenA = new ERC20Mock();
tokenB = new ERC20Mock();
tokenWith6Decimals = new ERC20Mock6Decimals();
mockPoolFactory.createPool(address(tokenA));
mockPoolFactory.createPool(address(tokenWith6Decimals));
proxy = new ERC1967Proxy(address(thunderLoan), "");
thunderLoan = ThunderLoan(address(proxy));
thunderLoan.initialize(address(mockPoolFactory));
assetTokenB = thunderLoan.setAllowedToken(
IERC20(address(tokenB)),
true
);
assetToken6Decimals = thunderLoan.setAllowedToken(
IERC20(address(tokenWith6Decimals)),
true
);
tokenA.mint(depositer, 50000 * 10 ** tokenA.decimals()); //fund depositer with tokenA
//fund depositer with token with 6 decimals
tokenWith6Decimals.mint(
depositer,
5000 * 10 ** tokenWith6Decimals.decimals()
); // 5000 tokens in 6-decimal representation
}
function testThunderLoanInterface() public {
// This test use Basetest.t.sol setup
tokenA.mint(depositer, 50000 * 10 ** tokenA.decimals()); //fund depositer with tokenA
assetTokenA = thunderLoan.setAllowedToken(
IERC20(address(tokenA)),
true
);
// AssetTokenA has 0 tokenA deposited, now depositer will deposit TokenA and allows flashLoan
vm.startPrank(depositer);
uint256 depositAmount = tokenA.balanceOf(depositer);
tokenA.approve(address(thunderLoan), depositAmount);
thunderLoan.deposit(tokenA, depositAmount);
vm.stopPrank();
vm.startPrank(flahLoanReceiver);
FlashLoanReceiver receiverContract = new FlashLoanReceiver(
address(thunderLoan)
);
tokenA.mint(address(receiverContract), 100 * 10 ** tokenA.decimals()); // fund receiverContract with tokenA to pay fee
receiverContract.requestFlashLoan(address(tokenA));
}
}
contract FlashLoanReceiver {
IThunderLoan private immutable i_thunderLoan;
constructor(address thunderLoan) {
i_thunderLoan = IThunderLoan(thunderLoan);
}
//amount 1
function requestFlashLoan(address _underlyingToken) external {
uint256 flashLoanAmount = 1 * 10 ** ERC20Mock(_underlyingToken).decimals();
i_thunderLoan.flashloan(
address(this),
IERC20(_underlyingToken),
flashLoanAmount,
""
);
}
function executeOperation(
address token,
uint256 amount,
uint256 fee,
address initiator,
bytes calldata params
) external {
IERC20(token).approve(address(i_thunderLoan), amount + fee);
i_thunderLoan.repay(token, amount + fee);
}
}

Recommended Mitigation

Implement all the public and external functions.
Import IERC20 from openZeppelin and change address token with IERC20 token on repay() function

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
+ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IThunderLoanFixed {
+ function initialize(address tswapAddress) external;
+ function deposit(address token, uint256 amount) external;
+ function redeem(address token, uint256 amountOfAssetToken) external;
+ function flashloan(
+ address receiverAddress,
+ address token,
+ uint256 amount,
+ bytes calldata params
+ ) external;
- function repay(address token, uint256 amount) external;
+ function repay(IERC20 token, uint256 amount) external;
+ function setAllowedToken(address token, bool allowed) external returns (address);
+ function getCalculatedFee(address token, uint256 amount) external view returns (uint256);
+ function updateFlashLoanFee(uint256 newFee) external;
+ function isAllowedToken(address token) external view returns (bool);
+ function getAssetFromToken(address token) external view returns (address);
+ function isCurrentlyFlashLoaning(address token) external view returns (bool);
+ function getFee() external view returns (uint256);
+ function getFeePrecision() external view returns (uint256);
}
Updates

Lead Judging Commences

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