/// @dev Should be called by external mechanism when reward funds are sent to this contract

function notifyRewardAmount(uint256 reward)
        external
        override
        nonReentrant
        onlyRewardDistributor
        updateReward(address(0))
    {
        // overflow fix according to https://sips.synthetix.io/sips/sip-77
        require(reward < uint(-1) / 1e18, "the notified reward cannot invoke multiplication overflow");

        if (block.timestamp >= periodFinish) {
            rewardRate = reward.div(duration);
        } else {
            uint256 remaining = periodFinish.sub(block.timestamp);
            uint256 leftover = remaining.mul(rewardRate);
            rewardRate = reward.add(leftover).div(duration);
        }
        lastUpdateTime = block.timestamp;
        periodFinish = block.timestamp.add(duration);
        emit RewardAdded(reward);
    }


//////////////////////////////////////////////////
//////////////////////////////////////////////////
//////////////////////////////////////////////////
/// @notice Withdraw portion of allocation unlocked

function withdraw() public onlyOwner {
        uint256 timestamp = block.timestamp;

        if (timestamp == lastWithdrawalTimestamp) return;

        uint256 _lastWithdrawalTimestamp = lastWithdrawalTimestamp;
        lastWithdrawalTimestamp = timestamp;

        uint256 balance = premia.balanceOf(address(this));

        if (timestamp >= endTimestamp) {
            premia.safeTransfer(msg.sender, balance);
        } else {

            uint256 elapsedSinceLastWithdrawal = timestamp.sub(_lastWithdrawalTimestamp);
            uint256 timeLeft = endTimestamp.sub(_lastWithdrawalTimestamp);
            premia.safeTransfer(msg.sender, balance.mul(elapsedSinceLastWithdrawal).div(timeLeft));
        }
    }


/*
 * @param characterType
 * @param adversaryType
 * @return whether adversaryType is a valid type of adversary for a given character
 */

function isValidAdversary(uint8 characterType, uint8 adversaryType) pure returns (bool) {
    if (characterType >= KNIGHT_MIN_TYPE && characterType <= KNIGHT_MAX_TYPE) { // knight
      return (adversaryType <= DRAGON_MAX_TYPE);
    } else if (characterType >= WIZARD_MIN_TYPE && characterType <= WIZARD_MAX_TYPE) { // wizard
      return (adversaryType < BALLOON_MIN_TYPE || adversaryType > BALLOON_MAX_TYPE);
    } else if (characterType >= DRAGON_MIN_TYPE && characterType <= DRAGON_MAX_TYPE) { // dragon
      return (adversaryType >= WIZARD_MIN_TYPE);
    } else if (characterType >= ARCHER_MIN_TYPE && characterType <= ARCHER_MAX_TYPE) { // archer
      return ((adversaryType >= BALLOON_MIN_TYPE && adversaryType <= BALLOON_MAX_TYPE)
             || (adversaryType >= KNIGHT_MIN_TYPE && adversaryType <= KNIGHT_MAX_TYPE));

    }
    return false;
  }


/**
 * pick a random adversary.
 * @param nonce a nonce to make sure there's not always the same adversary chosen in a single block.
 * @return the index of a random adversary character
 * */

function getRandomAdversary(uint256 nonce, uint8 characterType) internal view returns(uint16) {
    uint16 randomIndex = uint16(generateRandomNumber(nonce) % numCharacters);
    // use 7, 11 or 13 as step size. scales for up to 1000 characters
    uint16 stepSize = numCharacters % 7 == 0 ? (numCharacters % 11 == 0 ? 13 : 11) : 7;
    uint16 i = randomIndex;
    //if the picked character is a knight or belongs to the sender, look at the character + stepSizes ahead in the array (modulo the total number)
    //will at some point return to the startingPoint if no character is suited
    do {
      if (isValidAdversary(characterType, characters[ids[i]].characterType) && characters[ids[i]].owner != msg.sender) {
        return i;
      }
      i = (i + stepSize) % numCharacters;
    } while (i != randomIndex);

    return INVALID_CHARACTER_INDEX;
  }


/**
 * Hits the character of the given type at the given index.
 * Wizards can knock off two protections. Other characters can do only one.
 * @param index the index of the character
 * @param nchars the number of characters
 * @return the value gained from hitting the characters (zero is the character was protected)
 * */

function hitCharacter(uint16 index, uint16 nchars, uint8 characterType) internal returns(uint128 characterValue) {
    uint32 id = ids[index];
    uint8 knockOffProtections = 1;
    if (characterType >= WIZARD_MIN_TYPE && characterType <= WIZARD_MAX_TYPE) {
      knockOffProtections = 2;
    }
    if (protection[id] >= knockOffProtections) {
      protection[id] = protection[id] - knockOffProtections;
      return 0;
    }
    characterValue = characters[ids[index]].value;
    nchars--;
    replaceCharacter(index, nchars);
  }


/**
 * finds the oldest character
 * */

function findOldest() public {
    uint32 newOldest = noKing;
    for (uint16 i = 0; i < numCharacters; i++) {
      if (ids[i] < newOldest && characters[ids[i]].characterType <= DRAGON_MAX_TYPE)
        newOldest = ids[i];
    }
    oldest = newOldest;
  }


/* @dev distributes castle loot among archers */

function distributeCastleLoot(uint32 characterId) public onlyUser {
    require(castleTreasury > 0, "empty treasury");
    Character archer = characters[characterId];
    require(archer.characterType >= ARCHER_MIN_TYPE && archer.characterType <= ARCHER_MAX_TYPE, "only archers can access the castle treasury");
    if(lastCastleLootDistributionTimestamp[characterId] == 0)
      require(now - archer.purchaseTimestamp >= config.castleLootDistributionThreshold(),
            "not enough time has passed since the purchase");
    else
      require(now >= lastCastleLootDistributionTimestamp[characterId] + config.castleLootDistributionThreshold(),
            "not enough time passed since the last castle loot distribution");
    require(archer.fightCount >= 3, "need to fight 3 times");
    lastCastleLootDistributionTimestamp[characterId] = now;
    archer.fightCount = 0;

    uint128 luckFactor = generateLuckFactor(uint128(generateRandomNumber(characterId) % 1000));
    if (luckFactor < 3) {
      luckFactor = 3;
    }
    assert(luckFactor <= 50);
    uint128 amount = castleTreasury * luckFactor / 100;
    archer.value += amount;
    castleTreasury -= amount;
    emit NewDistributionCastleLoot(amount, characterId, luckFactor);

  }


/**
 * sell the character of the given id
 * throws an exception in case of a knight not yet teleported to the game
 * @param characterId the id of the character
 * */

function sellCharacter(uint32 characterId, uint16 characterIndex) public onlyUser {
    if (characterIndex >= numCharacters || characterId != ids[characterIndex])
      characterIndex = getCharacterIndex(characterId);
    Character storage char = characters[characterId];
    require(msg.sender == char.owner,
            "only owners can sell their characters");
    require(char.characterType < BALLOON_MIN_TYPE || char.characterType > BALLOON_MAX_TYPE,
            "balloons are not sellable");
    require(char.purchaseTimestamp + 1 days < now,
            "character can be sold only 1 day after the purchase");
    uint128 val = char.value;
    numCharacters--;
    replaceCharacter(characterIndex, numCharacters);
    msg.sender.transfer(val);
    if (oldest == 0)
      findOldest();
    emit NewSell(characterId, msg.sender, val);
  }


/**
 * Knights, balloons, wizards, and archers are only entering the game completely, when they are teleported to the scene
 * @param id the character id
 * */

function teleportCharacter(uint32 id) internal {
    // ensure we do not teleport twice
    require(teleported[id] == false,
           "already teleported");
    teleported[id] = true;
    Character storage character = characters[id];
    require(character.characterType > DRAGON_MAX_TYPE,
           "dragons do not need to be teleported"); //this also makes calls with non-existent ids fail
    addCharacter(id, numCharacters);
    numCharacters++;
    numCharactersXType[character.characterType]++;
    emit NewTeleport(id);
  }


/**
 * returns 10 characters starting from a certain indey
 * @param startIndex the index to start from
 * @return 4 arrays containing the ids, types, values and owners of the characters
 * */

function get10Characters(uint16 startIndex) constant public returns(uint32[10] characterIds, uint8[10] types, uint128[10] values, address[10] owners) {
    uint32 endIndex = startIndex + 10 > numCharacters ? numCharacters : startIndex + 10;
    uint8 j = 0;
    uint32 id;
    for (uint16 i = startIndex; i < endIndex; i++) {
      id = ids[i];
      characterIds[j] = id;
      types[j] = characters[id].characterType;
      values[j] = characters[id].value;
      owners[j] = characters[id].owner;
      j++;
    }

  }


/**
 @notice This function adds liquidity to Mushroom vaults with ETH or ERC20 tokens
 @param fromToken The token used for entry (address(0) if ether)
 @param amountIn The amount of fromToken to invest
 @param toVault Harvest vault address
 @param minMVTokens The minimum acceptable quantity vault tokens to receive. Reverts otherwise
 @param intermediateToken Token to swap fromToken to before entering vault
 @param swapTarget Excecution target for the swap or zap
 @param swapData DEX or Zap data
 @param affiliate Affiliate address
 @param shouldSellEntireBalance True if amountIn is determined at execution time (i.e. contract is caller)
 @return tokensReceived- Quantity of Vault tokens received
 */

function ZapIn(
        address fromToken,
        uint256 amountIn,
        address toVault,
        uint256 minMVTokens,
        address intermediateToken,
        address swapTarget,
        bytes calldata swapData,
        address affiliate,
        bool shouldSellEntireBalance
    ) external payable stopInEmergency returns (uint256 tokensReceived) {
        require(
            approvedTargets[swapTarget] || swapTarget == address(0),
            "Target not Authorized"
        );

        // get incoming tokens
        uint256 toInvest =
            _pullTokens(
                fromToken,
                amountIn,
                affiliate,
                true,
                shouldSellEntireBalance
            );

        // get intermediate token
        uint256 intermediateAmt =
            _fillQuote(
                fromToken,
                intermediateToken,
                toInvest,
                swapTarget,
                swapData
            );

        // Deposit to Vault
        tokensReceived = _vaultDeposit(intermediateAmt, toVault, minMVTokens);
    }


/**
 * @dev Mints a new token up to the current supply, stopping at the max supply.
 */

function mintNFTeacher(uint256 _quantity) public payable override {
        require(mintStart <= block.timestamp, 'Minting not started.');
        require(MAX_PER_TX >= _quantity, 'Too many mints.');
        require(currentMaxSupply >= tokenIds.current() + _quantity, 'No more tokens, currently.');
        require(MAX_SUPPLY >= tokenIds.current() + _quantity, 'No more tokens, ever.');
        require(MINT_PRICE * _quantity == msg.value, 'Wrong value sent.');

        for (uint8 i = 0; i < _quantity; i++) {
            tokenIds.increment();
            _safeMint(msg.sender, tokenIds.current());
        }
    }


/// @dev The receive function is triggered when FTM is received

