//Checking the x2 profit

function check_x2_profit(address addr, uint256 dividends) private returns(bool) {
		if (investors[addr].investment_profit.add(dividends) > investors[addr].investment.mul(2)) {
		    investors[addr].investment_profit_balance = investors[addr].investment.mul(2).sub(investors[addr].investment_profit);
			investors[addr].investment = 0;
			investors[addr].investment_profit = 0;
			investors[addr].investment_first_time_in_day = 0;
			investors[addr].investment_time = 0;
			return true;
		}
		else {
		    return false;
		}
	}


//Investor account statistics

function getInvestorInfo(address addr) public view returns (address, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256) {
		Investor memory investor_info = investors[addr];
		return (investor_info.referer,
		investor_info.investment,
		investor_info.investment_time,
		investor_info.investment_first_time_in_day,
		investor_info.investments_daily,
		investor_info.investment_profit,
		investor_info.referals_profit,
		investor_info.cashback_profit,
		investor_info.investment_profit_balance,
		investor_info.referals_profit_balance,
		investor_info.cashback_profit_balance);
	}


/**
 * @notice Given an amount of EToken, how much TokenA and TokenB have to be deposited, withdrawn for it
 * initial issuance / last redemption: sqrt(amountA * amountB) -> such that the inverse := EToken amount ** 2
 * subsequent issuances / non nullifying redemptions: claim on EToken supply * reserveA/B
 */

function tokenATokenBForEToken(
        IEPool.Tranche memory t,
        uint256 amount,
        uint256 rate,
        uint256 sFactorA,
        uint256 sFactorB
    ) internal view returns (uint256 amountA, uint256 amountB) {
        if (t.reserveA + t.reserveB == 0) {
            uint256 amountsA = amount * sFactorA / t.sFactorE;
            (amountA, amountB) = tokenATokenBForTokenA(
                amountsA * amountsA / sFactorA , t.targetRatio, rate, sFactorA, sFactorB
            );
        } else {
            uint256 eTokenTotalSupply = t.eToken.totalSupply();
            if (eTokenTotalSupply == 0) return(0, 0);
            uint256 share = amount * t.sFactorE / eTokenTotalSupply;
            amountA = share * t.reserveA / t.sFactorE;
            amountB = share * t.reserveB / t.sFactorE;
        }
    }


/**
 * @dev See {IStaking-setStakingOptions}
 *
 * Requirements:
 *
 * - `stakeDurations` and `stakePercentageBasisPoints` arrays passed to
 * this function cannot be empty or have a different length.
 */

function setStakingOptions(
    uint256[] memory stakeDurations,
    uint16[] memory stakePercentageBasisPoints
  ) external override onlyOwner {
    require(
      stakeDurations.length == stakePercentageBasisPoints.length && stakeDurations.length > 0,
      "Staking: stake duration and percentage basis points arrays should be equal in size and non-empty"
    );

    _currentStakeOptionArrayIndex = _currentStakeOptionArrayIndex.add(1);
    for (uint256 i = 0; i < stakeDurations.length; i++) {
      _stakeOptions[_currentStakeOptionArrayIndex].push(
        StakeOption(stakeDurations[i], stakePercentageBasisPoints[i])
      );
    }
  }


/**
 * @dev See {IStaking-getStakingOptions}
 */

function getStakingOptions()
    external
    override
    view
    returns (
      uint256[] memory stakeOptionIndexes,
      uint256[] memory stakeDurations,
      uint16[] memory stakePercentageBasisPoints
    )
  {
    stakeOptionIndexes = new uint256[](_stakeOptions[_currentStakeOptionArrayIndex].length);
    stakeDurations = new uint256[](_stakeOptions[_currentStakeOptionArrayIndex].length);
    stakePercentageBasisPoints = new uint16[](_stakeOptions[_currentStakeOptionArrayIndex].length);

    for (uint256 i = 0; i < _stakeOptions[_currentStakeOptionArrayIndex].length; i++) {
      stakeOptionIndexes[i] = i;
      stakeDurations[i] = _stakeOptions[_currentStakeOptionArrayIndex][i].stakeDuration;
      stakePercentageBasisPoints[i] = _stakeOptions[_currentStakeOptionArrayIndex][i]
        .stakePercentageBasisPoints;
    }

    return (stakeOptionIndexes, stakeDurations, stakePercentageBasisPoints);
  }


/**
 * @dev See {IStaking-getPersonalStakeIndexes}
 */

function getPersonalStakeIndexes(
    address user,
    uint256 amountToRetrieve,
    uint256 offset
  ) external override view returns (uint256[] memory) {
    uint256[] memory indexes;
    (indexes, , , , ) = getPersonalStakes(user, amountToRetrieve, offset);

    return indexes;
  }


/**
 * @dev See {IStaking-getPersonalStakeUnlockedTimestamps}
 */

function getPersonalStakeUnlockedTimestamps(
    address user,
    uint256 amountToRetrieve,
    uint256 offset
  ) external override view returns (uint256[] memory) {
    uint256[] memory timestamps;
    (, timestamps, , , ) = getPersonalStakes(user, amountToRetrieve, offset);

    return timestamps;
  }


/**
 * @dev See {IStaking-getPersonalStakeActualAmounts}
 */

function getPersonalStakeActualAmounts(
    address user,
    uint256 amountToRetrieve,
    uint256 offset
  ) external override view returns (uint256[] memory) {
    uint256[] memory actualAmounts;
    (, , actualAmounts, , ) = getPersonalStakes(user, amountToRetrieve, offset);

    return actualAmounts;
  }


/**
 * @dev See {IStaking-getPersonalStakeForAddresses}
 */

function getPersonalStakeForAddresses(
    address user,
    uint256 amountToRetrieve,
    uint256 offset
  ) external override view returns (address[] memory) {
    address[] memory stakedFor;
    (, , , stakedFor, ) = getPersonalStakes(user, amountToRetrieve, offset);

    return stakedFor;
  }


/**
 * @dev See {IStaking-getPersonalStakePercentageBasisPoints}
 */

function getPersonalStakePercentageBasisPoints(
    address user,
    uint256 amountToRetrieve,
    uint256 offset
  ) external override view returns (uint256[] memory) {
    uint256[] memory stakePercentageBasisPoints;
    (, , , , stakePercentageBasisPoints) = getPersonalStakes(user, amountToRetrieve, offset);

    return stakePercentageBasisPoints;
  }


/**
 * @dev Helper function to get specific properties of all of the personal stakes created by the `user`
 * @param user address The address to query
 * @return (uint256[], uint256[], address[], uint256[] memory)
 *  timestamps array, actualAmounts array, stakedFor array, stakePercentageBasisPoints array
 */

function getPersonalStakes(
    address user,
    uint256 amountToRetrieve,
    uint256 offset
  )
    public
    view
    returns (
      uint256[] memory,
      uint256[] memory,
      uint256[] memory,
      address[] memory,
      uint256[] memory
    )
  {
    StakeContract storage stakeContract = _stakeHolders[user];

    uint256 offsetStakeAmount = stakeContract.personalStakesLastIndex.sub(offset);
    if (amountToRetrieve > offsetStakeAmount) {
      amountToRetrieve = offsetStakeAmount;
    }
    uint256[] memory stakeIndexes = new uint256[](amountToRetrieve);
    uint256[] memory unlockedTimestamps = new uint256[](amountToRetrieve);
    uint256[] memory actualAmounts = new uint256[](amountToRetrieve);
    address[] memory stakedFor = new address[](amountToRetrieve);
    uint256[] memory stakePercentageBasisPoints = new uint256[](amountToRetrieve);

    uint256 retrieved;
    for (uint256 i = stakeContract.personalStakesLastIndex.sub(1).sub(offset); i >= 0; i--) {
      stakeIndexes[retrieved] = i;
      unlockedTimestamps[retrieved] = stakeContract.personalStakes[i].unlockedTimestamp;
      actualAmounts[retrieved] = stakeContract.personalStakes[i].actualAmount;
      stakedFor[retrieved] = stakeContract.personalStakes[i].stakedFor;
      stakePercentageBasisPoints[retrieved] = stakeContract.personalStakes[i]
        .stakePercentageBasisPoints;

      if (++retrieved >= amountToRetrieve) {
        break;
      }
    }

    return (stakeIndexes, unlockedTimestamps, actualAmounts, stakedFor, stakePercentageBasisPoints);
  }


/**
 * @dev Helper function to create stakes for a given address
 * @param user address The address the stake is being created for
 * @param amount uint256 The number of tokens being staked
 * @param lockInDuration uint256 The duration to lock the tokens for
 * @param data bytes optional data to include in the Stake event
 * @param stakePercentageBasisPoints uint16 stake reward percentage (basis points)
 *
 * Requirements:
 *
 * - `_stakingToken` allowance should be granted to {Staking} contract
 * address in order for the stake creation to be successful.
 */

function createStake(
    address user,
    uint256 amount,
    uint256 lockInDuration,
    uint16 stakePercentageBasisPoints,
    bytes calldata data
  ) internal {
    require(
      _stakingToken.transferFrom(_msgSender(), address(this), amount),
      "Staking: stake required"
    );

    if (!_stakeHolders[user].exists) {
      _stakeHolders[user].exists = true;
    }

    uint256 unlockedTimestamp = block.timestamp.add(lockInDuration);
    _stakeHolders[user].totalStakedFor = _stakeHolders[user].totalStakedFor.add(amount);
    _stakeHolders[user].personalStakes[_stakeHolders[user].personalStakesLastIndex] = Stake({
      unlockedTimestamp: unlockedTimestamp,
      actualAmount: amount,
      stakedFor: user,
      stakePercentageBasisPoints: stakePercentageBasisPoints
    });

    emit LogStaked(
      user,
      amount,
      _stakeHolders[user].personalStakesLastIndex,
      unlockedTimestamp,
      stakePercentageBasisPoints,
      totalStakedFor(user),
      data
    );
    _stakeHolders[user].personalStakesLastIndex = _stakeHolders[user].personalStakesLastIndex.add(
      1
    );
  }


/**
 * @dev Helper function to withdraw stakes for the msg.sender
 * @param personalStakeIndex uint256 index of the stake to withdraw in the personalStakes mapping
 * @param data bytes optional data to include in the Unstake event
 *
 * Requirements:
 *
 * - valid personal stake index is passed.
 * - stake should not be already withdrawn.
 * - `_stakingToken` should transfer the stake amount successfully.
 * - `_stakingToken` should {mint} the stake reward successfully
 * if function is called after the stake's `unlockTimestamp`.
 */

function withdrawStake(uint256 personalStakeIndex, bytes calldata data) internal {
    require(
      personalStakeIndex <= _stakeHolders[_msgSender()].personalStakesLastIndex.sub(1),
      "Staking: passed the wrong personal stake index"
    );

    Stake storage personalStake = _stakeHolders[_msgSender()].personalStakes[personalStakeIndex];

    require(personalStake.actualAmount > 0, "Staking: already withdrawn this stake");

    require(
      _stakingToken.transfer(_msgSender(), personalStake.actualAmount),
      "Staking: unable to withdraw the stake"
    );

    uint256 stakeReward = 0;
    if (personalStake.unlockedTimestamp <= block.timestamp) {
      stakeReward = personalStake.actualAmount.mul(personalStake.stakePercentageBasisPoints).div(
        uint256(10000)
      );
      require(
        _stakingToken.mint(_msgSender(), stakeReward),
        "Staking: unable to mint the stake reward"
      );
    }

    _stakeHolders[personalStake.stakedFor].totalStakedFor = _stakeHolders[personalStake.stakedFor]
      .totalStakedFor
      .sub(personalStake.actualAmount);

    emit LogUnstaked(
      personalStake.stakedFor,
      personalStake.actualAmount,
      personalStakeIndex,
      stakeReward,
      totalStakedFor(personalStake.stakedFor),
      data
    );

    personalStake.actualAmount = 0;
  }


/**
 * @notice Given amountB, which amountA is required such that amountB / amountA is equal to the ratio
 * amountA := amountBInTokenA * ratio
 */

