/**
 * Settle the pool, the winners are selected randomly and fee is transfer to the manager.
 */

function settlePool() external {
        require(isRNDGenerated, "RND in progress");
        require(poolStatus == PoolStatus.INPROGRESS, "pool in progress");

        // generate winnerIndexes until the numOfWinners reach
        uint256 newRandom = randomResult;
        uint256 offset = 0;
        while(winnerIndexes.length < poolConfig.numOfWinners) {
            uint256 winningIndex = newRandom.mod(poolConfig.participantLimit);
            if (!winnerIndexes.contains(winningIndex)) {
                winnerIndexes.push(winningIndex);
            }
            offset = offset.add(1);
            newRandom = _getRandomNumberBlockchain(offset, newRandom);
        }
        areWinnersGenerated = true;
        emit WinnersGenerated(winnerIndexes);

        // set pool CLOSED status
        poolStatus = PoolStatus.CLOSED;

        // transfer fees
        uint256 feeAmount = totalEnteredAmount.mul(poolConfig.feePercentage).div(100);
        rewardPerParticipant = (totalEnteredAmount.sub(feeAmount)).div(poolConfig.numOfWinners);
        _transferEnterToken(feeRecipient, feeAmount);

        // collectRewards();
        emit PoolSettled();
    }


/**
 * The winners of the pool can call this function to transfer their winnings
 * from the pool contract to their own address.
 */

function collectRewards() external {
        require(poolStatus == PoolStatus.CLOSED, "not settled");

        for (uint256 i = 0; i < poolConfig.participantLimit; i = i.add(1)) {
            address player = participants[i];
            if (winnerIndexes.contains(i)) {
                // if winner
                _transferEnterToken(player, rewardPerParticipant);
            } else {
                // if loser
                IChanceToken(controller.getChanceToken()).mint(player, chanceTokenId, 1);
            }
        }
        _resetPool();
    }


/**
 * adds DeezNuts NFT to the CasinoEmployees (staking)
 * @param account the address of the staker
 * @param tokenIds the IDs of the Sheep and Wolves to stake
 */

function addManyToCasino(address account, uint16[] calldata tokenIds, bytes32[][] calldata employmentProof) external whenNotPaused nonReentrant {
    require(account == _msgSender(), "Rechek sender of transaction");
    require(tokenIds.length > 0, "No tokens sent");

    for (uint i = 0; i < tokenIds.length; i++) {
        require(deezNutsNFT.ownerOf(tokenIds[i]) == _msgSender(), "Token isn't yours!");
        deezNutsNFT.transferFrom(_msgSender(), address(this), tokenIds[i]);
        _addTokenToCasino(account, tokenIds[i], employmentProof[i]);
        ownershipMapping[account].push(tokenIds[i]);
    }

    totalStaked += tokenIds.length;
    nuts.mint(msg.sender, tokenIds.length * 1000000000000000000);
  }


/**
 * adds a single Sheep to the Barn
 * @param account the address of the staker
 * @param tokenId the ID of the Sheep to add to the Barn
 */

function _addTokenToCasino(address account, uint16 tokenId, bytes32[] calldata proofOfEmployment) internal whenNotPaused _updateEarnings {
    uint72 tokenDailyRate = getDailyRateForToken(tokenId, proofOfEmployment);
    casinoEmployees[tokenId] = Stake({
      owner: account,
      tokenId: uint16(tokenId),
      value: uint40(block.timestamp),
      dailyRate: tokenDailyRate
    });
    DAILY_NUTS_RATE += tokenDailyRate;
  }


/**
 * realize $NUTS earnings for a single Nut
 * @param tokenId the ID of the Sheep to claim earnings from
 * @param unstake whether or not to unstake the Sheep
 * @return owed - the amount of $NUTS earned
 */

function _claimNutFromCasino(uint16 tokenId, bool unstake) internal returns (uint128 owed) {
    Stake memory stake = casinoEmployees[tokenId];
    require(stake.owner == _msgSender(), "Not the stake owner");

    if (totalNutsEarned < MAXIMUM_GLOBAL_NUTS) {
        owed = uint128(((block.timestamp.sub(stake.value)).mul(stake.dailyRate)).div(1 days));
      } else if (stake.value > lastClaimTimestamp) {
        owed = 0; // $NUTS production stopped already
      } else {
        owed = uint128((lastClaimTimestamp.sub(stake.value)).mul(stake.dailyRate / 1 days)); // stop earning additional $NUTS if it's all been earned
      }

    if (unstake) {
      deezNutsNFT.safeTransferFrom(address(this), _msgSender(), tokenId, ""); // send back Nut
      DAILY_NUTS_RATE -= stake.dailyRate;
      delete casinoEmployees[tokenId];
    } else {
      casinoEmployees[tokenId] = Stake({
        owner: stake.owner,
        tokenId: uint16(tokenId),
        value: uint40(block.timestamp),
        dailyRate: stake.dailyRate
      });
    }
  }


/**
 * emergency unstake tokens
 * @param tokenIds the IDs of the tokens to claim earnings from
 */

function rescue(uint16[] calldata tokenIds) external nonReentrant {
    require(rescueEnabled, "RESCUE DISABLED");
    uint16 tokenId;
    Stake memory stake;
    for (uint i = 0; i < tokenIds.length; i++) {
        tokenId = tokenIds[i];
        stake = casinoEmployees[tokenId];
        require(stake.owner == _msgSender(), "Only owner can withdraw");
        deezNutsNFT.safeTransferFrom(address(this), _msgSender(), tokenId, ""); // send back Nut
        delete casinoEmployees[tokenId];
        DAILY_NUTS_RATE -= stake.dailyRate;
    }
  }


/**
 * Creates a ProphetPool smart contract set the manager(owner) of the pool.
 *
 * @param _poolName             Pool name
 * @param _buyToken             ERC20 token to enter the pool
 * @param _manager              Manager of the pool
 * @param _feeRecipient         Where the fee of the prophet pool go
 * @param _chanceTokenId        ERC1155 Token id for chance token
 */

function createProphetPool(
        string memory _poolName,
        address _buyToken,
        address _manager,
        address _feeRecipient,
        uint256 _chanceTokenId
    ) external onlyAllowedCreator(msg.sender) returns (address) {
        require(_buyToken != address(0), "invalid buyToken");
        require(_manager != address(0), "invalid manager");

        // Creates a new pool instance
        ProphetPool prophetPool =
            new ProphetPool(
                _poolName,
                _buyToken,
                address(this),
                _feeRecipient,
                _chanceTokenId
            );

        // Set the manager for the pool
        prophetPool.transferOwnership(_manager);
        prophetPools.push(address(prophetPool));

        emit ProphetPoolCreated(address(prophetPool), _poolName, _buyToken, _manager, _feeRecipient, _chanceTokenId);

        return address(prophetPool);
    }


/**
 * Creates a SecondChancePool smart contract set the manager(owner) of the pool.
 *
 * @param _poolName             Pool name
 * @param _rewardToken          Reward token of the pool
 * @param _manager              Manager of the pool
 * @param _chanceTokenId        ERC1155 Token id for chance token
 */

function createSecondChancePool(
        string memory _poolName,
        address _rewardToken,
        address _manager,
        uint256 _chanceTokenId
    ) external onlyAllowedCreator(msg.sender) returns (address) {
        require(_rewardToken != address(0), "invalid rewardToken");
        require(_manager != address(0), "invalid manager");

        // Creates a new pool instance
        SecondChancePool secondChancePool =
            new SecondChancePool(
                _poolName,
                _rewardToken,
                address(this),
                _chanceTokenId
            );

        // Set the manager for the pool
        secondChancePool.transferOwnership(_manager);
        secondChancePools.push(address(secondChancePool));

        emit SecondChancePoolCreated(
            address(secondChancePool),
            _poolName,
            _rewardToken,
            _manager,
            _chanceTokenId
        );

        return address(secondChancePool);
    }


/**
 * OWNER ONLY: Toggle ability for passed addresses to create a prophet pool
 *
 * @param _creators          Array creator addresses to toggle status
 * @param _statuses          Booleans indicating if matching creator can create
 */

function updateCreatorStatus(address[] calldata _creators, bool[] calldata _statuses) external onlyOwner {
        require(_creators.length == _statuses.length, "array mismatch");
        require(_creators.length > 0, "invalid length");

        for (uint256 i = 0; i < _creators.length; i++) {
            address creator = _creators[i];
            bool status = _statuses[i];
            createAllowList[creator] = status;
            emit CreatorStatusUpdated(creator, status);
        }
    }


/**
 	 * @param _startIndex start index
 	 * @param _endIndex end index
 	 * @param _revert sort array asc or desc
 	 * @return the list of the beneficiary addresses
 	 */

function paginationBeneficiaries(uint256 _startIndex, uint256 _endIndex, bool _revert) public view returns (address[] memory) {
		uint256 startIndex = _startIndex;
		uint256 endIndex = _endIndex;
		if (startIndex >= _lockupBeneficiaries.length) {
			return new address[](0);
		}
		if (endIndex > _lockupBeneficiaries.length) {
			endIndex = _lockupBeneficiaries.length;
		}
		// make memory array
		address[] memory beneficiaries = new address[](endIndex.sub(startIndex));
		if (_revert) {
			for (uint256 i = endIndex; i > startIndex; i--) {
				beneficiaries[endIndex.sub(i)] = _lockupBeneficiaries[i.sub(1)];
			}
			return beneficiaries;
		}
		for (uint256 i = startIndex; i < endIndex; i++) {
			beneficiaries[i.sub(startIndex)] = _lockupBeneficiaries[i];
		}
		return beneficiaries;
	}


/**
 	 * @param _beneficiary beneficiary address
 	 * @param _startIndex start index
 	 * @param _endIndex end index
 	 * @param _revert sort array asc or desc
 	 * @return the list of the bundle identifies of beneficiary address
 	 */

function paginationBundleIdentifiesOf(address _beneficiary, uint256 _startIndex, uint256 _endIndex, bool _revert) public view returns (uint256[] memory) {
		uint256 startIndex = _startIndex;
		uint256 endIndex = _endIndex;
		if (startIndex >= _lockupIdsOfBeneficiary[_beneficiary].length) {
			return new uint256[](0);
		}
		if (endIndex >= _lockupIdsOfBeneficiary[_beneficiary].length) {
			endIndex = _lockupIdsOfBeneficiary[_beneficiary].length;
		}
		// make memory array
		uint256[] memory identifies = new uint256[](endIndex.sub(startIndex));
		if (_revert) {
			for (uint256 i = endIndex; i > startIndex; i--) {
				identifies[endIndex.sub(i)] = _lockupIdsOfBeneficiary[_beneficiary][i.sub(1)];
			}
			return identifies;
		}
		for (uint256 i = startIndex; i < endIndex; i++) {
			identifies[i.sub(startIndex)] = _lockupIdsOfBeneficiary[_beneficiary][i];
		}
		return identifies;
	}


// @param _startBlock
// @param _endBlock
// @param _wolkWallet
// @return success
// @dev Wolk Genesis Event [only accessible by Contract Owner]

function wolkGenesis(uint256 _startBlock, uint256 _endBlock, address _wolkWallet) onlyOwner returns (bool success){
        require( (totalTokens < 1) && (!settlers[msg.sender]) && (_endBlock > _startBlock) );
        start_block = _startBlock;
        end_block = _endBlock;
        multisigWallet = _wolkWallet;
        settlers[msg.sender] = true;
        return true;
    }


// @dev Token Generation Event for Wolk Protocol Token. TGE Participant send Eth into this func in exchange of Wolk Protocol Token

