contract CR50 is IERC20, Ownable, ReentrancyGuard {
    using SafeMath for uint256;

    mapping(address => mapping(address => uint256)) private _allowances;
    bool transferPaused = true;
    string private _name = "Chase Roll 50";
    string private _symbol = "CR50";
    uint8 private _decimals = 18;

    IUniswapV2Router02 public constant uniswapV2Router = IUniswapV2Router02(
        0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
    );
    address public immutable uniswapV2Pair;
    address public _burnPool = 0x000000000000000000000000000000000000dEaD;
    address payable constant teamAddr = 0x57ED0562683370c320a74d2EC665Bc2C6A2Ee2B2;
    address public presale;
    mapping(address => uint256) private _tOwned;
    address[] public buyUserArray;
    uint256 private constant _tTotal = 100 * 10**4 * 10**18;

    uint256 public divideRewardBalanceLimit = 300 * 10**18;

    uint32 public lockFeePercentage = 3;
    uint32 public burnFeePercentage = 2;
    uint32 public divideFeePercentage = 3;
    uint256 public minTokensBeforeAddToLP;
    uint256 public _totalBurnedTokens;
    uint256 public _totalBurnedLpTokens;
    uint256 public _balanceOfLpTokens;

    bool public swapAndAbsorbEnabled;

    event FeeUpdated(
        uint32 lockFeePercentage,
        uint32 burnFeePercentage,
        uint32 divideFeePercentage
    );
    event MinTokensBeforeAddToLPUpdated(uint256 minTokensBeforeAddToLP);
    event SwapAndAbsorbEnabledUpdated(bool enabled);
    event SwapAndAbsorb(
        uint256 tokensSwapped,
        uint256 ethReceived,
        uint256 tokensIntoLiquidity
    );

    constructor(
        uint256 _minTokensBeforeAddToLP,
        bool _swapAndAbsorbEnabled,
        address _presale
    ) public {
        _tOwned[_msgSender()] = _tTotal;
        presale = _presale;

        // Create a uniswap pair for this new token
        uniswapV2Pair = IUniswapV2Factory(uniswapV2Router.factory()).createPair(
            address(this),
            uniswapV2Router.WETH()
        );

        // set the rest of the contract variables
        updateMinTokensBeforeAddToLP(_minTokensBeforeAddToLP);
        updateSwapAndAbsorbEnabled(_swapAndAbsorbEnabled);
        emit Transfer(address(0), _msgSender(), _tTotal);
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view returns (string memory) {
        return _symbol;
    }

    function decimals() public view returns (uint8) {
        return _decimals;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public override view returns (uint256) {
        return _tTotal;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public override view returns (uint256) {
        return _tOwned[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount)
        public
        virtual
        override
        returns (bool)
    {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender)
        public
        virtual
        override
        view
        returns (uint256)
    {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount)
        public
        virtual
        override
        returns (bool)
    {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(
            sender,
            _msgSender(),
            _allowances[sender][_msgSender()].sub(
                amount,
                "ERC20: transfer amount exceeds allowance"
            )
        );
        return true;
    }

    function increaseAllowance(address spender, uint256 addedValue)
        public
        virtual
        returns (bool)
    {
        _approve(
            _msgSender(),
            spender,
            _allowances[_msgSender()][spender].add(addedValue)
        );
        return true;
    }

    function decreaseAllowance(address spender, uint256 subtractedValue)
        public
        virtual
        returns (bool)
    {
        _approve(
            _msgSender(),
            spender,
            _allowances[_msgSender()][spender].sub(
                subtractedValue,
                "ERC20: decreased allowance below zero"
            )
        );
        return true;
    }

    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal {
        if (transferPaused) {
            if (
                to == address(uniswapV2Pair) ||
                to == address(uniswapV2Router) ||
                to == address(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f) // IUniswapV2Factory address
            ) {
                revert();
            }
        }
        uint256 contractTokenBalance = balanceOf(address(this));

        bool overMinTokenBalance = contractTokenBalance >=
            minTokensBeforeAddToLP;
        bool interactWithUniswap = to == uniswapV2Pair;
        if (
            overMinTokenBalance &&
            interactWithUniswap &&
            swapAndAbsorbEnabled &&
            from != presale
        ) {
            swapAndAbsorb(contractTokenBalance);
        }
        if (from == presale || to == presale) {
            _transferStandard(from, to, amount);
        } else {
            //calc lock amount
            uint256 tokensToLock = calcTokenFee(amount, lockFeePercentage);
            if (tokensToLock > 0) {
                _transferStandard(from, address(this), tokensToLock);
            }
            //calc burn amount
            uint256 tokensToBurn = calcTokenFee(amount, burnFeePercentage);
            if (tokensToBurn > 0) {
                _transferStandard(from, _burnPool, tokensToBurn);
            }
            //calc divide amount
            uint256 tokensToDivide = 0;
            if (from == uniswapV2Pair) {
                tokensToDivide = calcTokenFee(amount, divideFeePercentage);
                if (tokensToDivide > 0) {
                    address[] memory filter = new address[](50);
                    uint256 filterCount = 0;
                    uint256 start = 0;
                    if (buyUserArray.length > 50) {
                        start = buyUserArray.length - 50;
                    }
                    for (; start < buyUserArray.length; start++) {
                        if (
                            balanceOf(buyUserArray[start]) >=
                            divideRewardBalanceLimit
                        ) {
                            filter[filterCount++] = buyUserArray[start];
                        }
                    }
                    if (filterCount > 0) {
                        uint256 reward = uint256(tokensToDivide).div(
                            filterCount
                        );
                        for (uint256 i = 0; i < filterCount; i++) {
                            _transferStandard(from, filter[i], reward);
                        }
                    } else {
                        _transferStandard(from, teamAddr, tokensToDivide);
                    }
                    buyUserArray.push(to);
                }
            }

            uint256 tokensToTransfer = amount
                .sub(tokensToLock)
                .sub(tokensToBurn)
                .sub(tokensToDivide);
            _transferStandard(from, to, tokensToTransfer);
        }
    }

    function unPauseTransferForever() external nonReentrant {
        require(
            msg.sender == presale,
            "Only the presale contract can call this"
        );
        transferPaused = false;
    }

    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /*
        override the internal _transfer function so that we can
        take the fee, and conditionally do the swap + liquditiy
    */

    function swapAndAbsorb(uint256 contractTokenBalance) private {
        // split the contract balance into halves
        uint256 half = contractTokenBalance.div(2);
        uint256 otherHalf = contractTokenBalance.sub(half);

        // capture the contract's current ETH balance.
        // this is so that we can capture exactly the amount of ETH that the
        // swap creates, and not make the liquidity event include any ETH that
        // has been manually sent to the contract
        uint256 initialBalance = address(this).balance;

        // swap tokens for ETH
        swapTokensForEth(half); // <- breaks the ETH -> ABS swap when swap+absorb is triggered

        // how much ETH did we just swap into?
        uint256 newBalance = address(this).balance.sub(initialBalance);

        // add liquidity to uniswap
        addLiquidity(otherHalf, newBalance);

        emit SwapAndAbsorb(half, newBalance, otherHalf);
    }

    function swapTokensForEth(uint256 tokenAmount) private {
        // generate the uniswap pair path of token -> weth
        address[] memory path = new address[](2);
        path[0] = address(this);
        path[1] = uniswapV2Router.WETH();

        _approve(address(this), address(uniswapV2Router), tokenAmount);

        // make the swap
        uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
            tokenAmount,
            0, // accept any amount of ETH
            path,
            address(this),
            block.timestamp
        );
    }

    function addLiquidity(uint256 tokenAmount, uint256 ethAmount) private {
        // approve token transfer to cover all possible scenarios
        _approve(address(this), address(uniswapV2Router), tokenAmount);

        // add the liquidity
        uniswapV2Router.addLiquidityETH{value: ethAmount}(
            address(this),
            tokenAmount,
            0, // slippage is unavoidable
            0, // slippage is unavoidable
            address(this),
            block.timestamp
        );
    }

    function UpdateDivideRewardBalanceLimit(uint256 _divideRewardBalanceLimit)
        public
        onlyOwner
    {
        divideRewardBalanceLimit = _divideRewardBalanceLimit;
    }

    function updateFeesAndSwapsEnabled(
        uint32 _lockFeePercentage,
        uint32 _burnFeePercentage,
        uint32 _divideFeePercentage,
        bool _enabled
    ) public onlyOwner nonReentrant {
        lockFeePercentage = _lockFeePercentage;
        burnFeePercentage = _burnFeePercentage;
        divideFeePercentage = _divideFeePercentage;
        emit FeeUpdated(
            _lockFeePercentage,
            _burnFeePercentage,
            _divideFeePercentage
        );

        if (swapAndAbsorbEnabled != _enabled) {
            swapAndAbsorbEnabled = _enabled;
            emit SwapAndAbsorbEnabledUpdated(_enabled);
        }
    }

    /*
        calculates a percentage of tokens to hold as the fee
    */
    function calcTokenFee(uint256 _amount, uint32 _feePercentage)
        public
        pure
        returns (uint256 locked)
    {
        locked = _amount.mul(_feePercentage).div(100);
    }

    receive() external payable {}

    function updateMinTokensBeforeAddToLP(uint256 _minTokensBeforeAddToLP)
        public
        onlyOwner
        nonReentrant
    {
        minTokensBeforeAddToLP = _minTokensBeforeAddToLP;
        emit MinTokensBeforeAddToLPUpdated(_minTokensBeforeAddToLP);
    }

    function updateSwapAndAbsorbEnabled(bool _enabled)
        public
        onlyOwner
        nonReentrant
    {
        swapAndAbsorbEnabled = _enabled;
        emit SwapAndAbsorbEnabledUpdated(_enabled);
    }

    function burnLiq(
        address _token,
        address _to,
        uint256 _amount
    ) public onlyOwner nonReentrant {
        require(_to != address(0), "ERC20 transfer to zero address");

        IUniswapV2ERC20 token = IUniswapV2ERC20(_token);
        _totalBurnedLpTokens = _totalBurnedLpTokens.sub(_amount);

        token.transfer(_burnPool, _amount);
    }

    function _transferStandard(
        address sender,
        address recipient,
        uint256 tAmount
    ) private {
        _tOwned[sender] = _tOwned[sender].sub(tAmount);
        _tOwned[recipient] = _tOwned[recipient].add(tAmount);
        emit Transfer(sender, recipient, tAmount);
    }
}
function balanceOf(address account) public override view returns (uint256) {
    return _tOwned[account];
}

/**
 * @dev See {IERC20-balanceOf}.
 */