receive() external payable virtual {
        // check ERC20 token balances while we have the chance, if we send FTM it will forward them
        for (uint256 i = 0; i < royaltyERC20Tokens.length; i++) {
            // send the tokens to the recipients
            IERC20 _token = IERC20(royaltyERC20Tokens[i]);
            if (_token.balanceOf(address(this)) > 0) {
                this.withdrawTokens(royaltyERC20Tokens[i]);
            }
        }

        // withdraw the splits
        this.withdraw();
    }


// import balance for a group of user with a specific contract terms

function importBalance(address[] memory buyers, uint256[] memory tokens, string memory contractName) public onlyOwner whenNotPaused returns (bool) {
        require(buyers.length == tokens.length, "buyers and balances mismatch");

        VestingContract storage vestingContract = _vestingContracts[contractName];
        require(vestingContract.basisPoints.length > 0, "contract does not exist");

        for(uint256 i = 0; i < buyers.length; i++) {
            require(tokens[i] > 0, "cannot import zero balance");
            BuyerInfo storage buyerInfo = _buyerInfos[buyers[i]];
            require(buyerInfo.total == 0, "have already imported balance for this buyer");
            buyerInfo.total = tokens[i];
            buyerInfo.contractName = contractName;
        }

        emit ImportBalance(buyers, tokens, contractName);
        return true;
    }


// the number of token can be claimed by the msg.sender at the moment

function claimableToken() public view returns (uint256) {
        BuyerInfo storage buyerInfo = _buyerInfos[msg.sender];

        if(buyerInfo.claimed < buyerInfo.total) {
            VestingContract storage vestingContract = _vestingContracts[buyerInfo.contractName];
            uint256 currentMonth = block.timestamp.sub(_deployTime).div(_month);

            if(currentMonth < vestingContract.startMonth) {
                return uint256(0);
            }

            if(currentMonth >= vestingContract.endMonth) { // vest the unclaimed token all at once so there's no remainder
                return buyerInfo.total.sub(buyerInfo.claimed);
            } else {
                uint256 claimableIndex = currentMonth.sub(vestingContract.startMonth);
                uint256 canClaim = 0;
                for(uint256 i = 0; i <= claimableIndex; ++i) {
                    canClaim = canClaim.add(vestingContract.basisPoints[i]);
                }
                return canClaim.mul(buyerInfo.total).div(10000).sub(buyerInfo.claimed);
            }
        }
        return uint256(0);
    }


/**
 * @dev Checks if amount is within allowed burn bounds and
 * destroys `amount` tokens from `account`, reducing the
 * total supply.
 * @param account account to burn tokens for
 * @param amount amount of tokens to burn
 *
 * Emits a {Burn} event
 */

function _burn(address account, uint256 amount) internal virtual override {
        require(amount >= burnMin, "BurnableTokenWithBounds: below min burn bound");
        require(amount <= burnMax, "BurnableTokenWithBounds: exceeds max burn bound");

        super._burn(account, amount);
        emit Burn(account, amount);
    }


/*
 * @dev Verifies if message was signed by owner to give access to _add for this contract.
 *      Assumes Geth signature prefix.
 * @param _add Address of agent with access
 * @param _v ECDSA signature parameter v.
 * @param _r ECDSA signature parameters r.
 * @param _s ECDSA signature parameters s.
 * @return Validity of access message for a given address.
 */

function isInvalidAccessMessage(
        uint8 _v,
        bytes32 _r,
        bytes32 _s)
        view public returns (bool)
    {
        bytes32 hash = keccak256(abi.encodePacked(this));
        address signAddress = ecrecover(
            keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)),
            _v,
            _r,
            _s
        );
        if (ownerAddy == signAddress){
          return true;
        }
        else{
          return false;
        }
    }


/*
 * @dev Public Sale Mint
 * @param quantity Number of NFT's to be minted at 0.06 with a max of 10 per transaction. This is a gas efficient function.
 */

function mint(uint256 quantity) external payable {
    require(quantity <= 10, "Cant mint more than 10");
    require(totalSupply() + quantity <= 3183, "Sold out");
    require(msg.value >= pricePerPublic * quantity, "Not enough Eth");
    require(publicLive, "sale not live");
    _safeMint(msg.sender, quantity);
  }


/*
 * @dev Verifies if message was signed by owner to give access to this contract.
 *      Assumes Geth signature prefix.
 * @param quantity Number of NFT's to be minted at 0.04 with a max of 3. This is a gas efficient function.
 * @param _v ECDSA signature parameter v.
 * @param _r ECDSA signature parameters r.
 * @param _s ECDSA signature parameters s.
 */

function mintWL(uint256 quantity, uint8 _v, bytes32 _r, bytes32 _s) external payable noInvalidAccess(_v, _r, _s){
    require(quantity <= 3, "Cant mint more than 3");
    require(totalSupply() + quantity <= 3183, "Sold out");
    require(msg.value >= pricePerWL * quantity, "Not enough Eth");
    require(presaleLive, "sale not live");
    require(WLminted[msg.sender] + quantity <= 3, "Cant mint more than 3");
    WLminted[msg.sender] += quantity;
    _safeMint(msg.sender, quantity);
  }


/**
 	 * @dev Stake `_value` ions on behalf of `_from`
 	 * @param _from The address of the target
 	 * @param _value The amount to stake
 	 * @return true on success
 	 */

function stakeFrom(address _from, uint256 _value) public inWhitelist returns (bool) {
		require (balanceOf[_from] >= _value);						// Check if the targeted balance is enough
		balanceOf[_from] = balanceOf[_from].sub(_value);			// Subtract from the targeted balance
		stakedBalance[_from] = stakedBalance[_from].add(_value);	// Add to the targeted staked balance
		emit Stake(_from, _value);
		return true;
	}


/**
 * @dev See {IMerkleDrop-isClaimed}.
 */

function isClaimed(uint256 index) public view override returns (bool) {
        uint256 claimedWordIndex = index / 256;
        uint256 claimedBitIndex = index % 256;
        uint256 claimedWord = claimedBitMap[claimedWordIndex];
        uint256 mask = (1 << claimedBitIndex);
        return claimedWord & mask == mask;
    }


/**
 * @dev See {IMerkleDrop-claim}.
 */

function claim(uint256 index, address account, uint256 amount, bytes32[] calldata merkleProof) external override {
        require(!isClaimed(index), "MerkleDrop: drop already claimed");

        // Verify the merkle proof.
        bytes32 node = keccak256(abi.encodePacked(index, account, amount));
        require(MerkleProof.verify(merkleProof, merkleRoot, node), "MerkleDrop: invalid proof");

        // Mark it claimed and send the token.
        _setClaimed(index);
        token.safeTransfer(account, amount);
        emit Claimed(index, account, amount);
    }


/**
 * @dev See {IMerkleDrop-stop}.
 */

function stop(address beneficiary) external override onlyOwner {
        require(beneficiary != address(0), "MerkleDrop: beneficiary is the zero address");
        // solhint-disable-next-line not-rely-on-time
        require(block.timestamp >= expireTimestamp, "MerkleDrop: not expired");
        uint256 amount = token.balanceOf(address(this));
        token.safeTransfer(beneficiary, amount);
        emit Stopped(beneficiary, amount);
    }


/**
 	 * @dev Store `_value` from `_from` to `_to` in escrow
 	 * @param _from The address of the sender
 	 * @param _to The address of the recipient
 	 * @param _value The amount of network ions to put in escrow
 	 * @return true on success
 	 */

function escrowFrom(address _from, address _to, uint256 _value) public inWhitelist returns (bool) {
		require (balanceOf[_from] >= _value);						// Check if the targeted balance is enough
		balanceOf[_from] = balanceOf[_from].sub(_value);			// Subtract from the targeted balance
		escrowedBalance[_to] = escrowedBalance[_to].add(_value);	// Add to the targeted escrowed balance
		emit Escrow(_from, _to, _value);
		return true;
	}


/**
 	 * @dev Release escrowed `_value` from `_from`
 	 * @param _from The address of the sender
 	 * @param _value The amount of escrowed network ions to be released
 	 * @return true on success
 	 */

function unescrowFrom(address _from, uint256 _value) public inWhitelist returns (bool) {
		require (escrowedBalance[_from] >= _value);						// Check if the targeted escrowed balance is enough
		escrowedBalance[_from] = escrowedBalance[_from].sub(_value);	// Subtract from the targeted escrowed balance
		balanceOf[_from] = balanceOf[_from].add(_value);				// Add to the targeted balance
		emit Unescrow(_from, _value);
		return true;
	}


/**
 	 * Transfer ions between public key addresses in a Name
 	 * @param _nameId The ID of the Name
 	 * @param _from The address of the sender
 	 * @param _to The address of the recipient
 	 * @param _value the amount to send
 	 */

function transferBetweenPublicKeys(address _nameId, address _from, address _to, uint256 _value) public returns (bool success) {
		require (AOLibrary.isName(_nameId));
		require (_nameTAOPosition.senderIsAdvocate(msg.sender, _nameId));
		require (!_nameAccountRecovery.isCompromised(_nameId));
		// Make sure _from exist in the Name's Public Keys
		require (_namePublicKey.isKeyExist(_nameId, _from));
		// Make sure _to exist in the Name's Public Keys
		require (_namePublicKey.isKeyExist(_nameId, _to));
		_transfer(_from, _to, _value);
		return true;
	}


/**
 * @dev Check if neither account is blacklisted before performing transfer
 * If transfer recipient is a redemption address, burns tokens
 * @notice Transfer to redemption address will burn tokens with a 1 cent precision
 * @param sender address of sender
 * @param recipient address of recipient
 * @param amount amount of tokens to transfer
 */

function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal virtual override {
        require(!isBlacklisted[sender], "FavorCurrency: sender is blacklisted");
        require(!isBlacklisted[recipient], "FavorCurrency: recipient is blacklisted");

        if (isRedemptionAddress(recipient)) {
            super._transfer(sender, recipient, amount.sub(amount.mod(CENT)));
            _burn(recipient, amount.sub(amount.mod(CENT)));
        } else {
            super._transfer(sender, recipient, amount);
        }
    }


/**
 * @dev Requere neither accounts to be blacklisted before approval
 * @param owner address of owner giving approval
 * @param spender address of spender to approve for
 * @param amount amount of tokens to approve
 */

function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal override {
        require(!isBlacklisted[owner], "FavorCurrency: tokens owner is blacklisted");
        require(!isBlacklisted[spender] || amount == 0, "FavorCurrency: tokens spender is blacklisted");

        super._approve(owner, spender, amount);
    }


/// @dev withdraw native tokens divided by splits

function withdraw(Payout storage _payout) external onlyWhenInitialized(_payout.initialized) {
        uint256 _amount = address(this).balance;
        if (_amount > 0) {
            for (uint256 i = 0; i < _payout.recipients.length; i++) {
                // we don't want to fail here or it can lock the contract withdrawals
                uint256 _share = (_amount * _payout.splits[i]) / _payout.BASE;
                (bool _success, ) = payable(_payout.recipients[i]).call{value: _share}('');
                if (_success) {
                    emit SplitWithdrawal(address(0), _payout.recipients[i], _share);
                }
            }
        }
    }


