//
// Private functions
//

function escrowFundsForBid(Order.Data _orderData) private returns (bool) {
        require(_orderData.moneyEscrowed == 0);
        require(_orderData.sharesEscrowed == 0);
        uint256 _attosharesToCover = _orderData.amount;
        uint256 _numberOfOutcomes = _orderData.market.getNumberOfOutcomes();

        // Figure out how many almost-complete-sets (just missing `outcome` share) the creator has
        uint256 _attosharesHeld = 2**254;
        for (uint256 _i = 0; _i < _numberOfOutcomes; _i++) {
            if (_i != _orderData.outcome) {
                uint256 _creatorShareTokenBalance = _orderData.market.getShareToken(_i).balanceOf(_orderData.creator);
                _attosharesHeld = SafeMathUint256.min(_creatorShareTokenBalance, _attosharesHeld);
            }
        }

        // Take shares into escrow if they have any almost-complete-sets
        if (_attosharesHeld > 0) {
            _orderData.sharesEscrowed = SafeMathUint256.min(_attosharesHeld, _attosharesToCover);
            _attosharesToCover -= _orderData.sharesEscrowed;
            for (_i = 0; _i < _numberOfOutcomes; _i++) {
                if (_i != _orderData.outcome) {
                    _orderData.market.getShareToken(_i).trustedOrderTransfer(_orderData.creator, _orderData.market, _orderData.sharesEscrowed);
                }
            }
        }
        // If not able to cover entire order with shares alone, then cover remaining with tokens
        if (_attosharesToCover > 0) {
            _orderData.moneyEscrowed = _attosharesToCover.mul(_orderData.price);
            require(_orderData.augur.trustedTransfer(_orderData.market.getDenominationToken(), _orderData.creator, _orderData.market, _orderData.moneyEscrowed));
        }

        return true;
    }


/**
 * @dev Update old contract data
 */

function oldAlexaSync(uint limit) public {
        require(address(oldAlexa) != address(0), "Initialize closed");
        require(msg.sender == ownerAddress, "Access denied");

        for (uint i = 0; i <= limit; i++) {
            UserStruct  memory olduser;
            address oldusers = oldAlexa.userList(oldAlexaId);
            (olduser.isExist,
            olduser.id,
            olduser.referrerID,
            olduser.currentLevel,
            olduser.totalEarningEth) = oldAlexa.users(oldusers);
            address ref = oldAlexa.userList(olduser.referrerID);

            if (olduser.isExist) {
                if (!users[oldusers].isExist) {
                    users[oldusers].isExist = true;
                    users[oldusers].id = oldAlexaId;
                    users[oldusers].referrerID = olduser.referrerID;
                    users[oldusers].currentLevel = olduser.currentLevel;
                    users[oldusers].totalEarningEth = olduser.totalEarningEth;
                    userList[oldAlexaId] = oldusers;
                    users[ref].referral.push(oldusers);
                    createdDate[oldusers] = now;

                    emit regLevelEvent(oldusers, ref, now);

                    for (uint j = 1; j <= 12; j++) {
                        users[oldusers].levelExpired[j] = oldAlexa.viewUserLevelExpired(oldusers, j);
                        EarnedEth[oldusers][j] = oldAlexa.EarnedEth(oldusers, j);
                    }
                }
                oldAlexaId++;
            } else {
                currentId = oldAlexaId.sub(1);
                break;

            }
        }
    }


/// @dev collectSwapFeesForBTC collects fees in the case of swap WBTC to BTC.
/// @param _destToken The address of target token.
/// @param _incomingAmount The spent amount. (WBTC)
/// @param _minerFee The miner fees of BTC transaction.
/// @param _rewardsAmount The fees that should be paid.

function collectSwapFeesForBTC(
        address _destToken,
        uint256 _incomingAmount,
        uint256 _minerFee,
        uint256 _rewardsAmount
    ) external override onlyOwner returns (bool) {
        require(_destToken == address(0), "_destToken should be address(0)");
        address _feesToken = WBTC_ADDR;
        if (_incomingAmount > 0) {
            uint256 swapAmount = _incomingAmount.sub(_rewardsAmount).sub(
                _minerFee
            );
            _swap(WBTC_ADDR, address(0), swapAmount.add(_minerFee));
        } else if (_incomingAmount == 0) {
            _feesToken = address(0);
        }
        _rewardsCollection(_feesToken, _rewardsAmount);
        return true;
    }


/**
 * @dev register many users
 */

function registerManyUsersFullExternal(
    address[] calldata _addresses,
    uint256 _validUntilTime,
    uint256[] calldata _values) external onlyOperator returns (bool)
  {
    require(_values.length <= extendedKeys_.length, "UR08");
    for (uint256 i = 0; i < _addresses.length; i++) {
      registerUserPrivate(_addresses[i], _validUntilTime);
      updateUserExtendedPrivate(userCount_, _values);
    }
    return true;
  }


/**
 * @dev attach many addresses to many users
 */

function attachManyAddressesExternal(
    uint256[] calldata _userIds,
    address[] calldata _addresses)
    external onlyOperator returns (bool)
  {
    require(_addresses.length == _userIds.length, "UR03");
    for (uint256 i = 0; i < _addresses.length; i++) {
      attachAddress(_userIds[i], _addresses[i]);
    }
    return true;
  }


/**
 * @dev update many users
 */

function updateManyUsersExternal(
    uint256[] calldata _userIds,
    uint256 _validUntilTime,
    bool _suspended) external onlyOperator returns (bool)
  {
    for (uint256 i = 0; i < _userIds.length; i++) {
      updateUser(_userIds[i], _validUntilTime, _suspended);
    }
    return true;
  }


/**
 * @dev update many user extended informations
 */

function updateManyUsersExtendedExternal(
    uint256[] calldata _userIds,
    uint256 _key, uint256 _value) external onlyOperator returns (bool)
  {
    for (uint256 i = 0; i < _userIds.length; i++) {
      updateUserExtended(_userIds[i], _key, _value);
    }
    return true;
  }


/// @dev recordIncomingFloat mints LP token.
/// @param _token The address of target token.
/// @param _addressesAndAmountOfFloat The address of recipient and amount.
/// @param _zerofee The flag to accept zero fees.
/// @param _txid The txids which is for recording.

function recordIncomingFloat(
        address _token,
        bytes32 _addressesAndAmountOfFloat,
        bool _zerofee,
        bytes32 _txid
    ) external override onlyOwner priceCheck returns (bool) {
        require(whitelist[_token], "_token is invalid");
        require(
            _issueLPTokensForFloat(
                _token,
                _addressesAndAmountOfFloat,
                _zerofee,
                _txid
            )
        );
        return true;
    }


/**
 * @dev update many users full
 */

function updateManyUsersFullExternal(
    uint256[] calldata _userIds,
    uint256 _validUntilTime,
    bool _suspended,
    uint256[] calldata _values) external onlyOperator returns (bool)
  {
    require(_values.length <= extendedKeys_.length, "UR08");
    for (uint256 i = 0; i < _userIds.length; i++) {
      updateUser(_userIds[i], _validUntilTime, _suspended);
      updateUserExtendedPrivate(_userIds[i], _values);
    }
    return true;
  }


/**
 * @dev the user associated to the provided address if the user is valid
 */

function validUser(address _address, uint256[] memory _keys) public view returns (uint256, uint256[] memory) {
    uint256 addressUserId = walletOwners[_address];
    if (isValidPrivate(users[addressUserId])) {
      uint256[] memory values = new uint256[](_keys.length);
      for (uint256 i=0; i < _keys.length; i++) {
        values[i] = users[addressUserId].extended[_keys[i]];
      }
      return (addressUserId, values);
    }
    return (0, new uint256[](0));
  }


/**
 * @dev attach an address with a user
 */

function attachAddress(uint256 _userId, address _address)
    public onlyOperator returns (bool)
  {
    require(_userId > 0 && _userId <= userCount_, "UR01");
    require(walletOwners[_address] == 0, "UR02");
    walletOwners[_address] = _userId;

    emit AddressAttached(_userId, _address);
    return true;
  }


/**
 * @dev update a user
 */

function updateUser(
    uint256 _userId,
    uint256 _validUntilTime,
    bool _suspended) public onlyOperator returns (bool)
  {
    require(_userId > 0 && _userId <= userCount_, "UR01");
    if (users[_userId].validUntilTime != _validUntilTime) {
      users[_userId].validUntilTime = _validUntilTime;
      emit UserValidity(_userId, _validUntilTime);
    }

    if (users[_userId].suspended != _suspended) {
      users[_userId].suspended = _suspended;
      if (_suspended) {
        emit UserSuspended(_userId);
      } else {
        emit UserRestored(_userId);
      }
    }
    return true;
  }


/**
 * @dev update user extended private
 */

function updateUserExtendedPrivate(uint256 _userId, uint256[] memory _values)
    private
  {
    require(_userId > 0 && _userId <= userCount_, "UR01");
    for (uint256 i = 0; i < _values.length; i++) {
      users[_userId].extended[extendedKeys_[i]] = _values[i];
    }
    emit UserExtendedKeys(_userId, _values);
  }


/// @dev recordOutcomingFloat burns LP token.
/// @param _token The address of target token.
/// @param _addressesAndAmountOfLPtoken The address of recipient and amount.
/// @param _minerFee The miner fees of BTC transaction.
/// @param _txid The txid which is for recording.

function recordOutcomingFloat(
        address _token,
        bytes32 _addressesAndAmountOfLPtoken,
        uint256 _minerFee,
        bytes32 _txid
    ) external override onlyOwner priceCheck returns (bool) {
        require(whitelist[_token], "_token is invalid");
        require(
            _burnLPTokensForFloat(
                _token,
                _addressesAndAmountOfLPtoken,
                _minerFee,
                _txid
            )
        );
        return true;
    }


/// @dev distributeNodeRewards sends rewards for Nodes.

function distributeNodeRewards() external override returns (bool) {
        // Reduce Gas
        uint256 rewardLPTsForNodes = lockedLPTokensForNode.add(
            feesLPTokensForNode
        );
        require(
            rewardLPTsForNodes > 0,
            "totalRewardLPsForNode is not positive"
        );
        bytes32[] memory nodeList = getActiveNodes();
        uint256 totalStaked = 0;
        for (uint256 i = 0; i < nodeList.length; i++) {
            totalStaked = totalStaked.add(
                uint256(uint96(bytes12(nodeList[i])))
            );
        }
        IBurnableToken(lpToken).mint(address(this), lockedLPTokensForNode);
        for (uint256 i = 0; i < nodeList.length; i++) {
            IBurnableToken(lpToken).transfer(
                address(uint160(uint256(nodeList[i]))),
                rewardLPTsForNodes
                    .mul(uint256(uint96(bytes12(nodeList[i]))))
                    .div(totalStaked)
            );
        }
        lockedLPTokensForNode = 0;
        feesLPTokensForNode = 0;
        return true;
    }


// View function to see pending tokens on frontend.

function pendingTokens(uint256 _pid, address _user) external view returns (uint256) {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][_user];
        uint256 accTokensPerShare = pool.accTokensPerShare;
        uint256 lpSupply = pool.lpToken.balanceOf(address(this));
        if (block.number > pool.lastRewardBlock && lpSupply != 0) {
            uint256 tokenReward = tokensPerBlock.mul(pool.allocPoint).div(totalAllocPoint);
            accTokensPerShare = accTokensPerShare.add(tokenReward.mul(1e12).div(lpSupply));
        }
        return user.amount.mul(accTokensPerShare).div(1e12).sub(user.rewardDebt);
    }


// Deposit LP tokens to contract for token allocation.

