/**
 @dev Update a slate and its associated requests
 @param slateID The slate to update
 */

function acceptSlate(uint slateID) private {
        // Mark the slate as accepted
        Slate storage s = slates[slateID];
        s.status = SlateStatus.Accepted;

        // Record the incumbent
        if (incumbent[s.resource] != s.recommender) {
            incumbent[s.resource] = s.recommender;
        }

        // mark all of its requests as approved
        uint[] memory requestIDs = s.requests;
        for (uint i = 0; i < requestIDs.length; i++) {
            uint requestID = requestIDs[i];
            requests[requestID].approved = true;
        }
    }


// --- Prepare ---
// Sets the two deployer dependencies. This needs to be called by the deployUsr

function prepare(address lender_, address borrower_, address oracle_, address[] memory poolAdmins_) public {
        require(deployUsr == msg.sender);

        borrowerDeployer = BorrowerDeployerLike(borrower_);
        lenderDeployer = LenderDeployerLike_1(lender_);
        oracle = oracle_;
        poolAdmins = poolAdmins_;

        deployUsr = address(0); // disallow the deploy user to call this more than once.
    }


// --- Deploy ---
// After going through the deploy process on the lender and borrower method, this method is called to connect
// lender and borrower contracts.

function deploy() public {
        require(address(borrowerDeployer) != address(0) && address(lenderDeployer) != address(0) && deployed == false);
        deployed = true;

        address reserve_ = lenderDeployer.reserve();
        address shelf_ = borrowerDeployer.shelf();

        // Borrower depends
        DependLike_3(borrowerDeployer.collector()).depend("reserve", reserve_);
        DependLike_3(borrowerDeployer.shelf()).depend("lender", reserve_);
        DependLike_3(borrowerDeployer.shelf()).depend("reserve", reserve_);

        // Lender depends
        address navFeed = borrowerDeployer.feed();

        DependLike_3(reserve_).depend("shelf", shelf_);
        DependLike_3(lenderDeployer.assessor()).depend("navFeed", navFeed);

        // Lender wards
        if (oracle != address(0)) AuthLike_3(navFeed).rely(oracle);

        // directly relying governance so it can be used to directly add/remove pool admins without going through the root
        PoolAdminLike poolAdmin = PoolAdminLike(lenderDeployer.poolAdmin());
        PoolAdminLike(poolAdmin).rely(governance);

        for (uint i = 0; i < poolAdmins.length; i++) {
            PoolAdminLike(poolAdmin).relyAdmin(poolAdmins[i]);
        }
    }


////////////////////
/// @notice get all nfts of a person

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


// transferOwnership add a pending owner

function transferOwnership(address newOwner) public override onlyOwner {
        require(!isBlackListed[newOwner], "Pending owner can not be in blackList");
        require(newOwner != owner(), "Pending owner and current owner need to be different");
        require(newOwner != address(0), "Pending owner can not be zero address");

        _pendingOwner = newOwner;

        emit TransferOwnerShip(newOwner);
    }


// acceptOwnership allows the pending owner to accept the ownership of the SPWN token contract
// along with grant new owner MINT_BURN_ROLE role and remove MINT_BURN_ROLE from old owner

function acceptOwnership() public {
        require(_pendingOwner != address(0), "Please set pending owner first");
        require(_pendingOwner == msg.sender, "Only pending owner is able to accept the ownership");
        require(!isBlackListed[msg.sender], "Pending owner can not be in blackList");

        address oldOwner = owner();

        _owner = _pendingOwner;

        _setupRole(DEFAULT_ADMIN_ROLE, _pendingOwner);
        _grantAccess(MINT_BURN_ROLE, _pendingOwner);

        _revokeAccess(MINT_BURN_ROLE, oldOwner);
        _revokeAccess(DEFAULT_ADMIN_ROLE, oldOwner);

        emit AcceptOwnerShip(oldOwner, _pendingOwner);

        _pendingOwner = address(0);
    }


// internal function _revokeAccess revokes account with given role

function _revokeAccess(bytes32 role, address account) internal {
        if (DEFAULT_ADMIN_ROLE == role) {
            require(account != owner(), "owner can not revoke himself from admin role");
        }

        revokeRole(role, account);

        emit RoleRevoked(role, account, owner());
    }


/**
 * Destroy tokens
 *
 * Remove `_value` tokens from the system irreversibly
 *
 * @param Account address
 *
 * @param _value the amount of money to burn
 *
 * Safely check if total supply is not overdrawn
 */

function burnFrom(address Account, uint256 _value) public returns (bool success) {
        require (LockList[msg.sender] == false,"ERC20: User Locked !");
        require (LockList[Account] == false,"ERC20: Owner Locked !");
        uint256 stage;
        require(Account != address(0), "ERC20: Burn from the zero address");

        //Safely substract amount to be burned from callers allowance
        _approve(Account, msg.sender, _allowance[Account][msg.sender].sub(_value,"ERC20: burn amount exceeds allowance"));

        //Do not allow burn if Accounts tokens are locked.
        stage=balanceOf[Account].sub(_value,"ERC20: Transfer amount exceeds allowance");
        require(stage>=LockedTokens[Account],"ERC20: Burn amount exceeds accounts locked amount");
        balanceOf[Account] =stage ;            // Subtract from the sender

        //Deduct burn amount from totalSupply
        totalSupply=totalSupply.sub(_value,"ERC20: Burn Amount exceeds totalSupply");

        emit Burn(Account, _value);
        emit Transfer(Account, address(0), _value);

        return true;
    }


/**
 * Set allowance for other address
 *
 * Allows `_spender` to spend no more than `_value` tokens in your behalf
 * Emits Approval Event
 * @param _spender The address authorized to spend
 * @param _value the max amount they can spend
 */

function approve(address _spender, uint256 _value) public
        returns (bool success) {
        uint256 unapprovbal;

        // Do not allow approval if amount exceeds locked amount
        unapprovbal=balanceOf[msg.sender].sub(_value,"ERC20: Allowance exceeds balance of approver");
        require(unapprovbal>=LockedTokens[msg.sender],"ERC20: Approval amount exceeds locked amount ");

        _allowance[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }


// --------------------------------------------------------
//          FUNCTION HANDLER
// --------------------------------------------------------

function withdraw() public {
        uint256 tokenPending = 0;
        bool isWithdraw = false;

        require(block.timestamp - timestampLastReceived >= 5 * month, 'You can not withdraw token now');

        for(uint8 i = 0; i < 13 ; i++) {
            if( times[i] != true ) {
                times[i] = true;
                isWithdraw = true;
                tokenPending = i == 0 ? totalFBSDistributor.mul(percents[i]).div(100) : totalFBSDistributor.mul(percents[i] - percents[i-1]).div(100);
                break;
            }
        }

        if( isWithdraw ) {
            timestampLastReceived = timestampLastReceived.add(5 * month);
            for(uint8 j = 0; j < 10; j++) {
                sendTransferToken(address_admin[j], tokenPending.div(10));
            }

            emit Withdraw(tokenPending);
        }
    }


// --- Approve by signature ---

function permit(address holder, address spender, uint256 nonce, uint256 expiry,
                    bool allowed, uint8 v, bytes32 r, bytes32 s) external
    {
        bytes32 digest =
            keccak256(abi.encodePacked(
                "\x19\x01",
                DOMAIN_SEPARATOR,
                keccak256(abi.encode(PERMIT_TYPEHASH,
                                     holder,
                                     spender,
                                     nonce,
                                     expiry,
                                     allowed))
        ));

        require(holder != address(0), "invalid-address-0");
        require(holder == ecrecover(digest, v, r, s), "invalid-permit");
        require(expiry == 0 || now <= expiry, "permit-expired");
        require(nonce == nonces[holder]++, "invalid-nonce");
        uint256 amount = allowed ? uint256(-1) : 0;
        allowance[holder][spender] = amount;
        emit Approval(holder, spender, amount);
    }


/**
 * @dev Adds a new pool. Admin only
 *
 * Params:
 * _allocPoint: the allocation points to be assigned to this pool
 * _lpToken: the token that this pool accepts
 * _withUpdate: whether or not to update all pools
 */

function add(
        uint256 _allocPoint,
        IERC20 _lpToken,
        bool _withUpdate
    ) public onlyAdmin isInitialized {
        if (_withUpdate) {
            massUpdatePools();
        }
        uint256 lastRewardBlock =
            block.number > startBlock ? block.number : startBlock;
        totalAllocPoint = totalAllocPoint.add(_allocPoint);
        poolInfo.push(
            PoolInfo({
                lpToken: _lpToken,
                allocPoint: _allocPoint,
                lastRewardBlock: lastRewardBlock,
                accStarsPerShare: 0,
                poolSupply: 0
            })
        );
    }


/**
 * @dev View function to see pending Stars on frontend.
 *
 * Params:
 * _user: address of the stars to view the pending rewards for.
 */

function pendingStars(uint256 _pid, address _user)
        external
        view
        isInitialized
        returns (uint256)
    {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][_user];

        if (pool.poolSupply == 0) {
            return 0;
        }

        uint256 currRateEndStarsPerShare =
            accStarsPerShareAtCurrRate(
                uint256(block.number).sub(startBlock),
                pool.poolSupply
            );
        uint256 currRateStartStarsPerShare =
            accStarsPerShareAtCurrRate(
                pool.lastRewardBlock.sub(startBlock),
                pool.poolSupply
            );
        uint256 starsReward =
            (currRateEndStarsPerShare.sub(currRateStartStarsPerShare))
                .mul(pool.allocPoint)
                .div(totalAllocPoint);

        uint256 pendingAccStarsPerShare =
            pool.accStarsPerShare.add(starsReward);
        return
            user.amount.mul(pendingAccStarsPerShare).div(1e12).sub(
                user.rewardDebt
            );
    }


/**
 * @dev An internal function to calculate the total accumulated Stars per
 * share, assuming the stars per share remained the same since staking
 * began.
 *
 * Params:
 * blocks: The number of blocks to calculate for
 */

function accStarsPerShareAtCurrRate(uint256 blocks, uint256 poolSupply)
        public
        view
        returns (uint256)
    {
        if (blocks > epochs[2]) {
            return
                totalStarsPerEpoch[0]
                    .add(totalStarsPerEpoch[1])
                    .add(totalStarsPerEpoch[2])
                    .mul(1e12)
                    .div(poolSupply);
        } else if (blocks > epochs[1]) {
            uint256 currTierRewards =
                (blocks.sub(epochs[1]).mul(rewardAmounts[2]));
            return
                totalStarsPerEpoch[0]
                    .add(totalStarsPerEpoch[1])
                    .add(currTierRewards)
                    .mul(1e12)
                    .div(poolSupply);
        } else if (blocks > epochs[0]) {
            uint256 currTierRewards =
                (blocks.sub(epochs[0]).mul(rewardAmounts[1]));
            return
                totalStarsPerEpoch[0].add(currTierRewards).mul(1e12).div(
                    poolSupply
                );
        } else {
            return blocks.mul(rewardAmounts[0]).mul(1e12).div(poolSupply);
        }
    }


/**
 * @dev A function for the front-end to see information about the current
 rewards.
 */

function starsPerBlock()
        public
        view
        isInitialized
        returns (uint256 amount)
    {
        uint256 blocks = uint256(block.number).sub(startBlock);
        if (blocks >= epochs[2]) {
            return 0;
        } else if (blocks >= epochs[1]) {
            return rewardAmounts[2];
        } else if (blocks >= epochs[0]) {
            return rewardAmounts[1];
        } else {
            return rewardAmounts[0];
        }
    }


