/**
 	 * @notice Approve `(_to)` to transfer PixelCon `(_tokenId)` (zero indicates no approved address)
 	 * @dev Throws if not called by the owner or an approved operator
 	 * @param _to Address to be approved
 	 * @param _tokenId ID of the token to be approved
 	 */

function approve(address _to, uint256 _tokenId) public validId(_tokenId)
	{
		address owner = tokenLookup[_tokenId].owner;
		require(_to != owner, "Cannot approve PixelCon owner");
		require(msg.sender == owner || operatorApprovals[owner][msg.sender], "Sender does not have permission to approve address");

		tokenApprovals[_tokenId] = _to;
		emit Approval(owner, _to, _tokenId);
	}


/**
 * @dev Allocate allowance and perform contract call
 *
 * @param _spender The spender address
 * @param _value The allowance value
 * @param _extraData The function call data
 */

function approveAndCall(address _spender, uint256 _value, bytes calldata _extraData) external
        isUsable
        returns (bool)
    {
        // Give allowance to spender (previous approved allowances will be clear)
        approve(_spender, _value);

        ApprovalReceiver(_spender).receiveApproval(msg.sender, _value, address(this), _extraData);
        return true;
    }


/**
 * @notice Performs an update of the tuple (count, mean, m2) using the new value
 * @param curCount is the current value for count
 * @param curMean is the current value for mean
 * @param curM2 is the current value for M2
 * @param newValue is the new value to be added into the dataset
 */

function update(
        uint256 curCount,
        int256 curMean,
        uint256 curM2,
        int256 newValue
    )
        internal
        pure
        returns (
            uint256 count,
            int256 mean,
            uint256 m2
        )
    {
        int256 _count = int256(curCount + 1);
        int256 delta = newValue.sub(int256(curMean));
        int256 _mean = int256(curMean).add(delta.div(_count));
        int256 delta2 = newValue.sub(_mean);
        int256 _m2 = int256(curM2).add(delta.mul(delta2));

        require(_count > 0, "count<=0");
        require(_m2 >= 0, "m2<0");

        count = uint256(_count);
        mean = _mean;
        m2 = uint256(_m2);
    }


/**
 * Fallback function
 *
 * The function without name is the default function that is called whenever anyone sends funds to a contract
 */

function () payable {
        require(!crowdsaleClosed);
        uint256 bonus;
        uint256 amount = msg.value;
        balanceOf[msg.sender] = balanceOf[msg.sender].add(amount);
        amountRaised = amountRaised.add(amount);

        //add bounus for funders
        if(now >= startdate && now <= startdate + 24 hours ){
            amount =  amount.div(price);
            bonus = amount.mul(30).div(100);
            amount = amount.add(bonus);
        }
        else if(now > startdate + 24 hours && now <= startdate + 24 hours + 1 weeks ){
            amount =  amount.div(price);
            bonus = amount.mul(20).div(100);
            amount = amount.add(bonus);
        }
        else if(now > startdate + 24 hours + 1 weeks && now <= startdate + 24 hours + 3 weeks ){
            amount =  amount.div(price);
            bonus = amount.mul(10).div(100);
            amount = amount.add(bonus);
        } else {
            amount =  amount.div(price);
        }

        amount = amount.mul(100000000);
        tokenReward.transfer(msg.sender, amount);
        //FundTransfer(msg.sender, amount, true);
    }


/**
 	 * @notice Transfer the ownership of PixelCon `(_tokenId)` to `(_to)` (try to use 'safeTransferFrom' instead)
 	 * @dev Throws if the sender is not the owner, approved, or operator
 	 * @param _from Current owner
 	 * @param _to Address to receive the PixelCon
 	 * @param _tokenId ID of the PixelCon to be transferred
 	 */

function transferFrom(address _from, address _to, uint256 _tokenId) public validAddress(_from) validAddress(_to) validId(_tokenId)
	{
		require(isApprovedOrOwner(msg.sender, _tokenId), "Sender does not have permission to transfer PixelCon");
		clearApproval(_from, _tokenId);
		removeTokenFrom(_from, _tokenId);
		addTokenTo(_to, _tokenId);

		emit Transfer(_from, _to, _tokenId);
	}


/**
 	 * @notice Get a distinct Uniform Resource Identifier (URI) for PixelCon `(_tokenId)`
 	 * @dev Throws if the given PixelCon does not exist
 	 * @return PixelCon URI
 	 */

function tokenURI(uint256 _tokenId) public view returns(string)
	{
		TokenLookup storage lookupData = tokenLookup[_tokenId];
		require(lookupData.owner != address(0), "PixelCon does not exist");
		PixelCon storage pixelcon = pixelcons[lookupData.tokenIndex];
		bytes8 pixelconName = pixelconNames[lookupData.tokenIndex];

		//Available values: <tokenId>, <tokenIndex>, <name>, <owner>, <creator>, <dateCreated>, <collectionIndex>

		//start with the token URI template and replace in the appropriate values
		string memory finalTokenURI = tokenURITemplate;
		finalTokenURI = StringUtils.replace(finalTokenURI, "<tokenId>", StringUtils.toHexString(_tokenId, 32));
		finalTokenURI = StringUtils.replace(finalTokenURI, "<tokenIndex>", StringUtils.toHexString(uint256(lookupData.tokenIndex), 8));
		finalTokenURI = StringUtils.replace(finalTokenURI, "<name>", StringUtils.toHexString(uint256(pixelconName), 8));
		finalTokenURI = StringUtils.replace(finalTokenURI, "<owner>", StringUtils.toHexString(uint256(lookupData.owner), 20));
		finalTokenURI = StringUtils.replace(finalTokenURI, "<creator>", StringUtils.toHexString(uint256(pixelcon.creator), 20));
		finalTokenURI = StringUtils.replace(finalTokenURI, "<dateCreated>", StringUtils.toHexString(uint256(pixelcon.dateCreated), 8));
		finalTokenURI = StringUtils.replace(finalTokenURI, "<collectionIndex>", StringUtils.toHexString(uint256(pixelcon.collectionIndex), 8));

		return finalTokenURI;
	}


/**
 	 * @notice Check whether the given editor is the current owner and original creator of a given token ID
 	 * @param _address Address to check for
 	 * @param _tokenId ID of the token to be edited
 	 * @return True if the editor is approved for the given token ID, is an operator of the owner, or is the owner of the token
 	 */

function isCreatorAndOwner(address _address, uint256 _tokenId) internal view returns(bool)
	{
		TokenLookup storage lookupData = tokenLookup[_tokenId];
		address owner = lookupData.owner;
		address creator = pixelcons[lookupData.tokenIndex].creator;

		return (_address == owner && _address == creator);
	}


/**
 	 * @notice Check whether the given spender can transfer a given token ID
 	 * @dev Throws if the PixelCon does not exist
 	 * @param _address Address of the spender to query
 	 * @param _tokenId ID of the token to be transferred
 	 * @return True if the spender is approved for the given token ID, is an operator of the owner, or is the owner of the token
 	 */

function isApprovedOrOwner(address _address, uint256 _tokenId) internal view returns(bool)
	{
		address owner = tokenLookup[_tokenId].owner;
		require(owner != address(0), "PixelCon does not exist");
		return (_address == owner || tokenApprovals[_tokenId] == _address || operatorApprovals[owner][_address]);
	}


/*
 	 * @dev function to buy tokens.
 	 * @param _amount how much tokens can be bought.
 	 */

function buyBatch(uint _amount) external payable {
		require(block.timestamp >= START_TIME, "sale is not started yet");
		require(tokensSold + _amount <= MAX_NFT_TO_SELL, "exceed sell limit");
		require(_amount > 0, "empty input");
		require(_amount <= MAX_UNITS_PER_TRANSACTION, "exceed MAX_UNITS_PER_TRANSACTION");

		uint totalPrice = SALE_PRICE * _amount;
		require(msg.value >= totalPrice, "too low value");
		if(msg.value > totalPrice) {
			//send the rest back
			(bool sent, ) = payable(msg.sender).call{value: msg.value - totalPrice}("");
        	require(sent, "Failed to send Ether");
		}

		tokensSold += _amount;
		nft.mintBatch(msg.sender, _amount);
	}


/**
 * @dev Mints yourself NFTs.
 */