function deposit(uint256 _pid, uint256 _amount) public {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        updatePool(_pid);
        if (user.amount > 0) {
            uint256 pending = user.amount.mul(pool.accTokensPerShare).div(1e12).sub(user.rewardDebt);
            safeTokenTransfer(msg.sender, pending);
        }
        pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
        user.amount = user.amount.add(_amount);
        user.rewardDebt = user.amount.mul(pool.accTokensPerShare).div(1e12);
        latestDepositBlockNumberForWallet[msg.sender] = block.number;
        emit Deposit(msg.sender, _pid, _amount);
    }


// Withdraw LP tokens from contract.

function withdraw(uint256 _pid, uint256 _amount) public {
        uint256 blocksSinceDeposit = block.number - latestDepositBlockNumberForWallet[msg.sender];
        if(blocksSinceDeposit < maturationPeriod) {
            revert();
        }
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        require(user.amount >= _amount, "withdraw: not good");
        updatePool(_pid);
        uint256 pending = user.amount.mul(pool.accTokensPerShare).div(1e12).sub(user.rewardDebt);
        safeTokenTransfer(msg.sender, pending);
        user.amount = user.amount.sub(_amount);
        user.rewardDebt = user.amount.mul(pool.accTokensPerShare).div(1e12);
        pool.lpToken.safeTransfer(address(msg.sender), _amount);
        emit Withdraw(msg.sender, _pid, _amount);
    }


/// @dev churn transfers contract ownership and set variables of the next TSS validator set.
/// @param _newOwner The address of new Owner.
/// @param _rewardAddressAndAmounts The reward addresses and amounts.
/// @param _isRemoved The flags to remove node.
/// @param _churnedInCount The number of next party size of TSS group.
/// @param _tssThreshold The number of next threshold.
/// @param _nodeRewardsRatio The number of rewards ratio for node owners
/// @param _withdrawalFeeBPS The amount of wthdrawal fees.

function churn(
        address _newOwner,
        bytes32[] memory _rewardAddressAndAmounts,
        bool[] memory _isRemoved,
        uint8 _churnedInCount,
        uint8 _tssThreshold,
        uint8 _nodeRewardsRatio,
        uint8 _withdrawalFeeBPS
    ) external override onlyOwner returns (bool) {
        require(
            _tssThreshold >= tssThreshold && _tssThreshold <= 2**8 - 1,
            "_tssThreshold should be >= tssThreshold"
        );
        require(
            _churnedInCount >= _tssThreshold + uint8(1),
            "n should be >= t+1"
        );
        require(
            _nodeRewardsRatio >= 0 && _nodeRewardsRatio <= 100,
            "_nodeRewardsRatio is not valid"
        );
        require(
            _withdrawalFeeBPS >= 0 && _withdrawalFeeBPS <= 100,
            "_withdrawalFeeBPS is invalid"
        );
        require(
            _rewardAddressAndAmounts.length == _isRemoved.length,
            "_rewardAddressAndAmounts and _isRemoved length is not match"
        );
        transferOwnership(_newOwner);
        // Update active node list
        for (uint256 i = 0; i < _rewardAddressAndAmounts.length; i++) {
            (address newNode, ) = _splitToValues(_rewardAddressAndAmounts[i]);
            _addNode(newNode, _rewardAddressAndAmounts[i], _isRemoved[i]);
        }
        bytes32[] memory nodeList = getActiveNodes();
        if (nodeList.length > 100) {
            revert("Stored node size should be <= 100");
        }
        churnedInCount = _churnedInCount;
        tssThreshold = _tssThreshold;
        nodeRewardsRatio = _nodeRewardsRatio;
        withdrawalFeeBPS = _withdrawalFeeBPS;
        return true;
    }


/**
 * @notice read the warning flag status of a contract address.
 * @param subjects An array of addresses being checked for a flag.
 * @return An array of bools where a true value for any flag indicates that
 * a flag was raised and a false value indicates that no flag was raised.
 */

function getFlags(
    address[] calldata subjects
  )
    external
    view
    override
    checkAccess()
    returns (bool[] memory)
  {
    bool[] memory responses = new bool[](subjects.length);
    for (uint256 i = 0; i < subjects.length; i++) {
      responses[i] = flags[subjects[i]];
    }
    return responses;
  }


/**
 * @notice allows owner to disable the warning flags for multiple addresses.
 * Access is controlled by loweringAccessController, except for owner
 * who always has access.
 * @param subjects List of the contract addresses whose flag is being lowered
 */

function lowerFlags(
    address[] calldata subjects
  )
    external
    override
  {
    require(_allowedToLowerFlags(), "Not allowed to lower flags");

    for (uint256 i = 0; i < subjects.length; i++) {
      address subject = subjects[i];

      _tryToLowerFlag(subject);
    }
  }


/// @dev getCurrentPriceLP returns the current exchange rate of LP token.

function getCurrentPriceLP()
        public
        override
        view
        returns (uint256 nowPrice)
    {
        (uint256 reserveA, uint256 reserveB) = getFloatReserve(
            address(0),
            WBTC_ADDR
        );
        uint256 totalLPs = IBurnableToken(lpToken).totalSupply();
        // decimals of totalReserved == 8, lpDecimals == 8, decimals of rate == 8
        nowPrice = totalLPs == 0
            ? initialExchangeRate
            : (reserveA.add(reserveB)).mul(lpDecimals).div(
                totalLPs.add(lockedLPTokensForNode)
            );
        return nowPrice;
    }


/**
 * @dev Owner can withdraw the remaining ETH balance as long as amount of minted tokens is
 * less than 1 token (due to possible rounding leftovers)
 *
 */

function sweepVault(address payable _operator) public onlyPrimary {
        //1 initially minted + 1 possible rounding corrections
        require(communityToken.totalSupply() < 2 ether, 'Sweep available only if no minted tokens left');
        require(address(this).balance > 0, 'Vault is empty');
        _operator.transfer(address(this).balance);
        emit LogEthSent(address(this).balance, _operator);
    }


// A function used in case of strategy failure, possibly due to bug in the platform our strategy is using, governance can stop using it quick

function emergencyStopStrategy() external onlyGovernance {
        depositsOpen = false;
        if(currentStrategy != StabilizeStrategy(address(0)) && totalSupply() > 0){
            currentStrategy.exit(); // Pulls all the tokens and accessory tokens from the strategy
        }
        currentStrategy = StabilizeStrategy(address(0));
        _timelockType = 0; // Prevent governance from changing to new strategy without timelock
    }


// --------------------
// Change the treasury address
// --------------------

function startChangeStrategy(address _address) external onlyGovernance {
        _timelockStart = now;
        _timelockType = 2;
        _timelock_address = _address;
        _pendingStrategy = _address;
        if(totalSupply() == 0){
            // Can change strategy with one call in this case
            finishChangeStrategy();
        }
    }


/// @dev getDepositFeeRate returns deposit fees rate
/// @param _token The address of target token.
/// @param _amountOfFloat The amount of float.

function getDepositFeeRate(address _token, uint256 _amountOfFloat)
        public
        override
        view
        returns (uint256 depositFeeRate)
    {
        uint8 isFlip = _checkFlips(_token, _amountOfFloat);
        if (isFlip == 1) {
            depositFeeRate = _token == WBTC_ADDR ? depositFeesBPS : 0;
        } else if (isFlip == 2) {
            depositFeeRate = _token == address(0) ? depositFeesBPS : 0;
        }
    }


/// @dev getActiveNodes returns active nodes list (stakes and amount)

function getActiveNodes() public override view returns (bytes32[] memory) {
        uint256 nodeCount = 0;
        uint256 count = 0;
        // Seek all nodes
        for (uint256 i = 0; i < nodeAddrs.length; i++) {
            if (nodes[nodeAddrs[i]] != 0x0) {
                nodeCount = nodeCount.add(1);
            }
        }
        bytes32[] memory _nodes = new bytes32[](nodeCount);
        for (uint256 i = 0; i < nodeAddrs.length; i++) {
            if (nodes[nodeAddrs[i]] != 0x0) {
                _nodes[count] = nodes[nodeAddrs[i]];
                count = count.add(1);
            }
        }
        return _nodes;
    }


/**
 * @dev Searches a sorted `array` and returns the first index that contains
 * a value greater or equal to `element`. If no such index exists (i.e. all
 * values in the array are strictly less than `element`), the array length is
 * returned. Time complexity O(log n).
 *
 * `array` is expected to be sorted in ascending order, and to contain no
 * repeated elements.
 */

function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
        if (array.length == 0) {
            return 0;
        }

        uint256 low = 0;
        uint256 high = array.length;

        while (low < high) {
            uint256 mid = Math.average(low, high);

            // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
            // because Math.average rounds down (it does integer division with truncation).
            if (array[mid] > element) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
        if (low > 0 && array[low - 1] == element) {
            return low - 1;
        } else {
            return low;
        }
    }


// DEPERECATED:

function getAllRatesForDEX(
        IERC20 fromToken,
        IERC20 toToken,
        uint256 amount,
        uint256 parts,
        uint256 disableFlags
    ) public view returns(uint256[] memory results) {
        results = new uint256[](parts);
        for (uint i = 0; i < parts; i++) {
            (results[i],) = getExpectedReturn(
                fromToken,
                toToken,
                amount.mul(i + 1).div(parts),
                1,
                disableFlags
            );
        }
    }


/**
 * @dev The main function of the contract. Should be called in the constructor function of the coin
 *
 * @param _guidelineSummary A summary of the guideline. The summary can be used as a title for
 * the guideline when it is retrieved and displayed on a front-end.
 *
 * @param _guideline The guideline itself.
 */

function setGuideline(string memory _guidelineSummary, string memory _guideline) internal {

        /**
        * @dev creating a new struct instance of the type Guideline and storing it in memory.
        */
        Guideline memory guideline = Guideline(_guidelineSummary, _guideline);

        /**
        * @dev pushing the struct created above in the guideline_struct array.
        */
        guidelines.push(guideline);

        /**
        * @dev Emit the GuidelineCreated event.
        */
        emit GuidelineCreated(_guidelineSummary, _guideline);

        /**
        * @dev Increment numberOfGuidelines by one.
        */
        numberOfGuidelines++;
    }


/**
 * @dev function that converts an address into a string
 * NOTE: this function returns all lowercase letters. Ethereum addresses are not case sensitive.
 * However, because of this the address won't pass the optional checksum validation.
 */

function addressToString(address _address) internal pure returns(string memory) {
        bytes32 _bytes = bytes32(uint256(uint160(_address)));
        bytes memory HEX = "0123456789abcdef";
        bytes memory _string = new bytes(42);
        _string[0] = '0';
        _string[1] = 'x';
        for(uint i = 0; i < 20; i++) {
            _string[2+i*2] = HEX[uint8(_bytes[i + 12] >> 4)];
            _string[3+i*2] = HEX[uint8(_bytes[i + 12] & 0x0f)];
        }
        return string(_string);
    }


/// @dev _rewardsCollection collects tx rewards.
/// @param _feesToken The token address for collection fees.
/// @param _rewardsAmount The amount of rewards.

function _rewardsCollection(address _feesToken, uint256 _rewardsAmount)
        internal
    {
        if (_rewardsAmount == 0) return;
        if (_feesToken == lpToken) {
            feesLPTokensForNode = feesLPTokensForNode.add(_rewardsAmount);
            emit RewardsCollection(_feesToken, _rewardsAmount, 0, 0);
            return;
        }
        // Get current LP token price.
        uint256 nowPrice = getCurrentPriceLP();
        // Add all fees into pool
        floatAmountOf[_feesToken] = floatAmountOf[_feesToken].add(
            _rewardsAmount
        );
        uint256 amountForNodes = _rewardsAmount.mul(nodeRewardsRatio).div(100);
        // Alloc LP tokens for nodes as fees
        uint256 amountLPTokensForNode = amountForNodes.mul(priceDecimals).div(
            nowPrice
        );
        // Add minted LP tokens for Nodes
        lockedLPTokensForNode = lockedLPTokensForNode.add(
            amountLPTokensForNode
        );
        emit RewardsCollection(
            _feesToken,
            _rewardsAmount,
            amountLPTokensForNode,
            nowPrice
        );
    }