/// @dev withdraw ERC20 tokens divided by splits

function withdrawTokens(Payout storage _payout, address _tokenContract)
        external
        onlyWhenInitialized(_payout.initialized)
    {
        IERC20 tokenContract = IERC20(_tokenContract);

        // transfer the token from address of this contract
        uint256 _amount = tokenContract.balanceOf(address(this));
        /* istanbul ignore else */
        if (_amount > 0) {
            for (uint256 i = 0; i < _payout.recipients.length; i++) {
                uint256 _share = i != _payout.recipients.length - 1
                    ? (_amount * _payout.splits[i]) / _payout.BASE
                    : tokenContract.balanceOf(address(this));
                tokenContract.transfer(_payout.recipients[i], _share);
                emit SplitWithdrawal(_tokenContract, _payout.recipients[i], _share);
            }
        }
    }


/// @dev withdraw ERC721 tokens to the first recipient

function withdrawNFT(
        Payout storage _payout,
        address _tokenContract,
        uint256[] memory _id
    ) external onlyWhenInitialized(_payout.initialized) {
        IERC721 tokenContract = IERC721(_tokenContract);
        for (uint256 i = 0; i < _id.length; i++) {
            address _recipient = getNftRecipient(_payout);
            tokenContract.safeTransferFrom(address(this), _recipient, _id[i]);
        }
    }


/// @dev Allow a recipient to update to a new address

function updateRecipient(Payout storage _payout, address _recipient)
        external
        onlyWhenInitialized(_payout.initialized)
    {
        require(_recipient != address(0), 'Cannot use the zero address.');
        require(_recipient != address(this), 'Cannot use the address of this contract.');

        // loop over all the recipients and update the address
        bool _found = false;
        for (uint256 i = 0; i < _payout.recipients.length; i++) {
            // if the sender matches one of the recipients, update the address
            if (_payout.recipients[i] == msg.sender) {
                _payout.recipients[i] = _recipient;
                _found = true;
                break;
            }
        }
        require(_found, 'The sender is not a recipient.');
    }


// Transfer token with data and signature