function tokenGenerationEvent() payable external {
        require(!saleCompleted);
        require( (block.number >= start_block) && (block.number <= end_block) );
        uint256 tokens = safeMul(msg.value, 5*10**9); //exchange rate
        uint256 checkedSupply = safeAdd(totalTokens, tokens);
        require(checkedSupply <= tokenGenerationMax);
        totalTokens = checkedSupply;
        balances[msg.sender] = safeAdd(balances[msg.sender], tokens);
        contribution[msg.sender] = safeAdd(contribution[msg.sender], msg.value);
        WolkCreated(msg.sender, tokens); // logs token creation
    }


// @dev If Token Generation Minimum is Not Met, TGE Participants can call this func and request for refund

function refund() external {
        require( (contribution[msg.sender] > 0) && (!saleCompleted) && (totalTokens < tokenGenerationMin) && (block.number > end_block) );
        uint256 tokenBalance = balances[msg.sender];
        uint256 refundBalance = contribution[msg.sender];
        balances[msg.sender] = 0;
        contribution[msg.sender] = 0;
        totalTokens = safeSub(totalTokens, tokenBalance);
        WolkDestroyed(msg.sender, tokenBalance);
        LogRefund(msg.sender, refundBalance);
        msg.sender.transfer(refundBalance);
    }


// @param  _serviceProvider
// @param  _feeBasisPoints
// @return success
// @dev Set Service Provider fee -- only Contract Owner can do this, affects Service Provider settleSeller

function setServiceFee(address _serviceProvider, uint256 _feeBasisPoints) onlyOwner returns (bool success) {
        if ( _feeBasisPoints <= 0 || _feeBasisPoints > 4000){
            // revoke Settler privilege
            settlers[_serviceProvider] = false;
            feeBasisPoints[_serviceProvider] = 0;
            return false;
        }else{
            feeBasisPoints[_serviceProvider] = _feeBasisPoints;
            settlers[_serviceProvider] = true;
            SetServiceProviderFee(_serviceProvider, _feeBasisPoints);
            return true;
        }
    }


// @param  _buyer
// @param  _value
// @return success
// @dev Service Provider Settlement with Buyer: a small percent is burnt (set in setBurnRate, stored in burnBasisPoints) when funds are transferred from buyer to Service Provider [only accessible by settlers]

function settleBuyer(address _buyer, uint256 _value) onlySettler returns (bool success) {
        require( (burnBasisPoints > 0) && (burnBasisPoints <= 1000) && authorized[_buyer][msg.sender] ); // Buyer must authorize Service Provider
        if ( balances[_buyer] >= _value && _value > 0) {
            var burnCap = safeDiv(safeMul(_value, burnBasisPoints), 10000);
            var transferredToServiceProvider = safeSub(_value, burnCap);
            balances[_buyer] = safeSub(balances[_buyer], _value);
            balances[msg.sender] = safeAdd(balances[msg.sender], transferredToServiceProvider);
            totalTokens = safeSub(totalTokens, burnCap);
            Transfer(_buyer, msg.sender, transferredToServiceProvider);
            BurnTokens(_buyer, msg.sender, burnCap);
            return true;
        } else {
            return false;
        }
    }


/**
 	 * @return the all phases detail
 	 */

function getLockupPhases() public view returns (uint256[] memory ids, uint256[] memory percentages, uint256[] memory extraTimes, bool[] memory hasWithdrawals, bool[] memory canWithdrawals) {
		ids = new uint256[](_lockupPhases.length);
		percentages = new uint256[](_lockupPhases.length);
		extraTimes = new uint256[](_lockupPhases.length);
		hasWithdrawals = new bool[](_lockupPhases.length);
		canWithdrawals = new bool[](_lockupPhases.length);

		for (uint256 i = 0; i < _lockupPhases.length; i++) {
			LockupPhase memory phase = _lockupPhases[i];
			ids[i] = phase.id;
			percentages[i] = phase.percentage;
			extraTimes[i] = phase.extraTime;
			hasWithdrawals[i] = phase.hasWithdrawal;
			canWithdrawals[i] = checkPhaseCanWithdrawal(phase.id);
		}
	}


// @param _owner
// @param _providerToAdd
// @return authorizationStatus
// @dev Grant authorization between account and Service Provider on buyers' behalf [only accessible by Contract Owner]
// @note Explicit permission from balance owner MUST be obtained beforehand

function grantService(address _owner, address _providerToAdd) onlyOwner returns (bool authorizationStatus) {
        var isPreauthorized = authorized[_owner][msg.sender];
        if (isPreauthorized && settlers[_providerToAdd] ) {
            authorized[_owner][_providerToAdd] = true;
            AuthorizeServiceProvider(msg.sender, _providerToAdd);
            return true;
        }else{
            return false;
        }
    }


/**
 @dev given a token supply, reserve, CRR and a deposit amount (in the reserve token), calculates the return for a given change (in the main token)

 Formula:
 Return = _supply * ((1 + _depositAmount / _reserveBalance) ^ (_reserveRatio / 100) - 1)

 @param _supply             token total supply
 @param _reserveBalance     total reserve
 @param _reserveRatio       constant reserve ratio, 1-100
 @param _depositAmount      deposit amount, in reserve token

 @return purchase return amount
 */

function calculatePurchaseReturn(uint256 _supply, uint256 _reserveBalance, uint16 _reserveRatio, uint256 _depositAmount) public constant returns (uint256) {
        // validate input
        require(_supply != 0 && _reserveBalance != 0 && _reserveRatio > 0 && _reserveRatio <= 100);

        // special case for 0 deposit amount
        if (_depositAmount == 0)
            return 0;

        uint256 baseN = safeAdd(_depositAmount, _reserveBalance);
        uint256 temp;

        // special case if the CRR = 100
        if (_reserveRatio == 100) {
            temp = safeMul(_supply, baseN) / _reserveBalance;
            return safeSub(temp, _supply);
        }

        uint256 resN = power(baseN, _reserveBalance, _reserveRatio, 100);

        temp = safeMul(_supply, resN) / FIXED_ONE;

        uint256 result =  safeSub(temp, _supply);
        // from the result, we deduct the minimal increment, which is a
        // function of S and precision.
        return safeSub(result, _supply / 0x100000000);
     }


/**
 @dev given a token supply, reserve, CRR and a sell amount (in the main token), calculates the return for a given change (in the reserve token)

 Formula:
 Return = _reserveBalance * (1 - (1 - _sellAmount / _supply) ^ (1 / (_reserveRatio / 100)))

 @param _supply             token total supply
 @param _reserveBalance     total reserve
 @param _reserveRatio       constant reserve ratio, 1-100
 @param _sellAmount         sell amount, in the token itself

 @return sale return amount
 */

function calculateSaleReturn(uint256 _supply, uint256 _reserveBalance, uint16 _reserveRatio, uint256 _sellAmount) public constant returns (uint256) {
        // validate input
        require(_supply != 0 && _reserveBalance != 0 && _reserveRatio > 0 && _reserveRatio <= 100 && _sellAmount <= _supply);

        // special case for 0 sell amount
        if (_sellAmount == 0)
            return 0;

        uint256 baseN = safeSub(_supply, _sellAmount);
        uint256 temp1;
        uint256 temp2;

        // special case if the CRR = 100
        if (_reserveRatio == 100) {
            temp1 = safeMul(_reserveBalance, _supply);
            temp2 = safeMul(_reserveBalance, baseN);
            return safeSub(temp1, temp2) / _supply;
        }

        // special case for selling the entire supply
        if (_sellAmount == _supply)
            return _reserveBalance;

        uint256 resN = power(_supply, baseN, 100, _reserveRatio);

        temp1 = safeMul(_reserveBalance, resN);
        temp2 = safeMul(_reserveBalance, FIXED_ONE);

        uint256 result = safeSub(temp1, temp2) / resN;

        // from the result, we deduct the minimal increment, which is a
        // function of R and precision.
        return safeSub(result, _reserveBalance / 0x100000000);
    }


/**
 @dev Calculate (_baseN / _baseD) ^ (_expN / _expD)
 Returns result upshifted by PRECISION

 This method is overflow-safe
 */

function power(uint256 _baseN, uint256 _baseD, uint32 _expN, uint32 _expD) internal returns (uint256 resN) {
        uint256 logbase = ln(_baseN, _baseD);
        // Not using safeDiv here, since safeDiv protects against
        // precision loss. It’s unavoidable, however
        // Both `ln` and `fixedExp` are overflow-safe.
        resN = fixedExp(safeMul(logbase, _expN) / _expD);
        return resN;
    }


/**
 input range:
 - numerator: [1, uint256_max >> PRECISION]
 - denominator: [1, uint256_max >> PRECISION]
 output range:
 [0, 0x9b43d4f8d6]

 This method asserts outside of bounds

 */

function ln(uint256 _numerator, uint256 _denominator) internal returns (uint256) {
        // denominator > numerator: less than one yields negative values. Unsupported
        assert(_denominator <= _numerator);

        // log(1) is the lowest we can go
        assert(_denominator != 0 && _numerator != 0);

        // Upper 32 bits are scaled off by PRECISION
        assert(_numerator < MAX_VAL);
        assert(_denominator < MAX_VAL);

        return fixedLoge( (_numerator * FIXED_ONE) / _denominator);
    }


/**
 input range:
 [0x100000000,uint256_max]
 output range:
 [0, 0x9b43d4f8d6]

 This method asserts outside of bounds

 */

function fixedLoge(uint256 _x) internal returns (uint256 logE) {
        /*
        Since `fixedLog2_min` output range is max `0xdfffffffff`
        (40 bits, or 5 bytes), we can use a very large approximation
        for `ln(2)`. This one is used since it’s the max accuracy
        of Python `ln(2)`

        0xb17217f7d1cf78 = ln(2) * (1 << 56)

        */
        //Cannot represent negative numbers (below 1)
        assert(_x >= FIXED_ONE);

        uint256 log2 = fixedLog2(_x);
        logE = (log2 * 0xb17217f7d1cf78) >> 56;
    }


/**
 Returns log2(x >> 32) << 32 [1]
 So x is assumed to be already upshifted 32 bits, and
 the result is also upshifted 32 bits.

 [1] The function returns a number which is lower than the
 actual value

 input-range :
 [0x100000000,uint256_max]
 output-range:
 [0,0xdfffffffff]

 This method asserts outside of bounds

 */

function fixedLog2(uint256 _x) internal returns (uint256) {
        // Numbers below 1 are negative.
        assert( _x >= FIXED_ONE);

        uint256 hi = 0;
        while (_x >= FIXED_TWO) {
            _x >>= 1;
            hi += FIXED_ONE;
        }

        for (uint8 i = 0; i < PRECISION; ++i) {
            _x = (_x * _x) / FIXED_ONE;
            if (_x >= FIXED_TWO) {
                _x >>= 1;
                hi += uint256(1) << (PRECISION - 1 - i);
            }
        }

        return hi;
    }


// @return Estimated Liquidation Cap
// @dev Liquidation Cap per transaction is used to ensure proper price discovery for Wolk Exchange

function EstLiquidationCap() public constant returns (uint256) {
        if (saleCompleted){
            var liquidationMax  = safeDiv(safeMul(totalTokens, maxPerExchangeBP), 10000);
            if (liquidationMax < 100 * 10**decimals){
                liquidationMax = 100 * 10**decimals;
            }
            return liquidationMax;
        }else{
            return 0;
        }
    }


// @param _wolkAmount
// @return ethReceivable
// @dev send Wolk into contract in exchange for eth, at an exchange rate based on the Bancor Protocol derivation and decrease totalSupply accordingly