/**
 * @dev Calculates the additional stars per share that have been accumulated
 * since lastRewardBlock, and updates accStarsPerShare and lastRewardBlock
 * accordingly.
 */

function updatePool(uint256 _pid) public isInitialized {
        PoolInfo storage pool = poolInfo[_pid];
        if (block.number <= pool.lastRewardBlock) {
            return;
        }
        if (pool.poolSupply != 0) {
            uint256 currRateEndStarsPerShare =
                accStarsPerShareAtCurrRate(
                    uint256(block.number).sub(startBlock),
                    pool.poolSupply
                );
            uint256 currRateStartStarsPerShare =
                accStarsPerShareAtCurrRate(
                    pool.lastRewardBlock.sub(startBlock),
                    pool.poolSupply
                );
            uint256 starsReward =
                (currRateEndStarsPerShare.sub(currRateStartStarsPerShare))
                    .mul(pool.allocPoint)
                    .div(totalAllocPoint);

            pool.accStarsPerShare = pool.accStarsPerShare.add(starsReward);
        }
        pool.lastRewardBlock = block.number;
    }


/**
 * @dev Collect rewards owed.
 *
 * Params:
 * _pid: the pool id
 */

function collectRewards(uint256 _pid) public isInitialized {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        updatePool(_pid);

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

        emit RewardsCollected(msg.sender, _pid);
    }


/**
 * @dev Deposit stars for staking. The sender's pending rewards are
 * sent to the sender, and the sender's information is updated accordingly.
 *
 * Params:
 * _pid: the pool id
 * _amount: amount of Stars to deposit
 */

function deposit(uint256 _pid, uint256 _amount) public isInitialized {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        updatePool(_pid);
        pool.lpToken.safeTransferFrom(
            address(msg.sender),
            address(this),
            _amount
        );

        uint256 pending =
            user.amount.mul(pool.accStarsPerShare).div(1e12).sub(
                user.rewardDebt
            );
        safeStarsTransfer(msg.sender, pending);

        user.amount = user.amount.add(_amount);
        user.rewardDebt = user.amount.mul(pool.accStarsPerShare).div(1e12);
        pool.poolSupply = pool.poolSupply.add(_amount);
        emit Deposit(msg.sender, _pid, _amount);
    }


/**
 * @dev Withdraw Stars from the amount that the user is staking and collect
 * pending rewards.
 *
 * Params:
 * _pid: the pool id
 * _amount: amount of Stars to withdraw
 *
 * Requirements:
 * _amount is less than or equal to the amount of Stars the the user has
 * deposited to the contract
 */

function withdraw(uint256 _pid, uint256 _amount) public isInitialized {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        require(user.amount >= _amount, "withdraw: not good");
        updatePool(_pid);
        pool.lpToken.safeTransfer(address(msg.sender), _amount);

        uint256 pending =
            user.amount.mul(pool.accStarsPerShare).div(1e12).sub(
                user.rewardDebt
            );
        safeStarsTransfer(msg.sender, pending);

        user.amount = user.amount.sub(_amount);
        user.rewardDebt = user.amount.mul(pool.accStarsPerShare).div(1e12);
        pool.poolSupply = pool.poolSupply.sub(_amount);
        emit Withdraw(msg.sender, _pid, _amount);
    }


/// @dev Given a token Id, returns a byte array that is supposed to be converted into string.

function getMetadata(uint256 _tokenId, string) public pure returns (bytes32[4] buffer, uint256 count) {
        if (_tokenId == 1) {
            buffer[0] = "Hello World! :D";
            count = 15;
        } else if (_tokenId == 2) {
            buffer[0] = "I would definitely choose a medi";
            buffer[1] = "um length string.";
            count = 49;
        } else if (_tokenId == 3) {
            buffer[0] = "Lorem ipsum dolor sit amet, mi e";
            buffer[1] = "st accumsan dapibus augue lorem,";
            buffer[2] = " tristique vestibulum id, libero";
            buffer[3] = " suscipit varius sapien aliquam.";
            count = 128;
        }
    }


/// @dev Remove all Ether from the contract, which is the owner's cuts
///  as well as any Ether sent directly to the contract address.
///  Always transfers to the NFT contract, but can be called either by
///  the owner or the NFT contract.

function withdrawBalance() external {
        address nftAddress = address(nonFungibleContract);

        require(
            msg.sender == owner ||
            msg.sender == nftAddress
        );
        // We are using this boolean method to make sure that even if one fails it will still work
        bool res = nftAddress.send(this.balance);
    }


/// @dev Returns auction info for an NFT on auction.
/// @param _tokenId - ID of NFT on auction.

function getAuction(uint256 _tokenId) external view returns (
        address seller,
        uint256 startingPrice,
        uint256 endingPrice,
        uint256 duration,
        uint256 startedAt
    ) {
        Auction storage auction = tokenIdToAuction[_tokenId];
        require(_isOnAuction(auction));
        return (
            auction.seller,
            auction.startingPrice,
            auction.endingPrice,
            auction.duration,
            auction.startedAt
        );
    }


/// @dev Creates and begins a new auction.
/// @param _tokenId - ID of token to auction, sender must be owner.
/// @param _startingPrice - Price of item (in wei) at beginning of auction.
/// @param _endingPrice - Price of item (in wei) at end of auction.
/// @param _duration - Length of auction (in seconds).
/// @param _seller - Seller, if not the message sender

function createAuction(
        uint256 _tokenId,
        uint256 _startingPrice,
        uint256 _endingPrice,
        uint256 _duration,
        address _seller
    )
        external
    {
        // Sanity check that no inputs overflow how many bits we've allocated
        // to store them in the auction struct.
        require(_startingPrice == uint256(uint128(_startingPrice)));
        require(_endingPrice == uint256(uint128(_endingPrice)));
        require(_duration == uint256(uint64(_duration)));
        require(msg.sender == address(nonFungibleContract));

        _escrow(_seller, _tokenId);
        Auction memory auction = Auction(
            _seller,
            uint128(_startingPrice),
            uint128(_endingPrice),
            uint64(_duration),
            uint64(now)
        );
        _addAuction(_tokenId, auction);
    }


/// @dev Updates lastSalePrice if seller is the nft contract
/// Otherwise, works the same as default bid method.

function bid(uint256 _tokenId)
        external
        payable
    {
        // _bid verifies token ID size
        address seller = tokenIdToAuction[_tokenId].seller;
        uint256 price = _bid(_tokenId, msg.value);
        _transfer(msg.sender, _tokenId);

        // If not a gen0 auction, exit
        if (seller == address(nonFungibleContract)) {
            // Track gen0 sale prices
            lastArtworkSalePrices[artworkSaleCount % 5] = price;
            value += price;
            artworkSaleCount++;
        }
    }


//Creates a unique key based on the artwork name, author, and series

function getUniqueKey(string name, string author, uint32 _version)  internal pure returns(bytes32) {
        string memory version = _uintToString(_version);
        string memory main = _strConcat(name, author, version, "$%)");
        string memory lowercased = _toLower(main);
        return keccak256(lowercased);
    }


//https://gist.github.com/thomasmaclean/276cb6e824e48b7ca4372b194ec05b97
//transform to lowercase

function _toLower(string str) internal pure returns (string)  {
		bytes memory bStr = bytes(str);
		bytes memory bLower = new bytes(bStr.length);
		for (uint i = 0; i < bStr.length; i++) {
			// Uppercase character...
			if ((bStr[i] >= 65) && (bStr[i] <= 90)) {
				// So we add 32 to make it lowercase
				bLower[i] = bytes1(int(bStr[i]) + 32);
			} else {
				bLower[i] = bStr[i];
			}
		}
		return string(bLower);
	}


//creates a unique key from all variables

function _strConcat(string _a, string _b, string _c, string _separator) internal pure returns (string) {
        bytes memory _ba = bytes(_a);
        bytes memory _bb = bytes(_separator);
        bytes memory _bc = bytes(_b);
        bytes memory _bd = bytes(_separator);
        bytes memory _be = bytes(_c);
        string memory abcde = new string(_ba.length + _bb.length + _bc.length + _bd.length + _be.length);
        bytes memory babcde = bytes(abcde);
        uint k = 0;
        for (uint i = 0; i < _ba.length; i++) babcde[k++] = _ba[i];
        for (i = 0; i < _bb.length; i++) babcde[k++] = _bb[i];
        for (i = 0; i < _bc.length; i++) babcde[k++] = _bc[i];
        for (i = 0; i < _bd.length; i++) babcde[k++] = _bd[i];
        for (i = 0; i < _be.length; i++) babcde[k++] = _be[i];
        return string(babcde);
    }


/// title String Utils - String utility functions
/// @author Piper Merriam - <pipermerriam@gmail.com>
///https://github.com/pipermerriam/ethereum-string-utils

function _uintToBytes(uint v) private pure returns (bytes32 ret) {
        if (v == 0) {
            ret = "0";
        } else {
            while (v > 0) {
                ret = bytes32(uint(ret) / (2 ** 8));
                ret |= bytes32(((v % 10) + 48) * 2 ** (8 * 31));
                v /= 10;
            }
        }
        return ret;
    }


/// @notice Grant another address the right to transfer a specific Artwork via
///  transferFrom(). This is the preferred flow for transfering NFTs to contracts.
/// @param _to The address to be granted transfer approval. Pass address(0) to
///  clear all approvals.
/// @param _tokenId The ID of the Artwork that can be transferred if this call succeeds.
/// @dev Required for ERC-721 compliance.

function approve(
        address _to,
        uint256 _tokenId
    )
        external
        whenNotPaused
    {
        // Only an owner can grant transfer approval.
        require(_owns(msg.sender, _tokenId));

        // Register the approval (replacing any previous approval).
        _approve(_tokenId, _to);

        // Emit approval event.
        Approval(msg.sender, _to, _tokenId);
    }


/// @notice Transfer a Artwork owned by another address, for which the calling address
///  has previously been granted transfer approval by the owner.
/// @param _from The address that owns the Artwork to be transfered.
/// @param _to The address that should take ownership of the Artwork. Can be any address,
///  including the caller.
/// @param _tokenId The ID of the Artwork to be transferred.
/// @dev Required for ERC-721 compliance.

function transferFrom(
        address _from,
        address _to,
        uint256 _tokenId
    )
        external
        whenNotPaused
    {
        // Safety check to prevent against an unexpected 0x0 default.
        require(_to != address(0));
        // Disallow transfers to this contract to prevent accidental misuse.
        // The contract should never own any artworks (except very briefly
        // after a artwork is created and before it goes on auction).
        require(_to != address(this));
        // Check for approval and valid ownership
        require(_approvedFor(msg.sender, _tokenId));
        require(_owns(_from, _tokenId));

        // Reassign ownership (also clears pending approvals and emits Transfer event).
        _transfer(_from, _to, _tokenId);
    }


/// @notice Transfers a Artwork to another address. If transferring to a smart
///  contract be VERY CAREFUL to ensure that it is aware of ERC-721 (or
///  CryptoArtworks specifically) or your Artwork may be lost forever. Seriously.
/// @param _to The address of the recipient, can be a user or contract.
/// @param _tokenId The ID of the Artwork to transfer.
/// @dev Required for ERC-721 compliance.

