contract FlashloanAaveLiquidate is FlashLoanReceiverBase {
    using SafeERC20 for IERC20;

    // address public bank = address(0xdE7B3b2Fe0E7b4925107615A5b199a4EB40D9ca9);

    // //https://bloxy.info/address/892b14321a4fcba80669ae30bd0cd99a7ecf6ac0
    // address public creamUnitroller = address(
    //     0x3d5BC3c8d13dcB8bF317092d84783c2697AE9258
    // );
    // address public crCream = address(
    //     0x892B14321a4FCba80669aE30Bd0cd99a7ECF6aC0
    // );
    // //https://bloxy.info/address/0xd06527d5e56a3495252a528c4987003b712860ee
    // address public crEth = address(0xD06527D5e56A3495252A528C4987003b712860eE);

    address[] public reserve2BorrowRouting;
    address[] public swap2TokenRouting;

    address public uniRouterV2 = address(
        0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
    );
    address public weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // used for for <> weth <> usdc route

    constructor(address _addressProvider)
        public
        FlashLoanReceiverBase(_addressProvider)
    {}

    function doApprove(address erc20, address spender) public {
        IERC20(erc20).safeApprove(spender, 0);
        IERC20(erc20).safeApprove(spender, uint256(-1));
    }

    function myLiquidationFunction(
        address _collateral,
        address _reserve,
        address _user,
        uint256 _purchaseAmount,
        bool _receiveaToken
    ) public {
        if (_reserve != ethAddress) {
            //require(IERC20(_reserve).approve(addressesProvider.getLendingPoolCore(), _purchaseAmount), "Approval error");
            doApprove(_reserve, addressesProvider.getLendingPoolCore());
        }

        ILendingPool lendingPool = ILendingPool(
            addressesProvider.getLendingPool()
        );

        // Assumes this contract already has `_purchaseAmount` of `_reserve`.
        lendingPool.liquidationCall{
            value: _reserve == ethAddress ? _purchaseAmount : 0
        }(_collateral, _reserve, _user, _purchaseAmount, _receiveaToken);
    }

    /**
        This function is called after your contract has received the flash loaned amount
     */
    function executeOperation(
        address _reserve,
        uint256 _amount,
        uint256 _fee,
        bytes calldata _params
    ) external override {
        require(
            _amount <= getBalanceInternal(address(this), _reserve),
            "Invalid balance, was the flashLoan successful?"
        );

        //
        // Your logic goes here.
        // !! Ensure that *this contract* has enough of `_reserve` funds to payback the `_fee` !!
        //

        (
            // address borrower,
            // address cTokenBorrow,
            // address cTokenCollateral,
            // uint256 repayAmount
            address underlyingCollateral,
            address _underlyingBorrow,
            address _user,
            uint256 repayAmount
        ) = abi.decode(_params, (address, address, address, uint256));

        // 1. 从aave借_reserve
        // 2. 授权ETH，在uniswap上卖ETH，买CREAM
        // 3. 授权CREAM到crCream
        // 4. 调用crCream的清算方法，清算CREAM，获得crETH清算奖励(cTokenCollateral)，borrower为被清算人，repayAmount为CREAM清算数量，cTokenCollateral为crETH
        // 5. 得到crETH后，调用crETH的redeem，用crETH赎回ETH
        // 6. 还款aave ETH，还本金和利息（万分之九利息）
        // address underlyingCollateral = ICrToken(cTokenCollateral).underlying();

        if (_reserve != _underlyingBorrow) {
            // reserve2BorrowRouting = [_reserve, weth, underlyingBorrow];
            address[] memory _weth2BorrowRouting = new address[](2);
            _weth2BorrowRouting[0] = weth;
            _weth2BorrowRouting[1] = _underlyingBorrow;

            if (_reserve == ethAddress) {
                IUniswapRouter(uniRouterV2).swapExactETHForTokens(
                    0,
                    _weth2BorrowRouting,
                    address(this),
                    now.add(1800)
                );
            } else {
                doApprove(_reserve, uniRouterV2);
                // reserve2BorrowRouting = [_reserve, weth, _underlyingBorrow];
                reserve2BorrowRouting = new address[](3);
                reserve2BorrowRouting[0] = _reserve;
                reserve2BorrowRouting[1] = weth;
                reserve2BorrowRouting[2] = underlyingCollateral;

                IUniswapRouter(uniRouterV2).swapExactTokensForTokens(
                    IERC20(_reserve).balanceOf(address(this)),
                    0,
                    reserve2BorrowRouting,
                    address(this),
                    now.add(1800)
                );
            }
        }

        doApprove(_underlyingBorrow, addressesProvider.getLendingPool()); // 授权aave LendingPool
        myLiquidationFunction(
            underlyingCollateral,
            _underlyingBorrow,
            _user,
            repayAmount,
            false
        );

        if (_reserve != underlyingCollateral) {
            if (underlyingCollateral == ethAddress) {
                address[] memory _weth2ReserveRouting = new address[](2);
                _weth2ReserveRouting[0] = weth;
                _weth2ReserveRouting[1] = _reserve;

                IUniswapRouter(uniRouterV2).swapExactETHForTokens(
                    0,
                    _weth2ReserveRouting,
                    address(this),
                    now.add(1800)
                );
            } else {
                //use uniswap
                doApprove(underlyingCollateral, uniRouterV2);
                // swap2TokenRouting = [underlyingCollateral, weth, _reserve];
                swap2TokenRouting = new address[](3);
                swap2TokenRouting[0] = underlyingCollateral;
                swap2TokenRouting[1] = weth;
                swap2TokenRouting[2] = _reserve;

                IUniswapRouter(uniRouterV2).swapExactTokensForTokens(
                    IERC20(underlyingCollateral).balanceOf(address(this)),
                    0,
                    swap2TokenRouting,
                    address(this),
                    now.add(1800)
                );
            }
        }

        uint256 totalDebt = _amount.add(_fee);
        transferFundsBackToPoolInternal(_reserve, totalDebt);
    }

    /**
        Flash loan _amount worth of `_asset` with _data
     */
    function flashloan(
        address _asset,
        uint256 _amount,
        bytes calldata _data
    ) external onlyOwner {
        ILendingPool lendingPool = ILendingPool(
            addressesProvider.getLendingPool()
        );
        lendingPool.flashLoan(address(this), _asset, _amount, _data);
    }

    function set(
        // address _bank,
        address _weth,
        address _uniRouterV2,
        address _addressesProvider
    ) external onlyOwner {
        // bank = _bank;
        weth = _weth;
        uniRouterV2 = _uniRouterV2;
        addressesProvider = ILendingPoolAddressesProvider(_addressesProvider);
    }

    // function setRouting(
    //     address[] calldata _reserve2BorrowRouting,
    //     address[] calldata _swap2TokenRouting
    // ) external onlyOwner {
    //     reserve2BorrowRouting = _reserve2BorrowRouting;
    //     swap2TokenRouting = _swap2TokenRouting;
    // }
}
function flashloan(
    address _asset,
    uint256 _amount,
    bytes calldata _data
) external onlyOwner {
    ILendingPool lendingPool = ILendingPool(
        addressesProvider.getLendingPool()
    );
    lendingPool.flashLoan(address(this), _asset, _amount, _data);
}

/**
 Flash loan _amount worth of `_asset` with _data
 */