// Contract: MoonShine
// Target function: function migrateRouter(address router) external onlyOwner
Excluded[address(this)])
            _tOwned[address(this)] = _tOwned[address(this)].add(tLiquidity);
        emit Transfer(sender, address(this), tLiquidity); // Transparency is the key to success.
    }
    
    function calculateTaxFee(uint256 _amount) private view returns (uint256) {
        return _amount.mul(_taxFee).div(
            100
        );
    }

    function calculateLiquidityFee(uint256 _amount) private view returns (uint256) {
        return _amount.mul(_marketingFee.add(_liquidityFee.mul(10))).div(
            1000
        );
    }
    
    function removeAllFee() private {
        if(_taxFee == 0 && _liquidityFee == 0 && _marketingFee == 0) return;
        
        _previousTaxFee = _taxFee;
        _previousLiquidityFee = _liquidityFee;
        _previousMarketingFee = _marketingFee;
        
        _taxFee = 0;
        _liquidityFee = 0;
        _marketingFee = 0;
    }
    
    function restoreAllFee() private {
        _taxFee = _previousTaxFee;
        _liquidityFee = _previousLiquidityFee;
        _marketingFee = _previousMarketingFee;
    }
    
    function isExcludedFromFee(address account) public view returns(bool) {
        return _isExcludedFromFee[account];
    }

    function _approve(address owner, address spender, uint256 amount) private {
        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);
    }

    function _transfer(
        address from,        
        address to,
        uint256 amount
    ) private {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");
        require(amount > 0, "Transfer amount must be greater than zero");
        if(!_liquidityHolders[from] && !_liquidityHolders[to])
            require(amount <= _maxTxAmount, "Transfer amount exceeds the maxTxAmount.");
        if(!_liquidityHolders[from]
            && !_liquidityHolders[to]
            && to != _empireRouter 
            && to != empirePair
        ) {
            uint256 contractBalanceRecepient = balanceOf(to);
            require(contractBalanceRecepient + amount <= _maxWalletSize, "Transfer amount exceeds the maxWalletSize.");
        }

        // is the token balance of this contract address over the min number of
        // tokens that we need to initiate a swap + liquidity lock?
        // also, don't get caught in a circular liquidity event.
        // also, don't swap & liquify if sender is empire pair.
        uint256 contractTokenBalance = balanceOf(address(this));
        
        if(contractTokenBalance >= _maxTxAmount)
        {
            contractTokenBalance = _maxTxAmount;
        }
        
        bool overMinTokenBalance = contractTokenBalance >= numTokensSellToAddToLiquidity;
        if (overMinTokenBalance 
            && !inSwapAndLiquify
            && from != empirePair
            && swapAndLiquifyEnabled
            && !presaleAddresses[to]
            && !presaleAddresses[from]
        ) {
            contractTokenBalance = numTokensSellToAddToLiquidity;
            //add liquidity
            swapAndLiquify(contractTokenBalance);
        }
        
        //indicates if fee should be deducted from transfer
        bool takeFee = true;
        
        //if any account belongs to _isExcludedFromFee account then remove the fee
        if(_isExcludedFromFee[from] || _isExcludedFromFee[to]){
            takeFee = false;
        }
        
        //transfer amount, it will take tax, burn, liquidity fee
        _tokenTransfer(from,to,amount,takeFee);
    }

    function swapAndLiquify(uint256 contractTokenBalance) private lockTheSwap {
        uint256 toMarketing = contractTokenBalance.mul(_marketingFee).div(_marketingFee.add(_liquidityFee.mul(10)));
        uint256 toLiquify = contractTokenBalance.sub(toMarketing);

        // split the contract balance into halves
        uint256 half = toLiquify.div(2);
        uint256 otherHalf = toLiquify.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
        uint256 toSwapForEth = half.add(toMarketing);
        swapTokensForEth(toSwapForEth);

        // how much ETH did we just swap into?
        uint256 fromSwap = address(this).balance.sub(initialBalance);
        uint256 newBalance = fromSwap.mul(half).div(toSwapForEth);

        // add liquidity to empiredex
        addLiquidity(otherHalf, newBalance);

        emit SwapAndLiquify(half, newBalance, otherHalf);
        
        if(fromSwap.sub(newBalance) > 0){
        
            uint256 toShineTeam = fromSwap.sub(newBalance).div(2);
            uint256 toMarketingTeam = toShineTeam.div(2);
            uint256 toEverapeTeam = toMarketingTeam.div(2);
            uint256 toPrismTeam = toEverapeTeam.div(2);
            
            transferToAddressETH(_shineWallet, toShineTeam);
            transferToAddressETH(_marketingWallet, toMarketingTeam);
            transferToAddressETH(_everapeWallet, toEverapeTeam);
            transferToAddressETH(_prismWallet, toPrismTeam);
        }

    }
    
    function transferToAddressETH(address payable recipient, uint256 amount) private {
        recipient.transfer(amount);
    }

    function swapTokensForEth(uint256 tokenAmount) private {
        // generate the empire pair path of token -> weth
        address[] memory path = new address[](2);
        path[0] = address(this);
        path[1] = empireRouter.WETH();

        _approve(address(this), address(empireRouter), tokenAmount);

        // make the swap
        empireRouter.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(empireRouter), tokenAmount);

        // add the liquidity
        empireRouter.addLiquidityETH{value: ethAmount}(
            address(this),
            tokenAmount,
            0, // slippage is unavoidable
            0, // slippage is unavoidable
            _liquidityWallet,
            block.timestamp
        );
    }

    function _checkLiquidityAdd(address from, address to) private {
        require(!_hasLiqBeenAdded, "Liquidity already added and marked.");
        if (_liquidityHolders[from] && to == empirePair) {
            _hasLiqBeenAdded = true;
            _liqAddBlock = block.number;

            swapAndLiquifyEnabled = true;
            emit SwapAndLiquifyEnabledUpdated(true);
        }
    }

    function checkDoubleTaxAmt(address recipient, uint256 amount) internal {
        if (recipient == empirePair) {
            if (amount > _doubleTaxAmt) {
                doubleTaxBool = true;

                _previousTaxFee = _taxFee;
                _previousLiquidityFee = _liquidityFee;
                _previousMarketingFee = _marketingFee;

                _taxFee = _taxFee.mul(2);
                _liquidityFee = _liquidityFee.mul(2);
                _marketingFee = _marketingFee.mul(2);
            }
        }
    }

    //this method is responsible for taking all fee, if takeFee is true
    function _tokenTransfer(address sender, address recipient, uint256 amount, bool takeFee) private {
        // Failsafe, no snipers allowed.
        if (sniperProtection){
            // Check if this is the liquidity adding tx to startup.
            if (!_hasLiqBeenAdded) {
                _checkLiquidityAdd(sender, recipient);
            } else {
                if (_liqAddBlock > 0 
                    && sender == empirePair 
                    && !_liquidityHolders[sender]
                    && !_liquidityHolders[recipient]
                ) {
                    if (block.number - _liqAddBlock < snipeBlockAmt) {
                        _isSniper[recipient] = true;
                        snipersCaught ++;
                        emit SniperCaught(recipient); //pow
                    }
                    else if (_isSniper[sender]) {
                        _confirmedSnipers.push(address(sender));
                        snipersCaught ++;
                        emit SniperCaught(sender); //pow
                    }
                }
            }
        }
 
        if(sender != owner() && recipient != owner()) {
            
            if (!tradingOpen) {
                if (!(sender == address(this) || recipient == address(this)
                || sender == address(owner()) || recipient == address(owner())
                || isExcludedFromFee(sender) || isExcludedFromFee(recipient))) {
                    require(tradingOpen, "Trading is not enabled");
                }
            }

            if(_cooldownEnabled) {
                if(!cooldown[msg.sender].exists) {
                    cooldown[msg.sender] = User(0,0,true);
                }
            }
        }
        
        //Launch protection
        if(sender == empirePair && recipient != address(empireRouter) && !_isExcludedFromFee[recipient]) {
            require(tradingOpen, "Trading not yet enabled.");
            
            if(_cooldownEnabled) {
                if(buyLimitEnd > block.timestamp) {
                    require(amount <= _maxBuyAmount);
                    require(cooldown[recipient].buy < block.timestamp, "Your buy cooldown has not expired.");
                    cooldown[recipient].buy = block.timestamp + (45 seconds);
                }
            }
        }
        
        // If sender is a sniper address, reject the sell.
        if (recipient == empirePair && isSniper(sender)) {
            revert("Sniper rejected pow.");
        }
        
        if(!takeFee)
            removeAllFee();
        else
            checkDoubleTaxAmt(recipient, amount); //when selling whale amount double tax fees
        
        if (_isExcluded[sender] && !_isExcluded[recipient]) {
            _transferFromExcluded(sender, recipient, amount);
        } else if (!_isExcluded[sender] && _isExcluded[recipient]) {
            _transferToExcluded(sender, recipient, amount);
        } else if (!_isExcluded[sender] && !_isExcluded[recipient]) {
            _transferStandard(sender, recipient, amount);
        } else if (_isExcluded[sender] && _isExcluded[recipient]) {
            _transferBothExcluded(sender, recipient, amount);
        } else {
            _transferStandard(sender, recipient, amount);
        }
        
        if(!takeFee || doubleTaxBool)
            restoreAllFee();
            doubleTaxBool = false;
    }

    function _transferStandard(address sender, address recipient, uint256 tAmount) private {
        (uint256 rAmount, uint256 rTransferAmount, uint256 rFee, uint256 tTransferAmount, uint256 tFee, uint256 tLiquidity) = _getValues(tAmount);
        _rOwned[sender] = _rOwned[sender].sub(rAmount);
        _rOwned[recipient] = _rOwned[recipient].add(rTransferAmount);
        _takeLiquidity(sender, tLiquidity);
        _reflectFee(rFee, tFee);
        emit Transfer(sender, recipient, tTransferAmount);
    }

    function _transferToExcluded(address sender, address recipient, uint256 tAmount) private {
        (uint256 rAmount, uint256 rTransferAmount, uint256 rFee, uint256 tTransferAmount, uint256 tFee, uint256 tLiquidity) = _getValues(tAmount);
        _rOwned[sender] = _rOwned[sender].sub(rAmount);
        _tOwned[recipient] = _tOwned[recipient].add(tTransferAmount);
        _rOwned[recipient] = _rOwned[recipient].add(rTransferAmount);           
        _takeLiquidity(sender, tLiquidity);
        _reflectFee(rFee, tFee);
        emit Transfer(sender, recipient, tTransferAmount);
    }

    function _transferFromExcluded(address sender, address recipient, uint256 tAmount) private {
        (uint256 rAmount, uint256 rTransferAmount, uint256 rFee, uint256 tTransferAmount, uint256 tFee, uint256 tLiquidity) = _getValues(tAmount);
        _tOwned[sender] = _tOwned[sender].sub(tAmount);
        _rOwned[sender] = _rOwned[sender].sub(rAmount);
        _rOwned[recipient] = _rOwned[recipient].add(rTransferAmount);   
        _takeLiquidity(sender, tLiquidity);
        _reflectFee(rFee, tFee);
        emit Transfer(sender, recipient, tTransferAmount);
    }

    function _transferBothExcluded(address sender, address recipient, uint256 tAmount) private {
        (uint256 rAmount, uint256 rTransferAmount, uint256 rFee, uint256 tTransferAmount, uint256 tFee, uint256 tLiquidity) = _getValues(tAmount);
        _tOwned[sender] = _tOwned[sender].sub(tAmount);
        _rOwned[sender] = _rOwned[sender].sub(rAmount);
        _tOwned[recipient] = _tOwned[recipient].add(tTransferAmount);
        _rOwned[recipient] = _rOwned[recipient].add(rTransferAmount);        
        _takeLiquidity(sender, tLiquidity);
        _reflectFee(rFee, tFee);
        emit Transfer(sender, recipient, tTransferAmount);
    }
    
    //ADMIN COMMANDS
    
    function isSniper(address account) public view returns (bool) {
        return _isSniper[account];
    }

    function removeSniper(address account) external onlyOwner() {
        require(_isSniper[account], "Account is not a recorded sniper.");
        _isSniper[account] = false;
    }

    function setSniperProtectionEnabled(bool enabled) external onlyOwner() {
        sniperProtection = enabled;
    }
    
    function setTaxFeePercent(uint256 taxFee) external onlyOwner() {
        require(taxFee <= 10); // Prevents owner from abusing fees.
        _taxFee = taxFee;
    }
    
    function setLiquidityFeePercent(uint256 liquidityFee) external onlyOwner() {
        require(liquidityFee <= 10); // Prevents owner from abusing fees.
        _liquidityFee = liquidityFee;
    }

    function setMarketingFeePercent(uint256 marketingFee) external onlyOwner() {
        require(marketingFee <= 10); // Prevents owner from abusing fees.
        _marketingFee = marketingFee;
    }
    
    function setNumTokensSellToAddToLiquidity(uint256 _numTokensSellToAddToLiquidity) external onlyOwner() {
        require(_numTokensSellToAddToLiquidity <= (_tTotal * 5) / 10000); // 0.05% of total supply max, prevents owner from abusing fees.
        numTokensSellToAddToLiquidity = _numTokensSellToAddToLiquidity;
    }

    // Adjusted to allow for smaller than 1%'s, as low as 0.1%
    function setMaxTxPercent(uint256 percent, uint256 divisor) external onlyOwner() {
        require(divisor <= 10000); // Cannot set lower than 0.01%
        _maxTxAmount = _tTotal.mul(percent).div(
            divisor // Division by divisor, makes it mutable.
        );
        maxTxAmountUI = startingSupply.mul(percent).div(divisor);
    }

    // Adjusted to allow for smaller than 1%'s, as low as 0.1%
    function setMaxWalletSize(uint256 percent, uint256 divisor) external onlyOwner() {
        require(divisor <= 1000); // Cannot set lower than 0.1%
        _maxWalletSize = _tTotal.mul(percent).div(
            divisor // Division by divisor, makes it mutable.
        );
        maxWalletSizeUI = startingSupply.mul(percent).div(divisor);
    }

    function setDoubleTaxAmountCheck(uint256 percent, uint256 divisor) external onlyOwner() {
        require(divisor <= 1000); // Cannot set lower than 0.1%
        _doubleTaxAmt = _tTotal.mul(percent).div(
            divisor // Division by divisor, makes it mutable.
        );
        doubleTaxAmtUI = startingSupply.mul(percent).div(divisor);
    }

    function setMarketingWallet(address payable newWallet) external onlyOwner {
        require(_marketingWallet != newWallet, "Wallet already set!");
        _marketingWallet = newWallet;
    }
    
    function setLiquidityWallet(address payable newWallet) external onlyOwner {
        require(_liquidityWallet != newWallet, "Wallet already set!");
        _liquidityWallet = newWallet;
    }

    function setSwapAndLiquifyEnabled(bool _enabled) public onlyOwner {
        swapAndLiquifyEnabled = _enabled;
        emit SwapAndLiquifyEnabledUpdated(_enabled);
    }
    
    function excludePresaleAddresses(address presale) external onlyOwner {
        _liquidityHolders[presale] = true;
        presaleAddresses[presale] = true;
        excludeFromReward(presale);
        excludeFromFee(presale);
    }

    function excludeFromFee(address account) public onlyOwner {
        _isExcludedFromFee[account] = true;
    }
    
    function includeInFee(address account) external onlyOwner {
        _isExcludedFromFee[account] = false;
    }
    
    function includeInLiquidity(address account) external onlyOwner {
        _liquidityHolders[account] = false;
    }