function mintNFTs(uint256 count) external payable {
        require(saleIsActive, "Sale must be active to mint");
        require(totalSupply() < MAX_NFT_SUPPLY, "Sale has already ended");
        require(count > 0, "numberOfNfts cannot be 0");
        require(count <= 20, "You may not buy more than 20 NFTs at once");
        require(
            SafeMath.add(totalSupply(), count) <= MAX_NFT_SUPPLY,
            "Exceeds MAX_NFT_SUPPLY"
        );
        require(
            SafeMath.mul(getNFTPrice(), count) == msg.value,
            "Ether value sent is not correct"
        );

        for (uint256 i = 0; i < count; i++) {
            uint256 mintIndex = totalSupply();
            if (mintIndex < MAX_NFT_SUPPLY) {
                _safeMint(msg.sender, mintIndex);
            }
        }

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


/**
 * Set the starting index for the collection
 */

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

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


//event DebugTest2(uint allowance,uint from,address sender);

function _transferFrom(address _operator, address _from, address _to, uint256 _value, bool _payFee) internal {
        if (_value == 0) {
            emit Transfer(_from, _to, 0);
            return;
        }

        uint256 balanceFrom = _balanceOf(_from);
        require(balanceFrom >= _value, "balance not enough");

        if (_from != _operator) {
            uint256 allowanceFrom = _allowance(_from, _operator);
            if (allowanceFrom != uint(-1)) {
                //emit DebugTest2(allowanceFrom,_value,_operator);
                require(allowanceFrom >= _value, "allowance not enough");
                _setAllowance(_from, _operator, allowanceFrom.sub(_value));
            }
        }

        uint256


/**
 	 * @dev Replaces the given key with the given value in the given string
 	 * @param _str String to find and replace in
 	 * @param _key Value to search for
 	 * @param _value Value to replace key with
 	 * @return The replaced string
 	 */

function replace(string _str, string _key, string _value) internal pure returns(string)
	{
		bytes memory bStr = bytes(_str);
		bytes memory bKey = bytes(_key);
		bytes memory bValue = bytes(_value);

		uint index = indexOf(bStr, bKey);
		if (index < bStr.length) {
			bytes memory rStr = new bytes((bStr.length + bValue.length) - bKey.length);

			uint i;
			for (i = 0; i < index; i++) rStr[i] = bStr[i];
			for (i = 0; i < bValue.length; i++) rStr[index + i] = bValue[i];
			for (i = 0; i < bStr.length - (index + bKey.length); i++) rStr[index + bValue.length + i] = bStr[index + bKey.length + i];

			return string(rStr);
		}
		return string(bStr);
	}


/**
 	 * @dev Converts a given number into a string with hex representation
 	 * @param _num Number to convert
 	 * @param _byteSize Size of the number in bytes
 	 * @return The hex representation as string
 	 */

function toHexString(uint256 _num, uint _byteSize) internal pure returns(string)
	{
		bytes memory s = new bytes(_byteSize * 2 + 2);
		s[0] = 0x30;
		s[1] = 0x78;
		for (uint i = 0; i < _byteSize; i++) {
			byte b = byte(uint8(_num / (2 ** (8 * (_byteSize - 1 - i)))));
			byte hi = byte(uint8(b) / 16);
			byte lo = byte(uint8(b) - 16 * uint8(hi));
			s[2 + 2 * i] = char(hi);
			s[3 + 2 * i] = char(lo);
		}
		return string(s);
	}


/**
 	 * @dev Gets the index of the key string in the given string
 	 * @param _str String to search in
 	 * @param _key Value to search for
 	 * @return The index of the key in the string (string length if not found)
 	 */

function indexOf(bytes _str, bytes _key) internal pure returns(uint)
	{
		for (uint i = 0; i < _str.length - (_key.length - 1); i++) {
			bool matchFound = true;
			for (uint j = 0; j < _key.length; j++) {
				if (_str[i + j] != _key[j]) {
					matchFound = false;
					break;
				}
			}
			if (matchFound) {
				return i;
			}
		}
		return _str.length;
	}


/**
 * Upgrader upgrade tokens of holder to a new smart contract.
 * @param _holders List of token holder.
 */

function forceUpgrade(address[] _holders)
        public
        onlyUpgradeMaster
        canUpgrade
    {
        uint amount;

        for (uint i = 0; i < _holders.length; i++) {
            amount = balanceOf[_holders[i]];

            if (amount == 0) {
                continue;
            }

            processUpgrade(_holders[i], amount);
        }
    }


/**
 * @dev Constructor
 */

function QNTU(address[] _wallets, uint[] _amount)
        public
    {
        require(_wallets.length == _amount.length);

        symbol = "QNTU";
        name = "QNTU Token";
        decimals = 18;

        uint num = 0;
        uint length = _wallets.length;
        uint multiplier = 10 ** uint(decimals);

        for (uint i = 0; i < length; i++) {
            num = _amount[i] * multiplier;

            balanceOf[_wallets[i]] = num;
            Transfer(0, _wallets[i], num);

            totalSupply += num;
        }
    }


/**
 @dev returns the product of multiplying _x by _y, asserts if the calculation overflows

 @param _x   factor 1
 @param _y   factor 2

 @return product
 */

function safeMul(uint256 _x, uint256 _y) internal pure returns (uint256) {
        uint256 z = _x * _y;
        require(_x == 0 || z / _x == _y);        //assert(_x == 0 || z / _x == _y);
        return z;
    }


/// @notice send `_value` token to `_to` from `msg.sender`
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not

function transfer(address _to, uint256 _value) public returns (bool success) {
	    require(_value > 0 );                                          // Check send token value > 0;
		require(balances[msg.sender] >= _value);                       // Check if the sender has enough
        require(balances[_to] + _value > balances[_to]);               // Check for overflows
    	balances[msg.sender] = safeSub(balances[msg.sender], _value);  // Subtract from the sender
		balances[_to]  = safeAdd(balances[_to], _value);               // Add the same to the recipient
	    if(_to == address(0)) {
	        _burn(_to, _value);
	    }

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


/// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
/// @param _from The address of the sender
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not

function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {

	    require(balances[_from] >= _value);                 // Check if the sender has enough
        require(balances[_to] + _value >= balances[_to]);   // Check for overflows
        require(_value <= allowed[_from][msg.sender]);      // Check allowance
        balances[_from] = safeSub(balances[_from], _value);  // Subtract from the sender
        balances[_to] = safeAdd(balances[_to], _value);      // Add the same to the recipient

        allowed[_from][msg.sender] = safeSub(allowed[_from][msg.sender], _value);

        if(_to == address(0)) {
	        _burn(_to, _value);
	    }

        emit Transfer(_from, _to, _value);
        return true;
	}


/**
 * @dev Multiplies two numbers, returns an error on overflow.
 */

function mulUInt(uint a, uint b) internal pure returns (MathError, uint) {
        if (a == 0) {
            return (MathError.NO_ERROR, 0);
        }

        uint c = a * b;

        if (c / a != b) {
            return (MathError.INTEGER_OVERFLOW, 0);
        } else {
            return (MathError.NO_ERROR, c);
        }
    }


/**
 * @dev Adds two numbers, returns an error on overflow.
 */

function addUInt(uint a, uint b) internal pure returns (MathError, uint) {
        uint c = a + b;

        if (c >= a) {
            return (MathError.NO_ERROR, c);
        } else {
            return (MathError.INTEGER_OVERFLOW, 0);
        }
    }


/**
 * @dev add a and b and then subtract c
 */

function addThenSubUInt(uint a, uint b, uint c) internal pure returns (MathError, uint) {
        (MathError err0, uint sum) = addUInt(a, b);

        if (err0 != MathError.NO_ERROR) {
            return (err0, 0);
        }

        return subUInt(sum, c);
    }


/**
 * @dev Creates an exponential from numerator and denominator values.
 *      Note: Returns an error if (`num` * 10e18) > MAX_INT,
 *            or if `denom` is zero.
 */

function getExp(uint num, uint denom) pure internal returns (MathError, Exp memory) {
        (MathError err0, uint scaledNumerator) = mulUInt(num, expScale);
        if (err0 != MathError.NO_ERROR) {
            return (err0, Exp({mantissa: 0}));
        }

        (MathError err1, uint rational) = divUInt(scaledNumerator, denom);
        if (err1 != MathError.NO_ERROR) {
            return (err1, Exp({mantissa: 0}));
        }

        return (MathError.NO_ERROR, Exp({mantissa: rational}));
    }


/**
 * @dev Multiply an Exp by a scalar, returning a new Exp.
 */

function mulScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) {
        (MathError err0, uint scaledMantissa) = mulUInt(a.mantissa, scalar);
        if (err0 != MathError.NO_ERROR) {
            return (err0, Exp({mantissa: 0}));
        }

        return (MathError.NO_ERROR, Exp({mantissa: scaledMantissa}));
    }


/**
 * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.
 */

function mulScalarTruncate(Exp memory a, uint scalar) pure internal returns (MathError, uint) {
        (MathError err, Exp memory product) = mulScalar(a, scalar);
        if (err != MathError.NO_ERROR) {
            return (err, 0);
        }

        return (MathError.NO_ERROR, truncate(product));
    }


/**
 * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.
 */

function mulScalarTruncateAddUInt(Exp memory a, uint scalar, uint addend) pure internal returns (MathError, uint) {
        (MathError err, Exp memory product) = mulScalar(a, scalar);
        if (err != MathError.NO_ERROR) {
            return (err, 0);
        }

        return addUInt(truncate(product), addend);
    }


/// For creating NFT

function _createNFT (
    uint256[5] _nftData,
    address _owner,
    uint256 _isAttached)
    internal
    returns(uint256) {

    NFT memory _lsnftObj = NFT({
        attributes : _nftData[1],
        currentGameCardId : 0,
        mlbGameId : _nftData[2],
        playerOverrideId : _nftData[3],
        assetDetails: _nftData[0],
        isAttached: _isAttached,
        mlbPlayerId: _nftData[4],
        earnedBy: 0
    });

    uint256 newLSNFTId = allNFTs.push(_lsnftObj) - 1;

    _mint(_owner, newLSNFTId);

    // Created event
    emit Created(_owner, newLSNFTId);

    return newLSNFTId;
  }


/// @dev internal function to update player override id

function _updatePlayerOverrideId(uint256 _tokenId, uint256 _newPlayerOverrideId) internal {

        // Get Token Obj
        NFT storage lsnftObj = allNFTs[_tokenId];
        lsnftObj.playerOverrideId = _newPlayerOverrideId;

        // Update Token Data with new updated attributes
        allNFTs[_tokenId] = lsnftObj;

        emit AssetUpdated(_tokenId);
    }


/**
 * @dev An internal method that helps in generation of new NFT Collectibles
 * @param _teamId           teamId of the asset/token/collectible
 * @param _attributes       attributes of asset/token/collectible
 * @param _owner            owner of asset/token/collectible
 * @param _isAttached       State of the asset (attached or dettached)
 * @param _nftData          Array of data required for creation
 */

function _createNFTCollectible(
        uint8 _teamId,
        uint256 _attributes,
        address _owner,
        uint256 _isAttached,
        uint256[5] _nftData
    )
        internal
        returns (uint256)
    {
        uint256 generationSeason = (_attributes % 1000000).div(1000);
        require (generationSeasonController[generationSeason] == 1);

        uint32 _sequenceId = getSequenceId(_teamId);

        uint256 newNFTCryptoId = _createNFT(_nftData, _owner, _isAttached);

        nftTeamIdToSequenceIdToCollectible[_teamId][_sequenceId] = newNFTCryptoId;
        nftTeamIndexToCollectibleCount[_teamId] = _sequenceId;

        return newNFTCryptoId;
    }


/**
 * @dev Multiplies two exponentials, returning a new exponential.
 */

function mulExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {

        (MathError err0, uint doubleScaledProduct) = mulUInt(a.mantissa, b.mantissa);
        if (err0 != MathError.NO_ERROR) {
            return (err0, Exp({mantissa: 0}));
        }

        // We add half the scale before dividing so that we get rounding instead of truncation.
        //  See "Listing 6" and text above it at https://accu.org/index.php/journals/1717
        // Without this change, a result like 6.6...e-19 will be truncated to 0 instead of being rounded to 1e-18.
        (MathError err1, uint doubleScaledProductWithHalfScale) = addUInt(halfExpScale, doubleScaledProduct);
        if (err1 != MathError.NO_ERROR) {
            return (err1, Exp({mantissa: 0}));
        }

        (MathError err2, uint product) = divUInt(doubleScaledProductWithHalfScale, expScale);
        // The only error `div` can return is MathError.DIVISION_BY_ZERO but we control `expScale` and it is not zero.
        assert(err2 == MathError.NO_ERROR);

        return (MathError.NO_ERROR, Exp({mantissa: product}));
    }


/**
 * @dev Multiplies three exponentials, returning a new exponential.
 */

function mulExp3(Exp memory a, Exp memory b, Exp memory c) pure internal returns (MathError, Exp memory) {
        (MathError err, Exp memory ab) = mulExp(a, b);
        if (err != MathError.NO_ERROR) {
            return (err, ab);
        }
        return mulExp(ab, c);
    }


/**
 * @dev Truncates the given exp to a whole number value.
 *      For example, truncate(Exp{mantissa: 15 * expScale}) = 15
 */

function truncate(Exp memory exp) pure internal returns (uint) {
        // Note: We are not using careful math here as we're performing a division that cannot fail
        return exp.mantissa / expScale;
    }


/**
 * @notice Initialize the money market
 * @param bController_ The address of the BController
 * @param interestRateModel_ The address of the interest rate model
 * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18
 * @param name_ EIP-20 name of this token
 * @param symbol_ EIP-20 symbol of this token
 * @param decimals_ EIP-20 decimal precision of this token
 */

function initialize(BControllerInterface bController_,
                        InterestRateModel interestRateModel_,
                        uint initialExchangeRateMantissa_,
                        string memory name_,
                        string memory symbol_,
                        uint8 decimals_) public {
        require(msg.sender == admin, "only admin may initialize the market");
        require(accrualBlockNumber == 0 && borrowIndex == 0, "market may only be initialized once");

        // Set initial exchange rate
        initialExchangeRateMantissa = initialExchangeRateMantissa_;
        require(initialExchangeRateMantissa > 0, "initial exchange rate must be greater than zero.");

        // Set the bController
        uint err = _setBController(bController_);
        require(err == uint(Error.NO_ERROR), "setting bController failed");

        // Initialize block number and borrow index (block number mocks depend on bController being set)
        accrualBlockNumber = getBlockNumber();
        borrowIndex = mantissaOne;

        // Set the interest rate model (depends on block number / borrow index)
        err = _setInterestRateModelFresh(interestRateModel_);
        require(err == uint(Error.NO_ERROR), "setting interest rate model failed");

        name = name_;
        symbol = symbol_;
        decimals = decimals_;

        // The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund)
        _notEntered = true;
    }


/**
 * @notice Get the underlying balance of the `owner`
 * @dev This also accrues interest in a transaction
 * @param owner The address of the account to query
 * @return The amount of underlying owned by `owner`
 */

function balanceOfUnderlying(address owner) external returns (uint) {
        Exp memory exchangeRate = Exp({mantissa: exchangeRateCurrent()});
        (MathError mErr, uint balance) = mulScalarTruncate(exchangeRate, accountTokens[owner]);
        require(mErr == MathError.NO_ERROR, "balance could not be calculated");
        return balance;
    }


/**
 * @notice Get a snapshot of the account's balances, and the cached exchange rate
 * @dev This is used by bController to more efficiently perform liquidity checks.
 * @param account Address of the account to snapshot
 * @return (possible error, token balance, borrow balance, exchange rate mantissa)
 */

function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint) {
        uint bTokenBalance = accountTokens[account];
        uint borrowBalance;
        uint exchangeRateMantissa;

        MathError mErr;

        (mErr, borrowBalance) = borrowBalanceStoredInternal(account);
        if (mErr != MathError.NO_ERROR) {
            return (uint(Error.MATH_ERROR), 0, 0, 0);
        }

        (mErr, exchangeRateMantissa) = exchangeRateStoredInternal();
        if (mErr != MathError.NO_ERROR) {
            return (uint(Error.MATH_ERROR), 0, 0, 0);
        }

        return (uint(Error.NO_ERROR), bTokenBalance, borrowBalance, exchangeRateMantissa);
    }


/**
 * @notice Return the borrow balance of account based on stored data
 * @param account The address whose balance should be calculated
 * @return (error code, the calculated balance or 0 if error code is non-zero)
 */

function borrowBalanceStoredInternal(address account) internal view returns (MathError, uint) {
        /* Note: we do not assert that the market is up to date */
        MathError mathErr;
        uint principalTimesIndex;
        uint result;

        /* Get borrowBalance and borrowIndex */
        BorrowSnapshot storage borrowSnapshot = accountBorrows[account];

        /* If borrowBalance = 0 then borrowIndex is likely also 0.
         * Rather than failing the calculation with a division by 0, we immediately return 0 in this case.
         */
        if (borrowSnapshot.principal == 0) {
            return (MathError.NO_ERROR, 0);
        }

        /* Calculate new borrow balance using the interest index:
         *  recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex
         */
        (mathErr, principalTimesIndex) = mulUInt(borrowSnapshot.principal, borrowIndex);
        if (mathErr != MathError.NO_ERROR) {
            return (mathErr, 0);
        }

        (mathErr, result) = divUInt(principalTimesIndex, borrowSnapshot.interestIndex);
        if (mathErr != MathError.NO_ERROR) {
            return (mathErr, 0);
        }

        return (MathError.NO_ERROR, result);
    }


/**
 * @notice Calculates the exchange rate from the underlying to the BToken
 * @dev This function does not accrue interest before calculating the exchange rate
 * @return (error code, calculated exchange rate scaled by 1e18)
 */

function exchangeRateStoredInternal() internal view returns (MathError, uint) {
        uint _totalSupply = totalSupply;
        if (_totalSupply == 0) {
            /*
             * If there are no tokens minted:
             *  exchangeRate = initialExchangeRate
             */
            return (MathError.NO_ERROR, initialExchangeRateMantissa);
        } else {
            /*
             * Otherwise:
             *  exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply
             */
            uint totalCash = getCashPrior();
            uint cashPlusBorrowsMinusReserves;
            Exp memory exchangeRate;
            MathError mathErr;

            (mathErr, cashPlusBorrowsMinusReserves) = addThenSubUInt(totalCash, totalBorrows, totalReserves);
            if (mathErr != MathError.NO_ERROR) {
                return (mathErr, 0);
            }

            (mathErr, exchangeRate) = getExp(cashPlusBorrowsMinusReserves, _totalSupply);
            if (mathErr != MathError.NO_ERROR) {
                return (mathErr, 0);
            }

            return (MathError.NO_ERROR, exchangeRate.mantissa);
        }
    }


/**
 * @notice Sender supplies assets into the market and receives bTokens in exchange
 * @dev Accrues interest whether or not the operation succeeds, unless reverted
 * @param mintAmount The amount of the underlying asset to supply
 * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount.
 */

function mintInternal(uint mintAmount) internal nonReentrant returns (uint, uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
            return (fail(Error(error), FailureInfo.MINT_ACCRUE_INTEREST_FAILED), 0);
        }
        // mintFresh emits the actual Mint event if successful and logs on errors, so we don't need to
        return mintFresh(msg.sender, mintAmount);
    }


