// Contract: FX99
// Target function: function tokensToEth(uint256 _tokens)
);
        _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;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(address sender, address recipient, uint256 amount) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements
     *
     * - `to` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
     *
     * This is 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 { }
}

contract FX99 is ERC20("FX99", "XCIX") {
    address public wallet_99_strong_hand = 0xdCD272B4e7597640f20287a1D6D525f4d0034394;
    uint256 public initialPrice = 0.001 szabo;
    uint256 public increment = 0.001 szabo;
    uint256 public minPurchase = 1 ether;
    uint256 public reinvestFee = 10;
    uint256 public transferFee = 10;
    uint256 public withdrawFee = 10;
    uint256 public referrerFee = 5;
    uint256 public buyFee = 10;
    uint256 public sellFee = 10;
    uint256 public dividendsPerShare;
    uint256 public start;
    uint256 constant internal magnitude = 1 ether;
    mapping (address => address) public referrer;
    mapping (address => uint256) public referralEarnings;
    mapping (address => int256) public dividendsPayouts;

    event Reinvest(address _member, uint256 _ether, uint256 _tokens);
    event Withdraw(address _member, uint256 _ether);
    event Buy(address _member, uint256 _ether, uint256 _tokens, address _referrer);
    event Sell(address _member, uint256 _ether, uint256 _tokens);

    constructor () public {
        start = now;
    }

    /**
     * Calculate Dividends
     */
    function getDividends(address _member)
        public
        view
        returns(uint256)
    {
        uint256 _balance = balanceOf(_member);
        uint256 _dividends = (uint256)((int256)(_balance.mul(dividendsPerShare)) - dividendsPayouts[_member])
        .div(magnitude);
        uint256 _contractBalance = address(this).balance;

        if (_dividends > _contractBalance) return _contractBalance;
        return _dividends;
    }

    /**
     * Reinvest
     */
    function reinvest()
        public
        returns(uint256)
    {
        uint256 _ether = getDividends(msg.sender);

        dividendsPayouts[msg.sender] = dividendsPayouts[msg.sender] 
            + (int256)(_ether.mul(magnitude));

        _ether = _ether
        .add(referralEarnings[msg.sender]);
        referralEarnings[msg.sender] = 0;

        uint256 _fee = _ether.mul(reinvestFee).div(100);
        uint256 _reinvest = _ether.sub(_fee);

        uint256 _tokens = ethToTokens(_reinvest);

        require (_tokens >= minPurchase, 
            'Token equivalent amount should not be less than 1 token');

        dividendsPayouts[msg.sender] = dividendsPayouts[msg.sender]
            + (int256)(dividendsPerShare.mul(_tokens));

        emit Reinvest(msg.sender, _ether, _tokens);
        _mint(msg.sender, _tokens);

        uint256 _supply = totalSupply();
        if (_supply > 0) {
            dividendsPerShare = dividendsPerShare
            .add(_fee.mul(magnitude).div(_supply));
        } else {
            dividendsPerShare = 0;
        }        
    }

    /**
     * Withdraw dividends
     */
    // function withdrawPart(uint256 _ether)
    //     public
    //     returns(bool)
    // {
    //     uint256 _dividends = getDividends(msg.sender);
    //     require(_ether <= _dividends, 'Not enough dividends');

    //     dividendsPayouts[msg.sender] = dividendsPayouts[msg.sender] 
    //         + (int256)(_ether.mul(magnitude));

    //     uint256 _fee = _ether.mul(withdrawFee).div(100);
    //     uint256 _withdraw = _ether.sub(_fee);

    //     uint256 _tokens = ethToTokens(_withdraw);

    //     require (_tokens >= minPurchase, 
    //         'Token equivalent amount should not be less than 1 token');

    //     emit Withdraw(msg.sender, _ether);

    //     payable(wallet_99_strong_hand).transfer(_fee);
    //     payable(msg.sender).transfer(_withdraw);
    //     return true;
    // }

    /**
     * Withdraw dividends
     */
    function withdraw()
        public
        returns(bool)
    {
        uint256 _ether = getDividends(msg.sender);
        dividendsPayouts[msg.sender] = dividendsPayouts[msg.sender] 
            + (int256)(_ether.mul(magnitude));

        _ether = _ether
        .add(referralEarnings[msg.sender]);
        referralEarnings[msg.sender] = 0;

        uint256 _fee = _ether.mul(withdrawFee).div(100);
        uint256 _withdraw = _ether.sub(_fee);

        uint256 _tokens = ethToTokens(_withdraw);

        require (_tokens >= minPurchase, 
            'Token equivalent amount should not be less than 1 token');

        emit Withdraw(msg.sender, _ether);

        payable(wallet_99_strong_hand).transfer(_fee);
        payable(msg.sender).transfer(_withdraw);
        return true;
    }

    function transfer(address _recipient, uint256 _amount) public override returns (bool) {
        require (_amount >= minPurchase, 
            'Token amount should not be less than 1 token');
        require (_amount <= balanceOf(msg.sender), 
            'Token amount should not be greater than balance');
        uint256 _fee = _amount.mul(transferFee).div(100);
        uint256 _tokens = _amount.sub(_fee);

        dividendsPayouts[msg.sender] = dividendsPayouts[msg.sender]
            - (int256)(dividendsPerShare.mul(_amount));
        dividendsPayouts[_recipient] = dividendsPayouts[_recipient]
            + (int256)(dividendsPerShare.mul(_tokens));

        _burn(msg.sender, _fee);

        dividendsPerShare = dividendsPerShare
        .add(tokensToEth(_fee).mul(magnitude).div(totalSupply()));
        return super.transfer(_recipient, _tokens);
    }

    function transferFrom(address _sender, address _recipient, uint256 _amount) public override returns (bool) {
        require (_amount >= minPurchase, 
            'Token amount should not be less than 1 token');
        require (_amount <= balanceOf(_sender), 
            'Token amount should not be greater than balance');
        uint256 _fee = _amount.mul(transferFee).div(100);
        uint256 _tokens = _amount.sub(_fee);

        dividendsPayouts[msg.sender] = dividendsPayouts[msg.sender]
            - (int256)(dividendsPerShare.mul(_amount));
        dividendsPayouts[_recipient] = dividendsPayouts[_recipient]
            + (int256)(dividendsPerShare.mul(_tokens));

        _burn(_sender, _fee);

        dividendsPerShare = dividendsPerShare
        .add(tokensToEth(_fee).mul(magnitude).div(totalSupply()));
        return super.transferFrom(_sender, _recipient, _tokens);
    }    
    
    receive() payable external {
        purchase(msg.value, address(0));
    }

    function buy(address _referrer) payable external {
        purchase(msg.value, _referrer);
    }

    function purchase(uint256 _amount, address _referrer) internal {
        require (msg.sender != _referrer, 'You can not be your referrer');
        uint256 _refFee = _amount.mul(referrerFee).div(100);
        uint256 _buyFee = _amount.mul(buyFee).div(100);
        uint256 _totalFee = _refFee.add(_buyFee);
        if (referrer[msg.sender] == address(0)
            && _referrer != address(0)
            && balanceOf(_referrer) >= 99 ether) {
            referrer[msg.sender] = _referrer;
            referralEarnings[_referrer] = referralEarnings[_referrer]
            .add(_refFee);
        } else {
            _buyFee = _totalFee;
        }
        
        uint256 _tokens = ethToTokens(_amount.sub(_totalFee));

        require (_tokens >= minPurchase, 
            'Tokens amount should not be less than 1 token');

        emit Buy(msg.sender, _amount, _tokens, _referrer);

        _mint(msg.sender, _tokens);
        uint256 _supply = totalSupply();

        uint256 _extra = _buyFee
        .mul(magnitude)
        .div(_supply);

        if (dividendsPerShare > 0) {
            dividendsPayouts[msg.sender] = dividendsPayouts[msg.sender]
            + (int256)(dividendsPerShare.mul(_tokens));
        }

        dividendsPerShare = dividendsPerShare
        .add(_extra);
    }

    function sell(uint256 _tokens) external {
        require (_tokens >= minPurchase, 
            'Tokens amount should not be less than 1 token');
        require (_tokens <= balanceOf(msg.sender), 
            'Not enough tokens');
        uint256 _amount = tokensToEth(_tokens);
        uint256 _fee = _amount.mul(sellFee).div(100);

        emit Sell(msg.sender, _amount, _tokens);

        _burn(msg.sender, _tokens);

        dividendsPayouts[msg.sender] = dividendsPayouts[msg.sender] 
            - (int256)(dividendsPerShare.mul(_tokens));

        uint256 _supply = totalSupply();

        if (_supply > 0) { 
            dividendsPerShare = dividendsPerShare
            .add(_fee.mul(magnitude).div(_supply));
        } else {
            dividendsPerShare = 0;
            dividendsPayouts[msg.sender] = dividendsPayouts[msg.sender]
                - (int256)(_fee.mul(magnitude));
        }

        payable(msg.sender)
        .transfer(_amount.sub(_fee));
    }

    function getCurrentPrice () public view returns(uint256) {
        uint256 supply = totalSupply();
        uint256 supplyInt = supply.div(1 ether);
        return supplyInt.mul(increment).add(initialPrice);
    }

    /**
     * Calculate Token price based on an amount of incoming ethereum
     */
    function ethToTokens(uint256 _ether)
        public
        view
        returns(uint256)
    {
        // return _ether;
        uint256 eth = _ether;
        uint256 supply = totalSupply();
        uint256 supplyInt = supply.div(1 ether);
        uint256 supplyFract = supply.sub(supplyInt.mul(1 ether));
        uint256 currentPrice = supplyInt.mul(increment).add(initialPrice);
        uint256 tokens;
        uint256 tempTokens = eth.mul(1 ether).div(currentPrice);

        if (tempTokens < supplyFract) {
            return tempTokens;
        }

        tokens = tokens.add(supplyFract);

        eth = eth.sub(supplyFract.mul(currentPrice).div(1 ether));
        if (supplyFract > 0) {
            currentPrice = currentPrice.add(increment);
        }
        tempTokens = eth.mul(1 ether).div(currentPrice);

        if (tempTokens <= 1 ether) {
            return tokens.add(tempTokens);
        }

        uint256 d = currentPrice.mul(2)
        .sub(increment);
        d = d.mul(d);
        d = d.add(increment.mul(eth).mul(8));

        uint256 sqrtD = sqrt(d);
        
        tempTokens = increment
        .add(sqrtD)
        .sub(currentPrice.mul(2));

        tempTokens = tempTokens
        .mul(1 ether)
        .div(increment.mul(2));

        tokens = tokens.add(tempTokens);

        return tokens;
    }