pragma solidity ^0.8.6;



interface IUniswapV2Router01 {
    function factory() external pure returns (address);

    function WETH() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity);

    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);

    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB);

    function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountToken, uint amountETH);

    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountA, uint amountB);

    function removeLiquidityETHWithPermit(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountToken, uint amountETH);

    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);

    function swapTokensForExactTokens(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);

    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
    external
    payable
    returns (uint[] memory amounts);

    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
    external
    returns (uint[] memory amounts);

    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
    external
    returns (uint[] memory amounts);

    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
    external
    payable
    returns (uint[] memory amounts);

    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);

    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);

    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);

    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);

    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}

interface IUniswapV2Router02 is IUniswapV2Router01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);

    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;

    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;

    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}

interface IUniswapV2Factory {
    event PairCreated(address indexed token0, address indexed token1, address pair, uint);

    function feeTo() external view returns (address);

    function feeToSetter() external view returns (address);

    function getPair(address tokenA, address tokenB) external view returns (address pair);

    function allPairs(uint) external view returns (address pair);

    function allPairsLength() external view returns (uint);

    function createPair(address tokenA, address tokenB) external returns (address pair);

    function setFeeTo(address) external;

    function setFeeToSetter(address) external;
}

contract CYSWAP is Ownable {
    // pancake 0x10ED43C718714eb63d5aA57B78B54704E256024E
    // uniV2 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
    address public _router = 0x10ED43C718714eb63d5aA57B78B54704E256024E;

    function buyWithTry(address _path0, address _path1, uint256 _amountIn, uint256 _slippage) external onlyOwner {
        require(_slippage >= 0 || _slippage <= 100, "Slippage error");

        address[] memory path = new address[](2);
        unchecked {
            path[0] = _path0;
            path[1] = _path1;
        }
        IERC20(_path0).approve(msg.sender, _amountIn);
        IERC20(_path0).approve(_router, _amountIn);
        // balance before buy
        uint256 beforeBuyToken = IERC20(_path1).balanceOf(address(this));
        uint256 amountOut = IUniswapV2Router01(_router).getAmountsOut(_amountIn, path)[1];
        uint256 amountOutMin = amountOut * (100 - _slippage) / 100;
        IUniswapV2Router02(_router).swapExactTokensForTokensSupportingFeeOnTransferTokens(_amountIn, amountOutMin, path, address(this), block.timestamp);
        // balance after buy
        uint256 afterBuyToken = IERC20(_path1).balanceOf(address(this)) - beforeBuyToken;
        uint256 afterBuyETH = IERC20(_path0).balanceOf(address(this));
        // check buy status
        require(afterBuyToken > 0, "buy failed");

        address[] memory sellPath = new address[](2);
        unchecked {
            sellPath[0] = _path1;
            sellPath[1] = _path0; 
        }
        IERC20(_path1).approve(msg.sender, afterBuyToken);
        IERC20(_path1).approve(_router, afterBuyToken);
        uint256 _amounts = IUniswapV2Router01(_router).getAmountsOut(afterBuyToken, sellPath)[1];
        uint256 sellAmountOutMin = _amounts * (100 - _slippage) / 100;
        IUniswapV2Router02(_router).swapExactTokensForTokensSupportingFeeOnTransferTokens(afterBuyToken, sellAmountOutMin, sellPath, address(this), block.timestamp);
        uint256 afterSellETH = IERC20(_path0).balanceOf(address(this));
        require(afterSellETH > afterBuyETH, "sell failed");
        // require(afterSellETH <= _amounts * _slippage / 100, "sell failed");


        // 正式买入
        IERC20(_path0).approve(msg.sender, _amountIn);
        IERC20(_path0).approve(_router, _amountIn);
        IUniswapV2Router02(_router).swapExactTokensForTokensSupportingFeeOnTransferTokens(_amountIn, amountOutMin, path, address(this), block.timestamp);
    }

    // 通过合约调用钱包交易
    function walletSwap(address _path0, address _path1, uint256 _amountIn, uint256 _slippage) external onlyOwner {
        // 先在weth上授权本合约
        require(_slippage >= 0 || _slippage <= 100, "Slippage error");
        IERC20(_path0).transferFrom(msg.sender, address(this), _amountIn);
        IERC20(_path0).approve(_router, _amountIn);
        address[] memory path = new address[](2);
        path[0] = _path0;
        path[1] = _path1;
        uint256[] memory _amountOutMinSlice = IUniswapV2Router01(_router).getAmountsOut(_amountIn, path);
        uint256 amountOutMin = _amountOutMinSlice[1] * (100 - _slippage) / 100;
        IUniswapV2Router02(_router).swapExactTokensForTokensSupportingFeeOnTransferTokens(_amountIn, amountOutMin, path, address(this), block.timestamp);
    }

    // 合约直接交易
    function contractSwap(address _path0, address _path1, uint256 _amountIn, uint256 _slippage) external onlyOwner {
        require(_slippage >= 0 || _slippage <= 100, "Slippage error");
        IERC20(_path0).approve(_router, _amountIn);
        IERC20(_path0).approve(msg.sender, _amountIn);
        address[] memory path = new address[](2);
        path[0] = _path0;
        path[1] = _path1;
        uint256[] memory _amountOutMinSlice = IUniswapV2Router01(_router).getAmountsOut(_amountIn, path);
        uint256 amountOutMin = _amountOutMinSlice[1] * (100 - _slippage) / 100;
        IUniswapV2Router02(_router).swapExactTokensForTokensSupportingFeeOnTransferTokens(_amountIn, amountOutMin, path, address(this), block.timestamp);
    }

    function setRouter(address __router) external onlyOwner {
        _router = __router;
    }

    // 提现合约内指定token
    function withdraw(address token) external onlyOwner {
        IERC20(token).transfer(owner(), IERC20(token).balanceOf(address(this)));
    }

    // 提现合约内指定数量的token
    function withdrawAmount(address token, uint256 amount) external onlyOwner {
        IERC20(token).transfer(owner(), amount);
    }

    // 提现合约内的WETH
    function withdrawETH() external payable onlyOwner {
        payable(address(owner())).transfer(address(this).balance);
    }

    // 查询合约指定合约的余额
    function checkBalance(address _token) public view returns (uint256) {
        uint256 tokenBalance = IERC20(_token).balanceOf(address(this));
        return tokenBalance;
    }

    // 当合约中没有任何匹配的函数可调用时，调用fallback()函数
    fallback() payable external {}

    // 确保合约可以接收WETH
    receive() payable external {}

}
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"_router","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_path0","type":"address"},{"internalType":"address","name":"_path1","type":"address"},{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_slippage","type":"uint256"}],"name":"buyWithTry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"checkBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_path0","type":"address"},{"internalType":"address","name":"_path1","type":"address"},{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_slippage","type":"uint256"}],"name":"contractSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"__router","type":"address"}],"name":"setRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_path0","type":"address"},{"internalType":"address","name":"_path1","type":"address"},{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_slippage","type":"uint256"}],"name":"walletSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawETH","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]