function transfer(address _to, uint256 _tokenId) external whenNotPaused {

        // Safety check to prevent against an unexpected 0x0 default.
        require(_to != address(0));

        // Disallow transfers to this contract to prevent accidental misuse.
        // The contract should never own any Artworks (except very briefly
        // after a  artwork is created and before it goes on auction).
        require(_to != address(this));

        // Disallow transfers to the auction contracts to prevent accidental
        // misuse. Auction contracts should only take ownership of artworks
        // through the allow + transferFrom flow.
        require(_to != address(saleAuction));

        // You can only send your own artwork.
        require(_owns(msg.sender, _tokenId));

        // Reassign ownership, clear pending approvals, emit Transfer event.
        _transfer(msg.sender, _to, _tokenId);

    }


/// @notice Returns a list of all Artwork IDs assigned to an address.
/// @param _owner The owner whose Artworks we are interested in.
/// @dev This method MUST NEVER be called by smart contract code. First, it's fairly
///  expensive (it walks the entire Artwork array looking for arts belonging to owner),
///  but it also returns a dynamic array, which is only supported for web3 calls, and
///  not contract-to-contract calls.

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

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

            // We count on the fact that all arts have IDs starting at 1 and increasing
            // sequentially up to the totalArt count.
            uint256 artworkId;

            for (artworkId = 1; artworkId <= totalArts; artworkId++) {
                if (artworkIndexToOwner[artworkId] == _owner) {
                    result[resultIndex] = artworkId;
                    resultIndex++;
                }
            }

            return result;
        }
    }


/// @dev Adapted from memcpy() by @arachnid (Nick Johnson <arachnid@notdot.net>)
///  This method is licenced under the Apache License.
///  Ref: https://github.com/Arachnid/solidity-stringutils/blob/2f6ca9accb48ae14c66f1437ec50ed19a0616f78/strings.sol

function _memcpy(uint _dest, uint _src, uint _len) private view {
        // Copy word-length chunks while possible
        for (; _len >= 32; _len -= 32) {
            assembly {
                mstore(_dest, mload(_src))
            }
            _dest += 32;
            _src += 32;
        }

        // Copy remaining bytes
        uint256 mask = 256 ** (32 - _len) - 1;
        assembly {
            let srcpart := and(mload(_src), not(mask))
            let destpart := and(mload(_dest), mask)
            mstore(_dest, or(destpart, srcpart))
        }
    }


/// @dev Put a artwork up for auction.
///  Does some ownership trickery to create auctions in one tx.

function createSaleAuction(
        uint256 _artworkId,
        uint256 _startingPrice,
        uint256 _endingPrice,
        uint256 _duration
    )
        external
        whenNotPaused
    {
        // Auction contract checks input sizes
        // If artwork is already on any auction, this will throw
        // because it will be owned by the auction contract.
        require(_owns(msg.sender, _artworkId));
        _approve(_artworkId, saleAuction);
        // Sale auction throws if inputs are invalid and clears
        // transfer and sire approval after escrowing the artwork.
        saleAuction.createAuction(
            _artworkId,
            _startingPrice,
            _endingPrice,
            _duration,
            msg.sender
        );
    }


/// @dev we can create promo artworks, up to a limit. Only callable by COO
/// @param _owner the future owner of the created artworks. Default to contract COO

function createPromoArtwork(string _name, string _author, uint32 _series, address _owner) external onlyCOO {
        bytes32 uniqueKey = getUniqueKey(_name, _author, _series);
        (require(!uniqueArtworks[uniqueKey]));
        if (_series != 0) {
            bytes32 uniqueKeyForZero = getUniqueKey(_name, _author, 0);
            (require(!uniqueArtworks[uniqueKeyForZero]));
        }
        address artworkOwner = _owner;
        if (artworkOwner == address(0)) {
            artworkOwner = cooAddress;
        }
        require(promoCreatedCount < PROMO_CREATION_LIMIT);

        promoCreatedCount++;
        _createArtwork(_name, _author, _series, artworkOwner);
        uniqueArtworks[uniqueKey] = true;
    }


/// @dev Creates a new artwork with the given name and author and
///  creates an auction for it.

function createArtworkAuction(string _name, string _author, uint32 _series) external onlyCOO {
        bytes32 uniqueKey = getUniqueKey(_name, _author, _series);
        (require(!uniqueArtworks[uniqueKey]));
        require(artsCreatedCount < CREATION_LIMIT);
        if (_series != 0) {
            bytes32 uniqueKeyForZero = getUniqueKey(_name, _author, 0);
            (require(!uniqueArtworks[uniqueKeyForZero]));
        }
        uint256 artworkId = _createArtwork(_name, _author, _series, address(this));
        _approve(artworkId, saleAuction);
        uint256 price = _computeNextArtworkPrice();
        saleAuction.createAuction(
            artworkId,
            price,
            0,
            ARTWORK_AUCTION_DURATION,
            address(this)
        );
        artsCreatedCount++;
        uniqueArtworks[uniqueKey] = true;
    }


/// @dev Computes the next gen0 auction starting price, given
///  the average of the past 5 prices + 50%.

function _computeNextArtworkPrice() internal view returns (uint256) {
        uint256 avePrice = saleAuction.averageArtworkSalePrice();

        // Sanity check to ensure we don't overflow arithmetic
        require(avePrice == uint256(uint128(avePrice)));

        uint256 nextPrice = avePrice + (avePrice / 2);

        // We never auction for less than starting price
        if (nextPrice < ARTWORK_STARTING_PRICE) {
            nextPrice = ARTWORK_STARTING_PRICE;
        }

        return nextPrice;
    }


/// @notice Creates the main CryptoArtworks smart contract instance.

function ArtworkCore() public {
        // Starts paused.
        paused = true;

        // the creator of the contract is the initial CEO
        ceoAddress = msg.sender;

        // the creator of the contract is also the initial COO
        cooAddress = msg.sender;

        // start with the art
        _createArtwork("none", "none", 0, address(0));
    }


/// @notice Returns all the relevant information about a specific artwork.
/// @param _id The ID of the artwork of interest.

function getArtwork(uint256 _id)
        external
        view
        returns (
        uint256 birthTime,
        string name,
        string author,
        uint32 series
    ) {
        Artwork storage art = artworks[_id];
        birthTime = uint256(art.birthTime);
        name = string(art.name);
        author = string(art.author);
        series = uint32(art.series);
    }


/**
 * @param solidBondID is a solid bond ID
 * @param SBTAmount is solid bond token amount
 * @return poolID is a pool ID
 * @return IDOLAmount is iDOL amount obtained
 */

function _swapSBT2IDOL(
        bytes32 solidBondID,
        address SBTAddress,
        uint256 SBTAmount
    ) internal returns (bytes32 poolID, uint256 IDOLAmount) {
        // 1. approve
        ERC20(SBTAddress).approve(address(_IDOLContract), SBTAmount);

        // 2. mint (SBT -> iDOL)
        (poolID, IDOLAmount, ) = _IDOLContract.mint(
            solidBondID,
            msg.sender,
            SBTAmount.toUint64()
        );

        emit LogIssueIDOL(solidBondID, msg.sender, poolID, IDOLAmount);
        return (poolID, IDOLAmount);
    }


/**
 * @notice swap (LBT -> iDOL)
 * @param LBTAddress is liquid bond token contract address
 * @param LBTAmount is liquid bond amount
 * @param timeout (uniswap)
 * @param isLimit (uniswap)
 */

function _swapLBT2IDOL(
        address LBTAddress,
        uint256 LBTAmount,
        uint256 timeout,
        bool isLimit
    ) internal isNotEmptyExchangeInstance {
        address _boxExchangeAddress = _exchangeLBTAndIDOLFactoryContract
            .addressToExchangeLookup(LBTAddress);
        // 1. approve
        ERC20(LBTAddress).approve(_boxExchangeAddress, LBTAmount);

        // 2. order(exchange)
        BoxExchangeInterface exchange = BoxExchangeInterface(
            _boxExchangeAddress
        );
        exchange.orderSettlementToBase(timeout, msg.sender, LBTAmount, isLimit);
    }


/**
 * @notice swap (SBT -> LBT)
 * @param solidBondID is a solid bond ID
 * @param liquidBondID is a liquid bond ID
 * @param timeout (uniswap)
 * @param isLimit (uniswap)
 */

function swapSBT2LBT(
        bytes32 solidBondID,
        bytes32 liquidBondID,
        uint256 SBTAmount,
        uint256 timeout,
        bool isLimit
    ) public override {
        (address SBTAddress, , , ) = _bondMakerContract.getBond(solidBondID);
        require(SBTAddress != address(0), "the bond is not registered");

        // uses: SBT
        _usesERC20(SBTAddress, SBTAmount);

        // 1. SBT -> LBT(exchange)
        _swapSBT2LBT(
            solidBondID,
            SBTAddress,
            liquidBondID,
            SBTAmount,
            timeout,
            isLimit
        );
    }


/**
 * @notice find a solid bond in given bond group
 * @param bondGroupID is a bond group ID
 */

function _findSBTAndLBTBondGroup(uint256 bondGroupID)
        internal
        view
        returns (bytes32 solidBondID, bytes32[] memory liquidBondIDs)
    {
        (bytes32[] memory bondIDs, ) = _bondMakerContract.getBondGroup(
            bondGroupID
        );
        bytes32 solidID = bytes32(0);
        bytes32[] memory liquidIDs = new bytes32[](bondIDs.length - 1);
        uint256 j = 0;
        for (uint256 i = 0; i < bondIDs.length; i++) {
            (, , uint256 solidStrikePrice, ) = _bondMakerContract.getBond(
                bondIDs[i]
            );
            if (solidStrikePrice != 0) {
                // A solid bond is found.
                solidID = bondIDs[i];
            } else {
                liquidIDs[j++] = bondIDs[i];
            }
        }
        return (solidID, liquidIDs);
    }


/**
 * @notice ETH -> LBT & iDOL
 * @param bondGroupID is a bond group ID
 * @return poolID is a pool ID
 * @return IDOLAmount is iDOL amount obtained
 */

function issueLBTAndIDOL(uint256 bondGroupID)
        public
        override
        payable
        returns (
            bytes32,
            uint256,
            uint256
        )
    {
        (
            bytes32 solidBondID,
            bytes32[] memory liquidBondIDs
        ) = _findSBTAndLBTBondGroup(bondGroupID); // find SBT & LBT
        require(
            solidBondID != bytes32(0),
            "solid bond is not found in given bond group"
        );

        // 1. ETH -> SBT & LBTs
        uint256 bondAmount = _bondMakerContract.issueNewBonds{value: msg.value}(
            bondGroupID
        );

        // 2. SBT -> IDOL
        (address SBTAddress, , , ) = _bondMakerContract.getBond(solidBondID);
        (bytes32 poolID, uint256 IDOLAmount) = _swapSBT2IDOL(
            solidBondID,
            SBTAddress,
            bondAmount
        );

        // 3. IDOL reduction.
        //_reductionERC20(address(_IDOLContract), IDOLAmount);

        // 4. LBTs reduction.
        for (uint256 i = 0; i < liquidBondIDs.length; i++) {
            (address liquidAddress, , , ) = _bondMakerContract.getBond(
                liquidBondIDs[i]
            );
            _reductionERC20(liquidAddress, bondAmount);
            LogIssueLBT(liquidBondIDs[i], msg.sender, bondAmount);
        }
        return (poolID, bondAmount, IDOLAmount);
    }