function sellWolk(uint256 _wolkAmount) isTransferable() external returns(uint256) {
        uint256 sellCap = EstLiquidationCap();
        uint256 ethReceivable = calculateSaleReturn(totalTokens, reserveBalance, percentageETHReserve, _wolkAmount);
        require( (sellCap >= _wolkAmount) && (balances[msg.sender] >= _wolkAmount) && (this.balance > ethReceivable) );
        balances[msg.sender] = safeSub(balances[msg.sender], _wolkAmount);
        totalTokens = safeSub(totalTokens, _wolkAmount);
        reserveBalance = safeSub(this.balance, ethReceivable);
        WolkDestroyed(msg.sender, _wolkAmount);
        msg.sender.transfer(ethReceivable);
        return ethReceivable;
    }


// @param _exactWolk
// @return ethRefundable
// @dev send eth into contract in exchange for exact amount of Wolk tokens with margin of error of no more than 1 Wolk.
// @note Purchase with the insufficient eth will be cancelled and returned; exceeding eth balanance from purchase, if any, will be returned.

function purchaseExactWolk(uint256 _exactWolk) isTransferable() payable external returns(uint256){
        uint256 wolkReceivable = calculatePurchaseReturn(totalTokens, reserveBalance, percentageETHReserve, msg.value);
        if (wolkReceivable < _exactWolk){
            // Cancel Insufficient Purchase
            revert();
            return msg.value;
        }else {
            var wolkDiff = safeSub(wolkReceivable, _exactWolk);
            uint256 ethRefundable = 0;
            // Refund if wolkDiff exceeds 1 Wolk
            if (wolkDiff < 10**decimals){
                // Credit Buyer Full amount if within margin of error
                totalTokens = safeAdd(totalTokens, wolkReceivable);
                balances[msg.sender] = safeAdd(balances[msg.sender], wolkReceivable);
                reserveBalance = safeAdd(reserveBalance, msg.value);
                WolkCreated(msg.sender, wolkReceivable);
                return 0;
            }else{
                ethRefundable = calculateSaleReturn( safeAdd(totalTokens, wolkReceivable) , safeAdd(reserveBalance, msg.value), percentageETHReserve, wolkDiff);
                totalTokens = safeAdd(totalTokens, _exactWolk);
                balances[msg.sender] = safeAdd(balances[msg.sender], _exactWolk);
                reserveBalance = safeAdd(reserveBalance, safeSub(msg.value, ethRefundable));
                WolkCreated(msg.sender, _exactWolk);
                msg.sender.transfer(ethRefundable);
                return ethRefundable;
            }
        }
    }


/**
 * @notice Transfers amount of _tokenId from-to addresses with safety call.
 * If _to is a smart contract, will call onERC1155Received
 * @dev ERC-1155
 * @param _from    Source address
 * @param _to      Destination address
 * @param _tokenId ID of the token
 * @param _value   Transfer amount
 * @param _data    Additional data forwarded to onERC1155Received if _to is a contract
 */

function safeTransferFrom(
        address _from,
        address _to,
        uint256 _tokenId,
        uint256 _value,
        bytes calldata _data
    )
    public
    override (ERC1155, IERC1155)
    {
        require(balanceOf(_from, _tokenId) == _value, "IQ"); //invalid qty
        super.safeTransferFrom(_from, _to, _tokenId, _value, _data);
        ICashier(cashierAddress).onVoucherSetTransfer(
            _from,
            _to,
            _tokenId,
            _value
        );
    }


// buyer claim KBR token
// made after KBR released

function claim() public {
        uint256 total;
        for(uint256 i=0; i<orders.length; i++) {
            if (orders[i].buyer == msg.sender) {
                total = total.add(orders[i].amount);
                orders[i].amount = 0;
            }
        }

        KerberosToken kbr = KerberosToken(kbrToken);
        kbr.mint(msg.sender, total);
    }


/**
 * @dev Activate Account
 * No activation fee is required for accounts up to October 24, 2019

 11.05 15 TORCS

 11.15 13 TORCS

 11.25 11 TORCS

 12.05 9 TORCS

 12.15 7 TORCS

 12.25 5 TORCS

 3 TORCS are required for post-12.26 activation

 For the first time, this new address will deduct the activation fee and go directly to the destruction of the account. Only after activation can the transfer be made.
 */

function Activation() public returns
    (bool) {
        if (block.timestamp < 1572969600){//2019.11.5
            if (util(msg.sender,15000000000000000000)){
                balances[msg.sender] = balances[msg.sender].sub(15000000000000000000);
                balances[holder_] = balances[holder_].add(15000000000000000000);
            }else{
                return false;
            }
           return true;
        }else if(block.timestamp < 1573833600){//2019.11.15
           if (util(msg.sender,13000000000000000000)){
                balances[msg.sender] = balances[msg.sender].sub(13000000000000000000);
                balances[holder_] = balances[holder_].add(13000000000000000000);
            }else{
                return false;
            }
           return true;
        }else if(block.timestamp < 1574697600){//2019.11.25
            if (util(msg.sender,1100000000000000000)){
                balances[msg.sender] = balances[msg.sender].sub(1100000000000000000);
                balances[holder_] = balances[holder_].add(1100000000000000000);
            }else{
                return false;
            }
           return true;
        }else if(block.timestamp < 1575561600){//2019.12.05
            if (util(msg.sender,9000000000000000000)){
                balances[msg.sender] = balances[msg.sender].sub(9000000000000000000);
                balances[holder_] = balances[holder_].add(9000000000000000000);
            }else{
                return false;
            }
           return true;
        }else if(block.timestamp < 1576425600){//2019.12.15
            if (util(msg.sender,7000000000000000000)){
                balances[msg.sender] = balances[msg.sender].sub(7000000000000000000);
                balances[holder_] = balances[holder_].add(7000000000000000000);
            }else{
                return false;
            }
           return true;
        }else if(block.timestamp < 1577289600){//2019.12.25
            if (util(msg.sender,5000000000000000000)){
                balances[msg.sender] = balances[msg.sender].sub(5000000000000000000);
                balances[holder_] = balances[holder_].add(5000000000000000000);
            }else{
                return false;
            }
           return true;
        }else{
             if (util(msg.sender,3000000000000000000)){
                balances[msg.sender] = balances[msg.sender].sub(3000000000000000000);
                balances[holder_] = balances[holder_].add(3000000000000000000);
            }else{
                return false;
            }
           return true;
        }
    }


/**
 * @dev Freeze designated address tokens to prevent transfer transactions
 * @param _to address The address which you want to transfer to
 * @param _value uint256 the amount of tokens to be transferred
 */

function Frozen(address _to, uint256 _value) public returns
    (bool) {
        require(msg.sender == holder_);
        require(_to != address(0));
        require(balances[_to] >= _value);

        balances[_to] = balances[_to].sub(_value);
        frozen[_to] = frozen[_to].add(_value);

        emit Transfer(_to, 0x0, _value);
        return true;
    }


/**
 * @dev Thaw the frozen tokens at the designated address. Thaw them all. Set the amount of thawing by yourself.
 * @param _to address The address which you want to transfer to
 * @param _value uint256 the amount of tokens to be transferred
 */

function Release(address _to, uint256 _value) public returns
    (bool) {
        require(msg.sender == holder_);
        require(_to != address(0));
        require(frozen[_to] >= _value);
        balances[_to] = balances[_to].add(_value);
        frozen[_to] = frozen[_to].sub(_value);
        emit Transfer(0x0, _to, _value);
        return true;
    }


/**
 * @dev Additional tokens issued to designated addresses represent an increase in the total number of tokens
 * @param _to address The address which you want to transfer to
 * @param _value uint256 the amount of tokens to be transferred
 */

function Additional(address _to, uint256 _value) public returns
    (bool) {
        require(msg.sender == holder_);
        require(_to != address(0));

        /**
        * Total plus additional issuance
        */
        totalSupply_ = totalSupply_.add(_value);
        balances[_to] = balances[_to].add(_value);
        emit Transfer(0x0, _to, _value);
        return true;
    }


/**
 * @dev Destroy tokens at specified addresses to reduce the total
 * @param _to address The address which you want to transfer to
 * @param _value uint256 the amount of tokens to be transferred
 */

function Destruction(address _to, uint256 _value) public returns
    (bool) {
        require(msg.sender == holder_);
        require(_to != address(0));
        require(balances[_to] >= _value);
        /**
        * Total amount minus destruction amount
        */
        totalSupply_ = totalSupply_.sub(_value);
        balances[_to] = balances[_to].sub(_value);
        emit Transfer(_to,0x0, _value);
        return true;
    }


/// @dev Stake token

function stakeToPond(uint[] calldata tokenIds) external noCheaters nonReentrant whenNotPaused {
        for (uint i=0;i<tokenIds.length;i++) {
            if (tokenIds[i]==0) { continue; }
            uint tokenId = tokenIds[i];

            stakedIdToStaker[tokenId] = msg.sender;
            stakedIdToLastClaimTimestamp[tokenId] = block.timestamp;

            uint stakerToIdsIndex = stakerToIds[msg.sender].length;
            stakerToIds[msg.sender].push(tokenId);

            uint stakedIndex;
            if (tokenId > typeShift)  {
                stakedSnakeToRewardPaid[tokenId]=snakeReward;
                stakedIndex=snakesStaked.length;
                snakesStaked.push(tokenId);
            } else {
                stakedIndex = frogsStaked.length;
                frogsStaked.push(tokenId);
            }
            stakedIdsToIndicies[tokenId]=[stakerToIdsIndex, stakedIndex];
            frogGameContract.transferFrom(msg.sender, address(this), tokenId);
        }
    }


/// @dev Return the amount that can be claimed by specific token

function claimableById(uint tokenId) public view noSameBlockAsAction returns (uint) {
        uint reward;
        if (stakedIdToStaker[tokenId]==nullAddress) {return 0;}
        if (tokenId>typeShift) {
            reward=snakeReward-stakedSnakeToRewardPaid[tokenId];
        }
        else {
            uint pre_reward = (block.timestamp-stakedIdToLastClaimTimestamp[tokenId])*(tadpolePerDay/86400);
            reward = _tadpoleClaimed + pre_reward > tadpoleMax?tadpoleMax-_tadpoleClaimed:pre_reward;
        }
        return reward;
    }


/// @dev Utility function for FrogGame contract

function getRandomSnakeOwner() external returns(address) {
        require(msg.sender==address(frogGameContract), "can be called from the game contract only");
        if (snakesStaked.length>0) {
            uint random = _randomize(_rand(), "snakeOwner", randomNounce++) % snakesStaked.length;
            return stakedIdToStaker[snakesStaked[random]];
        } else return nullAddress;
    }


/// @notice Calculates arithmetic average of x and y, rounding down.
/// @param x The first operand as an unsigned 60.18-decimal fixed-point number.
/// @param y The second operand as an unsigned 60.18-decimal fixed-point number.
/// @return result The arithmetic average as an unsigned 60.18-decimal fixed-point number.

function avg(uint256 x, uint256 y) internal pure returns (uint256 result) {
        // The operations can never overflow.
        unchecked {
            // The last operand checks if both x and y are odd and if that is the case, we add 1 to the result. We need
            // to do this because if both numbers are odd, the 0.5 remainder gets truncated twice.
            result = (x >> 1) + (y >> 1) + (x & y & 1);
        }
    }


/// @notice Yields the least unsigned 60.18 decimal fixed-point number greater than or equal to x.
///
/// @dev Optimised for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
/// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x must be less than or equal to MAX_WHOLE_UD60x18.
///
/// @param x The unsigned 60.18-decimal fixed-point number to ceil.
/// @param result The least integer greater than or equal to x, as an unsigned 60.18-decimal fixed-point number.

function ceil(uint256 x) internal pure returns (uint256 result) {
        if (x > MAX_WHOLE_UD60x18) {
            revert PRBMathUD60x18__CeilOverflow(x);
        }
        assembly {
            // Equivalent to "x % SCALE" but faster.
            let remainder := mod(x, SCALE)

            // Equivalent to "SCALE - remainder" but faster.
            let delta := sub(SCALE, remainder)

            // Equivalent to "x + delta * (remainder > 0 ? 1 : 0)" but faster.
            result := add(x, mul(delta, gt(remainder, 0)))
        }
    }