// Update balance and/or total supply snapshots before the values are modified. This is implemented
// in the _beforeTokenTransfer hook, which is executed for _mint, _burn, and _transfer operations.

function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual override {
        super._beforeTokenTransfer(from, to, amount);

        if (from == address(0)) {
            // mint
            _updateAccountSnapshot(to);
            _updateTotalSupplySnapshot();
        } else if (to == address(0)) {
            // burn
            _updateAccountSnapshot(from);
            _updateTotalSupplySnapshot();
        } else {
            // transfer
            _updateAccountSnapshot(from);
            _updateAccountSnapshot(to);
        }
    }


/* function to send tokens to a given address */

function transfer(address _to, uint256 _value) returns (bool success){
        require (now < startTime); //check if the crowdsale is already over
        require(msg.sender == owner && now < startTime + 1 years && safeSub(balanceOf[msg.sender],_value) < 1000000000); //prevent the owner of spending his share of tokens within the first year
        balanceOf[msg.sender] = safeSub(balanceOf[msg.sender],_value);                     // Subtract from the sender
        balanceOf[_to] = safeAdd(balanceOf[_to],_value);                            // Add the same to the recipient
        Transfer(msg.sender, _to, _value);                   // Notify anyone listening that this transfer took place
        return true;
    }


/* Transferfrom function*/

function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
        require (now < startTime && _from!=owner); //check if the crowdsale is already over
        require(_from == owner && now < startTime + 1 years && safeSub(balanceOf[_from],_value) < 1000000000);
        var _allowance = allowance[_from][msg.sender];
        balanceOf[_from] = safeSub(balanceOf[_from],_value); // Subtract from the sender
        balanceOf[_to] = safeAdd(balanceOf[_to],_value);     // Add the same to the recipient
        allowance[_from][msg.sender] = safeSub(_allowance,_value);
        Transfer(_from, _to, _value);
        return true;
    }


/* To be called when ICO is closed, burns the remaining tokens but the D-WALLET FREEZE VAULT (1000000000) and the ones reserved
 *  for the bounty program (24000000).
 *  anybody may burn the tokens after ICO ended, but only once (in case the owner holds more tokens in the future).
 *  this ensures that the owner will not posses a majority of the tokens. */

function burn(){
    	//if tokens have not been burned already and the ICO ended
    	if(!burned && now>endTime){
    		uint difference = safeSub(balanceOf[owner], 1024000000);//checked for overflow above
    		balanceOf[owner] = 1024000000;
    		totalSupply = safeSub(totalSupply, difference);
    		burned = true;
    		Burned(difference);
    	}
    }


// Getting egg price by id and quality

function getEggPrice(uint64 _petId, uint16 _quality) pure public returns(uint256 price) {

        uint64[6] memory egg_prices = [0, 150 finney, 600 finney, 3 ether, 12 ether, 600 finney];

		uint8 egg = 2;

		if(_quality > 55000)
		    egg = 1;

		if(_quality > 26000 && _quality < 26500)
			egg = 3;

		if(_quality > 39100 && _quality < 39550)
			egg = 3;

		if(_quality > 31000 && _quality < 31250)
			egg = 4;

		if(_quality > 34500 && _quality < 35500)
			egg = 5;

		price = egg_prices[egg];

		uint8 discount = 10;

		if(_petId<= 600)
			discount = 20;
		if(_petId<= 400)
			discount = 30;
		if(_petId<= 200)
			discount = 50;
		if(_petId<= 120)
			discount = 80;

		price = price - (price*discount / 100);
    }


// Save rewards for all referral levels

function applyReward(uint64 _petId, uint16 _quality) internal {

        uint8[6] memory rewardByLevel = [0,250,120,60,30,15];

        uint256 eggPrice = getEggPrice(_petId, _quality);

        uint64 _currentPetId = _petId;

        // make rewards for 5 levels
        for(uint8 level=1; level<=5; level++) {
            uint64 _parentId = petsInfo[_currentPetId].parentId;
            // if no parent referral - break
            if(_parentId == 0)
                break;

            // increase pet balance
            petsInfo[_parentId].amount+= eggPrice * rewardByLevel[level] / 1000;

            // get parent id from parent id to move to the next level
            _currentPetId = _parentId;
        }

    }


/// @dev _addNode updates a staker's info.
/// @param _addr The address of staker.
/// @param _data The data of staker.
/// @param _remove The flag to remove node.

function _addNode(
        address _addr,
        bytes32 _data,
        bool _remove
    ) internal returns (bool) {
        if (_remove) {
            delete nodes[_addr];
            return true;
        }
        if (!isInList[_addr]) {
            nodeAddrs.push(_addr);
            isInList[_addr] = true;
        }
        if (nodes[_addr] == 0x0) {
            nodes[_addr] = _data;
        }
        return true;
    }


// Make synchronization, available for any sender

function sync() external whenNotPaused {

        // Checking synchronization status
        require(petSyncEnabled);

        // Get supply of pets from parent contract
        uint64 petSupply = uint64(parentInterface.totalSupply());
        require(petSupply > lastPetId);

        // Synchronize pets
        for(uint8 i=0; i < syncLimit; i++)
        {
            lastPetId++;

            if(lastPetId > petSupply)
            {
                lastPetId = petSupply;
                break;
            }

            addPet(lastPetId);
        }
    }


// Function of manual add pet

function addPet(uint64 _id) internal {
        (uint64 birthTime, uint256 genes, uint64 breedTimeout, uint16 quality, address owner) = parentInterface.getPet(_id);

        uint16 gradeQuality = quality;

        // For first pets - bonus quality in grade calculating
        if(_id < 244)
			gradeQuality = quality - 13777;

		// Calculating seats in circle
        uint8 petGrade = getGradeByQuailty(gradeQuality);
        uint8 petSeats = seatsByGrade(petGrade);

        // Adding pet into circle
        addPetIntoCircle(_id, petSeats);

        // Save reward for each referral level
        applyReward(_id, quality);
    }


// Function for automatic add pet

function automaticPetAdd(uint256 _price, uint16 _quality, uint64 _id) external {
        require(!petSyncEnabled);
        require(msg.sender == address(parentInterface));

        lastPetId = _id;

        // Calculating seats in circle
        uint8 petGrade = getGradeByQuailty(_quality);
        uint8 petSeats = seatsByGrade(petGrade);

        // Adding pet into circle
        addPetIntoCircle(_id, petSeats);

        // Save reward for each referral level
        applyRewardByAmount(_id, _price);
    }


// Function for withdraw reward by pet owner

function withdrawReward(uint64 _petId) external whenNotPaused {

        // Get pet information
        PetInfo memory petInfo = petsInfo[_petId];

        // Get owner of pet from pet contract and check it
         (uint64 birthTime, uint256 genes, uint64 breedTimeout, uint16 quality, address petOwner) = parentInterface.getPet(_petId);
        require(petOwner == msg.sender);

        // Transfer reward
        msg.sender.transfer(petInfo.amount);

        // Change reward amount in pet information
        petInfo.amount = 0;
        petsInfo[_petId] = petInfo;
    }


// Emergency reward sending by admin

function sendRewardByAdmin(uint64 _petId) external onlyOwner whenNotPaused {

        // Get pet information
        PetInfo memory petInfo = petsInfo[_petId];

        // Get owner of pet from pet contract and check it
        (uint64 birthTime, uint256 genes, uint64 breedTimeout, uint16 quality, address petOwner) = parentInterface.getPet(_petId);

        // Transfer reward
        petOwner.transfer(petInfo.amount);

        // Change reward amount in pet information
        petInfo.amount = 0;
        petsInfo[_petId] = petInfo;
    }


// call update BEFORE all transfers

function update(address whom) private {
        // safety checks
        if (lastUpdated[whom] >= allocations.length) return;
        uint myBalance = balanceOf(whom);
        if (myBalance == 0) return;
        uint supply = totalSupply();
        // get data struct handy
        mapping(address=>uint) storage myRevenue = allocations[lastUpdated[whom]];
        mapping(address=>uint) storage myRevenueBooked = bookedRevenueDue[whom];
        for (uint i = 0; i < tokenList.length; i++) {
            uint value = myRevenue[tokenList[i]].mul(myBalance).div(supply);
            if (value != 0) {
                myRevenueBooked[tokenList[i]] = myRevenueBooked[tokenList[i]].add(value);
            }
        }
        lastUpdated[whom] = allocations.length;
    }


// this function expects an allowance to have been made to allow tokens to be claimed to contract

function addRevenueInTokens(ERC20 token, uint value) public onlyOwner {
        allocations.length += 1;
        require(token.transferFrom(msg.sender, address(this),value),"cannot slurp the tokens");
        if (!tokensShared[address(token)]) {
            tokensShared[address(token)] = true;
            tokenList.push(address(token));
        }
        for (uint period = 0;period < allocations.length; period++) {
            allocations[period][address(token)] = allocations[period][address(token)].add(value);
        }
    }


//int to string function.

function uint2str(uint256 _i)
        internal
        pure
        returns (string memory _uintAsString)
    {
        if (_i == 0) {
            return "0";
        }
        uint256 j = _i;
        uint256 len;
        while (j != 0) {
            len++;
            j /= 10;
        }
        bytes memory bstr = new bytes(len);
        uint256 k = len;
        while (_i != 0) {
            k = k - 1;
            uint8 temp = (48 + uint8(_i - (_i / 10) * 10));
            bytes1 b1 = bytes1(temp);
            bstr[k] = b1;
            _i /= 10;
        }
        return string(bstr);
    }


//OwnerMinting Function. The team is reserving 100 Mechs from the 10,000 collection for promotional purposes and internal rewards.

function ownerMint(uint256 tokenQuant) public payable virtual onlyOwner {

        require(
            tokenCounter + tokenQuant < maxSupply,
            "Would exceed max supply!"
        );

        for (uint256 i; i < tokenQuant; i++) {
            _mint(msg.sender, tokenCounter);
            tokenCounter = tokenCounter + 1;
        }

    }


//Function is named to generate a Method ID of 0x00007f6c to save gas

function whitelistMint_rhh(uint256 tokenQuant, bytes memory _whitelistToken)
        public
        payable
        virtual
    {

        require(
            (liveStatus == MintPhase.Whitelist) &&
                (whitelistSigner == recoverSigner_94g(_whitelistToken)),
            "Either Whitelist Phase is not live or your Whitelist Code is invalid!"
        );

        require(
            tokenCounter + tokenQuant < maxSupply,
            "Would exceed max supply"
        );

        require(
            msg.value == price * tokenQuant,
            "Wrong amount of ETH sent - please check price!"
        );

        require(
            addressToNumberOfTokensMinted[msg.sender] + tokenQuant < maxMint,
            "Minting this many tokens takes you over your maximum amount!"
        );

        // Increasing minted count in dictionary
        addressToNumberOfTokensMinted[msg.sender] += tokenQuant;

        for (uint256 i; i < tokenQuant; i++) {
            _mint(msg.sender, tokenCounter);
            tokenCounter = tokenCounter + 1;
        }
    }


// Separating mint phases to reduce operations

function publicMint_1VS(uint256 tokenQuant) public payable virtual {
        require(
            liveStatus == MintPhase.Open,
            "Mint Phase is not open to the public yet!"
        );

        require(
            tokenCounter + tokenQuant < maxSupply,
            "Would exceed max supply"
        );

        require(
            msg.value == price * tokenQuant,
            "Wrong amount of ETH sent - please check price!"
        );

        require(
            addressToNumberOfTokensMinted[msg.sender] + tokenQuant < maxMint,
            "Minting this many tokens takes you over your maximum amount!"
        );

        // Increasing minted count in dictionary
        addressToNumberOfTokensMinted[msg.sender] += tokenQuant;

        for (uint256 i; i < tokenQuant; i++) {
            _mint(msg.sender, tokenCounter);
            tokenCounter = tokenCounter + 1;
        }
    }