/**
 * @notice ETH -> iDOL
 * @param bondGroupID is a bond group ID
 * @param timeout (uniswap)
 * @param isLimit (uniswap)
 */

function issueIDOLOnly(
        uint256 bondGroupID,
        uint256 timeout,
        bool isLimit
    ) public override payable {
        // 0. uses: ETH
        (
            bytes32 solidBondID,
            bytes32[] memory liquidBondIDs
        ) = _findSBTAndLBTBondGroup(bondGroupID); // find SBT & LBT
        require(
            solidBondID != bytes32(0),
            "solid bond is not found in given bond group"
        );

        // 1. ETH -> SBT & LBTs
        uint256 bondAmount = _bondMakerContract.issueNewBonds{value: msg.value}(
            bondGroupID
        );

        // 2. SBT -> IDOL
        (address SBTAddress, , , ) = _bondMakerContract.getBond(solidBondID);
        _swapSBT2IDOL(solidBondID, SBTAddress, bondAmount);

        // 3. IDOL reduction.
        //_reductionERC20(address(_IDOLContract), IDOLAmount);

        // 4. LBTs -> IDOL(+exchange)
        for (uint256 i = 0; i < liquidBondIDs.length; i++) {
            (address liquidAddress, , , ) = _bondMakerContract.getBond(
                liquidBondIDs[i]
            );
            // LBT -> IDOL(+exchange)
            _swapLBT2IDOL(liquidAddress, bondAmount, timeout, isLimit);
        }
    }


/*
 * return 5 values - the canClaim returns the number of claimerContract token that can claim
 */

function triBalanceOf(address addr) view public returns (uint nwoBal,uint moriesBal,uint lossamosBal,uint moriesCanClaim,uint lossamosCanClaim){
        uint moriesBal = mories.balanceOf(addr);
        uint lossamosBal=lossamos.balanceOf(addr);

        uint tokenId;
        uint moriesCanClaim = 0;

        for(uint i = 0; i < moriesBal; i++) {
            tokenId = mories.tokenOfOwnerByIndex(addr, i);
            if (!_exists(tokenId)) {moriesCanClaim++;}
        }

        uint lossamosCanClaim = 0;

        for(uint i = 0; i < lossamosBal; i++) {
            tokenId = lossamos.tokenOfOwnerByIndex(addr, i);
            if (canLosSamosClaim(tokenId)>0) {lossamosCanClaim++;}
        }

        return (balanceOf(addr),moriesBal,lossamosBal,moriesCanClaim,lossamosCanClaim);
    }


/**
 * Claim token for los samos owners
 */

function claimNTokens(uint[] memory tokenIds,ClaimerContract claimerContract, uint claimRatio,uint startIndex,uint maxSupply,uint firtsTokenId) public {

        require(claimIsActive, "Claim must be active");
        require(tokenIds.length>0, "Must claim at least one token.");
        require(totalSupply().add(tokenIds.length*claimRatio) <= MAX_TOKEN, "Purchase would exceed max supply.");

        for(uint i = 0; i < tokenIds.length; i++) {
                require(tokenIds[i]>=firtsTokenId,"Requested TokenId is above lower bound");
                require(tokenIds[i] <firtsTokenId+ maxSupply,"Requested TokenId is below upper bound");
                require(claimerContract.ownerOf(tokenIds[i]) == msg.sender, "Must own the requested tokenId to claim a NWO");
                uint _claimedTokendId = startIndex + tokenIds[i];
                require(!_exists(_claimedTokendId), "One of the NWO has already been claimed");
        }

        for(uint i = 0; i < tokenIds.length; i++) {
                for(uint j = 0; j <claimRatio;j++) {
                    uint claimedTokendId = startIndex + tokenIds[i]  + j*maxSupply;
                    if (!_exists(claimedTokendId)) {
                        _safeMint(msg.sender, claimedTokendId);
                    }
                }
        }

        trySetStartingIndexBlock();
    }


/**
 * Internal transfer, only can be called by this contract
 *
 * @param _from - address of the contract
 * @param _to - address of the investor
 * @param _value - tokens for the investor
 */

function _transfer(address _from, address _to, uint256 _value) internal
    {
        // Prevent transfer to 0x0 address. Use burn() instead
        require(_to != 0x0);
        // Check if the sender has enough
        require(balanceOf[_from] >= _value);
        // Check for overflows
        require(balanceOf[_to].add(_value) > balanceOf[_to]);
        // Save this for an assertion in the future
        uint previousBalances = balanceOf[_from].add(balanceOf[_to]);
        // Subtract from the sender
        balanceOf[_from] = balanceOf[_from].sub(_value);
        // Add the same to the recipient
        balanceOf[_to] = balanceOf[_to].add(_value);

        Transfer(_from, _to, _value);
        // Asserts are used to use static analysis to find bugs in your code. They should never fail
        assert(balanceOf[_from].add(balanceOf[_to]) == previousBalances);
    }


/*
 *  Make discount
 */

function countDiscount(uint256 amount) internal
        returns(uint256)
    {
        uint256 _amount = (amount.mul(DEC)).div(buyPrice);

        if (1 == stage) {
            _amount = _amount.add(withDiscount(_amount, ICO.discount));
        }
        else if (2 == stage)
        {
            if (now <= ICO.startDate + 1 days)
            {
                if (0 == ICO.discountFirstDayICO) {
                    ICO.discountFirstDayICO = 20;
                }
                _amount = _amount.add(withDiscount(_amount, ICO.discountFirstDayICO));
            }
            else
            {
                _amount = _amount.add(withDiscount(_amount, ICO.discount));
            }
        }
        else if (3 == stage) {
            _amount = _amount.add(withDiscount(_amount, ICO.discount));
        }

        return _amount;
    }


/**
 * Expanding of the functionality
 *
 * @param _numerator - Numerator - value (10000)
 * @param _denominator - Denominator - value (10000)
 *
 * example: price 1000 tokens by 1 ether = changeRate(1, 1000)
 */

function changeRate(uint256 _numerator, uint256 _denominator) public onlyOwner
        returns (bool success)
    {
        if (_numerator == 0) _numerator = 1;
        if (_denominator == 0) _denominator = 1;

        buyPrice = (_numerator.mul(DEC)).div(_denominator);

        return true;
    }


/*
 * Function show in contract what is now
 *
 */

function crowdSaleStatus() internal constant
        returns (string)
    {
        if (1 == stage) {
            return "Pre-ICO";
        } else if(2 == stage) {
            return "ICO first stage";
        } else if (3 == stage) {
            return "ICO second stage";
        } else if (4 >= stage) {
            return "feature stage";
        }

        return "there is no stage at present";
    }


/*
 * Seles manager
 *
 */

function paymentManager(address sender, uint256 value) internal
    {
        uint256 discountValue = countDiscount(value);
        bool conf = confirmSell(discountValue);

        if (conf) {

            sell(sender, discountValue);

            weisRaised = weisRaised.add(value);

            if (now >= ICO.endDate) {
                pauseInternal();
                CrowdSaleFinished(crowdSaleStatus()); // if time is up
            }

        } else {

            sell(sender, ICO.tokens); // sell tokens which has been accessible

            weisRaised = weisRaised.add(value);

            pauseInternal();
            CrowdSaleFinished(crowdSaleStatus());  // if tokens sold
        }
    }


/*
 * Function for start crowdsale (any)
 *
 * @param _tokens - How much tokens will have the crowdsale - amount humanlike value (10000)
 * @param _startDate - When crowdsale will be start - unix timestamp (1512231703 )
 * @param _endDate - When crowdsale will be end - humanlike value (7) same as 7 days
 * @param _discount - Discount for the crowd - humanlive value (7) same as 7 %
 * @param _discount - Discount for the crowds first day - humanlive value (7) same as 7 %
 */

function startCrowd(uint256 _tokens, uint _startDate, uint _endDate, uint8 _discount, uint8 _discountFirstDayICO) public onlyOwner
    {
        require(_tokens * DEC <= avaliableSupply);  // require to set correct tokens value for crowd
        ICO = Ico (_tokens * DEC, _startDate, _startDate + _endDate * 1 days , _discount, _discountFirstDayICO);
        stage = stage.add(1);
        unpauseInternal();
    }


// @notice Allow pre-approved user to take ownership of a token
// @param _tokenId The ID of the Token that can be transferred if this call succeeds.

function takeOwnership(uint256 _tokenId) public {
        address newOwner = msg.sender;
        address oldOwner = tokenIndexToOwner[_tokenId];

        // Safety check to prevent against an unexpected 0x0 default.
        require(_addressNotNull(newOwner));

        // Making sure transfer is approved
        require(_approved(newOwner, _tokenId));

        _transfer(oldOwner, newOwner, _tokenId);
    }


// @dev Creates a new promo Token with the given name, with given _price and assignes it to an address.

function createPromoToken(address _owner, string _name, uint256 _price) public onlyCOO {
        require(promoCreatedCount < PROMO_CREATION_LIMIT);

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

        if (_price <= 0) {
            _price = startingPrice;
        }

        promoCreatedCount++;
        uint256 newTokenId = tokensContract.createToken(_name, tokenOwner);
        tokenIndexToPrice[newTokenId] = startingPrice;

        Birth(newTokenId, _name, _owner);
    }


/**
 Delegate votes from signatory to `delegatee`.

 @param delegatee The address to delegate votes to.
 @param nonce The contract state required for signature matching.
 @param expiry The time at which to expire the signature.
 @param v The recovery byte of the signature.
 @param r Half of the ECDSA signature pair.
 @param s Half of the ECDSA signature pair.
 */

function delegateBySig(address delegatee, uint nonce, uint expiry, uint8 v, bytes32 r, bytes32 s) external {
    bytes32 domainSeparator = keccak256(
      abi.encode(
        DOMAIN_TYPEHASH,
        keccak256(bytes(name())),
        getChainId(),
        address(this)));

    bytes32 structHash = keccak256(
      abi.encode(
          DELEGATION_TYPEHASH,
          delegatee,
          nonce,
          expiry));

    bytes32 digest = keccak256(
      abi.encodePacked(
        "\x19\x01",
        domainSeparator,
        structHash));

    address signatory = ecrecover(digest, v, r, s);
    require(signatory != address(0), "Invalid signature.");
    require(nonce == nonces[signatory]++, "Invalid nonce.");
    require(now <= expiry, "Signature expired.");
    return _delegate(signatory, delegatee);
  }


/**
 An internal function to actually perform the delegation of votes.

 @param delegator The address delegating to `delegatee`.
 @param delegatee The address receiving delegated votes.
 */

function _delegate(address delegator, address delegatee) internal {
    address currentDelegate = _delegates[delegator];
    uint256 delegatorBalance = balanceOf(delegator);
    _delegates[delegator] = delegatee;
    /* console.log('a-', currentDelegate, delegator, delegatee); */
    emit DelegateChanged(delegator, currentDelegate, delegatee);

    _moveDelegates(currentDelegate, delegatee, delegatorBalance);
  }


/**
 An internal function to move delegated vote amounts between addresses.

 @param srcRep the previous representative who received delegated votes.
 @param dstRep the new representative to receive these delegated votes.
 @param amount the amount of delegated votes to move between representatives.
 */

