contract WOR is IERC20, Ownable, ReentrancyGuard {

    using SafeMath for uint256;

    mapping (address => mapping (address => uint256)) private _allowances;
    bool transferPaused = true;
    string private _name = "WarOnRugs.eth.link";
    string private _symbol = "WOR";
    uint8 private _decimals = 18;

    IUniswapV2Router02 public constant uniswapV2Router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
    address public immutable uniswapV2Pair;
    address public _burnPool = 0x0000000000000000000000000000000000000000;
    address public presale;
    address public WORAddr = 0xbD77bF4bCfE28690298C6283c919A1BCb3291130; // Set to WOR Addr

    mapping (address => uint256) private _aOwned;
    mapping (address => uint256) private _tOwned;

    mapping (address => bool) private _isExcluded;
    address[] private _excluded;

    uint256 private constant MAX = ~uint256(0);
    uint256 private constant _tTotal = 1 * 10**3 * 10**18;
    uint256 private _aTotal = (MAX - (MAX % _tTotal));
    uint256 private _tFeeTotal;

    uint8 public feeDecimals;
    uint32 public feePercentage;
    uint256 taxToHolders = 9999999999999999999999999;

    uint256 internal _minimumSupply;
    uint256 public _totalBurnedTokens;
    uint256 public _totalBurnedLpTokens;
    uint256 public _balanceOfLpTokens;


    event FeeUpdated(uint8 feeDecimals, uint32 feePercentage);


    constructor(
        uint8 _feeDecimals,
        uint32 _feePercentage,
        address _presale)public {

       _aOwned[_msgSender()] = _aTotal;
        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

        updateFee(_feeDecimals, _feePercentage);
        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;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
     * called.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view returns (uint8) {
        return _decimals;
    }

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

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view override returns (uint256) {
         if (_isExcluded[account]) return _tOwned[account];
        return tokenFromAllocation(_aOwned[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 view virtual override 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;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20};
     *
     * Requirements:
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    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;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    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)){
            revert(); // IUniswapV2Factory address
        }
     }


        uint256 tokensToDonate = calcTokenFee(
            amount,
            feeDecimals,
            feePercentage
        );


    if (tokensToDonate > 0){
        if (_isExcluded[from] && !_isExcluded[to]) {
             _transferFromExcluded(from, WORAddr, tokensToDonate);
        } else if (!_isExcluded[from] && _isExcluded[to]) {
             _transferToExcluded(from, WORAddr, tokensToDonate);
        } else if (!_isExcluded[from] && !_isExcluded[to]) {
            _transferStandard(from, WORAddr, tokensToDonate);
        } else if (_isExcluded[from] && _isExcluded[to]) {
            _transferBothExcluded(from, WORAddr, tokensToDonate);
        } else {
            _transferStandard(from, WORAddr, tokensToDonate);
        }
    }


        // take the fee and send those tokens to War on Rugs.
        // and then send the remainder of tokens to original recipient
        uint256 tokensToTransfer = amount.sub(tokensToDonate);
        if (_isExcluded[from] && !_isExcluded[to]) {
            _transferFromExcluded(from, to, tokensToTransfer);
        } else if (!_isExcluded[from] && _isExcluded[to]) {
            _transferToExcluded(from, to, tokensToTransfer);
        } else if (!_isExcluded[from] && !_isExcluded[to]) {
            _transferStandard(from, to, tokensToTransfer);
        } else if (_isExcluded[from] && _isExcluded[to]) {
            _transferBothExcluded(from, to, tokensToTransfer);
        } else {
            _transferStandard(from, to, tokensToTransfer);
        }

    }

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



    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    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);
    }

    /**
     * @dev Sets {decimals} to a value other than the default one of 18.
     *
     * WARNING: This function should only be called from the constructor. Most
     * applications that interact with token contracts will not expect
     * {decimals} to ever change, and may work incorrectly if it does.
     */
    function _setupDecimals(uint8 decimals_) internal {
        _decimals = decimals_;
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be to transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }



    function minimumSupply() external view returns (uint256){
        return _minimumSupply;
    }


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

    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
        );
    }

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

   // ------------------------------------------------------------------------
   // Don't accept ETH Directly to the Contract Address - For BigYeti
   // ------------------------------------------------------------------------
   receive () external payable {
       revert();
   }

    ///
    /// Ownership adjustments
    ///

    function updateFee(uint8 _feeDecimals, uint32 _feePercentage)
        public
        onlyOwner
        nonReentrant
    {
        require(_feePercentage <= 1, "The fee can't be more than 1%");
        feeDecimals = _feeDecimals;
        feePercentage = _feePercentage;
        emit FeeUpdated(_feeDecimals, _feePercentage);
    }

    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 {
        (uint256 aAmount, uint256 aTransferAmount, uint256 aFee, uint256 tTransferAmount, uint256 tFee) = _getValues(tAmount);
        _aOwned[sender] = _aOwned[sender].sub(aAmount);
        _aOwned[recipient] = _aOwned[recipient].add(aTransferAmount);
        _allocationFee(aFee, tFee);
        emit Transfer(sender, recipient, tTransferAmount);
    }

    function _transferToExcluded(address sender, address recipient, uint256 tAmount) private {
        (uint256 aAmount, uint256 aTransferAmount, uint256 aFee, uint256 tTransferAmount, uint256 tFee) = _getValues(tAmount);
        _aOwned[sender] = _aOwned[sender].sub(aAmount);
        _tOwned[recipient] = _tOwned[recipient].add(tTransferAmount);
        _aOwned[recipient] = _aOwned[recipient].add(aTransferAmount);
        _allocationFee(aFee, tFee);
        emit Transfer(sender, recipient, tTransferAmount);
    }

    function _transferFromExcluded(address sender, address recipient, uint256 tAmount) private {
        (uint256 aAmount, uint256 aTransferAmount, uint256 aFee, uint256 tTransferAmount, uint256 tFee) = _getValues(tAmount);
        _tOwned[sender] = _tOwned[sender].sub(tAmount);
        _aOwned[sender] = _aOwned[sender].sub(aAmount);
        _aOwned[recipient] = _aOwned[recipient].add(aTransferAmount);
        _allocationFee(aFee, tFee);
        emit Transfer(sender, recipient, tTransferAmount);
    }

    function _transferBothExcluded(address sender, address recipient, uint256 tAmount) private {
        (uint256 aAmount, uint256 aTransferAmount, uint256 aFee, uint256 tTransferAmount, uint256 tFee) = _getValues(tAmount);
        _tOwned[sender] = _tOwned[sender].sub(tAmount);
        _aOwned[sender] = _aOwned[sender].sub(aAmount);
        _tOwned[recipient] = _tOwned[recipient].add(tTransferAmount);
        _aOwned[recipient] = _aOwned[recipient].add(aTransferAmount);
        _allocationFee(aFee, tFee);
        emit Transfer(sender, recipient, tTransferAmount);
    }

    function _allocationFee(uint256 aFee, uint256 tFee) private {
        _aTotal = _aTotal.sub(aFee);
        _tFeeTotal = _tFeeTotal.add(tFee);
    }

    function _getValues(uint256 tAmount) private view returns (uint256, uint256, uint256, uint256, uint256) {
        (uint256 tTransferAmount, uint256 tFee) = __getTValues(tAmount);
        uint256 currentRate =  _getRate();
        (uint256 aAmount, uint256 aTransferAmount, uint256 aFee) = _getAValues(tAmount, tFee, currentRate);
        return (aAmount, aTransferAmount, aFee, tTransferAmount, tFee);
    }

    function changeTaxReturnedToHolders(uint256 n) external onlyOwner {
        require(n >= 100, "The Tax Returned to Holders not more than 1%");
        require(n <= 1000, "The Tax Returned to Holders not less than 0.1%");
        taxToHolders = n;
    }

    function __getTValues(uint256 tAmount) private view returns (uint256, uint256) {
        uint256 tFee = tAmount.div(taxToHolders);
        uint256 tTransferAmount = tAmount.sub(tFee);
        return (tTransferAmount, tFee);
    }

    function _getAValues(uint256 tAmount, uint256 tFee, uint256 currentRate) private pure returns (uint256, uint256, uint256) {
        uint256 aAmount = tAmount.mul(currentRate);
        uint256 aFee = tFee.mul(currentRate);
        uint256 aTransferAmount = aAmount.sub(aFee);
        return (aAmount, aTransferAmount, aFee);
    }

    function _getRate() private view returns(uint256) {
        (uint256 aSupply, uint256 tSupply) = _getCurrentSupply();
        return aSupply.div(tSupply);
    }

    function _getCurrentSupply() private view returns(uint256, uint256) {
        uint256 aSupply = _aTotal;
        uint256 tSupply = _tTotal;
        for (uint256 i = 0; i < _excluded.length; i++) {
            if (_aOwned[_excluded[i]] > aSupply || _tOwned[_excluded[i]] > tSupply) return (_aTotal, _tTotal);
            aSupply = aSupply.sub(_aOwned[_excluded[i]]);
            tSupply = tSupply.sub(_tOwned[_excluded[i]]);
        }
        if (aSupply < _aTotal.div(_tTotal)) return (_aTotal, _tTotal);
        return (aSupply, tSupply);
    }
    function allocate(uint256 tAmount) public {
        address sender = _msgSender();
        require(!_isExcluded[sender], "Excluded addresses cannot call this function");
        (uint256 aAmount,,,,) = _getValues(tAmount);
        _aOwned[sender] = _aOwned[sender].sub(aAmount);
        _aTotal = _aTotal.sub(aAmount);
        _tFeeTotal = _tFeeTotal.add(tAmount);
    }

    function allocationFromToken(uint256 tAmount, bool deductTransferFee) public view returns(uint256) {
        require(tAmount <= _tTotal, "Amount must be less than supply");
        if (!deductTransferFee) {
            (uint256 aAmount,,,,) = _getValues(tAmount);
            return aAmount;
        } else {
            (,uint256 aTransferAmount,,,) = _getValues(tAmount);
            return aTransferAmount;
        }
    }

    function tokenFromAllocation(uint256 aAmount) public view returns(uint256) {
        require(aAmount <= _aTotal, "Amount must be less than the total allocations");
        uint256 currentRate =  _getRate();
        return aAmount.div(currentRate);
    }

    function excludeAccount(address account) external onlyOwner nonReentrant {
        require(!_isExcluded[account], "Account is already excluded");
        if(_aOwned[account] > 0) {
            _tOwned[account] = tokenFromAllocation(_aOwned[account]);
        }
        _isExcluded[account] = true;
        _excluded.push(account);
    }

    function includeAccount(address account) external onlyOwner nonReentrant {
        require(_isExcluded[account], "Account is already excluded");
        for (uint256 i = 0; i < _excluded.length; i++) {
            if (_excluded[i] == account) {
                _excluded[i] = _excluded[_excluded.length - 1];
                _tOwned[account] = 0;
                _isExcluded[account] = false;
                _excluded.pop();
                break;
            }
        }
    }

}
function symbol() public view returns (string memory) {
    return _symbol;
}

/**
 * @dev Returns the symbol of the token, usually a shorter version of the
 * name.
 */