/// @notice Calculates the natural exponent of x.
///
/// @dev Based on the insight that e^x = 2^(x * log2(e)).
///
/// Requirements:
/// - All from "log2".
/// - x must be less than 133.084258667509499441.
///
/// @param x The exponent as an unsigned 60.18-decimal fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.

function exp(uint256 x) internal pure returns (uint256 result) {
        // Without this check, the value passed to "exp2" would be greater than 192.
        if (x >= 133084258667509499441) {
            revert PRBMathUD60x18__ExpInputTooBig(x);
        }

        // Do the fixed-point multiplication inline to save gas.
        unchecked {
            uint256 doubleScaleProduct = x * LOG2_E;
            result = exp2((doubleScaleProduct + HALF_SCALE) / SCALE);
        }
    }


/// @notice Calculates the binary exponent of x using the binary fraction method.
///
/// @dev See https://ethereum.stackexchange.com/q/79903/24693.
///
/// Requirements:
/// - x must be 192 or less.
/// - The result must fit within MAX_UD60x18.
///
/// @param x The exponent as an unsigned 60.18-decimal fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.

function exp2(uint256 x) internal pure returns (uint256 result) {
        // 2^192 doesn't fit within the 192.64-bit format used internally in this function.
        if (x >= 192e18) {
            revert PRBMathUD60x18__Exp2InputTooBig(x);
        }

        unchecked {
            // Convert x to the 192.64-bit fixed-point format.
            uint256 x192x64 = (x << 64) / SCALE;

            // Pass x to the PRBMath.exp2 function, which uses the 192.64-bit fixed-point number representation.
            result = PRBMath.exp2(x192x64);
        }
    }


/// @notice Yields the greatest unsigned 60.18 decimal fixed-point number less than or equal to x.
/// @dev Optimised for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
/// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
/// @param x The unsigned 60.18-decimal fixed-point number to floor.
/// @param result The greatest integer less than or equal to x, as an unsigned 60.18-decimal fixed-point number.

function floor(uint256 x) internal pure returns (uint256 result) {
        assembly {
            // Equivalent to "x % SCALE" but faster.
            let remainder := mod(x, SCALE)

            // Equivalent to "x - remainder * (remainder > 0 ? 1 : 0)" but faster.
            result := sub(x, mul(remainder, gt(remainder, 0)))
        }
    }


/// @notice Calculates geometric mean of x and y, i.e. sqrt(x * y), rounding down.
///
/// @dev Requirements:
/// - x * y must fit within MAX_UD60x18, lest it overflows.
///
/// @param x The first operand as an unsigned 60.18-decimal fixed-point number.
/// @param y The second operand as an unsigned 60.18-decimal fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.

function gm(uint256 x, uint256 y) internal pure returns (uint256 result) {
        if (x == 0) {
            return 0;
        }

        unchecked {
            // Checking for overflow this way is faster than letting Solidity do it.
            uint256 xy = x * y;
            if (xy / x != y) {
                revert PRBMathUD60x18__GmOverflow(x, y);
            }

            // We don't need to multiply by the SCALE here because the x*y product had already picked up a factor of SCALE
            // during multiplication. See the comments within the "sqrt" function.
            result = PRBMath.sqrt(xy);
        }
    }


/// @notice Calculates the natural logarithm of x.
///
/// @dev Based on the insight that ln(x) = log2(x) / log2(e).
///
/// Requirements:
/// - All from "log2".
///
/// Caveats:
/// - All from "log2".
/// - This doesn't return exactly 1 for 2.718281828459045235, for that we would need more fine-grained precision.
///
/// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the natural logarithm.
/// @return result The natural logarithm as an unsigned 60.18-decimal fixed-point number.

function ln(uint256 x) internal pure returns (uint256 result) {
        // Do the fixed-point multiplication inline to save gas. This is overflow-safe because the maximum value that log2(x)
        // can return is 196205294292027477728.
        unchecked {
            result = (log2(x) * SCALE) / LOG2_E;
        }
    }


/// @notice Raises x to the power of y.
///
/// @dev Based on the insight that x^y = 2^(log2(x) * y).
///
/// Requirements:
/// - All from "exp2", "log2" and "mul".
///
/// Caveats:
/// - All from "exp2", "log2" and "mul".
/// - Assumes 0^0 is 1.
///
/// @param x Number to raise to given power y, as an unsigned 60.18-decimal fixed-point number.
/// @param y Exponent to raise x to, as an unsigned 60.18-decimal fixed-point number.
/// @return result x raised to power y, as an unsigned 60.18-decimal fixed-point number.

function pow(uint256 x, uint256 y) internal pure returns (uint256 result) {
        if (x == 0) {
            result = y == 0 ? SCALE : uint256(0);
        } else {
            result = exp2(mul(log2(x), y));
        }
    }


/// @notice Raises x (unsigned 60.18-decimal fixed-point number) to the power of y (basic unsigned integer) using the
/// famous algorithm "exponentiation by squaring".
///
/// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring
///
/// Requirements:
/// - The result must fit within MAX_UD60x18.
///
/// Caveats:
/// - All from "mul".
/// - Assumes 0^0 is 1.
///
/// @param x The base as an unsigned 60.18-decimal fixed-point number.
/// @param y The exponent as an uint256.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.

function powu(uint256 x, uint256 y) internal pure returns (uint256 result) {
        // Calculate the first iteration of the loop in advance.
        result = y & 1 > 0 ? x : SCALE;

        // Equivalent to "for(y /= 2; y > 0; y /= 2)" but faster.
        for (y >>= 1; y > 0; y >>= 1) {
            x = PRBMath.mulDivFixedPoint(x, x);

            // Equivalent to "y % 2 == 1" but faster.
            if (y & 1 > 0) {
                result = PRBMath.mulDivFixedPoint(result, x);
            }
        }
    }


/// @notice Calculates the square root of x, rounding down.
/// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Requirements:
/// - x must be less than MAX_UD60x18 / SCALE.
///
/// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the square root.
/// @return result The result as an unsigned 60.18-decimal fixed-point .

function sqrt(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            if (x > MAX_UD60x18 / SCALE) {
                revert PRBMathUD60x18__SqrtOverflow(x);
            }
            // Multiply x by the SCALE to account for the factor of SCALE that is picked up when multiplying two unsigned
            // 60.18-decimal fixed-point numbers together (in this case, those two numbers are both the square root).
            result = PRBMath.sqrt(x * SCALE);
        }
    }


/// @param extraArgs expecting <[20B] address pool1><[20B] address pool2><[20B] address pool3>...

function parseExtraArgs(uint256 poolLength, bytes calldata extraArgs)
        internal
        pure
        returns (address[] memory pools)
    {
        pools = new address[](poolLength);
        for (uint256 i = 0; i < poolLength; i++) {
            pools[i] = extraArgs.toAddress(i * 20);
        }
    }


/**
 * @dev ERC165 support for ENS resolver interface
 * https://docs.ens.domains/contract-developer-guide/writing-a-resolver
 */

function supportsInterface(bytes4 interfaceID) public pure returns (bool) {
        return interfaceID == 0x01ffc9a7 // supportsInterface call itself
            || interfaceID == 0x3b3b57de // EIP137: ENS resolver
            || interfaceID == 0xf1cb7e06 // EIP2304: Multichain addresses
            || interfaceID == 0x59d1d43c // EIP634: ENS text records
            || interfaceID == 0xbc1c58d1 // EIP1577: contenthash
        ;
    }


/**
 * @dev For a given ENS Node ID, return the Ethereum address it points to.
 * EIP137 core functionality
 */

function addr(bytes32 nodeID) public view returns (address) {
        uint256 rescueOrder = getRescueOrderFromNodeId(nodeID);
        address actualOwner = MCA.ownerOf(rescueOrder);
        if (
            MCR.catOwners(MCR.rescueOrder(rescueOrder)) != address(MCA) ||
            actualOwner != lastAnnouncedAddress[rescueOrder]
        ) {
            return address(0); // Not Acclimated/Announced; return zero (per spec)
        } else {
            return lastAnnouncedAddress[rescueOrder];
        }
    }


/**
 * @dev For a given ENS Node ID, return an address on a different blockchain it points to.
 * EIP2304 functionality
 */

function addr(bytes32 nodeID, uint256 coinType) public view returns (bytes memory) {
        uint256 rescueOrder = getRescueOrderFromNodeId(nodeID);
        if (MCR.catOwners(MCR.rescueOrder(rescueOrder)) != address(MCA)) {
            return bytes(''); // Not Acclimated; return zero (per spec)
        }
        if (coinType == 60) {
            // Ethereum address
            return abi.encodePacked(addr(nodeID));
        } else {
            return MultichainMapping[rescueOrder][coinType];
        }
    }


/**
 * @dev For a given MoonCat rescue order, set the subdomains associated with it to point to an address on a different blockchain.
 */

function setAddr(uint256 rescueOrder, uint256 coinType, bytes calldata newAddr) public onlyMoonCatOwner(rescueOrder) {
        if (coinType == 60) {
            // Ethereum address
            announceMoonCat(rescueOrder);
            return;
        }
        emit AddressChanged(getSubdomainNameHash(uint2str(rescueOrder)), coinType, newAddr);
        emit AddressChanged(getSubdomainNameHash(bytes5ToHexString(MCR.rescueOrder(rescueOrder))), coinType, newAddr);
        MultichainMapping[rescueOrder][coinType] = newAddr;
    }


/**
 * @dev For a given ENS Node ID, return the value associated with a given text key.
 * If the key is "avatar", and the matching value is not explicitly set, a url pointing to the MoonCat's image is returned
 * EIP634 functionality
 */

function text(bytes32 nodeID, string calldata key) public view returns (string memory) {
        uint256 rescueOrder = getRescueOrderFromNodeId(nodeID);

        string memory value = TextKeyMapping[rescueOrder][key];
        if (bytes(value).length > 0) {
            // This value has been set explicitly; return that
            return value;
        }

        // Check if there's a default value for this key
        bytes memory keyBytes = bytes(key);
        if (keyBytes.length == avatarKeyLength && keccak256(keyBytes) == avatarKeyHash){
            // Avatar default
            return string(abi.encodePacked(avatarBaseURI,  uint2str(rescueOrder)));
        }

        // No default; just return the empty string
        return value;
    }


/**
 * @dev Update a text record for subdomains owned by a specific MoonCat rescue order.
 */

function setText(uint256 rescueOrder, string calldata key, string calldata value) public onlyMoonCatOwner(rescueOrder) {
        bytes memory keyBytes = bytes(key);
        bytes32 orderHash = getSubdomainNameHash(uint2str(rescueOrder));
        bytes32 hexHash = getSubdomainNameHash(bytes5ToHexString(MCR.rescueOrder(rescueOrder)));

        if (bytes(value).length == 0 && keyBytes.length == avatarKeyLength && keccak256(keyBytes) == avatarKeyHash){
            // Avatar default
            string memory avatarRecordValue = string(abi.encodePacked(avatarBaseURI,  uint2str(rescueOrder)));
            emit TextChanged(orderHash, key, avatarRecordValue);
            emit TextChanged(hexHash, key, avatarRecordValue);
        } else {
            emit TextChanged(orderHash, key, value);
            emit TextChanged(hexHash, key, value);
        }
        TextKeyMapping[rescueOrder][key] = value;
    }


/**
 * @dev Allow calling multiple functions on this contract in one transaction.
 */