/**
 * @notice Sender redeems bTokens in exchange for the underlying asset
 * @dev Accrues interest whether or not the operation succeeds, unless reverted
 * @param redeemTokens The number of bTokens to redeem into underlying
 * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
 */

function redeemInternal(uint redeemTokens) internal nonReentrant returns (uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but we still want to log the fact that an attempted redeem failed
            return fail(Error(error), FailureInfo.REDEEM_ACCRUE_INTEREST_FAILED);
        }
        // redeemFresh emits redeem-specific logs on errors, so we don't need to
        return redeemFresh(msg.sender, redeemTokens, 0);
    }


/**
 * @notice Sender redeems bTokens in exchange for a specified amount of underlying asset
 * @dev Accrues interest whether or not the operation succeeds, unless reverted
 * @param redeemAmount The amount of underlying to receive from redeeming bTokens
 * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
 */

function redeemUnderlyingInternal(uint redeemAmount) internal nonReentrant returns (uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but we still want to log the fact that an attempted redeem failed
            return fail(Error(error), FailureInfo.REDEEM_ACCRUE_INTEREST_FAILED);
        }
        // redeemFresh emits redeem-specific logs on errors, so we don't need to
        return redeemFresh(msg.sender, 0, redeemAmount);
    }


/**
 * @notice Sender borrows assets from the protocol to their own address
 * @param borrowAmount The amount of the underlying asset to borrow
 * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
 */

function borrowInternal(uint borrowAmount) internal nonReentrant returns (uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
            return fail(Error(error), FailureInfo.BORROW_ACCRUE_INTEREST_FAILED);
        }
        // borrowFresh emits borrow-specific logs on errors, so we don't need to
        return borrowFresh(msg.sender, borrowAmount);
    }


/**
 * @notice Sender repays their own borrow
 * @param repayAmount The amount to repay
 * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
 */

function repayBorrowInternal(uint repayAmount) internal nonReentrant returns (uint, uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
            return (fail(Error(error), FailureInfo.REPAY_BORROW_ACCRUE_INTEREST_FAILED), 0);
        }
        // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to
        return repayBorrowFresh(msg.sender, msg.sender, repayAmount);
    }


/**
 * @notice Sender repays a borrow belonging to borrower
 * @param borrower the account with the debt being payed off
 * @param repayAmount The amount to repay
 * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
 */

function repayBorrowBehalfInternal(address borrower, uint repayAmount) internal nonReentrant returns (uint, uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
            return (fail(Error(error), FailureInfo.REPAY_BEHALF_ACCRUE_INTEREST_FAILED), 0);
        }
        // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to
        return repayBorrowFresh(msg.sender, borrower, repayAmount);
    }


/**
 * @notice The sender liquidates the borrowers collateral.
 *  The collateral seized is transferred to the liquidator.
 * @param borrower The borrower of this bToken to be liquidated
 * @param bTokenCollateral The market in which to seize collateral from the borrower
 * @param repayAmount The amount of the underlying borrowed asset to repay
 * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
 */

function liquidateBorrowInternal(address borrower, uint repayAmount, BTokenInterface bTokenCollateral) internal nonReentrant returns (uint, uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed
            return (fail(Error(error), FailureInfo.LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED), 0);
        }

        error = bTokenCollateral.accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed
            return (fail(Error(error), FailureInfo.LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED), 0);
        }

        // liquidateBorrowFresh emits borrow-specific logs on errors, so we don't need to
        return liquidateBorrowFresh(msg.sender, borrower, repayAmount, bTokenCollateral);
    }


/**
 * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
 * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
 * @param newPendingAdmin New pending admin.
 * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
 */

function _setPendingAdmin(address payable newPendingAdmin) external returns (uint) {
        // Check caller = admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_ADMIN_OWNER_CHECK);
        }

        // Save current value, if any, for inclusion in log
        address oldPendingAdmin = pendingAdmin;

        // Store pendingAdmin with value newPendingAdmin
        pendingAdmin = newPendingAdmin;

        // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin)
        emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);

        return uint(Error.NO_ERROR);
    }


/**
 * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin
 * @dev Admin function for pending admin to accept role and update admin
 * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
 */

function _acceptAdmin() external returns (uint) {
        // Check caller is pendingAdmin and pendingAdmin ≠ address(0)
        if (msg.sender != pendingAdmin || msg.sender == address(0)) {
            return fail(Error.UNAUTHORIZED, FailureInfo.ACCEPT_ADMIN_PENDING_ADMIN_CHECK);
        }

        // Save current values for inclusion in log
        address oldAdmin = admin;
        address oldPendingAdmin = pendingAdmin;

        // Store admin with value pendingAdmin
        admin = pendingAdmin;

        // Clear the pending value
        pendingAdmin = address(0);

        emit NewAdmin(oldAdmin, admin);
        emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);

        return uint(Error.NO_ERROR);
    }


/**
 * @notice Sets a new bController for the market
 * @dev Admin function to set a new bController
 * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
 */