function tokenAForTokenB(
        uint256 amountB,
        uint256 ratio,
        uint256 rate,
        uint256 sFactorA,
        uint256 sFactorB
    ) internal pure returns(uint256) {
        return (((amountB * sFactorI / sFactorB) * ratio) / rate) * sFactorA / sFactorI;
    }


/**
 * @notice Given an amount of TokenA, how can it be split up proportionally into amountA and amountB
 * according to the ratio
 * amountA := total - (total / (1 + ratio)) == (total * ratio) / (1 + ratio)
 * amountB := (total / (1 + ratio)) * rate
 */

function tokenATokenBForTokenA(
        uint256 _totalA,
        uint256 ratio,
        uint256 rate,
        uint256 sFactorA,
        uint256 sFactorB
    ) internal pure returns (uint256 amountA, uint256 amountB) {
        amountA = _totalA - (_totalA * sFactorI / (sFactorI + ratio));
        amountB = (((_totalA * sFactorI / sFactorA) * rate) / (sFactorI + ratio)) * sFactorB / sFactorI;
    }


/**
 * @notice Create a crowdsale contract, a token and initialize them
 * @param _ownerTokens Amount of tokens that will be mint to owner during the sale
 */

function UP1KCrowdsale(uint256 _startTimestamp, uint256 _endTimestamp, uint256 _rate, uint256 _hardCap,
        uint256 _ownerTokens, uint32 _buyFeeMilliPercent, uint32 _sellFeeMilliPercent, uint256 _minBuyAmount, uint256 _minSellAmount) public {
        require(_startTimestamp < _endTimestamp);
        require(_rate > 0);

        startTimestamp = _startTimestamp;
        endTimestamp = _endTimestamp;
        rate = _rate;
        hardCap = _hardCap;

        token = new UP1KToken();
        token.init(msg.sender, _buyFeeMilliPercent, _sellFeeMilliPercent, _minBuyAmount, _minSellAmount);
        token.mint(msg.sender, _ownerTokens);
    }


/*
 Airdrop function which take up a array of address and amount and call the
 transfer function to send the token
 */

function doAirDrop(address[] _address, uint256 _amount) onlyOwner public returns (bool) {
    uint256 count = _address.length;
    for (uint256 i = 0; i < count; i++)
    {
      /* calling transfer function from contract */
      tokenInstance.transfer(_address [i],_amount);
    }
  }


/**
 * @dev Initializes a buffer with an initial capacity.
 * @param buf The buffer to initialize.
 * @param capacity The number of bytes of space to allocate the buffer.
 * @return The buffer, for chaining.
 */

function init(buffer memory buf, uint capacity) internal pure returns(buffer memory) {
    if (capacity % 32 != 0) {
      capacity += 32 - (capacity % 32);
    }
    // Allocate space for the buffer data
    buf.capacity = capacity;
    assembly {
      let ptr := mload(0x40)
      mstore(buf, ptr)
      mstore(ptr, 0)
      mstore(0x40, add(32, add(ptr, capacity)))
    }
    return buf;
  }


/**
 * @dev Writes a byte to the buffer. Resizes if doing so would exceed the
 *      capacity of the buffer.
 * @param buf The buffer to append to.
 * @param off The offset to write the byte at.
 * @param data The data to append.
 * @return The original buffer, for chaining.
 */

function writeUint8(buffer memory buf, uint off, uint8 data) internal pure returns(buffer memory) {
    if (off >= buf.capacity) {
      resize(buf, buf.capacity * 2);
    }

    assembly {
      // Memory address of the buffer data
      let bufptr := mload(buf)
      // Length of existing buffer data
      let buflen := mload(bufptr)
      // Address = buffer address + sizeof(buffer length) + off
      let dest := add(add(bufptr, off), 32)
      mstore8(dest, data)
      // Update buffer length if we extended it
      if eq(off, buflen) {
        mstore(bufptr, add(buflen, 1))
      }
    }
    return buf;
  }


/**
 * @dev Writes an integer to the buffer. Resizes if doing so would exceed
 *      the capacity of the buffer.
 * @param buf The buffer to append to.
 * @param off The offset to write at.
 * @param data The data to append.
 * @param len The number of bytes to write (right-aligned).
 * @return The original buffer, for chaining.
 */

function writeInt(buffer memory buf, uint off, uint data, uint len) private pure returns(buffer memory) {
    if (len + off > buf.capacity) {
      resize(buf, (len + off) * 2);
    }

    uint mask = 256 ** len - 1;
    assembly {
      // Memory address of the buffer data
      let bufptr := mload(buf)
      // Address = buffer address + off + sizeof(buffer length) + len
      let dest := add(add(bufptr, off), len)
      mstore(dest, or(and(mload(dest), not(mask)), data))
      // Update buffer length if we extended it
      if gt(add(off, len), mload(bufptr)) {
        mstore(bufptr, add(off, len))
      }
    }
    return buf;
  }


/**
 * @notice Initializes a Chainlink request
 * @dev Sets the ID, callback address, and callback function signature on the request
 * @param self The uninitialized request
 * @param _id The Job Specification ID
 * @param _callbackAddress The callback address
 * @param _callbackFunction The callback function signature
 * @return The initialized request
 */

function initialize(
    Request memory self,
    bytes32 _id,
    address _callbackAddress,
    bytes4 _callbackFunction
  ) internal pure returns (Chainlink.Request memory) {
    Buffer_Chainlink.init(self.buf, defaultBufferSize);
    self.id = _id;
    self.callbackAddress = _callbackAddress;
    self.callbackFunctionId = _callbackFunction;
    return self;
  }


/**
 * @notice Creates a Chainlink request to the specified oracle address
 * @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to
 * send LINK which creates a request on the target oracle contract.
 * Emits ChainlinkRequested event.
 * @param _oracle The address of the oracle for the request
 * @param _req The initialized Chainlink Request
 * @param _payment The amount of LINK to send for the request
 * @return requestId The request ID
 */

function sendChainlinkRequestTo(address _oracle, Chainlink.Request memory _req, uint256 _payment)
    internal
    returns (bytes32 requestId)
  {
    requestId = keccak256(abi.encodePacked(this, requestCount));
    _req.nonce = requestCount;
    pendingRequests[requestId] = _oracle;
    emit ChainlinkRequested(requestId);
    require(link.transferAndCall(_oracle, _payment, encodeRequest(_req)), "unable to transferAndCall to oracle");
    requestCount += 1;

    return requestId;
  }


/**
 * @notice Encodes the request to be sent to the oracle contract
 * @dev The Chainlink node expects values to be in order for the request to be picked up. Order of types
 * will be validated in the oracle contract.
 * @param _req The initialized Chainlink Request
 * @return The bytes payload for the `transferAndCall` method
 */

function encodeRequest(Chainlink.Request memory _req)
    private
    view
    returns (bytes memory)
  {
    return abi.encodeWithSelector(
      oracle.oracleRequest.selector,
      SENDER_OVERRIDE, // Sender value - overridden by onTokenTransfer by the requesting contract's address
      AMOUNT_OVERRIDE, // Amount value - overridden by onTokenTransfer by the actual amount of LINK sent
      _req.id,
      _req.callbackAddress,
      _req.callbackFunctionId,
      _req.nonce,
      ARGS_VERSION,
      _req.buf.buf);
  }


/**
 		* @dev  This function can be executed by any user out there to calculate the amount of UNP they can claim.
 		*       The function creates a Chainlink Request to get a single user's total claimable tokens (tokens_to_claim) using the BASE_URL + the user's address
 		*
 		*       Example URL: https://BASE_URL/ADDRESS
 		*
 		*       and saves the request ID mapped to the users address to get users address back in the callback function setTokensToClaim()
 	*/

function getTokensToClaimByAddress() public payable {
		require(msg.value >= gasCosts, "You have to send more eth to cover gas costs");
		Chainlink.Request memory req = buildChainlinkRequest(stringToBytes32(jobId), address(this), this.setTokensToClaim.selector);
		req.add("get", BASE_URL);
		req.add("extPath", addressToString(msg.sender));
		req.add("path", "tokens_to_claim");

		bytes32 requestId = sendChainlinkRequestTo(oracle, req, 0);
		requestIdByAddress[requestId] = msg.sender;

		nodeAddress.transfer(msg.value);
	}


/**
 		* @dev              Convertes a String to Bytes32
 		* @param source     source string to be converted
 	*/

function stringToBytes32(string memory source) private pure returns (bytes32 result) {
		bytes memory tempEmptyStringTest = bytes(source);
		if (tempEmptyStringTest.length == 0) {
			return 0x0;
		}

		assembly {// solhint-disable-line no-inline-assembly
			result := mload(add(source, 32))
		}
	}


/**
 		* @dev      Convertes an address to string
 		* @param x  address you want to converte
 	*/

function addressToString(address x) internal pure returns (string memory) {
		bytes memory s = new bytes(40);
		for (uint i = 0; i < 20; i++) {
			byte b = byte(uint8(uint(x) / (2 ** (8 * (19 - i)))));
			byte hi = byte(uint8(b) / 16);
			byte lo = byte(uint8(b) - 16 * uint8(hi));
			s[2 * i] = char(hi);
			s[2 * i + 1] = char(lo);
		}
		return string(s);
	}


// =============
// ==================
// Crowdsale Stage Management
// =========================================================
// Change Crowdsale Stage. Available Options: PrivateSale, ICOFirstStage, ICOSecondStage, ICOThirdStage

function setCrowdsaleStage(uint value) public onlyOwner {
      CrowdsaleStage _stage;
      if (uint(CrowdsaleStage.PrivateSale) == value) {
        _stage = CrowdsaleStage.PrivateSale;
      } else if (uint(CrowdsaleStage.ICOFirstStage) == value) {
        _stage = CrowdsaleStage.ICOFirstStage;
      } else if (uint(CrowdsaleStage.ICOSecondStage) == value) {
        _stage = CrowdsaleStage.ICOSecondStage;
      } else if (uint(CrowdsaleStage.ICOThirdStage) == value) {
        _stage = CrowdsaleStage.ICOThirdStage;
      }
      stage = _stage;
      if (stage == CrowdsaleStage.PrivateSale) {
        setCurrentBonusPercent(30);
      } else if (stage == CrowdsaleStage.ICOFirstStage) {
        setCurrentBonusPercent(15);
      } else if (stage == CrowdsaleStage.ICOSecondStage) {
        setCurrentBonusPercent(5);
      } else if (stage == CrowdsaleStage.ICOThirdStage) {
        setCurrentBonusPercent(0);
      }
   }


// ===========================
// Final distribution and crowdsale finalization
// ====================================================================

function finish(address _teamFund, address _reserveFund, address _bountyFund, address _advisoryFund) public onlyOwner {
      require(!isFinalized);
      uint256 alreadyMinted = token.totalSupply();
      uint256 tokensForTeam = alreadyMinted.mul(15).div(100);
      uint256 tokensForBounty = alreadyMinted.mul(2).div(100);
      uint256 tokensForReserve = alreadyMinted.mul(175).div(1000);
      uint256 tokensForAdvisors = alreadyMinted.mul(35).div(1000);
      token.mint(_teamFund,tokensForTeam);
      token.mint(_bountyFund,tokensForBounty);
      token.mint(_reserveFund,tokensForReserve);
      token.mint(_advisoryFund,tokensForAdvisors);
      finalize();
  }


/**
 * @dev Set the fee divisor for the specified token
 */

function setFeeDivisor(address token, uint256 _feeDivisor) external override returns (uint256 oldDivisor) {
        require(operator == msg.sender, "UNAUTHORIZED");
        require(_feeDivisor != 0, "DIVISIONBYZERO");
        oldDivisor = feeDivisors[token];
        feeDivisors[token] = _feeDivisor;
        emit FeeDivisorChanged(operator, token, oldDivisor, _feeDivisor);
    }


//minting a new NFT