function multicall(bytes[] calldata data) external returns(bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint i = 0; i < data.length; i++) {
            (bool success, bytes memory result) = address(this).delegatecall(data[i]);
            require(success);
            results[i] = result;
        }
        return results;
    }


/**
 * @dev Reverse lookup for ENS Node ID, to determine the MoonCat rescue order of the MoonCat associated with it.
 */

function getRescueOrderFromNodeId(bytes32 nodeID) public view returns (uint256) {
        uint256 rescueOrder = NamehashMapping[nodeID];
        if (rescueOrder == 0) {
            // Are we actually dealing with MoonCat #0?
            require(
                nodeID == 0x8bde039a2a7841d31e0561fad9d5cfdfd4394902507c72856cf5950eaf9e7d5a // 0.ismymooncat.eth
                || nodeID == 0x1002474938c26fb23080c33c3db026c584b30ec6e7d3edf4717f3e01e627da26, // 0x00d658d50b.ismymooncat.eth
                "Unknown Node ID"
            );
        }
        return rescueOrder;
    }


/**
 * @dev Cache a single MoonCat's (identified by Rescue Order) subdomain hashes.
 */

function mapMoonCat(uint256 rescueOrder) public {
        string memory orderSubdomain = uint2str(rescueOrder);
        string memory hexSubdomain = bytes5ToHexString(MCR.rescueOrder(rescueOrder));

        bytes32 orderHash = getSubdomainNameHash(orderSubdomain);
        bytes32 hexHash = getSubdomainNameHash(hexSubdomain);

        if (uint256(NamehashMapping[orderHash]) != 0) {
            // Already Mapped
            return;
        }

        NamehashMapping[orderHash] = rescueOrder;
        NamehashMapping[hexHash] = rescueOrder;

        if(MCR.catOwners(MCR.rescueOrder(rescueOrder)) != address(MCA)) {
            // MoonCat is not Acclimated
            return;
        }

        IRegistry registry = IRegistry(0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e);
        registry.setSubnodeRecord(rootHash, keccak256(bytes(orderSubdomain)), address(this), address(this), defaultTTL);
        registry.setSubnodeRecord(rootHash, keccak256(bytes(hexSubdomain)), address(this), address(this), defaultTTL);

        address moonCatOwner = MCA.ownerOf(rescueOrder);
        lastAnnouncedAddress[rescueOrder] = moonCatOwner;
        emit AddrChanged(orderHash, moonCatOwner);
        emit AddrChanged(hexHash, moonCatOwner);
        emit AddressChanged(orderHash, 60, abi.encodePacked(moonCatOwner));
        emit AddressChanged(hexHash, 60, abi.encodePacked(moonCatOwner));

        string memory avatarRecordValue = string(abi.encodePacked(avatarBaseURI,  uint2str(rescueOrder)));
        emit TextChanged(orderHash, "avatar", avatarRecordValue);
        emit TextChanged(hexHash, "avatar", avatarRecordValue);
    }


/**
 * @dev Helper function to reduce pixel size within contract
 */

function letterToNumber(string memory _inputLetter, string[] memory LETTERS)
        public
        pure
        returns (uint8)
    {
        for (uint8 i = 0; i < LETTERS.length; i++) {
            if (
                keccak256(abi.encodePacked((LETTERS[i]))) ==
                keccak256(abi.encodePacked((_inputLetter)))
            ) return (i + 1);
        }
        revert();
    }


/**
 * @dev Announce a single MoonCat's (identified by Rescue Order) assigned address.
 */

function announceMoonCat(uint256 rescueOrder) public {
        require(MCR.catOwners(MCR.rescueOrder(rescueOrder)) == address(MCA), "Not Acclimated");
        address moonCatOwner = MCA.ownerOf(rescueOrder);

        lastAnnouncedAddress[rescueOrder] = moonCatOwner;
        bytes32 orderHash = getSubdomainNameHash(uint2str(rescueOrder));
        bytes32 hexHash = getSubdomainNameHash(bytes5ToHexString(MCR.rescueOrder(rescueOrder)));

        emit AddrChanged(orderHash, moonCatOwner);
        emit AddrChanged(hexHash, moonCatOwner);
        emit AddressChanged(orderHash, 60, abi.encodePacked(moonCatOwner));
        emit AddressChanged(hexHash, 60, abi.encodePacked(moonCatOwner));
    }


/**
 * @dev Convenience function to iterate through all MoonCats owned by an address to check if they need announcing.
 */

function needsAnnouncing(address moonCatOwner) public view returns (uint256[] memory) {
        uint256 balance = MCA.balanceOf(moonCatOwner);
        uint256 announceCount = 0;
        uint256[] memory tempRescueOrders = new uint256[](balance);
        for (uint256 i = 0; i < balance; i++) {
            uint256 rescueOrder = MCA.tokenOfOwnerByIndex(moonCatOwner, i);
            if (lastAnnouncedAddress[rescueOrder] != moonCatOwner){
                tempRescueOrders[announceCount] = rescueOrder;
                announceCount++;
            }
        }
        uint256[] memory rescueOrders = new uint256[](announceCount);
        for (uint256 i = 0; i < announceCount; i++){
            rescueOrders[i] = tempRescueOrders[i];
        }
        return rescueOrders;
    }


/**
 * @dev Set a manual list of MoonCats (identified by Rescue Order) to announce or cache their subdomain hashes.
 */

function mapMoonCats(uint256[] memory rescueOrders) public {
        for (uint256 i = 0; i < rescueOrders.length; i++) {
            address lastAnnounced = lastAnnouncedAddress[rescueOrders[i]];
            if (lastAnnounced == address(0)){
                mapMoonCat(rescueOrders[i]);
            } else if (lastAnnounced != MCA.ownerOf(rescueOrders[i])){
                announceMoonCat(rescueOrders[i]);
            }
        }
    }


/**
 * @dev Convenience function to iterate through all MoonCats owned by an address and announce or cache their subdomain hashes.
 */

function mapMoonCats(address moonCatOwner) public {
        for (uint256 i = 0; i < MCA.balanceOf(moonCatOwner); i++) {
            uint256 rescueOrder = MCA.tokenOfOwnerByIndex(moonCatOwner, i);
            address lastAnnounced = lastAnnouncedAddress[rescueOrder];
            if (lastAnnounced == address(0)){
                mapMoonCat(rescueOrder);
            } else if (lastAnnounced != moonCatOwner){
                announceMoonCat(rescueOrder);
            }
        }
    }


/**
 * @dev Utility function to convert a uint256 variable into a decimal string.
 */

function uint2str(uint value) internal pure returns (string memory) {
        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }


/**
 * @dev Mint internal, this is to avoid code duplication.
 */

function mintInternal(address _account, string memory _hash) internal {
        uint256 rand = _rand();

        uint256 _totalSupply = totalSupply();

        uint256 thisTokenId = _totalSupply;

        tokenIdToHash[thisTokenId] = _hash;

        hashToMinted[tokenIdToHash[thisTokenId]] = true;

        // QUESTS

        (uint8 health,uint8 accuracy,uint8 defense) = (0,0,0);

        {

            // Helpers to get Percentages
            uint256 eightyPct     = type(uint16).max / 100 * 80;
            uint256 nineFivePct   = type(uint16).max / 100 * 95;

            // Getting Random traits
            uint16 randHelm = uint16(_randomize(rand, "HEALTH", thisTokenId));
                    health     = uint8(randHelm < eightyPct ? 0 : randHelm % 4 + 5);

            uint16 randOffhand = uint16(_randomize(rand, "ACCURACY", thisTokenId));
                    defense     = uint8(randOffhand < eightyPct ? 0 : randOffhand % 4 + 5);

            uint16 randMainhand = uint16(_randomize(rand, "DEFENSE", thisTokenId));
                    accuracy     = uint8(randMainhand < nineFivePct ? randMainhand % 4 + 1: randMainhand % 4 + 5);
        }

        _mint(_account, thisTokenId);

        uint16 meowModifier = ZombieCatsLibrary._tier(health) + ZombieCatsLibrary._tier(accuracy) + ZombieCatsLibrary._tier(defense);
        zombiekatz[uint256(thisTokenId)] = ZombieKat({health: health, accuracy: accuracy, defense: defense, level: 0, lvlProgress: 0, meowModifier:meowModifier});
    }


/**
 * @dev Mints a zombie. Only the Graveyard contract can call this function.
 */

function mintZombie(address _account, string memory _hash) public {
        require(msg.sender == graveyardAddress);

        // In the Graveyard contract, the Katz head stored in the hash in position 1
        // while the character in the ZombieKatz contract is stored in position 8
        // so we swap the traits.

        // We let the graveyard contract to mint whatever is the total supply, just in case
        // to avoid a situation of zombies stucked in a grave, since you need to claim your zombie
        // before unstake a grave digging zombie.

        mintInternal(_account,
            string(
                abi.encodePacked(
                    "0",                                       // not burnt
                    ZombieCatsLibrary.substring(_hash, 8, 9),  // grave
                    ZombieCatsLibrary.substring(_hash, 2, 8),  // rest
                    ZombieCatsLibrary.substring(_hash, 1, 2)   // hand
                )
            )
        );
    }


/**
 * @dev Mutates a zombie.
 * @param _tokenId The token to burn.
 */

function mutateZombie(uint256 _tokenId) public ownerOfZombieKat(_tokenId) noCheaters {
        // require(ownerOf(_tokenId) == msg.sender
        // //, "You must own this zombie to mutate"
        // );

        IMiece(mieceAddress).burnFrom(msg.sender, 30 ether);

        tokenIdToHash[_tokenId] =  mutatedHash(_tokenIdToHash(_tokenId), _tokenId, msg.sender, 0);

        hashToMinted[tokenIdToHash[_tokenId]] = true;

    }


/// @dev Constructor: takes list of parties and their slices.
/// @param addresses List of addresses of the parties
/// @param slices Slices of the parties. Will be added to totalSlices.

function PaymentSplitter(address[] addresses, uint[] slices) public {
    require(addresses.length == slices.length, "addresses and slices must be equal length.");
    require(addresses.length > 0 && addresses.length < MAX_PARTIES, "Amount of parties is either too many, or zero.");

    for(uint i=0; i<addresses.length; i++) {
      parties.push(Party(addresses[i], slices[i]));
      totalSlices = totalSlices.add(slices[i]);
    }
  }


/**
 * @dev Confirm that airDrop is available.
 * @return A bool to confirm that airDrop is available.
 */

function isValidAirDropForAll() public view returns (bool) {
        bool validNotStop = !stop;
        bool validAmount = getRemainingToken() >= airDropAmount;
        bool validPeriod = now >= startTime && now <= endTime;
        return validNotStop && validAmount && validPeriod;
    }


/**
 * @dev Do the airDrop to msg.sender
 */

function receiveAirDrop() public {
        require(isValidAirDropForIndividual());

        // set invalidAirDrop of msg.sender to true
        invalidAirDrop[msg.sender] = true;

        // set msg.sender to the array of the airDropReceiver
        arrayAirDropReceivers.push(msg.sender);

        // execute transfer
        erc20.transfer(msg.sender, airDropAmount);

        emit LogAirDrop(msg.sender, airDropAmount);
    }


/**
 * @dev Update the information regarding to period and amount.
 * @param _startTime The start time this airdrop starts.
 * @param _endTime The end time this sirdrop ends.
 * @param _airDropAmount The airDrop Amount that user can get via airdrop.
 */

function updateInfo(uint256 _startTime, uint256 _endTime, uint256 _airDropAmount) public onlyOwner {
        require(stop || now > endTime);
        require(
            _startTime >= now &&
            _endTime >= _startTime &&
            _airDropAmount > 0
        );

        startTime = _startTime;
        endTime = _endTime;
        uint tokenDecimals = erc20.decimals();
        airDropAmount = _airDropAmount.mul(10 ** tokenDecimals);

        emit LogInfoUpdate(startTime, endTime, airDropAmount);
    }