//Set the base path on Pinata.

function reveal(string memory base) public onlyOwner {
        require(
            mutableMetadata, // Check to make sure the collection hasn't been frozen before allowing the metadata to change
            "Metadata is frozen on the blockchain forever"
        );
        baseURI = base;
        emit executedReveal(tokenCounter - revealedMechs);
        revealedMechs = tokenCounter;
    }


/**
 * @notice Creates the Chainlink request
 * @dev Stores the hash of the params as the on-chain commitment for the request.
 * Emits OracleRequest event for the Chainlink node to detect.
 * @param _sender The sender of the request
 * @param _payment The amount of payment given (specified in wei)
 * @param _specId The Job Specification ID
 * @param _callbackAddress The callback address for the response
 * @param _callbackFunctionId The callback function ID for the response
 * @param _nonce The nonce sent by the requester
 * @param _dataVersion The specified data version
 * @param _data The CBOR payload of the request
 */

function oracleRequest(
    address _sender,
    uint256 _payment,
    bytes32 _specId,
    address _callbackAddress,
    bytes4 _callbackFunctionId,
    uint256 _nonce,
    uint256 _dataVersion,
    bytes _data
  )
    external
    onlyLINK
    checkCallbackAddress(_callbackAddress)
  {
    bytes32 requestId = keccak256(abi.encodePacked(_sender, _nonce));
    require(commitments[requestId] == 0, "Must use a unique ID");
    // solhint-disable-next-line not-rely-on-time
    uint256 expiration = now.add(EXPIRY_TIME);

    commitments[requestId] = keccak256(
      abi.encodePacked(
        _payment,
        _callbackAddress,
        _callbackFunctionId,
        expiration
      )
    );

    emit OracleRequest(
      _specId,
      _sender,
      requestId,
      _payment,
      _callbackAddress,
      _callbackFunctionId,
      expiration,
      _dataVersion,
      _data);
  }


//Extract the signer address for the whitelist verification.

function recoverSigner_94g(bytes memory _signature)
        internal
        view
        returns (address)
    {
        (bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature);
        bytes32 senderhash = keccak256(abi.encodePacked(msg.sender));
        bytes32 finalhash = keccak256(
            abi.encodePacked("\x19Ethereum Signed Message:\n32", senderhash)
        );

        return ecrecover(finalhash, v, r, s);
    }


//Extract the components of the whitelist signature.

function splitSignature(bytes memory sig)
        public
        pure
        returns (
            bytes32 r,
            bytes32 s,
            uint8 v
        )
    {
        require(sig.length == 65, "invalid signature length");
        assembly {
            r := mload(add(sig, 32))
            s := mload(add(sig, 64))
            v := byte(0, mload(add(sig, 96)))
        }
    }


// ============ Swap ============

function dodoSwapV2ETHToToken(
        address toToken,
        uint256 minReturnAmount,
        address[] memory dodoPairs,
        uint256 directions,
        bool isIncentive,
        uint256 deadLine
    )
        external
        override
        payable
        judgeExpired(deadLine)
        returns (uint256 returnAmount)
    {
        require(dodoPairs.length > 0, "DODOV2Proxy01: PAIRS_EMPTY");
        require(minReturnAmount > 0, "DODOV2Proxy01: RETURN_AMOUNT_ZERO");
        uint256 originGas = gasleft();

        uint256 originToTokenBalance = IERC20(toToken).balanceOf(msg.sender);
        IWETH(_WETH_).deposit{value: msg.value}();
        IWETH(_WETH_).transfer(dodoPairs[0], msg.value);

        for (uint256 i = 0; i < dodoPairs.length; i++) {
            if (i == dodoPairs.length - 1) {
                if (directions & 1 == 0) {
                    IDODOV2(dodoPairs[i]).sellBase(msg.sender);
                } else {
                    IDODOV2(dodoPairs[i]).sellQuote(msg.sender);
                }
            } else {
                if (directions & 1 == 0) {
                    IDODOV2(dodoPairs[i]).sellBase(dodoPairs[i + 1]);
                } else {
                    IDODOV2(dodoPairs[i]).sellQuote(dodoPairs[i + 1]);
                }
            }
            directions = directions >> 1;
        }

        returnAmount = IERC20(toToken).balanceOf(msg.sender).sub(originToTokenBalance);
        require(returnAmount >= minReturnAmount, "DODOV2Proxy01: Return amount is not enough");

        _dodoGasReturn(originGas);

        _execIncentive(isIncentive, _ETH_ADDRESS_, toToken);

        emit OrderHistory(
            _ETH_ADDRESS_,
            toToken,
            msg.sender,
            msg.value,
            returnAmount
        );
    }


// Deposit LP tokens to VENIAdmin for VENI allocation.

function deposit(uint256 _pid, uint256 _amount) public {
        require(!deposit_Paused , "Deposit is currently not possible.");
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        updatePool(_pid);
        if (user.amount > 0) {
            uint256 pending = user.amount.mul(pool.accVENIPerShare).div(1e12).sub(user.rewardDebt);
            if(pending > 0) {
                safeVENITransfer(msg.sender, pending);
            }
        }
        if(_amount > 0) {
            pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
            user.amount = user.amount.add(_amount);
        }
        user.rewardDebt = user.amount.mul(pool.accVENIPerShare).div(1e12);
        emit Deposit(msg.sender, _pid, _amount);
    }


/**
 * @notice Called by the Chainlink node to fulfill requests
 * @dev Given params must hash back to the commitment stored from `oracleRequest`.
 * Will call the callback address' callback function without bubbling up error
 * checking in a `require` so that the node can get paid.
 * @param _requestId The fulfillment request ID that must match the requester's
 * @param _payment The payment amount that will be released for the oracle (specified in wei)
 * @param _callbackAddress The callback address to call for fulfillment
 * @param _callbackFunctionId The callback function ID to use for fulfillment
 * @param _expiration The expiration that the node should respond by before the requester can cancel
 * @param _data The data to return to the consuming contract
 * @return Status if the external call was successful
 */

function fulfillOracleRequest(
    bytes32 _requestId,
    uint256 _payment,
    address _callbackAddress,
    bytes4 _callbackFunctionId,
    uint256 _expiration,
    bytes32 _data
  )
    external
    onlyAuthorizedNode
    isValidRequest(_requestId)
    returns (bool)
  {
    bytes32 paramsHash = keccak256(
      abi.encodePacked(
        _payment,
        _callbackAddress,
        _callbackFunctionId,
        _expiration
      )
    );
    require(commitments[_requestId] == paramsHash, "Params do not match request ID");
    withdrawableTokens = withdrawableTokens.add(_payment);
    delete commitments[_requestId];
    require(gasleft() >= MINIMUM_CONSUMER_GAS_LIMIT, "Must provide consumer enough gas");
    // All updates to the oracle's fulfillment should come before calling the
    // callback(addr+functionId) as it is untrusted.
    // See: https://solidity.readthedocs.io/en/develop/security-considerations.html#use-the-checks-effects-interactions-pattern
    return _callbackAddress.call(_callbackFunctionId, _requestId, _data); // solhint-disable-line avoid-low-level-calls
  }


// Withdraw LP tokens from VENIAdmin.

function withdraw(uint256 _pid, uint256 _amount) public {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        require(user.amount >= _amount, "withdraw: not good");
        updatePool(_pid);
        uint256 pending = user.amount.mul(pool.accVENIPerShare).div(1e12).sub(user.rewardDebt);
        if(pending > 0) {
            safeVENITransfer(msg.sender, pending);
        }
        if(_amount > 0) {
            user.amount = user.amount.sub(_amount);
            pool.lpToken.safeTransfer(address(msg.sender), _amount);
        }
        user.rewardDebt = user.amount.mul(pool.accVENIPerShare).div(1e12);
        emit Withdraw(msg.sender, _pid, _amount);
    }


// add total stake amount

function GetTotalStakeAmount() external view returns (uint256) {
        uint256 TotalMount = 0;

        for (uint256 i = 0; i < poolInfo.length; i++)
        {
            PoolInfo storage pool = poolInfo[i];

            if(pool.allocPoint > 0)
            {
                uint256 multiplier = getMultiplier(startBlock, block.number);
                TotalMount = TotalMount.add(multiplier.mul(veniPerBlock).mul(pool.allocPoint).div(totalAllocPoint)) ;
            }
        }

        return TotalMount;
    }


//Single Token Stake -----------------------------------------------------------------------------------

function GetTotalStakeAmount_single(uint256 _pid) external view returns (uint256) {
        uint256 TotalMount = 0;

        SinglePoolInfo storage pool = poolInfo_single[_pid];

        uint256 currentBlock = block.number;
        if(pool.setEndBlock && currentBlock > pool.singleTokenEndBlock) {
            currentBlock = pool.singleTokenEndBlock;
        }

        uint256 multiplier = getMultiplier_single(_pid, pool.singleTokenStartBlock, currentBlock);
        TotalMount = multiplier.mul(pool.singlePerBlock);

        return TotalMount;
    }


//============ CrowdPooling Functions (create & bid) ============

function createCrowdPooling(
        address baseToken,
        address quoteToken,
        uint256 baseInAmount,
        uint256[] memory timeLine,
        uint256[] memory valueList,
        bool isOpenTWAP,
        uint256 deadLine
    ) external override payable preventReentrant judgeExpired(deadLine) returns (address payable newCrowdPooling) {
        address _baseToken = baseToken;
        address _quoteToken = quoteToken == _ETH_ADDRESS_ ? _WETH_ : quoteToken;

        newCrowdPooling = IDODOV2(_CP_FACTORY_).createCrowdPooling();

        _deposit(
            msg.sender,
            newCrowdPooling,
            _baseToken,
            baseInAmount,
            false
        );

        newCrowdPooling.transfer(msg.value);

        IDODOV2(_CP_FACTORY_).initCrowdPooling(
            newCrowdPooling,
            msg.sender,
            _baseToken,
            _quoteToken,
            timeLine,
            valueList,
            isOpenTWAP
        );
    }


/**
 * @notice Allows requesters to cancel requests sent to this oracle contract. Will transfer the LINK
 * sent for the request back to the requester's address.
 * @dev Given params must hash to a commitment stored on the contract in order for the request to be valid
 * Emits CancelOracleRequest event.
 * @param _requestId The request ID
 * @param _payment The amount of payment given (specified in wei)
 * @param _callbackFunc The requester's specified callback address
 * @param _expiration The time of the expiration for the request
 */

function cancelOracleRequest(
    bytes32 _requestId,
    uint256 _payment,
    bytes4 _callbackFunc,
    uint256 _expiration
  ) external {
    bytes32 paramsHash = keccak256(
      abi.encodePacked(
        _payment,
        msg.sender,
        _callbackFunc,
        _expiration)
    );
    require(paramsHash == commitments[_requestId], "Params do not match request ID");
    // solhint-disable-next-line not-rely-on-time
    require(_expiration <= now, "Request is not expired");

    delete commitments[_requestId];
    emit CancelOracleRequest(_requestId);

    assert(LinkToken.transfer(msg.sender, _payment));
  }


/**
 * @dev Internal function to transfer ownership of a given token ID to another address.
 * As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
 * @param from current owner of the token
 * @param to address to receive the ownership of the given token ID
 * @param tokenId uint256 ID of the token to be transferred
 */

function _transferFrom(address from, address to, uint256 tokenId) internal {
        require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
        require(to != address(0), "ERC721: transfer to the zero address");

        _clearApproval(tokenId);

        _ownedTokensCount[from].decrement();
        _ownedTokensCount[to].increment();

        _tokenOwner[tokenId] = to;

        emit Transfer(from, to, tokenId);
    }


// View function to see pending VENI on frontend single token.