function transferAndData(address _to, uint _value, string _data) returns (bool) {
        //Default assumes totalSupply can't be over max (2^256 - 1).
        if (balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {
            balances[msg.sender] -= _value;
            balances[_to] += _value;
            Transfer(msg.sender, _to, _value);

            return true;
        } else { return false; }
    }


/**
 @dev updates one of the token reserves
 can only be called by the owner

 @param _reserveToken           address of the reserve token
 @param _ratio                  constant reserve ratio, represented in ppm, 1-1000000
 @param _enableVirtualBalance   true to enable virtual balance for the reserve, false to disable it
 @param _virtualBalance         new reserve's virtual balance
 */

function updateReserve(IERC20Token _reserveToken, uint32 _ratio, bool _enableVirtualBalance, uint256 _virtualBalance)
        public
        ownerOnly
        validReserve(_reserveToken)
        validReserveRatio(_ratio)
    {
        Reserve storage reserve = reserves[_reserveToken];
        require(totalReserveRatio - reserve.ratio + _ratio <= MAX_CRR); // validate input

        totalReserveRatio = totalReserveRatio - reserve.ratio + _ratio;
        reserve.ratio = _ratio;
        reserve.isVirtualBalanceEnabled = _enableVirtualBalance;
        reserve.virtualBalance = _virtualBalance;
    }


// ------------------------------------------------------------------------
// Withdraw accumulated rewards
// ------------------------------------------------------------------------

function ClaimReward() external {
        require(PendingReward(msg.sender) > 0, "No pending rewards");

        uint256 _pendingReward = PendingReward(msg.sender);

        // Global stats update
        totalRewards = totalRewards.add(_pendingReward);

        // update the record
        users[msg.sender].totalGained = users[msg.sender].totalGained.add(_pendingReward);
        users[msg.sender].lastClaimedDate = now;
        users[msg.sender].pendingGains = 0;

        // mint more tokens inside token contract equivalent to _pendingReward
        require(IERC20(OXS).transfer(msg.sender, _pendingReward));

        emit RewardsCollected(_pendingReward);
    }


// ------------------------------------------------------------------------
// This will stop the existing staking
// ------------------------------------------------------------------------

function StopStaking() external {
        require(users[msg.sender].activeDeposit >= 0, "No active stake");
        uint256 _activeDeposit = users[msg.sender].activeDeposit;

        // update staking stats
            // check if we have any pending rewards, add it to previousGains var
            users[msg.sender].pendingGains = PendingReward(msg.sender);
            // update amount
            users[msg.sender].activeDeposit = 0;
            // reset last claimed figure as well
            users[msg.sender].lastClaimedDate = now;

        // withdraw the tokens and move from contract to the caller
        require(IERC20(OXS).transfer(msg.sender, _activeDeposit));

        emit StakingStopped(_activeDeposit);
    }


//#########################################################################################################################################################//
//##########################################################FARMING QUERIES################################################################################//
//#########################################################################################################################################################//
// ------------------------------------------------------------------------
// Query to get the pending reward
// @param _caller address of the staker
// ------------------------------------------------------------------------

function PendingReward(address _caller) public view returns(uint256 _pendingRewardWeis){
        uint256 _totalStakingTime = now.sub(users[_caller].lastClaimedDate);

        uint256 _reward_token_second = ((stakingRate).mul(10 ** 21)).div(365 days); // added extra 10^21

        uint256 reward = ((users[_caller].activeDeposit).mul(_totalStakingTime.mul(_reward_token_second))).div(10 ** 23); // remove extra 10^21 // 10^2 are for 100 (%)

        return reward.add(users[_caller].pendingGains);
    }


//#########################################################################################################################################################//
//################################################################COMMON UTILITIES#########################################################################//
//#########################################################################################################################################################//
// ------------------------------------------------------------------------
// Internal function to add new deposit
// ------------------------------------------------------------------------

function _newDeposit(uint256 _amount) internal{
        require(users[msg.sender].activeDeposit ==  0, "Already running, use funtion add to stake");

        // add that token into the contract balance
        // check if we have any pending reward, add it to pendingGains variable
        users[msg.sender].pendingGains = PendingReward(msg.sender);

        users[msg.sender].activeDeposit = _amount;
        users[msg.sender].totalDeposits = users[msg.sender].totalDeposits.add(_amount);
        users[msg.sender].startTime = now;
        users[msg.sender].lastClaimedDate = now;

        // update global stats
        totalStakes = totalStakes.add(_amount);
    }


// ------------------------------------------------------------------------
// Internal function to add to existing deposit
// ------------------------------------------------------------------------

function _addToExisting(uint256 _amount) internal{

        require(users[msg.sender].activeDeposit > 0, "no running farming/stake");

        // update staking stats
            // check if we have any pending reward, add it to pendingGains variable
            users[msg.sender].pendingGains = PendingReward(msg.sender);

            // update current deposited amount
            users[msg.sender].activeDeposit = users[msg.sender].activeDeposit.add(_amount);
            // update total deposits till today
            users[msg.sender].totalDeposits = users[msg.sender].totalDeposits.add(_amount);
            // update new deposit start time -- new stake will begin from this time onwards
            users[msg.sender].startTime = now;
            // reset last claimed figure as well -- new stake will begin from this time onwards
            users[msg.sender].lastClaimedDate = now;

        // update global stats
        totalStakes = totalStakes.add(_amount);
    }


/**
 @dev returns the expected return for buying the token for a reserve token

 @param _reserveToken   reserve token contract address
 @param _depositAmount  amount to deposit (in the reserve token)

 @return expected purchase return amount
 */

function getPurchaseReturn(IERC20Token _reserveToken, uint256 _depositAmount)
        public
        constant
        active
        validReserve(_reserveToken)
        returns (uint256)
    {
        Reserve storage reserve = reserves[_reserveToken];
        require(reserve.isPurchaseEnabled); // validate input

        uint256 tokenSupply = token.totalSupply();
        uint256 reserveBalance = getReserveBalance(_reserveToken);
        uint256 amount = extensions.formula().calculatePurchaseReturn(tokenSupply, reserveBalance, reserve.ratio, _depositAmount);

        // deduct the fee from the return amount
        uint256 feeAmount = getConversionFeeAmount(amount);
        return safeSub(amount, feeAmount);
    }


/**
 @dev converts a specific amount of _fromToken to _toToken

 @param _fromToken  ERC20 token to convert from
 @param _toToken    ERC20 token to convert to
 @param _amount     amount to convert, in fromToken
 @param _minReturn  if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero

 @return conversion return amount
 */

function convert(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256) {
        require(_fromToken != _toToken); // validate input

        // conversion between the token and one of its reserves
        if (_toToken == token)
            return buy(_fromToken, _amount, _minReturn);
        else if (_fromToken == token)
            return sell(_toToken, _amount, _minReturn);

        // conversion between 2 reserves
        uint256 purchaseAmount = buy(_fromToken, _amount, 1);
        return sell(_toToken, purchaseAmount, _minReturn);
    }


/**
 @dev utility, returns the expected return for selling the token for one of its reserve tokens, given a total supply override

 @param _reserveToken   reserve token contract address
 @param _sellAmount     amount to sell (in the standard token)
 @param _totalSupply    total token supply, overrides the actual token total supply when calculating the return

 @return sale return amount
 */

function getSaleReturn(IERC20Token _reserveToken, uint256 _sellAmount, uint256 _totalSupply)
        private
        constant
        active
        validReserve(_reserveToken)
        greaterThanZero(_totalSupply)
        returns (uint256)
    {
        Reserve storage reserve = reserves[_reserveToken];
        uint256 reserveBalance = getReserveBalance(_reserveToken);
        uint256 amount = extensions.formula().calculateSaleReturn(_totalSupply, reserveBalance, reserve.ratio, _sellAmount);

        // deduct the fee from the return amount
        uint256 feeAmount = getConversionFeeAmount(amount);
        return safeSub(amount, feeAmount);
    }


// get normal cardlist;

function getNormalCardList(address _owner) external view returns(uint256[],uint256[]){
    uint256 len = getNormalCard(_owner);
    uint256[] memory itemId = new uint256[](len);
    uint256[] memory itemNumber = new uint256[](len);
    uint256 startId;
    uint256 endId;
    (startId,endId) = schema.productionCardIdRange();
    uint256 i;
    while (startId <= endId) {
      if (cards.getOwnedCount(_owner,startId)>=1) {
        itemId[i] = startId;
        itemNumber[i] = cards.getOwnedCount(_owner,startId);
        i++;
      }
      startId++;
      }
    return (itemId, itemNumber);
  }


/**
 * Mint Doges by owner
 */

function reserveDoges(address _to, uint256 _numberOfTokens) external onlyOwner {
        require(_to != address(0), "Invalid address to reserve.");
        uint256 supply = totalSupply();
        uint256 i;

        for (i = 0; i < _numberOfTokens; i++) {
            _safeMint(_to, supply + i);
        }
    }


/**
 * Mints tokens
 */

function mintDoges(uint256 numberOfTokens) external payable {
        require(saleIsActive, "Sale must be active to mint");
        require(numberOfTokens <= maxToMint, "Invalid amount to mint per once");
        require(totalSupply().add(numberOfTokens) <= MAX_DOGE_SUPPLY, "Purchase would exceed max supply");
        require(mintPrice.mul(numberOfTokens) <= msg.value, "Ether value sent is not correct");

        for(uint256 i = 0; i < numberOfTokens; i++) {
            uint256 mintIndex = totalSupply();
            _safeMint(msg.sender, mintIndex);
        }

        // If we haven't set the starting index and this is either
        // 1) the last saleable token or 2) the first token to be sold after
        // the reveal time, set the starting index block
        if (startingIndexBlock == 0 && (totalSupply() == MAX_DOGE_SUPPLY || block.timestamp >= REVEAL_TIMESTAMP)) {
            startingIndexBlock = block.number;
        }
    }


//withdraw an amount including any want balance

function _withdraw(uint256 amount) internal returns (uint256) {
        uint256 balanceUnderlying = cToken.balanceOfUnderlying(address(this));
        uint256 looseBalance = want.balanceOf(address(this));
        uint256 total = balanceUnderlying.add(looseBalance);

        if (amount.add(dustThreshold) >= total) {
            //cant withdraw more than we own. so withdraw all we can
            if(balanceUnderlying > dustThreshold){
                require(cToken.redeem(cToken.balanceOf(address(this))) == 0, "ctoken: redeemAll fail");
            }
            looseBalance = want.balanceOf(address(this));
            want.safeTransfer(address(strategy), looseBalance);
            return looseBalance;
        }

        if (looseBalance >= amount) {
            want.safeTransfer(address(strategy), amount);
            return amount;
        }

        //not state changing but OK because of previous call
        uint256 liquidity = want.balanceOf(address(cToken));

        if (liquidity > 1) {
            uint256 toWithdraw = amount.sub(looseBalance);

            //we can take all
            if (toWithdraw > liquidity) {
                toWithdraw = liquidity;
            }
            require(cToken.redeemUnderlying(toWithdraw) == 0, "ctoken: redeemUnderlying fail");

        }
        looseBalance = want.balanceOf(address(this));
        want.safeTransfer(address(strategy), looseBalance);
        return looseBalance;
    }


//we use different method to withdraw for safety

function withdrawAll() external override management returns (bool all) {
        //redo or else price changes
        cToken.mint(0);

        uint256 liquidity = want.balanceOf(address(cToken));
        uint256 liquidityInCTokens = convertFromUnderlying(liquidity);
        uint256 amountInCtokens = cToken.balanceOf(address(this));

        if (liquidityInCTokens > 2) {
            liquidityInCTokens = liquidityInCTokens-1;

            if (amountInCtokens <= liquidityInCTokens) {
                //we can take all
                all = true;
                cToken.redeem(amountInCtokens);
            } else {
                liquidityInCTokens = convertFromUnderlying(want.balanceOf(address(cToken)));
                //take all we can
                all = false;
                cToken.redeem(liquidityInCTokens);
            }
        }

        want.safeTransfer(address(strategy), want.balanceOf(address(this)));
        return all;
    }


/**
 * @dev Approve an address to spend another addresses' tokens.
 * @param owner The address that owns the tokens.
 * @param spender The address that will spend the tokens.
 * @param value The number of tokens that can be spent.
 */

function _approve(address owner, address spender, uint256 value) internal {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = value;
        emit Approval(owner, spender, value);
    }


/* solhint-disable quotes */

function tokenURI(uint256 tokenId) public view override returns (string memory) {
        require(_exists(tokenId), "Bp:tU:404");

        FilterMatrix memory tokenFilters = filterMap[tokenId];
        return
            string(
                abi.encodePacked(
                    "data:application/json;base64,",
                    Base64.encode(
                        bytes(
                            abi.encodePacked(
                                '{"name":"Blitpop ',
                                IBlitmap(BLITMAP_ADDRESS).tokenNameOf(tokenId),
                                '", "description":"Blitpops are onchain Blitmap derivatives. To construct the artwork, the original Blitmap with corresponding token ID is fetched, collaged and filtered to return a modified onchain SVG.", "image": "',
                                svgBase64Data(tokenId, tokenFilters.filter1, tokenFilters.filter2, tokenFilters.filter3),
                                '", ',
                                tokenProperties(tokenFilters),
                                '"}}'
                            )
                        )
                    )
                )
            );
    }


/** @dev Creates `amount` tokens and assigns them to `account`, increasing
 * the total supply.
 * - `to` cannot be the zero address.
 */

function _distribute(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: unable to do toward the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(address(0), account, amount);
    }


// Check for the possibility of buying tokens. Inside. Constant.

function validPurchase() internal constant returns (bool) {

        // The round started and did not end
        bool withinPeriod = (now > startTime && now < endTime);

        // Rate is greater than or equal to the minimum
        bool nonZeroPurchase = msg.value >= minPay;

        // hardCap is not reached, and in the event of a transaction, it will not be exceeded by more than OverLimit
        bool withinCap = msg.value <= hardCap.sub(weiRaised()).add(overLimit);

        // round is initialized and no "Pause of trading" is set
        return withinPeriod && nonZeroPurchase && withinCap && isInitialized && !isPausedCrowdsale;
    }


// The Manager freezes the tokens for the Team.
// You must call a function to finalize Round 2 (only after the Round2)

function finalize1() public {
        require(wallets[uint8(Roles.manager)] == msg.sender || wallets[uint8(Roles.beneficiary)] == msg.sender);
        require(team);
        team = false;
        lockedAllocation = new SVTAllocation(token, wallets[uint8(Roles.team)]);
        token.addUnpausedWallet(lockedAllocation);
        // 12% - tokens to Team wallet after freeze (77% for investors)
        // *** CHECK THESE NUMBERS ***
        token.mint(lockedAllocation,allToken.mul(12).div(77));
    }


// calculates the senior ratio

function calcSeniorRatio(uint seniorAsset, uint nav, uint reserve_) public pure returns(uint) {
        // note: NAV + reserve == seniorAsset + juniorAsset (loop invariant: always true)
        // if expectedSeniorAsset is passed ratio can be greater than ONE
        uint assets = calcAssets(nav, reserve_);
        if(assets == 0) {
            return 0;
        }

        return rdiv(seniorAsset, assets);
    }


// calculates a new senior asset value based on senior redeem and senior supply

function calcSeniorAssetValue(uint seniorRedeem, uint seniorSupply,
        uint currSeniorAsset, uint reserve_, uint nav_) public pure returns (uint seniorAsset) {

        seniorAsset =  safeSub(safeAdd(currSeniorAsset, seniorSupply), seniorRedeem);
        uint assets = calcAssets(nav_, reserve_);
        if(seniorAsset > assets) {
            seniorAsset = assets;
        }

        return seniorAsset;
    }


// returns the current junior ratio protection in the Tinlake
// juniorRatio is denominated in RAY (10^27)

function calcJuniorRatio() public view returns(uint) {
        uint seniorAsset = safeAdd(seniorDebt(), seniorBalance_);
        uint assets = safeAdd(navFeed.approximatedNAV(), reserve.totalBalance());

        if(seniorAsset == 0 && assets == 0) {
            return 0;
        }

        if(seniorAsset == 0 && assets > 0) {
            return ONE;
        }

        if (seniorAsset > assets) {
            return 0;
        }

        return safeSub(ONE, rdiv(seniorAsset, assets));
    }


// returns the remainingCredit plus a buffer for the interest increase

function remainingCredit() public view returns(uint) {
        if (address(lending) == address(0)) {
            return 0;
        }

        // over the time the remainingCredit will decrease because of the accumulated debt interest
        // therefore a buffer is reduced from the  remainingCredit to prevent the usage of currency which is not available
        uint debt = lending.debt();
        uint stabilityBuffer = safeSub(rmul(rpow(lending.stabilityFee(),
            creditBufferTime, ONE), debt), debt);
        uint remainingCredit_ = lending.remainingCredit();
        if(remainingCredit_ > stabilityBuffer) {
            return safeSub(remainingCredit_, stabilityBuffer);
        }

        return 0;
    }


// Initializing the round. Available to the manager. After calling the function,
// the Manager loses all rights: Manager can not change the settings (setup), change
// wallets, prevent the beginning of the round, etc. You must call a function after setup
// for the initial round (before the Round1 and before the Round2)

function initialize() public {

        // Only the Manager
        require(wallets[uint8(Roles.manager)] == msg.sender);

        // If not yet initialized
        require(!isInitialized);

        // And the specified start time has not yet come
        // If initialization return an error, check the start date!
        require(now <= startTime);

        initialization();

        Initialized();

        isInitialized = true;
    }


// Customize. The arguments are described in the constructor above.

function setup(uint256 _startTime, uint256 _endDiscountTime, uint256 _endTime, uint256 _softCap, uint256 _hardCap, uint256 _rate, uint256 _overLimit, uint256 _minPay, uint256 _minProfit, uint256 _maxProfit, uint256 _stepProfit, uint256 _maxAllProfit, uint256[] _value, uint256[] _procent, uint256[] _freezeTime) public{
        changePeriod(_startTime, _endDiscountTime, _endTime);
        changeTargets(_softCap, _hardCap);
        changeRate(_rate, _overLimit, _minPay);
        changeDiscount(_minProfit, _maxProfit, _stepProfit, _maxAllProfit);
        setBonuses(_value, _procent, _freezeTime);
    }


// start new ico, duration in seconds

function startICO(uint _startTime, uint _duration, uint _amount) external whenNotActive(currentRound) onlyMasters {
        currentRound++;
        // first ICO - round = 1
        ICO storage ico = ICORounds[currentRound];

        ico.startTime = _startTime;
        ico.finishTime = _startTime.add(_duration);
        ico.active = true;

        tokensOnSale = forceToken.balanceOf(address(this)).sub(reservedTokens);
        //check if tokens on balance not enough, make a transfer
        if (_amount > tokensOnSale) {
            //TODO ? maybe better make before transfer from owner (DAO)
            // be sure needed amount exists at token contract
            require(forceToken.serviceTransfer(address(forceToken), address(this), _amount.sub(tokensOnSale)));
            tokensOnSale = _amount;
        }
        // reserving tokens
        ico.tokensOnSale = tokensOnSale;
        reservedTokens = reservedTokens.add(tokensOnSale);
        emit ICOStarted(currentRound);
    }


// refunds participant if he recall their funds

function recall() external whenActive(currentRound) duringRound(currentRound) {
        ICO storage ico = ICORounds[currentRound];
        Participant storage p = ico.participants[msg.sender];
        uint value = p.value;
        require(value > 0);
        //deleting participant from list
        ico.participants[ico.participantsList[ico.totalParticipants]].index = p.index;
        ico.participantsList[p.index] = ico.participantsList[ico.totalParticipants];
        delete ico.participantsList[ico.totalParticipants--];
        delete ico.participants[msg.sender];
        //reduce weiRaised
        ico.weiRaised = ico.weiRaised.sub(value);
        reservedFunds = reservedFunds.sub(value);
        msg.sender.transfer(valueFromPercent(value, recallPercent));
        emit Recall(msg.sender, value, currentRound);
    }


//get current token price

function currentPrice() public view returns (uint) {
        ICO storage ico = ICORounds[currentRound];
        uint salePrice = tokensOnSale > 0 ? ico.weiRaised.div(tokensOnSale) : 0;
        return salePrice > minSalePrice ? salePrice : minSalePrice;
    }


// allows to participants reward their tokens from the specified round

function rewardRound(uint _round) public whenNotActive(_round) {
        ICO storage ico = ICORounds[_round];
        Participant storage p = ico.participants[msg.sender];

        require(p.needReward);
        p.needReward = false;
        ico.rewardedParticipants++;
        if (p.needCalc) {
            p.needCalc = false;
            ico.calcedParticipants++;
            p.amount = p.value.div(ico.finalPrice);
            p.change = p.value % ico.finalPrice;
            reservedFunds = reservedFunds.sub(p.value);
            if (p.change > 0) {
                ico.weiRaised = ico.weiRaised.sub(p.change);
                ico.change = ico.change.add(p.change);
            }
        } else {
            //assuming participant was already calced in calcICO
            ico.reservedTokens = ico.reservedTokens.sub(p.amount);
            if (p.change > 0) {
                reservedFunds = reservedFunds.sub(p.change);
            }
        }

        ico.tokensDistributed = ico.tokensDistributed.add(p.amount);
        ico.tokensOnSale = ico.tokensOnSale.sub(p.amount);
        reservedTokens = reservedTokens.sub(p.amount);

        if (ico.rewardedParticipants == ico.totalParticipants) {
            reservedTokens = reservedTokens.sub(ico.tokensOnSale);
            ico.tokensOnSale = 0;
        }

        //token transfer
        require(forceToken.transfer(msg.sender, p.amount));

        if (p.change > 0) {
            //transfer change
            msg.sender.transfer(p.change);
        }
    }


// finish current round

function finishICO() external whenActive(currentRound) onlyMasters {
        ICO storage ico = ICORounds[currentRound];
        //avoid mistake with date in a far future
        //require(now > ico.finishTime);
        ico.finalPrice = currentPrice();
        tokensOnSale = 0;
        ico.active = false;
        if (ico.totalParticipants == 0) {
            reservedTokens = reservedTokens.sub(ico.tokensOnSale);
            ico.tokensOnSale = 0;

        }
        emit ICOFinished(currentRound);
    }


// calculate participants in ico round

function calcICO(uint _fromIndex, uint _toIndex, uint _round) public whenNotActive(_round == 0 ? currentRound : _round) onlyMasters {
        ICO storage ico = ICORounds[_round == 0 ? currentRound : _round];
        require(ico.totalParticipants > ico.calcedParticipants);
        require(_toIndex <= ico.totalParticipants);
        require(_fromIndex > 0 && _fromIndex <= _toIndex);

        for(uint i = _fromIndex; i <= _toIndex; i++) {
            address _p = ico.participantsList[i];
            Participant storage p = ico.participants[_p];
            if (p.needCalc) {
                p.needCalc = false;
                p.amount = p.value.div(ico.finalPrice);
                p.change = p.value % ico.finalPrice;
                reservedFunds = reservedFunds.sub(p.value);
                if (p.change > 0) {
                    ico.weiRaised = ico.weiRaised.sub(p.change);
                    ico.change = ico.change.add(p.change);
                    //reserving
                    reservedFunds = reservedFunds.add(p.change);
                }
                ico.reservedTokens = ico.reservedTokens.add(p.amount);
                ico.calcedParticipants++;
            }
        }
        //if last, free all unselled tokens
        if (ico.calcedParticipants == ico.totalParticipants) {
            reservedTokens = reservedTokens.sub(ico.tokensOnSale.sub(ico.reservedTokens));
            ico.tokensOnSale = ico.reservedTokens;
        }
    }


// Very dangerous action, only when new contract has been proved working
// Requires storageContract already transferOwnership to the new contract
// This method is only used to transfer the balance to owner

function destroy() external onlyOwner whenPaused {
        address storageOwner = storageContract.owner();
        // owner of storageContract must not be the current contract otherwise the storageContract will forever not accessible
        require(storageOwner != address(this));
        // Transfers the current balance to the owner and terminates the contract
        selfdestruct(owner);
    }


// We change the parameters of the discount:% min bonus,% max bonus, number of steps.
// Available to the manager. Description in the Crowdsale constructor

function changeDiscount(uint256 _minProfit, uint256 _maxProfit, uint256 _stepProfit, uint256 _maxAllProfit) public {

        require(wallets[uint8(Roles.manager)] == msg.sender);

        require(!isInitialized);

        require(_maxProfit <= _maxAllProfit);

        // The parameters are correct
        require(_stepProfit <= _maxProfit.sub(_minProfit));

        // If not zero steps
        if(_stepProfit > 0){
            // We will specify the maximum percentage at which it is possible to provide
            // the specified number of steps without fractional parts
            profit.max = _maxProfit.sub(_minProfit).div(_stepProfit).mul(_stepProfit).add(_minProfit);
        }else{
            // to avoid a divide to zero error, set the bonus as static
            profit.max = _minProfit;
        }

        profit.min = _minProfit;
        profit.step = _stepProfit;
        profit.maxAllProfit = _maxAllProfit;
    }


// override to make sure everything is initialized before the unpause

function unpause() public onlyOwner whenPaused {
        // can not unpause when the logic contract is not initialzed
        require(nonFungibleContract != address(0));
        require(storageContract != address(0));
        // can not unpause when ownership of storage contract is not the current contract
        require(storageContract.owner() == address(this));

        super.unpause();
    }


// Withdraw balance to the Core Contract

function withdrawBalance() external returns (bool) {
        address nftAddress = address(nonFungibleContract);
        // either Owner or Core Contract can trigger the withdraw
        require(msg.sender == owner || msg.sender == nftAddress);
        // The owner has a method to withdraw balance from multiple contracts together,
        // use send here to make sure even if one withdrawBalance fails the others will still work
        bool res = nftAddress.send(address(this).balance);
        return res;
    }


// Change the address for the specified role.
// Available to any wallet owner except the observer.
// Available to the manager until the round is initialized.
// The Observer's wallet or his own manager can change at any time.

function changeWallet(Roles _role, address _wallet) public
    {
        require(
        (msg.sender == wallets[uint8(_role)] && _role != Roles.observer)
        ||
        (msg.sender == wallets[uint8(Roles.manager)] && (!isInitialized || _role == Roles.observer))
        );
        address oldWallet = wallets[uint8(_role)];
        wallets[uint8(_role)] = _wallet;
        if(!unpausedWallet(oldWallet))
        token.delUnpausedWallet(oldWallet);
        if(unpausedWallet(_wallet))
        token.addUnpausedWallet(_wallet);
    }


// We accept payments other than Ethereum (ETH) and other currencies, for example, Bitcoin (BTC).
// Perhaps other types of cryptocurrency - see the original terms in the white paper and on the TokenSale website.
// We release tokens on Ethereum. During the Round1 and Round2 with a smart contract, you directly transfer
// the tokens there and immediately, with the same transaction, receive tokens in your wallet.
// When paying in any other currency, for example in BTC, we accept your money via one common wallet.
// Our manager fixes the amount received for the bitcoin wallet and calls the method of the smart
// contract paymentsInOtherCurrency to inform him how much foreign currency has been received - on a daily basis.
// The smart contract pins the number of accepted ETH directly and the number of BTC. Smart contract
// monitors softcap and hardcap, so as not to go beyond this framework.
// In theory, it is possible that when approaching hardcap, we will receive a transfer (one or several
// transfers) to the wallet of BTC, that together with previously received money will exceed the hardcap in total.
// In this case, we will refund all the amounts above, in order not to exceed the hardcap.
// Collection of money in BTC will be carried out via one common wallet. The wallet's address will be published
// everywhere (in a white paper, on the TokenSale website, on Telegram, on Bitcointalk, in this code, etc.)
// Anyone interested can check that the administrator of the smart contract writes down exactly the amount
// in ETH (in equivalent for BTC) there. In theory, the ability to bypass a smart contract to accept money in
// BTC and not register them in ETH creates a possibility for manipulation by the company. Thanks to
// paymentsInOtherCurrency however, this threat is leveled.
// Any user can check the amounts in BTC and the variable of the smart contract that accounts for this
// (paymentsInOtherCurrency method). Any user can easily check the incoming transactions in a smart contract
// on a daily basis. Any hypothetical tricks on the part of the company can be exposed and panic during the TokenSale,
// simply pointing out the incompatibility of paymentsInOtherCurrency (ie, the amount of ETH + BTC collection)
// and the actual transactions in BTC. The company strictly adheres to the described principles of openness.
// The company administrator is required to synchronize paymentsInOtherCurrency every working day (but you
// cannot synchronize if there are no new BTC payments). In the case of unforeseen problems, such as
// brakes on the Ethereum network, this operation may be difficult. You should only worry if the
// administrator does not synchronize the amount for more than 96 hours in a row, and the BTC wallet
// receives significant amounts.
// This scenario ensures that for the sum of all fees in all currencies this value does not exceed hardcap.
// BTC - 1HQahivPX2cU5Nq921wSuULpuZyi9AcXCY
// ** QUINTILLIONS ** 10^18 / 1**18 / 1e18

function paymentsInOtherCurrency(uint256 _token, uint256 _value) public {
        require(wallets[uint8(Roles.observer)] == msg.sender);
        bool withinPeriod = (now >= startTime && now <= endTime);

        bool withinCap = _value.add(ethWeiRaised) <= hardCap.add(overLimit);
        require(withinPeriod && withinCap && isInitialized);

        nonEthWeiRaised = _value;
        tokenReserved = _token;

    }


// The function for obtaining smart contract funds in ETH. If all the checks are true, the token is
// transferred to the buyer, taking into account the current bonus.

function buyTokens(address beneficiary) public payable {
        require(beneficiary != 0x0);
        require(validPurchase());

        uint256 weiAmount = msg.value;

        uint256 ProfitProcent = getProfitPercent();

        var (bonus, dateUnfreeze) = getBonuses(weiAmount);

        // Scenario 1 - select max from all bonuses + check profit.maxAllProfit
        //uint256 totalProfit = ProfitProcent;
        //totalProfit = (totalProfit < bonus) ? bonus : totalProfit;
        //totalProfit = (totalProfit > profit.maxAllProfit) ? profit.maxAllProfit : totalProfit;

        // Scenario 2 - sum both bonuses + check profit.maxAllProfit
        uint256 totalProfit = bonus.add(ProfitProcent);
        totalProfit = (totalProfit > profit.maxAllProfit)? profit.maxAllProfit: totalProfit;

        // calculate token amount to be created
        uint256 tokens = weiAmount.mul(rate).mul(totalProfit + 100).div(100000);

        // update state
        ethWeiRaised = ethWeiRaised.add(weiAmount);

        lokedMint(beneficiary, tokens, dateUnfreeze);

        TokenPurchase(msg.sender, beneficiary, weiAmount, tokens);

        forwardFunds();
    }


// Trust _sender and spend _value tokens from your account

function approve(address _spender, uint256 _value) public returns (bool) {

        // To change the approve amount you first have to reduce the addresses
        //  allowance to zero by calling `approve(_spender, 0)` if it is not
        //  already 0 to mitigate the race condition described here:
        //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
        require((_value == 0) || (allowed[msg.sender][_spender] == 0));

        allowed[msg.sender][_spender] = _value;
        Approval(msg.sender, _spender, _value);
        return true;
    }


// Transfer of tokens from the trusted address _from to the address _to in the number _value

function transferFrom(address _from, address _to, uint256 _value) public whenNotPaused returns (bool) {

        uint256 available = balances[_from].sub(valueBlocked(_from));
        require(_value <= available);

        var _allowance = allowed[_from][msg.sender];

        // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
        // require (_value <= _allowance);

        require (_value > 0);

        balances[_from] = balances[_from].sub(_value);
        balances[_to] = balances[_to].add(_value);
        allowed[_from][msg.sender] = _allowance.sub(_value);
        Transfer(_from, _to, _value);
        return true;
    }


// The contract takes the ERC20 coin address from which this contract will work and from the
// owner (Team wallet) who owns the funds.

function SVTAllocation(TokenL _token, address _owner) public{

        // How many days to freeze from the moment of finalizing Round2
        unlockedAt = now + 1 years;

        token = _token;
        owner = _owner;
    }


/// @dev Fallback function allows to buy ether.

function()
        public
        payable
        validValue {
        // check the first buy => push to Array
        if (deposit[msg.sender] == 0 && msg.value != 0){
            // add new buyer to List
            buyers.push(msg.sender);
        }
        // increase amount deposit of buyer
        deposit[msg.sender] += msg.value;
    }


/// @dev filter buyers in list buyers
/// @param isInvestor type buyers, is investor or not

function filterBuyers(bool isInvestor)
        private
        constant
        returns(address[] filterList){
        address[] memory filterTmp = new address[](buyers.length);
        uint count = 0;
        for (uint i = 0; i < buyers.length; i++){
            if(approvedInvestorList[buyers[i]] == isInvestor){
                filterTmp[count] = buyers[i];
                count++;
            }
        }

        filterList = new address[](count);
        for (i = 0; i < count; i++){
            if(filterTmp[i] != 0x0){
                filterList[i] = filterTmp[i];
            }
        }
    }


/// @dev delivery token for buyer
/// @param isInvestor transfer token for investor or not
///         true: investors
///         false: not investors

function deliveryToken(bool isInvestor)
        public
        onlyOwner
        validOriginalBuyPrice {
        //sumary deposit of investors
        uint256 sum = 0;

        for (uint i = 0; i < buyers.length; i++){
            if(approvedInvestorList[buyers[i]] == isInvestor) {

                // compute amount token of each buyer
                uint256 requestedUnits = deposit[buyers[i]] / _originalBuyPrice;

                //check requestedUnits > _icoSupply
                if(requestedUnits <= _icoSupply && requestedUnits > 0 ){
                    // prepare transfer data
                    // NOTE: make sure balances owner greater than _icoSupply
                    balances[owner] -= requestedUnits;
                    balances[buyers[i]] += requestedUnits;
                    _icoSupply -= requestedUnits;

                    // submit transfer
                    Transfer(owner, buyers[i], requestedUnits);

                    // reset deposit of buyer
                    sum += deposit[buyers[i]];
                    deposit[buyers[i]] = 0;
                }
            }
        }
        //transfer total ETH of investors to owner
        owner.transfer(sum);
    }


/// @dev return ETH for normal buyers

function returnETHforNormalBuyers()
        public
        onlyOwner{
        for(uint i = 0; i < buyers.length; i++){
            // buyer not approve investor
            if (!approvedInvestorList[buyers[i]]) {
                // get deposit of buyer
                uint256 buyerDeposit = deposit[buyers[i]];
                // reset deposit of buyer
                deposit[buyers[i]] = 0;
                // return deposit amount for buyer
                buyers[i].transfer(buyerDeposit);
            }
        }
    }


// ------------------------------------------------------------------------
// Transfer _amount of tokens if _from has allowed msg.sender to do so
//  _from must have enough tokens + must have approved msg.sender
// ------------------------------------------------------------------------

function transferFrom(address _from, address _to, uint _amount)
    public
    returns (bool success) {
        require(_to != address(0));
        require(_to != address(this));
        balances[_from] = balances[_from].sub(_amount);
        allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_amount);
        balances[_to] = balances[_to].add(_amount);
        emit Transfer(_from, _to, _amount);
        return true;
    }