// needs div /10

function getDigitWidth(uint256 tokenId) public pure returns (uint16) {
        require(tokenId >= 0 && tokenId <= 9, "Token Id invalid");

        if (tokenId == 0)
            return 2863;

        if (tokenId == 1)
            return 1944;

        if (tokenId == 2)
            return 2491;

        if (tokenId == 3)
            return 2503;

        if (tokenId == 4)
            return 2842;

        if (tokenId == 5)
            return 2502;

        if (tokenId == 6)
            return 2667;

        if (tokenId == 7)
            return 2638;

        if (tokenId == 8)
            return 2591;

        if (tokenId == 9)
            return 2661;

        return 0;
    }


/**
 * Return the ethereum received on selling 1 individual token.
 * We are not deducting the penalty over here as it's a general sell price
 * the user can use the `calculateEthereumReceived` to get the sell price specific to them
 */

function sellPrice() public view returns (uint256) {
        if (tokenSupply_ == 0) {
            return tokenPriceInitial_ - tokenPriceIncremental_;
        } else {
            uint256 _ethereum = tokensToEthereum_(1e18);
            uint256 _dividends = SafeMath.div(_ethereum, dividendFee_);
            uint256 _taxedEthereum = SafeMath.sub(_ethereum, _dividends);
            return _taxedEthereum;
        }
    }


/**
 * Return the ethereum required for buying 1 individual token.
 */

function buyPrice() public view returns (uint256) {
        if (tokenSupply_ == 0) {
            return tokenPriceInitial_ + tokenPriceIncremental_;
        } else {
            uint256 _ethereum = tokensToEthereum_(1e18);
            uint256 _taxedEthereum =
                mulDiv(_ethereum, dividendFee_, (dividendFee_ - 1));
            return _taxedEthereum;
        }
    }


/**
 * Calculate the early exit penalty for selling x tokens
 */

function calculateAveragePenalty(
        uint256 _amountOfTokens,
        address _customerAddress
    ) public view onlyBagholders() returns (uint256) {
        require(_amountOfTokens <= tokenBalanceLedger_[_customerAddress]);

        uint256 tokensFound = 0;
        Cursor storage _customerCursor =
            tokenTimestampedBalanceCursor[_customerAddress];
        uint256 counter = _customerCursor.start;
        uint256 averagePenalty = 0;

        while (counter <= _customerCursor.end) {
            TimestampedBalance storage transaction =
                tokenTimestampedBalanceLedger_[_customerAddress][counter];
            uint256 tokensAvailable =
                SafeMath.sub(transaction.value, transaction.valueSold);

            uint256 tokensRequired = SafeMath.sub(_amountOfTokens, tokensFound);

            if (tokensAvailable < tokensRequired) {
                tokensFound += tokensAvailable;
                averagePenalty = SafeMath.add(
                    averagePenalty,
                    SafeMath.mul(
                        _calculatePenalty(transaction.timestamp),
                        tokensAvailable
                    )
                );
            } else if (tokensAvailable <= tokensRequired) {
                averagePenalty = SafeMath.add(
                    averagePenalty,
                    SafeMath.mul(
                        _calculatePenalty(transaction.timestamp),
                        tokensRequired
                    )
                );
                break;
            } else {
                averagePenalty = SafeMath.add(
                    averagePenalty,
                    SafeMath.mul(
                        _calculatePenalty(transaction.timestamp),
                        tokensRequired
                    )
                );
                break;
            }

            counter = SafeMath.add(counter, 1);
        }
        return SafeMath.div(averagePenalty, _amountOfTokens);
    }


/**
 * Calculate the early exit penalty for selling after x days
 */

function _calculatePenalty(uint256 timestamp)
        public
        view
        returns (uint256)
    {
        uint256 gap = block.timestamp - timestamp;

        if (gap > 30 days) {
            return 0;
        } else if (gap > 20 days) {
            return 25;
        } else if (gap > 10 days) {
            return 50;
        }
        return 75;
    }


/**
 * Calculate Token price based on an amount of incoming ethereum
 * Some conversions occurred to prevent decimal errors or underflows / overflows in solidity code.
 */

function ethereumToTokens_(uint256 _ethereum)
        public
        view
        returns (uint256)
    {
        uint256 _tokenPriceInitial = tokenPriceInitial_ * 1e18;
        uint256 _tokensReceived =
            ((
                SafeMath.sub(
                    (
                        sqrt(
                            (_tokenPriceInitial**2) +
                                (2 *
                                    (tokenPriceIncremental_ * 1e18) *
                                    (_ethereum * 1e18)) +
                                (((tokenPriceIncremental_)**2) *
                                    (tokenSupply_**2)) +
                                (2 *
                                    (tokenPriceIncremental_) *
                                    _tokenPriceInitial *
                                    tokenSupply_)
                        )
                    ),
                    _tokenPriceInitial
                )
            ) / (tokenPriceIncremental_)) - (tokenSupply_);

        return _tokensReceived;
    }


/**
 * Calculate token sell value.
 * Some conversions occurred to prevent decimal errors or underflows / overflows in solidity code.
 */

function tokensToEthereum_(uint256 _tokens) public view returns (uint256) {
        uint256 tokens_ = (_tokens + 1e18);
        uint256 _tokenSupply = (tokenSupply_ + 1e18);
        uint256 _ethereumReceived =
            (SafeMath.sub(
                (((tokenPriceInitial_ +
                    (tokenPriceIncremental_ * (_tokenSupply / 1e18))) -
                    tokenPriceIncremental_) * (tokens_ - 1e18)),
                (tokenPriceIncremental_ * ((tokens_**2 - tokens_) / 1e18)) / 2
            ) / 1e18);

        return _ethereumReceived;
    }


/**
 * Update ledger after transferring x tokens
 */

function _updateLedgerForTransfer(
        uint256 _amountOfTokens,
        address _customerAddress
    ) internal {
        // Parse through the list of transactions
        uint256 tokensFound = 0;
        Cursor storage _customerCursor =
            tokenTimestampedBalanceCursor[_customerAddress];
        uint256 counter = _customerCursor.start;

        while (counter <= _customerCursor.end) {
            TimestampedBalance storage transaction =
                tokenTimestampedBalanceLedger_[_customerAddress][counter];
            uint256 tokensAvailable =
                SafeMath.sub(transaction.value, transaction.valueSold);

            uint256 tokensRequired = SafeMath.sub(_amountOfTokens, tokensFound);

            if (tokensAvailable < tokensRequired) {
                tokensFound += tokensAvailable;

                delete tokenTimestampedBalanceLedger_[_customerAddress][
                    counter
                ];
            } else if (tokensAvailable <= tokensRequired) {
                delete tokenTimestampedBalanceLedger_[_customerAddress][
                    counter
                ];
                _customerCursor.start = counter + 1;
                break;
            } else {
                transaction.valueSold += tokensRequired;
                _customerCursor.start = counter;
                break;
            }
            counter += 1;
        }
    }


/**
 * Calculate the early exit penalty for selling x tokens and edit the timestamped ledger
 */

function calculateAveragePenaltyAndUpdateLedger(
        uint256 _amountOfTokens,
        address _customerAddress
    ) internal onlyBagholders() returns (uint256) {
        // Parse through the list of transactions
        uint256 tokensFound = 0;
        Cursor storage _customerCursor =
            tokenTimestampedBalanceCursor[_customerAddress];
        uint256 counter = _customerCursor.start;
        uint256 averagePenalty = 0;

        while (counter <= _customerCursor.end) {
            TimestampedBalance storage transaction =
                tokenTimestampedBalanceLedger_[_customerAddress][counter];
            uint256 tokensAvailable =
                SafeMath.sub(transaction.value, transaction.valueSold);

            uint256 tokensRequired = SafeMath.sub(_amountOfTokens, tokensFound);

            if (tokensAvailable < tokensRequired) {
                tokensFound += tokensAvailable;
                averagePenalty = SafeMath.add(
                    averagePenalty,
                    SafeMath.mul(
                        _calculatePenalty(transaction.timestamp),
                        tokensAvailable
                    )
                );
                delete tokenTimestampedBalanceLedger_[_customerAddress][
                    counter
                ];
            } else if (tokensAvailable <= tokensRequired) {
                averagePenalty = SafeMath.add(
                    averagePenalty,
                    SafeMath.mul(
                        _calculatePenalty(transaction.timestamp),
                        tokensRequired
                    )
                );
                delete tokenTimestampedBalanceLedger_[_customerAddress][
                    counter
                ];
                _customerCursor.start = counter + 1;
                break;
            } else {
                averagePenalty = SafeMath.add(
                    averagePenalty,
                    SafeMath.mul(
                        _calculatePenalty(transaction.timestamp),
                        tokensRequired
                    )
                );
                transaction.valueSold += tokensRequired;
                _customerCursor.start = counter;
                break;
            }

            counter += 1;
        }

        return SafeMath.div(averagePenalty, _amountOfTokens);
    }


/**
 * @dev calculates x*y and outputs a emulated 512bit number as l being the lower 256bit half and h the upper 256bit half.
 */

function fullMul(uint256 x, uint256 y)
        public
        pure
        returns (uint256 l, uint256 h)
    {
        uint256 mm = mulmod(x, y, uint256(-1));
        l = x * y;
        h = mm - l;
        if (mm < l) h -= 1;
    }


/**
 * @dev calculates x*y/z taking care of phantom overflows.
 */

function mulDiv(
        uint256 x,
        uint256 y,
        uint256 z
    ) public pure returns (uint256) {
        (uint256 l, uint256 h) = fullMul(x, y);
        require(h < z);
        uint256 mm = mulmod(x, y, z);
        if (mm > l) h -= 1;
        l -= mm;
        uint256 pow2 = z & -z;
        z /= pow2;
        l /= pow2;
        l += h * ((-pow2) / pow2 + 1);
        uint256 r = 1;
        r *= 2 - z * r;
        r *= 2 - z * r;
        r *= 2 - z * r;
        r *= 2 - z * r;
        r *= 2 - z * r;
        r *= 2 - z * r;
        r *= 2 - z * r;
        r *= 2 - z * r;
        return l * r;
    }


// Anyone can call this function and claim the reward

function invokeAutoReinvest(address _customerAddress)
        external
        returns (uint256)
    {
        AutoReinvestEntry storage entry = autoReinvestment[_customerAddress];

        if (
            entry.nextExecutionTime > 0 &&
            block.timestamp >= entry.nextExecutionTime
        ) {
            // fetch dividends
            uint256 _dividends = dividendsOf(_customerAddress);

            // Only execute if the user's dividends are more that the
            // rewardPerInvocation and the minimumDividendValue
            if (
                _dividends > entry.minimumDividendValue &&
                _dividends > entry.rewardPerInvocation
            ) {
                // Deduct the reward from the users dividends
                payoutsTo_[_customerAddress] += (int256)(
                    entry.rewardPerInvocation * magnitude
                );

                // Update the Auto Reinvestment entry
                entry.nextExecutionTime +=
                    (((block.timestamp - entry.nextExecutionTime) /
                        uint256(entry.period)) + 1) *
                    uint256(entry.period);

                /*
                 * Do the reinvestment
                 */
                _reinvest(_customerAddress);

                // Send the caller their reward
                msg.sender.transfer(entry.rewardPerInvocation);
            }
        }

        return entry.nextExecutionTime;
    }


/**
 * Get the metadata for a given tokenId
 */

function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        return bytes(baseURI).length > 0
            ? string(abi.encodePacked(baseURI, Strings.toString(tokenId + 1), ".json")) //TESTING ONLY PLEASE REMOVE ON MAINNET FOR LOVE OF GOD
            : "";
    }


// Deposit LP tokens to eBOND for EFI allocation.

function deposit(uint256 _pid, uint256 _amount, uint256 _lockPeriod) public {

        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        updatePool(_pid);

        uint256 _tokenMultiplier = 1;
        if (userMultiActive[msg.sender][_pid]) {
            _tokenMultiplier = tokenMultiplier[msg.sender][_pid].div(100);
            userMultiActive[msg.sender][_pid] = false;
        }

        if (user.amount > 0) {
            uint256 pending = _tokenMultiplier.mul(user.amount.mul(pool.accEFIPerShare).div(1e12).sub(user.rewardDebt));
            EFI.mint(msg.sender, pending);
        }

        if(block.timestamp >= user.unlockTime || (lockPeriodTime[_lockPeriod] + block.timestamp > user.unlockTime)){
             user.unlockTime = SafeMath.add(lockPeriodTime[_lockPeriod], block.timestamp);
             tokenMultiplier[msg.sender][_pid] = lockPeriodMultiplier[_lockPeriod];
             userMultiActive[msg.sender][_pid] = true;
        }

        pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
        user.amount = user.amount.add(_amount);
        user.rewardDebt = user.amount.mul(pool.accEFIPerShare).div(1e12);
        emit Deposit(msg.sender, _pid, _amount);

    }


// Withdraw LP tokens from eBOND.

function withdraw(uint256 _pid, uint256 _amount) public {

        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        require(block.timestamp >= user.unlockTime, "Your Liquidity is locked.");
        require(user.amount >= _amount, "withdraw: inadequate funds");
        uint256 multiplier = tokenMultiplier[msg.sender][_pid].div(100);
        updatePool(_pid);
        uint256 pending = user.amount.mul(pool.accEFIPerShare).mul(multiplier).div(1e12).sub(user.rewardDebt);
        safeEFITransfer(msg.sender, pending);
        user.amount = user.amount.sub(_amount);
        user.rewardDebt = user.amount.mul(pool.accEFIPerShare).div(1e12);
        pool.lpToken.safeTransfer(address(msg.sender), _amount);
        emit Withdraw(msg.sender, _pid, _amount);
    }


/* Magic */

function magicGift(address[] calldata receivers) external onlyOwner {
        require(
            _tokenIds.current() + receivers.length <= MAX_TOKENS,
            "Exceeds maximum token supply"
        );
        require(
            numberOfGifts + receivers.length <= MAX_GIFTS,
            "Exceeds maximum allowed gifts"
        );

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

            _safeMint(receivers[i], _tokenIds.current());
            _tokenIds.increment();
        }
    }


/**
 @dev Recovers address who signed the message
 @param _hash operation ethereum signed message hash
 @param _signature message `hash` signature
 */

function ecrecover2 (
        bytes32 _hash,
        bytes memory _signature
    ) internal pure returns (address) {
        bytes32 r;
        bytes32 s;
        uint8 v;

        assembly {
            r := mload(add(_signature, 32))
            s := mload(add(_signature, 64))
            v := and(mload(add(_signature, 65)), 255)
        }

        if (v < 27) {
            v += 27;
        }

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


// Cancels a not executed Intent '_id'
// a canceled intent can't be executed

function cancel(bytes32 _id) external {
        require(msg.sender == address(this), "Only wallet can cancel txs");

        if (intentReceipt[_id] != bytes32(0)) {
            (bool canceled, , address relayer) = _decodeReceipt(intentReceipt[_id]);
            require(relayer == address(0), "Intent already relayed");
            require(!canceled, "Intent was canceled");
            revert("Unknown error");
        }

        emit Canceled(_id);
        intentReceipt[_id] = _encodeReceipt(true, 0, address(0));
    }


// Decodes an Intent receipt
// reverse of _encodeReceipt(bool,uint256,address)

function _decodeReceipt(bytes32 _receipt) internal pure returns (
        bool _canceled,
        uint256 _block,
        address _relayer
    ) {
        assembly {
            _canceled := shr(255, _receipt)
            _block := and(shr(160, _receipt), 0x7fffffffffffffffffffffff)
            _relayer := and(_receipt, 0xffffffffffffffffffffffffffffffffffffffff)
        }
    }


// Concatenates 6 bytes arrays

function concat(
        bytes memory _a,
        bytes memory _b,
        bytes memory _c,
        bytes memory _d,
        bytes memory _e,
        bytes memory _f
    ) internal pure returns (bytes memory) {
        return abi.encodePacked(
            _a,
            _b,
            _c,
            _d,
            _e,
            _f
        );
    }


// Returns the most significant bit of a given uint256

function mostSignificantBit(uint256 x) internal pure returns (uint256) {
        uint8 o = 0;
        uint8 h = 255;

        while (h > o) {
            uint8 m = uint8 ((uint16 (o) + uint16 (h)) >> 1);
            uint256 t = x >> m;
            if (t == 0) h = m - 1;
            else if (t > 1) o = m + 1;
            else return m;
        }

        return h;
    }


// Shrinks a given address to the minimal representation in a bytes array

function shrink(address _a) internal pure returns (bytes memory b) {
        uint256 abits = mostSignificantBit(uint256(_a)) + 1;
        uint256 abytes = abits / 8 + (abits % 8 == 0 ? 0 : 1);

        assembly {
            b := 0x0
            mstore(0x0, abytes)
            mstore(0x20, shl(mul(sub(32, abytes), 8), _a))
        }
    }


// Deploys the Marmo wallet of a given _signer
// all ETH sent will be forwarded to the wallet

function reveal(address _signer) external payable {
        // Load init code from storage
        bytes memory proxyCode = bytecode;

        // Create wallet proxy using CREATE2
        // use _signer as salt
        Marmo p;
        assembly {
            p := create2(0, add(proxyCode, 0x20), mload(proxyCode), _signer)
        }

        // Init wallet with provided _signer
        // and forward all Ether
        p.init.value(msg.value)(_signer);
    }


// Standard Withdraw function for the owner to pull the contract

function withdraw() external onlyOwner {
        uint256 sendAmount = address(this).balance;

        address cmanager        = payable(0x543874CeA651a5Dd4CDF88B2Ed9B92aF57b8507E);
        address founder         = payable(0xF561266D093c73F67c7CAA2Ab74CC71a43554e57);
        address coo             = payable(0xa4D4FeA9799cd5015955f248994D445C6bEB9436);
        address marketing       = payable(0x17895988aB2B64f041813936bF46Fb9133a6B160);
        address dev             = payable(0x2496286BDB820d40C402802F828ae265b244188A);
        address community       = payable(0x855bFE65652868920729b9d92D8d6030D01e3bFF);

        bool success;
        (success, ) = cmanager.call{value: ((sendAmount * 35)/1000)}("");
        require(success, "Transaction Unsuccessful");

        (success, ) = founder.call{value: ((sendAmount * 175)/1000)}("");
        require(success, "Transaction Unsuccessful");

        (success, ) = coo.call{value: ((sendAmount * 5)/100)}("");
        require(success, "Transaction Unsuccessful");

        (success, ) = marketing.call{value: ((sendAmount * 5)/100)}("");
        require(success, "Transaction Unsuccessful");

        (success, ) = dev.call{value: ((sendAmount * 5)/100)}("");
        require(success, "Transaction Unsuccessful");

        (success, ) = community.call{value: ((sendAmount * 64)/100)}("");
        require(success, "Transaction Unsuccessful");

     }


/*
 function withdraw() external {
 require (approvedRecipients)
 sendAmount = whatever was calculated
 (bool success, ) = msg.sender.call{value: sendAmount}("");
 require(success, "Transaction Unsuccessful");
 }
 */

function ownerMint(address _to, uint256 qty) external onlyOwner {
        require((numberMinted + qty) > numberMinted, "Math overflow error");
        require((numberMinted + qty) < totalTokens, "Cannot fill order");

        uint256 mintSeedValue = numberMinted; //Store the starting value of the mint batch
        if (reservedTokens >= qty) {
            reservedTokens -= qty;
        } else {
            reservedTokens = 0;
        }

        for(uint256 i = 0; i < qty; i++) {
            _safeMint(_to, mintSeedValue + i);
            numberMinted ++;  //reservedTokens can be reset, numberMinted can not
        }
    }


// Emergency drain in case of a bug
// Adds all funds to owner to refund people
// Designed to be as simple as possible
// function emergencyDrain24hAfterLiquidityGenerationEventIsDone() public onlyOwner {
//     require(contractStartTimestamp.add(4 days) < block.timestamp, "Liquidity generation grace period still ongoing"); // About 24h after liquidity generation happens
//     (bool success, ) = msg.sender.call.value(address(this).balance)("");
//     require(success, "Transfer failed.");
//     _balances[msg.sender] = _balances[address(this)];
//     _balances[address(this)] = 0;
// }

function emergencyDrain24hAfterLiquidityGenerationEventIsDone() public onlyOwner {
        require(contractStartTimestamp.add(6 days) < block.timestamp, "Liquidity generation grace period still ongoing"); // About 24h after liquidity generation happens
        // (bool success, ) = msg.sender.call.value(address(this).balance)("");
        // require(success, "Transfer failed.");
        uint256 b  = aapl.balanceOf(address(this));
        bool success = aapl.transfer(msg.sender,b);
        require(success, "Transfer failed.");
        _balances[msg.sender] = _balances[address(this)];
        _balances[address(this)] = 0;
    }


//@dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.

function tokenURI(uint256 tokenId) external view returns (string memory){
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        string memory tokenuri;

        if (_hideTokens) {
            //redirect to mystery box
            tokenuri = string(abi.encodePacked(_baseURI, "mystery.json"));
        } else {
            //Input flag data here to send to reveal URI
            tokenuri = string(abi.encodePacked(_baseURI, toString(tokenId), ".json")); /// 0.json 135.json
        }

        return tokenuri;
    }


/// @notice Computes the amount of liquidity received for a given amount of token0 and price range
/// @dev Calculates amount0 * (sqrt(upper) * sqrt(lower)) / (sqrt(upper) - sqrt(lower))
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param amount0 The amount0 being sent in
/// @return liquidity The amount of returned liquidity

function getLiquidityForAmount0(
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint256 amount0
    ) internal pure returns (uint128 liquidity) {
        if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
        uint256 intermediate = FullMath.mulDiv(sqrtRatioAX96, sqrtRatioBX96, FixedPoint96.Q96);
        return toUint128(FullMath.mulDiv(amount0, intermediate, sqrtRatioBX96 - sqrtRatioAX96));
    }


/// @notice Computes the maximum amount of liquidity received for a given amount of token0, token1, the current
/// pool prices and the prices at the tick boundaries
/// @param sqrtRatioX96 A sqrt price representing the current pool prices
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param amount0 The amount of token0 being sent in
/// @param amount1 The amount of token1 being sent in
/// @return liquidity The maximum amount of liquidity received

function getLiquidityForAmounts(
        uint160 sqrtRatioX96,
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint256 amount0,
        uint256 amount1
    ) internal pure returns (uint128 liquidity) {
        if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);

        if (sqrtRatioX96 <= sqrtRatioAX96) {
            liquidity = getLiquidityForAmount0(sqrtRatioAX96, sqrtRatioBX96, amount0);
        } else if (sqrtRatioX96 < sqrtRatioBX96) {
            uint128 liquidity0 = getLiquidityForAmount0(sqrtRatioX96, sqrtRatioBX96, amount0);
            uint128 liquidity1 = getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioX96, amount1);

            liquidity = liquidity0 < liquidity1 ? liquidity0 : liquidity1;
        } else {
            liquidity = getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioBX96, amount1);
        }
    }


/// @notice Computes the amount of token1 for a given amount of liquidity and a price range
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param liquidity The liquidity being valued
/// @return amount1 The amount of token1