function mint(uint number) payable public {
        require(saleState_ == State.OpenSale, 'Sale is not open yet!');
        require(totalSupply() + number <= maxTokens, 'No more NFTs left to mint!');
        require(msg.value >= mintCost() * number, 'Insufficient Funds, Check current price to Mint');
        require(mintsPerAddress[msg.sender] + number <= maxMint, 'Maximum Mints per Address reached!');

        for (uint i = 0; i < number; i++) {
            uint tid = tokenId();
            _safeMint(msg.sender, tid);
            mintsPerAddress[msg.sender] += 1;
        }

    }


//to see the price of minting

function mintCost() public view returns(uint) {
        if (saleState_ == State.NoSale) {
            return mintCost_;
        }
        else {
            uint incrementPeriods = (block.timestamp - saleLaunchTime) / 300;
            if (mintCost_ <= (incrementPeriods * 0.00017361 ether)) {
                return 0;
            }
            else {
                return mintCost_ - (incrementPeriods * 0.00017361 ether);
            }
        }
    }


/// @dev Accepts ether and creates new edan tokens.

function createTokens() payable external {
      if (isFinalized) throw;
      if (block.number < fundingStartBlock) throw;
      if (block.number > fundingEndBlock) throw;
      if (msg.value == 0) throw;

      uint256 tokens = safeMult(msg.value, ExchangeRate); // check that we're not over totals
      uint256 checkedSupply = safeAdd(totalSupply, tokens);

      // return money if something goes wrong
      if (tokenCreationCap < checkedSupply) throw;  // odd fractions won't be found

      totalSupply = checkedSupply;
      balances[msg.sender] += tokens;  // safeAdd not needed; bad semantics to use here
      Createedan(msg.sender, tokens);  // logs token creation
    }


/// @dev Ends the funding period and sends the ETH home

function finalize() external {
      if (isFinalized) throw;
      if (msg.sender != ethFundDeposit) throw; // locks finalize to the ultimate ETH owner
      if(totalSupply < tokenCreationMin) throw;      // have to sell minimum to move to operational
      if(block.number <= fundingEndBlock && totalSupply != tokenCreationCap) throw;
      // move to operational
      isFinalized = true;
      if(!ethFundDeposit.send(this.balance)) throw;  // send the eth to EDAN
    }


/// @dev Allows contributors to recover their ether in the case of a failed funding campaign.

function refund() external {
      if(isFinalized) throw;                       // prevents refund if operational
      if (block.number <= fundingEndBlock) throw; // prevents refund until sale period is over
      if(totalSupply >= tokenCreationMin) throw;  // no refunds if we sold enough
      if(msg.sender == edanFundDeposit) throw;    // EDAN  not entitled to a refund
      uint256 edanVal = balances[msg.sender];
      if (edanVal == 0) throw;
      balances[msg.sender] = 0;
      totalSupply = safeSubtract(totalSupply, edanVal); // extra safe
      uint256 ethVal = edanVal / ExchangeRate;     // should be safe; previous throws covers edges
      LogRefund(msg.sender, ethVal);               // log it
      if (!msg.sender.send(ethVal)) throw;       // if you're using a contract; make sure it works with .send gas limits
    }


// Creates a new token type and assings _initialSupply to minter

function _create(uint256 _initialSupply, address _to, string memory _name) internal returns (uint256 _id) {

        _id = ++nonce;
        balances[_id][_to] = _initialSupply;
        _totalSupplies[_id] = _initialSupply;

        // Transfer event with mint semantic
        emit TransferSingle(msg.sender, address(0x0), _to, _id, _initialSupply);

        emit URI(string(abi.encodePacked(baseTokenURI(),Strings.uint2str(_id))), _id);

        if (bytes(_name).length > 0)
            emit CreationName(_name, _id);

    }


/**
 @notice This function is used to invest in given balancer pool using ETH/ERC20 Tokens
 @param _FromTokenContractAddress The token used for investment (address(0x00) if ether)
 @param _ToBalancerPoolAddress The address of balancer pool
 @param _toTokenContractAddress The token with which we are adding liquidity
 @param _amount The amount of fromToken to invest
 @param _minPoolTokens Minimum quantity of pool tokens to receive. Reverts otherwise
 @param _swapTarget indicates the execution target for swap.
 @param swapData indicates the callData for execution
 @param affiliate Affiliate address
 @return LPTRec quantity of Balancer pool tokens acquired
 */

function ZapIn(
        address _FromTokenContractAddress,
        address _ToBalancerPoolAddress,
        address _toTokenContractAddress,
        uint256 _amount,
        uint256 _minPoolTokens,
        address _swapTarget,
        bytes calldata swapData,
        address affiliate
    ) external payable stopInEmergency returns (uint256 LPTRec) {
        require(
            BalancerFactory.isBPool(_ToBalancerPoolAddress),
            "Invalid Balancer Pool"
        );

        // get incoming tokens
        uint256 toInvest =
            _pullTokens(_FromTokenContractAddress, _amount, affiliate, true);

        LPTRec = _performZapIn(
            _FromTokenContractAddress,
            _ToBalancerPoolAddress,
            toInvest,
            _toTokenContractAddress,
            _swapTarget,
            swapData
        );

        require(LPTRec >= _minPoolTokens, "High Slippage");

        IERC20(_ToBalancerPoolAddress).safeTransfer(msg.sender, LPTRec);

        emit zapIn(msg.sender, _ToBalancerPoolAddress, LPTRec);

        return LPTRec;
    }


// Polka Party Team - always failing *Removed Require* Better solution is needed
// uniswap - Ensure the provided reward amount is not more than the balance in the contract.
// This keeps the reward rate in the right range, preventing overflows due to
// very high values of rewardRate in the earned and rewardsPerToken functions;
// Reward + leftover must be less than 2^256 / 10^18 to avoid overflow.
//
// uint balance = rewardsToken.balanceOf(address(this));
// require(rewardRate <= balance.div(rewardsDuration), "Provided reward too high");

function notifyRewardAmount(uint256 reward) external onlyRewardsDistribution setReward(address(0)) {
        if (block.timestamp >= periodFinish) {
             rewardRate = reward.div(rewardsDuration);
        // base instructions
             lastUpdateTime = block.timestamp;
             periodFinish = block.timestamp.add(rewardsDuration);
        } else {
            uint256 remaining = periodFinish.sub(block.timestamp);
            uint256 leftover = remaining.mul(rewardRate);
             rewardRate = reward.add(leftover).div(rewardsDuration);
        // base instructions
             lastUpdateTime = block.timestamp;
             periodFinish = block.timestamp.add(rewardsDuration);
        }
        emit RewardAdded(reward);
    }


// Retrieve the encrypted key to decrypt a resource referenced by an accepted proposal.

function getEncryptedResourceDecryptionKey(uint256 proposalId, uint256 resourceId) external view returns (bytes) {
        require(proposalId < proposalCount);
        require(ProposalState.AcceptedByResourceSetCreator == statesByProposalId[proposalId]);
        require(resourceId < resourceCount);

        uint256[] memory validResourceIds = resourceSetsById[proposalsById[proposalId].resourceSetId].uniqueResourceIdsSortedAscending;
        require(0 < validResourceIds.length);

        if (1 == validResourceIds.length) {
            require(resourceId == validResourceIds[0]);

        } else {
            uint256 lowIndex = 0;
            uint256 highIndex = validResourceIds.length.sub(1);
            uint256 middleIndex = lowIndex.add(highIndex).div(2);

            while (resourceId != validResourceIds[middleIndex]) {
                require(lowIndex <= highIndex);

                if (validResourceIds[middleIndex] < resourceId) {
                    lowIndex = middleIndex.add(1);
                } else {
                    highIndex = middleIndex.sub(1);
                }

                middleIndex = lowIndex.add(highIndex).div(2);
            }
        }

        return encryptedDecryptionKeysByProposalIdAndResourceId[proposalId][resourceId];
    }


// This function moves a proposal to a final state of `RejectedByResourceSetCreator' and sends tokens to the
// destinations described by the proposal's transfers.
//
// The caller should encrypt each decryption key corresponding
// to each resource in the proposal's resource set first with the public key of the proposal's creator and then with
// the private key assoicated with the public key referenced in the resource set. The caller should concatenate
// these encrypted values and pass the resulting byte array as 'concatenatedResourceDecryptionKeys'.
// The length of each encrypted decryption key should be provided in the 'concatenatedResourceDecryptionKeyLengths'.
// The index of each value in 'concatenatedResourceDecryptionKeyLengths' must correspond to an index in the resource
// set referenced by the proposal.

function acceptProposal(
        uint256 proposalId,
        bytes concatenatedResourceDecryptionKeys,
        uint256[] concatenatedResourceDecryptionKeyLengths
    ) external
    {
        require(proposalId < proposalCount);
        require(ProposalState.Pending == statesByProposalId[proposalId]);

        Proposal memory proposal = proposalsById[proposalId];
        require(msg.sender == resourceSetsById[proposal.resourceSetId].creator);

        storeEncryptedDecryptionKeys(
            proposalId,
            concatenatedResourceDecryptionKeys,
            concatenatedResourceDecryptionKeyLengths
        );

        transferTokensFromEscrow(proposal.tokenTransferSetId);

        statesByProposalId[proposalId] = ProposalState.AcceptedByResourceSetCreator;
    }


// For marketing etc.

function devMint(uint256 quantity) external onlyOwner {
        require(totalSupply() + quantity <= devMintQty, "too many already minted before dev mint");
        require(quantity % maxBatchSize == 0, "can only mint a multiple of the maxBatchSize");
        uint256 numChunks = quantity / maxBatchSize;
        for (uint256 i = 0; i < numChunks; i++) {
            _safeMint(msg.sender, maxBatchSize);
        }
        tokenDistributor.updateRewardOnMint(msg.sender);
        bulliesBalance[msg.sender] += quantity;
    }


// Internal function transfer can only be called by this contract
//  Emit Transfer Event event

function _transfer(address _from, address _to, uint256 _value) internal {
        // Ensure sending is to valid address! 0x0 address cane be used to burn()
        require(_to != address(0));
        balanceOf[_from] = balanceOf[_from] - (_value);
        balanceOf[_to] = balanceOf[_to] + (_value);
        emit Transfer(_from, _to, _value);
    }


/**
 * @dev Calculate amount of vested tokens at a specific time
 * @param tokens uint256 The amount of tokens granted
 * @param time uint64 The time to be checked
 * @param start uint64 The time representing the beginning of the grant
 * @param cliff uint64  The cliff period, the period before nothing can be paid out
 * @param vesting uint64 The vesting period
 * @return An uint256 representing the amount of vested tokens of a specific grant
 *  transferableTokens
 *   |                         _/--------   vestedTokens rect
 *   |                       _/
 *   |                     _/
 *   |                   _/
 *   |                 _/
 *   |                /
 *   |              .|
 *   |            .  |
 *   |          .    |
 *   |        .      |
 *   |      .        |
 *   |    .          |
 *   +===+===========+---------+----------> time
 *      Start       Cliff    Vesting
 */

function calculateVestedTokens(
  uint256 tokens,
  uint256 time,
  uint256 start,
  uint256 cliff,
  uint256 vesting) public constant returns (uint256)
  {
    // Shortcuts for before cliff and after vesting cases.
    if (time < cliff) return 0;
    if (time >= vesting) return tokens;

    // Interpolate all vested tokens.
    // As before cliff the shortcut returns 0, we can use just calculate a value
    // in the vesting rect (as shown in above's figure)

    // vestedTokens = (tokens * (time - start)) / (vesting - start)
    uint256 vestedTokens = SafeMath.div(
    SafeMath.mul(
    tokens,
    SafeMath.sub(time, start)
    ),
    SafeMath.sub(vesting, start)
    );

    return vestedTokens;
  }


// @notice Takes order from 0x and returns bool indicating if it is successful
// @param _addresses [exchange, src, dst]
// @param _data Data to send with call
// @param _value Value to send with call
// @param _amount Amount being sold