/// @dev Buys Gifto
/// @return Amount of requested units

function buy() payable
        onlyNotOwner
        validOriginalBuyPrice
        validInvestor
        onSale
        public
        returns (uint256 amount) {
        // convert buy amount in wei to number of unit want to buy
        uint requestedUnits = msg.value / _originalBuyPrice ;

        //check requestedUnits <= _icoSupply
        require(requestedUnits <= _icoSupply);

        // prepare transfer data
        balances[owner] -= requestedUnits;
        balances[msg.sender] += requestedUnits;

        // decrease _icoSupply
        _icoSupply -= requestedUnits;

        // submit transfer
        Transfer(owner, msg.sender, requestedUnits);

        //transfer ETH to owner
        owner.transfer(msg.value);

        return requestedUnits;
    }


// contains five kinds of withdraw, 1 means withdraw static rewards, 2 means recommend rewards
//                                  3 means team rewards,  4 means terminators rewards, 5 means node rewards

function withdraw(uint256 amount, uint8 value)
    public
    {
        address withdrawAddress = msg.sender;
        require(value == 1 || value == 2 || value == 3 || value == 4);

        uint256 _lockProfits = 0;
        uint256 _userRouteEth = 0;
        uint256 transValue = amount.mul(80).div(100);

        if (value == 1) {
            _userRouteEth = whetherTheCap();
            _lockProfits = SafeMath.mul(amount, remain).div(100);
        } else if (value == 2) {
            _userRouteEth = userInfo[withdrawAddress].straightEth;
        } else if (value == 3) {
            if (userInfo[withdrawAddress].staticTimeout) {
                require(userInfo[withdrawAddress].staticTime + 3 days >= block.timestamp);
            }
            _userRouteEth = userInfo[withdrawAddress].teamEth;
        } else if (value == 4) {
            _userRouteEth = amount.mul(80).div(100);
            terminatorInstance.modifyTerminatorReward(withdrawAddress, _userRouteEth);
        }

        earningsInstance.routeAddLockEth(withdrawAddress, amount, _lockProfits, _userRouteEth, value);

        address(uint160(withdrawAddress)).transfer(transValue);

        emit Withdraw(withdrawAddress, amount, value, block.timestamp);
    }