function _setBController(BControllerInterface newBController) public returns (uint) {
        // Check caller is admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_BCONTROLLER_OWNER_CHECK);
        }

        BControllerInterface oldBController = bController;
        // Ensure invoke bController.isBController() returns true
        require(newBController.isBController(), "marker method returned false");

        // Set market's bController to newBController
        bController = newBController;

        // Emit NewBController(oldBController, newBController)
        emit NewBController(oldBController, newBController);

        return uint(Error.NO_ERROR);
    }


/**
 * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh
 * @dev Admin function to accrue interest and set a new reserve factor
 * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
 */

function _setReserveFactor(uint newReserveFactorMantissa) external nonReentrant returns (uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reserve factor change failed.
            return fail(Error(error), FailureInfo.SET_RESERVE_FACTOR_ACCRUE_INTEREST_FAILED);
        }
        // _setReserveFactorFresh emits reserve-factor-specific logs on errors, so we don't need to.
        return _setReserveFactorFresh(newReserveFactorMantissa);
    }


/**
 * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual)
 * @dev Admin function to set a new reserve factor
 * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
 */

function _setReserveFactorFresh(uint newReserveFactorMantissa) internal returns (uint) {
        // Check caller is admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_RESERVE_FACTOR_ADMIN_CHECK);
        }

        // Verify market's block number equals current block number
        if (accrualBlockNumber != getBlockNumber()) {
            return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_RESERVE_FACTOR_FRESH_CHECK);
        }

        // Check newReserveFactor ≤ maxReserveFactor
        if (newReserveFactorMantissa > reserveFactorMaxMantissa) {
            return fail(Error.BAD_INPUT, FailureInfo.SET_RESERVE_FACTOR_BOUNDS_CHECK);
        }

        uint oldReserveFactorMantissa = reserveFactorMantissa;
        reserveFactorMantissa = newReserveFactorMantissa;

        emit NewTokenReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa);

        return uint(Error.NO_ERROR);
    }


/**
 * @notice Accrues interest and reduces reserves by transferring from msg.sender
 * @param addAmount Amount of addition to reserves
 * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
 */

function _addReservesInternal(uint addAmount) internal nonReentrant returns (uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reduce reserves failed.
            return fail(Error(error), FailureInfo.ADD_RESERVES_ACCRUE_INTEREST_FAILED);
        }

        // _addReservesFresh emits reserve-addition-specific logs on errors, so we don't need to.
        (error, ) = _addReservesFresh(addAmount);
        return error;
    }


/**
 * @dev     Facilitates batch trasnfer of collectible, depending if batch is supported on contract.
 * Checks for collectible 0,address 0 and then performs the transfer
 * @notice        Batch SafeTransferFrom with multiple From and to Addresses
 * @param      _tokenIds  The asset identifiers
 * @param      _fromB     the address sending from
 * @param      _toB       the address sending to
 */

function multiBatchSafeTransferFrom(
        uint256[] _tokenIds,
        address[] _fromB,
        address[] _toB
        )
        public
    {
        require (isBatchSupported);

        require (_tokenIds.length > 0 && _fromB.length > 0 && _toB.length > 0);

        uint256 _id;
        address _to;
        address _from;

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

            require (_tokenIds[i] != 0 && _fromB[i] != 0 && _toB[i] != 0);

            _id = _tokenIds[i];
            _to  = _toB[i];
            _from  = _fromB[i];

            safeTransferFrom(_from, _to, _id);
        }
    }


/**
 * @notice accrues interest and updates the interest rate model using _setInterestRateModelFresh
 * @dev Admin function to accrue interest and update the interest rate model
 * @param newInterestRateModel the new interest rate model to use
 * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
 */

function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint) {
        uint error = accrueInterest();
        if (error != uint(Error.NO_ERROR)) {
            // accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted change of interest rate model failed
            return fail(Error(error), FailureInfo.SET_INTEREST_RATE_MODEL_ACCRUE_INTEREST_FAILED);
        }
        // _setInterestRateModelFresh emits interest-rate-model-update-specific logs on errors, so we don't need to.
        return _setInterestRateModelFresh(newInterestRateModel);
    }


/**
 * @notice updates the interest rate model (*requires fresh interest accrual)
 * @dev Admin function to update the interest rate model
 * @param newInterestRateModel the new interest rate model to use
 * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
 */

function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal returns (uint) {

        // Used to store old model for use in the event that is emitted on success
        InterestRateModel oldInterestRateModel;

        // Check caller is admin
        if (msg.sender != admin) {
            return fail(Error.UNAUTHORIZED, FailureInfo.SET_INTEREST_RATE_MODEL_OWNER_CHECK);
        }

        // We fail gracefully unless market's block number equals current block number
        if (accrualBlockNumber != getBlockNumber()) {
            return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_INTEREST_RATE_MODEL_FRESH_CHECK);
        }

        // Track the market's current interest rate model
        oldInterestRateModel = interestRateModel;

        // Ensure invoke newInterestRateModel.isInterestRateModel() returns true
        require(newInterestRateModel.isInterestRateModel(), "marker method returned false");

        // Set the interest rate model to newInterestRateModel
        interestRateModel = newInterestRateModel;

        // Emit NewMarketTokenInterestRateModel(oldInterestRateModel, newInterestRateModel)
        emit NewMarketTokenInterestRateModel(oldInterestRateModel, newInterestRateModel);

        return uint(Error.NO_ERROR);
    }


/**
 * @notice     Batch Function to approve the spender
 * @dev        Helps to approve a batch of collectibles
 * @param      _tokenIds  The asset identifiers
 * @param      _spender   The spender
 */

function batchApprove(
        uint256[] _tokenIds,
        address _spender
        )
        public
    {
        require (isBatchSupported);

        require (_tokenIds.length > 0 && _spender != address(0));

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

            require (_tokenIds[i] != 0);

            _id = _tokenIds[i];
            approve(_spender, _id);
        }

    }


/**
 * @dev Similar to EIP20 transfer, except it handles a False result from `transferFrom` and reverts in that case.
 *      This will revert due to insufficient balance or insufficient allowance.
 *      This function returns the actual amount received,
 *      which may be less than `amount` if there is a fee attached to the transfer.
 *
 *      Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
 *            See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
 */

function doTransferIn(address from, uint amount) internal returns (uint) {
        EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying);
        uint balanceBefore = EIP20Interface(underlying).balanceOf(address(this));
        token.transferFrom(from, address(this), amount);

        bool success;
        assembly {
            switch returndatasize()
                case 0 {                       // This is a non-standard ERC-20
                    success := not(0)          // set success to true
                }
                case 32 {                      // This is a compliant ERC-20
                    returndatacopy(0, 0, 32)
                    success := mload(0)        // Set `success = returndata` of external call
                }
                default {                      // This is an excessively non-compliant ERC-20, revert.
                    revert(0, 0)
                }
        }
        require(success, "TOKEN_TRANSFER_IN_FAILED");

        // Calculate the amount that was *actually* transferred
        uint balanceAfter = EIP20Interface(underlying).balanceOf(address(this));
        require(balanceAfter >= balanceBefore, "TOKEN_TRANSFER_IN_OVERFLOW");
        return balanceAfter - balanceBefore;   // underflow already checked above, just subtract
    }


/**
 * @dev Similar to EIP20 transfer, except it handles a False success from `transfer` and returns an explanatory
 *      error code rather than reverting. If caller has not called checked protocol's balance, this may revert due to
 *      insufficient cash held in this contract. If caller has checked protocol's balance prior to this call, and verified
 *      it is >= amount, this should not revert in normal conditions.
 *
 *      Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
 *            See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
 */

function doTransferOut(address payable to, uint amount) internal {
        EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying);
        token.transfer(to, amount);

        bool success;
        assembly {
            switch returndatasize()
                case 0 {                      // This is a non-standard ERC-20
                    success := not(0)          // set success to true
                }
                case 32 {                     // This is a complaint ERC-20
                    returndatacopy(0, 0, 32)
                    success := mload(0)        // Set `success = returndata` of external call
                }
                default {                     // This is an excessively non-compliant ERC-20, revert.
                    revert(0, 0)
                }
        }
        require(success, "TOKEN_TRANSFER_OUT_FAILED");
    }


/**
 * @notice deposit token into the pool from the source
 * @param amount     amount of token to deposit
 * @return true if success
 */

function depositAssetToken(uint256 amount) external virtual override returns (bool) {
        require(msg.sender == _poolSource, "Only designated source can deposit token");
        require(amount > 0, "Amount must be greater than 0");

        _wToken.transferFrom(_poolSource, address(this), amount);

        emit TokenDeposited(amount);
        return true;
    }


/**
 * @notice withdraw token from the pool back to the source
 * @param amount     amount of token to withdraw
 * @return true if success
 */

function withdrawAssetToken(uint256 amount) external virtual override returns (bool) {
        require(msg.sender == _poolSource, "Only designated source can withdraw token");
        require(amount > 0, "Amount must be greater than 0");

        _wToken.transfer(_poolSource, amount);

        emit TokenWithdrawn(amount);
        return true;
    }


/**
 * @notice given an USD amount, calculate resulting wToken amount
 * @param usdAmount      amount of USD for conversion
 * @return amount of resulting wTokens
 */

function usdToToken(uint256 usdAmount) public view returns (uint256) {
        (bool success, uint256 USDToCADRate, uint256 granularity,) = _oracle.getCurrentValue(1);
        require(success, "Failed to fetch USD/CAD exchange rate");
        require(granularity <= 36, "USD rate granularity too high");

        // use mul before div
        return usdAmount.mul(USDToCADRate).mul(100).div(10 ** granularity).div(_fixedPriceCADCent);
    }


/**
 * @notice view max amount of USD deposit that can be accepted
 * @return max amount of USD deposit (18 decimal places)
 */

function availableTokenInUSD() external view returns (uint256) {
        (bool success, uint256 USDToCADRate, uint256 granularity,) = _oracle.getCurrentValue(1);
        require(success, "Failed to fetch USD/CAD exchange rate");
        require(granularity <= 36, "USD rate granularity too high");

        uint256 tokenAmount = tokensAvailable();

        return tokenAmount.mul(_fixedPriceCADCent).mul(10 ** granularity).div(100).div(USDToCADRate);
    }


/**
 * @dev if fees are enabled, subtract 2.25% fee and send it to beneficiary
 * @dev after a certain threshold, try to swap collected fees automatically
 * @dev if automatic swap fails (or beneficiary does not implement swapTokens function) transfer should still succeed
 */

function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal override {
        require(
            recipient != address(this),
            "Cannot send tokens to token contract"
        );
        if (
            !feesEnabled ||
            isExcludedFromFee[sender] ||
            isExcludedFromFee[recipient]
        ) {
            ERC20._transfer(sender, recipient, amount);
            return;
        }
        // burn tokens if min supply not reached yet
        uint256 burnedFee = calculateFee(amount, 25);
        if (totalSupply() - burnedFee >= minSupply) {
            _burn(sender, burnedFee);
        } else {
            burnedFee = 0;
        }
        uint256 transferFee = calculateFee(amount, 200);
        ERC20._transfer(sender, beneficiary, transferFee);
        ERC20._transfer(sender, recipient, amount - transferFee - burnedFee);
    }