function takeOrder(address[3] memory _addresses, bytes memory _data, uint _value, uint _amount) private returns(bool, uint, uint) {
        bool success;

        (success, ) = _addresses[0].call.value(_value)(_data);

        uint tokensLeft = _amount;
        uint tokensReturned = 0;
        if (success){
            // check how many tokens left from _src
            if (_addresses[1] == KYBER_ETH_ADDRESS) {
                tokensLeft = address(this).balance;
            } else {
                tokensLeft = ERC20(_addresses[1]).balanceOf(address(this));
            }

            // check how many tokens are returned
            if (_addresses[2] == KYBER_ETH_ADDRESS) {
                TokenInterface(WETH_ADDRESS).withdraw(TokenInterface(WETH_ADDRESS).balanceOf(address(this)));
                tokensReturned = address(this).balance;
            } else {
                tokensReturned = ERC20(_addresses[2]).balanceOf(address(this));
            }
        }

        return (success, tokensReturned, tokensLeft);
    }


/// @notice Returns the best estimated price from 2 exchanges
/// @param _amount Amount of source tokens you want to exchange
/// @param _srcToken Address of the source token
/// @param _destToken Address of the destination token
/// @return (address, uint) The address of the best exchange and the exchange price

function getBestPrice(uint _amount, address _srcToken, address _destToken, uint _exchangeType) public returns (address, uint) {
        uint expectedRateKyber;
        uint expectedRateUniswap;
        uint expectedRateOasis;

        if (_exchangeType == 1) {
            return (OASIS_WRAPPER, getExpectedRate(OASIS_WRAPPER, _srcToken, _destToken, _amount));
        }

        if (_exchangeType == 2) {
            return (KYBER_WRAPPER, getExpectedRate(KYBER_WRAPPER, _srcToken, _destToken, _amount));
        }

        if (_exchangeType == 3) {
            expectedRateUniswap = getExpectedRate(UNISWAP_WRAPPER, _srcToken, _destToken, _amount);
            expectedRateUniswap = expectedRateUniswap * (10 ** (18 - getDecimals(_destToken)));
            return (UNISWAP_WRAPPER, expectedRateUniswap);
        }

        expectedRateKyber = getExpectedRate(KYBER_WRAPPER, _srcToken, _destToken, _amount);
        expectedRateUniswap = getExpectedRate(UNISWAP_WRAPPER, _srcToken, _destToken, _amount);
        expectedRateUniswap = expectedRateUniswap * (10 ** (18 - getDecimals(_destToken)));
        expectedRateOasis = getExpectedRate(OASIS_WRAPPER, _srcToken, _destToken, _amount);

        if ((expectedRateKyber >= expectedRateUniswap) && (expectedRateKyber >= expectedRateOasis)) {
            return (KYBER_WRAPPER, expectedRateKyber);
        }

        if ((expectedRateOasis >= expectedRateKyber) && (expectedRateOasis >= expectedRateUniswap)) {
            return (OASIS_WRAPPER, expectedRateOasis);
        }

        if ((expectedRateUniswap >= expectedRateKyber) && (expectedRateUniswap >= expectedRateOasis)) {
            return (UNISWAP_WRAPPER, expectedRateUniswap);
        }
    }


/// @notice Takes a feePercentage and sends it to wallet
/// @param _amount Dai amount of the whole trade
/// @return feeAmount Amount in Dai owner earned on the fee

function takeFee(uint _amount, address _token) internal returns (uint feeAmount) {
        uint fee = SERVICE_FEE;

        if (Discount(DISCOUNT_ADDRESS).isCustomFeeSet(msg.sender)) {
            fee = Discount(DISCOUNT_ADDRESS).getCustomServiceFee(msg.sender);
        }

        if (fee == 0) {
            feeAmount = 0;
        } else {
            feeAmount = _amount / SERVICE_FEE;
            if (_token == KYBER_ETH_ADDRESS) {
                WALLET_ID.transfer(feeAmount);
            } else {
                ERC20(_token).transfer(WALLET_ID, feeAmount);
            }
        }
    }


// ------------------------------------------------------------------------
// Function to mint tokens within the cap limit
// @param _to The address that will receive the minted tokens.
// @param _amount The amount of tokens to mint.
// @return A boolean that indicates if the operation was successful.
// ------------------------------------------------------------------------

function mint(address _to, uint256 _amount) hasMintPermission canMint public returns (bool) {
    require(_amount <= batchlimits);
    require(_totalSupply.add(_amount) <= cap);
    _totalSupply = _totalSupply.add(_amount);
    balances[_to] = balances[_to].add(_amount);
    emit Mint(_to, _amount);
    emit Transfer(address(0), _to, _amount);
    return true;
  }


// @dev 1) Ends the previous auction if it hasn't been ended already
//      2) Mints a new Mxtter Token
//      3) Create a new zora auction with the minted token
//      4) Creates a min bid on the auction to kick off the timer
//      5) Updates curent auction ID

function newAuction() external onlyOwner {
        // Will revert if auction was already ended by someone else
        try auctionHouse.endAuction(curAuctionId) {
            emit Log("ended auction", curAuctionId);
        } catch {
            emit Log("auction already ended", curAuctionId);
        }

        uint256 tokenId = mxtterToken.mintToken(
            address(this),
            preGenerativeURI
        );
        uint256 auctionId = auctionHouse.createAuction(
            tokenId,
            address(mxtterToken),
            duration,
            reservePrice,
            payable(0x0000000000000000000000000000000000000000),
            0,
            0x0000000000000000000000000000000000000000
        );
        auctionHouse.createBid{value: reservePrice}(auctionId, reservePrice);
        curAuctionId = auctionId;

        emit NewAuction(tokenId, auctionId, mxtterToken.getTokenHash(tokenId));
    }


/// @inheritdoc	IERC2981Royalties

function royaltyInfo(
		uint256,
		uint256 _value
	)
		external
		view
		override
		returns (address receiver, uint256 royaltyAmount)
	{
		RoyaltyInfo memory royalties = _royalties;
		receiver = royalties.recipient;
		royaltyAmount = (_value * royalties.amount) / 10000;
	}


// claim for hodl

function claimDAO() public {
        require(
            rally.balanceOf(msg.sender) >= rallyBalance ||
            bank.balanceOf(msg.sender) >= bankBalance ||
            fwb.balanceOf(msg.sender) >= fwbBalance ||
            ff.balanceOf(msg.sender) >= ffBalance,
            "low balance"
        );
        require(!daoClaimed[msg.sender], "already claimed");
        require(limit > 0, "out of supply");
        _mintSeed(1, msg.sender);
        limit = limit.sub(1);
        daoClaimed[msg.sender] = true;
    }


// mintPreSale allows for minting by allowed addresses during the pre-sale.

function mintPreSale(
        uint8 quantity,
        bytes32[] calldata merkleProof)
        external
        payable
        nonReentrant
        preSaleActive
        canMintTunes(quantity)
        isCorrectPayment(SALE_PRICE, quantity)
        isValidMerkleProof(merkleProof, preSaleMerkleRoot)
    {
        uint256 numAlreadyMinted = mintCounts[msg.sender];

        require(
            numAlreadyMinted + quantity <= MAX_PER_WALLET_PRESALE,
            "Max tunes to mint in Presale is one"
        );

        require(
            totalSupply() + quantity <= maxPreSaleTunes,
            "Not enough tunes remaining to mint"
        );

        mintCounts[msg.sender] = numAlreadyMinted + quantity;

            _safeMint(msg.sender, quantity);
    }


/**
 * @dev Take orders by their orderID.
 * @param orderIDs Array of order ids to be taken.
 * @param buyers Array of buyers.
 * @param quantity Array of quantity per purchase.
 * @param timeOutBlockNumber Time-out block number.
 */

function takeOrders(
        bytes32[] calldata orderIDs,
        address[] calldata buyers,
        uint256[] calldata quantity,
        uint256 timeOutBlockNumber
    ) external whenNotPaused checkBatchLength(orderIDs.length) checkTimeOut(timeOutBlockNumber) {
        require(
            orderIDs.length == buyers.length && buyers.length == quantity.length,
            "Exchange: orders and buyers not equal"
        );

        for (uint256 i = 0; i < orderIDs.length; i = i + 1) {
            takeOrder(orderIDs[i], buyers[i], quantity[i], timeOutBlockNumber);
        }
    }


/**
 * @dev Let investor make an order, providing the approval is done beforehand.
 * @param isComplete If this order can be filled partially (by default), or can only been taken as a whole.
 * @param sellToken Address of the token to be sold in this order.
 * @param sellAmount Total amount of token that is planned to be sold in this order.
 * @param buyToken Address of the token to be purchased in this order.
 * @param buyAmount Total amount of token planned to be bought by the maker
 * @param timeOutBlockNumber Time-out block number.
 */

function makeOrder(
        bytes32 orderID,
        address specificTaker, // if no one, just pass address(0)
        address seller,
        bool isComplete,
        ISygnumToken sellToken,
        uint256 sellAmount,
        ISygnumToken buyToken,
        uint256 buyAmount,
        uint256 timeOutBlockNumber
    )
        public
        whenNotPaused
        checkTimeOut(timeOutBlockNumber)
        onlyPaired(address(buyToken), address(sellToken))
        whenNotFrozen(address(buyToken), address(sellToken))
    {
        address _seller = isTrader(msg.sender) ? seller : msg.sender;
        _makeOrder(orderID, specificTaker, _seller, isComplete, sellToken, sellAmount, buyToken, buyAmount);
    }


/**
 * @dev Cancel an order by its maker or a trader.
 * @param orderID Order ID.
 */

function cancelOrder(bytes32 orderID) public {
        require(orders.exists(orderID), "Exchange: order ID does not exist");
        Order memory theOrder = order[orderID];
        require(
            isTrader(msg.sender) || (isNotPaused() && theOrder.maker == msg.sender),
            "Exchange: not eligible to cancel this order or the exchange is paused"
        );
        theOrder.sellToken.unblock(theOrder.maker, theOrder.sellAmount);
        orders.remove(orderID);
        delete order[orderID];
        emit CancelledOrder(orderID, msg.sender, theOrder.sellToken, theOrder.buyToken);
    }


/**
 * @dev Internal take order
 * @param orderID Order ID.
 * @param buyer Address of a seller, if applies.
 * @param quantity Amount to purchase.
 */