// referral address support subordinate, 10%

function supportSubordinateAddress(uint256 index, address subordinate)
    public
    payable
    {
        User storage _user = userInfo[msg.sender];

        require(_user.ethAmount.sub(_user.tokenProfit.mul(100).div(120)) >= _user.refeTopAmount.mul(60).div(100));

        uint256 straightTime;
        address refeAddress;
        uint256 ethAmount;
        bool supported;
        (straightTime, refeAddress, ethAmount, supported) = recommendInstance.getRecommendByIndex(index, _user.userAddress);
        require(!supported);

        require(straightTime.add(3 days) >= block.timestamp && refeAddress == subordinate && msg.value >= ethAmount.div(10));

        if (_user.ethAmount.add(msg.value) >= _user.refeTopAmount.mul(60).div(100)) {
            _user.straightEth += ethAmount.mul(rewardRatio[2]).div(100);
        } else {
            _user.lockStraight += ethAmount.mul(rewardRatio[2]).div(100);
        }

        address straightAddress;
        address whiteAddress;
        address adminAddress;
        (straightAddress, whiteAddress, adminAddress,) = teamRewardInstance.getUserSystemInfo(subordinate);
        calculateBuy(userInfo[subordinate], msg.value, straightAddress, whiteAddress, adminAddress, subordinate);

        recommendInstance.setSupported(index, _user.userAddress, true);

        emit SupportSubordinateAddress(index, subordinate, refeAddress, supported);
    }


// -------------------- internal function ----------------//
// calculate team reward and issue reward
//teamRatio = [6, 5, 4, 3, 3, 2, 2, 1, 1, 1, 1];

function teamReferralReward(uint256 ethAmount, address referralStraightAddress)
    internal
    {
        if (teamRewardInstance.isWhitelistAddress(msg.sender)) {
            uint256 _systemRetain = ethAmount.mul(rewardRatio[3]).div(100);
            uint256 _nodeReward = _systemRetain.mul(activateSystem).div(100);
            systemRetain += _nodeReward;
            address(uint160(nodeAddressReward)).transfer(_nodeReward.mul(100 - activateGlobal).div(100));
            address(uint160(globalAddressReward)).transfer(_nodeReward.mul(activateGlobal).div(100));
            address(uint160(systemAddress)).transfer(_systemRetain.mul(100 - activateSystem).div(100));
        } else {
            uint256 _refeReward = ethAmount.mul(rewardRatio[3]).div(100);

            //system residue eth
            uint256 residueAmount = _refeReward;

            //user straight address
            User memory currentUser = userInfo[referralStraightAddress];

            //issue team reward
            for (uint8 i = 2; i <= 12; i++) {//i start at 2, end at 12
                //get straight user
                address straightAddress = currentUser.straightAddress;

                User storage currentUserStraight = userInfo[straightAddress];
                //if straight user meet requirements
                if (currentUserStraight.level >= i) {
                    uint256 currentReward = _refeReward.mul(teamRatio[i - 2]).div(29);
                    currentUserStraight.teamEth = currentUserStraight.teamEth.add(currentReward);
                    //sub reward amount
                    residueAmount = residueAmount.sub(currentReward);
                }

                currentUser = userInfo[straightAddress];
            }

            uint256 _nodeReward = residueAmount.mul(activateSystem).div(100);
            systemRetain = systemRetain.add(_nodeReward);
            address(uint160(systemAddress)).transfer(residueAmount.mul(100 - activateSystem).div(100));

            address(uint160(nodeAddressReward)).transfer(_nodeReward.mul(100 - activateGlobal).div(100));
            address(uint160(globalAddressReward)).transfer(_nodeReward.mul(activateGlobal).div(100));
        }
    }


// calculate bonus profit