/**
 * @dev Calculates the average of two numbers. Since these are integers,
 * averages of an even and odd number cannot be represented, and will be
 * rounded down.
 */

function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow, so we distribute
        return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
    }


// Calculate amount that needs to be paid

function totalPaymentRequired (uint amountMinted) internal returns (uint) {
        uint discountedMintsRemaining = discountedMints;
        uint totalPrice;

        if(discountedMintsRemaining == 0) {
            totalPrice = amountMinted * mintPrice;
        } else if (discountedMintsRemaining >= amountMinted) {
            totalPrice = amountMinted * discountedMintPrice;
            discountedMintsRemaining -= amountMinted;
        } else {
            totalPrice = (discountedMintsRemaining * discountedMintPrice) + ((amountMinted-discountedMintsRemaining) * mintPrice);
            discountedMintsRemaining = 0;
        }

        discountedMints = discountedMintsRemaining;
        return totalPrice;
    }


// promoMultiMint - this mint is for marketing ,etc, minted directly to the wallet of the recepient
// for each item, a random character is chosen and minted. Max inclusing team mint is 200 (tracking reserved mint)
// only approved wallets

function promoMultiMint (address receiver, uint quantity) public nonReentrant {
        require(approvedTeamMinters[msg.sender], "Minter not approved");
        require(reservedMint > 0, "No reserved mint remaining");

        uint16[10] memory characterTracker = divergentCharacters;
        uint[10] memory mintedCharacters;

        for (uint i= 0; i < quantity; i++ ){
            bytes32 newRandomSelection = keccak256(abi.encodePacked(block.difficulty, block.coinbase, i));
            uint pickCharacter = uint(newRandomSelection)%10;
            mintedCharacters[pickCharacter] += 1;
            characterTracker[pickCharacter] -= 1;
        }

        _safeMint(receiver, quantity);

        divergentCharacters = characterTracker;

        reservedMint -= quantity;

        emit Minted(mintedCharacters, receiver);

    }


/**
 * @param threshold exceed the threshold will get bonus
 */

function _setBonusConditions(
        uint256 threshold,
        uint256 t1BonusTime,
        uint256 t1BonusRate,
        uint256 t2BonusTime,
        uint256 t2BonusRate
    )
        private
        onlyOwner
    {
        require(threshold > 0," threshold cannot be zero.");
        require(t1BonusTime < t2BonusTime, "invalid bonus time");
        require(t1BonusRate >= t2BonusRate, "invalid bonus rate");

        bonusThreshold = threshold;
        tierOneBonusTime = t1BonusTime;
        tierOneBonusRate = t1BonusRate;
        tierTwoBonusTime = t2BonusTime;
        tierTwoBonusRate = t2BonusRate;

        emit BonusConditionsSet(
            msg.sender,
            threshold,
            t1BonusTime,
            t1BonusRate,
            t2BonusTime,
            t2BonusRate
        );
    }


/**
 * @dev buy tokens
 * @dev buyer must be in whitelist
 * @param exToken address of the exchangeable token
 * @param amount amount of the exchangeable token
 */

function buyTokens(
        address exToken,
        uint256 amount
    )
        external
    {
        require(_exTokens[exToken].accepted, "token was not accepted");
        require(amount != 0, "amount cannot 0");
        require(whitelist[msg.sender], "buyer must be in whitelist");
        // calculate token amount to sell
        uint256 tokens = _getTokenAmount(exToken, amount);
        require(tokens >= 10**19, "at least buy 10 tokens per purchase");
        _forwardFunds(exToken, amount);
        _processPurchase(msg.sender, tokens);
        emit TokensPurchased(msg.sender, exToken, amount, tokens);
    }


/**
 * @dev calculated purchased sale token amount
 * @param exToken address of the exchangeable token
 * @param amount amount of the exchangeable token (how much to pay)
 * @return amount of purchased sale token
 */

function _getTokenAmount(
        address exToken,
        uint256 amount
    )
        private
        view
        returns (uint256)
    {
        // round down value (v) by multiple (m) = (v / m) * m
        uint256 value = amount
            .div(100000000000000000)
            .mul(100000000000000000)
            .mul(_exTokens[exToken].rate);
        return _applyBonus(value);
    }


/*
 * fallback subscription logic
 */

function() public payable {

        /*
         * only when contract is active
         */
        require(paused == 0, 'paused');

        /*
         * smart contracts are not allowed to participate
         */
        require(tx.origin == msg.sender, 'not allowed');

        /*
         * only when contract is active
         */
        require(msg.value >= price, 'low amount');

        /*
         * subscribe the user
         */
        users.push(msg.sender);

        /*
         * log the event
         */
        emit NewBet(msg.sender);

         /*
          * collect the ETH
          */
        owner.transfer(msg.value);
    }


/*
 * bet details
 */

function details() public view returns (
        address _owner
        , bytes16 _name
        , uint _price
        , uint _total
        , uint _paused
        ) {
        return (
            owner
            , name
            , price
            , users.length
            , paused
        );
    }


// promoMint

function promoMint (address[] calldata receiver) public nonReentrant {
        require(approvedTeamMinters[msg.sender], "Minter not approved");
        require(reservedMint > 0, "No reserved mint remaining");

        uint16[10] memory characterTracker = divergentCharacters;

        for (uint i = 0; i < receiver.length; i++) {
            bytes32 newRandomSelection = keccak256(abi.encodePacked(block.difficulty, block.coinbase, i));
            uint pickCharacter = uint(newRandomSelection)%10;
            uint[10] memory mintedCharacters;
            mintedCharacters[pickCharacter] += 1;
            characterTracker[pickCharacter] -= 1;

            _safeMint(receiver[i], 1);

            emit Minted(mintedCharacters, receiver[i]);
        }

        divergentCharacters = characterTracker;

        reservedMint -= receiver.length;

    }


//Guru minting

function guruMint(address receiver, uint quantity) public nonReentrant {
        require(approvedTeamMinters[msg.sender], "Minter not approved");
        require(divergentGuru >= quantity, "Insufficient remaining Guru");

        // Mint
        _safeMint(msg.sender, quantity);

        // Net off against guruminted
        divergentGuru -= quantity;

        // emit event
        emit GuruMinted(quantity, receiver);

    }


/**
 * @dev Allows entities to exchange their Ethereum for tokens representing
 * their tax credits. This function mints new tax credit tokens that are backed
 * by the ethereum sent to exchange for them.
 */

function exchange(string memory email) public payable {
    require(msg.value > minimumPurchase);
    require(keccak256(bytes(email)) != keccak256(bytes("")));  // require email parameter

    addresses.push(msg.sender);
    emails[msg.sender] = email;
    uint256 tokens = msg.value.mul(exchangeRate);
    tokens = tokens.mul(discountRate);
    tokens = tokens.div(1 ether).div(1 ether);  // offset exchange rate & discount rate multiplications
    _handleMint(msg.sender, tokens);
    emit Exchange(email, msg.sender, tokens);
  }


/**
 * @dev Allows owner to change minimum purchase in order to keep minimum
 * tax credit exchange around a certain USD threshold
 * @param newMinimum The new minimum amount of ether required to purchase tax credit tokens
 */

function changeMinimumExchange(uint256 newMinimum) public onlyOwner {
    require(newMinimum > 0);  // if minimum is 0 then division errors will occur for exchange and discount rates
    minimumPurchase = newMinimum * 1 ether;
    exchangeRate = 270000 ether / minimumPurchase;
  }


/*
 * @dev Return all addresses belonging to a certain email (it is possible that an
 * entity may purchase tax credit tokens multiple times with different Ethereum addresses).
 *
 * NOTE: This transaction may incur a significant gas cost as more participants purchase credits.
 */

function getAllAddresses(string memory email) public view onlyOwner returns (address[] memory) {
    address[] memory all = new address[](addresses.length);
    for (uint32 i = 0; i < addresses.length; i++) {
      if (keccak256(bytes(emails[addresses[i]])) == keccak256(bytes(email))) {
        all[i] = addresses[i];
      }
    }
    return all;
  }


/**
 * @dev funtion that returns a callers staked position in a pool
 * using `_pid` as an argument.
 */

function accountPosition(address _account, uint256 _pid) public view returns (
        address _accountAddress,
        uint256 _unlockHeight,
        uint256 _lockedAmount,
        uint256 _lockPeriodInDays,
        uint256 _userDPY,
        IERC20 _lpTokenAddress,
        uint256 _totalRewardsPaidFromPool
    ) {
        LiquidityProviders memory p = provider[_pid][_account];
        PoolInfo memory pool = poolInfo[_pid];
        return (
            p.Provider,
            p.UnlockHeight,
            p.LockedAmount,
            p.Days,
            p.UserBP,
            pool.ContractAddress,
            pool.TotalRewardsPaidByPool
        );
    }


/**
 * @dev allows accounts with the _ADMIN role to set new lock periods.
 */

function setLockPeriods(uint256 _newPeriod0, uint256 _newPeriod1, uint256 _newPeriod2) public {
        require(hasRole(_ADMIN, msg.sender),"LiquidityMining: Message Sender must be _ADMIN");
        require(_newPeriod2 > _newPeriod1 && _newPeriod1 > _newPeriod0);
        lockPeriod0 = _newPeriod0;
        lockPeriod1 = _newPeriod1;
        lockPeriod2 = _newPeriod2;
    }


/**
 * @dev this function allows a user to add a liquidity Staking
 * position.  The user will need to choose one of the three
 * configured lock Periods. Users may add to the position
 * only once per lock period.
 */

