/// @dev Clears record of a Contributor from a Group's record
/// @param _tokenId Token ID of Group to be cleared
/// @param _userAdd Address of Contributor

function _clearContributorRecordInGroup(uint256 _tokenId, address _userAdd) private returns (uint256 refundBalance) {
    var group = tokenIndexToGroup[_tokenId];

    // Index was saved is 1 + the array's index, b/c 0 is the default value
    //  in a mapping.
    uint cIndex = group.addressToContributorArrIndex[_userAdd] - 1;
    uint lastCIndex = group.contributorArr.length - 1;
    refundBalance = group.addressToContribution[_userAdd];

    // clear contribution record in group
    tokenIndexToGroup[_tokenId].addressToContributorArrIndex[_userAdd] = 0;
    tokenIndexToGroup[_tokenId].addressToContribution[_userAdd] = 0;

    // move address in last position to deleted contributor's spot
    if (lastCIndex > 0) {
      tokenIndexToGroup[_tokenId].addressToContributorArrIndex[group.contributorArr[lastCIndex]] = cIndex;
      tokenIndexToGroup[_tokenId].contributorArr[cIndex] = group.contributorArr[lastCIndex];
    }

    tokenIndexToGroup[_tokenId].contributorArr.length -= 1;
    tokenIndexToGroup[_tokenId].contributedBalance -= refundBalance;
  }


/**
 * @notice Stake the staking token for the token to be paid as reward.
 */

function stake(address token, uint128 amount)
        external
        override
        nonReentrant
        updateTerm(token)
        updateReward(token, msg.sender)
    {
        if (_accountInfo[token][msg.sender].userTerm < _currentTerm[token]) {
            return;
        }

        require(amount != 0, "staking amount should be positive number");

        _updVoteAdd(msg.sender, amount);
        _stake(msg.sender, token, amount);
        _stakingToken.safeTransferFrom(msg.sender, address(this), amount);
    }


/// @dev Redistribute proceeds from token purchase
/// @param _tokenId Token ID of token to be purchased
/// @param _amount Amount paid into contract for token

function _distributeProceeds(uint256 _tokenId, uint256 _amount) private {
    uint256 fundsForDistribution = uint256(SafeMath.div(SafeMath.mul(_amount,
      distributionNumerator), distributionDenominator));
    uint256 commission = _amount;

    for (uint i = 0; i < tokenIndexToGroup[_tokenId].contributorArr.length; i++) {
      address userAdd = tokenIndexToGroup[_tokenId].contributorArr[i];

      // calculate contributor's sale proceeds and add to their withdrawable balance
      uint256 userProceeds = uint256(SafeMath.div(SafeMath.mul(fundsForDistribution,
        tokenIndexToGroup[_tokenId].addressToContribution[userAdd]),
        tokenIndexToGroup[_tokenId].contributedBalance));

      _clearGroupRecordInContributor(_tokenId, userAdd);

      // clear contributor record on group
      tokenIndexToGroup[_tokenId].addressToContribution[userAdd] = 0;
      tokenIndexToGroup[_tokenId].addressToContributorArrIndex[userAdd] = 0;

      commission -= userProceeds;
      userAddressToContributor[userAdd].withdrawableBalance += userProceeds;
      ProceedsDeposited(_tokenId, userAdd, userProceeds);
    }

    commissionBalance += commission;
    Commission(_tokenId, commission);

    activeGroups -= 1;
    tokenIndexToGroup[_tokenId].exists = false;
    tokenIndexToGroup[_tokenId].contributorArr.length = 0;
    tokenIndexToGroup[_tokenId].contributedBalance = 0;
    tokenIndexToGroup[_tokenId].purchasePrice = 0;
  }


/// @dev Calculates next price of celebrity token
/// @param _oldPrice Previous price

function _newPrice(uint256 _oldPrice) private view returns (uint256 newPrice) {
    if (_oldPrice < firstStepLimit) {
      // first stage
      newPrice = SafeMath.div(SafeMath.mul(_oldPrice, 200), 94);
    } else if (_oldPrice < secondStepLimit) {
      // second stage
      newPrice = SafeMath.div(SafeMath.mul(_oldPrice, 120), 94);
    } else {
      // third stage
      newPrice = SafeMath.div(SafeMath.mul(_oldPrice, 115), 94);
    }
  }


/**
 * @notice Withdraw the staking token for the token to be paid as reward.
 */

function withdraw(address token, uint128 amount)
        external
        override
        nonReentrant
        updateTerm(token)
        updateReward(token, msg.sender)
    {
        if (_accountInfo[token][msg.sender].userTerm < _currentTerm[token]) {
            return;
        }

        require(amount != 0, "withdrawing amount should be positive number");

        _updVoteSub(msg.sender, amount);
        _withdraw(msg.sender, token, amount);
        _stakingToken.safeTransfer(msg.sender, amount);
    }


/**
 * @notice Receive the reward for your staking in the token.
 */

function receiveReward(address token)
        external
        override
        nonReentrant
        updateTerm(token)
        updateReward(token, msg.sender)
        returns (uint256 rewards)
    {
        rewards = _accountInfo[token][msg.sender].rewards;
        if (rewards != 0) {
            _totalRemainingRewards[token] = _totalRemainingRewards[token].sub(rewards); // subtract the total unpaid reward
            _accountInfo[token][msg.sender].rewards = 0;
            if (token == ETH_ADDRESS) {
                _transferETH(msg.sender, rewards);
            } else {
                IERC20(token).safeTransfer(msg.sender, rewards);
            }
            emit RewardPaid(token, msg.sender, rewards);
        }
    }


//THE ONLY ADMIN FUNCTIONS ^^^^