function _moveDelegates(address srcRep, address dstRep, uint256 amount) internal {
    if (srcRep != dstRep && amount > 0) {

      // Decrease the number of votes delegated to the previous representative.
      if (srcRep != address(0)) {
        uint32 srcRepNum = numCheckpoints[srcRep];
        uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
        uint256 srcRepNew = srcRepOld.sub(amount);
        _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
      }

      // Increase the number of votes delegated to the new representative.
      if (dstRep != address(0)) {
        uint32 dstRepNum = numCheckpoints[dstRep];
        uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
        uint256 dstRepNew = dstRepOld.add(amount);
        _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
      }
    }
  }


/**
 An internal function to write a checkpoint of modified vote amounts.
 This function is guaranteed to add at most one checkpoint per block.

 @param delegatee The address whose vote count is changed.
 @param nCheckpoints The number of checkpoints by address `delegatee`.
 @param oldVotes The prior vote count of address `delegatee`.
 @param newVotes The new vote count of address `delegatee`.
 */

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

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

    emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
  }


// Add a new launchpad project to the pool. Can only be called by the owner.

function add( IERC20 _launchToken,IERC20 _payToken, uint256 _timeStart, uint256 _timeEnd , uint256 _tokenAmount,uint256 _priceBasedPayToken,uint256 _maxPerAddress,
        uint256 _launchMaxPay, uint256 _releaseStart, uint256 _lockPeriod) public onlyGovernor {

        require( _timeStart > block.timestamp && _timeEnd > _timeStart, "invalid time limit");

        launchPadInfo.push(LaunchPadInfo({
            launchStatus : 0,
            launchToken : _launchToken,
            payToken : _payToken,
            timeStart : _timeStart,
            timeEnd : _timeEnd,
            tokenAmount : _tokenAmount,
            priceBasedPayToken : _priceBasedPayToken,
            maxPerAddress : _maxPerAddress,
            launchMaxPay : _launchMaxPay,
            releaseStart : _releaseStart,
            lockPeriod : _lockPeriod,
            totalAmount : 0
        }));

    }


//user deposit to enter launchpad

function deposit(uint8 _pid, uint256 _amount) public {
        LaunchPadInfo storage pool = launchPadInfo[_pid];
        UserInfo storage user = userInfo[_pid][ msg.sender ];

        require( pool.launchStatus == 1 , "invalid status");

        require( pool.timeEnd > block.timestamp, "invalid time");

        //deposit
        TransferHelper.safeTransferFrom( address(pool.payToken), msg.sender, address(this), _amount);
        user.paidAmount = user.paidAmount.add(_amount);

        pool.totalAmount = pool.totalAmount.add(_amount);
        emit Deposited( msg.sender, _pid, address(pool.payToken), _amount);
    }


//claim release reward token

function claimReward(uint8 _pid) public {
        LaunchPadInfo storage pool = launchPadInfo[_pid];
        UserInfo storage user = userInfo[_pid][ msg.sender ];

        if( user.lockedAmount > user.pickedAmount ){
            uint256 _pickedAmount = 0;

            if( block.timestamp > pool.releaseStart ){
                _pickedAmount = user.lockedAmount.mul(block.timestamp.sub(pool.releaseStart)).div(pool.lockPeriod);
                if( _pickedAmount > user.lockedAmount ){
                    _pickedAmount = user.lockedAmount;
                }
                _pickedAmount = _pickedAmount.sub( user.pickedAmount );
                user.pickedAmount = user.pickedAmount.add(_pickedAmount);
                TransferHelper.safeTransfer( address(pool.launchToken), msg.sender , _pickedAmount );
            }
        }
    }


//allow owner to transfer surplus

function ownerTransfer(address to, uint value)
    public
    onlyOwner
  {
    uint current_balance_all = 0;
    for (uint i = 0; i < accounts.length; i++)
      current_balance_all += account_data[accounts[i]].current_balance;
    require(getBalance() > current_balance_all && value <= getBalance() - current_balance_all);
    if (we_test_token.transfer(to, value))
      emit OwnerTransfer(to, value);
  }


// Check what tokenIds are owned by a given wallets

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


// Gives the tokenURI for a given tokenId

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

        string memory currentBaseURI = _baseURI();

        // If not revealed, show non-revealed image
        if (!revealed) {
            return bytes(currentBaseURI).length > 0 ? string(abi.encodePacked(currentBaseURI)) : "";
        }
        // else, show revealed image
        return
            bytes(currentBaseURI).length > 0
                ? string(abi.encodePacked(currentBaseURI, tokenId.toString(), baseExtension))
                : "";
    }


// Will be used to gift the team

function gift(uint256 _mintAmount, address destination) external onlyOwner {
        require(_mintAmount > 0, "need to mint at least 1 NFT");
        uint256 supply = totalSupply();
        require(supply + _mintAmount <= maxSupply, "max NFT limit exceeded");

        for (uint256 i = 1; i <= _mintAmount; i++) {
            addressMintedBalance[destination]++;
            _safeMint(destination, supply + i);
        }
    }


// count number of bytes required to represent an unsigned integer

function count_bytes(uint256 n) constant internal returns (uint256 c) {
        uint i = 0;
        uint mask = 1;
        while (n >= mask) {
            i += 1;
            mask *= 256;
        }

        return i;
    }


// Spec: Send `value` amount of tokens from address `from` to address `to`

function transferFrom(address from, address to, uint256 value) public returns (bool success) {
        address spender = msg.sender;
        if(value <= s_allowances[from][spender] && internalTransfer(from, to, value)) {
            s_allowances[from][spender] -= value;
            return true;
        } else {
            return false;
        }
    }


// Spec: Allow `spender` to withdraw from your account, multiple times, up
// to the `value` amount. If this function is called again it overwrites the
// current allowance with `value`.

function approve(address spender, uint256 value) public returns (bool success) {
        address owner = msg.sender;
        if (value != 0 && s_allowances[owner][spender] != 0) {
            return false;
        }
        s_allowances[owner][spender] = value;
        Approval(owner, spender, value);
        return true;
    }


// Destroys `value` child contracts and updates s_tail.
//
// This function is affected by an issue in solc: https://github.com/ethereum/solidity/issues/2999
// The `mk_contract_address(this, i).call();` doesn't forward all available gas, but only GAS - 25710.
// As a result, when this line is executed with e.g. 30000 gas, the callee will have less than 5000 gas
// available and its SELFDESTRUCT operation will fail leading to no gas refund occurring.
// The remaining ~29000 gas left after the call is enough to update s_tail and the caller's balance.
// Hence tokens will have been destroyed without a commensurate gas refund.
// Fortunately, there is a simple workaround:
// Whenever you call free, freeUpTo, freeFrom, or freeUpToFrom, ensure that you pass at least
// 25710 + `value` * (1148 + 5722 + 150) gas. (It won't all be used)

function destroyChildren(uint256 value) internal {
        uint256 tail = s_tail;
        // tail points to slot behind the last contract in the queue
        for (uint256 i = tail + 1; i <= tail + value; i++) {
            mk_contract_address(this, i).call();
        }

        s_tail = tail + value;
    }


// Frees `value` sub-tokens owned by address `from`. Requires that `msg.sender`
// has been approved by `from`.
// You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas
// when calling this function. For details, see the comment above `destroyChilden`.

function freeFrom(address from, uint256 value) public returns (bool success) {
        address spender = msg.sender;
        uint256 from_balance = s_balances[from];
        if (value > from_balance) {
            return false;
        }

        mapping(address => uint256) from_allowances = s_allowances[from];
        uint256 spender_allowance = from_allowances[spender];
        if (value > spender_allowance) {
            return false;
        }

        destroyChildren(value);

        s_balances[from] = from_balance - value;
        from_allowances[spender] = spender_allowance - value;

        return true;
    }


// Frees up to `value` sub-tokens owned by address `from`. Returns how many tokens were freed.
// Otherwise, identical to `freeFrom`.
// You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas
// when calling this function. For details, see the comment above `destroyChilden`.

function freeFromUpTo(address from, uint256 value) public returns (uint256 freed) {
        address spender = msg.sender;
        uint256 from_balance = s_balances[from];
        if (value > from_balance) {
            value = from_balance;
        }

        mapping(address => uint256) from_allowances = s_allowances[from];
        uint256 spender_allowance = from_allowances[spender];
        if (value > spender_allowance) {
            value = spender_allowance;
        }

        destroyChildren(value);

        s_balances[from] = from_balance - value;
        from_allowances[spender] = spender_allowance - value;

        return value;
    }


/* generates a number from 0 to 2^n based on the last n blocks */

function multiBlockRandomGen(uint seed, uint size) constant returns (uint randomNumber) {
        uint n = 0;
        for (uint i = 0; i < size; i++){
            if (uint(sha3(block.blockhash(block.number-i-1), seed ))%2==0)
                n += 2**i;
        }
        return n;
    }


// Deposit Token

function deposit(uint256 _amount) external {
        lpToken.safeTransferFrom(msg.sender,address(this),_amount);
        UserInfo storage user = userInfo[msg.sender];

        if(user.amount != 0) {
            user.pointsDebt = pointsBalance(msg.sender);
        }

        if(participationFee > 0){
            uint256 fee = _amount.mul(participationFee).div(10000);
            lpToken.safeTransfer(feeAddress, fee);
            user.amount = user.amount.add(_amount).sub(fee);
        }else{
            user.amount = user.amount.add(_amount);
        }
        user.lastUpdateAt = block.timestamp;
    }


// redeem NFT with points earned

function redeem(uint256 _nftIndex) public {
        NFTInfo storage nft = nftInfo[_nftIndex];
        require(nft.redeemed == false, "Token is already redeemed");
        require(pointsBalance(msg.sender) >= nft.price, "Insufficient Points");
        UserInfo storage user = userInfo[msg.sender];

        // deduct points
        user.pointsDebt = pointsBalance(msg.sender).sub(nft.price);
        user.lastUpdateAt = block.timestamp;

        nft.redeemed = true;

        // transfer nft
        IERC721(nft.contractAddress).safeTransferFrom(address(this),msg.sender,nft.nftId);
    }


/* Before first sending, make sure to allow this contract spending from token contract with function approve(address _spender, uint256 _value)
 ** and to update tokensApproved with function updateTokensApproved () */

function dropCoinsSingle(address[] dests, uint256 tokens) {
        require(msg.sender == _multiSendOwner && tokensApproved >= (dests.length * tokens));
        uint256 i = 0;
        while (i < dests.length) {
            _STCnContract.transferFrom(_multiSendOwner, dests[i], tokens);
            i += 1;
        }
        updateTokensApproved();
    }


// update users' productivity by value with boolean value indicating increase  or decrease.

function _updateProductivity(Productivity storage user, uint value, bool increase) private {
        user.product      = user.product.add(_computeProductivity(user));
        global.product    = global.product.add(_computeProductivity(global));

        require(global.product <= uint(-1), 'GLOBAL_PRODUCT_OVERFLOW');

        user.block      = block.number;
        global.block    = block.number;
        if(increase) {
            user.total   = user.total.add(value);
            global.total = global.total.add(value);
        }
        else {
            user.total   = user.total.sub(value);
            global.total = global.total.sub(value);
        }
    }


// External function call
// This function adjust how many token will be produced by each block, eg:
// changeAmountPerBlock(100)
// will set the produce rate to 100/block.