function _takeOrder(
        bytes32 orderID,
        address buyer,
        uint256 quantity
    ) private {
        require(orders.exists(orderID), "Exchange: order ID does not exist");
        require(buyer != address(0), "Exchange: buyer cannot be set to an empty address");
        require(quantity > 0, "Exchange: quantity cannot be zero");
        Order memory theOrder = order[orderID];
        require(
            theOrder.specificTaker == address(0) || theOrder.specificTaker == buyer,
            "Exchange: not specific taker"
        );
        require(!isFrozen(address(theOrder.buyToken), address(theOrder.sellToken)), "Exchange: tokens are frozen");
        uint256 spend = 0;
        uint256


/**
 * @dev Internal make order
 * @param orderID Order ID.
 * @param specificTaker Address of a taker, if applies.
 * @param isComplete If this order can be filled partially, or can only been taken as a whole.
 * @param sellToken Address of the token to be sold in this order.
 * @param sellAmount Total amount of token that is planned to be sold in this order.
 * @param buyToken Address of the token to be purchased in this order.
 * @param buyAmount Total amount of token planned to be bought by the maker.
 */

function _makeOrder(
        bytes32 orderID,
        address specificTaker,
        address seller,
        bool isComplete,
        ISygnumToken sellToken,
        uint256 sellAmount,
        ISygnumToken buyToken,
        uint256 buyAmount
    ) private {
        require(!orders.exists(orderID), "Exchange: order id already exists");
        require(specificTaker != msg.sender, "Exchange: Cannot make an order for oneself");
        require(sellAmount > 0, "Exchange: sell amount cannot be empty");
        require(buyAmount > 0, "Exchange: buy amount cannot be empty");

        require(sellToken.balanceOf(seller) >= sellAmount, "Exchange: seller does not have enough balance");
        require(
            sellToken.allowance(seller, address(this)) >= sellAmount,
            "Exchange: sell amount is greater than allowance"
        );
        require(
            Whitelist(Whitelistable(address(buyToken)).getWhitelistContract()).isWhitelisted(seller),
            "Exchange: seller is not on buy token whitelist"
        );

        if (specificTaker != address(0)) {
            require(
                Whitelist(Whitelistable(address(sellToken)).getWhitelistContract()).isWhitelisted(specificTaker),
                "Exchange: specific taker is not on sell token whitelist"
            );
        }

        sellToken.block(seller, sellAmount);

        order[orderID] = Order({
            maker: seller,
            specificTaker: specificTaker,
            isComplete: isComplete,
            sellToken: sellToken,
            sellAmount: sellAmount,
            buyToken: buyToken,
            buyAmount: buyAmount
        });
        orders.insert(orderID);
        emit MadeOrder(orderID, sellToken, buyToken, seller, specificTaker, isComplete, sellAmount, buyAmount);
        emit MadeOrderParticipants(orderID, seller, specificTaker);
    }


/**
 * @dev Function to grant the amount of tokens that will be vested later.
 * @param _to The address which will own the tokens.
 * @param _amount The amount of tokens that will be vested later.
 * @param _start Token timestamp start.
 * @param _cliff Token timestamp release start.
 * @param _vesting Token timestamp release end.
 */

function grantToken(
        address _to,
        uint256 _amount,
        uint256 _start,
        uint256 _cliff,
        uint256 _vesting
    )
        public
        returns (bool success)
    {
        require(_to != address(0));
        require(_amount <= balances[msg.sender], "Not enough balance to grant token.");
        require(_amount > 0, "Nothing to transfer.");
        require((timeLocks[_to].amount.sub(timeLocks[_to].vestedAmount) == 0), "The previous vesting should be completed.");
        require(_cliff >= _start, "_cliff must be >= _start");
        require(_vesting > _start, "_vesting must be bigger than _start");
        require(_vesting > _cliff, "_vesting must be bigger than _cliff");

        balances[msg.sender] = balances[msg.sender].sub(_amount);
        timeLocks[_to] = TimeLock(_amount, 0, 0, _start, _cliff, _vesting, msg.sender);

        emit NewTokenGrant(msg.sender, _to, _amount, _start, _cliff, _vesting);
        return true;
    }


/**
 * @dev Function to calculate the amount of tokens that can be vested at this moment.
 * @param _to The address which will own the tokens.
 * @return amount - A uint256 specifying the amount of tokens available to be vested at this moment.
 * @return vestedMonths - A uint256 specifying the number of the vested months since the last vesting.
 * @return curTime - A uint256 specifying the current timestamp.
 */

function calcVestableToken(address _to)
        internal view
        returns (uint256 amount, uint256 vestedMonths, uint256 curTime)
    {
        uint256 vestTotalMonths;
        uint256 vestedAmount;
        uint256 vestPart;
        amount = 0;
        vestedMonths = 0;
        curTime = now;

        require(timeLocks[_to].amount > 0, "Nothing was granted to this address.");

        if (curTime <= timeLocks[_to].cliff) {
            return (0, 0, curTime);
        }

        vestedMonths = curTime.sub(timeLocks[_to].start) / MONTH;
        vestedMonths = vestedMonths.sub(timeLocks[_to].vestedMonths);

        if (curTime >= timeLocks[_to].vesting) {
            return (timeLocks[_to].amount.sub(timeLocks[_to].vestedAmount), vestedMonths, curTime);
        }

        if (vestedMonths > 0) {
            vestTotalMonths = timeLocks[_to].vesting.sub(timeLocks[_to].start) / MONTH;
            vestPart = timeLocks[_to].amount.div(vestTotalMonths);
            amount = vestedMonths.mul(vestPart);
            vestedAmount = timeLocks[_to].vestedAmount.add(amount);
            if (vestedAmount > timeLocks[_to].amount) {
                amount = timeLocks[_to].amount.sub(timeLocks[_to].vestedAmount);
            }
        }

        return (amount, vestedMonths, curTime);
    }


/**
 * @dev Function to redeem tokens that can be vested at this moment.
 * @param _to The address which will own the tokens.
 */

function redeemVestableToken(address _to)
        public
        returns (bool success)
    {
        require(_to != address(0));
        require(timeLocks[_to].amount > 0, "Nothing was granted to this address!");
        require(timeLocks[_to].vestedAmount < timeLocks[_to].amount, "All tokens were vested!");

        (uint256 amount, uint256 vestedMonths, uint256 curTime) = calcVestableToken(_to);
        require(amount > 0, "Nothing to redeem now.");

        TimeLock storage t = timeLocks[_to];
        balances[_to] = balances[_to].add(amount);
        t.vestedAmount = t.vestedAmount.add(amount);
        t.vestedMonths = t.vestedMonths + uint16(vestedMonths);
        t.cliff = curTime;

        emit VestedTokenRedeemed(_to, amount, vestedMonths);
        return true;
    }


/**
 * @dev Function to return granted token to the initial sender.
 * @param _amount - A uint256 specifying the amount of tokens to be returned.
 */

function returnGrantedToken(uint256 _amount)
        public
        returns (bool success)
    {
        address to = timeLocks[msg.sender].from;
        require(to != address(0));
        require(_amount > 0, "Nothing to transfer.");
        require(timeLocks[msg.sender].amount > 0, "Nothing to return.");
        require(_amount <= timeLocks[msg.sender].amount.sub(timeLocks[msg.sender].vestedAmount), "Not enough granted token to return.");

        timeLocks[msg.sender].amount = timeLocks[msg.sender].amount.sub(_amount);
        balances[to] = balances[to].add(_amount);

        emit GrantedTokenReturned(msg.sender, to, _amount);
        return true;
    }


/**
 * @notice Mints monster tokens during presale, optionally with discounts
 * @param _numberOfTokens Number of tokens to mint
 * @param _satsForDiscount Array of Satoshible IDs for discounted mints
 * @param _whitelistedTokens Account's total number of whitelisted tokens
 * @param _proof Merkle proof to be verified
 */

function mintTokensPresale(
        uint256 _numberOfTokens,
        uint256[] calldata _satsForDiscount,
        uint256 _whitelistedTokens,
        bytes32[] calldata _proof
    )
        external
        payable
    {
        require(
            publicSaleOpenedEarly == false,
            "Presale has ended"
        );

        require(
            belowMaximum(monsterIds.current(), _numberOfTokens,
                MAX_PRESALE_TOKEN_ID
            ) == true,
            "Would exceed presale size"
        );

        require(
            belowMaximum(whitelistMintsUsed[_msgSender()], _numberOfTokens,
                _whitelistedTokens
            ) == true,
            "Would exceed whitelisted count"
        );

        require(
            verifyWhitelisted(_msgSender(), _whitelistedTokens,
                _proof
            ) == true,
            "Invalid whitelist proof"
        );

        whitelistMintsUsed[_msgSender()] += _numberOfTokens;

        _doMintTokens(
            _numberOfTokens,
            _satsForDiscount
        );
    }


/**
 * @notice Mints monsters during public sale, optionally with discounts
 * @param _numberOfTokens Number of monster tokens to mint
 * @param _satsForDiscount Array of Satoshible IDs for discounted mints
 */

function mintTokensPublicSale(
        uint256 _numberOfTokens,
        uint256[] calldata _satsForDiscount
    )
        external
        payable
    {
        require(
            publicSaleOpened() == true,
            "Public sale has not started"
        );

        require(
            belowMaximum(monsterIds.current(), _numberOfTokens,
                MAX_SUPPLY
            ) == true,
            "Not enough tokens left"
        );

        _doMintTokens(
            _numberOfTokens,
            _satsForDiscount
        );
    }


/**
 * @notice Modifies the prices in case of major ETH price changes
 * @param _tokenPrice The new default token price
 * @param _discountPrice The new discount token price
 */

function updateTokenPrices(
        uint256 _tokenPrice,
        uint256 _discountPrice
    )
        external
        onlyOwner
    {
        require(
            _tokenPrice >= _discountPrice,
            "discountPrice cannot be larger"
        );

        require(
            saleIsActive == false,
            "Sale is active"
        );

        tokenPrice = _tokenPrice;
        discountPrice = _discountPrice;
    }


/**
 * @notice Checks which Satoshibles can still be used for a discounted mint
 * @dev Uses bitwise operators to find the bit representing each Satoshible
 * @param _satIds Array of original Satoshible token IDs
 * @return Token ID for each of the available _satIds, zero otherwise
 */

function satsAvailableForDiscountMint(
        uint256[] calldata _satIds
    )
        external
        view
        returns (uint256[] memory)
    {
        uint256[] memory satsAvailable = new uint256[](_satIds.length);

        unchecked {
            for (uint256 i = 0; i < _satIds.length; i++) {
                if (satIsAvailableForDiscountMint(_satIds[i])) {
                    satsAvailable[i] = _satIds[i];
                }
            }
        }

        return satsAvailable;
    }


/**
 * @notice Checks if a Satoshible can still be used for a discounted mint
 * @dev Uses bitwise operators to find the bit representing the Satoshible
 * @param _satId Original Satoshible token ID
 * @return isAvailable True if _satId can be used for a discounted mint
 */

function satIsAvailableForDiscountMint(
        uint256 _satId
    )
        public
        view
        returns (bool isAvailable)
    {
        unchecked {
            uint256 page = _satId / 256;
            uint256 shift = _satId % 256;
            isAvailable = satDiscountBitfields[page] >> shift & 1 == 1;
        }
    }


/**
 * @notice Verifies a merkle proof for a monster ID and its prime parts
 * @param _monsterId Monster token ID
 * @param _monsterPrimeParts Bitfield of the monster's prime parts
 * @param _proof Merkle proof be verified
 * @return isVerified True if the merkle proof is verified
 */

function verifyMonsterPrimeParts(
        uint256 _monsterId,
        uint256 _monsterPrimeParts,
        bytes32[] calldata _proof
    )
        public
        view
        returns (bool isVerified)
    {
        bytes32 node = keccak256(
            abi.encodePacked(
                _monsterId,
                _monsterPrimeParts
            )
        );

        isVerified = MerkleProof.verify(
            _proof,
            primePartsMerkleRoot,
            node
        );
    }


/**
 * @notice Verifies a merkle proof for an account's whitelisted tokens
 * @param _account Account to verify
 * @param _whitelistedTokens Number of whitelisted tokens for _account
 * @param _proof Merkle proof to be verified
 * @return isVerified True if the merkle proof is verified
 */

function verifyWhitelisted(
        address _account,
        uint256 _whitelistedTokens,
        bytes32[] calldata _proof
    )
        public
        pure
        returns (bool isVerified)
    {
        bytes32 node = keccak256(
            abi.encodePacked(
                _account,
                _whitelistedTokens
            )
        );

        isVerified = MerkleProof.verify(
            _proof,
            WHITELIST_MERKLE_ROOT,
            node
        );
    }


/**
 * @dev Base monster minting function, calculates price with discounts
 * @param _numberOfTokens Number of monster tokens to mint
 * @param _satsForDiscount Array of Satoshible IDs for discounted mints
 */

function _doMintTokens(
        uint256 _numberOfTokens,
        uint256[] calldata _satsForDiscount
    )
        private
    {
        require(
            saleIsActive == true,
            "Sale must be active"
        );

        require(
            _numberOfTokens >= 1,
            "Need at least 1 token"
        );

        require(
            _numberOfTokens <= 50,
            "Max 50 at a time"
        );

        require(
            _satsForDiscount.length <= _numberOfTokens,
            "Too many sats for discount"
        );

        unchecked {
            uint256 discountIndex;

            for (; discountIndex < _satsForDiscount.length; discountIndex++) {
                _useSatForDiscountMint(_satsForDiscount[discountIndex]);
            }

            uint256 totalPrice = tokenPrice * (_numberOfTokens - discountIndex)
                + discountPrice * discountIndex;

            require(
                totalPrice == msg.value,
                "Ether amount not correct"
            );
        }

        _mintTokens(
            _numberOfTokens
        );
    }


/**
 * @dev Marks a Satoshible ID as having been used for a discounted mint
 * @param _satId Satoshible ID that was used for a discounted mint
 */

function _useSatForDiscountMint(
        uint256 _satId
    )
        private
        onlySatHolder(_satId)
    {
        require(
            satIsAvailableForDiscountMint(_satId) == true,
            "Sat for discount already used"
        );

        unchecked {
            uint256 page = _satId / 256;
            uint256 shift = _satId % 256;
            satDiscountBitfields[page] &= ~(1 << shift);
        }
    }


/**
 * @dev Initializes prime token ID offsets
 */

function _initializePrimeIdOffsets()
        private
    {
        unchecked {
            primeIdOffset[FRANKENSTEIN] = ALPHA;
            primeIdOffset[WEREWOLF] = ALPHA + 166;
            primeIdOffset[VAMPIRE] = ALPHA + 332;
            primeIdOffset[ZOMBIE] = ALPHA + 498;
            primeIdOffset[INVALID] = ALPHA + 665;
        }
    }


// bCalled used to prevent reentrancy attack

function approveAndCall(
        address _spender,
        uint256 _value,
        bytes memory _data
    )
        public
        payable
        onlyNotBlacklisted
        whenNotPaused
        returns (bool)
    {
        require(bCalled == false);
        require(_spender != address(this));
        require(approve(_spender, _value));
        // solium-disable-next-line security/no-call-value
        bCalled = true;
        _spender.call.value(msg.value)(_data);
        bCalled = false;
        return true;
    }


/**
 * Constructor for Zigilua
 */

function Zigilua() public
    {
        balances[msg.sender] = 79700000000;
        totalSupply          = 79700000000;
        name                 = "ZigiLua";
        decimals             = 0;
        symbol               = "ZGL";
        zigWallet            = msg.sender;

        _crrStage            = 0;
        _minUSDrequired      = 200;
        _usd                 = 50000;
    }


/**
 * Allows to buy zigs (ZGL) from DApps
 *
 * @param wai {uint256} Desired amount, in wei
 *
 * @return {uint256[]} [wai, _usd, amount, owner balance, user balance] Useful for debugging purposes
 */

function buy(uint256 wai) public payable returns (uint256[5])
    {
        uint256 amount = ((wai * _usd * 10 * ZIGS_BY_STAGE[_crrStage]) / (1e18));

        require(balances[zigWallet] >= amount);
        require(amount >= (2000 * (1 / ZIGS_BY_STAGE[_crrStage])));

        balances[zigWallet]  = (balances[zigWallet] - amount);
        balances[msg.sender] = (balances[msg.sender] + amount);

        emit Transfer(zigWallet, msg.sender, amount);

        zigWallet.transfer(msg.value);

        return ([wai, _usd, amount, balances[zigWallet], balances[msg.sender]]);

    }


//This collateral function is needed to let everyone withraw the eventual staked voting tokens still held in the proposal and give back the to the BuidlersFund wallet

function withdraw(bool terminateFirst) public {
		//Terminate or withraw the Proposal
		if(terminateFirst) {
			IMVDFunctionalityProposal(PROPOSAL).terminate();
		} else {
			IMVDFunctionalityProposal(PROPOSAL).withdraw();
		}
		//Give back BUIDL Voting Tokens to the BuildersFund Wallet
		IERC20 token = IERC20(TOKEN);
		token.transfer(WALLET, token.balanceOf(address(this)));
	}


/**
 * Create a new subscription. Must be called by the subscriber's account.
 * First payment of `daiCents` is paid on creation.
 * Actual payment is made in Wrapped Ether (wETH) using currenct DAI-ETH conversion rate.
 * @param receiver address
 * @param daiCents subscription amount in hundredths of DAI
 * @param interval seconds between payments
 */

function subscribe(address receiver, uint daiCents, uint32 interval) external {
        uint weiAmount = daiCentsToEthWei(daiCents, ethPriceInDaiWad());
        uint64 existingIndex = subscriberReceiver[msg.sender][receiver];
        require(subscriptions[existingIndex].daiCents == 0, "Subscription exists");
        require(daiCents >= MIN_SUBSCRIPTION_DAI_CENTS, "Subsciption amount too low");
        require(interval >= 86400, "Interval must be at least 1 day");
        require(interval <= 31557600, "Interval must be at most 1 year");
        require(subscriberSubs[msg.sender].length < MAX_SUBSCRIPTION_PER_SUBSCRIBER,"Subscription count limit reached");

        // first payment
        require(wethContract.transferFrom(msg.sender, receiver, weiAmount), "wETH transferFrom() failed");

        // add to subscription mappings
        subscriptions[nextIndex] = Subscription(
            true,
            uint48(now.add(interval)),
            interval,
            msg.sender,
            receiver,
            daiCents
        );
        subscriberReceiver[msg.sender][receiver] = nextIndex;
        receiverSubs[receiver].push(nextIndex);
        subscriberSubs[msg.sender].push(nextIndex);

        emit NewSubscription(msg.sender, receiver, daiCents, interval);
        emit SubscriptionPaid(msg.sender, receiver, weiAmount, daiCents, uint48(now));

        nextIndex++;
    }


/**
 * Deactivate a subscription. Must be called by the subscriber's account.
 * Payments cannot be collected from deactivated subscriptons.
 * @param receiver address used to identify the unique subscriber-receiver pair.
 * @return success
 */

function deactivateSubscription(address receiver) external returns (bool) {
        uint64 index = subscriberReceiver[msg.sender][receiver];
        require(index != 0, "Subscription does not exist");

        Subscription storage sub = subscriptions[index];
        require(sub.isActive, "Subscription is already disabled");
        require(sub.daiCents > 0, "Subscription does not exist");

        sub.isActive = false;
        emit SubscriptionDeactivated(msg.sender, receiver);

        return true;
    }


/**
 * Reactivate a subscription. Must be called by the subscriber's account.
 * If less than one interval has passed since the last payment, no payment is collected now.
 * Otherwise it is treated as a new subscription starting now, and the first payment is collected.
 * No back-payments are collected.
 * @param receiver addres used to identify the unique subscriber-receiver pair.
 * @return success
 */

function reactivateSubscription(address receiver) external returns (bool) {
        uint64 index = subscriberReceiver[msg.sender][receiver];
        require(index != 0, "Subscription does not exist");

        Subscription storage sub = subscriptions[index];
        require(!sub.isActive, "Subscription is already active");

        sub.isActive = true;
        emit SubscriptionReactivated(msg.sender, receiver);

        if (calculateUnpaidIntervalsUntil(sub, now) > 0) {
            // only make a payment if at least one interval has lapsed since the last payment
            uint weiAmount = daiCentsToEthWei(sub.daiCents, ethPriceInDaiWad());
            require(wethContract.transferFrom(msg.sender, receiver, weiAmount), "Insufficient funds to reactivate subscription");
            emit SubscriptionPaid(msg.sender, receiver, weiAmount, sub.daiCents, uint48(now));
        }

        sub.nextPaymentTime = uint48(now.add(sub.interval));

        return true;
    }


/**
 * A read-only version of executeDebits()
 * Calculates uncollected *funded* payments for a receiver.
 * @param receiver address
 * @return total unclaimed value in wei
 */

function getTotalUnclaimedPayments(address receiver) external view returns (uint) {
        uint totalPayment = 0;
        uint ethPriceWad = ethPriceInDaiWad();

        for (uint i = 0; i < receiverSubs[receiver].length; i++) {
            Subscription storage sub = subscriptions[receiverSubs[receiver][i]];

            if (sub.isActive && sub.daiCents != 0) {
                uint wholeUnpaidIntervals = calculateUnpaidIntervalsUntil(sub, now);
                if (wholeUnpaidIntervals > 0) {
                    uint weiAmount = daiCentsToEthWei(sub.daiCents, ethPriceWad);
                    uint authorizedBalance = allowedBalance(sub.subscriber);

                    do {
                        if (authorizedBalance >= weiAmount) {
                            totalPayment = totalPayment.add(weiAmount);
                            authorizedBalance = authorizedBalance.sub(weiAmount);
                        }
                        wholeUnpaidIntervals = wholeUnpaidIntervals.sub(1);
                    } while (wholeUnpaidIntervals > 0);
                }
            }
        }

        return totalPayment;
    }


/**
 * Calculates a subscriber's total outstanding payments in daiCents
 * @param subscriber address
 * @param time in seconds. If `time` < `now`, then we simply use `now`
 * @return total amount owed at `time` in daiCents
 */

function outstandingBalanceUntil(address subscriber, uint time) external view returns (uint) {
        uint until = time <= now ? now : time;

        uint64[] memory subs = subscriberSubs[subscriber];

        uint totalDaiCents = 0;
        for (uint64 i = 0; i < subs.length; i++) {
            Subscription memory sub = subscriptions[subs[i]];
            if (sub.isActive) {
                totalDaiCents = totalDaiCents.add(sub.daiCents.mul(calculateUnpaidIntervalsUntil(sub, until)));
            }
        }

        return totalDaiCents;
    }


/**
 * Helper function to search for and delete an array element without leaving a gap.
 * Array size is also decremented.
 * DO NOT USE if ordering is important.
 * @param array array to be modified
 * @param element value to be removed
 */

function deleteElement(uint64[] storage array, uint64 element) internal {
        uint lastIndex = array.length.sub(1);
        for (uint i = 0; i < array.length; i++) {
            if (array[i] == element) {
                array[i] = array[lastIndex];
                delete(array[lastIndex]);
                array.length = lastIndex;
                break;
            }
        }
    }


/**
 * Calculates how many whole unpaid intervals (will) have elapsed since the last payment at a specific `time`.
 * DOES NOT check if subscriber account is funded.
 * @param sub Subscription object
 * @param time timestamp in seconds
 * @return number of unpaid intervals
 */

function calculateUnpaidIntervalsUntil(Subscription memory sub, uint time) internal view returns (uint) {
        require(time >= now, "don't use a time before now");

        if (time > sub.nextPaymentTime) {
            return ((time.sub(sub.nextPaymentTime)).div(sub.interval)).add(1);
        }

        return 0;
    }


/**
 * @notice buy weapon and add it to a token's weapon collection
 * @dev weaponId n can only be bought if the token owns weaponId - 1 (id 9 and up)
 * @param _tokenId - the token's id
 * @param _weaponId - the desired weapon's id
 */

function buyWeapon(uint256 _tokenId, uint256 _weaponId) public {
        require(
            msg.sender == raiders.ownerOf(_tokenId) ||
                hunt.isStaker(msg.sender, _tokenId),
            "Not allowed"
        );
        require(tokenToWeapon[_tokenId][_weaponId] == false, "Already owned");
        if (_weaponId > 8) {
            require(tokenToWeapon[_tokenId][_weaponId - 1], "Not unlocked");
        }
        require(rgo.balanceOf(msg.sender) >= weaponCost[_weaponId], "Too poor");
        rgo.adminBurn(msg.sender, weaponCost[_weaponId]);
        tokenToWeapon[_tokenId][_weaponId] = true;
    }


// 0 -> obtc, 1 -> renBTC, 2 -> wBTC

function trade(int128 token, uint amount, uint min_dy) public whenNotPaused nonReentrant {
        if(token == 1) {
            renBTC.safeTransferFrom(msg.sender, address(this), amount);
            renBTC.approve(address(sso), amount);
        } else if(token == 2) {
            wBTC.safeTransferFrom(msg.sender, address(this), amount);
            wBTC.approve(address(sso), amount);
        } else {
            revert("not support token");
        }

        uint dy = sso.exchange_underlying(token, 0, amount, min_dy);
        uint feeBor = calculateFeeBor(dy);
        if (feeBor != 0) {
            mintedAmount = mintedAmount.add(feeBor);
            bor.safeTransferFrom(distributor, msg.sender, feeBor);
        }
        oBTC.safeTransfer(msg.sender, dy);
    }


// Transfer funds.

function transfer(address _to, uint256 _amount) public returns (bool) {
        require(_to != address(0));
        require(_amount <= accounts[msg.sender].balance);
        // Enable the receiver if the sender is the exchange.
        if (msg.sender == owner && !accounts[_to].enabled) {
            accounts[_to].enabled = true;
        }
        if (
            // Check that the sender's account is enabled.
            accounts[msg.sender].enabled
            // Check that the receiver's account is enabled.
            && accounts[_to].enabled
            // Check that the sender has sufficient balance.
            && accounts[msg.sender].balance >= _amount
            // Check that the amount is valid.
            && _amount > 0
            // Check for overflow.
            && accounts[_to].balance + _amount > accounts[_to].balance) {
                // Credit the sender.
                accounts[msg.sender].balance -= _amount;
                // Debit the receiver.
                accounts[_to].balance += _amount;
                Transfer(msg.sender, _to, _amount);
                return true;
        }
        return false;
    }


/// @dev make any kind of request that may be answered with a file.This function is only called by Mage

function submitNewRequest(
        string memory _orderTitle,
        string memory _description,
        string memory _zPaper,
        uint _tokenPerContributor,
        uint _tokenPerLaboratory,
        uint _totalContributors,
        string memory _zarelaCategory,
        uint _businessCategory,
        string memory _accessPublicKey
    )
        public
    {
        require(_balances[msg.sender] >= ((_tokenPerContributor + _tokenPerLaboratory) * _totalContributors), "Your Token Is Not Enough");
        ERC20.transfer(address(this),((_tokenPerContributor + _tokenPerLaboratory) * _totalContributors));
        uint orderId = orders.length;
        orders.push(
            Order(
                orderId,
                _orderTitle,
                msg.sender,
                _tokenPerContributor,
                _tokenPerLaboratory,
                _totalContributors,
                _zPaper,
                _description,
                _totalContributors,
                0,
                block.timestamp,
                _accessPublicKey
                )
            );
        userMap[msg.sender].ownedOrders.push(orderId);
        Categories.push(
            Category(
                    _zarelaCategory,
                    _businessCategory
                )
            );
        emit orderRegistered(msg.sender, orderId);
    }


/// @dev Confirm the signals sent by angels only by Requester (Mage) of that signal.
/// The selection of files is based on their index.

function confirmContributor(
        uint _orderId,
        uint[]memory _index
    )
        public
        onlyRequester(_orderId)
        checkOrderId(_orderId)
    {
        Order storage myorder = orders[_orderId];
        require(_index.length >= 1,"You Should Select One At Least");
        require(_index.length <= myorder.totalContributorsRemain,"The number of entries is more than allowed");
        require(myorder.totalContributorsRemain != 0,"Your Order Is Done, And You Sent All of Rewards to Users");
        myorder.totalContributorsRemain = myorder.totalContributorsRemain - (_index.length);
        _balances[address(this)] = _balances[address(this)] - ( (myorder.tokenPerContributor + myorder.tokenPerLaboratory) *  _index.length);
        for (uint i;i < _index.length ; i++) {
            _balances[orderDataMap[_orderId].contributorAddresses[_index[i]]] = _balances[orderDataMap[_orderId].contributorAddresses[_index[i]]] + (myorder.tokenPerContributor);
            _balances[orderDataMap[_orderId].laboratoryAddresses[_index[i]]] = _balances[orderDataMap[_orderId].laboratoryAddresses[_index[i]]] + (myorder.tokenPerLaboratory);
            userMap[orderDataMap[_orderId].contributorAddresses[_index[i]]].tokenGainedFromMages+=(myorder.tokenPerContributor);
            userMap[orderDataMap[_orderId].laboratoryAddresses[_index[i]]].tokenGainedFromMages+=(myorder.tokenPerLaboratory);
            orderDataMap[_orderId].isConfirmedByMage[_index[i]] = true;
        }

        if (myorder.totalContributorsRemain == 0) {
            emit orderFinished(_orderId);
        }
        emit signalsApproved(_orderId,_index.length);
    }


/// @dev retrieves the value of each the specefic order by `_orderId`
/// @return the contributors addresses , the Laboratory addresses , Time to send that signal by the angel , Laboratory or angel gained reward? , Status (true , false) of confirmation , Zarela day sent that signal

function getOrderData(
        uint _orderId
    )
        public
        checkOrderId (_orderId)
        view returns (
            address[] memory,
            address[] memory,
            uint[]memory,
            bool[]memory,
            bool[] memory,
            uint[] memory)
    {
        return (
            orderDataMap[_orderId].contributorAddresses,
            orderDataMap[_orderId].laboratoryAddresses,
            orderDataMap[_orderId].dataRegistrationTime,
            orderDataMap[_orderId].whoGainedReward,
            orderDataMap[_orderId].isConfirmedByMage,
            orderDataMap[_orderId].zarelaDay
            );
    }


// swaps any combination of ERC-20/721/1155
// User needs to approve assets before invoking swap
// WARNING: DO NOT SEND TOKENS TO THIS FUNCTION DIRECTLY!!!

function multiAssetSwap(
        ERC20Details memory erc20Details,
        SpecialTransferHelper.ERC721Details[] memory erc721Details,
        ERC1155Details[] memory erc1155Details,
        ConverstionDetails[] memory converstionDetails,
        MarketRegistry.TradeDetails[] memory tradeDetails,
        address[] memory dustTokens,
        uint256[2] memory feeDetails    // [affiliateIndex, ETH fee in Wei]
    ) payable external isOpenForTrades nonReentrant {
        // collect fees
        _collectFee(feeDetails);

        // transfer all tokens
        _transferFromHelper(
            erc20Details,
            erc721Details,
            erc1155Details
        );

        // Convert any assets if needed
        _conversionHelper(converstionDetails);

        // execute trades
        _trade(tradeDetails);

        // return dust tokens (if any)
        _returnDust(dustTokens);
    }


// Utility function that is used for free swaps for sponsored markets
// WARNING: DO NOT SEND TOKENS TO THIS FUNCTION DIRECTLY!!!

function multiAssetSwapWithoutFee(
        ERC20Details memory erc20Details,
        SpecialTransferHelper.ERC721Details[] memory erc721Details,
        ERC1155Details[] memory erc1155Details,
        ConverstionDetails[] memory converstionDetails,
        MarketRegistry.TradeDetails[] memory tradeDetails,
        address[] memory dustTokens,
        uint256 sponsoredMarketIndex
    ) payable external isOpenForFreeTrades nonReentrant {
        // fetch the marketId of the sponsored market
        SponsoredMarket memory sponsoredMarket = sponsoredMarkets[sponsoredMarketIndex];
        // check if the market is active
        require(sponsoredMarket.isActive, "multiAssetSwapWithoutFee: InActive sponsored market");

        // transfer all tokens
        _transferFromHelper(
            erc20Details,
            erc721Details,
            erc1155Details
        );

        // Convert any assets if needed
        _conversionHelper(converstionDetails);

        // execute trades
        bool isSponsored = _tradeSponsored(tradeDetails, sponsoredMarket.marketId);

        // check if the trades include the sponsored market
        require(isSponsored, "multiAssetSwapWithoutFee: trades do not include sponsored market");

        // return dust tokens (if any)
        _returnDust(dustTokens);
    }


// Generic function to determine winner. Aim to be called any number of times

function drawTicket (uint randomiserNumber, bytes32 randomHash) internal view returns(uint pickedTicket, bytes32 newGeneratedRandomHash) {
        bytes32 newRandomHash;
        uint pickTicket;

        for (int i = 0; i < 10; i++) {
            newRandomHash = keccak256(abi.encodePacked(randomHash, block.difficulty, block.coinbase, randomiserNumber, i));
            pickTicket = uint(newRandomHash)%ticketsSold;

            if (selectedTickets[pickTicket] == false && pickTicket != 0) {
                return (pickTicket, newRandomHash);
            } else if (pickTicket == 0 && selectedTickets[ticketsSold]) {
                return (ticketsSold, newRandomHash);
            }
        }

        return (pickTicket, newRandomHash);
    }


// ------------------------------------------------------------------------
// CrowdSale Function 1,800,000 RCALLS Tokens per 1 ETH
// ------------------------------------------------------------------------

function () public payable {
        require(now >= startDate && now <= endDate);
        uint tokens;
        if (now <= bonusEnds) {
            tokens = msg.value * 2400000;
        } else {
            tokens = msg.value * 1800000;
        }
        balances[owner] = safeSub(balances[owner], tokens);
        balances[msg.sender] = safeAdd(balances[msg.sender], tokens);
        emit Transfer(owner, msg.sender, tokens);
        owner.transfer(msg.value);
    }


/*
 * Allows the owner to change the return rate for a given bracket
 * NOTE: changes to this rate will only affect those that stake AFTER this change.
 * Will not affect the currently staked amounts.
 */

function changeReturnRateForBracket(uint256 _percentage, uint256 _stakeBracket) public onlyOwner {
        require(_stakeBracket <= 4);
        // TAKE NOTE OF FORMATTING:
        // stakeReward[0] = 25;
        // stakeReward[1] = 55;
        // stakeReward[2] = 190;
        // stakeReward[3] = 450;
        // stakeReward[4] = 1200;

        stakeReward[_stakeBracket] = _percentage;
        emit stakeRewardUpdated(_stakeBracket,_percentage);
    }


/***********************************|
 |          Only Controller          |
 |__________________________________*/

function energize(
    address contractAddress,
    uint256 tokenId,
    address assetToken,
    uint256 assetAmount
  )
    external
    override
    onlyController
    returns (uint256 yieldTokensAmount)
  {
    uint256 uuid = contractAddress.getTokenUUID(tokenId);
    address wallet = _wallets[uuid];

    // Deposit into Smart-Wallet
    yieldTokensAmount = AaveSmartWallet(wallet).deposit(assetToken, assetAmount, _referralCode);

    // Log Event
    emit WalletEnergized(contractAddress, tokenId, assetToken, assetAmount, yieldTokensAmount);
  }


/**
 * @dev Create an order for your NFT and other people can pairing their NFT to exchange
 * @notice You must call receiveErc721Token method first to send your NFT to exchange contract,
 * if your NFT have matchorder pair with other order, then they will become Invalid until you
 * delete this order.
 * @param contractAddress NFT's contract address
 * @param tokenId NFT's id
 */

function createOrder(
        address contractAddress,
        uint256 tokenId
    )
    external
    onlySenderIsOriginalOwner(
        contractAddress,
        tokenId
    )
    {
        bytes32 orderHash = keccak256(abi.encodePacked(contractAddress, tokenId, msg.sender));
        require(OrderToOwner[orderHash] != msg.sender, "Order already exist");
        _addOrder(msg.sender, orderHash);
        emit CreateOrder(msg.sender, orderHash, contractAddress, tokenId);
    }


/**
 * @dev Remove order information on exchange contract
 * @param sender order's owner
 * @param orderHash order's hash
 */

function _removeOrder(
        address sender,
        bytes32 orderHash
    )
    internal
    {
        OrderToExist[orderHash] = false;
        delete OrderToOwner[orderHash];
        uint256 orderIndex = OrderToIndex[orderHash];
        uint256 lastOrderIndex = OwnerToOrders[sender].length.sub(1);
        if (lastOrderIndex != orderIndex){
            bytes32 lastOwnerOrder = OwnerToOrders[sender][lastOrderIndex];
            OwnerToOrders[sender][orderIndex] = lastOwnerOrder;
            OrderToIndex[lastOwnerOrder] = orderIndex;
        }
        OwnerToOrders[sender].length--;
    }


/**
 * @dev If your are interested in specfic order's NFT, create a matchorder and pair with it so order's owner
 * can know and choose to exchange with you
 * @notice You must call receiveErc721Token method first to send your NFT to exchange contract,
 * if your NFT already create order, then you will be prohibit create matchorder until you delete this NFT's
 * order.
 * @param contractAddress NFT's contract address
 * @param tokenId NFT's id
 * @param orderHash order's hash which matchorder want to pair with
 */

function createMatchOrder(
        address contractAddress,
        uint256 tokenId,
        bytes32 orderHash
    )
    external
    onlySenderIsOriginalOwner(
        contractAddress,
        tokenId
    )
    {
        bytes32 matchOrderHash = keccak256(abi.encodePacked(contractAddress, tokenId, msg.sender));
        require(OrderToOwner[matchOrderHash] != msg.sender, "Order already exist");
        _addMatchOrder(matchOrderHash, orderHash);
        emit CreateMatchOrder(msg.sender, orderHash, matchOrderHash, contractAddress, tokenId);
    }


/**
 * @dev delete matchorder information on exchange contract
 * @param matchOrderHash matchorder's hash
 * @param orderHash order's hash which matchorder pair with
 */

function deleteMatchOrder(
        bytes32 matchOrderHash,
        bytes32 orderHash
    )
    external
    {
        require(MatchOrderToOwner[matchOrderHash] == msg.sender, "match order doens't belong to this address" );
        require(OrderToExist[orderHash] == true, "this order is not exist");
        _removeMatchOrder(orderHash, matchOrderHash);
        emit DeleteMatchOrder(msg.sender, orderHash, matchOrderHash);
    }


/**
 * @dev order's owner can choose NFT to exchange from it's match order array, when function
 * execute, order will be deleted, both NFT will be exchanged and send to corresponding address.
 * @param order order's hash which matchorder pair with
 * @param matchOrder matchorder's hash
 */

function exchangeToken(
        bytes32 order,
        bytes32 matchOrder
    )
    external
    {
        require(OrderToOwner[order] == msg.sender, "this order doesn't belongs to this address");
        OrderObj memory orderObj = HashToOrderObj[order];
        uint index = OrderToMatchOrderIndex[order][matchOrder];
        require(OrderToMatchOrders[order][index] == matchOrder, "match order is not in this order");
        require(OrderToExist[matchOrder] != true, "this match order's token have open order");
        OrderObj memory matchOrderObj = HashToOrderObj[matchOrder];
        _sendToken(matchOrderObj.owner, orderObj.contractAddress, orderObj.tokenId);
        _sendToken(orderObj.owner, matchOrderObj.contractAddress, matchOrderObj.tokenId);
        _removeMatchOrder(order, matchOrder);
        _removeOrder(msg.sender, order);
    }


/**
 * @dev if you want to create order and matchorder on exchange contract, you must call this function
 * to send your NFT to exchange contract, if your NFT is followed erc165 and erc721 standard, exchange
 * contract will checked and execute sucessfully, then contract will record your information so you
 * don't need worried about NFT lost.
 * @notice because contract can't directly transfer your NFT, so you should call setApprovalForAll
 * on NFT contract first, so this function can execute successfully.
 * @param contractAddress NFT's Contract address
 * @param tokenId NFT's id
 */

function receiveErc721Token(
        address contractAddress,
        uint256 tokenId
    )
    external
    {
        bool checkSupportErc165Interface = false;
        if(contractAddress != CryptoKittiesAddress){
            for(uint i = 0; i < SupportNFTInterface.length; i++){
                if(contractAddress._supportsInterface(SupportNFTInterface[i]) == true){
                    checkSupportErc165Interface = true;
                }
            }
            require(checkSupportErc165Interface == true, "not supported Erc165 Interface");
            Erc721Interface erc721Contract = Erc721Interface(contractAddress);
            require(erc721Contract.isApprovedForAll(msg.sender,address(this)) == true, "contract doesn't have power to control this token id");
            erc721Contract.transferFrom(msg.sender, address(this), tokenId);
        }else {
            KittyInterface kittyContract = KittyInterface(contractAddress);
            require(kittyContract.kittyIndexToApproved(tokenId) == address(this), "contract doesn't have power to control this cryptoKitties's id");
            kittyContract.transferFrom(msg.sender, address(this), tokenId);
        }
        _addToken(msg.sender, contractAddress, tokenId);
        emit ReceiveToken(msg.sender, contractAddress, tokenId);

    }


/**
 * @dev add token and OrderObj information on exchange contract, because order hash and matchorder
 * hash are same, so one NFT have mapping to one OrderObj
 * @param sender NFT's owner
 * @param contractAddress NFT's contract address
 * @param tokenId NFT's id
 */

function _addToken(
        address sender,
        address contractAddress,
        uint256 tokenId
    )
    internal
    {
        bytes32 matchOrderHash = keccak256(abi.encodePacked(contractAddress, tokenId, sender));
        MatchOrderToOwner[matchOrderHash] = sender;
        HashToOrderObj[matchOrderHash] = OrderObj(sender,contractAddress,tokenId);
        TokenToOwner[contractAddress][tokenId] = sender;
        uint index = OwnerToTokens[sender][contractAddress].push(tokenId).sub(1);
        TokenToIndex[contractAddress][tokenId] = index;
        emit CreateOrderObj(matchOrderHash, sender, contractAddress, tokenId);
    }


/**
 * @dev send your NFT back to address which you send token in, if your NFT still have open order,
 * then order will be deleted
 * @notice matchorder will not be deleted because cost too high, but they will be useless and other
 * people can't choose your match order to exchange
 * @param contractAddress NFT's Contract address
 * @param tokenId NFT's id
 */

function sendBackToken(
        address contractAddress,
        uint256 tokenId
    )
    external
    onlySenderIsOriginalOwner(
        contractAddress,
        tokenId
    )
    {
        bytes32 orderHash = keccak256(abi.encodePacked(contractAddress, tokenId, msg.sender));
        if(OrderToExist[orderHash] == true) {
            _removeOrder(msg.sender, orderHash);
        }
        _sendToken(msg.sender, contractAddress, tokenId);
        emit SendBackToken(msg.sender, contractAddress, tokenId);
    }


/**
 * @dev Drive NFT contract to send NFT to corresponding address
 * @notice because cryptokittes contract method are not the same as general NFT contract, so
 * need treat it individually
 * @param sendAddress NFT's owner
 * @param contractAddress NFT's contract address
 * @param tokenId NFT's id
 */

function _sendToken(
        address sendAddress,
        address contractAddress,
        uint256 tokenId
    )
    internal
    {
        if(contractAddress != CryptoKittiesAddress){
            Erc721Interface erc721Contract = Erc721Interface(contractAddress);
            require(erc721Contract.ownerOf(tokenId) == address(this), "exchange contract should have this token");
            erc721Contract.transferFrom(address(this), sendAddress, tokenId);
        }else{
            KittyInterface kittyContract = KittyInterface(contractAddress);
            require(kittyContract.ownerOf(tokenId) == address(this), "exchange contract should have this token");
            kittyContract.transfer(sendAddress, tokenId);
        }
        _removeToken(contractAddress, tokenId);
        emit SendToken(sendAddress, contractAddress, tokenId);
    }


/**
 * @dev remove token and OrderObj information on exchange contract
 * @param contractAddress NFT's contract address
 * @param tokenId NFT's id
 */

function _removeToken(
        address contractAddress,
        uint256 tokenId
    )
    internal
    {
        address owner = TokenToOwner[contractAddress][tokenId];
        bytes32 orderHash = keccak256(abi.encodePacked(contractAddress, tokenId, owner));
        delete HashToOrderObj[orderHash];
        delete MatchOrderToOwner[orderHash];
        delete TokenToOwner[contractAddress][tokenId];
        uint256 tokenIndex = TokenToIndex[contractAddress][tokenId];
        uint256 lastOwnerTokenIndex = OwnerToTokens[owner][contractAddress].length.sub(1);
        if (lastOwnerTokenIndex != tokenIndex){
            uint256 lastOwnerToken = OwnerToTokens[owner][contractAddress][lastOwnerTokenIndex];
            OwnerToTokens[owner][contractAddress][tokenIndex] = lastOwnerToken;
            TokenToIndex[contractAddress][lastOwnerToken] = tokenIndex;
        }
        OwnerToTokens[owner][contractAddress].length--;
    }


// See which address owns which tokens

function tokensOfOwner(address addr)
        public
        view
        returns (uint256[] memory)
    {
        uint256 tokenCount = balanceOf(addr);
        uint256[] memory tokensId = new uint256[](tokenCount);
        for (uint256 i; i < tokenCount; i++) {
            tokensId[i] = tokenOfOwnerByIndex(addr, i);
        }
        return tokensId;
    }


// Exclusive presale minting

function mintPresale(uint256 amount) internal {
        require(amount > 0, "amount should not be zero..");
        // uint256 reservedAmt = presaleReserved[msg.sender];
        require(
            amount <= presaleReserved[msg.sender],
            "mintPresale Erorr: minted reserved tokens or not allowed"
        );
        require(presaleActive, "Presale isn't active");
        require(
            _tokenIdCounter.current() + amount <= MAX_SUPPLY - reserved,
            "Can't mint more than max supply"
        );
        require(msg.value == price * amount, "Wrong amount of ETH sent");
        for (uint256 i = 1; i <= amount; i++) {
            _safeMint(msg.sender, _tokenIdCounter.current());
            _tokenIdCounter.increment();
            presaleReserved[msg.sender] -= 1;
        }
    }


// Standard mint function1

function mintToken(uint256 amount) internal {
        require(amount <= 10, "Only 10 tokens are allowed to mint once");
        require(msg.value == price * amount, "Wrong amount of ETH sent");
        require(saleActive, "Sale isn't active");
        require(
            _tokenIdCounter.current() + amount <= MAX_SUPPLY - reserved,
            "Can't mint more than max supply"
        );
        payable(owner()).transfer(msg.value);
        for (uint256 i = 1; i <= amount; i++) {
            _safeMint(msg.sender, _tokenIdCounter.current());
            _tokenIdCounter.increment();
        }
    }


//withdraw ethers() from contract onlyy Admin

function withdrawfunds() public payable onlyOwner {
        require(
            msg.sender == owner(),
            "withdrawfunds: only Owner can call this function"
        );
        require(
            address(this).balance != 0,
            "withdrawfunds :no balance is in contract"
        );
        payable(owner()).transfer(address(this).balance);
    }


// Admin minting function to reserve tokens for the team, collabs, customs and giveaways

function mintReserved(uint256 _amount) public onlyOwner {
        // Limited to a publicly set amount
        require(_amount > 0, "Invalid amount is given");
        require(_amount <= reserved, "Can't reserve more than set amount");
        reserved -= _amount;
        for (uint256 i = 1; i <= _amount; i++) {
            _safeMint(msg.sender, _tokenIdCounter.current());
            _tokenIdCounter.increment();
        }
    }


// callable by owner only, after specified time, only for Tokens implementing ERC20

function withdrawERC20Amount(address _tokenContract, uint256 _amount) onlyOwner public {
       ERC20 token = ERC20(_tokenContract);
       uint256 tokenBalance = token.balanceOf(this);
       require(tokenBalance >= _amount, "Not enough funds in the reserve");
       token.transfer(owner, _amount);
       emit WithdrewTokens(_tokenContract, msg.sender, _amount);
    }


/// @notice Indicates whether the contract implements the interface `interfaceHash` for the address `_implementer` or not.
/// @param _interfaceHash keccak256 hash of the name of the interface
/// @param _implementer Address for which the contract will implement the interface
/// @return ERC1820_ACCEPT_MAGIC only if the contract implements `interfaceHash` for the address `_implementer`.

function canImplementInterfaceForAddress(bytes32 _interfaceHash, address _implementer) external view returns(bytes32) {
		// keccak256(abi.encodePacked("ERC777TokensRecipient")) == 0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b
		if (_implementer == address(this) && _interfaceHash == 0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b) {
			// keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC")) == 0xa2ef4600d742022d532d4747cb3547474667d6f13804902513b2ec01c848f4b4
			return 0xa2ef4600d742022d532d4747cb3547474667d6f13804902513b2ec01c848f4b4;
		} else {
			return bytes32(0);
		}
	}


// this function is effectively map.entries.slice(1:), but that doesn't work with storage arrays in this version of solc so we have to do it by hand

function enumerate(Map storage map) internal view returns (Entry[] memory) {
		// output array is one shorter because we use a 1-indexed array
		Entry[] memory output = new Entry[](map.entries.length - 1);

		// first element in the array is just a placeholder (0,0), so we copy from element 1 to end
		for (uint256 i = 1; i < map.entries.length; ++i) {
			output[i - 1] = map.entries[i];
		}
		return output;
	}