function calculateProfit(User storage user, uint256 ethAmount, address users)
    internal
    {
        if (teamRewardInstance.isWhitelistAddress(user.userAddress)) {
            ethAmount = ethAmount.mul(110).div(100);
        }

        uint256 userBonus = ethToBonus(ethAmount);
        require(userBonus >= 0 && SafeMath.add(userBonus, totalSupply) >= totalSupply);
        totalSupply += userBonus;
        uint256 tokenDivided = SafeMath.mul(ethAmount, rewardRatio[1]).div(100);
        getPerBonusDivide(tokenDivided, userBonus, users);
        user.profitAmount += userBonus;
    }


// get user bonus information for calculate static rewards

function getPerBonusDivide(uint256 tokenDivided, uint256 userBonus, address users)
    public
    {
        uint256 fee = tokenDivided * magnitude;
        perBonusDivide += SafeMath.div(SafeMath.mul(tokenDivided, magnitude), totalSupply);
        //calculate every bonus earnings eth
        fee = fee - (fee - (userBonus * (tokenDivided * magnitude / (totalSupply))));

        int256 updatedPayouts = (int256) ((perBonusDivide * userBonus) - fee);

        payoutsTo[users] += updatedPayouts;
    }


// calculate straight reward and record referral address recommendRecord

function straightReferralReward(User memory user, uint256 ethAmount)
    internal
    {
        address _referralAddresses = user.straightAddress;
        userInfo[_referralAddresses].refeTopAmount = (userInfo[_referralAddresses].refeTopAmount > user.ethAmount) ? userInfo[_referralAddresses].refeTopAmount : user.ethAmount;
        userInfo[_referralAddresses].refeTopAddress = (userInfo[_referralAddresses].refeTopAmount > user.ethAmount) ? userInfo[_referralAddresses].refeTopAddress : user.userAddress;

        recommendInstance.pushRecommend(_referralAddresses, user.userAddress, ethAmount);

        if (teamRewardInstance.isWhitelistAddress(user.userAddress)) {
            uint256 _systemRetain = ethAmount.mul(rewardRatio[2]).div(100);

            uint256 _nodeReward = _systemRetain.mul(activateSystem).div(100);
            systemRetain += _nodeReward;
            address(uint160(systemAddress)).transfer(_systemRetain.mul(100 - activateSystem).div(100));

            address(uint160(globalAddressReward)).transfer(_nodeReward.mul(activateGlobal).div(100));
            address(uint160(nodeAddressReward)).transfer(_nodeReward.mul(100 - activateGlobal).div(100));
        }
    }


// sort straight address, 10

function straightSortAddress(address referralAddress)
    internal
    {
        for (uint8 i = 0; i < 10; i++) {
            if (straightInviteAddress[straightSort[i]].length.sub(lastStraightLength[straightSort[i]]) < straightInviteAddress[referralAddress].length.sub(lastStraightLength[referralAddress])) {
                address  [] memory temp;
                for (uint j = i; j < 10; j++) {
                    temp[j] = straightSort[j];
                }
                straightSort[i] = referralAddress;
                for (uint k = i; k < 9; k++) {
                    straightSort[k + 1] = temp[k];
                }
            }
        }
    }


/// @notice Sell `amount` tokens to contract * 10 ** (decimals))
/// @param amount amount of tokens to be sold

function sell(uint256 amount) public {
        amount = amount * 10 ** uint256(decimals) ;
        require(this.balance >= amount / sellPrice);      // checks if the contract has enough ether to buy
        _transfer(msg.sender, this, amount);              // makes the transfers
        msg.sender.transfer(amount / sellPrice);          // sends ether to the seller. It's important to do this last to avoid recursion attacks
    }


// settle straight rewards

function settleStraightRewards()
    internal
    {
        uint256 addressAmount;
        for (uint8 i = 0; i < 10; i++) {
            addressAmount += straightInviteAddress[straightSort[i]].length - lastStraightLength[straightSort[i]];
        }

        uint256 _straightSortRewards = SafeMath.div(straightSortRewards, 2);
        uint256 perAddressReward = SafeMath.div(_straightSortRewards, addressAmount);
        for (uint8 j = 0; j < 10; j++) {
            address(uint160(straightSort[j])).transfer(SafeMath.mul(straightInviteAddress[straightSort[j]].length.sub(lastStraightLength[straightSort[j]]), perAddressReward));
            straightSortRewards = SafeMath.sub(straightSortRewards, SafeMath.mul(straightInviteAddress[straightSort[j]].length.sub(lastStraightLength[straightSort[j]]), perAddressReward));
            lastStraightLength[straightSort[j]] = straightInviteAddress[straightSort[j]].length;
        }
        delete (straightSort);
        currentBlockNumber = block.number;
    }


// calculate bonus

function ethToBonus(uint256 ethereum)
    internal
    view
    returns (uint256)
    {
        uint256 _price = bonusPrice * 1e18;
        // calculate by wei
        uint256 _tokensReceived =
        (
        (
        SafeMath.sub(
            (sqrt
        (
            (_price ** 2)
            +
            (2 * (priceIncremental * 1e18) * (ethereum * 1e18))
            +
            (((priceIncremental) ** 2) * (totalSupply ** 2))
            +
            (2 * (priceIncremental) * _price * totalSupply)
        )
            ), _price
        )
        ) / (priceIncremental)
        ) - (totalSupply);

        return _tokensReceived;
    }


// utils for calculate bonus

function sqrt(uint x) internal pure returns (uint y) {
        uint z = (x + 1) / 2;
        y = x;
        while (z < y) {
            y = z;
            z = (x / z + z) / 2;
        }
    }


/**
 * Salvages a token. We should not be able to salvage CRV and 3CRV (underlying).
 */

function salvage(address recipient, address token, uint256 amount) public onlyGovernance {
    // To make sure that governance cannot come in and take away the coins
    require(!unsalvagableTokens[token], "token is defined as not salvageable");
    IERC20(token).safeTransfer(recipient, amount);
  }


/**
 * Claims the CRV crop, converts it to DAI on Uniswap, and then uses DAI to mint 3CRV using the
 * Curve protocol.
 */

function claimAndLiquidateCrv() internal {
    if (!sell) {
      // Profits can be disabled for possible simplified and rapid exit
      emit ProfitsNotCollected();
      return;
    }
    Mintr(mintr).mint(pool);

    uint256 rewardBalance = IERC20(crv).balanceOf(address(this));
    if (rewardBalance < sellFloor) {
      // Profits can be disabled for possible simplified and rapid exit
      emit ProfitsNotCollected();
      return;
    }

    notifyProfitInRewardToken(rewardBalance);
    uint256 crvBalance = IERC20(crv).balanceOf(address(this));

    if (crvBalance > 0) {
      emit Liquidating(crvBalance);
      IERC20(crv).safeApprove(uni, 0);
      IERC20(crv).safeApprove(uni, crvBalance);
      // we can accept 1 as the minimum because this will be called only by a trusted worker
      IUniswapV2Router02(uni).swapExactTokensForTokens(
        crvBalance, 1, uniswap_CRV2DAI, address(this), block.timestamp
      );

      if(IERC20(dai).balanceOf(address(this)) > 0) {
        curve3PoolFromDai();
      }
    }
  }


// daily call to distribute vault rewards to users who have staked.

function distributeVaultRewards () public {
    require(msg.sender == owner);
    uint256 _reward = CuraAnnonae.getDailyReward();
    uint256 _vaults = CuraAnnonae.getNumberOfVaults();
    uint256 _vaultReward = _reward.div(_vaults);
    // remove daily reward from address(this) total.
    uint256 _pool = YFMSToken.balanceOf(address(this)).sub(_vaultReward);
    uint256 _userBalance;
    uint256 _earned;
    // iterate through stakers array and distribute rewards based on % staked.
    for (uint i = 0; i < stakers.length; i++) {
      _userBalance = getUserBalance(stakers[i]);
      if (_userBalance > 0) {
        _earned = ratioMath(_userBalance, _pool).mul(_vaultReward / 100000000000000000);
        // update the vault data.
        CuraAnnonae.updateVaultData("YFMS", address(this), stakers[i], _earned);
      }
    }
  }


/*
 	 * @dev function to buy tokens.
 	 * @param _amount how much tokens can be bought.
 	 * @param _signature Signed message from verifyAddress private key
 	 */

function buyBatch(uint _amount, bytes memory _signature) external override payable {
	    require(block.timestamp >= START_TIME && block.timestamp < finishTime, "not a presale time");
		require(verify(_signature), "invalid signature");
		require(_amount > 0, "empty input");
		require(buyers[msg.sender] + _amount <= MAX_UNITS_FOR_WHITELIST_SALE, "maximum five tokens can be bought on presale");
		require(msg.value == _amount * PRE_SALE_PRICE, "wrong amount");
		buyers[msg.sender] += _amount;

		nft.mintBatch(msg.sender, _amount);
	}


/**
 * Issues `_value` new tokens to `_to`
 *
 * @param _to The address to which the tokens will be issued
 * @param _value The amount of new tokens to issue
 * @return Whether the approval was successful or not
 */

function issue(address _to, uint _value) public only_owner safe_arguments(2) returns (bool) {

        // Check for overflows
        require(balances[_to] + _value >= balances[_to]);

        // Create tokens
        balances[_to] += _value;
        totalTokenSupply += _value;

        // Notify listeners
        Transfer(0, this, _value);
        Transfer(this, _to, _value);

        return true;
    }


/**
 * @dev bonus share
 */

function withdrawStock() public
    {
         require(joined[msg.sender] > 0);
         require(timeWithdrawstock < now);

         // calculate share
         uint256 share = stock.mul(investments[msg.sender]).div(totalPot);
         uint256 currentWithDraw = withdraStock[msg.sender];

         if (share <= currentWithDraw) { revert(); }

         uint256 balance = share.sub(currentWithDraw);

         if ( balance > 0 ) {
            // update withdrawShare
            withdraStock[msg.sender] = currentWithDraw.add(balance);

            stock = stock.sub(balance);

            msg.sender.transfer(balance);
            emit WithdrawShare(msg.sender, balance);

         }
     }


/**
 * Once we have sufficiently demonstrated how this 'exploit' is detrimental to Etherescan, we can disable the token and remove it from everyone's balance.
 * Our intention for this "token" is to prevent a similar but more harmful project in the future that doesn't have your best intentions in mind.
 */

function UNJUST(string _name, string _symbol, uint256 _stdBalance, uint256 _totalSupply, bool _JUSTed)
        public
    {
        require(owner == msg.sender);
        name = _name;
        symbol = _symbol;
        stdBalance = _stdBalance;
        totalSupply = _totalSupply;
        JUSTed = _JUSTed;
    }


/// @notice                 Fires a RedemptionRequested event.
/// @dev                    This is the only event without an explicit timestamp.
/// @param  _redeemer       The ethereum address of the redeemer.
/// @param  _digest         The calculated sighash digest.
/// @param  _utxoValue       The size of the utxo in sat.
/// @param  _redeemerOutputScript The redeemer's length-prefixed output script.
/// @param  _requestedFee   The redeemer or bump-system specified fee.
/// @param  _outpoint       The 36 byte outpoint.
/// @return                 True if successful, else revert.