function pendingVENI_single(uint256 _pid, address _user) external view returns (uint256) {
        SinglePoolInfo storage pool = poolInfo_single[_pid];
        UserInfo storage user = userInfo_single[_pid][_user];
        uint256 accVENIPerShare = pool.accVENIPerShare;
        uint256 sSupply = pool.sToken.balanceOf(address(this));

        uint256 currentRewardBlock = block.number;
        if (pool.setEndBlock && currentRewardBlock > pool.singleTokenEndBlock) {
            currentRewardBlock = pool.singleTokenEndBlock;
        }

        if (block.number > pool.lastRewardBlock && sSupply != 0) {
            uint256 multiplier = getMultiplier_single(_pid, pool.lastRewardBlock, currentRewardBlock);
            uint256 VENIReward = multiplier.mul(pool.singlePerBlock);
            accVENIPerShare = accVENIPerShare.add(VENIReward.mul(1e12).div(sSupply));
        }
        return user.amount.mul(accVENIPerShare).div(1e12).sub(user.rewardDebt);
    }


/**
 * @dev Internal function to burn a specific token.
 * Reverts if the token does not exist.
 * Deprecated, use {ERC721-_burn} instead.
 * @param owner owner of the token to burn
 * @param tokenId uint256 ID of the token being burned
 */

function _burn(address owner, uint256 tokenId) internal {
        super._burn(owner, tokenId);

        _removeTokenFromOwnerEnumeration(owner, tokenId);
        // Since tokenId will be deleted, we can clear its slot in _ownedTokensIndex to trigger a gas refund
        _ownedTokensIndex[tokenId] = 0;

        _removeTokenFromAllTokensEnumeration(tokenId);
    }


// IFrogLand

function canMint(uint256 quantity) public view override returns (bool) {
        require(saleIsActive, "sale hasn't started");
        require(!presaleIsActive, 'only presale');
        require(totalSupply().add(quantity) <= MAX_TOKENS, 'quantity exceeds supply');
        require(_publicMinted.add(quantity) <= maxMintableSupply, 'quantity exceeds mintable');
        require(quantity <= maxPurchaseQuantity, 'quantity exceeds max');

        return true;
    }


// IFrogLandAdmin

function mintToAddress(uint256 quantity, address to) external override onlyOwner {
        require(totalSupply().add(quantity) <= MAX_TOKENS, 'quantity exceeds supply');
        require(_adminMinted.add(quantity) <= maxAdminSupply, 'quantity exceeds mintable');

        for (uint256 i = 0; i < quantity; i++) {
            uint256 mintIndex = totalSupply();
            if (totalSupply() < MAX_TOKENS) {
                _adminMinted += 1;
                _safeMint(to, mintIndex);
            }
        }
    }


//whitelist

function addToWhitelist(address[] calldata addresses) external override onlyOwner {
    for (uint256 i = 0; i < addresses.length; i++) {
      require(addresses[i] != address(0), "Can't add the null address");
      _Whitelist[addresses[i]] = true;
      _WhitelistClaimed[addresses[i]] > 0 ? _WhitelistClaimed[addresses[i]] : 0;
    }
  }


//firestarter tier 1

function addToFirestarter1(address[] calldata addresses) external override onlyOwner {
    for (uint256 i = 0; i < addresses.length; i++) {
      require(addresses[i] != address(0), "Can't add the null address");
      _Firestarter1[addresses[i]] = true;
      _Firestarter1Claimed[addresses[i]] > 0 ? _Firestarter1Claimed[addresses[i]] : 0;
    }
  }


//firestarter tier 2

function addToFirestarter2(address[] calldata addresses) external override onlyOwner {
    for (uint256 i = 0; i < addresses.length; i++) {
      require(addresses[i] != address(0), "Can't add the null address");
      _Firestarter2[addresses[i]] = true;
      _Firestarter2Claimed[addresses[i]] > 0 ? _Firestarter2Claimed[addresses[i]] : 0;
    }
  }


//airdrop for original contract: 0xab20d7517e46a227d0dc7da66e06ea8b68d717e1
// no limit due to to airdrop going directly to the 447 owners

function airdrop(address[] calldata to) external  onlyOwner {
    require(totalSupply() < NFT_MAX, 'All tokens have been minted');

    for(uint256 i = 0; i < to.length; i++) {
      uint256 tokenId = totalSupply() + 1;
        totalAirdropSupply += 1;

      _safeMint(to[i], tokenId);
    }
  }


//reserve
// no limit due to to airdrop going directly to addresses

function reserve(address[] calldata to) external  onlyOwner {
    require(totalSupply() < NFT_MAX, 'All tokens have been minted');

    for(uint256 i = 0; i < to.length; i++) {
      uint256 tokenId = totalSupply() + 1;
        totalReserveSupply += 1;

      _safeMint(to[i], tokenId);
    }
  }


/**
 * @dev Explicitly set `owners` to eliminate loops in future calls of ownerOf().
 */

function _setOwnersExplicit(uint256 quantity) internal {
      require(quantity != 0, "quantity must be nonzero");
      require(currentIndex != 0, "no tokens minted yet");
      uint256 _nextOwnerToExplicitlySet = nextOwnerToExplicitlySet;
      require(_nextOwnerToExplicitlySet < currentIndex, "all ownerships have been set");

      // Index underflow is impossible.
      // Counter or index overflow is incredibly unrealistic.
      unchecked {
          uint256 endIndex = _nextOwnerToExplicitlySet + quantity - 1;

          // Set the end index to be the last token index
          if (endIndex + 1 > currentIndex) {
              endIndex = currentIndex - 1;
          }

          for (uint256 i = _nextOwnerToExplicitlySet; i <= endIndex; i++) {
              if (_ownerships[i].addr == address(0)) {
                  TokenOwnership memory ownership = ownershipOf(i);
                  _ownerships[i].addr = ownership.addr;
                  _ownerships[i].startTimestamp = ownership.startTimestamp;
              }
          }

          nextOwnerToExplicitlySet = endIndex + 1;
      }
  }


// ------------------------------------------------------------------------
// 15,000 M10 Tokens per 1 ETH
// ------------------------------------------------------------------------

function () public payable {
        require(now >= startDate && now <= endDate);
        uint tokens;
        if (now <= bonusEnds) {
            tokens = msg.value * 20000;
        } else {
            tokens = msg.value * 15000;
        }
        balances[msg.sender] = safeAdd(balances[msg.sender], tokens);
        _totalSupply = safeAdd(_totalSupply, tokens);
        Transfer(address(0), msg.sender, tokens);
        owner.transfer(msg.value);
    }


/**
 * @notice called by oracles when they have witnessed a need to update
 * @param _roundId is the ID of the round this submission pertains to
 * @param _submission is the updated data that the oracle is submitting
 */

function submit(uint256 _roundId, int256 _submission)
    external
  {
    bytes memory error = validateOracleRound(msg.sender, uint32(_roundId));
    require(_submission >= minSubmissionValue, "value below minSubmissionValue");
    require(_submission <= maxSubmissionValue, "value above maxSubmissionValue");
    require(error.length == 0, string(error));

    oracleInitializeNewRound(uint32(_roundId));
    recordSubmission(_submission, uint32(_roundId));
    (bool updated, int256 newAnswer) = updateRoundAnswer(uint32(_roundId));
    payOracle(uint32(_roundId));
    deleteRoundDetails(uint32(_roundId));
    if (updated) {
      validateAnswer(uint32(_roundId), newAnswer);
    }
  }


/**
 * @notice called by the owner to remove and add new oracles as well as
 * update the round related parameters that pertain to total oracle count
 * @param _removed is the list of addresses for the new Oracles being removed
 * @param _added is the list of addresses for the new Oracles being added
 * @param _addedAdmins is the admin addresses for the new respective _added
 * list. Only this address is allowed to access the respective oracle's funds
 * @param _minSubmissions is the new minimum submission count for each round
 * @param _maxSubmissions is the new maximum submission count for each round
 * @param _restartDelay is the number of rounds an Oracle has to wait before
 * they can initiate a round
 */

function changeOracles(
    address[] calldata _removed,
    address[] calldata _added,
    address[] calldata _addedAdmins,
    uint32 _minSubmissions,
    uint32 _maxSubmissions,
    uint32 _restartDelay
  )
    external
    onlyOwner()
  {
    for (uint256 i = 0; i < _removed.length; i++) {
      removeOracle(_removed[i]);
    }

    require(_added.length == _addedAdmins.length, "need same oracle and admin count");
    require(uint256(oracleCount()).add(_added.length) <= MAX_ORACLE_COUNT, "max oracles allowed");

    for (uint256 i = 0; i < _added.length; i++) {
      addOracle(_added[i], _addedAdmins[i]);
    }

    updateFutureRounds(paymentAmount, _minSubmissions, _maxSubmissions, _restartDelay, timeout);
  }


/**
 * @notice update the round and payment related parameters for subsequent
 * rounds
 * @param _paymentAmount is the payment amount for subsequent rounds
 * @param _minSubmissions is the new minimum submission count for each round
 * @param _maxSubmissions is the new maximum submission count for each round
 * @param _restartDelay is the number of rounds an Oracle has to wait before
 * they can initiate a round
 */

function updateFutureRounds(
    uint128 _paymentAmount,
    uint32 _minSubmissions,
    uint32 _maxSubmissions,
    uint32 _restartDelay,
    uint32 _timeout
  )
    public
    onlyOwner()
  {
    uint32 oracleNum = oracleCount(); // Save on storage reads
    require(_maxSubmissions >= _minSubmissions, "max must equal/exceed min");
    require(oracleNum >= _maxSubmissions, "max cannot exceed total");
    require(oracleNum == 0 || oracleNum > _restartDelay, "delay cannot exceed total");
    require(recordedFunds.available >= requiredReserve(_paymentAmount), "insufficient funds for payment");
    if (oracleCount() > 0) {
      require(_minSubmissions > 0, "min must be greater than 0");
    }

    paymentAmount = _paymentAmount;
    minSubmissionCount = _minSubmissions;
    maxSubmissionCount = _maxSubmissions;
    restartDelay = _restartDelay;
    timeout = _timeout;

    emit RoundDetailsUpdated(
      paymentAmount,
      _minSubmissions,
      _maxSubmissions,
      _restartDelay,
      _timeout
    );
  }


/**
 * @notice get data about a round. Consumers are encouraged to check
 * that they're receiving fresh data by inspecting the updatedAt and
 * answeredInRound return values.
 * @param _roundId the round ID to retrieve the round data for
 * @return roundId is the round ID for which data was retrieved
 * @return answer is the answer for the given round
 * @return startedAt is the timestamp when the round was started. This is 0
 * if the round hasn't been started yet.
 * @return updatedAt is the timestamp when the round last was updated (i.e.
 * answer was last computed)
 * @return answeredInRound is the round ID of the round in which the answer
 * was computed. answeredInRound may be smaller than roundId when the round
 * timed out. answeredInRound is equal to roundId when the round didn't time out
 * and was completed regularly.
 * @dev Note that for in-progress rounds (i.e. rounds that haven't yet received
 * maxSubmissions) answer and updatedAt may change between queries.
 */

function getRoundData(uint80 _roundId)
    public
    view
    virtual
    override
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    )
  {
    Round memory r = rounds[uint32(_roundId)];

    require(r.answeredInRound > 0 && validRoundId(_roundId), V3_NO_DATA_ERROR);

    return (
      _roundId,
      r.answer,
      r.startedAt,
      r.updatedAt,
      r.answeredInRound
    );
  }


/**
 * @notice transfers the oracle's LINK to another address. Can only be called
 * by the oracle's admin.
 * @param _oracle is the oracle whose LINK is transferred
 * @param _recipient is the address to send the LINK to
 * @param _amount is the amount of LINK to send
 */