function sqrt(uint y) public pure returns (uint z) {
        if (y > 3) {
            z = y;
            uint x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }


/// @notice Allots a new stake out of the stake of the message sender
/// @dev Stakeholder only may call

function splitStake(address newHolder, uint256 newAmount) external {
        address holder = msg.sender;
        require(newHolder != holder, "PStakes: duplicated address");

        Stake memory stake = _getStake(holder);
        require(newAmount <= stake.allocated, "PStakes: too large allocated");

        uint256 updAmount = uint256(stake.allocated) - newAmount;
        uint256 updReleased = (uint256(stake.released) * updAmount) /
            uint256(stake.allocated);
        stakes[holder] = Stake(_safe96(updAmount), _safe96(updReleased));
        emit StakeSplit(holder, updAmount, updReleased);

        uint256 newVested = uint256(stake.released) - updReleased;
        stakes[newHolder] = Stake(_safe96(newAmount), _safe96(newVested));
        emit StakeSplit(newHolder, newAmount, newVested);
    }


//////////////////
//// Owner ////
//////////////////
/// @notice Inits the contract and adds stakes
/// @dev Owner only may call on a proxy (but not on the implementation)

function addStakes(
        uint256 _poolId,
        address[] calldata holders,
        uint256[] calldata allocations,
        uint256 unallocated
    ) external onlyOwner {
        if (allocation == 0) {
            _init(_poolId);
        } else {
            require(_poolId == poolId, "PStakes: pool mismatch");
        }

        uint256 nEntries = holders.length;
        require(nEntries == allocations.length, "PStakes: length mismatch");
        uint256 updAllocated = uint256(allocated);
        for (uint256 i = 0; i < nEntries; i++) {
            _throwZeroHolderAddress(holders[i]);
            require(
                stakes[holders[i]].allocated == 0,
                "PStakes: holder exists"
            );
            require(allocations[i] > 0, "PStakes: zero allocation");

            updAllocated += allocations[i];
            stakes[holders[i]] = Stake(_safe96(allocations[i]), 0);
            emit StakeAdded(holders[i], allocations[i]);
        }
        require(
            updAllocated + unallocated == allocation,
            "PStakes: invalid allocation"
        );
        allocated = _safe96(updAllocated);
    }


/// @notice Withdraws accidentally sent token from this contract
/// @dev Owner may call only

function claimErc20(
        address claimedToken,
        address to,
        uint256 amount
    ) external onlyOwner nonReentrant {
        IERC20 vestedToken = IERC20(address(_getToken()));
        if (claimedToken == address(vestedToken)) {
            uint256 balance = vestedToken.balanceOf(address(this));
            require(
                balance - amount >= allocation - released,
                "PStakes: too big amount"
            );
        }
        _claimErc20(claimedToken, to, amount);
    }


/// @notice Removes the contract from blockchain when tokens are released
/// @dev Owner only may call on a proxy (but not on the implementation)

function removeContract() external onlyOwner {
        // avoid accidental removing of the implementation
        _throwImplementation();

        require(allocation == released, "PStakes: unpaid stakes");

        IERC20 vestedToken = IERC20(address(_getToken()));
        uint256 balance = vestedToken.balanceOf(address(this));
        require(balance == 0, "PStakes: non-zero balance");

        selfdestruct(payable(msg.sender));
    }


/// @notice Initialize the contract
/// @dev May be called on a proxy only (but not on the implementation)

function _init(uint256 _poolId) internal {
        _throwImplementation();
        require(_poolId < 2**16, "PStakes:unsafePoolId");

        IVestingPools pools = _getVestingPools();
        address wallet = pools.getWallet(_poolId);
        require(wallet == address(this), "PStakes:invalidPool");
        PoolParams memory pool = pools.getPool(_poolId);
        require(pool.sAllocation != 0, "PStakes:zeroPool");

        poolId = uint16(_poolId);
        allocation = _safe96(uint256(pool.sAllocation) * SCALE);
    }


/// @dev Returns amount that may be released for the given stake and factor

function _releasableAmount(Stake memory stake, uint256 _factor)
        internal
        pure
        returns (uint256)
    {
        uint256 share = (_factor * uint256(stake.allocated)) / SCALE;
        if (share > stake.allocated) {
            // imprecise division safeguard
            share = uint256(stake.allocated);
        }
        return share - uint256(stake.released);
    }


/// @dev Sends the releasable amount of the specified placeholder

function _withdraw(address holder) internal {
        Stake memory stake = _getStake(holder);
        uint256 releasable = _releasableAmount(stake, uint256(factor));
        require(releasable > 0, "PStakes: nothing to withdraw");

        stakes[holder].released = _safe96(uint256(stake.released) + releasable);
        released = _safe96(uint256(released) + releasable);

        // (reentrancy attack impossible - known contract called)
        require(_getToken().transfer(holder, releasable), "PStakes:E1");
        emit Released(holder, releasable);
    }


/// @dev Gets collateral balance deposited into Vesper Grow Pool

function _getCollateralBalance() internal view returns (uint256) {
        uint256 _totalSupply = vToken.totalSupply();
        // avoids division by zero error when pool is empty
        return (_totalSupply != 0) ? (vToken.totalValue() * vToken.balanceOf(address(this))) / _totalSupply : 0;
    }


// When we add a shortable pynth, we need to know the iPynth as well
// This is so we can get the proper skew for the short rate.

function addShortablePynths(bytes32[2][] calldata requiredPynthAndInverseNamesInResolver, bytes32[] calldata pynthKeys)
        external
        onlyOwner
    {
        require(requiredPynthAndInverseNamesInResolver.length == pynthKeys.length, "Input array length mismatch");

        for (uint i = 0; i < requiredPynthAndInverseNamesInResolver.length; i++) {
            // setting these explicitly for clarity
            // Each entry in the array is [Pynth, iPynth]
            bytes32 pynth = requiredPynthAndInverseNamesInResolver[i][0];
            bytes32 iPynth = requiredPynthAndInverseNamesInResolver[i][1];

            if (!_shortablePynths.contains(pynth)) {
                // Add it to the address set lib.
                _shortablePynths.add(pynth);

                // store the mapping to the iPynth so we can get its total supply for the borrow rate.
                pynthToInversePynth[pynth] = iPynth;

                emit ShortablePynthAdded(pynth);

                // now the associated pynth key to the CollateralManagerState
                state.addShortCurrency(pynthKeys[i]);
            }
        }

        rebuildCache();
    }


//A mapping to store the player pairs in private rooms based on keccak256 ids generated locally
//constructor

function Lottery() public{
        owner = msg.sender;
        player_count = 0;
        ante = 0.054 ether;
        required_number_players = 2;
        winner_percentage = 98;
        oraclize_setProof(proofType_Ledger);
        oracle_price = 0.003 ether;
        oraclize_gas = 285000;
        private_rooms_index=0;
        paused = false;
        ticket_price = ante + oracle_price;
    }


// function when someone gambles a.k.a sends ether to the contract

function buy() whenNotPaused payable public{
        // No arguments are necessary, all
        // information is already part of
        // the transaction. The keyword payable
        // is required for the function to
        // be able to receive Ether.

        // If the bet is not equal to the ante + the oracle price,
        // send the money back.
        require (msg.value >= ante + oracle_price);//give it back, revert state changes, abnormal stop
        require (player_count <2); //make sure that the counter was reset
        if(msg.value > (ante+oracle_price)) {
            msg.sender.transfer(msg.value - ante - oracle_price); //If any extra eteher gets transferred return it back to the sender
        }

        player_count +=1;
        if (player_count == 1) {
            players.player1 = msg.sender;
        }
        if (player_count == 2) {
            players.player2 = msg.sender;
            ShowPlayers (players.player1,players.player2);
            update();
            player_count =0;
        }
    }


/* Address => (productName => amount) */

function TestConf () { name = "TestConf"; totalSupply = 1000000; initialIssuance = totalSupply; owner = 0x443B9375536521127DBfABff21f770e4e684475d; currentEthPrice = 20000; /* TODO: Oracle */ currentTokenPrice = 100; /* In cents */ symbol = "TEST1"; balances[owner] = 100; }


/**
 * @dev Mint new quantity amount of nfts
 */

function mint(uint quantity) public payable isOpen notPaused {
    require(quantity > 0, "You can't mint 0");
    require(quantity <= maxPurchaseSize, "Exceeds max per transaction");
    require(totalSupply() + quantity <= MAX_SUPPLY, "Not enough token left");

    uint256 price = mintPrice.mul(quantity);
    require(msg.value >= price, "Value below order price");
    for(uint i = 0; i < quantity; i++){
        _safeMint(msg.sender, totalSupply().add(1));
    }

    uint256 remaining = msg.value.sub(price);
    if (remaining > 0) {
      (bool success, ) = msg.sender.call{value: remaining}("");
      require(success);
    }
  }


/**
 * @dev Returns all the token IDs owned by address who
 */

function tokensOfAddress(address who) external view returns(uint256[] memory) {
    uint tokenCount = balanceOf(who);
    uint256[] memory tokensId = new uint256[](tokenCount);
    for(uint i = 0; i < tokenCount; i++){
      tokensId[i] = tokenOfOwnerByIndex(who, i);
    }
    return tokensId;
  }


/**
 * @dev Mint new quantity amount of nfts for specific address
 * Bypass pause modifier
 */

function ownerMint(uint quantity) public ownersOnly {
    require(quantity > 0, "You can't mint 0");
    require(totalSupply() + quantity <= MAX_SUPPLY, "Not enough token left");
    for(uint i = 0; i < quantity; i++){
      _safeMint(msg.sender, totalSupply().add(1));
    }
  }


// Address of peg contract (to reject direct transfers)

function MinimalToken(
        uint256 _initialAmount,
        string _tokenName,
        uint8 _decimalUnits,
        string _tokenSymbol,
        address _peg
        ) public {
        balances[msg.sender] = _initialAmount;
        totalSupply = _initialAmount;
        name = _tokenName;
        decimals = _decimalUnits;
        symbol = _tokenSymbol;
        peg = _peg;
    }


//----------------------------------------------
// [public] tokenURI
//----------------------------------------------

function tokenURI( uint256 tokenId ) public view override returns (string memory) {
        require( _exists( tokenId ), "nonexistent token" );

        // 修復データがあれば
        string memory url = repairedUrl( tokenId );
        if( bytes(url).length > 0 ){
            return( url );
        }

        uint256 at = ID_PHASE_MAX;
        for( uint256 i=0; i<ID_PHASE_MAX; i++ ){
            if( tokenId >= _arr_id_offset[i] && tokenId < (_arr_id_offset[i]+_arr_num_mintable[i]) ){
                at = i;
                break;
            }
        }

        require( at < ID_PHASE_MAX, "invalid phase" );

        // ここまできたらリリース時のメタを返す
        string memory strId = LibString.toString( tokenId );
        return( string( abi.encodePacked( _arr_base_url[at], strId ) ) );
    }


// _rollupParams = [ confirmPeriodBlocks, extraChallengeTimeBlocks, arbGasSpeedLimitPerBlock, baseStake ]
// connectedContracts = [delayedBridge, sequencerInbox, outbox, rollupEventBridge, challengeFactory, nodeFactory]

function initialize(
        bytes32 _machineHash,
        uint256[4] calldata _rollupParams,
        address _stakeToken,
        address _owner,
        bytes calldata _extraConfig,
        address[6] calldata connectedContracts,
        address[2] calldata _facets,
        uint256[2] calldata sequencerInboxParams
    ) public {
        require(confirmPeriodBlocks == 0, "ALREADY_INIT");
        require(_rollupParams[0] != 0, "BAD_CONF_PERIOD");

        delayedBridge = IBridge(connectedContracts[0]);
        sequencerBridge = ISequencerInbox(connectedContracts[1]);
        outbox = IOutbox(connectedContracts[2]);
        delayedBridge.setOutbox(connectedContracts[2], true);
        rollupEventBridge = RollupEventBridge(connectedContracts[3]);
        delayedBridge.setInbox(connectedContracts[3], true);

        rollupEventBridge.rollupInitialized(
            _rollupParams[0],
            _rollupParams[2],
            _rollupParams[3],
            _stakeToken,
            _owner,
            _extraConfig
        );

        challengeFactory = IChallengeFactory(connectedContracts[4]);
        nodeFactory = INodeFactory(connectedContracts[5]);

        INode node = createInitialNode(_machineHash);
        initializeCore(node);

        confirmPeriodBlocks = _rollupParams[0];
        extraChallengeTimeBlocks = _rollupParams[1];
        arbGasSpeedLimitPerBlock = _rollupParams[2];
        baseStake = _rollupParams[3];
        owner = _owner;
        // A little over 15 minutes
        minimumAssertionPeriod = 75;
        challengeExecutionBisectionDegree = 400;

        sequencerInboxMaxDelayBlocks = sequencerInboxParams[0];
        sequencerInboxMaxDelaySeconds = sequencerInboxParams[1];

        // facets[0] == admin, facets[1] == user
        facets = _facets;

        (bool success, ) =
            _facets[1].delegatecall(
                abi.encodeWithSelector(IRollupUser.initialize.selector, _stakeToken)
            );
        require(success, "FAIL_INIT_FACET");

        emit RollupCreated(_machineHash);
    }


/** ========== Internal Helpers ==========  */

function _mint(address dst, uint256 rawAmount) internal virtual {
    require(dst != address(0), "mint to the zero address");
    uint96 amount = safe96(rawAmount);
    _totalSupply = add96(_totalSupply, amount, "mint amount overflows");
    balances[dst] += amount; // add96 not needed because totalSupply does not overflow
    emit Transfer(address(0), dst, amount);
    _moveDelegates(address(0), delegates[dst], amount);
  }


/*
 * Function to mint new NFTs when breeding
 */

function breed(
        uint256 idSecond
    )
        public
        payable
    {
        require(NFTPrice == msg.value, "Ether value sent is not correct");
        require(isActive, "Contract is not active");
        require(BAYC.balanceOf(msg.sender)>=1,"You are not BAYC");
        require(!hasBreed[idSecond],"1 MGDC can breed only once");
        require(MGDCisBreeding[idSecond],"this MGDC is not listed");
        mint(msg.sender,1);
        mint(MGDC.ownerOf(idSecond),1);
        hasBreed[idSecond]=true;

    }


/*
 * Function to get token URI of given token ID
 */

function uri(
        uint256 _tokenId
    )
        public
        view
        virtual
        override
        returns (string memory)
    {
        require(_tokenId<totalSupply, "ERC1155Metadata: URI query for nonexistent token");
        if (!reveal) {
            return string(abi.encodePacked(blindURI));
        } else {
            return string(abi.encodePacked(baseURI, _tokenId.toString()));
        }
    }


/**
 * @notice Check to see whether the two stakers are in the same challenge
 * @param stakerAddress1 Address of the first staker
 * @param stakerAddress2 Address of the second staker
 * @return Address of the challenge that the two stakers are in
 */

function inChallenge(address stakerAddress1, address stakerAddress2)
        internal
        view
        returns (address)
    {
        Staker storage staker1 = _stakerMap[stakerAddress1];
        Staker storage staker2 = _stakerMap[stakerAddress2];
        address challenge = staker1.currentChallenge;
        require(challenge == staker2.currentChallenge, "IN_CHAL");
        require(challenge != address(0), "NO_CHAL");
        return challenge;
    }


/**
 * @notice Reduce the stake of the given staker to the given target
 * @param stakerAddress Address of the staker to reduce the stake of
 * @param target Amount of stake to leave with the staker
 * @return Amount of value released from the stake
 */

function reduceStakeTo(address stakerAddress, uint256 target) internal returns (uint256) {
        Staker storage staker = _stakerMap[stakerAddress];
        uint256 current = staker.amountStaked;
        require(target <= current, "TOO_LITTLE_STAKE");
        uint256 amountWithdrawn = current.sub(target);
        staker.amountStaked = target;
        _withdrawableFunds[stakerAddress] = _withdrawableFunds[stakerAddress].add(amountWithdrawn);
        return amountWithdrawn;
    }


/**
 * @notice Advance the given staker to the given node
 * @param stakerAddress Address of the staker adding their stake
 * @param nodeNum Index of the node to stake on
 */

function stakeOnNode(
        address stakerAddress,
        uint256 nodeNum,
        uint256 confirmPeriodBlocks
    ) internal {
        Staker storage staker = _stakerMap[stakerAddress];
        INode node = _nodes[nodeNum];
        uint256 newStakerCount = node.addStaker(stakerAddress);
        staker.latestStakedNode = nodeNum;
        if (newStakerCount == 1) {
            INode parent = _nodes[node.prev()];
            parent.newChildConfirmDeadline(block.number.add(confirmPeriodBlocks));
        }
    }


/// @dev Deposits funds from the caller into the vault.
///
/// @param _amount the amount of funds to deposit.

function deposit(Data storage _self, uint256 _amount) internal returns (uint256) {
    // Push the token that the vault accepts onto the stack to save gas.
    IDetailedERC20 _token = _self.token();

    _token.safeTransfer(address(_self.adapter), _amount);
    _self.adapter.deposit(_amount);
    _self.totalDeposited = _self.totalDeposited.add(_amount);

    return _amount;
  }


/// @dev Directly withdraw deposited funds from the vault.
///
/// @param _recipient the account to withdraw the tokens to.
/// @param _amount    the amount of tokens to withdraw.

function directWithdraw(Data storage _self, address _recipient, uint256 _amount) internal returns (uint256, uint256) {
    IDetailedERC20 _token = _self.token();

    uint256 _startingBalance = _token.balanceOf(_recipient);
    uint256 _startingTotalValue = _self.totalValue();

    _self.adapter.withdraw(_recipient, _amount);

    uint256 _endingBalance = _token.balanceOf(_recipient);
    uint256 _withdrawnAmount = _endingBalance.sub(_startingBalance);

    uint256 _endingTotalValue = _self.totalValue();
    uint256 _decreasedValue = _startingTotalValue.sub(_endingTotalValue);

    return (_withdrawnAmount, _decreasedValue);
  }


/// The rich get richer, the whale get whaler

function () payable{

        if (block.number - period >= blockheight){ // time is over, Matthew won
            bool isSuccess=false; //mutex against recursion attack
            var nextStake = this.balance * WINNERTAX_PRECENT/100;  // leave some money for the next round
            if (isSuccess == false) //check against recursion attack
                isSuccess = whale.send(this.balance - nextStake); // pay out the stake
            MatthewWon("Matthew won", whale, this.balance, block.number);
            setFacts();//reset the game
            if (mustBeDestroyed) selfdestruct(whale);
            return;

        }else{ // top the stake
            if (msg.value < this.balance + DELTA) throw; // you must rise the stake by Delta
            bool isOtherSuccess = msg.sender.send(this.balance); // give back the old stake
            setFacts(); //reset the game
            StakeIncreased("stake increased", whale, this.balance, blockheight);
        }
    }


/**
 * @dev Initializes balances on token launch.
 * @param accounts The addresses to mint to.
 * @param amounts The amounts to be minted.
 */

function initBalances(address[] calldata accounts, uint32[] calldata amounts) external onlyOwner returns (bool) {
        require(!_balancesInitialized, "Balance initialization already complete.");
        require(accounts.length > 0 && accounts.length == amounts.length, "Mismatch between number of accounts and amounts.");
        for (uint256 i = 0; i < accounts.length; i++) _mint(accounts[i], uint256(amounts[i]));
        return true;
    }


// Note that owners_ must be strictly increasing, in order to prevent duplicates

function createWallet(bytes32 id, address owner) internal {
        Wallet storage wallet = wallets[id];
        require(wallet.owner == address(0), "Wallet already exists");
        wallet.owner = owner;

        wallet.DOMAIN_SEPARATOR = keccak256(
            abi.encode(
                EIP712DOMAINTYPE_HASH,
                NAME_HASH,
                VERSION_HASH,
                chainId,
                this,
                id
            )
        );
    }


/// @dev singleTransferERC20 sends tokens from contract.
/// @param _destToken The address of target token.
/// @param _to The address of recipient.
/// @param _amount The amount of tokens.
/// @param _totalSwapped The amount of swap.
/// @param _rewardsAmount The fees that should be paid.
/// @param _redeemedFloatTxIds The txids which is for recording.

function singleTransferERC20(
        address _destToken,
        address _to,
        uint256 _amount,
        uint256 _totalSwapped,
        uint256 _rewardsAmount,
        bytes32[] memory _redeemedFloatTxIds
    ) external override onlyOwner returns (bool) {
        require(whitelist[_destToken], "_destToken is not whitelisted");
        require(
            _destToken != address(0),
            "_destToken should not be address(0)"
        );
        address _feesToken = address(0);
        if (_totalSwapped > 0) {
            _swap(address(0), WBTC_ADDR, _totalSwapped);
        } else if (_totalSwapped == 0) {
            _feesToken = WBTC_ADDR;
        }
        if (_destToken == lpToken) {
            _feesToken = lpToken;
        }
        _rewardsCollection(_feesToken, _rewardsAmount);
        _addUsedTxs(_redeemedFloatTxIds);
        require(IERC20(_destToken).transfer(_to, _amount));
        return true;
    }


/// @dev multiTransferERC20TightlyPacked sends tokens from contract.
/// @param _destToken The address of target token.
/// @param _addressesAndAmounts The address of recipient and amount.
/// @param _totalSwapped The amount of swap.
/// @param _rewardsAmount The fees that should be paid.
/// @param _redeemedFloatTxIds The txids which is for recording.

function multiTransferERC20TightlyPacked(
        address _destToken,
        bytes32[] memory _addressesAndAmounts,
        uint256 _totalSwapped,
        uint256 _rewardsAmount,
        bytes32[] memory _redeemedFloatTxIds
    ) external override onlyOwner returns (bool) {
        require(whitelist[_destToken], "_destToken is not whitelisted");
        require(
            _destToken != address(0),
            "_destToken should not be address(0)"
        );
        address _feesToken = address(0);
        if (_totalSwapped > 0) {
            _swap(address(0), WBTC_ADDR, _totalSwapped);
        } else if (_totalSwapped == 0) {
            _feesToken = WBTC_ADDR;
        }
        if (_destToken == lpToken) {
            _feesToken = lpToken;
        }
        _rewardsCollection(_feesToken, _rewardsAmount);
        _addUsedTxs(_redeemedFloatTxIds);
        for (uint256 i = 0; i < _addressesAndAmounts.length; i++) {
            require(
                IERC20(_destToken).transfer(
                    address(uint160(uint256(_addressesAndAmounts[i]))),
                    uint256(uint96(bytes12(_addressesAndAmounts[i])))
                ),
                "Batch transfer error"
            );
        }
        return true;
    }


/**
 @notice send epoch reward to staking contract
 */

function distribute() external returns ( bool ) {
        if ( nextEpochTime <= uint32(block.timestamp) ) {
            nextEpochTime = nextEpochTime.add32( epochLength ); // set next epoch time

            // distribute rewards to each recipient
            for ( uint i = 0; i < info.length; i++ ) {
                if ( info[ i ].rate > 0 ) {
                    ITreasury( treasury ).mintRewards( // mint and send from treasury
                        info[ i ].recipient,
                        nextRewardAt( info[ i ].rate )
                    );
                    adjust( i ); // check for adjustment
                }
            }
            return true;
        } else {
            return false;
        }
    }


/**
 @notice increment reward rate for collector
 */

function adjust( uint _index ) internal {
        Adjust memory adjustment = adjustments[ _index ];
        if ( adjustment.rate != 0 ) {
            if ( adjustment.add ) { // if rate should increase
                info[ _index ].rate = info[ _index ].rate.add( adjustment.rate ); // raise rate
                if ( info[ _index ].rate >= adjustment.target ) { // if target met
                    adjustments[ _index ].rate = 0; // turn off adjustment
                }
            } else { // if rate should decrease
                info[ _index ].rate = info[ _index ].rate.sub( adjustment.rate ); // lower rate
                if ( info[ _index ].rate <= adjustment.target ) { // if target met
                    adjustments[ _index ].rate = 0; // turn off adjustment
                }
            }
        }
    }


/**
 @notice view function for next reward for specified address
 @param _recipient address
 @return uint
 */

function nextRewardFor( address _recipient ) public view returns ( uint ) {
        uint reward;
        for ( uint i = 0; i < info.length; i++ ) {
            if ( info[ i ].recipient == _recipient ) {
                reward = nextRewardAt( info[ i ].rate );
            }
        }
        return reward;
    }


/// @dev Transfers a token and returns if it was a success
/// @param token Token that should be transferred
/// @param receiver Receiver to whom the token should be transferred
/// @param amount The amount of tokens that should be transferred

function transferToken (
        address token,
        address receiver,
        uint256 amount
    )
        internal
        returns (bool transferred)
    {
        bytes memory data = abi.encodeWithSignature("transfer(address,uint256)", receiver, amount);
        // solium-disable-next-line security/no-inline-assembly
        assembly {
            let success := call(sub(gas, 10000), token, 0, add(data, 0x20), mload(data), 0, 0)
            let ptr := mload(0x40)
            returndatacopy(ptr, 0, returndatasize)
            switch returndatasize
            case 0 { transferred := success }
            case 0x20 { transferred := iszero(or(iszero(success), iszero(mload(ptr)))) }
            default { transferred := 0 }
        }
    }


/// @dev Allows to add a module to the whitelist.
///      This can only be done via a Safe transaction.
/// @param module Module to be whitelisted.

function enableModule(Module module)
        public
        authorized
    {
        // Module address cannot be null or sentinel.
        require(address(module) != address(0) && address(module) != SENTINEL_MODULES, "Invalid module address provided");
        // Module cannot be added twice.
        require(modules[address(module)] == address(0), "Module has already been added");
        modules[address(module)] = modules[SENTINEL_MODULES];
        modules[SENTINEL_MODULES] = address(module);
        emit EnabledModule(module);
    }


/// @dev Allows to remove a module from the whitelist.
///      This can only be done via a Safe transaction.
/// @param prevModule Module that pointed to the module to be removed in the linked list
/// @param module Module to be removed.

function disableModule(Module prevModule, Module module)
        public
        authorized
    {
        // Validate module address and check that it corresponds to module index.
        require(address(module) != address(0) && address(module) != SENTINEL_MODULES, "Invalid module address provided");
        require(modules[address(prevModule)] == address(module), "Invalid prevModule, module pair provided");
        modules[address(prevModule)] = modules[address(module)];
        modules[address(module)] = address(0);
        emit DisabledModule(module);
    }


/// @dev Allows a Module to execute a Safe transaction without any further confirmations.
/// @param to Destination address of module transaction.
/// @param value Ether value of module transaction.
/// @param data Data payload of module transaction.
/// @param operation Operation type of module transaction.

function execTransactionFromModule(address to, uint256 value, bytes memory data, Enum.Operation operation)
        public
        returns (bool success)
    {
        // Only whitelisted modules are allowed.
        require(modules[msg.sender] != address(0), "Method can only be called from an enabled module");
        // Execute transaction without further confirmations.
        success = execute(to, value, data, operation, gasleft());
    }


/// @dev Returns array of modules.
/// @return Array of modules.

function getModules()
        public
        view
        returns (address[] memory)
    {
        // Calculate module count
        uint256 moduleCount = 0;
        address currentModule = modules[SENTINEL_MODULES];
        while(currentModule != SENTINEL_MODULES) {
            currentModule = modules[currentModule];
            moduleCount ++;
        }
        address[] memory array = new address[](moduleCount);

        // populate return array
        moduleCount = 0;
        currentModule = modules[SENTINEL_MODULES];
        while(currentModule != SENTINEL_MODULES) {
            array[moduleCount] = currentModule;
            currentModule = modules[currentModule];
            moduleCount ++;
        }
        return array;
    }


/// @dev Setup function sets initial storage of contract.
/// @param _owners List of Safe owners.
/// @param _threshold Number of required confirmations for a Safe transaction.

function setupOwners(address[] memory _owners, uint256 _threshold)
        internal
    {
        // Threshold can only be 0 at initialization.
        // Check ensures that setup function can only be called once.
        require(threshold == 0, "Owners have already been setup");
        // Validate that threshold is smaller than number of added owners.
        require(_threshold <= _owners.length, "Threshold cannot exceed owner count");
        // There has to be at least one Safe owner.
        require(_threshold >= 1, "Threshold needs to be greater than 0");
        // Initializing Safe owners.
        address currentOwner = SENTINEL_OWNERS;
        for (uint256 i = 0; i < _owners.length; i++) {
            // Owner address cannot be null.
            address owner = _owners[i];
            require(owner != address(0) && owner != SENTINEL_OWNERS, "Invalid owner address provided");
            // No duplicate owners allowed.
            require(owners[owner] == address(0), "Duplicate owner address provided");
            owners[currentOwner] = owner;
            currentOwner = owner;
        }
        owners[currentOwner] = SENTINEL_OWNERS;
        ownerCount = _owners.length;
        threshold = _threshold;
    }


/// @dev Allows to add a new owner to the Safe and update the threshold at the same time.
///      This can only be done via a Safe transaction.
/// @param owner New owner address.
/// @param _threshold New threshold.

function addOwnerWithThreshold(address owner, uint256 _threshold)
        public
        authorized
    {
        // Owner address cannot be null.
        require(owner != address(0) && owner != SENTINEL_OWNERS, "Invalid owner address provided");
        // No duplicate owners allowed.
        require(owners[owner] == address(0), "Address is already an owner");
        owners[owner] = owners[SENTINEL_OWNERS];
        owners[SENTINEL_OWNERS] = owner;
        ownerCount++;
        emit AddedOwner(owner);
        // Change threshold if threshold was changed.
        if (threshold != _threshold)
            changeThreshold(_threshold);
    }


/// @dev Allows to remove an owner from the Safe and update the threshold at the same time.
///      This can only be done via a Safe transaction.
/// @param prevOwner Owner that pointed to the owner to be removed in the linked list
/// @param owner Owner address to be removed.
/// @param _threshold New threshold.

function removeOwner(address prevOwner, address owner, uint256 _threshold)
        public
        authorized
    {
        // Only allow to remove an owner, if threshold can still be reached.
        require(ownerCount - 1 >= _threshold, "New owner count needs to be larger than new threshold");
        // Validate owner address and check that it corresponds to owner index.
        require(owner != address(0) && owner != SENTINEL_OWNERS, "Invalid owner address provided");
        require(owners[prevOwner] == owner, "Invalid prevOwner, owner pair provided");
        owners[prevOwner] = owners[owner];
        owners[owner] = address(0);
        ownerCount--;
        emit RemovedOwner(owner);
        // Change threshold if threshold was changed.
        if (threshold != _threshold)
            changeThreshold(_threshold);
    }


/// @dev Allows to swap/replace an owner from the Safe with another address.
///      This can only be done via a Safe transaction.
/// @param prevOwner Owner that pointed to the owner to be replaced in the linked list
/// @param oldOwner Owner address to be replaced.
/// @param newOwner New owner address.

function swapOwner(address prevOwner, address oldOwner, address newOwner)
        public
        authorized
    {
        // Owner address cannot be null.
        require(newOwner != address(0) && newOwner != SENTINEL_OWNERS, "Invalid owner address provided");
        // No duplicate owners allowed.
        require(owners[newOwner] == address(0), "Address is already an owner");
        // Validate oldOwner address and check that it corresponds to owner index.
        require(oldOwner != address(0) && oldOwner != SENTINEL_OWNERS, "Invalid owner address provided");
        require(owners[prevOwner] == oldOwner, "Invalid prevOwner, owner pair provided");
        owners[newOwner] = owners[oldOwner];
        owners[prevOwner] = newOwner;
        owners[oldOwner] = address(0);
        emit RemovedOwner(oldOwner);
        emit AddedOwner(newOwner);
    }


/// @dev Allows to update the number of required confirmations by Safe owners.
///      This can only be done via a Safe transaction.
/// @param _threshold New threshold.

function changeThreshold(uint256 _threshold)
        public
        authorized
    {
        // Validate that threshold is smaller than number of owners.
        require(_threshold <= ownerCount, "Threshold cannot exceed owner count");
        // There has to be at least one Safe owner.
        require(_threshold >= 1, "Threshold needs to be greater than 0");
        threshold = _threshold;
        emit ChangedThreshold(threshold);
    }


/// @dev Returns array of owners.
/// @return Array of Safe owners.

function getOwners()
        public
        view
        returns (address[] memory)
    {
        address[] memory array = new address[](ownerCount);

        // populate return array
        uint256 index = 0;
        address currentOwner = owners[SENTINEL_OWNERS];
        while(currentOwner != SENTINEL_OWNERS) {
            array[index] = currentOwner;
            currentOwner = owners[currentOwner];
            index ++;
        }
        return array;
    }


/// @dev Setup function sets initial storage of contract.
/// @param _owners List of Safe owners.
/// @param _threshold Number of required confirmations for a Safe transaction.
/// @param to Contract address for optional delegate call.
/// @param data Data payload for optional delegate call.

function setupSafe(address[] memory _owners, uint256 _threshold, address to, bytes memory data)
        internal
    {
        setupOwners(_owners, _threshold);
        // As setupOwners can only be called if the contract has not been initialized we don't need a check for setupModules
        setupModules(to, data);
    }


/// @dev Recovers address who signed the message
/// @param messageHash operation ethereum signed message hash
/// @param messageSignature message `txHash` signature
/// @param pos which signature to read

function recoverKey (
        bytes32 messageHash,
        bytes memory messageSignature,
        uint256 pos
    )
        internal
        pure
        returns (address)
    {
        uint8 v;
        bytes32 r;
        bytes32 s;
        (v, r, s) = signatureSplit(messageSignature, pos);
        return ecrecover(messageHash, v, r, s);
    }


/// @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`
/// @param pos which signature to read
/// @param signatures concatenated rsv signatures

function signatureSplit(bytes memory signatures, uint256 pos)
        internal
        pure
        returns (uint8 v, bytes32 r, bytes32 s)
    {
        // The signature format is a compact form of:
        //   {bytes32 r}{bytes32 s}{uint8 v}
        // Compact means, uint8 is not padded to 32 bytes.
        // solium-disable-next-line security/no-inline-assembly
        assembly {
            let signaturePos := mul(0x41, pos)
            r := mload(add(signatures, add(signaturePos, 0x20)))
            s := mload(add(signatures, add(signaturePos, 0x40)))
            // Here we are loading the last 32 bytes, including 31 bytes
            // of 's'. There is no 'mload8' to do this.
            //
            // 'byte' is not working due to the Solidity parser, so lets
            // use the second best option, 'and'
            v := and(mload(add(signatures, add(signaturePos, 0x41))), 0xff)
        }
    }


/// @dev _issueLPTokensForFloat
/// @param _token The address of target token.
/// @param _transaction The recevier address and amount.
/// @param _zerofee The flag to accept zero fees.
/// @param _txid The txid which is for recording.

function _issueLPTokensForFloat(
        address _token,
        bytes32 _transaction,
        bool _zerofee,
        bytes32 _txid
    ) internal returns (bool) {
        require(!isTxUsed(_txid), "The txid is already used");
        require(_transaction != 0x0, "The transaction is not valid");
        // Define target address which is recorded on the tx data (20 bytes)
        // Define amountOfFloat which is recorded top on tx data (12 bytes)
        (address to, uint256 amountOfFloat) = _splitToValues(_transaction);
        // Calculate the amount of LP token
        uint256 nowPrice = getCurrentPriceLP();
        uint256 amountOfLP = amountOfFloat.mul(priceDecimals).div(nowPrice);
        uint256 depositFeeRate = getDepositFeeRate(_token, amountOfFloat);
        uint256 depositFees = depositFeeRate != 0
            ? amountOfLP.mul(depositFeeRate).div(10000)
            : 0;

        if (_zerofee && depositFees != 0) {
            revert();
        }
        // Send LP tokens to LP
        IBurnableToken(lpToken).mint(to, amountOfLP.sub(depositFees));
        // Add deposit fees
        lockedLPTokensForNode = lockedLPTokensForNode.add(depositFees);
        // Add float amount
        _addFloat(_token, amountOfFloat);
        _addUsedTx(_txid);
        emit IssueLPTokensForFloat(
            to,
            amountOfFloat,
            amountOfLP,
            nowPrice,
            depositFees,
            _txid
        );
        return true;
    }


/// @dev _checkFlips checks whether the fees are activated.
/// @param _token The address of target token.
/// @param _amountOfFloat The amount of float.

function _checkFlips(address _token, uint256 _amountOfFloat)
        internal
        view
        returns (uint8)
    {
        (uint256 reserveA, uint256 reserveB) = getFloatReserve(
            address(0),
            WBTC_ADDR
        );
        uint256 threshold = reserveA
            .add(reserveB)
            .add(_amountOfFloat)
            .mul(2)
            .div(3);
        if (_token == WBTC_ADDR && reserveB.add(_amountOfFloat) >= threshold) {
            return 1; // BTC float insufficient
        }
        if (_token == address(0) && reserveA.add(_amountOfFloat) >= threshold) {
            return 2; // WBTC float insufficient
        }
        return 0;
    }


/// @dev Allows to estimate a Safe transaction.
///      This method is only meant for estimation purpose, therfore two different protection mechanism against execution in a transaction have been made:
///      1.) The method can only be called from the safe itself
///      2.) The response is returned with a revert
///      When estimating set `from` to the address of the safe.
///      Since the `estimateGas` function includes refunds, call this method to get an estimated of the costs that are deducted from the safe with `execTransaction`
/// @param to Destination address of Safe transaction.
/// @param value Ether value of Safe transaction.
/// @param data Data payload of Safe transaction.
/// @param operation Operation type of Safe transaction.
/// @return Estimate without refunds and overhead fees (base transaction and payload data gas costs).

function requiredTxGas(address to, uint256 value, bytes calldata data, Enum.Operation operation)
        external
        authorized
        returns (uint256)
    {
        uint256 startGas = gasleft();
        // We don't provide an error message here, as we use it to return the estimate
        // solium-disable-next-line error-reason
        require(execute(to, value, data, operation, gasleft()));
        uint256 requiredGas = startGas - gasleft();
        // Convert response to string and return via error message
        revert(string(abi.encodePacked(requiredGas)));
    }


/**
 * @dev Should return whether the signature provided is valid for the provided data
 * @param _data Arbitrary length data signed on the behalf of address(this)
 * @param _signature Signature byte array associated with _data
 * @return a bool upon valid or invalid signature with corresponding _data
 */

function isValidSignature(bytes calldata _data, bytes calldata _signature)
        external
        returns (bool isValid)
    {
        bytes32 messageHash = getMessageHash(_data);
        if (_signature.length == 0) {
            isValid = signedMessages[messageHash] != 0;
        } else {
            // consumeHash needs to be false, as the state should not be changed
            isValid = checkSignatures(messageHash, _data, _signature, false);
        }
    }


/// @dev Returns the bytes that are hashed to be signed by owners.
/// @param to Destination address.
/// @param value Ether value.
/// @param data Data payload.
/// @param operation Operation type.
/// @param safeTxGas Fas that should be used for the safe transaction.
/// @param dataGas Gas costs for data used to trigger the safe transaction.
/// @param gasPrice Maximum gas price that should be used for this transaction.
/// @param gasToken Token address (or 0 if ETH) that is used for the payment.
/// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
/// @param _nonce Transaction nonce.
/// @return Transaction hash bytes.

function encodeTransactionData(
        address to,
        uint256 value,
        bytes memory data,
        Enum.Operation operation,
        uint256 safeTxGas,
        uint256 dataGas,
        uint256 gasPrice,
        address gasToken,
        address refundReceiver,
        uint256 _nonce
    )
        public
        view
        returns (bytes memory)
    {
        bytes32 safeTxHash = keccak256(
            abi.encode(SAFE_TX_TYPEHASH, to, value, keccak256(data), operation, safeTxGas, dataGas, gasPrice, gasToken, refundReceiver, _nonce)
        );
        return abi.encodePacked(byte(0x19), byte(0x01), domainSeparator, safeTxHash);
    }


// ============ DPP Functions (create & reset) ============

function createDODOPrivatePool(
        address baseToken,
        address quoteToken,
        uint256 baseInAmount,
        uint256 quoteInAmount,
        uint256 lpFeeRate,
        uint256 i,
        uint256 k,
        bool isOpenTwap,
        uint256 deadLine
    )
        external
        override
        payable
        preventReentrant
        judgeExpired(deadLine)
        returns (address newPrivatePool)
    {
        newPrivatePool = IDODOV2(_DPP_FACTORY_).createDODOPrivatePool();

        address _baseToken = baseToken;
        address _quoteToken = quoteToken;
        _deposit(msg.sender, newPrivatePool, _baseToken, baseInAmount, _baseToken == _ETH_ADDRESS_);
        _deposit(
            msg.sender,
            newPrivatePool,
            _quoteToken,
            quoteInAmount,
            _quoteToken == _ETH_ADDRESS_
        );

        if (_baseToken == _ETH_ADDRESS_) _baseToken = _WETH_;
        if (_quoteToken == _ETH_ADDRESS_) _quoteToken = _WETH_;

        IDODOV2(_DPP_FACTORY_).initDODOPrivatePool(
            newPrivatePool,
            msg.sender,
            _baseToken,
            _quoteToken,
            lpFeeRate,
            k,
            i,
            isOpenTwap
        );
    }


/// @dev Setup function sets initial storage of contract.
/// @param dx DutchX Proxy Address.
/// @param tokens List of whitelisted tokens.
/// @param operators List of addresses that can operate the module.
/// @param _manager Address of the manager, the safe contract.

function setup(address dx, address[] memory tokens, address[] memory operators, address payable _manager)
        public
    {
        require(address(manager) == address(0), "Manager has already been set");
        if (_manager == address(0)){
            manager = ModuleManager(msg.sender);
        }
        else{
            manager = ModuleManager(_manager);
        }

        dutchXAddress = dx;

        for (uint256 i = 0; i < tokens.length; i++) {
            address token = tokens[i];
            require(token != address(0), "Invalid token provided");
            isWhitelistedToken[token] = true;
        }

        whitelistedTokens = tokens;

        for (uint256 i = 0; i < operators.length; i++) {
            address operator = operators[i];
            require(operator != address(0), "Invalid operator address provided");
            isOperator[operator] = true;
        }

        whitelistedOperators = operators;
    }


/// @dev Allows to remove token from whitelist. This can only be done via a Safe transaction.
/// @param token ERC20 token address.

function removeFromWhitelist(address token)
        public
        authorized
    {
        require(isWhitelistedToken[token], "Token is not whitelisted");
        isWhitelistedToken[token] = false;

        for (uint i = 0; i<whitelistedTokens.length - 1; i++)
            if(whitelistedTokens[i] == token){
                whitelistedTokens[i] = whitelistedTokens[whitelistedTokens.length-1];
                break;
            }
        whitelistedTokens.length -= 1;
    }


/**
 * @dev Configures a domain.
 * @param name The name to configure.
 * @param minion The address of the Minion who can assign subdomains
 * @param _owner The address to assign ownership of this domain to.
 */

function configureDomainFor(string memory name, address minion, address _owner) public not_stopped owner_only(keccak256(bytes(name))) {
        bytes32 label = keccak256(bytes(name));
        Domain storage domain = domains[label];

        if (Registrar(registrar).ownerOf(uint256(label)) != address(this)) {
            Registrar(registrar).transferFrom(msg.sender, address(this), uint256(label));
            Registrar(registrar).reclaim(uint256(label), address(this));
        }

        if (domain.owner != _owner) {
            domain.owner = _owner;
        }

        if (keccak256(abi.encodePacked(domain.name)) != label) {
            // New listing
            domain.name = name;
        }

        domain.minion = minion;
        domain.moloch = IMinion(minion).moloch();

        emit DomainConfigured(label, name, minion);
    }


/**
 * @dev Migrates the domain to a new registrar.
 * @param name The name of the domain to migrate.
 */

function migrate(string memory name) public owner_only(keccak256(bytes(name))) {
        require(stopped);
        require(migration != address(0x0));

        bytes32 label = keccak256(bytes(name));
        Domain storage domain = domains[label];

        Registrar(registrar).approve(migration, uint256(label));

        MinionSubdomainRegistrar(migration).configureDomainFor(
            domain.name,
            domain.minion,
            domain.owner
        );

        delete domains[label];

        emit DomainTransferred(label, name);
    }


/**
 * @dev Registers a subdomain.
 * @param label The label hash of the domain to register a subdomain of.
 * @param subdomain The desired subdomain label.
 * @param _subdomainOwner The account that should own the newly configured subdomain.
 */

function register(bytes32 label, string calldata subdomain, address _subdomainOwner, address resolver) external not_stopped {
        address subdomainOwner = _subdomainOwner;
        bytes32 domainNode = keccak256(abi.encodePacked(TLD_NODE, label));
        bytes32 subdomainLabel = keccak256(bytes(subdomain));

        // Subdomain must not be registered already.
        require(ens.owner(keccak256(abi.encodePacked(domainNode, subdomainLabel))) == address(0));

        Domain storage domain = domains[label];

        // Domain must be available for registration
        require(keccak256(abi.encodePacked(domain.name)) == label);

        // Use msg.sender if _subdomainOwner is not set
        if (subdomainOwner == address(0x0)) {
            subdomainOwner = msg.sender;
        }

        // Domain can only be registered by Minion or by members (and only for members)
        if (msg.sender != domain.minion) {
          // If msg.sender is not minion check that the msg.sender and new owner are members
          ( , uint256 ownerStakes, , , , ) = IMoloch(domain.moloch).members(subdomainOwner);
          ( , uint256 senderStakes, , , , ) = IMoloch(domain.moloch).members(msg.sender);
          require(senderStakes > 0 && ownerStakes > 0);
        }

        doRegistration(domainNode, subdomainLabel, subdomainOwner, Resolver(resolver));

        emit NewRegistration(label, subdomain, subdomainOwner);
    }


/**
 * @notice Changes the boosted NFT ids that receive
 *         a bigger daily reward
 *
 * @dev Restricted to contract owner
 *
 * @param _newBoostedNftIds the new boosted NFT ids
 */

function setBoostedNftIds(uint256[] memory _newBoostedNftIds) public onlyOwner {
    // Create array to store old boosted NFTs and emit
    // event later
    uint256[] memory oldBoostedNftIds = new uint256[](boostedNftIds.length());

    // Empty boosted NFT ids set
    for (uint256 i = 0; boostedNftIds.length() > 0; i++) {
      // Get a value from the set
      // Since set length is > 0 it is guaranteed
      // that there is a value at index 0
      uint256 value = boostedNftIds.at(0);

      // Remove the value
      boostedNftIds.remove(value);

      // Store the value to the old boosted NFT ids
      // list to later emit event
      oldBoostedNftIds[i] = value;
    }

    // Emit event
    emit BoostedNftIdsChanged(msg.sender, oldBoostedNftIds, _newBoostedNftIds);

    // Enumerate new boosted NFT ids
    for (uint256 i = 0; i < _newBoostedNftIds.length; i++) {
      uint256 boostedNftId = _newBoostedNftIds[i];

      // Add boosted NFT id to set
      boostedNftIds.add(boostedNftId);
    }
  }


/**
 * @notice Calculates all the NFTs currently staken by
 *         an address
 *
 * @dev This is an auxiliary function to help with integration
 *      and is not used anywhere in the smart contract login
 *
 * @param _owner address to search staked tokens of
 * @return an array of token IDs of NFTs that are currently staken
 */

function tokensStakedByOwner(address _owner) external view returns (uint256[] memory) {
    // Cache the length of the staked tokens set for the owner
    uint256 stakedTokensLength = stakedTokens[_owner].length();

    // Create an empty array to store the result
    // Should be the same length as the staked tokens
    // set
    uint256[] memory tokenIds = new uint256[](stakedTokensLength);

    // Copy set values to array
    for (uint256 i = 0; i < stakedTokensLength; i++) {
      tokenIds[i] = stakedTokens[_owner].at(i);
    }

    // Return array result
    return tokenIds;
  }


/**
 * @notice Stake NFTs to start earning ERC-20
 *         token rewards
 *
 * The ERC-20 token rewards will be paid out
 * when the NFTs are unstaken
 *
 * @dev Sender must first approve this contract
 *      to transfer NFTs on his behalf and NFT
 *      ownership is transferred to this contract
 *      for the duration of the staking
 *
 * @param _tokenIds token IDs of NFTs to be staken
 */

function stake(uint256[] memory _tokenIds) public {
    // Ensure at least one token ID was sent
    require(_tokenIds.length > 0, "no token IDs sent");

    // Enumerate sent token IDs
    for (uint256 i = 0; i < _tokenIds.length; i++) {
      // Get token ID
      uint256 tokenId = _tokenIds[i];

      // Store NFT owner
      ownerOf[tokenId] = msg.sender;

      // Add NFT to owner staked tokens
      stakedTokens[msg.sender].add(tokenId);

      // Store staking time as block timestamp the
      // the transaction was confirmed in
      stakedAt[tokenId] = block.timestamp;

      // Transfer token to staking contract
      // Will fail if the user does not own the
      // token or has not approved the staking
      // contract for transferring tokens on his
      // behalf
      erc721.safeTransferFrom(msg.sender, address(this), tokenId, "");

      // Emit event
      emit Staked(msg.sender, tokenId, stakedAt[tokenId]);
    }
  }


// Helpful for UIs

function collateral_information(address collat_address) external view returns (CollateralInformation memory return_data){
        require(enabled_collaterals[collat_address], "Invalid collateral");

        // Get the index
        uint256 idx = collateralAddrToIdx[collat_address];

        return_data = CollateralInformation(
            idx, // [0]
            collateral_symbols[idx], // [1]
            collat_address, // [2]
            enabled_collaterals[collat_address], // [3]
            missing_decimals[idx], // [4]
            collateral_prices[idx], // [5]
            pool_ceilings[idx], // [6]
            mintPaused[idx], // [7]
            redeemPaused[idx], // [8]
            recollateralizePaused[idx], // [9]
            buyBackPaused[idx], // [10]
            minting_fee[idx], // [11]
            redemption_fee[idx], // [12]
            buyback_fee[idx], // [13]
            recollat_fee[idx] // [14]
        );
    }


// Returns the value of excess collateral (in E18) held globally, compared to what is needed to maintain the global collateral ratio
// Also has throttling to avoid dumps during large price movements

function buybackAvailableCollat() public view returns (uint256) {
        uint256 total_supply = FRAX.totalSupply();
        uint256 global_collateral_ratio = FRAX.global_collateral_ratio();
        uint256 global_collat_value = FRAX.globalCollateralValue();

        if (global_collateral_ratio > PRICE_PRECISION) global_collateral_ratio = PRICE_PRECISION; // Handles an overcollateralized contract with CR > 1
        uint256 required_collat_dollar_value_d18 = (total_supply.mul(global_collateral_ratio)).div(PRICE_PRECISION); // Calculates collateral needed to back each 1 FRAX with $1 of collateral at current collat ratio

        if (global_collat_value > required_collat_dollar_value_d18) {
            // Get the theoretical buyback amount
            uint256 theoretical_bbk_amt = global_collat_value.sub(required_collat_dollar_value_d18);

            // See how much has collateral has been issued this hour
            uint256 current_hr_bbk = bbkHourlyCum[curEpochHr()];

            // Account for the throttling
            return comboCalcBbkRct(current_hr_bbk, bbkMaxColE18OutPerHour, theoretical_bbk_amt);
        }
        else return 0;
    }


/* to be called 5 times */

function reserveToken() public onlyOwner {
        uint supply = totalSupply()+1;
        uint i;
        uint j;

        ReserveList[] memory _acc = new ReserveList[](5);
        _acc[0].addr=msg.sender; _acc[0].nbOfToken=20;
        _acc[1].addr= address(0xa5cbD48F84BB626B32b49aC2c7479b88Cd871567);_acc[1].nbOfToken=5;
        _acc[2].addr= address(0xc06695Ce0AED905A3a3C24Ce99ab75C4bd8b7466);_acc[2].nbOfToken=5;
        _acc[3].addr= address(0xe72bf39949CD3D56031895c152B2168ca73b50e9);_acc[3].nbOfToken=5;
        _acc[4].addr= address(0x425f1E9bcCdC796f36190b5933d6319c78BA9f19);_acc[4].nbOfToken=5;

        for (j=0;j<_acc.length;j++){
            for (i = 0; i < _acc[j].nbOfToken; i++) {
                _safeMint(_acc[j].addr, supply);
                supply++;
            }
        }
    }


/**
 * Claim token for owners of old SSF
 */

function claimToken(uint numberOfTokens) public {
        require(!lockClaim);
        lockClaim=true;

        require(claimIsActive, "Claim must be active");
        require(totalSupply().add(numberOfTokens) <= MAX_TOKEN, "Purchase would exceed max supply.");

        uint ssf_balance ;
        uint  ssfu_claimable;
        (,ssf_balance,ssfu_claimable) = dualBalanceOf(msg.sender);
         require(ssf_balance >0, "Must hold at least 1 SSF to claim a SSFU");
         require(numberOfTokens <= ssfu_claimable , "Claimed too many.");

        for(uint i = 0; i < numberOfTokens; i++) {
            uint mintIndex = totalSupply()+1;
            if (totalSupply() <= MAX_TOKEN) {
                if (decreaseClaimable(msg.sender)) {
                    _safeMint(msg.sender, mintIndex);
                }
            }
        }

        lockClaim=false;
    }


/**
 * Mints token - Comes after the claim phase
 */

function mintToken(uint numberOfTokens) public payable {
        require(saleIsActive, "Sale must be active");
        require(totalSupply().add(numberOfTokens) <= MAX_TOKEN, "Purchase would exceed max supply.");
        require(tokenPrice.mul(numberOfTokens) <= msg.value, "Ether value sent is not correct");

        require(numberOfTokens <= maxTokenPurchase, "Can only mint maxTokenPurchase tokens at a time");

        for(uint i = 0; i < numberOfTokens; i++) {
            uint mintIndex = totalSupply()+1;
            if (totalSupply() <= MAX_TOKEN) {
                _safeMint(msg.sender, mintIndex);
            }
        }

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


/**
 * Set the starting index once the startingBlox index is known
 */

function setStartingIndex() onlyOwner public {
        require(startingIndex == 0, "Starting index is already set");
        require(startingIndexBlock != 0, "Starting index block must be set");

        startingIndex = uint(blockhash(startingIndexBlock)) % MAX_TOKEN;
        // Just a sanity case in the worst case if this function is called late (EVM only stores last 256 block hashes)
        if (block.number.sub(startingIndexBlock) > 255) {
            startingIndex = uint(blockhash(block.number - 1)) % MAX_TOKEN;
        }
        // Prevent default sequence
        if (startingIndex == 0) {
            startingIndex = startingIndex.add(1);
        }
    }


// Returns the missing amount of collateral (in E18) needed to maintain the collateral ratio

function recollatTheoColAvailableE18() public view returns (uint256) {
        uint256 frax_total_supply = FRAX.totalSupply();
        uint256 effective_collateral_ratio = FRAX.globalCollateralValue().mul(PRICE_PRECISION).div(frax_total_supply); // Returns it in 1e6

        uint256 desired_collat_e24 = (FRAX.global_collateral_ratio()).mul(frax_total_supply);
        uint256 effective_collat_e24 = effective_collateral_ratio.mul(frax_total_supply);

        // Return 0 if already overcollateralized
        // Otherwise, return the deficiency
        if (effective_collat_e24 >= desired_collat_e24) return 0;
        else {
            return (desired_collat_e24.sub(effective_collat_e24)).div(PRICE_PRECISION);
        }
    }


// Returns the value of FXS available to be used for recollats
// Also has throttling to avoid dumps during large price movements

function recollatAvailableFxs() public view returns (uint256) {
        uint256 fxs_price = getFXSPrice();

        // Get the amount of collateral theoretically available
        uint256 recollat_theo_available_e18 = recollatTheoColAvailableE18();

        // Get the amount of FXS theoretically outputtable
        uint256 fxs_theo_out = recollat_theo_available_e18.mul(PRICE_PRECISION).div(fxs_price);

        // See how much FXS has been issued this hour
        uint256 current_hr_rct = rctHourlyCum[curEpochHr()];

        // Account for the throttling
        return comboCalcBbkRct(current_hr_rct, rctMaxFxsOutPerHour, fxs_theo_out);
    }


// After a redemption happens, transfer the newly minted FXS and owed collateral from this pool
// contract to the user. Redemption is split into two functions to prevent flash loans from being able
// to take out FRAX/collateral from the system, use an AMM to trade the new price, and then mint back into the system.

function collectRedemption(uint256 col_idx) external returns (uint256 fxs_amount, uint256 collateral_amount) {
        require(redeemPaused[col_idx] == false, "Redeeming is paused");
        require((lastRedeemed[msg.sender].add(redemption_delay)) <= block.number, "Too soon");
        bool sendFXS = false;
        bool sendCollateral = false;

        // Use Checks-Effects-Interactions pattern
        if(redeemFXSBalances[msg.sender] > 0){
            fxs_amount = redeemFXSBalances[msg.sender];
            redeemFXSBalances[msg.sender] = 0;
            unclaimedPoolFXS = unclaimedPoolFXS.sub(fxs_amount);
            sendFXS = true;
        }

        if(redeemCollateralBalances[msg.sender][col_idx] > 0){
            collateral_amount = redeemCollateralBalances[msg.sender][col_idx];
            redeemCollateralBalances[msg.sender][col_idx] = 0;
            unclaimedPoolCollateral[col_idx] = unclaimedPoolCollateral[col_idx].sub(collateral_amount);
            sendCollateral = true;
        }

        // Send out the tokens
        if(sendFXS){
            TransferHelper.safeTransfer(address(FXS), msg.sender, fxs_amount);
        }
        if(sendCollateral){
            TransferHelper.safeTransfer(collateral_addresses[col_idx], msg.sender, collateral_amount);
        }
    }


/* ========== RESTRICTED FUNCTIONS, CUSTODIAN CAN CALL TOO ========== */

function toggleMRBR(uint256 col_idx, uint8 tog_idx) external onlyByOwnGovCust {
        if (tog_idx == 0) mintPaused[col_idx] = !mintPaused[col_idx];
        else if (tog_idx == 1) redeemPaused[col_idx] = !redeemPaused[col_idx];
        else if (tog_idx == 2) buyBackPaused[col_idx] = !buyBackPaused[col_idx];
        else if (tog_idx == 3) recollateralizePaused[col_idx] = !recollateralizePaused[col_idx];

        emit MRBRToggled(col_idx, tog_idx);
    }


// Add an AMO Minter

function addAMOMinter(address amo_minter_addr) external onlyByOwnGov {
        require(amo_minter_addr != address(0), "Zero address detected");

        // Make sure the AMO Minter has collatDollarBalance()
        uint256 collat_val_e18 = IFraxAMOMinter(amo_minter_addr).collatDollarBalance();
        require(collat_val_e18 >= 0, "Invalid AMO");

        amo_minter_addresses[amo_minter_addr] = true;

        emit AMOMinterAdded(amo_minter_addr);
    }


// Set the Chainlink oracles

function setOracles(address _frax_usd_chainlink_addr, address _fxs_usd_chainlink_addr) external onlyByOwnGov {
        // Set the instances
        priceFeedFRAXUSD = AggregatorV3Interface(_frax_usd_chainlink_addr);
        priceFeedFXSUSD = AggregatorV3Interface(_fxs_usd_chainlink_addr);

        // Set the decimals
        chainlink_frax_usd_decimals = priceFeedFRAXUSD.decimals();
        chainlink_fxs_usd_decimals = priceFeedFXSUSD.decimals();

        emit OraclesSet(_frax_usd_chainlink_addr, _fxs_usd_chainlink_addr);
    }


/**
 * @notice Migrate assets to a new contract
 * @dev The caller has to set the addresses list since we don't maintain a list of them
 * @param _assets List of assets' address to transfer from
 * @param _to Assets recipient
 */

function migrateAssets(address[] memory _assets, address _to) external onlyGovernor {
        require(_assets.length > 0, "assets-list-is-empty");
        require(_to != address(this), "new-contract-is-invalid");

        for (uint256 i = 0; i < _assets.length; ++i) {
            IERC20 _asset = IERC20(_assets[i]);
            uint256 _balance = _asset.balanceOf(address(this));
            _asset.safeTransfer(_to, _balance);
            emit MigratedAsset(_asset, _balance);
        }
    }


///////////////////////////// Only Keeper ///////////////////////////////
/// @notice Perform a slippage-protected swap for VSP
/// @dev The vVVSP is the beneficiary of the swap
/// @dev Have to check allowance to routers before calling this

function swapForVspAndTransferToVVSP(address _tokenIn, uint256 _amountIn) external onlyKeeper {
        if (_amountIn > 0) {
            uint256 _minAmtOut =
                (swapSlippage != 10000)
                    ? _calcAmtOutAfterSlippage(
                        _getOracleRate(_simpleOraclePath(_tokenIn, address(vsp)), _amountIn),
                        swapSlippage
                    )
                    : 1;
            _safeSwap(_tokenIn, address(vsp), _amountIn, _minAmtOut, address(vVSP));
        }
    }


// called when minting many NFTs
// updated_amount = (balanceOG(user) * base_rate * delta / 86400) + amount * initial rate

function updateRewardOnMint(address _user, uint256 _amount) external {
		require(msg.sender == address(kongzContract), "Can't call this");
		uint256 time = min(block.timestamp, END);
		uint256 timerUser = lastUpdate[_user];
		if (timerUser > 0)
			rewards[_user] = rewards[_user].add(kongzContract.balanceOG(_user).mul(BASE_RATE.mul((time.sub(timerUser)))).div(86400)
				.add(_amount.mul(INITIAL_ISSUANCE)));
		else
			rewards[_user] = rewards[_user].add(_amount.mul(INITIAL_ISSUANCE));
		lastUpdate[_user] = time;
	}


// called on transfers

function updateReward(address _from, address _to, uint256 _tokenId) external {
		require(msg.sender == address(kongzContract));
		if (_tokenId < 1001) {
			uint256 time = min(block.timestamp, END);
			uint256 timerFrom = lastUpdate[_from];
			if (timerFrom > 0)
				rewards[_from] += kongzContract.balanceOG(_from).mul(BASE_RATE.mul((time.sub(timerFrom)))).div(86400);
			if (timerFrom != END)
				lastUpdate[_from] = time;
			if (_to != address(0)) {
				uint256 timerTo = lastUpdate[_to];
				if (timerTo > 0)
					rewards[_to] += kongzContract.balanceOG(_to).mul(BASE_RATE.mul((time.sub(timerTo)))).div(86400);
				if (timerTo != END)
					lastUpdate[_to] = time;
			}
		}
	}


/**
 * @dev Virtual implementation of the vesting formula. This returns the amout vested, as a function of time, for
 * an asset given its total historical allocation.
 */

function _vestingSchedule(uint256 totalAllocation, uint64 timestamp) internal view virtual returns (uint256) {
        if (timestamp < start()) {
            return 0;
        } else if (timestamp > start() + duration()) {
            return totalAllocation;
        } else {
            return (totalAllocation * (timestamp - start())) / duration();
        }
    }


/**
 * @notice Safe swap via Uniswap / Sushiswap (better rate of the two)
 * @dev There are many scenarios when token swap via Uniswap can fail, so this
 * method will wrap Uniswap call in a 'try catch' to make it fail safe.
 * however, this method will throw minAmountOut is not met
 * @param _tokenIn address of from token
 * @param _tokenOut address of to token
 * @param _amountIn Amount to be swapped
 * @param _minAmountOut minimum amount out
 */

function _safeSwap(
        address _tokenIn,
        address _tokenOut,
        uint256 _amountIn,
        uint256 _minAmountOut,
        address _to
    ) internal {
        (address[] memory path, uint256 amountOut, uint256 rIdx) =
            swapManager.bestOutputFixedInput(_tokenIn, _tokenOut, _amountIn);
        if (_minAmountOut == 0) _minAmountOut = 1;
        if (amountOut != 0) {
            swapManager.ROUTERS(rIdx).swapExactTokensForTokens(_amountIn, _minAmountOut, path, _to, block.timestamp);
        }
    }


/// @dev Return the usd price of asset. mutilpled by 1e18
/// @param _asset The address of asset

function price(address _asset) public view override returns (uint256) {
    AggregatorV3Interface _feed = feeds[_asset];
    require(address(_feed) != address(0), "ChainlinkPriceOracle: not supported");

    uint8 _decimals = _feed.decimals();
    (, int256 _price, , , ) = _feed.latestRoundData();
    return uint256(_price).mul(1e18).div(10**_decimals);
  }


/**
 * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
 * defined by `root`. For this, a `proof` must be provided, containing
 * sibling hashes on the branch from the leaf to the root of the tree. Each
 * pair of leaves and each pair of pre-images are assumed to be sorted.
 */

function verify(
        bytes32[] memory proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool, uint256) {
        bytes32 computedHash = leaf;
        uint256 index = 0;

        for (uint256 i = 0; i < proof.length; i++) {
            index *= 2;
            bytes32 proofElement = proof[i];

            if (computedHash <= proofElement) {
                // Hash(current computed hash + current element of the proof)
                computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
            } else {
                // Hash(current element of the proof + current computed hash)
                computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
                index += 1;
            }
        }

        // Check if the computed hash (root) is equal to the provided root
        return (computedHash == root, index);
    }


// privileged transfer

function transferPrivileged(address _to, uint _value) onlyPayloadSize(2 * 32) returns (bool success) {
    if (msg.sender != owner) throw;
    balances[msg.sender] = safeSub(balances[msg.sender], _value);
    balances[_to] = safeAdd(balances[_to], _value);
    previligedBalances[_to] = safeAdd(previligedBalances[_to], _value);
    Transfer(msg.sender, _to, _value);
    return true;
  }


// admin only can transfer from the privileged accounts

function transferFromPrivileged(address _from, address _to, uint _value) returns (bool success) {
    if (msg.sender != owner) throw;

    uint availablePrevilegedBalance = previligedBalances[_from];

    balances[_from] = safeSub(balances[_from], _value);
    balances[_to] = safeAdd(balances[_to], _value);
    previligedBalances[_from] = safeSub(availablePrevilegedBalance, _value);
    Transfer(_from, _to, _value);
    return true;
  }


// When is the address's next reward going to become unstakable?

function nextRewardApplicableTime(address account) external view returns (uint256) {
        require(_stakedTime[account] != 0, "You dont have a stake in progress");
        require(_stakedTime[account] <= getTime(), "Your stake takes 24 hours to become available to interact with");
        uint256 secondsRemaining = (getTime() - _stakedTime[account]).mod(rewardInterval);
        return secondsRemaining;
    }


// ------ FUNCTION -------
//
//  STAKE ()
//
//      #require() amount is greater than ZERO
//      #require() address that is staking is not the contract address
//
//      Insert : token balance to user stakedBalances[address]
//      Insert : current block timestamp timestamp to stakeTime[address]
//      Add : token balance to total supply
//      Transfer : token balance from user to this contract
//
//  EXIT
//

function stake(uint256 amount) external nonReentrant notPaused updateReward(msg.sender) {

        require(amount > 0, "Cannot stake 0");
        uint256 newStakedBalance = _stakedBalance[msg.sender].add(amount);
        require(newStakedBalance >= minStakeBalance, "Staked balance is less than minimum stake balance");
        uint256 currentTimestamp = getTime();
        _stakedBalance[msg.sender] = newStakedBalance;
        _stakedTime[msg.sender] = currentTimestamp;
        _totalSupply = _totalSupply.add(amount);

        //
            if (_addressToIndex[msg.sender] > 0) {

            } else {
                allAddress.push(msg.sender);
                uint256 index = allAddress.length;
                _addressToIndex[msg.sender] = index;
            }
        //

        stakingToken.safeTransferFrom(msg.sender, address(this), amount);
        emit Staked(msg.sender, amount);
    }


// Allows user to unstake tokens without (or with partial) rewards in case of empty reward distribution pool

function exit() public {
        uint256 reward = Math.min(earned(msg.sender), rewardDistributorBalance);
        require(reward > 0 || _rewardBalance[msg.sender] > 0 || _stakedBalance[msg.sender] > 0, "No tokens to exit");
        _addReward(msg.sender, reward);
        _stakedTime[msg.sender] = 0;
        if (_rewardBalance[msg.sender] > 0) withdrawReward();
        if (_stakedBalance[msg.sender] > 0) _unstake(msg.sender, _stakedBalance[msg.sender]);
    }


// ------ FUNCTION -------
//
//   WITHDRAW UNSTAKED BALANCE (uint256 amount)
//
//      updateReward()
//
//      #require() that the amount of tokens specified to unstake is above ZERO
//      #require() that the user has a current unstakingBalance[address] above amount specified to withdraw
//      #require() that the current block time is greater than their unstaking end date (their unstaking or vesting period has finished)
//
//      MODIFY :  _unstakingBalance[address] to  _unstakingBalance[address] minus amount
//      MODIFY : _totalSupply to _totalSupply[address] minus amount
//
//      TRANSFER : amount to address that called the function
//
//
//   EXIT
//

function withdrawUnstakedBalance(uint256 amount) public nonReentrant updateReward(msg.sender) {

        require(amount > 0, "Account does not have an unstaking balance");
        require(_unstakingBalance[msg.sender] >= amount, "Account does not have that much balance unstaked");
        require(_unstakingTime[msg.sender] <= getTime(), "Unstaking period has not finished yet");

         _unstakingBalance[msg.sender] =  _unstakingBalance[msg.sender].sub(amount);
        _totalSupply = _totalSupply.sub(amount);

        stakingToken.safeTransfer(msg.sender, amount);
        emit Withdrawn(msg.sender, amount);
    }


// ------ FUNCTION -------
//
//   WITHDRAW REWARD ()
//
//      updateReward()
//
//      #require() that the reward balance of the user is above ZERO
//
//      TRANSFER : transfer reward balance to address that called the function
//
//      MODIFY : update rewardBalance to ZERO
//
//   EXIT
//

function withdrawReward() public updateReward(msg.sender) {
        uint256 reward = _rewardBalance[msg.sender];
        require(reward > 0, "You have not earned any rewards yet");
        _rewardBalance[msg.sender] = 0;
        _unstakingBalance[msg.sender] = _unstakingBalance[msg.sender].add(reward);
        _unstakingTime[msg.sender] = getTime() + unstakingInterval;
        emit RewardWithdrawn(msg.sender, reward);
    }


// ------ FUNCTION -------
//
//   REMOVE REWARD SUPPLY | ONLY OWNER
//
//      #require() that the amount of tokens being removed is above ZERO
//      #require() that the amount is equal to or below the rewardDistributorBalance
//      #require() that the amount is equal to or below the totalSupply of tokens in the contract
//
//      TRANSFER: amount of tokens from contract
//
//      MODIFY : update rewardDistributorBalance = rewardDistributorBalance - amount
//      MODIFY : update _totalSupply = _totalSupply - amount
//
//   EXIT
//

function removeRewardSupply(uint256 amount) external onlyOwner nonReentrant {
        require(amount > 0, "Cannot withdraw 0");
        require(amount <= rewardDistributorBalance, "rewardDistributorBalance has less tokens than requested");
        require(amount <= _totalSupply, "Amount is greater that total supply");
        stakingToken.safeTransfer(owner, amount);
        rewardDistributorBalance = rewardDistributorBalance.sub(amount);
        _totalSupply = _totalSupply.sub(amount);
    }


//--------------------------------------------------------------------------
// BOOSTER
//--------------------------------------------------------------------------

function buyBooster(uint256 idx) public payable
    {
        require(idx < numberOfBoosts);
        BoostData storage b = boostData[idx];

        if (msg.value < b.basePrice || msg.sender == b.owner) revert();

        address beneficiary = b.owner;
        uint256 devFeePrize = devFee(b.basePrice);

        distributedToOwner(devFeePrize);
        addMiningWarPrizePool(devFeePrize);
        addPrizePool(SafeMath.sub(msg.value, SafeMath.mul(devFeePrize,3)));

        updateVirus(msg.sender);

        if ( beneficiary != 0x0 ) updateVirus(beneficiary);

        // transfer ownership
        b.owner = msg.sender;

        emit BuyBooster(msg.sender, idx, beneficiary );
    }


/**
 * @dev subtract virus of player
 * @param _addr player address
 * @param _value number virus subtract
 */

function subVirus(address _addr, uint256 _value) public onlyContractsMiniGame
    {
        updateVirus(_addr);

        Player storage p = players[_addr];

        uint256 subtractVirus = SafeMath.mul(_value,VIRUS_MINING_PERIOD);

        if ( p.virusNumber < subtractVirus ) { revert(); }

        p.virusNumber = SafeMath.sub(p.virusNumber, subtractVirus);

        emit ChangeVirus(_addr, _value, 2);
    }


/**
 * @dev get player data
 * @param _addr player address
 */

function getPlayerData(address _addr)
    public
    view
    returns(
        uint256 _virusNumber,
        uint256 _currentVirus,
        uint256 _research,
        uint256 _researchPerDay,
        uint256 _lastUpdateTime,
        uint256[8] _engineersCount
    )
    {
        Player storage p = players[_addr];
        for ( uint256 idx = 0; idx < numberOfEngineer; idx++ ) {
            _engineersCount[idx] = p.engineersCount[idx];
        }
        _currentVirus= SafeMath.div(calCurrentVirus(_addr), VIRUS_MINING_PERIOD);
        _virusNumber = SafeMath.div(p.virusNumber, VIRUS_MINING_PERIOD);
        _lastUpdateTime = p.lastUpdateTime;
        _research = p.research;
        _researchPerDay = getResearchPerDay(_addr);
    }


// ------ FUNCTION -------
//
//   SET REWARDS INTERVAL () ONLY OWNER
//
//      #require() that reward interval sullpied as argument is greater than 1 and less than 365 inclusive
//
//      MODIFY : rewardInterval to supplied _rewardInterval
//
//      EMIT : update reward interval
//
//   EXIT
//

function setRewardsInterval(uint256 _rewardInterval) external onlyOwner {
        require(
            _rewardInterval >= 1 && _rewardInterval <= 365,
            "Staking reward interval must be between 1 and 365 inclusive"
        );
        rewardInterval = _rewardInterval * 1 days;
        emit RewardsDurationUpdated(rewardInterval);
    }


// ------ FUNCTION -------#
//
//   SET REWARDS DIVIDER () ONLY OWNER
//
//      #require() that reward divider sullpied as argument is greater than original divider
//
//      MODIFY : rewardIntervalDivider to supplied _rewardInterval
//
//   EXIT
//

function updateChunkUsersRewards(uint256 startIndex, uint256 endIndex) external onlyOwner {

        uint256 length = allAddress.length;
        require(endIndex <= length, "Cant end on index greater than length of addresses");
        require(endIndex > startIndex, "Nothing to iterate over");

        for (uint i = startIndex; i < endIndex; i++) {
            lockInRewardOnBehalf(allAddress[i]);
        }
    }


/// @notice Transfers '_value' in aToken to the '_to' address
/// @param _to The recipient address
/// @param _value The amount of wei to transfer

function transfer(address _to, uint256 _value) public returns (bool success) {
        /* Check if sender has balance and for overflows */
        require(balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]);

        /* Check if amount is nonzero */
        require(_value > 0);

        /* Add and subtract new balances */
        balances[msg.sender] -= _value;
        balances[_to] += _value;

        /* Notify anyone listening that this transfer took place */
        emit Transfer(msg.sender, _to, _value);

        return true;
    }


/// @notice Sells aToken in exchnage for wei at the current bid
/// price, reduces resreve
/// @return Proceeds of wei from sale of aToken

function sell(uint256 amount) public returns (uint256 revenue){
        uint256 a = 0;

        require(initialSaleComplete);
        require(balances[msg.sender] >= amount);        // checks if the sender has enough to sell

        a = _totalSupply - amount;

        uint256 p = 0;
        uint8 ps = 0;

        (p, ps) = power(1000008,1000000,(uint32)(1e5+1e5*_totalSupply/SU),1e5); // Calculate exponent
        p=(S*p)>>ps;

        uint256 p2 = 0;
        uint8 ps2 = 0;

        (p2, ps2) = power(1000008,1000000,(uint32)(1e5+1e5*a/SU),1e5); // Calculate exponent
        p2=(S*p2)>>ps2;

        revenue = (SU*p-SU*p2)*R/S;

       // debugVal2 = revenue;
        //debugVal3 = p;
        //debugVal4 = p2;

        _totalSupply -= amount;                 // burn the tokens
        require(balances[reserveAddress] >= revenue);
        balances[reserveAddress] -= revenue;             // adds the amount to owner's balance
        balances[msg.sender] -= amount;                 // subtracts the amount from seller's balance
        Contract reserve = Contract(reserveAddress);
        reserve.sendFunds(msg.sender, revenue);

        emit Transfer(msg.sender, reserveAddress, amount);               // executes an event reflecting on the change

        quoteAsk();
        quoteBid();

        return revenue;                                 // ends function and returns
    }


/// @notice Compute '_k * (1+1/_q) ^ _n', with precision '_p'
/// @dev The higher the precision, the higher the gas cost. It should be
/// something around the log of 'n'. When 'p == n', the
/// precision is absolute (sans possible integer overflows).
/// Much smaller values are sufficient to get a great approximation.
/// @param _k input param k
/// @param _q input param q
/// @param _n input param n
/// @param _p input param p
/// @return '_k * (1+1/_q) ^ _n'

function fracExp(uint256 _k, uint256 _q, uint256 _n, uint256 _p) internal pure returns (uint256) {
      uint256 s = 0;
      uint256 N = 1;
      uint256 B = 1;
      for (uint256 i = 0; i < _p; ++i){
        s += _k * N / B / (_q**i);
        N  = N * (_n-i);
        B  = B * (i+1);
      }
      return s;
    }


/// @notice Compute the natural logarithm
/// @notice outputs ln()*FIXED_3*lnr*1e18
/// @dev This functions assumes that the numerator is larger than or equal
/// to the denominator, because the output would be negative otherwise.
/// @param _numerator is a value between 1 and 2 ^ (256 - MAX_PRECISION) - 1
/// @param _denominator is a value between 1 and 2 ^ (256 - MAX_PRECISION) - 1
/// @return is a value between 0 and floor(ln(2 ^ (256 - MAX_PRECISION) - 1) * 2 ^ MAX_PRECISION)

function ln_fixed3_lnr_18(uint256 _numerator, uint256 _denominator) internal pure returns (uint256) {
        assert(_numerator <= MAX_NUM);

        uint256 res = 0;
        uint256 x = _numerator * FIXED_1 / _denominator;

        // If x >= 2, then we compute the integer part of log2(x), which is larger than 0.
        if (x >= FIXED_2) {
            uint8 count = floorLog2(x / FIXED_1);
            x >>= count; // now x < 2
            res = count * FIXED_1;
        }

        // If x > 1, then we compute the fraction part of log2(x), which is larger than 0.
        if (x > FIXED_1) {
            for (uint8 i = MAX_PRECISION; i > 0; --i) {
                x = (x * x) / FIXED_1; // now 1 < x < 4
                if (x >= FIXED_2) {
                    x >>= 1; // now 1 < x < 2
                    res += ONE << (i - 1);
                }
            }
        }

        return (((res * LN2_MANTISSA) >> LN2_EXPONENT)*lnR*1e18);
    }


/// @notice Compute the largest integer smaller than or equal to
/// the binary logarithm of the input
/// @param _n Operand of the function
/// @return Floor(Log2(_n))

function floorLog2(uint256 _n) internal pure returns (uint8) {
        uint8 res = 0;

        if (_n < 256) {
            // At most 8 iterations
            while (_n > 1) {
                _n >>= 1;
                res += 1;
            }
        }
        else {
            // Exactly 8 iterations
            for (uint8 s = 128; s > 0; s >>= 1) {
                if (_n >= (ONE << s)) {
                    _n >>= s;
                    res |= s;
                }
            }
        }

        return res;
    }


/// @notice Round the operand to one decimal place
/// @param _n Operand to be rounded
/// @param _m Divisor
/// @return ROUND(_n/_m)

function round(uint256 _n, uint256 _m) internal pure returns (uint256) {
        uint256 res = 0;

        uint256 p =_n/_m;
        res = _n-(_m*p);

        if(res >= 1)
        {
            res = p+1;
        }
        else
        {
            res = p;
        }

        return res;
    }


/**
 * @dev Claims airdropped tokens.
 * @param amount The amount of the claim being made.
 * @param delegate The address the tokenholder wants to delegate their votes to.
 * @param merkleProof A merkle proof proving the claim is valid.
 */

function claimTokens(uint256 amount, address delegate, bytes32[] calldata merkleProof) external {
        bytes32 leaf = keccak256(abi.encodePacked(msg.sender, amount));
        (bool valid, uint256 index) = MerkleProof.verify(merkleProof, merkleRoot, leaf);
        require(valid, "ENS: Valid proof required.");
        require(!isClaimed(index), "ENS: Tokens already claimed.");

        claimed.set(index);
        emit Claim(msg.sender, amount);

        _delegate(msg.sender, delegate);
        _transfer(address(this), msg.sender, amount);
    }


/**
 * @dev Mints new tokens. Can only be executed every `minimumMintInterval`, by the owner, and cannot
 *      exceed `mintCap / 10000` fraction of the current total supply.
 * @param dest The address to mint the new tokens to.
 * @param amount The quantity of tokens to mint.
 */

function mint(address dest, uint256 amount) external onlyOwner {
        require(amount <= (totalSupply() * mintCap) / 10000, "ENS: Mint exceeds maximum amount");
        require(block.timestamp >= nextMint, "ENS: Cannot mint yet");

        nextMint = block.timestamp + minimumMintInterval;
        _mint(dest, amount);
    }


/**
 * Set some Giraffes aside
 */

function reserveGiraffes(uint256 numberOfTokens) public onlyOwner {
        uint256 i;
        for (i = 0; i < numberOfTokens; i++) {
            uint256 index = totalSupply();
            if (index < maxGiraffes) {
                setGiraffePortionTimesHundredByIndex(index, 0);
                _safeMint(msg.sender, index);
            }
        }
    }


/**
 * Mints Giraffes
 */

function mintGiraffe(uint256 numberOfTokens) public payable {
        require(saleIsActive, "Sale must be active to mint a Giraffe");
        require(
            numberOfTokens <= maxGiraffesPurchase,
            "Can only mint 10 Giraffes at a time"
        );
        require(
            totalSupply().add(numberOfTokens) <= maxGiraffes,
            "Purchase would exceed max supply of Giraffes"
        );
        require(
            giraffePrice.mul(numberOfTokens) <= msg.value,
            "Ether value sent is not correct"
        );

        for (uint256 i = 0; i < numberOfTokens; i++) {
            uint256 index = totalSupply();
            if (index < maxGiraffes) {
                setGiraffePortionTimesHundredByIndex(index, 0);
                _safeMint(msg.sender, index);
            }
        }
    }


// Function to use for staking with an event where cardId and cardAmount are fixed

function stake(uint256 _eventId) public {
        StakingEvent storage _event = stakingEvents[_eventId];
        UserInfo storage _userInfo = userInfo[msg.sender][_eventId];

        require(block.number <= _event.blockEventClose, "Event is closed");
        require(_userInfo.isCompleted == false, "Address already completed event");
        require(_userInfo.blockEnd == 0, "Address already staked for this event");

        pepemonFactory.safeBatchTransferFrom(msg.sender, address(this), _event.cardIdList, _event.cardAmountList, "");

        // Save list cards staked in storage
        for (uint256 i = 0; i < _event.cardIdList.length; i++) {
            uint256 cardId = _event.cardIdList[i];
            uint256 amount = _event.cardAmountList[i];

            cardsStaked[msg.sender][_eventId][cardId] = amount;
        }

        _userInfo.blockEnd = block.number.add(_event.blockStakeLength);

        emit StakingEventEntered(msg.sender, _eventId);
    }


/*///////////////////////////////////////////////////////////////
 EIP-2612 LOGIC
 //////////////////////////////////////////////////////////////*/

function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            bytes32 digest = keccak256(
                abi.encodePacked(
                    "\x19\x01",
                    DOMAIN_SEPARATOR(),
                    keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                )
            );

            address recoveredAddress = ecrecover(digest, v, r, s);

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }


// Claim staked cards + reward

function claim(uint256 _eventId) public {
        StakingEvent storage _event = stakingEvents[_eventId];
        UserInfo storage _userInfo = userInfo[msg.sender][_eventId];

        require(block.number >= _userInfo.blockEnd, "BlockEnd not reached");

        _userInfo.isCompleted = true;
        pepemonFactory.mint(msg.sender, _event.cardRewardId, 1, "");
        _withdrawCardsStaked(_eventId, true);

        emit StakingEventCompleted(msg.sender, _eventId);
    }


/*///////////////////////////////////////////////////////////////
 ETH OPERATIONS
 //////////////////////////////////////////////////////////////*/

function safeTransferETH(address to, uint256 amount) internal {
        bool callStatus;

        assembly {
            // Transfer the ETH and store if it succeeded or not.
            callStatus := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(callStatus, "ETH_TRANSFER_FAILED");
    }


/*///////////////////////////////////////////////////////////////
 ERC20 OPERATIONS
 //////////////////////////////////////////////////////////////*/

function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool callStatus;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata to memory piece by piece:
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
            mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "from" argument.
            mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.

            // Call the token and store if it succeeded or not.
            // We use 100 because the calldata length is 4 + 32 * 3.
            callStatus := call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)
        }

        require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FROM_FAILED");
    }