// File: contracts/lib/IOwnable.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.17;

interface IOwnable {
    function owner() external view returns (address);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    function transferOwnership(address newOwner) external;
}

// File: contracts/lib/Ownable.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.17;

import "./IOwnable.sol";

contract Ownable is IOwnable {
    address _owner;

    constructor() {
        _owner = msg.sender;
    }

    modifier onlyOwner() {
        require(_owner == msg.sender, "caller is not the owner");
        _;
    }

    function owner() external view virtual returns (address) {
        return _owner;
    }

    function transferOwnership(address newOwner) external override onlyOwner {
        address lastOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(lastOwner, newOwner);
    }
}

// File: contracts/lib/Withdrawable.sol



abstract contract Withdrawable {
    address internal _withdrawAddress;

    constructor(address withdrawAddress__) {
        _withdrawAddress = withdrawAddress__;
    }

    modifier onlyWithdrawer() {
        require(msg.sender == _withdrawAddress);
        _;
    }

    function withdraw() external onlyWithdrawer {
        _withdraw();
    }

    function _withdraw() internal {
        payable(_withdrawAddress).transfer(address(this).balance);
    }

    function setWithdrawAddress(address newWithdrawAddress)
        external
        onlyWithdrawer
    {
        _withdrawAddress = newWithdrawAddress;
    }

    function withdrawAddress() external view returns (address) {
        return _withdrawAddress;
    }
}

// File: contracts/swaper/IUniswapV2Router02.sol



pragma solidity ^0.8.7;

interface IUniswapV2Router02 {
    function swapExactTokensForETH(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external;

    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external;

    function swapETHForExactTokens(
        uint256 amountOut,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable returns (uint256[] memory amounts);

    function factory() external pure returns (address);

    function WETH() external pure returns (address);

    function addLiquidityETH(
        address token,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    )
        external
        payable
        returns (uint256 amountToken, uint256 amountETH, uint256 liquidity);

    function swapExactETHForTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable returns (uint[] memory amounts);
}

// File: contracts/swaper/Swapper.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.17;

import "contracts/lib/Ownable.sol";
import "contracts/lib/Withdrawable.sol";
import "contracts/swaper/IUniswapV2Router02.sol";

contract Swapper is Ownable, Withdrawable {
    address constant dead = 0x000000000000000000000000000000000000dEaD;
    IUniswapV2Router02 internal constant _uniswapV2Router =
        IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
    address public tokenAddress;

    constructor(address tokenAddress_) Withdrawable(msg.sender) {
        tokenAddress = tokenAddress_;
    }

    receive() external payable {}

    function swapEthForTokens() external onlyWithdrawer {
        // generate the uniswap pair path of token -> weth
        address[] memory path = new address[](2);
        path[0] = _uniswapV2Router.WETH();
        path[1] = tokenAddress;

        // make the swap
        _uniswapV2Router.swapExactETHForTokens{value: address(this).balance}(
            0,
            path,
            dead,
            block.timestamp
        );
    }

    function setTokenAddress(address tokenAddress_) external onlyWithdrawer {
        tokenAddress = tokenAddress_;
    }

    function swapExactEthForTokens(uint256 eth) external onlyWithdrawer {
        // generate the uniswap pair path of token -> weth
        address[] memory path = new address[](2);
        path[0] = _uniswapV2Router.WETH();
        path[1] = tokenAddress;

        // make the swap
        _uniswapV2Router.swapExactETHForTokens{value: eth}(
            0,
            path,
            dead,
            block.timestamp
        );
    }
}