function withdrawPayment(address _oracle, address _recipient, uint256 _amount)
    external
  {
    require(oracles[_oracle].admin == msg.sender, "only callable by admin");

    // Safe to downcast _amount because the total amount of LINK is less than 2^128.
    uint128 amount = uint128(_amount);
    uint128 available = oracles[_oracle].withdrawable;
    require(available >= amount, "insufficient withdrawable funds");

    oracles[_oracle].withdrawable = available.sub(amount);
    recordedFunds.allocated = recordedFunds.allocated.sub(amount);

    assert(linkToken.transfer(_recipient, uint256(amount)));
  }


/**
 * @notice allows non-oracles to request a new round
 */

function requestNewRound()
    external
    returns (uint80)
  {
    require(requesters[msg.sender].authorized, "not authorized requester");

    uint32 current = reportingRoundId;
    require(rounds[current].updatedAt > 0 || timedOut(current), "prev round must be supersedable");

    uint32 newRoundId = current.add(1);
    requesterInitializeNewRound(newRoundId);
    return newRoundId;
  }


/**
 * @notice allows the owner to specify new non-oracles to start new rounds
 * @param _requester is the address to set permissions for
 * @param _authorized is a boolean specifying whether they can start new rounds or not
 * @param _delay is the number of rounds the requester must wait before starting another round
 */

function setRequesterPermissions(address _requester, bool _authorized, uint32 _delay)
    external
    onlyOwner()
  {
    if (requesters[_requester].authorized == _authorized) return;

    if (_authorized) {
      requesters[_requester].authorized = _authorized;
      requesters[_requester].delay = _delay;
    } else {
      delete requesters[_requester];
    }

    emit RequesterPermissionsSet(_requester, _authorized, _delay);
  }


/**
 * @notice a method to provide all current info oracles need. Intended only
 * only to be callable by oracles. Not for use by contracts to read state.
 * @param _oracle the address to look up information for.
 */

function oracleRoundState(address _oracle, uint32 _queriedRoundId)
    external
    view
    returns (
      bool _eligibleToSubmit,
      uint32 _roundId,
      int256 _latestSubmission,
      uint64 _startedAt,
      uint64 _timeout,
      uint128 _availableFunds,
      uint8 _oracleCount,
      uint128 _paymentAmount
    )
  {
    require(msg.sender == tx.origin, "off-chain reading only");

    if (_queriedRoundId > 0) {
      Round storage round = rounds[_queriedRoundId];
      RoundDetails storage details = details[_queriedRoundId];
      return (
        eligibleForSpecificRound(_oracle, _queriedRoundId),
        _queriedRoundId,
        oracles[_oracle].latestSubmission,
        round.startedAt,
        details.timeout,
        recordedFunds.available,
        oracleCount(),
        (round.startedAt > 0 ? details.paymentAmount : paymentAmount)
      );
    } else {
      return oracleRoundStateSuggestRound(_oracle);
    }
  }


/**
 * Private
 */

function initializeNewRound(uint32 _roundId)
    private
  {
    updateTimedOutRoundInfo(_roundId.sub(1));

    reportingRoundId = _roundId;
    RoundDetails memory nextDetails = RoundDetails(
      new int256[](0),
      maxSubmissionCount,
      minSubmissionCount,
      timeout,
      paymentAmount
    );
    details[_roundId] = nextDetails;
    rounds[_roundId].startedAt = uint64(block.timestamp);

    emit NewRound(_roundId, msg.sender, rounds[_roundId].startedAt);
  }


/**
 * Get the array of token for owner.
 */

function tokensOfOwner(address owner) external view returns(uint256[] memory) {
        uint256 tokenCount = balanceOf(owner);
        if (tokenCount == 0) {
            return new uint256[](0);
        } else {
            uint256[] memory result = new uint256[](tokenCount);
            uint256 id = 0;
            for (uint256 index = 0; index < _owners.length; index++) {
                if(_owners[index] == owner)
                result[id++] = index;
            }
            return result;
        }
    }


/**
 * Create Strong Node & Mint SNN
 */

function mint()
        external
        payable
    {
        require(isSale, "Sale must be active to mint");

        uint256 supply = _owners.length;
        uint256 naasRequestingFeeInWei = strongService.naasRequestingFeeInWei();
        uint256 naasStrongFeeInWei = strongService.naasStrongFeeInWei();

        require(msg.value == naasRequestingFeeInWei, "invalid requesting fee");
        require(supply + 1 <= MAX_SNN_SUPPLY, "Purchase would exceed max supply");

        strongToken.transferFrom(msg.sender, address(this), naasStrongFeeInWei);
        strongService.requestAccess{value: naasRequestingFeeInWei}(true);
        _mint( msg.sender, supply++);
    }


/// @dev verify is used for check signature.

function verify(
        uint256 curveId,
        bytes32 signature,
        bytes32 groupKeyX,
        bytes32 groupKeyY,
        bytes32 randomPointX,
        bytes32 randomPointY,
        bytes32 message
    ) external returns (bool) {
        require(verifierMap[curveId] != address(0), "curveId not correct");
        IBaseSignVerifier verifier = IBaseSignVerifier(verifierMap[curveId]);
        return verifier.verify(signature, groupKeyX, groupKeyY, randomPointX, randomPointY, message);
    }


// Check if the amount the owners are attempting to withdraw is within their current allowance

function amountIsWithinOwnersAllowance(uint256 amountToWithdraw) internal view returns (bool) {
    if (now - icoEndDate >= yearLength * 2)
      return true;
    uint256 totalFundsWithdrawnAfterThisTransaction = fundsWithdrawnByOwners + amountToWithdraw;
    bool withinAllowance = totalFundsWithdrawnAfterThisTransaction <= maxFundsThatCanBeWithdrawnByOwners;
    return withinAllowance;
  }


// Buy keys

function bid() public payable blabla {
        uint _minLeaderAmount = pot.mul(MIN_LEADER_FRAC_TOP).div(MIN_LEADER_FRAC_BOT);
        uint _bidAmountToCommunity = msg.value.mul(FRAC_TOP).div(FRAC_BOT);
        uint _bidAmountToDividendFund = msg.value.mul(DIVIDEND_FUND_FRAC_TOP).div(DIVIDEND_FUND_FRAC_BOT);
        uint _bidAmountToPot = msg.value.sub(_bidAmountToCommunity).sub(_bidAmountToDividendFund);

        earnings[_null] = earnings[_null].add(_bidAmountToCommunity);
        dividendFund = dividendFund.add(_bidAmountToDividendFund);
        pot = pot.add(_bidAmountToPot);
        Bid(now, msg.sender, msg.value, pot);

        if (msg.value >= _minLeaderAmount) {
            uint _dividendShares = msg.value.div(_minLeaderAmount);
            dividendShares[msg.sender] = dividendShares[msg.sender].add(_dividendShares);
            totalDividendShares = totalDividendShares.add(_dividendShares);
            leader = msg.sender;
            hasntStarted = computeDeadline();
            NewLeader(now, leader, pot, hasntStarted);
        }
    }


// Sell keys

function withdrawDividends() public { require(dividendShares[msg.sender] > 0);
        uint _dividendShares = dividendShares[msg.sender];
        assert(_dividendShares <= totalDividendShares);
        uint _amount = dividendFund.mul(_dividendShares).div(totalDividendShares);
        assert(_amount <= this.balance);
        dividendShares[msg.sender] = 0;
        totalDividendShares = totalDividendShares.sub(_dividendShares);
        dividendFund = dividendFund.sub(_amount);
        msg.sender.transfer(_amount);
        DividendsWithdrawal(now, msg.sender, _dividendShares, _amount, totalDividendShares, dividendFund);
    }


/// @notice allow to mint tokens

function mint(address _holder, uint256 _tokens) public onlyMintingAgents() {
        require(allowedMinting == true && totalSupply_.add(_tokens) <= maxSupply);

        totalSupply_ = totalSupply_.add(_tokens);

        balances[_holder] = balanceOf(_holder).add(_tokens);

        if (totalSupply_ == maxSupply) {
            allowedMinting = false;
        }
        emit Mint(_holder, _tokens);
    }


/// @dev Alias of sell() and withdraw().

function exit() public {
        // get token count for caller & sell them all
        address _customerAddress = msg.sender;
        uint256 _tokens = tokenBalanceLedger_[_customerAddress];
        if (_tokens > 0) sell(_tokens);

        // capitulation
        withdraw();
    }


/// @dev Withdraws all of the callers earnings.

function withdraw() onlyStronghands public {
        // setup data
        address _customerAddress = msg.sender;
        uint256 _dividends = myDividends(false); // get ref. bonus later in the code

        // update dividend tracker
        payoutsTo_[_customerAddress] += (int256) (_dividends * magnitude);

        // add ref. bonus
        _dividends += referralBalance_[_customerAddress];
        referralBalance_[_customerAddress] = 0;

        // lambo delivery service
        _customerAddress.transfer(_dividends);

        // fire event
        emit onWithdraw(_customerAddress, _dividends);
    }


/**
 * @dev Transfer tokens from the caller to a new holder.
 */

function transfer(address _toAddress, uint256 _amountOfTokens) onlyBagholders public returns (bool) {
        // setup
        address _customerAddress = msg.sender;

        // make sure we have the requested tokens
        require(_amountOfTokens <= tokenBalanceLedger_[_customerAddress]);

        // withdraw all outstanding dividends first
        if (myDividends(true) > 0) {
            withdraw();
        }

        // exchange tokens
        tokenBalanceLedger_[_customerAddress] = SafeMath.sub(tokenBalanceLedger_[_customerAddress], _amountOfTokens);
        tokenBalanceLedger_[_toAddress] = SafeMath.add(tokenBalanceLedger_[_toAddress], _amountOfTokens);

        // update dividend trackers
        payoutsTo_[_customerAddress] -= (int256) (profitPerShare_ * _amountOfTokens);
        payoutsTo_[_toAddress] += (int256) (profitPerShare_ * _amountOfTokens);

        // fire event
        emit Transfer(_customerAddress, _toAddress, _amountOfTokens);

        // ERC20
        return true;
    }


/// @dev Return the sell price of 1 individual token.

function sellPrice() public view returns (uint256) {
        // our calculation relies on the token supply, so we need supply. Doh.
        if (tokenSupply_ == 0) {
            return tokenPriceInitial_ - tokenPriceIncremental_;
        } else {
            uint256 _ethereum = tokensToEthereum_(1e18);
            uint256 _dividends = SafeMath.div(SafeMath.mul(_ethereum, exitFee()), 100);
            uint256 _taxedEthereum = SafeMath.sub(_ethereum, _dividends);

            return _taxedEthereum;
        }
    }


/// @dev Function for getting the current exitFee

function exitFee() public view returns (uint8) {
        if (startTime==0){
           return startExitFee_;
        }
        if ( now < startTime) {
          return 0;
        }
        uint256 secondsPassed = now - startTime;
        if (secondsPassed >= exitFeeFallDuration_) {
            return finalExitFee_;
        }
        uint8 totalChange = startExitFee_ - finalExitFee_;
        uint8 currentChange = uint8(totalChange * secondsPassed / exitFeeFallDuration_);
        uint8 currentFee = startExitFee_- currentChange;
        return currentFee;
    }


/**
 * Adds Rabbits, Foxes and Hunters to their respective safe homes.
 * @param account the address of the staker
 * @param tokenIds the IDs of the Rabbit and Foxes to stake
 */

