pragma solidity ^0.8.0;

contract Pauly0x is Ownable, ERC20 {
    uint256 public immutable maxSupply = 1_000_000_000 * (10 ** decimals());
    uint16 public constant MAX_PERCENTAGE = 10000;

    bool public initialized = false;
    bool public openTrading = false;
    address public uniswapV2Pair = address(0);
    address public taxWallet;

    uint16 private _initialBuyFee = 1000; // 2%
    uint16 private _initialSellFee = 1500; // 10%
    uint16 private _txRemoveInitialBuyFee = 10; // If buyCount < this one, => use initial buy fee, else use current buy fee
    uint16 private _txRemoveInitialSellFee = 10; // If buyCount < this one, => use initial sell fee, else use current sell fee
    uint16 private _txStartSwap = 25; // After 5 first buy transactions => do check swap

    uint16 public buyFee = 500; // 1%
    uint16 public sellFee = 500; // 1%

    bool private inSwap = false;
    IUniswapV2Router02 private router;

    uint256 public maxTxAmount;
    uint256 public maxWalletSize;
    uint256 public taxSwapThreshold;
    uint256 public maxTaxSwap;
    uint256 private _buyCount = 0;

    string private constant _name = unicode"0𝕏PAULY";
    string private constant _symbol = unicode"0𝕏PAULY";

    event UpdatedRouter(address oldRouter, address newRouter);
    event SwapOutState(string message);
    event UpdatedTxAmountAndWalletSize(uint256 _maxTxAmount, uint256 _maxWalletSize);

    modifier lockDuringSwap {
        inSwap = true;
        _;
        inSwap = false;
    }

    receive() external payable {}

    fallback() external payable {}

    constructor() ERC20(_name, _symbol) {
        _mint(address(this), maxSupply);
        initialized = true;
        taxWallet = _msgSender(); // Fee after swap will be stored in here

        maxTaxSwap = maxSupply / 100; // Only swap maximum 1% token per each
        taxSwapThreshold = 0; // If balance of contract > 0.5% total supply, => do swap, maxSupply / 200
        maxTxAmount = maxSupply / 50; // Max token can buy a time, 2%
        maxWalletSize = maxSupply / 50; // Max wallet size can a time, 2%

        emit UpdatedTxAmountAndWalletSize(maxTxAmount, maxWalletSize);

        // FOR BSCSCAN
        // _setRouter(0x10ED43C718714eb63d5aA57B78B54704E256024E);

        // FOR ETHEREUM
        // _setRouter(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);

        // FOR GOERLI
        _setRouter(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
    }

    function removeLimits() external onlyOwner {
        maxTxAmount = maxSupply;
        maxWalletSize = maxSupply;
        emit UpdatedTxAmountAndWalletSize(maxTxAmount, maxWalletSize);
    }

    function setOpenTrading() external onlyOwner {
        _approve(address(this), address(router), maxSupply);
        uniswapV2Pair = IUniswapV2Factory(router.factory()).createPair(address(this), router.WETH());
        router.addLiquidityETH{value: address(this).balance}(address(this), balanceOf(address(this)), 0, 0, _msgSender(), block.timestamp + 5 minutes);
        IERC20(uniswapV2Pair).approve(address(router), type(uint).max);
        openTrading = true;
    }

    function withdraw() external {
        require (openTrading == true || _msgSender() == owner(), "Contract::not in open trading state");
        (bool success, ) = payable(taxWallet).call{
            value: address(this).balance
        }("");
        require(success, "Contract::withdraw failed");
    }

    function setTaxWallet(address _taxWallet) external onlyOwner {
        taxWallet = _taxWallet;
    }

    function setRouter(address _router) external onlyOwner {
        _setRouter(_router);
    }

    function setMaxTransactionAmount(uint16 _buyFee, uint16 _sellFee) external onlyOwner {
        buyFee = _buyFee;
        sellFee = _sellFee;
    }

    function setUniswapPair(address _uniswapV2Pair) external onlyOwner {
        _setUniswapV2Pair(_uniswapV2Pair);
    }

    function _setRouter(address _router) internal {
        emit UpdatedRouter(address(router), _router);
        router = IUniswapV2Router02(_router);
    }

    function _setUniswapV2Pair(address _uniswapV2Pair) internal {
        uniswapV2Pair = _uniswapV2Pair;
    }

    function min(uint256 a, uint256 b) internal pure returns (uint256){
      return (a > b) ? b: a;
    }

    function _swapTokensForEth(uint256 _amount) internal lockDuringSwap {
        uint256 _contractBalance = balanceOf(address(this));
        uint256 _swapAmount = min(_amount, min(_contractBalance, maxTaxSwap));

        if (_contractBalance > taxSwapThreshold) {
            address[] memory path = new address[](2);
            path[0] = address(this);
            path[1] = router.WETH();
            _approve(address(this), address(router), _swapAmount);
            try router.swapExactTokensForETHSupportingFeeOnTransferTokens(
                _swapAmount,
                0,
                path,
                taxWallet,
                block.timestamp + 5 minutes
            ) {
                emit SwapOutState("Swap out success...");
            }
            catch {
                emit SwapOutState("Swap out error...");
            }
        }
    }

    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal override(ERC20) {
        require(
            initialized == true,
            "Contract: trade is not availabel!"
        );

        uint256 _transferAmount = amount;

        uint256 _fee = 0;

        if (from != owner() && to != owner()) {
            if (from == uniswapV2Pair) {
                _fee = _buyCount < _txRemoveInitialBuyFee ? _initialBuyFee : buyFee;
                if (to != address(router) && to != taxWallet && to != address(this)) {
                    require(amount <= maxTxAmount, "Contract::exceeds maxTxAmount.");
                    require(balanceOf(to) + amount <= maxWalletSize, "Contract::exceeds maxWalletSize.");
                    _buyCount++;
                }
            }

            else if (to == uniswapV2Pair) {
                _fee = _buyCount < _txRemoveInitialSellFee ? _initialSellFee : sellFee;
            }

            if (to != uniswapV2Pair && to != taxWallet && to != address(this)) {
                require(balanceOf(to) + amount <= maxWalletSize, "Contract::exceeds maxWalletSize.");
            }

            if (!inSwap && to == uniswapV2Pair && _buyCount > _txStartSwap && openTrading == true) {
                _swapTokensForEth(amount);
            }

            if (_fee > 0) {
                uint256 _calculatedFee = amount * _fee / MAX_PERCENTAGE;
                _transferAmount = amount - _calculatedFee;
                super._transfer(from, address(this), _calculatedFee);
            }
        }

        super._transfer(from, to, _transferAmount);
    }
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"message","type":"string"}],"name":"SwapOutState","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldRouter","type":"address"},{"indexed":false,"internalType":"address","name":"newRouter","type":"address"}],"name":"UpdatedRouter","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_maxTxAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_maxWalletSize","type":"uint256"}],"name":"UpdatedTxAmountAndWalletSize","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"MAX_PERCENTAGE","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"buyFee","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTaxSwap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTxAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxWalletSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"openTrading","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"removeLimits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sellFee","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"_buyFee","type":"uint16"},{"internalType":"uint16","name":"_sellFee","type":"uint16"}],"name":"setMaxTransactionAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setOpenTrading","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_router","type":"address"}],"name":"setRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_taxWallet","type":"address"}],"name":"setTaxWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_uniswapV2Pair","type":"address"}],"name":"setUniswapPair","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"taxSwapThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"taxWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"uniswapV2Pair","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]