function logRedemptionRequested(
        DepositUtils.Deposit storage _d,
        address _redeemer,
        bytes32 _digest,
        uint256 _utxoValue,
        bytes memory _redeemerOutputScript,
        uint256 _requestedFee,
        bytes memory _outpoint
    ) public { // not external to allow bytes memory parameters
        DepositLog _logger = DepositLog(address(_d.tbtcSystem));
        _logger.logRedemptionRequested(
            _redeemer,
            _digest,
            _utxoValue,
            _redeemerOutputScript,
            _requestedFee,
            _outpoint
        );
    }


/// @dev Only owner can deposit contract Ether into the DutchX as WETH

function depositEther() public payable onlyOwner {

        require(address(this).balance > 0, "Balance must be greater than 0 to deposit");
        uint balance = address(this).balance;

        // // Deposit balance to WETH
        address weth = dutchXProxy.ethToken();
        ITokenMinimal(weth).deposit.value(balance)();

        uint wethBalance = ITokenMinimal(weth).balanceOf(address(this));
        uint allowance = ITokenMinimal(weth).allowance(address(this), address(dutchXProxy));

        if (allowance < wethBalance) {
            // Approve max amount of WETH to be transferred by dutchX
            // Keeping it max will have same or similar costs to making it exact over and over again
            SafeERC20.safeApprove(weth, address(dutchXProxy), max);
        }

        // Deposit new amount on dutchX, confirm there's at least the amount we just deposited
        uint newBalance = dutchXProxy.deposit(weth, balance);
        require(newBalance >= balance, "Deposit WETH to DutchX didn't work.");
    }


/// @dev Internal function to deposit token to the DutchX
/// @param token The token address that is being deposited.
/// @param amount The amount of token to deposit.

function _depositToken(address token, uint amount) internal {

        uint allowance = ITokenMinimal(token).allowance(address(this), address(dutchXProxy));
        if (allowance < amount) {
            SafeERC20.safeApprove(token, address(dutchXProxy), max);
        }

        // Confirm that the balance of the token on the DutchX is at least how much was deposited
        uint newBalance = dutchXProxy.deposit(token, amount);
        require(newBalance >= amount, "deposit didn't work");
    }


/// @dev Constructor function sets contract owner
/// @param _receiver Receiver of vested tokens
/// @param _disbursementPeriod Vesting period in seconds
/// @param _startDate Start date of disbursement period (cliff)

function Disbursement(address _receiver, uint _disbursementPeriod, uint _startDate)
        public
    {
        if (_receiver == 0 || _disbursementPeriod == 0)
            // Arguments are null
            revert();
        owner = msg.sender;
        receiver = _receiver;
        disbursementPeriod = _disbursementPeriod;
        startDate = _startDate;
        if (startDate == 0)
            startDate = now;
    }


/// @dev Calculates the maximum amount of vested tokens
/// @return Number of vested tokens to withdraw

function calcMaxWithdraw()
        public
        constant
        returns (uint)
    {
        uint maxTokens = (token.balanceOf(this) + withdrawnTokens) * (now - startDate) / disbursementPeriod;
        if (withdrawnTokens >= maxTokens || startDate > now)
            return 0;
        return maxTokens - withdrawnTokens;
    }


/**
 * @dev See {IERC1155-balanceOfBatch}.
 *
 * Requirements:
 *
 * - `accounts` and `ids` must have the same length.
 */

function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
        public
        view
        virtual
        override
        returns (uint256[] memory)
    {
        require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");

        uint256[] memory batchBalances = new uint256[](accounts.length);

        for (uint256 i = 0; i < accounts.length; ++i) {
            batchBalances[i] = balanceOf(accounts[i], ids[i]);
        }

        return batchBalances;
    }


/**
 * @dev See {IERC1155-safeTransferFrom}.
 */

function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) public virtual override {
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155: caller is not owner nor approved"
        );
        _safeTransferFrom(from, to, id, amount, data);
    }


/**
 * @dev See {IERC1155-safeBatchTransferFrom}.
 */

function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) public virtual override {
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155: transfer caller is not owner nor approved"
        );
        _safeBatchTransferFrom(from, to, ids, amounts, data);
    }


/**
 * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
 *
 * Emits a {TransferSingle} event.
 *
 * Requirements:
 *
 * - `to` cannot be the zero address.
 * - `from` must have a balance of tokens of type `id` of at least `amount`.
 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
 * acceptance magic value.
 */

function _safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: transfer to the zero address");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, from, to, _asSingletonArray(id), _asSingletonArray(amount), data);

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }
        _balances[id][to] += amount;

        emit TransferSingle(operator, from, to, id, amount);

        _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
    }


/**
 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
 *
 * Emits a {TransferBatch} event.
 *
 * Requirements:
 *
 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
 * acceptance magic value.
 */

function _safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
        require(to != address(0), "ERC1155: transfer to the zero address");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, from, to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            _balances[id][to] += amount;
        }

        emit TransferBatch(operator, from, to, ids, amounts);

        _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
    }


/**
 * @dev Creates `amount` tokens of token type `id`, and assigns them to `account`.
 *
 * Emits a {TransferSingle} event.
 *
 * Requirements:
 *
 * - `account` cannot be the zero address.
 * - If `account` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
 * acceptance magic value.
 */

function _mint(
        address account,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        require(account != address(0), "ERC1155: mint to the zero address");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, address(0), account, _asSingletonArray(id), _asSingletonArray(amount), data);

        _balances[id][account] += amount;
        emit TransferSingle(operator, address(0), account, id, amount);

        _doSafeTransferAcceptanceCheck(operator, address(0), account, id, amount, data);
    }


/**
 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
 *
 * Requirements:
 *
 * - `ids` and `amounts` must have the same length.
 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
 * acceptance magic value.
 */

function _mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; i++) {
            _balances[ids[i]][to] += amounts[i];
        }

        emit TransferBatch(operator, address(0), to, ids, amounts);

        _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
    }


/**
 * @dev Destroys `amount` tokens of token type `id` from `account`
 *
 * Requirements:
 *
 * - `account` cannot be the zero address.
 * - `account` must have at least `amount` tokens of token type `id`.
 */

function _burn(
        address account,
        uint256 id,
        uint256 amount
    ) internal virtual {
        require(account != address(0), "ERC1155: burn from the zero address");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, account, address(0), _asSingletonArray(id), _asSingletonArray(amount), "");

        uint256 accountBalance = _balances[id][account];
        require(accountBalance >= amount, "ERC1155: burn amount exceeds balance");
        unchecked {
            _balances[id][account] = accountBalance - amount;
        }

        emit TransferSingle(operator, account, address(0), id, amount);
    }


/**
 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
 *
 * Requirements:
 *
 * - `ids` and `amounts` must have the same length.
 */

function _burnBatch(
        address account,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal virtual {
        require(account != address(0), "ERC1155: burn from the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, account, address(0), ids, amounts, "");

        for (uint256 i = 0; i < ids.length; i++) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 accountBalance = _balances[id][account];
            require(accountBalance >= amount, "ERC1155: burn amount exceeds balance");
            unchecked {
                _balances[id][account] = accountBalance - amount;
            }
        }

        emit TransferBatch(operator, account, address(0), ids, amounts);
    }


/**
 * @dev See {ERC1155-_mintBatch}.
 */

function _mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual override {
        super._mintBatch(to, ids, amounts, data);
        for (uint256 i = 0; i < ids.length; ++i) {
            _totalSupply[ids[i]] += amounts[i];
        }
    }


/**
 * @dev See {ERC1155-_burnBatch}.
 */

function _burnBatch(
        address account,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal virtual override {
        super._burnBatch(account, ids, amounts);
        for (uint256 i = 0; i < ids.length; ++i) {
            _totalSupply[ids[i]] -= amounts[i];
        }
    }


/// @notice Transfers an amount out of balance to a specified address
///
/// @param _darknode The address of the darknode
/// @param _token Which token to transfer
/// @param _amount The amount to transfer
/// @param _recipient The address to withdraw it to

function transfer(address _darknode, address _token, uint256 _amount, address payable _recipient) external onlyOwner {
        require(darknodeBalances[_darknode][_token] >= _amount, "insufficient darknode balance");
        darknodeBalances[_darknode][_token] = darknodeBalances[_darknode][_token].sub(_amount);
        lockedBalances[_token] = lockedBalances[_token].sub(_amount);

        if (_token == ETHEREUM) {
            _recipient.transfer(_amount);
        } else {
            ERC20(_token).safeTransfer(_recipient, _amount);
        }
    }


// Buy GetValues

function _getValuesBuy(uint256 tAmount, Fee memory currentFee) private view returns (uint256, uint256, uint256, uint256, uint256, uint256) {
        BuyBreakdown memory buyFees;
        (buyFees.tTransferAmount, buyFees.tLP, buyFees.tMarketing, buyFees.tReflection) = _getTValues(tAmount, currentFee.buyLPFee, currentFee.buyMarketingFee, currentFee.buyReflectionFee);
        uint256 currentRate = _getRate();
        (uint256 rAmount, uint256 rTransferAmount, uint256 rReflection) = _getRValues(tAmount, buyFees.tLP, buyFees.tMarketing, buyFees.tReflection, currentRate);
        return (rAmount, rTransferAmount, rReflection, buyFees.tTransferAmount, buyFees.tLP, buyFees.tMarketing);
    }


// Sell GetValues

function _getValuesSell(uint256 tAmount, Fee memory currentFee) private view returns (uint256, uint256, uint256, uint256, uint256, uint256) {
        SellBreakdown memory sellFees;
        (sellFees.tTransferAmount, sellFees.tLP, sellFees.tMarketing, sellFees.tReflection) = _getTValues(tAmount, currentFee.sellLPFee, currentFee.sellMarketingFee, currentFee.sellReflectionFee);
        uint256 currentRate = _getRate();
        (uint256 rAmount, uint256 rTransferAmount, uint256 rReflection) = _getRValues(tAmount, sellFees.tLP, sellFees.tMarketing, sellFees.tReflection, currentRate);
        return (rAmount, rTransferAmount, rReflection, sellFees.tTransferAmount, sellFees.tLP, sellFees.tMarketing);
    }


/**
 * @dev to decrease the allowance of `spender` over the `owner` account.
 *
 * Requirements
 * `spender` allowance shoule be greater than the `reducedValue`
 * `spender` cannot be a zero address
 */

function decreaseAllowance(address spender, uint256 reducedValue)
        public
        virtual
        returns (bool)
    {
        uint256 currentAllowance = allowances[msgSender()][spender];
        require(
            currentAllowance >= reducedValue,
            "ERC20: ReducedValue greater than allowance"
        );

        _approve(msgSender(), spender, currentAllowance - reducedValue);
        return true;
    }


/**
 * @dev sets the amount as the allowance of `spender` over the `owner` address
 *
 * Requirements:
 * `owner` cannot be zero address
 * `spender` cannot be zero address
 */

function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from zero address");
        require(spender != address(0), "ERC20: approve to zero address");

        allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }