/**
 * @notice Buy function. Used to buy tokens using ETH, USDT or USDC
 * @param _paymentAmount    --> Result of multiply number of tokens to buy per price per token. Must be always multiplied per 1000 to avoid decimals
 * @param _tokenPayment     --> Address of the payment token (or 0x0 if payment is ETH)
 */

function buy(uint256 _paymentAmount, address _tokenPayment) external payable {
        require(_isICOActive() == true, "ICONotActive");

        uint256 paidTokens = 0;

        if (msg.value == 0) {
            // Stable coin payment
            require(_paymentAmount > 0, "BadPayment");
            require(_tokenPayment == usdtToken || _tokenPayment == usdcToken, "TokenNotSupported");
            require(IERC20(_tokenPayment).transferFrom(msg.sender, address(this), _paymentAmount));
            paidTokens = _paymentAmount * 2666666666666666667 / 1000000000000000000;   // 0.375$ per token in the ICO
        } else {
            // ETH Payment
            uint256 usdETH = _getUSDETHPrice();
            uint256 paidUSD = msg.value * usdETH / 10**18;
            paidTokens = paidUSD * 2666666666666666666 / 1000000000000000000;   // 0.375$ per token in the ICO
        }

        require((paidTokens + 1*10**18) >= minTokensBuyAllowed, "BadTokensQuantity");    // One extra token as threshold rounding decimals
        require(maxICOTokens - icoTokensBought >= paidTokens, "NoEnoughTokensInICO");
        userBoughtTokens[msg.sender] += paidTokens;
        icoTokensBought += paidTokens;
        if (maxICOTokens - icoTokensBought < minTokensBuyAllowed) {
            // We finish the ICO
            icoFinished = true;
            emit onICOFinished(block.timestamp);
        }

        emit onTokensBought(msg.sender, paidTokens, _paymentAmount, _tokenPayment);
    }


/**
 * @notice Get the vesting unlock dates
 * @param _period --> There are 4 periods (0,1,2,3)
 * @return _date Returns the date in UnixDateTime UTC format
 */

function getVestingDate(uint256 _period) external view returns(uint256 _date) {
        if (_period == 0) {
            _date = tokenListingDate;
        } else if (_period == 1) {
            _date = tokenListingDate + _3_MONTHS_IN_SECONDS;
        } else if (_period == 2) {
            _date = tokenListingDate + _6_MONTHS_IN_SECONDS;
        } else if (_period == 3) {
            _date = tokenListingDate + _9_MONTHS_IN_SECONDS;
        }
    }


/**
 * @notice Uses Chainlink to query the USDETH price
 * @return Returns the ETH amount in weis (Fixed value of 3932.4 USDs in localhost development environments)
 */

function _getUSDETHPrice() internal view returns(uint256) {
        int price = 0;

        if (address(priceFeed) != address(0)) {
            (, price, , , ) = priceFeed.latestRoundData();
        } else {
            // For local testing
            price = 393240000000;
        }

        return uint256(price * 10**10);
    }


//create new escrow

function createNewEscrow(address escrowpayee, uint escrowamount) external
    {

        require(factorycontractactive, "Factory Contract should be Active");
        require(escrowid < maxuint, "Maximum escrowid reached");
        require(msg.sender != escrowpayee,"The Payer, payee should be different");
        require(escrowpayee != address(0),"The Escrow Payee can not be address(0)");
        require(escrowamount > 0,"Escrow amount has to be greater than 0");

        require(daiToken.allowance(msg.sender,address(this)) >= mathlib.add(escrowamount, escrowfee), "daiToken allowance exceeded");

        bytes32 esid = keccak256(abi.encodePacked(escrowid));

        Escrows[esid] = Escrow({escrowpayer:msg.sender, escrowpayee:escrowpayee, escrowamount:escrowamount,
            escrowsettlementamount:escrowamount, estatus:escrowstatus.ACTIVATED,escrowmoderator:address(0),escrowmoderatorfee:0});

        escrowid = mathlib.add(escrowid,1);

        //The Esrow Amount gets transferred to factory contract
        daiToken.transferFrom(msg.sender, address(this), escrowamount);

        //Transfer the escrow fee to factory manager
        daiToken.transferFrom(msg.sender, manager, escrowfee);

        emit NewEscrowEvent(esid, msg.sender, escrowpayee, escrowamount, now);

        emit NewEscrowEventById(esid, msg.sender, escrowpayee, escrowamount, now);

    }


//create new escrow overload

function createNewEscrow(address escrowpayee, uint escrowamount, address escrowmoderator, uint escrowmoderatorfee) external
    {

        require(factorycontractactive, "Factory Contract should be Active");
        require(escrowid < maxuint, "Maximum escrowid reached");
        require(msg.sender != escrowpayee && msg.sender != escrowmoderator && escrowpayee != escrowmoderator,"The Payer, payee & moderator should be different");
        require(escrowpayee != address(0) && escrowmoderator!=address(0),"Escrow Payee or moderator can not be address(0)");
        require(escrowamount > 0,"Escrow amount has to be greater than 0");

        uint dailockedinnewescrow = mathlib.add(escrowamount,escrowmoderatorfee);

        require(daiToken.allowance(msg.sender,address(this)) >= mathlib.add(dailockedinnewescrow, escrowfee), "daiToken allowance exceeded");

        bytes32 esid = keccak256(abi.encodePacked(escrowid));

        Escrows[esid] = Escrows[esid] = Escrow({escrowpayer:msg.sender, escrowpayee:escrowpayee, escrowamount:escrowamount,
            escrowsettlementamount:escrowamount, estatus:escrowstatus.ACTIVATED,escrowmoderator:escrowmoderator,escrowmoderatorfee:escrowmoderatorfee});

        escrowid = mathlib.add(escrowid,1);

        //The Esrow Amount and Moderator fee gets transferred to factory contract
        daiToken.transferFrom(msg.sender, address(this), dailockedinnewescrow);

        //Transfer the escrow fee to factory manager
        daiToken.transferFrom(msg.sender, manager, escrowfee);

        emit NewEscrowEvent(esid, msg.sender, escrowpayee, escrowamount, escrowmoderator, escrowmoderatorfee ,now);

        emit NewEscrowEventById(esid, msg.sender, escrowpayee, escrowamount, escrowmoderator, escrowmoderatorfee, now);

    }


/**
 * @dev Claim your share of the balance.
 */

function claim() public {
    address payee = msg.sender;

    require(shares[payee] > 0);

    uint256 totalReceived = this.balance.add(totalReleased);
    uint256 payment = totalReceived.mul(shares[payee]).div(totalShares).sub(released[payee]);

    require(payment != 0);
    require(this.balance >= payment);

    released[payee] = released[payee].add(payment);
    totalReleased = totalReleased.add(payment);

    payee.transfer(payment);
  }


/*function _transfer(address _from, address _to, uint256 _tokenId) internal {

 ownershipTokenCount[_to]++;
 // transfer ownership
 cardIndexToOwner[_tokenId] = _to;

 // Emit the transfer event.
 Transfer(_from, _to, _tokenId);

 }*/

function _createCard(uint256 _prototypeId, address _owner) internal returns (uint) {

        // This will assign ownership, and also emit the Transfer event as
        // per ERC721 draft
        require(uint256(1000000) > lastPrintedCard);
        lastPrintedCard++;
        tokenToCardIndex[lastPrintedCard] = _prototypeId;
        _setTokenOwner(lastPrintedCard, _owner);
        //_addTokenToOwnersList(_owner, lastPrintedCard);
        Transfer(0, _owner, lastPrintedCard);
        //tokenCountIndex[_prototypeId]++;

        //_transfer(0, _owner, lastPrintedCard); //<-- asd

        return lastPrintedCard;
    }


/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev This works identically to the other function with an extra data parameter,
///  except this function just sets data to ""
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer

function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable {
        require(_getApproved(_tokenId) == msg.sender);
        require(_ownerOf(_tokenId) == _from);
        require(_to != address(0));

        _clearApprovalAndTransfer(_from, _to, _tokenId);

        Approval(_from, 0, _tokenId);
        Transfer(_from, _to, _tokenId);

        if (isContract(_to)) {
            bytes4 value = ERC721TokenReceiver(_to).onERC721Received(_from, _tokenId, "");

            if (value != bytes4(keccak256("onERC721Received(address,uint256,bytes)"))) {
                revert();
            }
        }
    }


/// @inheritdoc IPeripheryPayments

function sweepToken(
        address token,
        uint256 amountMinimum,
        address recipient
    ) external payable override {
        uint256 balanceToken = IERC20(token).balanceOf(address(this));
        require(balanceToken >= amountMinimum, 'Insufficient token');

        if (balanceToken > 0) {
            TransferHelper.safeTransfer(token, recipient, balanceToken);
        }
    }


/// @param token The token to pay
/// @param payer The entity that must pay
/// @param recipient The entity that will receive payment
/// @param value The amount to pay

function pay(
        address token,
        address payer,
        address recipient,
        uint256 value
    ) internal {
        if (token == WETH9 && address(this).balance >= value) {
            // pay with WETH9
            IWETH9(WETH9).deposit{value: value}(); // wrap only what is needed to pay
            IWETH9(WETH9).transfer(recipient, value);
        } else if (payer == address(this)) {
            // pay with tokens already in the contract (for the exact input multihop case)
            TransferHelper.safeTransfer(token, recipient, value);
        } else {
            // pull payment
            TransferHelper.safeTransferFrom(token, payer, recipient, value);
        }
    }


/**
 * Returns whether the target address is a contract
 * @dev This function will return false if invoked during the constructor of a contract,
 * as the code is not actually created until after the constructor finishes.
 * @param _addr address to check
 * @return whether the target address is a contract
 */

function isContract(address _addr) internal view returns (bool) {
    uint256 size;
    // XXX Currently there is no better way to check if there is a contract in an address
    // than to check the size of the code at that address.
    // See https://ethereum.stackexchange.com/a/14016/36603
    // for more details about how this works.
    // TODO Check this again before the Serenity release, because all addresses will be
    // contracts then.
    // solium-disable-next-line security/no-inline-assembly
    assembly { size := extcodesize(_addr) }
    return size > 0;
  }


/**
 * @dev Transfers the ownership of a given token ID to another address
 * Usage of this method is discouraged, use `safeTransferFrom` whenever possible
 * Requires the msg sender to be the owner, approved, or operator
 * @param _from current owner of the token
 * @param _to address to receive the ownership of the given token ID
 * @param _tokenId uint256 ID of the token to be transferred
 */

function transferFrom(
    address _from,
    address _to,
    uint256 _tokenId
  )
    public
    override
  {
    require(isApprovedOrOwner(msg.sender, _tokenId));
    require(_from != address(0));
    require(_to != address(0));

    clearApproval(_from, _tokenId);
    removeTokenFrom(_from, _tokenId);
    addTokenTo(_to, _tokenId);

    emit Transfer(_from, _to, _tokenId);
  }


/**
 * @dev Returns whether the given spender can transfer a given token ID
 * @param _spender address of the spender to query
 * @param _tokenId uint256 ID of the token to be transferred
 * @return bool whether the msg.sender is approved for the given token ID,
 *  is an operator of the owner, or is the owner of the token
 */

function isApprovedOrOwner(
    address _spender,
    uint256 _tokenId
  )
    internal
    view
    returns (bool)
  {
    address owner = ownerOf(_tokenId);
    // Disable solium check because of
    // https://github.com/duaraghav8/Solium/issues/175
    // solium-disable-next-line operator-whitespace
    return (
      _spender == owner ||
      getApproved(_tokenId) == _spender ||
      isApprovedForAll(owner, _spender)
    );
  }


// set room message

function setRoomMessage(uint256 tokenId, string memory newRoomMessage) external payable {
    require(_tokenIdCounter.current() >= tokenId && tokenId > 0, tokenIdInvalid);
    require( msg.sender == ownerOf(tokenId), "token owner only");
    require( msg.value >= _price, "incorrect ETH sent" );
    _roomMessage[tokenId] = newRoomMessage;
  }


/**
 * @dev Returns an URI for a given token ID.
 * Throws if the token ID does not exist. May return an empty string.
 * @param tokenId uint256 ID of the token to query
 */

function tokenURI(uint256 tokenId) external view returns (string memory) {
        require(exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
        string memory url = _tokenURIs[tokenId];
        bytes memory urlAsBytes = bytes(url);
        if (urlAsBytes.length == 0) {
            bytes memory baseURIAsBytes = bytes(baseURI);
            bytes memory tokenIdAsBytes = uintToBytes(tokenId);
            bytes memory b = new bytes(baseURIAsBytes.length + tokenIdAsBytes.length);
            uint256 i;
            uint256 j;
            for (i = 0; i < baseURIAsBytes.length; i++) {
                b[j++] = baseURIAsBytes[i];
            }
            for (i = 0; i < tokenIdAsBytes.length; i++) {
                b[j++] = tokenIdAsBytes[i];
            }
            return string(b);
        } else {
            return _tokenURIs[tokenId];
        }
    }


/**
 * @dev Mint token
 *
 * @param _to address of token owner
 */

function mint(address _to,uint256 amount) public returns (uint256) {

        require(_tokenIds.current() + amount < _max_supply, "CulturalArtTreasure: maximum quantity reached");
        require(amount > 0, "You must buy at least one .");
        uint256 newTokenId = 1;
        for (uint256 i = 0; i < amount; i++) {
			_tokenIds.increment();
			 newTokenId = _tokenIds.current();

			_mint(_to, newTokenId);

			Attributes.Data storage attributes = attributesByTokenIds[newTokenId];
			attributes.init();
        }
        return newTokenId;
    }


// get room message

function getRoomMessage(uint256 tokenId) public view returns (string memory) {
    require(_tokenIdCounter.current() >= tokenId && tokenId > 0, tokenIdInvalid);
    bytes memory tempEmptyStringTest = bytes(_roomMessage[tokenId]);
    if (tempEmptyStringTest.length == 0) {
      uint256 randMsg = random("nft", tokenId);
      if (randMsg % 17 == 3)
        return "LFG!";
      else if (randMsg % 7 == 3)
        return "WAGMI!";
      else
        return "gm!";
    } else {
      return _roomMessage[tokenId];
    }
  }


// mint num of keycards

function mint(uint256 num) external payable nonReentrant {
    require( _publicSale, "public sale paused" );
    require( num <= 10, "max 10 per TX" );
    require( _tokenIdCounter.current() + num <= _maxSupply, "max supply reached" );
    require( msg.value >= _price * num, "incorrect ETH sent" );

    for( uint i = 0; i < num; i++ ) {
      _safeMint(_msgSender(), _tokenIdCounter.current() + 1);
      _tokenIdCounter.increment();
    }
  }


/**
 * @dev Creates a vesting contract that vests its balance of any ERC20 token to the
 * _beneficiary, gradually in a linear fashion until _start + _duration. By then all
 * of the balance will have vested.
 * @param _beneficiary address of the beneficiary to whom vested tokens are transferred
 * @param _cliff duration in seconds of the cliff in which tokens will begin to vest
 * @param _duration duration in seconds of the period in which the tokens will vest
 * @param _revocable whether the vesting is revocable or not
 */

function TokenVesting(address _beneficiary, uint256 _start, uint256 _cliff, uint256 _duration, bool _revocable) public {
    require(_beneficiary != address(0));
    require(_cliff <= _duration);

    beneficiary = _beneficiary;
    revocable = _revocable;
    duration = _duration;
    cliff = _start.add(_cliff);
    start = _start;
  }


/**
 * @dev Calculates the amount that has already vested.
 * @param token ERC20 token which is being vested
 */

function vestedAmount(ERC20Basic token) public view returns (uint256) {
    uint256 currentBalance = token.balanceOf(this);
    uint256 totalBalance = currentBalance.add(released[token]);

    if (now < cliff) {
      return 0;
    } else if (now >= start.add(duration) || revoked[token]) {
      return totalBalance;
    } else {
      return totalBalance.mul(now.sub(start)).div(duration);
    }
  }


/**
 * @dev Issues FTT to entitled accounts.
 * @param _user address to issue FTT to.
 * @param _fttAmount amount of FTT to issue.
 */

function issueFTT(address _user, uint256 _fttAmount)
        public
        onlyTdeIssuer
        tdeRunning
        returns(bool)
    {
        uint256 newAmountIssued = fttIssued.add(_fttAmount);
        require(_user != address(0));
        require(_fttAmount > 0);
        require(newAmountIssued <= FT_TOKEN_SALE_CAP);

        balances[_user] = balances[_user].add(_fttAmount);
        fttIssued = newAmountIssued;
        FTTIssued(tdeIssuer, _user, _fttAmount, block.timestamp);

        if (fttIssued == FT_TOKEN_SALE_CAP) {
            capReached = true;
        }

        return true;
    }


/**
 * @dev Allows the contract owner to finalize the TDE.
 */

function finalize()
        external
        tdeEnded
        onlyOwner
    {
        require(!isFinalized);

        // Deposit team fund amount into team vesting contract.
        uint256 teamVestingCliff = 15778476;  // 6 months
        uint256 teamVestingDuration = 1 years;
        TokenVesting teamVesting = new TokenVesting(owner, now, teamVestingCliff, teamVestingDuration, true);
        teamVesting.transferOwnership(owner);
        teamVestingAddress = address(teamVesting);
        balances[teamVestingAddress] = FT_TEAM_FUND;

        if (!capReached) {
            // Deposit unsold FTT into unsold vesting contract.
            uint256 unsoldVestingCliff = 3 years;
            uint256 unsoldVestingDuration = 10 years;
            TokenVesting unsoldVesting = new TokenVesting(owner, now, unsoldVestingCliff, unsoldVestingDuration, true);
            unsoldVesting.transferOwnership(owner);
            unsoldVestingAddress = address(unsoldVesting);
            balances[unsoldVestingAddress] = FT_TOKEN_SALE_CAP - fttIssued;
        }

        // Allocate operational reserve of FTT.
        balances[operationalReserveAddress] = FT_OPERATIONAL_FUND - FT_TEAM_FUND;

        isFinalized = true;
        TdeFinalized(block.timestamp);
    }


/**
 * @dev Transfer tokens from one address to another. Trading limited - requires the TDE to have ended.
 * @param _from address The address which you want to send tokens from
 * @param _to address The address which you want to transfer to
 * @param _value uint256 the amount of tokens to be transferred
 */

function transferFrom(address _from, address _to, uint256 _value)
        public
        returns (bool)
    {
        if (!isFinalized) return false;
        require(_to != address(0));
        require(_value <= balances[_from]);
        require(_value <= allowed[_from][msg.sender]);

        balances[_from] = balances[_from].sub(_value);
        balances[_to] = balances[_to].add(_value);
        allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
        Transfer(_from, _to, _value);
        return true;
    }


/**
 * @dev Transfer token for a specified address.  Trading limited - requires the TDE to have ended.
 * @param _to The address to transfer to.
 * @param _value The amount to be transferred.
 */

function transfer(address _to, uint256 _value)
        public
        returns (bool)
    {
        if (!isFinalized) return false;
        require(_to != address(0));
        require(_value <= balances[msg.sender]);

        balances[msg.sender] = balances[msg.sender].sub(_value);
        balances[_to] = balances[_to].add(_value);
        Transfer(msg.sender, _to, _value);
        return true;
    }


/**
 * @notice The Synthetix contract informs us when fees are paid.
 */

function feePaid(bytes4 currencyKey, uint amount)
        external
        onlySynthetix
    {
        uint xdrAmount;

        if (currencyKey != "XDR") {
            xdrAmount = synthetix.effectiveValue(currencyKey, amount, "XDR");
        } else {
            xdrAmount = amount;
        }

        // Keep track of in XDRs in our fee pool.
        recentFeePeriods[0].feesToDistribute = recentFeePeriods[0].feesToDistribute.add(xdrAmount);
    }


// generate metadata for stars

function haveStar(uint256 tokenId) private pure returns (string memory) {
    uint256 starSeed = random("star", tokenId);
    string memory traitTypeJson = ', {"trait_type": "Star", "value": "';
    if (starSeed % 47 == 1)
      return string(abi.encodePacked(traitTypeJson, 'Sirius"}'));
    if (starSeed % 11 == 1)
      return string(abi.encodePacked(traitTypeJson, 'Vega"}'));
    return '';
  }


// render stars in SVG

function renderStar(uint256 tokenId) private pure returns (string memory) {
    string memory starFirstPart = '<defs><linearGradient id="star" x1="100%" y1="100%"><stop offset="0%" stop-color="black" stop-opacity=".5"><animate attributeName="stop-color" values="black;black;black;black;gray;';
    string memory starLastPart = ';gray;black;black;black;black" dur="3s" repeatCount="indefinite" /></stop></linearGradient></defs><g style="transform:translate(130px,244px)"><g style="transform:scale(0.1,0.1)"><path fill="url(#star)" d="M189.413,84c-36.913,0-37.328,38.157-37.328,38.157c0-33.181-36.498-38.157-36.498-38.157  c37.328,0,36.498-38.157,36.498-38.157C152.085,84,189.413,84,189.413,84z" /></g></g>';
    uint256 starSeed = random("star", tokenId);
    if (starSeed % 47 == 1)
      return string(abi.encodePacked(starFirstPart, 'aqua', starLastPart));
    if (starSeed % 11 == 1)
      return string(abi.encodePacked(starFirstPart, 'white', starLastPart));
    return '';
  }


// generate metadata for keys

function haveKey(uint256 tokenId) private pure returns (string memory) {
    uint256 keySeed = random("key", tokenId);
    string memory traitTypeJson = ', {"trait_type": "Key", "value": "';
    if (keySeed % 301 == 1)
      return string(abi.encodePacked(traitTypeJson, 'Rainbow Key"}'));
    if (keySeed % 161 == 1)
      return string(abi.encodePacked(traitTypeJson, 'Crystal Key"}')); //afcfff
    if (keySeed % 59 == 1)
      return string(abi.encodePacked(traitTypeJson, 'Gold Key"}')); //ffff33
    if (keySeed % 31 == 1)
      return string(abi.encodePacked(traitTypeJson, 'Silver Key"}')); //dddddd
    if (keySeed % 11 == 1)
      return string(abi.encodePacked(traitTypeJson, 'Jade Key"}')); // 66ff66
    return string(abi.encodePacked(traitTypeJson, 'Copper Key"}')); // 995500
  }


// generate metadata for description

function getDescription(uint256 tokenId) private view returns (string memory) {
    string memory description0 = string(abi.encodePacked('This is a keycard to launch [#', toString(tokenId), ' ', getRoomTheme(tokenId), ' ', getRoomType(tokenId),'](', string(abi.encodePacked(_roomBaseUrl, toString(tokenId))), ') with one click.'));
    string memory description1 = ' And check the linked ';
    uint256 link1Id = getAssetLink1(tokenId);
      if (link1Id > 0) {
        string memory link1description = string(abi.encodePacked('[#', toString(link1Id), ' ', getRoomTheme(link1Id), ' ', getRoomType(link1Id), '](', string(abi.encodePacked(_roomBaseUrl, toString(link1Id))) ,')'));
        uint256 link2Id = getAssetLink2(tokenId);
        if (link2Id > 0) {
          string memory link2description = string(abi.encodePacked('[#', toString(link2Id), ' ', getRoomTheme(link2Id), ' ', getRoomType(link2Id), '](', string(abi.encodePacked(_roomBaseUrl, toString(link2Id))) ,')'));
          if (link2Id > link1Id)
            return string(abi.encodePacked(description0, description1, link1description,' and ',link2description, '.'));
          else
            return string(abi.encodePacked(description0, description1, link2description,' and ',link1description, '.'));
        } else {
          return string(abi.encodePacked(description0, description1, link1description,'.'));
        }
      } else {
        return description0;
      }
    }


/// @notice Returns PoolKey: the ordered tokens with the matched fee levels
/// @param tokenA The first token of a pool, unsorted
/// @param tokenB The second token of a pool, unsorted
/// @param fee The fee level of the pool
/// @return Poolkey The pool details with ordered token0 and token1 assignments

function getPoolKey(
        address tokenA,
        address tokenB,
        uint24 fee
    ) internal pure returns (PoolKey memory) {
        if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);
        return PoolKey({token0: tokenA, token1: tokenB, fee: fee});
    }


/// @notice Deterministically computes the pool address given the factory and PoolKey
/// @param factory The Uniswap V3 factory contract address
/// @param key The PoolKey
/// @return pool The contract address of the V3 pool

function computeAddress(address factory, PoolKey memory key) internal pure returns (address pool) {
        require(key.token0 < key.token1);
        pool = address(
            uint160(uint256(
                keccak256(
                    abi.encodePacked(
                        hex'ff',
                        factory,
                        keccak256(abi.encode(key.token0, key.token1, key.fee)),
                        POOL_INIT_CODE_HASH
                    )
                )
            ))
        );
    }


/**
 * @notice Calculate the Fee charged on top of a value being sent
 * @return Return the fee charged
 */

function transferFeeIncurred(uint value)
        public
        view
        returns (uint)
    {
        return value.multiplyDecimal(transferFeeRate);

        // Transfers less than the reciprocal of transferFeeRate should be completely eaten up by fees.
        // This is on the basis that transfers less than this value will result in a nil fee.
        // Probably too insignificant to worry about, but the following code will achieve it.
        //      if (fee == 0 && transferFeeRate != 0) {
        //          return _value;
        //      }
        //      return fee;
    }


/**
 @notice queue address to change boolean in mapping
 @param _managing MANAGING
 @param _address address
 @return bool
 */

function queue(MANAGING _managing, address _address) external onlyManager() returns (bool) {
        require(_address != address(0));
        if (_managing == MANAGING.RESERVEDEPOSITOR) {// 0
            reserveDepositorQueue[_address] = block.number.add(blocksNeededForQueue);
        } else if (_managing == MANAGING.RESERVESPENDER) {// 1
            reserveSpenderQueue[_address] = block.number.add(blocksNeededForQueue);
        } else if (_managing == MANAGING.RESERVETOKEN) {// 2
            reserveTokenQueue[_address] = block.number.add(blocksNeededForQueue);
        } else if (_managing == MANAGING.RESERVEMANAGER) {// 3
            ReserveManagerQueue[_address] = block.number.add(blocksNeededForQueue.mul(2));
        } else if (_managing == MANAGING.LIQUIDITYDEPOSITOR) {// 4
            LiquidityDepositorQueue[_address] = block.number.add(blocksNeededForQueue);
        } else if (_managing == MANAGING.LIQUIDITYTOKEN) {// 5
            LiquidityTokenQueue[_address] = block.number.add(blocksNeededForQueue);
        } else if (_managing == MANAGING.LIQUIDITYMANAGER) {// 6
            LiquidityManagerQueue[_address] = block.number.add(blocksNeededForQueue.mul(2));
        } else if (_managing == MANAGING.DEBTOR) {// 7
            debtorQueue[_address] = block.number.add(blocksNeededForQueue);
        } else if (_managing == MANAGING.REWARDMANAGER) {// 8
            rewardManagerQueue[_address] = block.number.add(blocksNeededForQueue);
        } else if (_managing == MANAGING.SGWS) {// 9
            sGWSQueue = block.number.add(blocksNeededForQueue);
        } else return false;

        emit ChangeQueued(_managing, _address);
        return true;
    }


/* A contract attempts to get the coins */

function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
        if (_to == 0x0) revert();                                // Prevent transfer to 0x0 address. Use burn() instead
        if (balanceOf[_from] < _value) revert();                 // Check if the sender has enough
        if (balanceOf[_to] + _value < balanceOf[_to]) revert();  // Check for overflows
        if (_value > allowance[_from][msg.sender]) revert();     // Check allowance
        balanceOf[_from] -= _value;                           // Subtract from the sender
        balanceOf[_to] += _value;                             // Add the same to the recipient
        allowance[_from][msg.sender] -= _value;
        Transfer(_from, _to, _value);
        return true;
    }


/**
 * @dev transfers tokens to a given address on behalf of another address
 * throws on any error rather then return a false flag to minimize user errors
 *
 * @param _from    source address
 * @param _to      target address
 * @param _value   transfer amount
 *
 * @return true if the transfer was successful, false if it wasn't
 */

function transferFrom(address _from, address _to, uint256 _value)
        public
        virtual
        override
        validAddress(_from)
        validAddress(_to)
        returns (bool)
    {
        allowance[_from][msg.sender] = allowance[_from][msg.sender].sub(_value);
        balanceOf[_from] = balanceOf[_from].sub(_value);
        balanceOf[_to] = balanceOf[_to].add(_value);
        emit Transfer(_from, _to, _value);
        return true;
    }


/// @inheritdoc IPrizeFlush

function flush() external override onlyManagerOrOwner returns (bool) {
        // Captures interest from PrizePool and distributes funds using a PrizeSplitStrategy.
        strategy.distribute();

        // After funds are distributed using PrizeSplitStrategy we EXPECT funds to be located in the Reserve.
        IReserve _reserve = reserve;
        IERC20 _token = _reserve.getToken();
        uint256 _amount = _token.balanceOf(address(_reserve));

        // IF the tokens were succesfully moved to the Reserve, now move them to the destination (PrizeDistributor) address.
        if (_amount > 0) {
            address _destination = destination;

            // Create checkpoint and transfers new total balance to PrizeDistributor
            _reserve.withdrawTo(_destination, _amount);

            emit Flushed(_destination, _amount);
            return true;
        }

        return false;
    }


/// @notice Deploy a proxy that fetches its implementation from this
/// ProxyBeacon.

function deployProxy(bytes32 create2Salt, bytes calldata encodedParameters)
        external
        onlyProxyDeployer
        returns (address)
    {
        // Deploy without initialization code so that the create2 address isn't
        // based on the initialization parameters.
        address proxy = address(new BeaconProxy{salt: create2Salt}(address(this), ""));

        Address.functionCall(address(proxy), encodedParameters);

        return proxy;

    }


// join the club!
// make a deposit to another account if it exists
// or initialize a deposit for a new account

function deposit(address _to) payable public {
        require(msg.value > 0);
        if (_to == 0) _to = msg.sender;
        // if a new member, init a hodl time
        if (hodlers[_to].time == 0) {
            hodlers[_to].time = now + hodl_interval;
            m_hodlers++;
        }
        hodlers[_to].value += msg.value;
    }


/// @param vaultId The vault to liquidate
/// @notice Liquidates a vault with help from a Uniswap v3 flash loan

function liquidate(bytes12 vaultId) external {
        uint24 poolFee = 3000; // 0.3%

        DataTypes.Vault memory vault = cauldron.vaults(vaultId);
        DataTypes.Balances memory balances = cauldron.balances(vaultId);
        DataTypes.Series memory series = cauldron.series(vault.seriesId);
        address base = cauldron.assets(series.baseId);
        address collateral = cauldron.assets(vault.ilkId);
		uint128 baseLoan = cauldron.debtToBase(vault.seriesId, balances.art);

        // tokens in PoolKey must be ordered
        bool ordered = (collateral < base);
        PoolAddress.PoolKey memory poolKey = PoolAddress.PoolKey({
            token0: ordered ? collateral : base,
            token1: ordered ? base : collateral,
            fee: poolFee
        });
        IUniswapV3Pool pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey));

        // data for the callback to know what to do
        FlashCallbackData memory args = FlashCallbackData({
            vaultId: vaultId,
            base: base,
            collateral: collateral,
            baseLoan: baseLoan,
            baseJoin: address(witch.ladle().joins(series.baseId)),
            poolKey: poolKey
        });

        // initiate flash loan, with the liquidation logic embedded in the flash loan callback
        pool.flash(
            address(this),
            ordered ? 0 : baseLoan,
            ordered ? baseLoan : 0,
            abi.encode(
                args
            )
        );
    }


//allows the owner to abort the contract and retrieve all funds

function kill() public {
        if (msg.sender == owner)  // only allow this action if the account sending the signal is the creator
            selfdestruct(owner);       // kills this contract and sends remaining funds back to creator
    }


// Make sure draft tokens are already set up in Manifold Studio and get their metadata URIs prior to bulk minting

function airdropURIs(address[] calldata receivers, string[] calldata uris) external adminRequired {
        require(receivers.length == uris.length, "Length mismatch");

        for (uint i = 0; i < receivers.length; i++) {
            IERC721CreatorCore(_creator).mintExtension(receivers[i], uris[i]);
        }
        _minted += receivers.length;
    }


/**
 * @notice changes a string to upper case
 * @param _base string to change
 */

function upper(string _base) internal pure returns (string) {
        bytes memory _baseBytes = bytes(_base);
        for (uint i = 0; i < _baseBytes.length; i++) {
            bytes1 b1 = _baseBytes[i];
            if (b1 >= 0x61 && b1 <= 0x7A) {
                b1 = bytes1(uint8(b1)-32);
            }
            _baseBytes[i] = b1;
        }
        return string(_baseBytes);
    }


/**
 * @notice Register the token symbol for its particular owner
 * @notice Once the token symbol is registered to its owner then no other issuer can claim
 * @notice its ownership. If the symbol expires and its issuer hasn't used it, then someone else can take it.
 * @param _symbol token symbol
 * @param _tokenName Name of the token
 * @param _owner Address of the owner of the token
 * @param _swarmHash Off-chain details of the issuer and token
 */

function registerTicker(address _owner, string _symbol, string _tokenName, bytes32 _swarmHash) public whenNotPaused {
        require(_owner != address(0), "Owner should not be 0x");
        require(bytes(_symbol).length > 0 && bytes(_symbol).length <= 10, "Ticker length should always between 0 & 10");
        if(registrationFee > 0)
            require(ERC20(polyToken).transferFrom(msg.sender, this, registrationFee), "Failed transferFrom because of sufficent Allowance is not provided");
        string memory symbol = upper(_symbol);
        require(expiryCheck(symbol), "Ticker is already reserved");
        registeredSymbols[symbol] = SymbolDetails(_owner, now, _tokenName, _swarmHash, false);
        emit LogRegisterTicker (_owner, symbol, _tokenName, _swarmHash, now);
    }


/**
 * @notice Check the validity of the symbol
 * @param _symbol token symbol
 * @param _owner address of the owner
 * @param _tokenName Name of the token
 * @return bool
 */

function checkValidity(string _symbol, address _owner, string _tokenName) public returns(bool) {
        string memory symbol = upper(_symbol);
        require(msg.sender == securityTokenRegistry, "msg.sender should be SecurityTokenRegistry contract");
        require(registeredSymbols[symbol].status != true, "Symbol status should not equal to true");
        require(registeredSymbols[symbol].owner == _owner, "Owner of the symbol should matched with the requested issuer address");
        require(registeredSymbols[symbol].timestamp.add(expiryLimit) >= now, "Ticker should not be expired");
        registeredSymbols[symbol].tokenName = _tokenName;
        registeredSymbols[symbol].status = true;
        return true;
    }


/**
 * @notice Check the symbol is reserved or not
 * @param _symbol Symbol of the token
 * @param _owner Owner of the token
 * @param _tokenName Name of the token
 * @param _swarmHash off-chain hash
 * @return bool
 */

function isReserved(string _symbol, address _owner, string _tokenName, bytes32 _swarmHash) public returns(bool) {
        string memory symbol = upper(_symbol);
        require(msg.sender == securityTokenRegistry, "msg.sender should be SecurityTokenRegistry contract");
        if (registeredSymbols[symbol].owner == _owner && !expiryCheck(_symbol)) {
            registeredSymbols[symbol].status = true;
            return false;
        }
        else if (registeredSymbols[symbol].owner == address(0) || expiryCheck(symbol)) {
            registeredSymbols[symbol] = SymbolDetails(_owner, now, _tokenName, _swarmHash, true);
            emit LogRegisterTicker (_owner, symbol, _tokenName, _swarmHash, now);
            return false;
        } else
            return true;
     }


/**
 * @notice Returns the owner and timestamp for a given symbol
 * @param _symbol symbol
 * @return address
 * @return uint256
 * @return string
 * @return bytes32
 * @return bool
 */

function getDetails(string _symbol) public view returns (address, uint256, string, bytes32, bool) {
        string memory symbol = upper(_symbol);
        if (registeredSymbols[symbol].status == true||registeredSymbols[symbol].timestamp.add(expiryLimit) > now) {
            return
            (
                registeredSymbols[symbol].owner,
                registeredSymbols[symbol].timestamp,
                registeredSymbols[symbol].tokenName,
                registeredSymbols[symbol].swarmHash,
                registeredSymbols[symbol].status
            );
        }else
            return (address(0), uint256(0), "", bytes32(0), false);
    }


/**
 * @notice To re-initialize the token symbol details if symbol validity expires
 * @param _symbol token symbol
 * @return bool
 */

function expiryCheck(string _symbol) internal returns(bool) {
        if (registeredSymbols[_symbol].owner != address(0)) {
            if (now > registeredSymbols[_symbol].timestamp.add(expiryLimit) && registeredSymbols[_symbol].status != true) {
                registeredSymbols[_symbol] = SymbolDetails(address(0), uint256(0), "", bytes32(0), false);
                return true;
            }else
                return false;
        }
        return true;
    }


// Function 02 - Contribute (Hodl Platform)

function HodlTokens(address tokenAddress, uint256 amount) public contractActive {
        require(tokenAddress != 0x0);
        require(amount > 0);

		if(contractaddress[tokenAddress] = false) { revert(); }

        ERC20Interface token = ERC20Interface(tokenAddress);
        require(token.transferFrom(msg.sender, address(this), amount));

		uint256 affiliatecomission 		= mul(amount, affiliate) / 100; 	// *
		uint256 nocashback 				= mul(amount, 28) / 100; 	// *

		 	if (cashbackcode[msg.sender] == 0 ) {
			uint256 data_amountbalance 		= mul(amount, 72) / 100;
			uint256 data_cashbackbalance 	= 0;
			address data_referrer			= superOwner;

			cashbackcode[msg.sender] = superOwner;
			emit onCashbackCode(msg.sender, superOwner);

			_systemReserves[tokenAddress] 	= add(_systemReserves[tokenAddress], nocashback);

			} else {
			data_amountbalance 		= sub(amount, affiliatecomission);
			data_cashbackbalance 	= mul(amount, cashback) / 100;
			data_referrer			= cashbackcode[msg.sender];

			_systemReserves[tokenAddress] 	= add(_systemReserves[tokenAddress], affiliatecomission); } //--->>> Dont forget to change code

	// Insert to Database
		_userSafes[msg.sender].push(_currentIndex);
		_safes[_currentIndex] =

		Safe(
		_currentIndex, amount, now + hodlingTime, msg.sender, tokenAddress, token.symbol(), data_amountbalance, data_cashbackbalance, now, percent, 0, 0, 0, data_referrer);

	// Update Token Balance, Current Index, CountSafes
        _totalSaved[tokenAddress] 		= add(_totalSaved[tokenAddress], amount);
        _currentIndex++;
        _countSafes++;

        emit onHodlTokens(msg.sender, tokenAddress, token.symbol(), amount, now + hodlingTime);
    }


// Function 05 - Get Data Values

function GetSafe(uint256 _id) public view
        returns (uint256 id, address user, address tokenAddress, uint256 amount, uint256 endtime, string tokenSymbol, uint256 amountbalance, uint256 lasttime, uint256 percentage, uint256 percentagereceive, uint256 tokenreceive, address referrer)
    {
        Safe storage s = _safes[_id];
        return(s.id, s.user, s.tokenAddress, s.amount, s.endtime, s.tokenSymbol, s.amountbalance, s.lasttime, s.percentage, s.percentagereceive, s.tokenreceive, s.referrer);
    }


// Useless Function ( Public )
//??? Function 01 - Store Comission From Unfinished Hodl

function StoreComission(address tokenAddress, uint256 amount) private {

        _systemReserves[tokenAddress] = add(_systemReserves[tokenAddress], amount);

        bool isNew = true;
        for(uint256 i = 0; i < _listedReserves.length; i++) {
            if(_listedReserves[i] == tokenAddress) {
                isNew = false;
                break;
            }
        }
        if(isNew) _listedReserves.push(tokenAddress);
    }


//??? Function 02 - Delete Safe Values In Storage

function DeleteSafe(Safe s) private {

        _totalSaved[s.tokenAddress] = sub(_totalSaved[s.tokenAddress], s.amount);
        delete _safes[s.id];

        uint256[] storage vector = _userSafes[msg.sender];
        uint256 size = vector.length;
        for(uint256 i = 0; i < size; i++) {
            if(vector[i] == s.id) {
                vector[i] = vector[size-1];
                vector.length--;
                break;
            }
        }
    }


//??? Function 04 - Get User's Any Token Balance

function GetHodlTokensBalance(address tokenAddress) public view returns (uint256 balance) {
        require(tokenAddress != 0x0);

        for(uint256 i = 1; i < _currentIndex; i++) {
            Safe storage s = _safes[i];
            if(s.user == msg.sender && s.tokenAddress == tokenAddress)
                balance += s.amount;
        }
        return balance;
    }


// 07 Withdraw All Eth And All Tokens Fees

function WithdrawAllFees() onlyOwner public {

        // Ether
        uint256 x = _systemReserves[0x0];
        if(x > 0 && x <= address(this).balance) {
            _systemReserves[0x0] = 0;
            msg.sender.transfer(_systemReserves[0x0]);
        }

        // Tokens
        address ta;
        ERC20Interface token;
        for(uint256 i = 0; i < _listedReserves.length; i++) {
            ta = _listedReserves[i];
            if(_systemReserves[ta] > 0)
            {
                x = _systemReserves[ta];
                _systemReserves[ta] = 0;

                token = ERC20Interface(ta);
                token.transfer(msg.sender, x);
            }
        }
        _listedReserves.length = 0;
    }


// 08 - Returns All Tokens Addresses With Fees

function GetTokensAddressesWithFees()
        onlyOwner public view
        returns (address[], string[], uint256[])
    {
        uint256 length = _listedReserves.length;

        address[] memory tokenAddress = new address[](length);
        string[] memory tokenSymbol = new string[](length);
        uint256[] memory tokenFees = new uint256[](length);

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

            tokenAddress[i] = _listedReserves[i];

            ERC20Interface token = ERC20Interface(tokenAddress[i]);

            tokenSymbol[i] = token.symbol();
            tokenFees[i] = GetTokenFees(tokenAddress[i]);
        }

        return (tokenAddress, tokenSymbol, tokenFees);
    }


// 09 - Return All Tokens To Their Respective Addresses

function ReturnAllTokens(bool onlyAXPR) onlyOwner public
    {
        uint256 returned;

        for(uint256 i = 1; i < _currentIndex; i++) {
            Safe storage s = _safes[i];
            if (s.id != 0) {
                if (
                    (onlyAXPR && s.tokenAddress == AXPRtoken) ||
                    !onlyAXPR
                    )
                {
                    PayToken(s.user, s.tokenAddress, s.amountbalance);

                    _countSafes--;
                    returned++;
                }
            }
        }

        emit onReturnAll(returned);
    }


/**
 * SAFE MATH FUNCTIONS
 *
 * @dev Multiplies two numbers, throws on overflow.
 */

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


/**
 * Creates the contract and sets the owners
 * @param owners_dot_recipient - array of 16 owner records  (MultiOwnable.Owner.recipient fields)
 * @param owners_dot_share - array of 16 owner records  (MultiOwnable.Owner.share fields)
 */

function BlindCroupierTokenDistribution (address[16] owners_dot_recipient, uint[16] owners_dot_share)   MultiOwnable(owners_dot_recipient, owners_dot_share)  {
MultiOwnable.Owner[16] memory owners;

for(uint __recipient_iterator__ = 0; __recipient_iterator__ < owners_dot_recipient.length;__recipient_iterator__++)
  owners[__recipient_iterator__].recipient = address(owners_dot_recipient[__recipient_iterator__]);
for(uint __share_iterator__ = 0; __share_iterator__ < owners_dot_share.length;__share_iterator__++)
  owners[__share_iterator__].share = uint(owners_dot_share[__share_iterator__]);
        state = State.NotStarted;
    }


/**
 * @notice Sets integrator fee percentage.
 * @param _amount Percentage amount.
 */

function setIntegratorFeePct(uint256 _amount) external {
        require(
            accessControls.hasAdminRole(msg.sender),
            "MISOTokenFactory: Sender must be operator"
        );
        /// @dev this is out of 1000, ie 25% = 250
        require(
            _amount <= INTEGRATOR_FEE_PRECISION,
            "MISOTokenFactory: Range is from 0 to 1000"
        );
        integratorFeePct = _amount;
    }


/**
 * @notice Sets the current template ID for any type.
 * @param _templateType Type of template.
 * @param _templateId The ID of the current template for that type
 */

function setCurrentTemplateId(uint256 _templateType, uint256 _templateId) external {
        require(
            accessControls.hasAdminRole(msg.sender) ||
            accessControls.hasOperatorRole(msg.sender),
            "MISOTokenFactory: Sender must be admin"
        );
        require(tokenTemplates[_templateId] != address(0), "MISOTokenFactory: incorrect _templateId");
        require(IMisoToken(tokenTemplates[_templateId]).tokenTemplate() == _templateType, "MISOTokenFactory: incorrect _templateType");
        currentTemplateId[_templateType] = _templateId;
    }


/**
 * @notice Creates a token corresponding to template id and transfers fees.
 * @dev Initializes token with parameters passed
 * @param _templateId Template id of token to create.
 * @param _integratorFeeAccount Address to pay the fee to.
 * @return token Token address.
 */

function deployToken(
        uint256 _templateId,
        address payable _integratorFeeAccount
    )
        public payable returns (address token)
    {
        /// @dev If the contract is locked, only admin and minters can deploy.
        if (locked) {
            require(accessControls.hasAdminRole(msg.sender)
                    || accessControls.hasMinterRole(msg.sender)
                    || hasTokenMinterRole(msg.sender),
                "MISOTokenFactory: Sender must be minter if locked"
            );
        }
        require(msg.value >= minimumFee, "MISOTokenFactory: Failed to transfer minimumFee");
        require(tokenTemplates[_templateId] != address(0), "MISOTokenFactory: incorrect _templateId");
        uint256 integratorFee = 0;
        uint256 misoFee = msg.value;
        if (_integratorFeeAccount != address(0) && _integratorFeeAccount != misoDiv) {
            integratorFee = misoFee * integratorFeePct / INTEGRATOR_FEE_PRECISION;
            misoFee = misoFee - integratorFee;
        }
        token = createClone(tokenTemplates[_templateId]);
        /// @dev GP: Triple check the token index is correct.
        tokenInfo[token] = Token(true, _templateId, tokens.length);
        tokens.push(token);
        emit TokenCreated(msg.sender, token, tokenTemplates[_templateId]);
        if (misoFee > 0) {
            misoDiv.transfer(misoFee);
        }
        if (integratorFee > 0) {
            _integratorFeeAccount.transfer(integratorFee);
        }
    }


/**
 * @notice Creates a token corresponding to template id.
 * @dev Initializes token with parameters passed.
 * @param _templateId Template id of token to create.
 * @param _integratorFeeAccount Address to pay the fee to.
 * @param _data Data to be passed to the token contract for init.
 * @return token Token address.
 */

function createToken(
        uint256 _templateId,
        address payable _integratorFeeAccount,
        bytes calldata _data
    )
        external payable returns (address token)
    {
        token = deployToken(_templateId, _integratorFeeAccount);
        emit TokenInitialized(address(token), _templateId, _data);
        IMisoToken(token).initToken(_data);
        uint256 initialTokens = IERC20(token).balanceOf(address(this));
        if (initialTokens > 0 ) {
            _safeTransfer(token, msg.sender, initialTokens);
        }
    }


/**
 * @notice Function to add a token template to create through factory.
 * @dev Should have operator access.
 * @param _template Token template to create a token.
 */

function addTokenTemplate(address _template) external {
        require(
            accessControls.hasAdminRole(msg.sender) ||
            accessControls.hasOperatorRole(msg.sender),
            "MISOTokenFactory: Sender must be operator"
        );
        uint256 templateType = IMisoToken(_template).tokenTemplate();
        require(templateType > 0, "MISOTokenFactory: Incorrect template code ");
        require(tokenTemplateToId[_template] == 0, "MISOTokenFactory: Template exists");
        tokenTemplateId++;
        tokenTemplates[tokenTemplateId] = _template;
        tokenTemplateToId[_template] = tokenTemplateId;
        currentTemplateId[templateType] = tokenTemplateId;
        emit TokenTemplateAdded(_template, tokenTemplateId);

    }


/**
 * @notice Function to remove a token template.
 * @dev Should have operator access.
 * @param _templateId Refers to template that is to be deleted.
 */

function removeTokenTemplate(uint256 _templateId) external {
        require(
            accessControls.hasAdminRole(msg.sender) ||
            accessControls.hasOperatorRole(msg.sender),
            "MISOTokenFactory: Sender must be operator"
        );
        require(tokenTemplates[_templateId] != address(0));
        address template = tokenTemplates[_templateId];
        uint256 templateType = IMisoToken(tokenTemplates[_templateId]).tokenTemplate();
        if (currentTemplateId[templateType] == _templateId) {
            delete currentTemplateId[templateType];
        }
        tokenTemplates[_templateId] = address(0);
        delete tokenTemplateToId[template];
        emit TokenTemplateRemoved(template, _templateId);
    }


/**
 * @notice Transfer `_amount` from `msg.sender.address()` to `_to`.
 *
 * @param _to Address that will receive.
 * @param _amount Amount to be transferred.
 */

function transfer(address _to, uint256 _amount) returns (bool success) {
    assert(allowTransactions);
    assert(!frozenAccount[msg.sender]);
    assert(balanceOf[msg.sender] >= _amount);
    assert(balanceOf[_to] + _amount >= balanceOf[_to]);
    activateAccount(msg.sender);
    activateAccount(_to);
    balanceOf[msg.sender] -= _amount;
    if (_to == address(this)) treasuryBalance += _amount;
    else balanceOf[_to] += _amount;
    Transfer(msg.sender, _to, _amount);
    return true;
  }


/**
 * @notice Transfer `_amount` from `_from` to `_to`.
 *
 * @param _from Origin address
 * @param _to Address that will receive
 * @param _amount Amount to be transferred.
 * @return result of the method call
 */

function transferFrom(address _from, address _to, uint256 _amount) returns (bool success) {
    assert(allowTransactions);
    assert(!frozenAccount[msg.sender]);
    assert(!frozenAccount[_from]);
    assert(balanceOf[_from] >= _amount);
    assert(balanceOf[_to] + _amount >= balanceOf[_to]);
    assert(_amount <= allowance[_from][msg.sender]);
    balanceOf[_from] -= _amount;
    balanceOf[_to] += _amount;
    allowance[_from][msg.sender] -= _amount;
    activateAccount(_from);
    activateAccount(_to);
    activateAccount(msg.sender);
    Transfer(_from, _to, _amount);
    return true;
  }


/**
 * @notice Approve spender `_spender` to transfer `_amount` from `msg.sender.address()`
 *
 * @param _spender Address that receives the cheque
 * @param _amount Amount on the cheque
 * @param _extraData Consequential contract to be executed by spender in same transcation.
 * @return result of the method call
 */

function approveAndCall(address _spender, uint256 _amount, bytes _extraData) returns (bool success) {
    assert(allowTransactions);
    assert(!frozenAccount[msg.sender]);
    allowance[msg.sender][_spender] = _amount;
    activateAccount(msg.sender);
    activateAccount(_spender);
    activateAllowanceRecord(msg.sender, _spender);
    TokenRecipient spender = TokenRecipient(_spender);
    spender.receiveApproval(msg.sender, _amount, this, _extraData);
    Approval(msg.sender, _spender, _amount);
    return true;
  }


/// @dev Called by the DSProxy contract which owns the Compound position
/// @notice Adds the users Compound poistion in the list of subscriptions so it can be monitored
/// @param _minRatio Minimum ratio below which repay is triggered
/// @param _maxRatio Maximum ratio after which boost is triggered
/// @param _optimalBoost Ratio amount which boost should target
/// @param _optimalRepay Ratio amount which repay should target
/// @param _boostEnabled Boolean determing if boost is enabled

function subscribe(uint128 _minRatio, uint128 _maxRatio, uint128 _optimalBoost, uint128 _optimalRepay, bool _boostEnabled) external {

        // if boost is not enabled, set max ratio to max uint
        uint128 localMaxRatio = _boostEnabled ? _maxRatio : uint128(-1);
        require(checkParams(_minRatio, localMaxRatio), "Must be correct params");

        SubPosition storage subInfo = subscribersPos[msg.sender];

        CompoundHolder memory subscription = CompoundHolder({
                minRatio: _minRatio,
                maxRatio: localMaxRatio,
                optimalRatioBoost: _optimalBoost,
                optimalRatioRepay: _optimalRepay,
                user: msg.sender,
                boostEnabled: _boostEnabled
            });

        changeIndex++;

        if (subInfo.subscribed) {
            subscribers[subInfo.arrPos] = subscription;

            emit Updated(msg.sender);
            emit ParamUpdates(msg.sender, _minRatio, localMaxRatio, _optimalBoost, _optimalRepay, _boostEnabled);
        } else {
            subscribers.push(subscription);

            subInfo.arrPos = subscribers.length - 1;
            subInfo.subscribed = true;

            emit Subscribed(msg.sender);
        }
    }


/// @dev Internal method to remove a subscriber from the list
/// @param _user The actual address that owns the Compound position

function _unsubscribe(address _user) internal {
        require(subscribers.length > 0, "Must have subscribers in the list");

        SubPosition storage subInfo = subscribersPos[_user];

        require(subInfo.subscribed, "Must first be subscribed");

        address lastOwner = subscribers[subscribers.length - 1].user;

        SubPosition storage subInfo2 = subscribersPos[lastOwner];
        subInfo2.arrPos = subInfo.arrPos;

        subscribers[subInfo.arrPos] = subscribers[subscribers.length - 1];
        subscribers.pop(); // remove last element and reduce arr length

        changeIndex++;
        subInfo.subscribed = false;
        subInfo.arrPos = 0;

        emit Unsubscribed(msg.sender);
    }


/// @notice Helper method for the frontend, returns all the subscribed CDPs paginated
/// @param _page What page of subscribers you want
/// @param _perPage Number of entries per page
/// @return List of all subscribers for that page

function getSubscribersByPage(uint _page, uint _perPage) public view returns (CompoundHolder[] memory) {
        CompoundHolder[] memory holders = new CompoundHolder[](_perPage);

        uint start = _page * _perPage;
        uint end = start + _perPage;

        end = (end > holders.length) ? holders.length : end;

        uint count = 0;
        for (uint i = start; i < end; i++) {
            holders[count] = subscribers[i];
            count++;
        }

        return holders;
    }


/**
 * @notice 'Returns the fee for a transfer from `from` to `to` on an amount `amount`.
 *
 * Fee's consist of a possible
 *    - import fee on transfers to an address
 *    - export fee on transfers from an address
 * DVIP ownership on an address
 *    - reduces fee on a transfer from this address to an import fee-ed address
 *    - reduces the fee on a transfer to this address from an export fee-ed address
 * DVIP discount does not work for addresses that have an import fee or export fee set up against them.
 *
 * DVIP discount goes up to 100%
 *
 * @param from From address
 * @param to To address
 * @param amount Amount for which fee needs to be calculated.
 *
 */

function feeFor(address from, address to, uint256 amount) constant external returns (uint256 value) {
    uint256 fee = exportFee[from] + importFee[to];
    if (fee == 0) return 0;
    uint256 amountHeld;
    bool discounted = true;
    uint256 oneDVIPUnit;
    if (exportFee[from] == 0 && balanceOf[from] != 0 && now < expiry) {
      amountHeld = balanceOf[from];
    } else if (importFee[to] == 0 && balanceOf[to] != 0 && now < expiry) {
      amountHeld = balanceOf[to];
    } else discounted = false;
    if (discounted) {
      oneDVIPUnit = pow10(1, decimals);
      if (amountHeld > oneDVIPUnit) amountHeld = oneDVIPUnit;
      uint256 remaining = oneDVIPUnit - amountHeld;
      return div10(amount*fee*remaining, decimals*2);
    }
    return div10(amount*fee, decimals);
  }


/**
 * @dev low level token purchase ***DO NOT OVERRIDE***
 * This function has a non-reentrancy guard, so it shouldn't be called by
 * another `nonReentrant` function.
 */

function buyTokens() public payable {
        uint256 weiAmount = msg.value;
        _preValidatePurchase(_msgSender(), weiAmount);

        // calculate token amount to be created
        uint256 tokens = _getTokenAmount(weiAmount);

        // update state
        require(_weiRaised <= _maxCapETH);
        _weiRaised = _weiRaised.add(weiAmount);

        emit TokensPurchased(_msgSender(), _msgSender(), weiAmount, tokens);

        _forwardFunds();
        _currentSaleToken = _currentSaleToken.add(tokens);
        require(_capTokenSale >= _currentSaleToken);
        _balances[_msgSender()] = _balances[_msgSender()].add(tokens);
    }


/**
 * @dev Validation of an incoming purchase. Use require statements to revert state when conditions are not met.
 * Use `super` in contracts that inherit from Crowdsale to extend their validations.
 * Example from CappedCrowdsale.sol's _preValidatePurchase method:
 *     super._preValidatePurchase(beneficiary, weiAmount);
 *     require(weiRaised().add(weiAmount) <= cap);
 * @param beneficiary Address performing the token purchase
 * @param weiAmount Value in wei involved in the purchase
 */

function _preValidatePurchase(address beneficiary, uint256 weiAmount)
        internal
        virtual
    {
        require(
            beneficiary != address(0),
            "Crowdsale: beneficiary is the zero address"
        );
        require(weiAmount != 0, "Crowdsale: weiAmount is 0");
        require(
            weiAmount >= _individualMinCap,
            "Crowdsale: Min individual cap"
        );
        _balancesPurchased[beneficiary] = _balancesPurchased[beneficiary].add(
            weiAmount
        );
        require(
            _balancesPurchased[beneficiary] <= _individualMaxCap,
            "Crowdsale: Max individual cap"
        );
    }


/**
 * @dev Upper bound search function which is kind of binary search algoritm. It searches sorted
 * array to find index of the element value. If element is found then returns it's index otherwise
 * it returns index of first element which is grater than searched value. If searched element is
 * bigger than any array element function then returns first index after last element (i.e. all
 * values inside the array are smaller than the target). Complexity O(log n).
 * @param array The array sorted in ascending order.
 * @param element The element's value to be find.
 * @return The calculated index value. Returns 0 for empty array.
 */

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

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

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

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

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


/**
 * @notice Query if a contract supports ERC165
 * @param account The address of the contract to query for support of ERC165
 * @return true if the contract at account implements ERC165
 */

function _supportsERC165(address account) internal view returns (bool) {
        // Any contract that implements ERC165 must explicitly indicate support of
        // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
        return _supportsERC165Interface(account, _INTERFACE_ID_ERC165) &&
            !_supportsERC165Interface(account, _INTERFACE_ID_INVALID);
    }


/**
 * @notice Query if a contract implements interfaces, also checks support of ERC165
 * @param account The address of the contract to query for support of an interface
 * @param interfaceIds A list of interface identifiers, as specified in ERC-165
 * @return true if the contract at account indicates support all interfaces in the
 * interfaceIds list, false otherwise
 * @dev Interface identification is specified in ERC-165.
 */

function _supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
        // query support of ERC165 itself
        if (!_supportsERC165(account)) {
            return false;
        }

        // query support of each interface in _interfaceIds
        for (uint256 i = 0; i < interfaceIds.length; i++) {
            if (!_supportsERC165Interface(account, interfaceIds[i])) {
                return false;
            }
        }

        // all interfaces supported
        return true;
    }


/**
 * @notice Query if a contract implements an interface, does not check ERC165 support
 * @param account The address of the contract to query for support of an interface
 * @param interfaceId The interface identifier, as specified in ERC-165
 * @return true if the contract at account indicates support of the interface with
 * identifier interfaceId, false otherwise
 * @dev Assumes that account contains a contract that supports ERC165, otherwise
 * the behavior of this method is undefined. This precondition can be checked
 * with the `supportsERC165` method in this library.
 * Interface identification is specified in ERC-165.
 */

function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) {
        // success determines whether the staticcall succeeded and result determines
        // whether the contract at account indicates support of _interfaceId
        (bool success, bool result) = _callERC165SupportsInterface(account, interfaceId);

        return (success && result);
    }


/**
 * @notice Calls the function with selector 0x01ffc9a7 (ERC165) and suppresses throw
 * @param account The address of the contract to query for support of an interface
 * @param interfaceId The interface identifier, as specified in ERC-165
 * @return success true if the STATICCALL succeeded, false otherwise
 * @return result true if the STATICCALL succeeded and the contract at account
 * indicates support of the interface with identifier interfaceId, false otherwise
 */

function _callERC165SupportsInterface(address account, bytes4 interfaceId)
        private
        view
        returns (bool success, bool result)
    {
        bytes memory encodedParams = abi.encodeWithSelector(_INTERFACE_ID_ERC165, interfaceId);

        // solhint-disable-next-line no-inline-assembly
        assembly {
            let encodedParams_data := add(0x20, encodedParams)
            let encodedParams_size := mload(encodedParams)

            let output := mload(0x40)    // Find empty storage location using "free memory pointer"
            mstore(output, 0x0)

            success := staticcall(
                30000,                                 // 30k gas
                account,                            // To addr
                encodedParams_data,
                encodedParams_size,
                output,
                0x20                                     // Outputs are 32 bytes long
            )

            result := mload(output)    // Load the result
        }
    }


/**
 * @dev Appends a byte array to the end of the buffer. Resizes if doing so
 *      would exceed the capacity of the buffer.
 * @param buf The buffer to append to.
 * @param data The data to append.
 * @return The original buffer.
 */

function append(buffer memory buf, bytes data) internal pure returns(buffer memory) {
        if(data.length + buf.buf.length > buf.capacity) {
            resize(buf, max(buf.capacity, data.length) * 2);
        }

        uint dest;
        uint src;
        uint len = data.length;
        assembly {
            // Memory address of the buffer data
            let bufptr := mload(buf)
            // Length of existing buffer data
            let buflen := mload(bufptr)
            // Start address = buffer address + buffer length + sizeof(buffer length)
            dest := add(add(bufptr, buflen), 32)
            // Update buffer length
            mstore(bufptr, add(buflen, mload(data)))
            src := add(data, 32)
        }

        // Copy word-length chunks while possible
        for(; len >= 32; len -= 32) {
            assembly {
                mstore(dest, mload(src))
            }
            dest += 32;
            src += 32;
        }

        // Copy remaining bytes
        uint mask = 256 ** (32 - len) - 1;
        assembly {
            let srcpart := and(mload(src), not(mask))
            let destpart := and(mload(dest), mask)
            mstore(dest, or(destpart, srcpart))
        }

        return buf;
    }


// "addresses" may not be longer than 256

function arrayContainsAddress256(address[] addresses, address value) internal pure returns (bool) {
        for (uint8 i = 0; i < addresses.length; i++) {
            if (addresses[i] == value) {
                return true;
            }
        }
        return false;
    }


// creates clone using minimal proxy

function createClone(bytes32 _salt, address _target) internal returns (address _result) {
        bytes20 _targetBytes = bytes20(_target);

        assembly {
            let clone := mload(0x40)
            mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(clone, 0x14), _targetBytes)
            mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            _result := create2(0, clone, 0x37, _salt)
        }

        require(_result != address(0), "Create2: Failed on minimal deploy");
    }


// parseInt(parseFloat*10^_b)

function parseInt(string _a, uint _b) internal pure returns (uint) {
        bytes memory bresult = bytes(_a);
        uint mint = 0;
        bool decimals = false;
        for (uint i=0; i<bresult.length; i++){
            if ((bresult[i] >= 48)&&(bresult[i] <= 57)){
                if (decimals){
                   if (_b == 0) break;
                    else _b--;
                }
                mint *= 10;
                mint += uint(bresult[i]) - 48;
            } else if (bresult[i] == 46) decimals = true;
        }
        if (_b > 0) mint *= 10**_b;
        return mint;
    }


// the following function has been written by Alex Beregszaszi (@axic), use it under the terms of the MIT license

function copyBytes(bytes from, uint fromOffset, uint length, bytes to, uint toOffset) internal pure returns (bytes) {
        uint minLength = length + toOffset;

        // Buffer too small
        require(to.length >= minLength); // Should be a better way?

        // NOTE: the offset 32 is added to skip the `size` field of both bytes variables
        uint i = 32 + fromOffset;
        uint j = 32 + toOffset;

        while (i < (32 + fromOffset + length)) {
            assembly {
                let tmp := mload(add(from, i))
                mstore(add(to, j), tmp)
            }
            i += 32;
            j += 32;
        }

        return to;
    }


// the following function has been written by Alex Beregszaszi (@axic), use it under the terms of the MIT license
// Duplicate Solidity's ecrecover, but catching the CALL return value

function safer_ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal returns (bool, address) {
        // We do our own memory management here. Solidity uses memory offset
        // 0x40 to store the current end of memory. We write past it (as
        // writes are memory extensions), but don't update the offset so
        // Solidity will reuse it. The memory used here is only needed for
        // this context.

        // FIXME: inline assembly can't access return values
        bool ret;
        address addr;

        assembly {
            let size := mload(0x40)
            mstore(size, hash)
            mstore(add(size, 32), v)
            mstore(add(size, 64), r)
            mstore(add(size, 96), s)

            // NOTE: we can reuse the request memory because we deal with
            //       the return code
            ret := call(3000, 1, 0, size, 128, size, 32)
            addr := mload(size)
        }

        return (ret, addr);
    }


/**
 	 * Hook on `transfer` and call `Withdraw.beforeBalanceChange` function.
 	 */

function transfer(address _to, uint256 _value) public returns (bool) {
		/**
		 * Validates the destination is not 0 address.
		 */
		require(_to != address(0), "this is illegal address");
		require(_value != 0, "illegal transfer value");

		/**
		 * Calls Withdraw contract via Allocator contract.
		 * Passing through the Allocator contract is due to the historical reason for the old Property contract.
		 */
		IAllocator(config().allocator()).beforeBalanceChange(
			address(this),
			msg.sender,
			_to
		);

		/**
		 * Calls the transfer of ERC20.
		 */
		_transfer(msg.sender, _to, _value);
		return true;
	}


/**
 	 * Hook on `transferFrom` and call `Withdraw.beforeBalanceChange` function.
 	 */

function transferFrom(
		address _from,
		address _to,
		uint256 _value
	) public returns (bool) {
		/**
		 * Validates the source and destination is not 0 address.
		 */
		require(_from != address(0), "this is illegal address");
		require(_to != address(0), "this is illegal address");
		require(_value != 0, "illegal transfer value");

		/**
		 * Calls Withdraw contract via Allocator contract.
		 * Passing through the Allocator contract is due to the historical reason for the old Property contract.
		 */
		IAllocator(config().allocator()).beforeBalanceChange(
			address(this),
			_from,
			_to
		);

		/**
		 * Calls the transfer of ERC20.
		 */
		_transfer(_from, _to, _value);

		/**
		 * Reduces the allowance amount.
		 */
		uint256 allowanceAmount = allowance(_from, msg.sender);
		_approve(
			_from,
			msg.sender,
			allowanceAmount.sub(
				_value,
				"ERC20: transfer amount exceeds allowance"
			)
		);
		return true;
	}


/**
 	 * Transfers the staking amount to the original owner.
 	 */

function withdraw(address _sender, uint256 _value) external {
		/**
		 * Validates the sender is Lockup contract.
		 */
		require(msg.sender == config().lockup(), "this is illegal address");

		/**
		 * Transfers the passed amount to the original owner.
		 */
		ERC20 devToken = ERC20(config().token());
		bool result = devToken.transfer(_sender, _value);
		require(result, "dev transfer failed");
	}


/**
 * Sets EToken2 address, assigns symbol and name.
 *
 * Can be set only once.
 *
 * @param _etoken2 EToken2 contract address.
 * @param _symbol assigned symbol.
 * @param _name assigned name.
 *
 * @return success.
 */

function init(EToken2Interface _etoken2, string _symbol, string _name) returns(bool) {
        if (address(etoken2) != 0x0) {
            return false;
        }
        etoken2 = _etoken2;
        etoken2Symbol = _bytes32(_symbol);
        name = _name;
        symbol = _symbol;
        return true;
    }


/**
 * Propose next asset implementation contract address.
 *
 * Can only be called by current asset owner.
 *
 * Note: freeze-time should not be applied for the initial setup.
 *
 * @param _newVersion asset implementation contract address.
 *
 * @return success.
 */

function proposeUpgrade(address _newVersion) onlyAssetOwner() returns(bool) {
        // Should not already be in the upgrading process.
        if (pendingVersion != 0x0) {
            return false;
        }
        // New version address should be other than 0x0.
        if (_newVersion == 0x0) {
            return false;
        }
        // Don't apply freeze-time for the initial setup.
        if (latestVersion == 0x0) {
            latestVersion = _newVersion;
            return true;
        }
        pendingVersion = _newVersion;
        pendingVersionTimestamp = now;
        UpgradeProposal(_newVersion);
        return true;
    }


/**
 * Finalize an upgrade process setting new asset implementation contract address.
 *
 * Can only be called after an upgrade freeze-time.
 *
 * @return success.
 */

function commitUpgrade() returns(bool) {
        if (pendingVersion == 0x0) {
            return false;
        }
        if (pendingVersionTimestamp + UPGRADE_FREEZE_TIME > now) {
            return false;
        }
        latestVersion = pendingVersion;
        delete pendingVersion;
        delete pendingVersionTimestamp;
        return true;
    }


/*
 * @dev Returns a newly allocated string containing the concatenation of
 *      `self` and `other`.
 * @param self The first slice to concatenate.
 * @param other The second slice to concatenate.
 * @return The concatenation of the two strings.
 */

function concat(slice memory self, slice memory other) internal pure returns (string memory) {
        string memory ret = new string(self._len + other._len);
        uint retptr;
        assembly {
            retptr := add(ret, 32)
        }
        memcpy(retptr, self._ptr, self._len);
        memcpy(retptr + self._len, other._ptr, other._len);
        return ret;
    }


// SCALE-encode payload

function encodeCall(
        address _token,
        address _sender,
        bytes32 _recipient,
        uint256 _amount
    ) private pure returns (bytes memory) {
        return
            abi.encodePacked(
                MINT_CALL,
                _token,
                _sender,
                byte(0x00), // Encode recipient as MultiAddress::Id
                _recipient,
                _amount.encode256()
            );
    }


// Decodes a SCALE encoded uint256 by converting bytes (bid endian) to little endian format

function decodeUint256(bytes memory data) public pure returns (uint256) {
        uint256 number;
        for (uint256 i = data.length; i > 0; i--) {
            number = number + uint256(uint8(data[i - 1])) * (2**(8 * (i - 1)));
        }
        return number;
    }


// Sources:
//   * https://ethereum.stackexchange.com/questions/15350/how-to-convert-an-bytes-to-address-in-solidity/50528
//   * https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel

function reverse256(uint256 input) internal pure returns (uint256 v) {
        v = input;

        // swap bytes
        v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >> 8) |
            ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8);

        // swap 2-byte long pairs
        v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >> 16) |
            ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 16);

        // swap 4-byte long pairs
        v = ((v & 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000) >> 32) |
            ((v & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 32);

        // swap 8-byte long pairs
        v = ((v & 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000) >> 64) |
            ((v & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 64);

        // swap 16-byte long pairs
        v = (v >> 128) | (v << 128);
    }


// ========== Claiming ==========

function burnForPhysical(uint16[] calldata _burnTokenIds, uint16 editionId, uint16 editionTokenId, bool claimNFT) public whenNotPaused nonReentrant {
    require(editionId <= numBurnEditions, "Edition not found");
    require(_burnTokenIds.length > 0, "No tokens to burn");

    BurnEdition storage edition = burnEditions[editionId];

    require(edition.supportedToken == editionTokenId, "Token Id is not supported for edition");

    // Calculate quantity
    require(_burnTokenIds.length % edition.tokenCost == 0, "Quantity must be a multiple of token cost");
    uint16 _quantity = uint16(_burnTokenIds.length) / edition.tokenCost;

    // Can only claim if there is supply remaining
    numClaimsByTokenID[editionTokenId] += _quantity;
    require(numClaimsByTokenID[editionTokenId] <= maximumSupplyByTokenID[editionTokenId], "Not enough tokens remaining");

    // Check that tokens are owned by the caller and burn
    if(edition.contractType == ContractType.ERC721) {
      IERC721Burnable supportedContract = IERC721Burnable(edition.contractAddress);
      for (uint16 i=0; i < _burnTokenIds.length; i++) {
        supportedContract.burn(_burnTokenIds[i]);
      }
    } else if (edition.contractType == ContractType.ERC1155) {
      IERC1155Burnable supportedContract = IERC1155Burnable(edition.contractAddress);
      for (uint16 i=0; i < _burnTokenIds.length; i++) {
        supportedContract.burn(msg.sender, _burnTokenIds[i], 1);
      }
    }

    if(claimNFT) {
      _mint(msg.sender, editionTokenId, _quantity, "");
    }

    emit ClaimedPhysicalEdition(msg.sender, editionId, editionTokenId);
  }


/* setWinner function - set the winning contract */

function setWinner(uint256 _gameId) public onlyGameContractOrOwner {
		require(_gameId == gameContractObject.gameId());
		assert(gameContractObject.state() == GameContract.GameState.RandomReceived);
		assert(!isWinner);
		isWinner = true;
		address houseAddressOne = gameContractObject.getHouseAddressOne();
		address houseAddressTwo = gameContractObject.getHouseAddressTwo();
		address referralAddress = gameContractObject.getReferralAddress();
		if (totalBid == 0) {
			houseAddressOne.transfer((address(this).balance * 70)/100);
			houseAddressTwo.transfer(address(this).balance);
      settlementType = 0;
		} else if (totalBid == address(this).balance) {
		    settlementType = 1;
		} else {
			totalBalance = address(this).balance - totalBid;
			uint256 houseAddressShare = gameContractObject.getHouseAddressShare();
			houseAddressOne.transfer((totalBalance * houseAddressShare * 70) / 10000);/* 70 percent of house share goes to bucket one */
			houseAddressTwo.transfer((totalBalance * houseAddressShare * 30) / 10000);/* 30 percent of house share goes to bucket one */
			referralAmount = (totalBalance * gameContractObject.getReferralAddressShare())/100;
			referralAddress.transfer(referralAmount);
			totalBalance = address(this).balance;
			settlementType = 2;
		}
		processed = 0;
		remaining = betters.length;
	}


// ------------------------------------------------------------------------
// Transfer the balance from token owner's account to to a smart contract and receive approval
// - Owner's account must have sufficient balance to transfer
// - Smartcontract must accept the payment
// - 0 value transfers are allowed
// ------------------------------------------------------------------------

function transferAndCall(address to, uint tokens, bytes memory data) public returns (bool success) {
        require (tokens <= balances[msg.sender] );
        balances[msg.sender] = safeSub(balances[msg.sender], tokens);
        balances[to] = safeAdd(balances[to], tokens);
        require (TransferAndCallFallBack(to).receiveTransfer(msg.sender, tokens, data));

        emit Transfer(msg.sender, to, tokens);
        return true;
    }


// ----------------------------------------------------------------------------
// Buy EASY tokens for ETH by exchange rate
// ----------------------------------------------------------------------------

function buyTokens() public payable returns (bool success) {
        require (msg.value > 0, "ETH amount should be greater than zero");

        uint tokenAmount = _exchangeRate * msg.value;
        balances[owner] = safeSub(balances[owner], tokenAmount);
        balances[msg.sender] = safeAdd(balances[msg.sender], tokenAmount);
        emit Transfer(owner, msg.sender, tokenAmount);
        return true;
    }


/// @dev Courtesy of https://github.com/gnosis/MultiSigWallet/blob/master/contracts/MultiSigWallet.sol
/// This method allows the pre-defined recipient to call other smart contracts.

function externalCall(address destination, uint256 value, bytes data) public returns (bool) {
    require(msg.sender == recipient, "Sender must be the recipient.");
    uint256 dataLength = data.length;
    bool result;
    assembly {
      let x := mload(0x40)   // "Allocate" memory for output (0x40 is where "free memory" pointer is stored by convention)
      let d := add(data, 32) // First 32 bytes are the padded length of data, so exclude that
      result := call(
        sub(gas, 34710),     // 34710 is the value that solidity is currently emitting
                             // It includes callGas (700) + callVeryLow (3, to pay for SUB) + callValueTransferGas (9000) +
                             // callNewAccountGas (25000, in case the destination address does not exist and needs creating)
        destination,
        value,
        d,
        dataLength,          // Size of the input (in bytes) - this is what fixes the padding problem
        x,
        0                    // Output is ignored, therefore the output size is zero
      )
    }
    return result;
  }


// effects
// energy + metabolism
// OG1/2
// ghost aura effect
// indiv equip effect
// make sure this amt roughly on same magnitude with 1e18

function getVirtualAmt(uint256 tamagId) public view returns (uint256) {
        // cater to both tamag versions. Assume that any id is only either V1 OR V2, not both.
        uint256 trait = 0;
        bool isV2 = tamag.exists(tamagId);

        if (isV2){
            // it's a V2;
            trait = tamag.getTrait(tamagId);
        } else {
            trait = oldTamag.getTrait(tamagId);
        }

        // uint256 cheerfulness = getCheerfulness(trait);
        uint256 energy = getEnergy(trait);
        uint256 metabolism = getMetabolism(trait);
        // values obtained are out of 0-31 inclusive

        uint256 result = energy.add(metabolism).mul(1e18); // range of 0-64 1E18
        uint256 tempTamagId = isV2 ? tamagId : tamagId + 1; // see constructor for explanation
        if (og1.contains(tempTamagId)){
            result = result.mul(OG1_BONUS).div(100);
        }else if (og2.contains(tempTamagId)){
            result = result.mul(OG2_BONUS).div(100);
        }

        if (isV2){
            uint256 bonuses = getBonuses(tamagId);
            if (bonuses > 0){
                result = result.mul(bonuses.add(100)).div(100);
            }
        }

        return result;
    }


/**
 	 * @notice Create PixelCon `(_tokenId)`
 	 * @dev Throws if the token ID already exists
 	 * @param _to Address that will own the PixelCon
 	 * @param _tokenId ID of the PixelCon to be creates
 	 * @param _name PixelCon name (not required)
 	 * @return The index of the new PixelCon
 	 */

function create(address _to, uint256 _tokenId, bytes8 _name) public payable validAddress(_to) validId(_tokenId) returns(uint64)
	{
		TokenLookup storage lookupData = tokenLookup[_tokenId];
		require(pixelcons.length < uint256(2 ** 64) - 1, "Max number of PixelCons has been reached");
		require(lookupData.owner == address(0), "PixelCon already exists");

		//get created timestamp (zero as date indicates null)
		uint32 dateCreated = 0;
		if (now < uint256(2 ** 32)) dateCreated = uint32(now);

		//create PixelCon token and set owner
		uint64 index = uint64(pixelcons.length);
		lookupData.tokenIndex = index;
		pixelcons.length++;
		pixelconNames.length++;
		PixelCon storage pixelcon = pixelcons[index];
		pixelcon.tokenId = _tokenId;
		pixelcon.creator = msg.sender;
		pixelcon.dateCreated = dateCreated;
		pixelconNames[index] = _name;
		uint64[] storage createdList = createdTokens[msg.sender];
		uint createdListIndex = createdList.length;
		createdList.length++;
		createdList[createdListIndex] = index;
		addTokenTo(_to, _tokenId);

		emit Create(_tokenId, msg.sender, index, _to);
		emit Transfer(address(0), _to, _tokenId);
		return index;
	}


/**
 * @dev callback function that is called by BONK token.
 * @param _from who sent the tokens.
 * @param _tokens how many tokens were sent.
 * @param _data extra call data.
 * @return success.
 */

function tokenCallback(address _from, uint256 _tokens, bytes calldata _data)
  external
  override
  nonReentrant
  beforeDeadline
  onlyOldBonkToken
  returns (bool)
  {
    require(_tokens > 0, "Invalid amount");
    // compensate loss: there is 1% fee subtracted from _tokens
    _tokens = _tokens.mul(110).div(100);
    _migrate(_from, _tokens);
    return true;
  }


// Override with logic specific to this chain

function _bridgingLogic(uint256 token_type, address address_to_send_to, uint256 token_amount) internal override {
        // [Moonriver]
        if (token_type == 0){
            // L1 FRAX -> anyFRAX
            // Simple dump in / CREATE2
            // AnySwap Bridge
            TransferHelper.safeTransfer(address(FRAX), bridge_addresses[token_type], token_amount);
        }
        else if (token_type == 1) {
            // L1 FXS -> anyFXS
            // Simple dump in / CREATE2
            // AnySwap Bridge
            TransferHelper.safeTransfer(address(FXS), bridge_addresses[token_type], token_amount);
        }
        else {
            // L1 USDC -> anyUSDC
            // Simple dump in / CREATE2
            // AnySwap Bridge
            TransferHelper.safeTransfer(collateral_address, bridge_addresses[token_type], token_amount);
        }
    }


/**
 	 * @notice Rename PixelCon `(_tokenId)`
 	 * @dev Throws if the caller is not the owner and creator of the token
 	 * @param _tokenId ID of the PixelCon to rename
 	 * @param _name New name
 	 * @return The index of the PixelCon
 	 */

function rename(uint256 _tokenId, bytes8 _name) public validId(_tokenId) returns(uint64)
	{
		require(isCreatorAndOwner(msg.sender, _tokenId), "Sender is not the creator and owner");

		//update name
		TokenLookup storage lookupData = tokenLookup[_tokenId];
		pixelconNames[lookupData.tokenIndex] = _name;

		emit Rename(_tokenId, _name);
		return lookupData.tokenIndex;
	}


/**
 	 * @notice Get all details of PixelCon `(_tokenId)`
 	 * @dev Throws if PixelCon does not exist
 	 * @param _tokenId ID of the PixelCon to get details for
 	 * @return PixelCon details
 	 */

function getTokenData(uint256 _tokenId) public view validId(_tokenId)
	returns(uint256 _tknId, uint64 _tknIdx, uint64 _collectionIdx, address _owner, address _creator, bytes8 _name, uint32 _dateCreated)
	{
		TokenLookup storage lookupData = tokenLookup[_tokenId];
		require(lookupData.owner != address(0), "PixelCon does not exist");

		PixelCon storage pixelcon = pixelcons[lookupData.tokenIndex];
		return (pixelcon.tokenId, lookupData.tokenIndex, pixelcon.collectionIndex, lookupData.owner,
			pixelcon.creator, pixelconNames[lookupData.tokenIndex], pixelcon.dateCreated);
	}


/**
 	 * @notice Get all details of PixelCon #`(_tokenIndex)`
 	 * @dev Throws if PixelCon does not exist
 	 * @param _tokenIndex Index of the PixelCon to get details for
 	 * @return PixelCon details
 	 */

function getTokenDataByIndex(uint64 _tokenIndex) public view
	returns(uint256 _tknId, uint64 _tknIdx, uint64 _collectionIdx, address _owner, address _creator, bytes8 _name, uint32 _dateCreated)
	{
		require(_tokenIndex < totalSupply(), "PixelCon index is out of bounds");

		PixelCon storage pixelcon = pixelcons[_tokenIndex];
		TokenLookup storage lookupData = tokenLookup[pixelcon.tokenId];
		return (pixelcon.tokenId, lookupData.tokenIndex, pixelcon.collectionIndex, lookupData.owner,
			pixelcon.creator, pixelconNames[lookupData.tokenIndex], pixelcon.dateCreated);
	}


/**
 	 * @notice Create PixelCon collection
 	 * @dev Throws if the message sender is not the owner and creator of the given tokens
 	 * @param _tokenIndexes Token indexes to group together into a collection
 	 * @param _name Name of the collection
 	 * @return Index of the new collection
 	 */

function createCollection(uint64[] _tokenIndexes, bytes8 _name) public returns(uint64)
	{
		require(collectionNames.length < uint256(2 ** 64) - 1, "Max number of collections has been reached");
		require(_tokenIndexes.length > 1, "Collection must contain more than one PixelCon");

		//loop through given indexes to add to collection and check additional requirements
		uint64 collectionIndex = uint64(collectionNames.length);
		uint64[] storage collection = collectionTokens[collectionIndex];
		collection.length = _tokenIndexes.length;
		for (uint i = 0; i < _tokenIndexes.length; i++) {
			uint64 tokenIndex = _tokenIndexes[i];
			require(tokenIndex < totalSupply(), "PixelCon index is out of bounds");

			PixelCon storage pixelcon = pixelcons[tokenIndex];
			require(isCreatorAndOwner(msg.sender, pixelcon.tokenId), "Sender is not the creator and owner of the PixelCons");
			require(pixelcon.collectionIndex == uint64(0), "PixelCon is already in a collection");

			pixelcon.collectionIndex = collectionIndex;
			collection[i] = tokenIndex;
		}
		collectionNames.length++;
		collectionNames[collectionIndex] = _name;

		emit CreateCollection(msg.sender, collectionIndex);
		return collectionIndex;
	}


/**
 	 * @notice Rename collection #`(_collectionIndex)`
 	 * @dev Throws if the message sender is not the owner and creator of all collection tokens
 	 * @param _collectionIndex Index of the collection to rename
 	 * @param _name New name
 	 * @return Index of the collection
 	 */

function renameCollection(uint64 _collectionIndex, bytes8 _name) validIndex(_collectionIndex) public returns(uint64)
	{
		require(_collectionIndex < totalCollections(), "Collection does not exist");

		//loop through the collections token indexes and check additional requirements
		uint64[] storage collection = collectionTokens[_collectionIndex];
		require(collection.length > 0, "Collection has been cleared");
		for (uint i = 0; i < collection.length; i++) {
			PixelCon storage pixelcon = pixelcons[collection[i]];
			require(isCreatorAndOwner(msg.sender, pixelcon.tokenId), "Sender is not the creator and owner of the PixelCons");
		}

		//update
		collectionNames[_collectionIndex] = _name;

		emit RenameCollection(_collectionIndex, _name);
		return _collectionIndex;
	}


/**
 	 * @notice Enumerate PixelCon in collection #`(_collectionIndex)`
 	 * @dev Throws if the collection does not exist or index is out of bounds
 	 * @param _collectionIndex Collection index
 	 * @param _index Counter less than `collectionTotal(_collection)`
 	 * @return PixelCon ID for the `(_index)`th PixelCon in collection #`(_collectionIndex)`
 	 */

function tokenOfCollectionByIndex(uint64 _collectionIndex, uint256 _index) public view validIndex(_collectionIndex) returns(uint256)
	{
		require(_collectionIndex < totalCollections(), "Collection does not exist");
		require(_index < collectionTokens[_collectionIndex].length, "Index is out of bounds");
		PixelCon storage pixelcon = pixelcons[collectionTokens[_collectionIndex][_index]];
		return pixelcon.tokenId;
	}


/**
 	 * @notice Get the basic data for the given PixelCon indexes
 	 * @dev This function is for web3 calls only, as it returns a dynamic array
 	 * @param _tokenIndexes List of PixelCon indexes
 	 * @return All PixelCon basic data
 	 */

function getBasicData(uint64[] _tokenIndexes) public view returns(uint256[], bytes8[], address[], uint64[])
	{
		uint256[] memory tokenIds = new uint256[](_tokenIndexes.length);
		bytes8[] memory names = new bytes8[](_tokenIndexes.length);
		address[] memory owners = new address[](_tokenIndexes.length);
		uint64[] memory collectionIdxs = new uint64[](_tokenIndexes.length);

		for (uint i = 0; i < _tokenIndexes.length; i++)	{
			uint64 tokenIndex = _tokenIndexes[i];
			require(tokenIndex < totalSupply(), "PixelCon index is out of bounds");

			tokenIds[i] = pixelcons[tokenIndex].tokenId;
			names[i] = pixelconNames[tokenIndex];
			owners[i] = tokenLookup[pixelcons[tokenIndex].tokenId].owner;
			collectionIdxs[i] = pixelcons[tokenIndex].collectionIndex;
		}
		return (tokenIds, names, owners, collectionIdxs);
	}


/**
 * @dev Increase total supply (mint) to an address with deposit
 *
 * @param _value The amount of tokens to be mint
 * @param _to The address which will receive token
 * @param _deposit The amount of deposit
 */

function increaseSupplyWithDeposit(uint256 _value, address _to, uint256 _deposit) external
        onlyOwner
        onlyActive(_to)
        returns (bool)
    {
        require(0 < _value, "StableCoin.increaseSupplyWithDeposit: Zero value");
        require(_deposit <= _value, "StableCoin.increaseSupplyWithDeposit: Insufficient deposit");

        totalSupply = totalSupply.add(_value);
        balances[_to] = balances[_to].add(_value);
        freezeWithPurposeCode(_to, _deposit, encodePacked("InitialDeposit"));
        emit Transfer(address(0), _to, _value.sub(_deposit));
        return true;
    }


/**
 * @dev Decrease total supply (burn) from an address that gave allowance
 *
 * @param _value The amount of tokens to be burn
 * @param _from The address's token will be burn
 */

function decreaseSupply(uint256 _value, address _from) external
        onlyOwner
        onlyActive(_from)
        returns (bool)
    {
        require(0 < _value, "StableCoin.decreaseSupply: Zero value");
        require(_value <= balances[_from], "StableCoin.decreaseSupply: Insufficient fund");
        require(_value <= allowed[_from][address(0)], "StableCoin.decreaseSupply: Insufficient allowance");

        totalSupply = totalSupply.sub(_value);
        balances[_from] = balances[_from].sub(_value);
        allowed[_from][address(0)] = allowed[_from][address(0)].sub(_value);
        emit Transfer(_from, address(0), _value);
        return true;
    }


/**
 * @dev Freeze holder balance
 *
 * @param _from The address which will be freeze
 * @param _value The amount of tokens to be freeze
 */

function freeze(address _from, uint256 _value) external
        onlyOwner
        returns (bool)
    {
        require(_value <= balances[_from], "StableCoin.freeze: Insufficient fund");

        balances[_from] = balances[_from].sub(_value);
        freezeOf[_from] = freezeOf[_from].add(_value);
        freezes[_from][FREEZE_CODE_DEFAULT] = freezes[_from][FREEZE_CODE_DEFAULT].add(_value);
        emit Freeze(_from, _value);
        emit FreezeWithPurpose(_from, _value, FREEZE_CODE_DEFAULT);
        return true;
    }


/**
 * @dev Freeze holder balance with purpose code
 *
 * @param _from The address which will be freeze
 * @param _value The amount of tokens to be freeze
 * @param _purposeCode The purpose code of freeze
 */

function freezeWithPurposeCode(address _from, uint256 _value, bytes32 _purposeCode) public
        onlyOwner
        returns (bool)
    {
        require(_value <= balances[_from], "StableCoin.freezeWithPurposeCode: Insufficient fund");

        balances[_from] = balances[_from].sub(_value);
        freezeOf[_from] = freezeOf[_from].add(_value);
        freezes[_from][_purposeCode] = freezes[_from][_purposeCode].add(_value);
        emit Freeze(_from, _value);
        emit FreezeWithPurpose(_from, _value, _purposeCode);
        return true;
    }


/**
 	 * @notice Get the names of all PixelCons from index `(_startIndex)` to `(_endIndex)`
 	 * @dev This function is for web3 calls only, as it returns a dynamic array
 	 * @return The names of the PixelCons in the given range
 	 */

function getNamesInRange(uint64 _startIndex, uint64 _endIndex) public view returns(bytes8[])
	{
		require(_startIndex <= totalSupply(), "Start index is out of bounds");
		require(_endIndex <= totalSupply(), "End index is out of bounds");
		require(_startIndex <= _endIndex, "End index is less than the start index");

		uint64 length = _endIndex - _startIndex;
		bytes8[] memory names = new bytes8[](length);
		for (uint i = 0; i < length; i++)	{
			names[i] = pixelconNames[_startIndex + i];
		}
		return names;
	}