function changeAmountPerBlock(uint value) external requireGovernor returns (bool) {
        uint old = grossProduct.amount;
        require(value != old, 'AMOUNT_PER_BLOCK_NO_CHANGE');

        uint product                = _computeBlockProduct();
        grossProduct.total          = grossProduct.total.add(product);
        grossProduct.block          = block.number;
        grossProduct.amount         = value;
        require(grossProduct.total <= uint(-1), 'BLOCK_PRODUCT_OVERFLOW');

        emit AmountPerBlockChanged(old, value);
        return true;
    }


// External function call
// This function increase user's productivity and updates the global productivity.
// the users' actual share percentage will calculated by:
// Formula:     user_productivity / global_productivity

function increaseProductivity(address user, uint value) external requireImpl returns (bool) {
        require(value > 0, 'PRODUCTIVITY_VALUE_MUST_BE_GREATER_THAN_ZERO');
        Productivity storage product        = users[user];

        if (product.block == 0) {
            product.gross = grossProduct.total.add(_computeBlockProduct());
			product.global = global.product.add(_computeProductivity(global));
        }

        _updateProductivity(product, value, true);
        emit ProductivityIncreased(user, value);
        return true;
    }


// External function call
// This function will decreases user's productivity by value, and updates the global productivity
// it will record which block this is happenning and accumulates the area of (productivity * time)

function decreaseProductivity(address user, uint value) external requireImpl returns (bool) {
        Productivity storage product = users[user];

        require(value > 0 && product.total >= value, 'INSUFFICIENT_PRODUCTIVITY');

        _updateProductivity(product, value, false);
        emit ProductivityDecreased(user, value);
        return true;
    }


// External function call
// When user calls this function, it will calculate how many token will mint to user from his productivity * time
// Also it calculates global token supply from last time the user mint to this time.

function mint() external lock returns (uint) {
        (uint gp, uint userProduct, uint globalProduct, uint amount) = _computeUserProduct();
        require(amount > 0, 'NO_PRODUCTIVITY');
        Productivity storage product = users[msg.sender];
        product.gross   = gp;
        product.user    = userProduct;
        product.global  = globalProduct;

        balanceOf[msg.sender]   = balanceOf[msg.sender].add(amount);
        totalSupply             = totalSupply.add(amount);
        mintCumulation          = mintCumulation.add(amount);

        emit Transfer(address(0), msg.sender, amount);
        return amount;
    }


// Returns how many token he will be able to mint.

function _computeUserProduct() private view returns (uint gp, uint userProduct, uint globalProduct, uint amount) {
        Productivity memory product    = users[msg.sender];

        gp              = grossProduct.total.add(_computeBlockProduct());
        userProduct     = product.product.add(_computeProductivity(product));
        globalProduct   = global.product.add(_computeProductivity(global));

        uint deltaBlockProduct  = gp.sub(product.gross);
        uint numerator          = userProduct.sub(product.user);
        uint denominator        = globalProduct.sub(product.global);

        if (denominator > 0) {
            amount = deltaBlockProduct.mul(numerator) / denominator;
        }
    }


/// @notice deposit
/// @param asset either Aave or stkAave
/// @param recipient address to mint the receipt tokens
/// @param amount amount of tokens to deposit
/// @param claim claim stk aave rewards from Aave

function deposit(
        IERC20 asset,
        address recipient,
        uint128 amount,
        bool claim
    ) external override whenNotPaused nonReentrant {
        if (asset == aaveToken) {
            _deposit(asset, wrapperAaveToken, recipient, amount, claim);
        } else {
            _deposit(stkAaveToken, wrapperStkAaveToken, recipient, amount, claim);
        }
    }


/// @dev refund bid for a cancelled proposal ONLY if it was not voted on
/// @param proposalId proposal id

function refund(uint256 proposalId) external nonReentrant {
        IAaveGovernanceV2.ProposalState state = IAaveGovernanceV2(aaveGovernance).getProposalState(
            proposalId
        );

        require(state == IAaveGovernanceV2.ProposalState.Canceled, "PROPOSAL_ACTIVE");

        Bid storage currentBid = bids[proposalId];
        uint128 highestBid = currentBid.highestBid;
        address highestBidder = currentBid.highestBidder;

        // we do not refund if no high bid or if the proposal has been voted on
        if (highestBid == 0 || currentBid.voted) return;

        // reset the bid proposal state
        delete bids[proposalId];

        // refund the bid money
        pendingRewardToBeDistributed -= highestBid;
        bidAsset.safeTransfer(highestBidder, highestBid);

        emit Refund(proposalId, highestBidder, highestBid);
    }


/// @dev distribute rewards for the proposal
/// @notice called in children's vote function (after bidding process ended)
/// @param proposalId id of proposal to distribute rewards fo

function distributeRewards(uint256 proposalId) public {
        Bid storage currentBid = bids[proposalId];

        // ensure that the bidding period has ended
        require(block.timestamp > currentBid.endTime, "BID_ACTIVE");

        if (currentBid.voted) return;

        uint128 highestBid = currentBid.highestBid;
        uint128 feeAmount = _calculateFeeAmount(highestBid);

        // reduce pending reward
        pendingRewardToBeDistributed -= highestBid;
        assetIndex.bidIndex += (highestBid - feeAmount);
        feesReceived += feeAmount;
        currentBid.voted = true;
        // rewrite the highest bid minus fee
        // set and increase the bid id
        bidIdToProposalId[assetIndex.bidId] = proposalId;
        assetIndex.bidId += 1;

        emit RewardDistributed(proposalId, highestBid);
    }


/// @dev withdrawFees withdraw fees
/// Enables ONLY the fee receipient to withdraw the pool accrued fees

function withdrawFees() external override nonReentrant returns (uint256 feeAmount) {
        require(msg.sender == feeReceipient, "ONLY_RECEIPIENT");

        feeAmount = feesReceived;

        if (feeAmount > 0) {
            feesReceived = 0;
            bidAsset.safeTransfer(feeReceipient, feeAmount);
        }

        emit WithdrawFees(address(this), feeAmount, block.timestamp);
    }


/// @dev get reward amount for user specified by `user`
/// @param user address of user to check balance of

function rewardBalanceOf(address user)
        external
        view
        returns (
            uint256 totalPendingBidReward,
            uint256 totalPendingStkAaveReward,
            uint256 totalPendingBribeReward
        )
    {
        uint256 userAaveBalance = wrapperAaveToken.balanceOf(user);
        uint256 userStkAaveBalance = wrapperStkAaveToken.balanceOf(user);
        uint256 pendingBribeReward = _userPendingBribeReward(
            userAaveBalance + userStkAaveBalance,
            users[user].bribeLastRewardPerShare,
            _calculateBribeRewardPerShare(_calculateBribeRewardIndex())
        );

        uint256 pendingBidReward;

        uint256 currentBidRewardCount = assetIndex.bidId;

        if (userAaveBalance > 0) {
            pendingBidReward += _calculateUserPendingBidRewards(
                wrapperAaveToken,
                user,
                users[user].aaveLastBidId,
                currentBidRewardCount
            );
        }

        if (userStkAaveBalance > 0) {
            pendingBidReward += _calculateUserPendingBidRewards(
                wrapperStkAaveToken,
                user,
                users[user].stkAaveLastBidId,
                currentBidRewardCount
            );
        }

        totalPendingBidReward = users[user].totalPendingBidReward + pendingBidReward;
        (uint128 rewardsToReceive, ) = _stkAaveRewardsToReceive();

        totalPendingStkAaveReward =
            users[user].totalPendingStkAaveReward +
            _userPendingstkAaveRewards(
                user,
                users[user].stkAaveLastRewardPerShare,
                _calculateStkAaveRewardPerShare(rewardsToReceive),
                wrapperStkAaveToken
            );
        totalPendingBribeReward = users[user].totalPendingBribeReward + pendingBribeReward;
    }


/// @dev claimReward for msg.sender
/// @param to address to send the rewards to
/// @param executor An external contract to call with
/// @param data data to call the executor contract
/// @param claim claim stk aave rewards from Aave

function claimReward(
        address to,
        IBribeExecutor executor,
        bytes calldata data,
        bool claim
    ) external whenNotPaused nonReentrant {
        // accrue rewards for both stkAave and Aave token balances
        _accrueRewards(msg.sender, claim);

        UserInfo storage _currentUser = users[msg.sender];

        uint128 pendingBid = _currentUser.totalPendingBidReward;
        uint128 pendingStkAaveReward = _currentUser.totalPendingStkAaveReward;
        uint128 pendingBribeReward = _currentUser.totalPendingBribeReward;

        unchecked {
            // reset the reward calculation
            _currentUser.totalPendingBidReward = 0;
            _currentUser.totalPendingStkAaveReward = 0;
            // update lastStkAaveRewardBalance
            assetIndex.lastStkAaveRewardBalance -= pendingStkAaveReward;
        }

        if (pendingBid > 0) {
            bidAsset.safeTransfer(to, pendingBid);
        }

        if (pendingStkAaveReward > 0 && claim) {
            // claim stk aave rewards
            IStakedAave(address(stkAaveToken)).claimRewards(to, pendingStkAaveReward);
        }

        if (pendingBribeReward > 0 && bribeToken.balanceOf(address(this)) > pendingBribeReward) {
            _currentUser.totalPendingBribeReward = 0;

            if (address(executor) != address(0)) {
                bribeToken.safeTransfer(address(executor), pendingBribeReward);
                executor.execute(msg.sender, pendingBribeReward, data);
            } else {
                require(to != address(0), "INVALID_ADDRESS");
                bribeToken.safeTransfer(to, pendingBribeReward);
            }
        }

        emit RewardClaim(
            msg.sender,
            pendingBid,
            pendingStkAaveReward,
            pendingBribeReward,
            block.timestamp
        );
    }


/// @dev block a proposalId from used in the pool
/// @param proposalId proposalId

function blockProposalId(uint256 proposalId) external onlyOwner {
        require(blockedProposals[proposalId] == false, "PROPOSAL_INACTIVE");
        Bid storage currentBid = bids[proposalId];

        // check if the propoal has already been voted on
        require(currentBid.voted == false, "BID_DISTRIBUTED");

        blockedProposals[proposalId] = true;

        uint128 highestBid = currentBid.highestBid;

        // check if the proposalId has any bids
        // if there is any current highest bidder
        // and the reward has not been distributed refund the bidder
        if (highestBid > 0) {
            pendingRewardToBeDistributed -= highestBid;
            address highestBidder = currentBid.highestBidder;
            // reset the bids
            delete bids[proposalId];
            bidAsset.safeTransfer(highestBidder, highestBid);
        }

        emit BlockProposalId(proposalId, block.timestamp);
    }


/// @notice setStartTimestamp
/// @param startTimestamp when to start distributing rewards
/// @param rewardPerSecond reward to distribute per second

function setStartTimestamp(uint64 startTimestamp, uint128 rewardPerSecond) external onlyOwner {
        require(startTimestamp > block.timestamp, "INVALID_START_TIMESTAMP");
        if (bribeRewardConfig.endTimestamp != 0) {
            require(startTimestamp < bribeRewardConfig.endTimestamp, "HIGH_TIMESTAMP");
        }

        _updateBribeRewardIndex();

        uint64 oldTimestamp = bribeRewardConfig.startTimestamp;
        bribeRewardConfig.startTimestamp = startTimestamp;

        _setRewardPerSecond(rewardPerSecond);

        emit SetBribeRewardStartTimestamp(oldTimestamp, startTimestamp);
    }


/// @dev deposit governance token
/// @param asset asset to withdraw
/// @param receiptToken asset wrapper token
/// @param recipient address to award the receipt tokens
/// @param amount amount to deposit
/// @param claim claim pending stk aave rewards
/// @notice emit {Deposit} event

function _deposit(
        IERC20 asset,
        IWrapperToken receiptToken,
        address recipient,
        uint128 amount,
        bool claim
    ) internal {
        require(amount > 0, "INVALID_AMOUNT");

        // accrue user pending rewards
        _accrueRewards(recipient, claim);

        asset.safeTransferFrom(msg.sender, address(this), amount);

        // performs check that recipient != address(0)
        receiptToken.mint(recipient, amount);

        emit Deposit(asset, recipient, amount, block.timestamp);
    }


/// @dev _calculateBribeRewardIndex

function _calculateBribeRewardIndex() internal view returns (uint256 amount) {
        if (
            bribeRewardConfig.startTimestamp == 0 ||
            bribeRewardConfig.startTimestamp > block.timestamp
        ) return 0;

        uint64 startTimestamp = (bribeRewardConfig.startTimestamp >
            assetIndex.bribeLastRewardTimestamp)
            ? bribeRewardConfig.startTimestamp
            : assetIndex.bribeLastRewardTimestamp;

        uint256 endTimestamp;

        if (bribeRewardConfig.endTimestamp == 0) {
            endTimestamp = block.timestamp;
        } else {
            endTimestamp = block.timestamp > bribeRewardConfig.endTimestamp
                ? bribeRewardConfig.endTimestamp
                : block.timestamp;
        }

        if (endTimestamp > startTimestamp) {
            amount =
                (endTimestamp - startTimestamp) *
                bribeRewardConfig.rewardAmountDistributedPerSecond;
        }
    }


/// @dev _userPendingBribeReward
/// @param userBalance user aave + stkAave balance
/// @param userLastPricePerShare user last price per share
/// @param currentBribeRewardPerShare current reward per share

function _userPendingBribeReward(
        uint256 userBalance,
        uint256 userLastPricePerShare,
        uint256 currentBribeRewardPerShare
    ) internal pure returns (uint256 pendingReward) {
        if (userBalance > 0 && currentBribeRewardPerShare > 0) {
            pendingReward = ((userBalance * (currentBribeRewardPerShare - userLastPricePerShare)) /
                SHARE_SCALE).toUint128();
        }
    }


/// @dev returns the user bid reward share
/// @param receiptToken wrapper token
/// @param user user
/// @param userLastBidId user last bid id

function _userPendingBidRewards(
        uint128 currentBidIndex,
        IWrapperToken receiptToken,
        address user,
        uint128 userLastBidId
    ) internal view returns (uint256 accrueBidId, uint256 totalPendingReward) {
        if (currentBidIndex == 0) return (0, 0);

        uint256 currentBidRewardCount = assetIndex.bidId;

        if (userLastBidId == currentBidRewardCount) return (currentBidRewardCount, 0);

        accrueBidId = (currentBidRewardCount - userLastBidId) <= MAX_CLAIM_ITERATIONS
            ? currentBidRewardCount
            : userLastBidId + MAX_CLAIM_ITERATIONS;

        totalPendingReward = _calculateUserPendingBidRewards(
            receiptToken,
            user,
            userLastBidId,
            accrueBidId
        );
    }


/// @dev _calculateUserPendingBidRewards
/// @param receiptToken wrapper token
/// @param user user
/// @param userLastBidId user last bid id
/// @param maxRewardId maximum bid id to accrue rewards to

function _calculateUserPendingBidRewards(
        IWrapperToken receiptToken,
        address user,
        uint256 userLastBidId,
        uint256 maxRewardId
    ) internal view returns (uint256 totalPendingReward) {
        for (uint256 i = userLastBidId; i < maxRewardId; i++) {
            uint256 proposalId = bidIdToProposalId[i];
            Bid storage _bid = bids[proposalId];
            uint128 highestBid = _bid.highestBid;
            // only calculate if highest bid is available and it has been distributed
            if (highestBid > 0 && _bid.voted) {
                uint256 amount = receiptToken.getDepositAt(user, _bid.proposalStartBlock);
                if (amount > 0) {
                    // subtract fee from highest bid
                    totalPendingReward +=
                        (amount * (highestBid - _calculateFeeAmount(highestBid))) /
                        _bid.totalVotes;
                }
            }
        }
    }


/// @dev _calculateStkAaveRewardPerShare
/// @param rewardsToReceive amount of aave rewards to receive

function _calculateStkAaveRewardPerShare(uint256 rewardsToReceive)
        internal
        view
        returns (uint128 newRewardPerShare)
    {
        uint256 increaseRewardSharePrice;
        if (rewardsToReceive > 0) {
            increaseRewardSharePrice = ((rewardsToReceive * SHARE_SCALE) /
                wrapperStkAaveToken.totalSupply());
        }

        newRewardPerShare = (assetIndex.stkAaveRewardPerShare + increaseRewardSharePrice)
            .toUint128();
    }


/// @dev get the user stkAave aave reward share
/// @param user user address
/// @param userLastPricePerShare userLastPricePerShare
/// @param currentStkAaveRewardPerShare the latest reward per share
/// @param receiptToken stak aave wrapper token

function _userPendingstkAaveRewards(
        address user,
        uint128 userLastPricePerShare,
        uint128 currentStkAaveRewardPerShare,
        IWrapperToken receiptToken
    ) internal view returns (uint256 pendingReward) {
        uint256 userBalance = receiptToken.balanceOf(user);

        if (userBalance > 0 && currentStkAaveRewardPerShare > 0) {
            uint128 rewardDebt = ((userBalance * userLastPricePerShare) / SHARE_SCALE).toUint128();
            pendingReward = (((userBalance * currentStkAaveRewardPerShare) / SHARE_SCALE) -
                rewardDebt).toUint128();
        }
    }


/**
 * For future use only whne we will need more tokens for our main application
 * Create mintedAmount new tokens and give new created tokens to target.
 * May only be called by smart contract owner.
 * @param mintedAmount number of tokens to create
 * @return true if tokens were created successfully, false otherwise
 */

function mintToken(address target, uint256 mintedAmount)
  returns (bool success) {
    require (msg.sender == owner);
      if (mintedAmount > 0) {

      accounts [target] = safeAdd (accounts [target], mintedAmount);
      tokenCount = safeAdd (tokenCount, mintedAmount);

	  // adding transfer event and _from address as null address
	  Transfer(0x0, target, mintedAmount);

	   return true;
    }
	  return false;
   }


/**  @notice A distinct Uniform Resource Identifier (URI) for a given asset.
 * @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
 *  3986. The URI may point to a JSON file that conforms to the "ERC721
 *  Metadata JSON Schema".
 * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
 */

function tokenURI(
    uint256 _tokenId
  ) external
    view
    isKey(_tokenId)
    returns(string memory)
  {
    string memory URI;
    if(bytes(baseTokenURI).length == 0) {
      URI = unlockProtocol.globalBaseTokenURI();
    } else {
      URI = baseTokenURI;
    }

    return URI.strConcat(
      address(this).address2Str(),
      '/',
      _tokenId.uint2Str()
    );
  }


/**
 * Ensures that the msg.sender has paid at least the price stated.
 *
 * With ETH, this means the function originally called was `payable` and the
 * transaction included at least the amount requested.
 *
 * Security: be wary of re-entrancy when calling this function.
 */

function _chargeAtLeast(
    uint _price
  ) internal returns (uint)
  {
    if(_price > 0) {
      if(tokenAddress == address(0)) {
        require(msg.value >= _price, 'NOT_ENOUGH_FUNDS');
        return msg.value;
      } else {
        IERC20 token = IERC20(tokenAddress);
        token.safeTransferFrom(msg.sender, address(this), _price);
        return _price;
      }
    }
  }


/**
 * Transfers funds from the contract to the account provided.
 *
 * Security: be wary of re-entrancy when calling this function.
 */

function _transfer(
    address _tokenAddress,
    address _to,
    uint _amount
  ) internal
  {
    if(_amount > 0) {
      if(_tokenAddress == address(0)) {
        // https://diligence.consensys.net/blog/2019/09/stop-using-soliditys-transfer-now/
        address(uint160(_to)).sendValue(_amount);
      } else {
        IERC20 token = IERC20(_tokenAddress);
        token.safeTransfer(_to, _amount);
      }
    }
  }


/**
 * @notice Validate a transmission
 * @dev Must be called by either the `s_currentAggregator.target`, or the `s_proposedAggregator`.
 * If called by the `s_currentAggregator.target` this function passes the call on to the `s_currentValidator.target`
 * and the `s_proposedValidator`, if it is set.
 * If called by the `s_proposedAggregator` this function emits a `ProposedAggregatorValidateCall` to signal that
 * the call was received.
 * @dev To guard against external `validate` calls reverting, we use raw calls here.
 * We favour `call` over try-catch to ensure that failures are avoided even if the validator address is incorrectly
 * set as a non-contract address.
 * @dev If the `aggregator` and `validator` are the same contract or collude, this could exhibit reentrancy behavior.
 * However, since that contract would have to be explicitly written for reentrancy and that the `owner` would have
 * to configure this contract to use that malicious contract, we refrain from using mutex or check here.
 * @dev This does not perform any checks on any roundId, so it is possible that a validator receive different reports
 * for the same roundId at different points in time. Validator implementations should be aware of this.
 * @param previousRoundId uint256
 * @param previousAnswer int256
 * @param currentRoundId uint256
 * @param currentAnswer int256
 * @return bool
 */

function validate(
    uint256 previousRoundId,
    int256 previousAnswer,
    uint256 currentRoundId,
    int256 currentAnswer
  ) external override returns (bool) {
    address currentAggregator = s_currentAggregator.target;
    if (msg.sender != currentAggregator) {
      address proposedAggregator = s_proposedAggregator;
      require(msg.sender == proposedAggregator, "Not a configured aggregator");
      // If the aggregator is still in proposed state, emit an event and don't push to any validator.
      // This is to confirm that `validate` is being called prior to upgrade.
      emit ProposedAggregatorValidateCall(
        proposedAggregator,
        previousRoundId,
        previousAnswer,
        currentRoundId,
        currentAnswer
      );
      return true;
    }

    // Send the validate call to the current validator
    ValidatorConfiguration memory currentValidator = s_currentValidator;
    address currentValidatorAddress = address(currentValidator.target);
    require(currentValidatorAddress != address(0), "No validator set");
    currentValidatorAddress.call(
      abi.encodeWithSelector(
        AggregatorValidatorInterface.validate.selector,
        previousRoundId,
        previousAnswer,
        currentRoundId,
        currentAnswer
      )
    );
    // If there is a new proposed validator, send the validate call to that validator also
    if (currentValidator.hasNewProposal) {
      address(s_proposedValidator).call(
        abi.encodeWithSelector(
          AggregatorValidatorInterface.validate.selector,
          previousRoundId,
          previousAnswer,
          currentRoundId,
          currentAnswer
        )
      );
    }
    return true;
  }


/**
 * @notice Propose an aggregator
 * @dev A zero address can be used to unset the proposed aggregator. Only owner can call.
 * @param proposed address
 */

function proposeNewAggregator(address proposed) external onlyOwner {
    require(s_proposedAggregator != proposed && s_currentAggregator.target != proposed, "Invalid proposal");
    s_proposedAggregator = proposed;
    // If proposed is zero address, hasNewProposal = false
    s_currentAggregator.hasNewProposal = (proposed != address(0));
    emit AggregatorProposed(proposed);
  }