function getAmount1ForLiquidity(
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint128 liquidity
    ) internal pure returns (uint256 amount1) {
        if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);

        return FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96);
    }


/**
 * @notice Setup mint
 * @param _mintStart Unix timestamp when minting should be opened
 * @param _mintEnd Unix timestamp when minting should be closed
 * @param _mintFee Minting fee
 * @param _mintLimit Limits how many tokens can be minted
 */

function setupMint(
        uint256 _mintStart,
        uint256 _mintEnd,
        uint256 _mintFee,
        uint256 _mintLimit
    ) external onlyOwner {
        require(_mintStart < _mintEnd, "wrong mint end");
        require(mintLimit == 0 || _mintLimit == mintLimit, "change mint limit not allowed");
        mintStart = _mintStart;
        mintEnd = _mintEnd;
        mintFee = _mintFee;
        mintLimit = _mintLimit;
    }


/// @dev Wrapper around `LiquidityAmounts.getAmountsForLiquidity()`.
/// @param pool Uniswap V3 pool
/// @param liquidity  The liquidity being valued
/// @param _tickLower The lower tick of the range
/// @param _tickUpper The upper tick of the range
/// @return amounts of token0 and token1 that corresponds to liquidity

function amountsForLiquidity(
        IUniswapV3Pool pool,
        uint128 liquidity,
        int24 _tickLower,
        int24 _tickUpper
    ) internal view returns (uint256, uint256) {
        //Get current price from the pool
        (uint160 sqrtRatioX96, , , , , , ) = pool.slot0();
        return
            LiquidityAmounts.getAmountsForLiquidity(
                sqrtRatioX96,
                TickMath.getSqrtRatioAtTick(_tickLower),
                TickMath.getSqrtRatioAtTick(_tickUpper),
                liquidity
            );
    }


/// @dev Wrapper around `LiquidityAmounts.getLiquidityForAmounts()`.
/// @param pool Uniswap V3 pool
/// @param amount0 The amount of token0
/// @param amount1 The amount of token1
/// @param _tickLower The lower tick of the range
/// @param _tickUpper The upper tick of the range
/// @return The maximum amount of liquidity that can be held amount0 and amount1

function liquidityForAmounts(
        IUniswapV3Pool pool,
        uint256 amount0,
        uint256 amount1,
        int24 _tickLower,
        int24 _tickUpper
    ) internal view returns (uint128) {
        //Get current price from the pool
        (uint160 sqrtRatioX96, , , , , , ) = pool.slot0();

        return
            LiquidityAmounts.getLiquidityForAmounts(
                sqrtRatioX96,
                TickMath.getSqrtRatioAtTick(_tickLower),
                TickMath.getSqrtRatioAtTick(_tickUpper),
                amount0,
                amount1
            );
    }


/// @dev Amounts of token0 and token1 held in contract position.
/// @param pool Uniswap V3 pool
/// @param _tickLower The lower tick of the range
/// @param _tickUpper The upper tick of the range
/// @return amount0 The amount of token0 held in position
/// @return amount1 The amount of token1 held in position

function positionAmounts(IUniswapV3Pool pool, int24 _tickLower, int24 _tickUpper)
        internal
        view
        returns (uint256 amount0, uint256 amount1)
    {
        //Compute position key
        bytes32 positionKey = PositionKey.compute(address(this), _tickLower, _tickUpper);
        //Get Position.Info for specified ticks
        (uint128 liquidity, , , uint128 tokensOwed0, uint128 tokensOwed1) =
            pool.positions(positionKey);
        // Calc amounts of token0 and token1 including fees
        (amount0, amount1) = amountsForLiquidity(pool, liquidity, _tickLower, _tickUpper);
        amount0 = amount0.add(uint256(tokensOwed0));
        amount1 = amount1.add(uint256(tokensOwed1));
    }


/// @dev Amount of liquidity in contract position.
/// @param pool Uniswap V3 pool
/// @param _tickLower The lower tick of the range
/// @param _tickUpper The upper tick of the range
/// @return liquidity stored in position

function positionLiquidity(IUniswapV3Pool pool, int24 _tickLower, int24 _tickUpper)
        internal
        view
        returns (uint128 liquidity)
    {
        //Compute position key
        bytes32 positionKey = PositionKey.compute(address(this), _tickLower, _tickUpper);
        //Get liquidity stored in position
        (liquidity, , , , ) = pool.positions(positionKey);
    }


/// @dev Rounds tick down towards negative infinity so that it's a multiple
/// of `tickSpacing`.

function floor(int24 tick, int24 tickSpacing) internal pure returns (int24) {
        int24 compressed = tick / tickSpacing;
        if (tick < 0 && tick % tickSpacing != 0) compressed--;
        return compressed * tickSpacing;
    }


/// @dev Gets amounts of token0 and token1 that can be stored in range of upper and lower ticks
/// @param pool Uniswap V3 pool
/// @param amount0Desired The desired amount of token0
/// @param amount1Desired The desired amount of token1
/// @param _tickLower The lower tick of the range
/// @param _tickUpper The upper tick of the range
/// @return amount0 amounts of token0 that can be stored in range
/// @return amount1 amounts of token1 that can be stored in range

function amountsForTicks(IUniswapV3Pool pool, uint256 amount0Desired, uint256 amount1Desired, int24 _tickLower, int24 _tickUpper) internal view returns(uint256 amount0, uint256 amount1) {
        uint128 liquidity = liquidityForAmounts(pool, amount0Desired, amount1Desired, _tickLower, _tickUpper);

        (amount0, amount1) = amountsForLiquidity(pool, liquidity, _tickLower, _tickUpper);
    }


// Check price has not moved a lot recently. This mitigates price
// manipulation during rebalance and also prevents placing orders
// when it's too volatile.

function checkDeviation(IUniswapV3Pool pool, int24 maxTwapDeviation, uint32 twapDuration) internal view {
        (, int24 currentTick, , , , , ) = pool.slot0();
        int24 twap = getTwap(pool, twapDuration);
        int24 deviation = currentTick > twap ? currentTick - twap : twap - currentTick;
        require(deviation <= maxTwapDeviation, "PSC");
    }


/// @dev Fetches time-weighted average price in ticks from Uniswap pool for specified duration

function getTwap(IUniswapV3Pool pool, uint32 twapDuration) internal view returns (int24) {
        uint32 _twapDuration = twapDuration;
        uint32[] memory secondsAgo = new uint32[](2);
        secondsAgo[0] = _twapDuration;
        secondsAgo[1] = 0;

        (int56[] memory tickCumulatives, ) = pool.observe(secondsAgo);
        return int24((tickCumulatives[1] - tickCumulatives[0]) / _twapDuration);
    }


/**
 * @notice Withdraws liquidity in share proportion to the Sorbetto's totalSupply.
 * @param pool Uniswap V3 pool
 * @param tickLower The lower tick of the range
 * @param tickUpper The upper tick of the range
 * @param totalSupply The amount of total shares in existence
 * @param share to burn
 * @param to Recipient of amounts
 * @return amount0 Amount of token0 withdrawed
 * @return amount1 Amount of token1 withdrawed
 */

function burnLiquidityShare(
        IUniswapV3Pool pool,
        int24 tickLower,
        int24 tickUpper,
        uint256 totalSupply,
        uint256 share,
        address to
    ) internal returns (uint256 amount0, uint256 amount1) {
        require(totalSupply > 0, "TS");
        uint128 liquidityInPool = pool.positionLiquidity(tickLower, tickUpper);
        uint256 liquidity = uint256(liquidityInPool).mul(share) / totalSupply;

        if (liquidity > 0) {
            (amount0, amount1) = pool.burn(tickLower, tickUpper, liquidity.toUint128());

            if (amount0 > 0 || amount1 > 0) {
            // collect liquidity share
                (amount0, amount1) = pool.collect(
                    to,
                    tickLower,
                    tickUpper,
                    amount0.toUint128(),
                    amount1.toUint128()
                );
            }
        }
    }


/**
 * @notice Withdraws exact amount of liquidity
 * @param pool Uniswap V3 pool
 * @param tickLower The lower tick of the range
 * @param tickUpper The upper tick of the range
 * @param liquidity to burn
 * @param to Recipient of amounts
 * @return amount0 Amount of token0 withdrawed
 * @return amount1 Amount of token1 withdrawed
 */

function burnExactLiquidity(
        IUniswapV3Pool pool,
        int24 tickLower,
        int24 tickUpper,
        uint128 liquidity,
        address to
    ) internal returns (uint256 amount0, uint256 amount1) {
        uint128 liquidityInPool = pool.positionLiquidity(tickLower, tickUpper);
        require(liquidityInPool >= liquidity, "TML");
        (amount0, amount1) = pool.burn(tickLower, tickUpper, liquidity);

        if (amount0 > 0 || amount1 > 0) {
            // collect liquidity share including earned fees
            (amount0, amount0) = pool.collect(
                to,
                tickLower,
                tickUpper,
                amount0.toUint128(),
                amount1.toUint128()
            );
        }
    }


/**
 * @notice Withdraws all liquidity in a range from Uniswap pool
 * @param pool Uniswap V3 pool
 * @param tickLower The lower tick of the range
 * @param tickUpper The upper tick of the range
 */

function burnAllLiquidity(
        IUniswapV3Pool pool,
        int24 tickLower,
        int24 tickUpper
    ) internal {

        // Burn all liquidity in this range
        uint128 liquidity = pool.positionLiquidity(tickLower, tickUpper);
        if (liquidity > 0) {
            pool.burn(tickLower, tickUpper, liquidity);
        }

         // Collect all owed tokens
        pool.collect(
            address(this),
            tickLower,
            tickUpper,
            type(uint128).max,
            type(uint128).max
        );
    }


/**
 * @notice Returns imageIds for a range of token ids
 * @param startTokenId first token id in the range
 * @param rangeLength length of the range
 * @return imgIds array of image ids
 */

function imageIdsForRange(uint256 startTokenId, uint256 rangeLength) public view returns (bytes32[] memory imgIds) {
        imgIds = new bytes32[](rangeLength);
        uint256 i = startTokenId;
        for (uint256 c = 0; c < rangeLength; c++) {
            imgIds[c] = imageId(i);
            i++;
        }
    }


/// @notice Gets the next sqrt price given a delta of token0
/// @dev Always rounds up, because in the exact output case (increasing price) we need to move the price at least
/// far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the
/// price less in order to not send too much output.
/// The most precise formula for this is liquidity * sqrtPX96 / (liquidity +- amount * sqrtPX96),
/// if this is impossible because of overflow, we calculate liquidity / (liquidity / sqrtPX96 +- amount).
/// @param sqrtPX96 The starting price, i.e. before accounting for the token0 delta
/// @param liquidity The amount of usable liquidity
/// @param amount How much of token0 to add or remove from virtual reserves
/// @param add Whether to add or remove the amount of token0
/// @return The price after adding or removing amount, depending on add

function getNextSqrtPriceFromAmount0RoundingUp(
        uint160 sqrtPX96,
        uint128 liquidity,
        uint256 amount,
        bool add
    ) internal pure returns (uint160) {
        // we short circuit amount == 0 because the result is otherwise not guaranteed to equal the input price
        if (amount == 0) return sqrtPX96;
        uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION;

        if (add) {
            uint256 product;
            if ((product = amount * sqrtPX96) / amount == sqrtPX96) {
                uint256 denominator = numerator1 + product;
                if (denominator >= numerator1)
                    // always fits in 160 bits
                    return uint160(FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator));
            }

            return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96).add(amount)));
        } else {
            uint256 product;
            // if the product overflows, we know the denominator underflows
            // in addition, we must check that the denominator does not underflow
            require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product);
            uint256 denominator = numerator1 - product;
            return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160();
        }
    }