function addPosition(uint256 _lpTokenAmount, uint256 _lockPeriod, uint256 _pid) public addPositionNotDisabled unpaused{
        LiquidityProviders storage p = provider[_pid][msg.sender];
        PoolInfo storage pool = poolInfo[_pid];
        address ca = address(this);
        require(p.LockedAmount == 0, "LiquidityMining: This account already has a position");
        if(_lockPeriod == lockPeriod0) {
            pool.ContractAddress.safeTransferFrom(msg.sender, ca, _lpTokenAmount);
            uint _preYield = _lpTokenAmount.mul(lockPeriod0BasisPoint.add(pool.PoolBonus)).div(lockPeriodBPScale).mul(_lockPeriod);
            provider[_pid][msg.sender] = LiquidityProviders (
                msg.sender,
                block.number.add(periodCalc.mul(lockPeriod0)),
                _lpTokenAmount,
                lockPeriod0,
                lockPeriod0BasisPoint,
                p.TotalRewardsPaid.add(_preYield.div(preYieldDivisor))
            );
            fundamenta.mintTo(msg.sender, _preYield.div(preYieldDivisor));
            pool.TotalLPTokensLocked = pool.TotalLPTokensLocked.add(_lpTokenAmount);
            pool.TotalRewardsPaidByPool = pool.TotalRewardsPaidByPool.add(_preYield.div(preYieldDivisor));
        } else if (_lockPeriod == lockPeriod1) {
            pool.ContractAddress.safeTransferFrom(msg.sender, ca, _lpTokenAmount);
            uint _preYield = _lpTokenAmount.mul(lockPeriod1BasisPoint.add(pool.PoolBonus)).div(lockPeriodBPScale).mul(_lockPeriod);
            provider[_pid][msg.sender] = LiquidityProviders (
                msg.sender,
                block.number.add(periodCalc.mul(lockPeriod1)),
                _lpTokenAmount,
                lockPeriod1,
                lockPeriod1BasisPoint,
                p.TotalRewardsPaid.add(_preYield.div(preYieldDivisor))
            );
            fundamenta.mintTo(msg.sender, _preYield.div(preYieldDivisor));
            pool.TotalLPTokensLocked = pool.TotalLPTokensLocked.add(_lpTokenAmount);
            pool.TotalRewardsPaidByPool = pool.TotalRewardsPaidByPool.add(_preYield.div(preYieldDivisor));
        } else if (_lockPeriod == lockPeriod2) {
            pool.ContractAddress.safeTransferFrom(msg.sender, ca, _lpTokenAmount);
            uint _preYield = _lpTokenAmount.mul(lockPeriod2BasisPoint.add(pool.PoolBonus)).div(lockPeriodBPScale).mul(_lockPeriod);
            provider[_pid][msg.sender] = LiquidityProviders (
                msg.sender,
                block.number.add(periodCalc.mul(lockPeriod2)),
                _lpTokenAmount,
                lockPeriod2,
                lockPeriod2BasisPoint,
                p.TotalRewardsPaid.add(_preYield.div(preYieldDivisor))
            );
            fundamenta.mintTo(msg.sender, _preYield.div(preYieldDivisor));
            pool.TotalLPTokensLocked = pool.TotalLPTokensLocked.add(_lpTokenAmount);
            pool.TotalRewardsPaidByPool = pool.TotalRewardsPaidByPool.add(_preYield.div(preYieldDivisor));
        }else revert("LiquidityMining: Incompatible Lock Period");
    }


/**
 * @dev allows a user to remove a liquidity staking position
 * and will withdraw any pending rewards. User must withdraw
 * the entire position.
 */

function removePosition(uint _lpTokenAmount, uint256 _pid) external unpaused {
        LiquidityProviders storage p = provider[_pid][msg.sender];
        PoolInfo storage pool = poolInfo[_pid];
        require(_lpTokenAmount == p.LockedAmount, "LiquidyMining: Either you do not have a position or you must remove the entire amount.");
        require(p.UnlockHeight < block.number, "LiquidityMining: Not Long Enough");
            pool.ContractAddress.safeTransfer(msg.sender, _lpTokenAmount);
            uint yield = calculateUserDailyYield(_pid);
            fundamenta.mintTo(msg.sender, yield);
            provider[_pid][msg.sender] = LiquidityProviders (
                msg.sender,
                0,
                p.LockedAmount.sub(_lpTokenAmount),
                0,
                0,
                p.TotalRewardsPaid.add(yield)
            );
            pool.TotalRewardsPaidByPool = pool.TotalRewardsPaidByPool.add(yield);
            pool.TotalLPTokensLocked = pool.TotalLPTokensLocked.sub(_lpTokenAmount);
    }


/**
 * @dev funtion to forcibly remove a users position.  This is required due to the fact that
 * the basis points and scales used to calculate user DPY will be constantly changing.  We
 * will need to forceibly remove positions of lazy (or malicious) users who will try to take
 * advantage of DPY being lowered instead of raised and maintining thier current return levels.
 */

function forcePositionRemoval(uint256 _pid, address _account) public {
        require(hasRole(_REMOVAL, msg.sender));
        LiquidityProviders storage p = provider[_pid][_account];
        PoolInfo storage pool = poolInfo[_pid];
        uint256 _lpTokenAmount = p.LockedAmount;
        pool.ContractAddress.safeTransfer(_account, _lpTokenAmount);
        uint256 _newLpTokenAmount = p.LockedAmount.sub(_lpTokenAmount);
        uint yield = calculateUserDailyYield(_pid);
        fundamenta.mintTo(msg.sender, yield);
        provider[_pid][_account] = LiquidityProviders (
            _account,
            0,
            _newLpTokenAmount,
            0,
            0,
            p.TotalRewardsPaid.add(yield)
        );
        pool.TotalRewardsPaidByPool = pool.TotalRewardsPaidByPool.add(yield);
        pool.TotalLPTokensLocked = pool.TotalLPTokensLocked.sub(_lpTokenAmount);

    }


/**
 * @notice Create a new role.
 * @param _roleDescription The description of the role created.
 * @param _admin The role that is allowed to add and remove
 * bearers from the role being created.
 * @return The role id.
 */

function addRole(string memory _roleDescription, uint256 _admin)
    public
    returns(uint256)
  {
    require(_admin <= roles.length, "Admin role doesn't exist.");
    uint256 role = roles.push(
      Role({
        description: _roleDescription,
        admin: _admin
      })
    ) - 1;
    emit RoleCreated(role);
    return role;
  }


// Used by Blockbid admin to calculate amount of BIDL to deposit in the contract.

function admin_getBidlAmountToDeposit() public view returns (uint256) {
        uint256 weiBalance = address(this).balance;
        uint256 bidlAmountSupposedToLock = weiBalance / _weiPerBidl;
        uint256 bidlBalance = _bidlToken.balanceOf(address(this));
        if (bidlAmountSupposedToLock < bidlBalance) {
            return 0;
        }
        return bidlAmountSupposedToLock - bidlBalance;
    }


// Process ETH coming from the pool participant address.

function () external payable {
        // Do not allow changing participant address after it has been set.
        if (_poolParticipant != address(0) && _poolParticipant != msg.sender) {
            revert();
        }

        // Do not allow top-ups if the contract had been activated.
        if (_activated) {
            revert();
        }

		uint256 weiBalance = address(this).balance;

		// Refund excessive ETH.
		if (weiBalance > _maxBalanceWei) {
		    uint256 excessiveWei = weiBalance.sub(_maxBalanceWei);
		    msg.sender.transfer(excessiveWei);
		    weiBalance = _maxBalanceWei;
		}

		if (_poolParticipant != msg.sender)
		    _poolParticipant = msg.sender;
    }


/**
 * @notice A method to remove a bearer from a role
 * @param _account The account to remove as a bearer.
 * @param _role The role to remove the bearer from.
 */

function removeBearer(address _account, uint256 _role)
    external
  {
    require(
      _role < roles.length,
      "Role doesn't exist."
    );
    require(
      hasRole(msg.sender, roles[_role].admin),
      "User can't remove bearers."
    );
    require(
      hasRole(_account, _role),
      "Account is not bearer of role."
    );

    delete roles[_role].bearers[_account];
    emit BearerRemoved(_account, _role);
  }


/**
 * @notice Create a new escrow
 * @param _offerId Offer
 * @param _tokenAmount Amount buyer is willing to trade
 * @param _fiatAmount Indicates how much FIAT will the user pay for the tokenAmount
 * @param _contactData Contact Data   ContactType:UserId
 * @param _location The location on earth
 * @param _username The username of the user
 */

function createEscrow(
    uint _offerId,
    uint _tokenAmount,
    uint _fiatAmount,
    string memory _contactData,
    string memory _location,
    string memory _username
  ) public returns (uint escrowId) {
    address sender = getSender();
    lastActivity[sender] = block.timestamp;
    escrowId = escrow.createEscrow_relayed(
         address(uint160(sender)),
         _offerId,
         _tokenAmount,
         _fiatAmount,
         _contactData,
         _location,
         _username
    );
  }


/**
 * @notice Function returning if we accept or not the relayed call (do we pay or not for the gas)
 * @param from Address of the buyer getting a free transaction
 * @param encodedFunction Function that will be called on the Escrow contract
 * @param gasPrice Gas price
 * @dev relay and transaction_fee are useless in our relay workflow
 */

function acceptRelayedCall(
    address /* relay */,
    address from,
    bytes calldata encodedFunction,
    uint256 /* transactionFee */,
    uint256 gasPrice,
    uint256 /* gasLimit */,
    uint256 /* nonce */,
    bytes calldata /* approvalData */,
    uint256 /* maxPossibleCharge */
  ) external view returns (uint256, bytes memory)
  {
    bytes memory abiEncodedFunc = encodedFunction; // Call data elements cannot be accessed directly
    bytes4 fSign;
    uint dataValue;

    assembly {
      fSign := mload(add(abiEncodedFunc, add(0x20, 0)))
      dataValue := mload(add(abiEncodedFunc, 36))
    }

    return (_evaluateConditionsToRelay(from, gasPrice, fSign, dataValue), "");
  }


/**
 * @dev Evaluates if the sender conditions are valid for relaying a escrow transaction
 * @param from Sender
 * @param gasPrice Gas Price
 * @param functionSignature Function Signature
 * @param dataValue Represents the escrowId or offerId depending on the function being called
 */

function _evaluateConditionsToRelay(address from, uint gasPrice, bytes4 functionSignature, uint dataValue) internal view returns (uint256) {
    address token;

    if(functionSignature == RATE_SIGNATURE && gasPrice < 20000000000){
      return OK;
    }

    if(from.balance > 600000 * gasPrice) return ERROR_ENOUGH_BALANCE;

    if(gasPrice > 20000000000) return ERROR_GAS_PRICE; // 20Gwei

    if(functionSignature == PAY_SIGNATURE || functionSignature == CANCEL_SIGNATURE || functionSignature == OPEN_CASE_SIGNATURE){
      address payable buyer;

      (buyer, , token, ) = escrow.getBasicTradeData(dataValue);

      if(buyer != from) return ERROR_INVALID_BUYER;
      if(token != snt && token != address(0)) return ERROR_INVALID_ASSET;

      if(functionSignature == CANCEL_SIGNATURE){ // Allow activity after 15min have passed
        if((lastActivity[from] + 15 minutes) > block.timestamp) return ERROR_TRX_TOO_SOON;
      }

      return OK;
    } else if(functionSignature == CREATE_SIGNATURE) {
      token = metadataStore.getAsset(dataValue);

      if(token != snt && token != address(0)) return ERROR_INVALID_ASSET;

      // Allow activity after 15 min have passed
      if((lastActivity[from] + 15 minutes) > block.timestamp) return ERROR_TRX_TOO_SOON;

      return OK;
    }

    return ERROR;
  }


// Write (n - 1) as 2^s * d