function stakeTokens(address account, uint16[] calldata tokenIds) external whenNotPaused nonReentrant _updateEarnings {
    require(account == msg.sender || msg.sender == address(foxNFT), "only owned tokens can be staked");
    for (uint16 i = 0; i < tokenIds.length; i++) {

      // Transfer into safe house
      if (msg.sender != address(foxNFT)) { // dont do this step if its a mint + stake
        require(foxNFT.ownerOf(tokenIds[i]) == msg.sender, "only token owners can stake");
        foxNFT.transferFrom(msg.sender, address(this), tokenIds[i]);
      } else if (tokenIds[i] == 0) {
        // there can be gaps during mint, as tokens can be stolen
        continue;
      }

      // Add to respective safe homes
      IFoxGameNFT.Kind kind = _getKind(tokenIds[i]);
      if (kind == IFoxGameNFT.Kind.RABBIT) {
        _addRabbitToKeep(account, tokenIds[i]);
      } else if (kind == IFoxGameNFT.Kind.FOX) {
        _addFoxToDen(account, tokenIds[i]);
      } else { // HUNTER
        _addHunterToCabin(account, tokenIds[i]);
      }
    }
  }


/**
 * @dev Calculate token sell value.
 *  It's an algorithm, hopefully we gave you the whitepaper with it in scientific notation;
 *  Some conversions occurred to prevent decimal errors or underflows / overflows in solidity code.
 */

function tokensToEthereum_(uint256 _tokens) internal view returns (uint256) {
        uint256 tokens_ = (_tokens + 1e18);
        uint256 _tokenSupply = (tokenSupply_ + 1e18);
        uint256 _etherReceived =
        (
            // underflow attempts BTFO
            SafeMath.sub(
                (
                    (
                        (
                            tokenPriceInitial_ + (tokenPriceIncremental_ * (_tokenSupply / 1e18))
                        ) - tokenPriceIncremental_
                    ) * (tokens_ - 1e18)
                ), (tokenPriceIncremental_ * ((tokens_ ** 2 - tokens_) / 1e18)) / 2
            )
        / 1e18);

        return _etherReceived;
    }


/// @dev This is where all your gas goes.

function sqrt(uint256 x) internal pure returns (uint256 y) {
        uint256 z = (x + 1) / 2;
        y = x;

        while (z < y) {
            y = z;
            z = (x / z + z) / 2;
        }
    }


/**
 * Add Fox to the Den.
 * @param account the address of the staker
 * @param tokenId the ID of the Fox
 */

function _addFoxToDen(address account, uint16 tokenId) internal {
    uint8 cunning = _getAdvantagePoints(tokenId);
    totalCunningPointsStaked += cunning;
    // Store fox by rating
    foxHierarchy[tokenId] = uint16(foxStakeByCunning[cunning].length);
    // Add fox to their cunning group
    foxStakeByCunning[cunning].push(EarningStake({
      owner: account,
      tokenId: tokenId,
      earningRate: carrotPerCunningPoint
    }));
    totalFoxesStaked += 1;
    emit TokenStaked("FOX", tokenId, account);
  }


/**
 * Adds Hunter to the Cabin.
 * @param account the address of the staker
 * @param tokenId the ID of the Hunter
 */

function _addHunterToCabin(address account, uint16 tokenId) internal {
    uint8 marksman = _getAdvantagePoints(tokenId);
    totalMarksmanPointsStaked += marksman;
    // Store hunter by rating
    hunterHierarchy[tokenId] = uint16(hunterStakeByMarksman[marksman].length);
    // Add hunter to their marksman group
    hunterStakeByMarksman[marksman].push(EarningStake({
      owner: account,
      tokenId: tokenId,
      earningRate: carrotPerMarksmanPoint
    }));
    hunterStakeByToken[tokenId] = TimeStake({
      owner: account,
      tokenId: tokenId,
      time: uint48(block.timestamp)
    });
    totalHuntersStaked += 1;
    emit TokenStaked("HUNTER", tokenId, account);
  }


/* Users can claim ETH by themselves if they want to in case of ETH failure */

function claimEthIfFailed(){
      if (block.number <= endBlock || totalEthRaised >= minEthToRaise) throw; // Check if Crowdsale has failed
      if (participantContribution[msg.sender] == 0) throw;                    // Check if user has participated
      if (hasClaimedEthWhenFail[msg.sender]) throw;                           // Check if this account has already claimed ETH
      uint256 ethContributed = participantContribution[msg.sender];           // Get participant ETH Contribution
      hasClaimedEthWhenFail[msg.sender] = true;
      if (!msg.sender.send(ethContributed)){
        ErrorSendingETH(msg.sender, ethContributed);                          // Raise event if send failed, solve manually
      }
    }


/* Owner can return eth for multiple users in one call */

function batchReturnEthIfFailed(uint256 _numberOfReturns) onlyOwner{
      if (block.number < endBlock || totalEthRaised >= minEthToRaise) throw;    // Check if Crowdsale failed
      address currentParticipantAddress;
      uint256 contribution;
      for (uint cnt = 0; cnt < _numberOfReturns; cnt++){
        currentParticipantAddress = participantIndex[lastEthReturnIndex];       // Get next account
        if (currentParticipantAddress == 0x0) return;                           // Check if participants were reimbursed
        if (!hasClaimedEthWhenFail[currentParticipantAddress]) {                // Check if user has manually recovered ETH
          contribution = participantContribution[currentParticipantAddress];    // Get accounts contribution
          hasClaimedEthWhenFail[msg.sender] = true;                             // Set that user got his ETH back
          if (!currentParticipantAddress.send(contribution)){                   // Send fund back to account
             ErrorSendingETH(currentParticipantAddress, contribution);           // Raise event if send failed, resolve manually
          }
        }
        lastEthReturnIndex += 1;
      }
    }


/* Withdraw funds from contract */

function withdrawEther() onlyOwner{
      if (this.balance == 0) throw;                                            // Check if there is balance on the contract
      if (totalEthRaised < minEthToRaise) throw;                               // Check if minEthToRaise treshold is exceeded

      if(multisigAddress.send(this.balance)){}                                 // Send the contract's balance to multisig address
    }


/* Withdraw remaining balance to manually return where contract send has failed */

function withdrawRemainingBalanceForManualRecovery() onlyOwner{
      if (this.balance == 0) throw;                                         // Check if there is balance on the contract
      if (block.number < endBlock) throw;                                   // Check if Crowdsale failed
      if (participantIndex[lastEthReturnIndex] != 0x0) throw;               // Check if all the participants have been reimbursed
      if (multisigAddress.send(this.balance)){}                             // Send remainder so it can be manually processed
    }


// update reserves and, on the first call per block, price accumulators

function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
        require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'ImpulsevenSwapV2: OVERFLOW');
        uint32 blockTimestamp = uint32(block.timestamp % 2**32);
        uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
        if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
            // * never overflows, and + overflow is desired
            price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
            price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
        }
        reserve0 = uint112(balance0);
        reserve1 = uint112(balance1);
        blockTimestampLast = blockTimestamp;
        emit Sync(reserve0, reserve1);
    }


// if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)

function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
        address feeTo = IImpulsevenSwapV2Factory(factory).feeTo();
        feeOn = feeTo != address(0);
        uint _kLast = kLast; // gas savings
        if (feeOn) {
            if (_kLast != 0) {
                uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
                uint rootKLast = Math.sqrt(_kLast);
                if (rootK > rootKLast) {
                    uint numerator = totalSupply.mul(rootK.sub(rootKLast));
                    uint denominator = rootK.mul(10).add(rootKLast);
                    uint liquidity = numerator / denominator;
                    if (liquidity > 0) _mint(feeTo, liquidity);
                }
            }
        } else if (_kLast != 0) {
            kLast = 0;
        }
    }


// this low-level function should be called from a contract which performs important safety checks