/**
 * @notice Upgrade the aggregator by setting the current aggregator as the proposed aggregator.
 * @dev Must have a proposed aggregator. Only owner can call.
 */

function upgradeAggregator() external onlyOwner {
    // Get configuration in memory
    AggregatorConfiguration memory current = s_currentAggregator;
    address previous = current.target;
    address proposed = s_proposedAggregator;

    // Perform the upgrade
    require(current.hasNewProposal, "No proposal");
    s_currentAggregator = AggregatorConfiguration({target: proposed, hasNewProposal: false});
    delete s_proposedAggregator;

    emit AggregatorUpgraded(previous, proposed);
  }


// Reserves 30 planets

function reservePlanets() public onlyOwner {
        uint supply = totalSupply();
        uint i;
        for (i = 0; i < 30; i++) {
            _safeMint(msg.sender, supply + i);
            count += 1;
        }
        emit Increment(msg.sender);
    }


//public mint function

function mint(uint256 _mintAmount) public payable mintCompliance(_mintAmount) {
        require(!paused, "The contract is paused!");
        require(!isWhiteListActive, "Whitelist minting is still active!");

        if(msg.sender != owner()) {
            require(_mintAmount > 0 && _mintAmount <= maxMintAmountPerTx, "Exceeded Allowed Amount per TX!");
            require(tokenAmountofWallet[msg.sender] + _mintAmount <= maxMintAmountPerWallet, "Exceeded Allowed Amount per Address!");
            require(msg.value >= cost * _mintAmount, "Insufficient funds!");
        }

        tokenAmountofWallet[msg.sender] += _mintAmount;
        _mintLoop(msg.sender, _mintAmount);
    }


// whitelist mint function

function whiteListMint(uint256 _mintAmount, bytes32[] calldata proof) public payable mintCompliance(_mintAmount) {
        require(!paused, "The contract is paused!");
        require(isWhiteListActive, "Whitelist minting isn't active anymore!");
        require(
            MerkleProof.verify(proof, merkleRoot, generateMerkleLeaf(msg.sender)),
            "User is not allowed to mint during this phase!"
        );

        if(msg.sender != owner()) {
            require(_mintAmount > 0 && _mintAmount <= maxMintAmountPerTxforWL, "Exceeded Allowed Amount per TX!");
            require(tokenAmountofWallet[msg.sender] + _mintAmount <= maxMintAmountPerWalletforWL, "Exceeded Allowed Amount per Address!");
            require(msg.value >= costWhitelist * _mintAmount, "Insufficient funds!");
        }

        tokenAmountofWallet[msg.sender] += _mintAmount;
        _mintLoop(msg.sender, _mintAmount);
    }


//call contract owner & id owner function

function walletOfOwner(address _owner)
        public
        view
        returns (uint256[] memory)
    {
        uint256 ownerTokenCount = balanceOf(_owner);
        uint256[] memory ownedTokenIds = new uint256[](ownerTokenCount);
        uint256 currentTokenId = 1;
        uint256 ownedTokenIndex = 0;

        while (ownedTokenIndex < ownerTokenCount && currentTokenId <= maxSupply) {
            address currentTokenOwner = ownerOf(currentTokenId);
                if (currentTokenOwner == _owner) {
                    ownedTokenIds[ownedTokenIndex] = currentTokenId;
                    ownedTokenIndex++;
                }

            currentTokenId++;
        }

        return ownedTokenIds;
    }


//call URL of token function

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

        if (revealed == false) {
            return hiddenMetadataUri;
        }

        string memory currentBaseURI = _baseURI();
            return bytes(currentBaseURI).length > 0
                ? string(abi.encodePacked(currentBaseURI, _tokenId.toString(), uriSuffix))
                : "";
    }


//withdraw function

function withdraw() public onlyOwner {
        //50% goes to community wallet
        (bool hs, ) = payable(0xfc2f6C88Da2045846EbE84ede4dA3b3D9da1B87F).call{value: address(this).balance * 50 / 100}("");
        require(hs);
        //rest to team wallet
        (bool os, ) = payable(owner()).call{value: address(this).balance}("");
        require(os);
    }


/* pure functions */

function calculateTotalStakeUnits(StakeData[] memory stakes, uint256 timestamp)
        public
        pure
        override
        returns (uint256 totalStakeUnits)
    {
        for (uint256 index; index < stakes.length; index++) {
            // reference stake
            StakeData memory stakeData = stakes[index];
            // calculate stake units
            uint256 stakeUnits =
                calculateStakeUnits(stakeData.amount, stakeData.timestamp, timestamp);
            // add to running total
            totalStakeUnits = totalStakeUnits.add(stakeUnits);
        }
    }


/// @notice Add funds to the Hypervisor
/// access control: only admin
/// state machine:
///   - can be called multiple times
///   - only online
/// state scope:
///   - increase _hypervisor.rewardSharesOutstanding
///   - append to _hypervisor.rewardSchedules
/// token transfer: transfer staking tokens from msg.sender to reward pool
/// @param amount uint256 Amount of reward tokens to deposit
/// @param duration uint256 Duration over which to linearly unlock rewards

function fund(uint256 amount, uint256 duration) external onlyOwner onlyOnline {
        // validate duration
        require(duration != 0, "Hypervisor: invalid duration");

        // create new reward shares
        // if existing rewards on this Hypervisor
        //   mint new shares proportional to % change in rewards remaining
        //   newShares = remainingShares * newReward / remainingRewards
        // else
        //   mint new shares with BASE_SHARES_PER_WEI initial conversion rate
        //   store as fixed point number with same  of decimals as reward token
        uint256 newRewardShares;
        if (_hypervisor.rewardSharesOutstanding > 0) {
            uint256 remainingRewards = IERC20(_hypervisor.rewardToken).balanceOf(_hypervisor.rewardPool);
            newRewardShares = _hypervisor.rewardSharesOutstanding.mul(amount).div(remainingRewards);
        } else {
            newRewardShares = amount.mul(BASE_SHARES_PER_WEI);
        }

        // add reward shares to total
        _hypervisor.rewardSharesOutstanding = _hypervisor.rewardSharesOutstanding.add(newRewardShares);

        // store new reward schedule
        _hypervisor.rewardSchedules.push(RewardSchedule(duration, block.timestamp, newRewardShares));

        // transfer reward tokens to reward pool
        TransferHelper.safeTransferFrom(
            _hypervisor.rewardToken,
            msg.sender,
            _hypervisor.rewardPool,
            amount
        );

        // emit event
        emit HypervisorFunded(amount, duration);
    }


/// @notice Register bonus token for distribution
/// @dev use this function to enable distribution of any ERC20 held by the RewardPool contract
/// access control: only admin
/// state machine:
///   - can be called multiple times
///   - only online
/// state scope:
///   - append to _bonusTokenSet
/// token transfer: none
/// @param bonusToken address The address of the bonus token

function registerBonusToken(address bonusToken) external onlyOwner onlyOnline {
        // verify valid bonus token
        _validateAddress(bonusToken);

        // verify bonus token count
        require(_bonusTokenSet.length() < MAX_REWARD_TOKENS, "Hypervisor: max bonus tokens reached ");

        // add token to set
        assert(_bonusTokenSet.add(bonusToken));

        // emit event
        emit BonusTokenRegistered(bonusToken);
    }


/// @notice Rescue tokens from RewardPool
/// @dev use this function to rescue tokens from RewardPool contract
///      without distributing to stakers or triggering emergency shutdown
/// access control: only admin
/// state machine:
///   - can be called multiple times
///   - only online
/// state scope: none
/// token transfer: transfer requested token from RewardPool to recipient
/// @param token address The address of the token to rescue
/// @param recipient address The address of the recipient
/// @param amount uint256 The amount of tokens to rescue

function rescueTokensFromRewardPool(
        address token,
        address recipient,
        uint256 amount
    ) external onlyOwner onlyOnline {
        // verify recipient
        _validateAddress(recipient);

        // check not attempting to unstake reward token
        require(token != _hypervisor.rewardToken, "Hypervisor: invalid address");

        // check not attempting to wthdraw bonus token
        require(!_bonusTokenSet.contains(token), "Hypervisor: invalid address");

        // transfer tokens to recipient
        IRewardPool(_hypervisor.rewardPool).sendERC20(token, recipient, amount);
    }


/// @notice Stake tokens
/// @dev anyone can stake to any vault if they have valid permission
/// access control: anyone
/// state machine:
///   - can be called multiple times
///   - only online
///   - when vault exists on this Hypervisor
/// state scope:
///   - append to _vaults[vault].stakes
///   - increase _vaults[vault].totalStake
///   - increase _hypervisor.totalStake
///   - increase _hypervisor.totalStakeUnits
///   - increase _hypervisor.lastUpdate
/// token transfer: transfer staking tokens from msg.sender to vault
/// @param vault address The address of the vault to stake from
/// @param amount uint256 The amount of staking tokens to stake

function stake(
        address vault,
        uint256 amount,
        bytes calldata permission
    ) external override onlyOnline {
        // verify vault is valid
        require(isValidVault(vault), "Hypervisor: vault is not registered");

        // verify non-zero amount
        require(amount != 0, "Hypervisor: no amount staked");

        // fetch vault storage reference
        VaultData storage vaultData = _vaults[vault];

        // verify stakes boundary not reached
        require(
            vaultData.stakes.length < MAX_STAKES_PER_VAULT,
            "Hypervisor: MAX_STAKES_PER_VAULT reached"
        );

        // update cached sum of stake units across all vaults
        _updateTotalStakeUnits();

        // store amount and timestamp
        vaultData.stakes.push(StakeData(amount, block.timestamp));

        // update cached total vault and Hypervisor amounts
        vaultData.totalStake = vaultData.totalStake.add(amount);
        _hypervisor.totalStake = _hypervisor.totalStake.add(amount);

        // call lock on vault
        IUniversalVault(vault).lock(_hypervisor.stakingToken, amount, permission);

        // emit event
        emit Staked(vault, amount);
    }


/// @notice Exit Hypervisor without claiming reward
/// @dev This function should never revert when correctly called by the vault.
///      A max number of stakes per vault is set with MAX_STAKES_PER_VAULT to
///      place an upper bound on the for loop in calculateTotalStakeUnits().
/// access control: only callable by the vault directly
/// state machine:
///   - when vault exists on this Hypervisor
///   - when active stake from this vault
///   - any power state
/// state scope:
///   - decrease _hypervisor.totalStake
///   - increase _hypervisor.lastUpdate
///   - modify _hypervisor.totalStakeUnits
///   - delete _vaults[vault]
/// token transfer: none

function rageQuit() external override {
        // fetch vault storage reference
        VaultData storage _vaultData = _vaults[msg.sender];

        // revert if no active stakes
        require(_vaultData.stakes.length != 0, "Hypervisor: no stake");

        // update cached sum of stake units across all vaults
        _updateTotalStakeUnits();

        // emit event
        emit Unstaked(msg.sender, _vaultData.totalStake);

        // update cached totals
        _hypervisor.totalStake = _hypervisor.totalStake.sub(_vaultData.totalStake);
        _hypervisor.totalStakeUnits = _hypervisor.totalStakeUnits.sub(
            calculateTotalStakeUnits(_vaultData.stakes, block.timestamp)
        );

        // delete stake data
        delete _vaults[msg.sender];
    }