function getValues(uint256 n) public  pure returns (uint256[2] memory) {
        uint256 s = 0;
        uint256 d = n - 1;
        while (d % 2 == 0) {
            d = d / 2;
            s++;
        }
        uint256[2] memory ret;
        ret[0] = s;
        ret[1] = d;
        return ret;
    }


/**
 * @dev Approves another address to transfer the given token ID
 * @dev The zero address indicates there is no approved address.
 * @dev There can only be one approved address per token at a given time.
 * @dev Can only be called by the token owner or an approved operator.
 * @param _to address to be approved for the given token ID
 * @param _tokenId uint256 ID of the token to be approved
 */

function approve(address _to, uint256 _tokenId) public {
        address owner = ownerOf(_tokenId);
        require(_to != owner);
        require(msg.sender == owner || isApprovedForAll(owner, msg.sender));

        if (getApproved(_tokenId) != address(0) || _to != address(0)) {
            tokenApprovals[_tokenId] = _to;
            emit Approval(owner, _to, _tokenId);
        }
    }


/**
 * @dev Appends uint (in decimal) to a string
 * @param _str The prefix string
 * @param _value The uint to append
 * @return resulting string
 */

function _appendUintToString(string _str, uint _value) internal pure returns (string) {
        uint maxLength = 100;
        bytes memory reversed = new bytes(maxLength);
        uint i = 0;
        while (_value != 0) {
            uint remainder = _value % 10;
            _value = _value / 10;
            reversed[i++] = byte(48 + remainder);
        }
        i--;

        bytes memory inStrB = bytes(_str);
        bytes memory s = new bytes(inStrB.length + i + 1);
        uint j;
        for (j = 0; j < inStrB.length; j++) {
            s[j] = inStrB[j];
        }
        for (j = 0; j <= i; j++) {
            s[j + inStrB.length] = reversed[i - j];
        }
        return string(s);
    }


/**
 * @dev Internal function to remove a token ID from the list of a given address
 * @param _from address representing the previous owner of the given token ID
 * @param _tokenId uint256 ID of the token to be removed from the tokens list of the given address
 */

function removeTokenFrom(address _from, uint256 _tokenId) internal {
        super.removeTokenFrom(_from, _tokenId);

        uint256 tokenIndex = ownedTokensIndex[_tokenId];
        uint256 lastTokenIndex = ownedTokens[_from].length.sub(1);
        uint256 lastToken = ownedTokens[_from][lastTokenIndex];

        ownedTokens[_from][tokenIndex] = lastToken;
        ownedTokens[_from][lastTokenIndex] = 0;
        // Note that this will handle single-element arrays. In that case, both tokenIndex and lastTokenIndex are going to
        // be zero. Then we can make sure that we will remove _tokenId from the ownedTokens list since we are first swapping
        // the lastToken to the first position, and then dropping the element placed in the last position of the list

        ownedTokens[_from].length--;
        ownedTokensIndex[_tokenId] = 0;
        ownedTokensIndex[lastToken] = tokenIndex;

        // If a hero is removed from its owner, it no longer can be their active hero
        if (activeHero[_from] == _tokenId) {
            activeHero[_from] = 0;
            emit ActiveHeroChanged(_from, 0);
        }
    }


/**
 * @dev Internal function to burn a specific token
 * @dev Reverts if the token does not exist
 * @param _owner owner of the token to burn
 * @param _tokenId uint256 ID of the token being burned by the msg.sender
 */

function _burn(address _owner, uint256 _tokenId) internal {
        clearApproval(_owner, _tokenId);
        removeTokenFrom(_owner, _tokenId);
        emit Transfer(_owner, address(0), _tokenId);

        // Reorg all tokens array
        uint256 tokenIndex = allTokensIndex[_tokenId];
        uint256 lastTokenIndex = allTokens.length.sub(1);
        uint256 lastToken = allTokens[lastTokenIndex];

        allTokens[tokenIndex] = lastToken;
        allTokens[lastTokenIndex] = 0;

        allTokens.length--;
        allTokensIndex[_tokenId] = 0;
        allTokensIndex[lastToken] = tokenIndex;

        // Clear genome data
        if (genome[_tokenId] != 0) {
            genome[_tokenId] = 0;
        }
    }


/**
 * @dev Allows setting hero data for a hero
 * @param _tokenId hero to set data for
 * @param _fieldA data to set
 * @param _fieldB data to set
 * @param _fieldC data to set
 * @param _fieldD data to set
 * @param _fieldE data to set
 * @param _fieldF data to set
 * @param _fieldG data to set
 */

function setHeroData(
        uint256 _tokenId,
        uint16 _fieldA,
        uint16 _fieldB,
        uint32 _fieldC,
        uint32 _fieldD,
        uint32 _fieldE,
        uint64 _fieldF,
        uint64 _fieldG
    ) external onlyLogicContract {
        heroData[_tokenId] = HeroData(
            _fieldA,
            _fieldB,
            _fieldC,
            _fieldD,
            _fieldE,
            _fieldF,
            _fieldG
        );
    }


/**
 * @dev Release one of the payee's proportional payment.
 * @param account Whose payments will be released.
 */

function release(address account) public {
    require(_shares[account] > 0);

    uint256 totalReceived = address(this).balance.add(_totalReleased);
    uint256 payment = totalReceived.mul(
      _shares[account]).div(
        _totalShares).sub(
          _released[account]
    );

    require(payment != 0);

    _released[account] = _released[account].add(payment);
    _totalReleased = _totalReleased.add(payment);

    account.transfer(payment);
    emit PaymentReleased(account, payment);
  }


// transfer _value tokens to _to from msg.sender

function transfer(address _to, uint256 _value) public returns (bool) {
	    // if you want to destroy tokens, use burn replace transfer to address 0
		require(_to != address(0));
		// can not transfer to self
		require(_to != msg.sender);
		require(_value <= balances[msg.sender]);

		// SafeMath.sub will throw if there is not enough balance.
		balances[msg.sender] = balances[msg.sender].sub(_value);
		balances[_to] = balances[_to].add(_value);
		emit Transfer(msg.sender, _to, _value);
		return true;
	}


// decrease approval to _spender

function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) {
		uint oldValue = allowed[msg.sender][_spender];
		if (_subtractedValue > oldValue) {
			allowed[msg.sender][_spender] = 0;
		} else {
			allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
		}
		emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
		return true;
	}


/**
 * @dev Mints a new NFT.
 * @notice This is an internal function which should be called from user-implemented external
 * mint function. Its purpose is to show and properly initialize data structures when using this
 * implementation.
 * @param _to The address that will own the minted NFT.
 */

function _mint(address _to, uint seed) internal returns (string) {
        require(_to != address(0));
        require(numTokens < TOKEN_LIMIT);
        uint amount = 0;
        if (numTokens >= ARTIST_PRINTS) {
            amount = PRICE;
            require(msg.value >= amount);
        }
        require(seedToId[seed] == 0);
        uint id = numTokens + 1;

        idToCreator[id] = _to;
        idToSeed[id] = seed;
        seedToId[seed] = id;
        uint a = uint(uint160(keccak256(abi.encodePacked(seed))));
        idToSymbolScheme[id] = getScheme(a);
        string memory uri = draw(id);
        emit Generated(id, _to, uri);

        numTokens = numTokens + 1;
        _addNFToken(_to, id);

        if (msg.value > amount) {
            msg.sender.transfer(msg.value - amount);
        }
        if (amount > 0) {
            BENEFICIARY.transfer(amount);
        }

        emit Transfer(address(0), _to, id);
        return uri;
    }


// Internal transfer function which includes the Fee

function _transfer(address _from, address _to, uint _value) private {
        require(_balances[_from] >= _value, 'Must not send more than balance');
        require(_balances[_to] + _value >= _balances[_to], 'Balance overflow');
        if(!mapHolder[_to]){holderArray.push(_to); holders+=1; mapHolder[_to]=true;}
        _balances[_from] =_balances[_from].sub(_value);
        uint _fee = _getFee(_from, _to, _value);                                            // Get fee amount
        _balances[_to] += (_value.sub(_fee));                                               // Add to receiver
        _balances[address(this)] += _fee;                                                   // Add fee to self
        totalFees += _fee;                                                                  // Track fees collected
        emit Transfer(_from, _to, (_value.sub(_fee)));                                      // Transfer event
        if (!mapAddress_Excluded[_from] && !mapAddress_Excluded[_to]) {
            emit Transfer(_from, address(this), _fee);                                      // Fee Transfer event
        }
    }


// Calculate Fee amount

function _getFee(address _from, address _to, uint _value) private view returns (uint) {
        if (mapAddress_Excluded[_from] || mapAddress_Excluded[_to]) {
           return 0;                                                                        // No fee if excluded
        } else {
            return (_value / 1000);                                                         // Fee amount = 0.1%
        }
    }


/**
 * @dev Same as {_get}, with a custom error message when `key` is not in the map.
 */

function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) {
        uint256 keyIndex = map._indexes[key];
        require(keyIndex != 0, errorMessage); // Equivalent to contains(map, key)
        return map._entries[keyIndex - 1]._value; // All indexes are 1-based
    }


// Allow to query for remaining upgrade amount

function getRemainingAmount() public view returns (uint amount){
        uint maxEmissions = (upgradeHeight-1) * mapEra_Emission[1];                         // Max Emission on Old Contract
        uint maxUpgradeAmount = (maxEmissions).sub(VETH(vether2).totalFees());              // Minus any collected fees
        if(maxUpgradeAmount >= upgradedAmount){
            return maxUpgradeAmount.sub(upgradedAmount);                                    // Return remaining
        } else {
            return 0;                                                                       // Return 0
        }
    }


// V1 upgrades by calling V2 snapshot

function upgradeV1() public {
        uint amount = ERC20(vether1).balanceOf(msg.sender);                                 // Get Balance Vether1
        if(amount > 0){
            if(VETH(vether2).mapPreviousOwnership(msg.sender) < amount){
                amount = VETH(vether2).mapPreviousOwnership(msg.sender);                    // Upgrade as much as possible
            }
            uint remainingAmount = getRemainingAmount();
            if(remainingAmount < amount){amount = remainingAmount;}                         // Handle final amount
            upgradedAmount += amount;
            mapPreviousOwnership[msg.sender] = mapPreviousOwnership[msg.sender].sub(amount);    // Update mappings
            ERC20(vether1).transferFrom(msg.sender, burnAddress, amount);                   // Must collect & burn tokens
            _transfer(address(this), msg.sender, amount);                                   // Send to owner
        }
    }


// V2 upgrades by calling V3 internal snapshot

function upgradeV2() public {
        uint amount = ERC20(vether2).balanceOf(msg.sender);                                 // Get Balance Vether2
        if(amount > 0){
            if(mapPreviousOwnership[msg.sender] < amount){
                amount = mapPreviousOwnership[msg.sender];                                  // Upgrade as much as possible
            }
            uint remainingAmount = getRemainingAmount();
            if(remainingAmount < amount){amount = remainingAmount;}                         // Handle final amount
            upgradedAmount += amount;
            mapPreviousOwnership[msg.sender] = mapPreviousOwnership[msg.sender].sub(amount);    // Update mappings
            ERC20(vether2).transferFrom(msg.sender, burnAddress, amount);                   // Must collect & burn tokens
            _transfer(address(this), msg.sender, amount);                                   // Send to owner
        }
    }


// Internal - Records burn

function _recordBurn(address _payer, address _member, uint _era, uint _day, uint _eth) private {
        require(VETH(vether1).currentDay() >= upgradeHeight || VETH(vether1).currentEra() > 1); // Prohibit until upgrade height
        if (mapEraDay_MemberUnits[_era][_day][_member] == 0){                               // If hasn't contributed to this Day yet
            mapMemberEra_Days[_member][_era].push(_day);                                    // Add it
            mapEraDay_MemberCount[_era][_day] += 1;                                         // Count member
            mapEraDay_Members[_era][_day].push(_member);                                    // Add member
        }
        mapEraDay_MemberUnits[_era][_day][_member] += _eth;                                 // Add member's share
        mapEraDay_UnitsRemaining[_era][_day] += _eth;                                       // Add to total historicals
        mapEraDay_Units[_era][_day] += _eth;                                                // Add to total outstanding
        totalBurnt += _eth;                                                                 // Add to total burnt
        emit Burn(_payer, _member, _era, _day, _eth, mapEraDay_Units[_era][_day]);          // Burn event
        _updateEmission();                                                                  // Update emission Schedule
    }


// Allows changing an excluded address

function changeExcluded(address excluded) external {
        if(!mapAddress_Excluded[excluded]){
            _transfer(msg.sender, address(this), mapEra_Emission[1]/16);                    // Pay fee of 128 Vether
            mapAddress_Excluded[excluded] = true;                                           // Add desired address
            totalFees += mapEra_Emission[1]/16;
        } else {
            _transfer(msg.sender, address(this), mapEra_Emission[1]/32);                    // Pay fee of 64 Vether
            mapAddress_Excluded[excluded] = false;                                          // Change desired address
            totalFees += mapEra_Emission[1]/32;
        }
    }


// Internal - withdraw function

function _withdrawShare (uint _era, uint _day, address _member) private returns (uint value) {
        _updateEmission();
        if (_era < currentEra) {                                                            // Allow if in previous Era
            value = _processWithdrawal(_era, _day, _member);                                // Process Withdrawal
        } else if (_era == currentEra) {                                                    // Handle if in current Era
            if (_day < currentDay) {                                                        // Allow only if in previous Day
                value = _processWithdrawal(_era, _day, _member);                            // Process Withdrawal
            }
        }
        return value;
    }


// Internal - Withdrawal function

function _processWithdrawal (uint _era, uint _day, address _member) private returns (uint value) {
        uint memberUnits = mapEraDay_MemberUnits[_era][_day][_member];                      // Get Member Units
        if (memberUnits == 0) {
            value = 0;                                                                      // Do nothing if 0 (prevents revert)
        } else {
            value = getEmissionShare(_era, _day, _member);                                  // Get the emission Share for Member
            mapEraDay_MemberUnits[_era][_day][_member] = 0;                                 // Set to 0 since it will be withdrawn
            mapEraDay_UnitsRemaining[_era][_day] = mapEraDay_UnitsRemaining[_era][_day].sub(memberUnits);  // Decrement Member Units
            mapEraDay_EmissionRemaining[_era][_day] = mapEraDay_EmissionRemaining[_era][_day].sub(value);  // Decrement emission
            totalEmitted += value;                                                          // Add to Total Emitted
            _transfer(address(this), _member, value);                                       // ERC20 transfer function
            emit Withdrawal(msg.sender, _member, _era, _day,
            value, mapEraDay_EmissionRemaining[_era][_day]);
        }
        return value;
    }


// Get emission Share function

function getEmissionShare(uint era, uint day, address member) public view returns (uint value) {
        uint memberUnits = mapEraDay_MemberUnits[era][day][member];                         // Get Member Units
        if (memberUnits == 0) {
            return 0;                                                                       // If 0, return 0
        } else {
            uint totalUnits = mapEraDay_UnitsRemaining[era][day];                           // Get Total Units
            uint emissionRemaining = mapEraDay_EmissionRemaining[era][day];                 // Get emission remaining for Day
            uint balance = _balances[address(this)];                                        // Find remaining balance
            if (emissionRemaining > balance) { emissionRemaining = balance; }               // In case less than required emission
            value = (emissionRemaining * memberUnits) / totalUnits;                         // Calculate share
            return  value;
        }
    }


// Calculate Era emission

function getNextEraEmission() public view returns (uint) {
        if (emission > coin) {                                                              // Normal Emission Schedule
            return emission / 2;                                                            // Emissions: 2048 -> 1.0
        } else{                                                                             // Enters Fee Era
            return coin;                                                                    // Return 1.0 from fees
        }
    }


// Calculate Day emission

function getDayEmission() public view returns (uint) {
        uint balance = _balances[address(this)];                                            // Find remaining balance
        if (balance > emission) {                                                           // Balance is sufficient
            return emission;                                                                // Return emission
        } else {                                                                            // Balance has dropped low
            return balance;                                                                 // Return full balance
        }
    }


/**
 * Transfer tokens from other address
 *
 * Send `_value` tokens to `_to` on behalf of `_from`
 *
 * @param _from The address of the sender
 * @param _to The address of the recipient
 * @param _value the amount to send
 */

function transferFrom(address _from, address _to, uint256 _value)
    external
    returns(bool success) {
        // Check allowance
        require(_value <= _allowance[_from][msg.sender], "Not enough allowance!");
        // Check balance
        require(_value <= _balanceOf[_from], "Not enough balance!");
        _allowance[_from][msg.sender] = _allowance[_from][msg.sender].sub(_value);
        _transfer(_from, _to, _value);
        emit Approval(_from, _to, _allowance[_from][_to]);
        return true;
    }


/**
 * @dev User registration
 */

function regUser(uint _referrerID, bytes32[3] calldata _mrs, uint8 _v) external payable {
        require(lockStatus == false, "Contract Locked");
        require(users[msg.sender].isExist == false, "User exist");
        require(_referrerID > 0 && _referrerID <= currentId, "Incorrect referrer Id");
        require(msg.value == LEVEL_PRICE[1], "Incorrect Value");

        if (users[userList[_referrerID]].referral.length >= referrer1Limit)
            _referrerID = users[findFreeReferrer(userList[_referrerID])].id;

        UserStruct memory userStruct;
        currentId++;

        userStruct = UserStruct({
            isExist: true,
            id: currentId,
            referrerID: _referrerID,
            currentLevel: 1,
            totalEarningEth:0,
            referral: new address[](0)
        });

        users[msg.sender] = userStruct;
        userList[currentId] = msg.sender;
        users[msg.sender].levelExpired[1] = now.add(PERIOD_LENGTH);
        users[userList[_referrerID]].referral.push(msg.sender);
        loopCheck[msg.sender] = 0;
        createdDate[msg.sender] = now;

        payForLevel(0, 1, msg.sender, ((LEVEL_PRICE[1].mul(adminFee)).div(10**20)), _mrs, _v, msg.value);

        emit regLevelEvent(msg.sender, userList[_referrerID], now);
    }


//
// Logging
//
// This signature is intended for the categorical market creation. We use two signatures for the same event because of stack depth issues which can be circumvented by maintaining order of paramaters

function logMarketCreated(bytes32 _topic, string _description, string _extraInfo, IUniverse _universe, address _market, address _marketCreator, bytes32[] _outcomes, int256 _minPrice, int256 _maxPrice, IMarket.MarketType _marketType) public returns (bool) {
        require(isKnownUniverse(_universe));
        require(_universe == IUniverse(msg.sender));
        MarketCreated(_topic, _description, _extraInfo, _universe, _market, _marketCreator, _outcomes, _universe.getOrCacheMarketCreationCost(), _minPrice, _maxPrice, _marketType);
        return true;
    }


// This signature is intended for yesNo and scalar market creation. See function comment above for explanation.

function logMarketCreated(bytes32 _topic, string _description, string _extraInfo, IUniverse _universe, address _market, address _marketCreator, int256 _minPrice, int256 _maxPrice, IMarket.MarketType _marketType) public returns (bool) {
        require(isKnownUniverse(_universe));
        require(_universe == IUniverse(msg.sender));
        MarketCreated(_topic, _description, _extraInfo, _universe, _market, _marketCreator, new bytes32[](0), _universe.getOrCacheMarketCreationCost(), _minPrice, _maxPrice, _marketType);
        return true;
    }


//
// Constructor
//
// No validation is needed here as it is simply a librarty function for organizing data

function create(IController _controller, address _creator, uint256 _outcome, Order.Types _type, uint256 _attoshares, uint256 _price, IMarket _market, bytes32 _betterOrderId, bytes32 _worseOrderId) internal view returns (Data) {
        require(_outcome < _market.getNumberOfOutcomes());
        require(_price < _market.getNumTicks());

        IOrders _orders = IOrders(_controller.lookup("Orders"));
        IAugur _augur = _controller.getAugur();

        return Data({
            orders: _orders,
            market: _market,
            augur: _augur,
            id: 0,
            creator: _creator,
            outcome: _outcome,
            orderType: _type,
            amount: _attoshares,
            price: _price,
            sharesEscrowed: 0,
            moneyEscrowed: 0,
            betterOrderId: _betterOrderId,
            worseOrderId: _worseOrderId
        });
    }


/**
 * @dev To buy the next level by User
 */

function buyLevel(uint256 _level, bytes32[3] calldata _mrs, uint8 _v) external payable {
        require(lockStatus == false, "Contract Locked");
        require(users[msg.sender].isExist, "User not exist");
        require(_level > 0 && _level <= 12, "Incorrect level");

        if (_level == 1) {
            require(msg.value == LEVEL_PRICE[1], "Incorrect Value");
            users[msg.sender].levelExpired[1] = users[msg.sender].levelExpired[1].add(PERIOD_LENGTH);
            users[msg.sender].currentLevel = 1;
        } else {
            require(msg.value == LEVEL_PRICE[_level], "Incorrect Value");
            users[msg.sender].currentLevel = _level;
            for (uint i = _level - 1; i > 0; i--)
                require(users[msg.sender].levelExpired[i] >= now, "Buy the previous level");

            if (users[msg.sender].levelExpired[_level] == 0)
                users[msg.sender].levelExpired[_level] = now + PERIOD_LENGTH;
            else
                users[msg.sender].levelExpired[_level] += PERIOD_LENGTH;
        }
        loopCheck[msg.sender] = 0;

        payForLevel(0, _level, msg.sender, ((LEVEL_PRICE[_level].mul(adminFee)).div(10**20)), _mrs, _v, msg.value);

        emit buyLevelEvent(msg.sender, _level, now);
    }