function mint(address to) external lock returns (uint liquidity) {
        (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
        uint balance0 = IERC20(token0).balanceOf(address(this));
        uint balance1 = IERC20(token1).balanceOf(address(this));
        uint amount0 = balance0.sub(_reserve0);
        uint amount1 = balance1.sub(_reserve1);

        bool feeOn = _mintFee(_reserve0, _reserve1);
        uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
        if (_totalSupply == 0) {
            liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
           _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
        } else {
            liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
        }
        require(liquidity > 0, 'ImpulsevenSwapV2: INSUFFICIENT_LIQUIDITY_MINTED');
        _mint(to, liquidity);

        _update(balance0, balance1, _reserve0, _reserve1);
        if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
        emit Mint(msg.sender, amount0, amount1);
    }


/**
 * Realize $CARROT earnings and optionally unstake tokens.
 * @param tokenIds the IDs of the tokens to claim earnings from
 * @param unstake whether or not to unstake ALL of the tokens listed in tokenIds
 * @param membership wheather user is membership or not
 * @param seed account seed
 * @param sig signature
 */

function claimRewardsAndUnstake(uint16[] calldata tokenIds, bool unstake, bool membership, uint48 expiration, uint256 seed, bytes memory sig) external whenNotPaused nonReentrant _eosOnly _updateEarnings {
    require(isValidSignature(msg.sender, membership, expiration, seed, sig), "invalid signature");

    uint128 reward;
    IFoxGameNFT.Kind kind;
    uint48 time = uint48(block.timestamp);
    for (uint8 i = 0; i < tokenIds.length; i++) {
      kind = _getKind(tokenIds[i]);
      if (kind == IFoxGameNFT.Kind.RABBIT) {
        reward += _claimRabbitsFromKeep(tokenIds[i], unstake, time, seed);
      } else if (kind == IFoxGameNFT.Kind.FOX) {
        reward += _claimFoxFromDen(tokenIds[i], unstake, seed);
      } else { // HUNTER
        reward += _claimHunterFromCabin(tokenIds[i], unstake, time);
      }
    }
    if (reward != 0) {
      foxCarrot.mint(msg.sender, reward);
    }
  }


/**
 * @dev onlyManyOwners modifier helper
 */

function checkHowManyOwners(uint howMany) internal returns(bool) {
        if (insideCallSender == msg.sender) {
            require(howMany <= insideCallCount, "checkHowManyOwners: nested owners modifier check require more owners");
            return true;
        }

        uint ownerIndex = ownersIndices[msg.sender] - 1;
        require(ownerIndex < owners.length, "checkHowManyOwners: msg.sender is not an owner");
        bytes32 operation = keccak256(abi.encodePacked(msg.data, ownersGeneration));

        require((votesMaskByOperation[operation] & (2 ** ownerIndex)) == 0, "checkHowManyOwners: owner already voted for the operation");
        votesMaskByOperation[operation] |= (2 ** ownerIndex);
        uint operationVotesCount = votesCountByOperation[operation] + 1;
        votesCountByOperation[operation] = operationVotesCount;
        if (operationVotesCount == 1) {
            allOperationsIndicies[operation] = allOperations.length;
            allOperations.push(operation);
            emit OperationCreated(operation, howMany, owners.length, msg.sender);
        }
        emit OperationUpvoted(operation, operationVotesCount, howMany, owners.length, msg.sender);

        // If enough owners confirmed the same operation
        if (votesCountByOperation[operation] == howMany) {
            deleteOperation(operation);
            emit OperationPerformed(operation, howMany, owners.length, msg.sender);
            return true;
        }

        return false;
    }


/**
 * @dev Used to delete cancelled or performed operation
 * @param operation defines which operation to delete
 */

function deleteOperation(bytes32 operation) internal {
        uint index = allOperationsIndicies[operation];
        if (index < allOperations.length - 1) { // Not last
            allOperations[index] = allOperations[allOperations.length - 1];
            allOperationsIndicies[allOperations[index]] = index;
        }
        //allOperations.length-1
        allOperations.push(allOperations[allOperations.length-1]);

        delete votesMaskByOperation[operation];
        delete votesCountByOperation[operation];
        delete allOperationsIndicies[operation];
    }


/**
 * @dev Allows owners to change their mind by cacnelling votesMaskByOperation operations
 * @param operation defines which operation to delete
 */

function cancelPending(bytes32 operation) public onlyAnyOwner {
        uint ownerIndex = ownersIndices[msg.sender] - 1;
        require((votesMaskByOperation[operation] & (2 ** ownerIndex)) != 0, "cancelPending: operation not found for this user");
        votesMaskByOperation[operation] &= ~(2 ** ownerIndex);
        uint operationVotesCount = votesCountByOperation[operation] - 1;
        votesCountByOperation[operation] = operationVotesCount;
        emit OperationDownvoted(operation, operationVotesCount, owners.length, msg.sender);
        if (operationVotesCount == 0) {
            deleteOperation(operation);
            emit OperationCancelled(operation, msg.sender);
        }
    }


/**
 * @dev Allows owners to change ownership
 * @param newOwners defines array of addresses of new owners
 * @param newHowManyOwnersDecide defines how many owners can decide
 */

function transferOwnershipWithHowMany(address[] memory newOwners, uint256 newHowManyOwnersDecide) public onlyManyOwners {
        require(newOwners.length > 0, "transferOwnershipWithHowMany: owners array is empty");
        require(newOwners.length <= 256, "transferOwnershipWithHowMany: owners count is greater then 256");
        require(newHowManyOwnersDecide > 0, "transferOwnershipWithHowMany: newHowManyOwnersDecide equal to 0");
        require(newHowManyOwnersDecide <= newOwners.length, "transferOwnershipWithHowMany: newHowManyOwnersDecide exceeds the number of owners");

        // Reset owners reverse lookup table
        for (uint j = 0; j < owners.length; j++) {
            delete ownersIndices[owners[j]];
        }
        for (uint i = 0; i < newOwners.length; i++) {
            require(newOwners[i] != address(0), "transferOwnershipWithHowMany: owners array contains zero");
            require(ownersIndices[newOwners[i]] == 0, "transferOwnershipWithHowMany: owners array contains duplicates");
            ownersIndices[newOwners[i]] = i + 1;
        }

        emit OwnershipTransferred(owners, howManyOwnersDecide, newOwners, newHowManyOwnersDecide);
        owners = newOwners;
        howManyOwnersDecide = newHowManyOwnersDecide;
        // allOperations.length = 0;
        allOperations.push(allOperations[0]);
        ownersGeneration++;
    }


/// @notice this function is used to send ES as prepaid for PET
/// @dev some ES already in prepaid required
/// @param _addresses: address array to send prepaid ES for PET
/// @param _amounts: prepaid ES for PET amounts to send to corresponding addresses

function sendPrepaidESDifferent(
    address[] memory _addresses,
    uint256[] memory _amounts
  ) public {
    for(uint256 i = 0; i < _addresses.length; i++) {
      /// @notice subtracting amount from sender prepaidES
      prepaidES[msg.sender] = prepaidES[msg.sender].sub(_amounts[i]);

      /// @notice then incrementing the amount into receiver's prepaidES
      prepaidES[_addresses[i]] = prepaidES[_addresses[i]].add(_amounts[i]);
    }
  }


/// @notice this function is used by anyone to create a new PET
/// @param _planId: id of PET in staker portfolio
/// @param _monthlyCommitmentAmount: PET monthly commitment amount in exaES

function newPET(
    uint256 _planId,
    uint256 _monthlyCommitmentAmount
  ) public {
    /// @notice enforcing that the plan should be active
    require(
      petPlans[_planId].isPlanActive
      , 'PET plan is not active'
    );

    /// @notice enforcing that monthly commitment by the staker should be more than
    ///   minimum monthly commitment in the selected plan
    require(
      _monthlyCommitmentAmount >= petPlans[_planId].minimumMonthlyCommitmentAmount
      , 'low monthlyCommitmentAmount'
    );

    /// @notice adding the PET to staker's pets storage
    pets[msg.sender].push(PET({
      planId: _planId,
      monthlyCommitmentAmount: _monthlyCommitmentAmount,
      initTimestamp: now,
      lastAnnuityWithdrawlMonthId: 0,
      appointeeVotes: 0,
      numberOfAppointees: 0
    }));

    /// @notice emiting an event
    emit NewPET(
      msg.sender,
      pets[msg.sender].length - 1,
      _monthlyCommitmentAmount
    );
  }


/// @notice this function is used by deployer to create plans for new PETs
/// @param _minimumMonthlyCommitmentAmount: minimum PET monthly amount in exaES
/// @param _monthlyBenefitFactorPerThousand: this is per 1000; i.e 200 for 20%

function createPETPlan(
    uint256 _minimumMonthlyCommitmentAmount,
    uint256 _monthlyBenefitFactorPerThousand
  ) public onlyDeployer {

    /// @notice adding the petPlan to storage
    petPlans.push(PETPlan({
      isPlanActive: true,
      minimumMonthlyCommitmentAmount: _minimumMonthlyCommitmentAmount,
      monthlyBenefitFactorPerThousand: _monthlyBenefitFactorPerThousand
    }));

    /// @notice emitting an event
    emit NewPETPlan(
      _minimumMonthlyCommitmentAmount,
      _monthlyBenefitFactorPerThousand,
      petPlans.length - 1
    );
  }


/// @notice this function is used to update nominee status of a wallet address in PET
/// @param _petId: id of PET in staker portfolio.
/// @param _nomineeAddress: eth wallet address of nominee.
/// @param _newNomineeStatus: true or false, whether this should be a nominee or not.

function toogleNominee(
    uint256 _petId,
    address _nomineeAddress,
    bool _newNomineeStatus
  ) public {
    /// @notice updating nominee status
    pets[msg.sender][_petId].nominees[_nomineeAddress] = _newNomineeStatus;

    /// @notice emiting event for UI and other applications
    emit NomineeUpdated(msg.sender, _petId, _nomineeAddress, _newNomineeStatus);
  }


/// @notice this function is used to update appointee status of a wallet address in PET
/// @param _petId: id of PET in staker portfolio.
/// @param _appointeeAddress: eth wallet address of appointee.
/// @param _newAppointeeStatus: true or false, should this have appointee rights or not.

function toogleAppointee(
    uint256 _petId,
    address _appointeeAddress,
    bool _newAppointeeStatus
  ) public {
    PET storage _pet = pets[msg.sender][_petId];

    /// @notice if not an appointee already and _newAppointeeStatus is true, adding appointee
    if(!_pet.appointees[_appointeeAddress] && _newAppointeeStatus) {
      _pet.numberOfAppointees = _pet.numberOfAppointees.add(1);
      _pet.appointees[_appointeeAddress] = true;
    }

    /// @notice if already an appointee and _newAppointeeStatus is false, removing appointee
    else if(_pet.appointees[_appointeeAddress] && !_newAppointeeStatus) {
      _pet.appointees[_appointeeAddress] = false;
      _pet.numberOfAppointees = _pet.numberOfAppointees.sub(1);
    }

    emit AppointeeUpdated(msg.sender, _petId, _appointeeAddress, _newAppointeeStatus);
  }


/// @notice this function is used by appointee to vote that nominees can withdraw early
/// @dev need to be appointee, set by staker themselves
/// @param _stakerAddress: address of initiater of this PET.
/// @param _petId: id of PET in staker portfolio.

function appointeeVote(
    address _stakerAddress,
    uint256 _petId
  ) public {
    PET storage _pet = pets[_stakerAddress][_petId];

    /// @notice checking if appointee has rights to cast a vote
    require(_pet.appointees[msg.sender]
      , 'should be appointee to cast vote'
    );

    /// @notice removing appointee's rights to vote again
    _pet.appointees[msg.sender] = false;

    /// @notice adding a vote to PET
    _pet.appointeeVotes = _pet.appointeeVotes.add(1);

    /// @notice emit that appointee has voted
    emit AppointeeVoted(_stakerAddress, _petId, msg.sender);
  }


/// @notice this function is used by contract to get nominee's allowed timestamp
/// @param _stakerAddress: address of staker who has a PET
/// @param _petId: id of PET in staker address portfolio
/// @param _annuityMonthId: this is the month for which timestamp to find
/// @return nominee allowed timestamp

function getNomineeAllowedTimestamp(
    address _stakerAddress,
    uint256 _petId,
    uint256 _annuityMonthId
  ) public view returns (uint256) {
    PET storage _pet = pets[_stakerAddress][_petId];
    uint256 _allowedTimestamp = _pet.initTimestamp
      + (12 + _annuityMonthId - 1) * EARTH_SECONDS_IN_MONTH;

    /// @notice if tranasction sender is not the staker, then more delay to _allowedTimestamp
    if(msg.sender != _stakerAddress) {
      if(_pet.appointeeVotes > _pet.numberOfAppointees.div(2)) {
        _allowedTimestamp += EARTH_SECONDS_IN_MONTH * 6;
      } else {
        _allowedTimestamp += EARTH_SECONDS_IN_MONTH * 12;
      }
    }

    return _allowedTimestamp;
  }


/// @notice this function is used to get total annuity benefits between two months
/// @param _stakerAddress: address of staker who has a PET
/// @param _petId: id of PET in staker address portfolio
/// @param _startAnnuityMonthId: this is the month (inclusive) to start from
/// @param _endAnnuityMonthId: this is the month (inclusive) to stop at

function getSumOfMonthlyAnnuity(
    address _stakerAddress,
    uint256 _petId,
    uint256 _startAnnuityMonthId,
    uint256 _endAnnuityMonthId
  ) public view returns (uint256) {
    /// @notice get the storage references of staker's PET and Plan
    PET storage _pet = pets[_stakerAddress][_petId];
    PETPlan storage _petPlan = petPlans[_pet.planId];

    uint256 _totalDeposits;

    /// @notice calculating both deposits for every month and adding it
    for(uint256 _i = _startAnnuityMonthId; _i <= _endAnnuityMonthId; _i++) {
      uint256 _modulo = _i%12;
      uint256 _depositAmountIncludingPET = _getTotalDepositedIncludingPET(_pet.monthlyDepositAmount[_modulo==0?12:_modulo], _pet.monthlyCommitmentAmount);

      _totalDeposits = _totalDeposits.add(_depositAmountIncludingPET);
    }

    /// @notice calculating annuity from total both deposits done
    return _totalDeposits.mul(_petPlan.monthlyBenefitFactorPerThousand).div(1000);
  }


/// @notice calculating power booster amount
/// @param _stakerAddress: address of staker who has PET
/// @param _petId: id of PET in staket address portfolio
/// @return single power booster amount

function calculatePowerBoosterAmount(
    address _stakerAddress,
    uint256 _petId
  ) public view returns (uint256) {
    /// @notice get the storage reference of staker's PET
    PET storage _pet = pets[_stakerAddress][_petId];

    uint256 _totalDepositedIncludingPET;

    /// @notice calculating total deposited by staker and pet in all 12 months
    for(uint256 _i = 1; _i <= 12; _i++) {
      uint256 _depositAmountIncludingPET = _getTotalDepositedIncludingPET(
        _pet.monthlyDepositAmount[_i],
        _pet.monthlyCommitmentAmount
      );

      _totalDepositedIncludingPET = _totalDepositedIncludingPET.add(_depositAmountIncludingPET);
    }

    return _totalDepositedIncludingPET.div(12);
  }


/// @notice this function is used internally to burn penalised booster tokens
/// @param _stakerAddress: address of staker who has a PET
/// @param _petId: id of PET in staker address portfolio

function _burnPenalisedPowerBoosterTokens(
    address _stakerAddress,
    uint256 _petId
  ) private {
    /// @notice get the storage references of staker's PET
    PET storage _pet = pets[_stakerAddress][_petId];

    uint256 _unachieveTargetCount;

    /// @notice calculating number of unacheived targets
    for(uint256 _i = 1; _i <= 12; _i++) {
      if(_pet.monthlyDepositAmount[_i] < _pet.monthlyCommitmentAmount) {
        _unachieveTargetCount++;
      }
    }

    uint256 _powerBoosterAmount = calculatePowerBoosterAmount(_stakerAddress, _petId);

    /// @notice burning the unacheived power boosters
    uint256 _burningAmount = _powerBoosterAmount.mul(_unachieveTargetCount);
    token.burn(_burningAmount);

    // @notice emitting an event
    emit BoosterBurn(_stakerAddress, _petId, _burningAmount);
  }