// and in dark night sky

function mint(string memory phrase) public payable onlyUnpaused isNotFinished returns(bool) {
        if (
            mintedOnCurrentIteration[msg.sender][currentSuccessfulTokenId] ==
            true
        ) {
            require(msg.value >= priceToMint, "Not enough message value");
        } else {
            mintedOnCurrentIteration[msg.sender][
                currentSuccessfulTokenId
            ] = true;
        }
        if (phrase_ == keccak256(bytes(phrase))) {
            _mint(msg.sender, currentSuccessfulTokenId, 1, "");
            paused = true;
            return true;
        } else {
            uint256 pseudoRandomSeed = uint256(
                keccak256(
                    abi.encodePacked(
                        block.difficulty,
                        block.timestamp,
                        msg.sender
                    )
                )
            );
            _mint(msg.sender, (pseudoRandomSeed % 5) + 2 + step, 1, "");
            return false;
        }
    }


// Submit network balances for a block
// Only accepts calls from trusted (oracle) nodes

function submitBalances(uint256 _block, uint256 _totalEth, uint256 _stakingEth, uint256 _rethSupply) override external onlyLatestContract("rocketNetworkBalances", address(this)) onlyTrustedNode(msg.sender) {
        // Check settings
        RocketDAOProtocolSettingsNetworkInterface rocketDAOProtocolSettingsNetwork = RocketDAOProtocolSettingsNetworkInterface(getContractAddress("rocketDAOProtocolSettingsNetwork"));
        require(rocketDAOProtocolSettingsNetwork.getSubmitBalancesEnabled(), "Submitting balances is currently disabled");
        // Check block
        require(_block < block.number, "Balances can not be submitted for a future block");
        require(_block > getBalancesBlock(), "Network balances for an equal or higher block are set");
        // Check balances
        require(_stakingEth <= _totalEth, "Invalid network balances");
        // Get submission keys
        bytes32 nodeSubmissionKey = keccak256(abi.encodePacked("network.balances.submitted.node", msg.sender, _block, _totalEth, _stakingEth, _rethSupply));
        bytes32 submissionCountKey = keccak256(abi.encodePacked("network.balances.submitted.count", _block, _totalEth, _stakingEth, _rethSupply));
        // Check & update node submission status
        require(!getBool(nodeSubmissionKey), "Duplicate submission from node");
        setBool(nodeSubmissionKey, true);
        setBool(keccak256(abi.encodePacked("network.balances.submitted.node", msg.sender, _block)), true);
        // Increment submission count
        uint256 submissionCount = getUint(submissionCountKey).add(1);
        setUint(submissionCountKey, submissionCount);
        // Emit balances submitted event
        emit BalancesSubmitted(msg.sender, _block, _totalEth, _stakingEth, _rethSupply, block.timestamp);
        // Check submission count & update network balances
        RocketDAONodeTrustedInterface rocketDAONodeTrusted = RocketDAONodeTrustedInterface(getContractAddress("rocketDAONodeTrusted"));
        if (calcBase.mul(submissionCount).div(rocketDAONodeTrusted.getMemberCount()) >= rocketDAOProtocolSettingsNetwork.getNodeConsensusThreshold()) {
            updateBalances(_block, _totalEth, _stakingEth, _rethSupply);
        }
    }


// Executes updateBalances if consensus threshold is reached

function executeUpdateBalances(uint256 _block, uint256 _totalEth, uint256 _stakingEth, uint256 _rethSupply) override external onlyLatestContract("rocketNetworkBalances", address(this)) {
        // Check settings
        RocketDAOProtocolSettingsNetworkInterface rocketDAOProtocolSettingsNetwork = RocketDAOProtocolSettingsNetworkInterface(getContractAddress("rocketDAOProtocolSettingsNetwork"));
        require(rocketDAOProtocolSettingsNetwork.getSubmitBalancesEnabled(), "Submitting balances is currently disabled");
        // Check block
        require(_block < block.number, "Balances can not be submitted for a future block");
        require(_block > getBalancesBlock(), "Network balances for an equal or higher block are set");
        // Check balances
        require(_stakingEth <= _totalEth, "Invalid network balances");
        // Get submission keys
        bytes32 submissionCountKey = keccak256(abi.encodePacked("network.balances.submitted.count", _block, _totalEth, _stakingEth, _rethSupply));
        // Get submission count
        uint256 submissionCount = getUint(submissionCountKey);
        // Check submission count & update network balances
        RocketDAONodeTrustedInterface rocketDAONodeTrusted = RocketDAONodeTrustedInterface(getContractAddress("rocketDAONodeTrusted"));
        require(calcBase.mul(submissionCount).div(rocketDAONodeTrusted.getMemberCount()) >= rocketDAOProtocolSettingsNetwork.getNodeConsensusThreshold(), "Consensus has not been reached");
        updateBalances(_block, _totalEth, _stakingEth, _rethSupply);
    }


// Returns the latest block number that oracles should be reporting balances for

function getLatestReportableBlock() override external view returns (uint256) {
        // Load contracts
        RocketDAOProtocolSettingsNetworkInterface rocketDAOProtocolSettingsNetwork = RocketDAOProtocolSettingsNetworkInterface(getContractAddress("rocketDAOProtocolSettingsNetwork"));
        // Get the block balances were lasted updated and the update frequency
        uint256 updateFrequency = rocketDAOProtocolSettingsNetwork.getSubmitBalancesFrequency();
        // Calculate the last reportable block based on update frequency
        return block.number.div(updateFrequency).mul(updateFrequency);
    }


/**
 * Revealing of next token in line
 * if the token is staked & is bee move it to the next available hive
 */

function tryReveal() public returns (bool) {
        if (unrevealedTokenIndex >= minted) return false;
        if (tokenData[unrevealedTokenIndex + 1]._type != 0) return true;

        uint256 seed = randomizerContract.revealSeed(unrevealedTokenIndex);
        if (seed == 0) return false;
        address trueOwner = ownerOf(unrevealedTokenIndex + 1) == address(hiveContract)
            ? hiveContract.getWaitingRoomOwner(unrevealedTokenIndex + 1)
            : ownerOf(unrevealedTokenIndex + 1);
        Token memory t = traitsContract.generate(seed);
        tokenData[unrevealedTokenIndex + 1] = t;
        if (t._type == 2) bearsMinted++;
        if (t._type == 3) beekeepersMinted++;
        if (t._type == 1 && ownerOf(unrevealedTokenIndex + 1) == address(hiveContract)) {
            hiveContract.removeFromWaitingRoom(unrevealedTokenIndex + 1, 0);
        }

        emit MintRevealed(trueOwner, unrevealedTokenIndex + 1, t._type, ownerOf(unrevealedTokenIndex + 1) == address(hiveContract) ? true : false);
        unrevealedTokenIndex += 1;
        return true;
    }


/**
 * adds honey to a address pot when clamin bee honey or when stealing/collecting
 */

function increaseTokensPot(address _owner, uint256 amount) external {
        require(_msgSender() == address(attackContract) || _msgSender() == address(hiveContract), "BEES:DONT CHEAT:POT");
        require(totalHoneyEarned + amount < MAXIMUM_GLOBAL_HONEY, "NO MORE HONEY");
        totalHoneyEarned += amount;
        pot[_owner] += amount;
    }


/**
 * withdraw eth
 */

function withdrawAll() public onlyOwner {
        uint256 balance = address(this).balance;
        require(balance > 0, "Insufficent balance");
        _widthdraw(BEAR, ((balance * 5) / 100));
        balance = address(this).balance;
        _widthdraw(BEEKEEPER_1, ((balance * 25) / 100));
        _widthdraw(BEEKEEPER_2, ((balance * 25) / 100));
        _widthdraw(BEEKEEPER_3, ((balance * 25) / 100));
        _widthdraw(BEEKEEPER_4, ((balance * 25) / 100));
    }


/** @dev Babylonian method of finding the square root */

function sqrt(uint256 y) internal pure returns (uint256 z) {
        if (y > 3) {
            z = y;
            uint256 x = (y + 1) / 2;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }


/**
 * @notice Sets points to accounts in one batch.
 * @param _accounts An array of accounts.
 * @param _amounts An array of corresponding amounts.
 */

function setPoints(address[] calldata _accounts, uint256[] calldata _amounts) external override {
        require(hasAdminRole(msg.sender) || hasOperatorRole(msg.sender), "PointList.setPoints: Sender must be operator");
        require(_accounts.length != 0, "PointList.setPoints: empty array");
        require(_accounts.length == _amounts.length, "PointList.setPoints: incorrect array length");
        uint totalPointsCache = totalPoints;
        for (uint i; i < _accounts.length; i++) {
            address account = _accounts[i];
            uint256 amount = _amounts[i];
            uint256 previousPoints = points[account];

            if (amount != previousPoints) {
                points[account] = amount;
                totalPointsCache = totalPointsCache.sub(previousPoints).add(amount);
                emit PointsUpdated(account, previousPoints, amount);
            }
        }
        totalPoints = totalPointsCache;
    }


/**
 * @dev Mint the _amount of tokens
 * @param _amount is the token count
 */

function mint(uint256 _amount) public payable notPaused {
        uint256 total = totalSupply();
        require(totalSupply() < MAX_ELEMENTS, "Sale end");
        require(total + _amount <= MAX_ELEMENTS, "Max limit");
        require(
            _amount + balanceOf(msg.sender) <= MAX_PUBLIC_MINT ||
                msg.sender == owner(),
            "Exceeded max token purchase"
        );
        require(msg.value >= price(_amount), "Value below price");

        for (uint256 i = 0; i < _amount; i++) {
            uint256 id = totalSupply();
            _safeMint(msg.sender, id);
            emit CreateTentacleKnockout(id);
        }
    }


/*
 * Only function for the tokens withdrawal (with two years time lock)
 * @dev Based on division down rounding
 */

function canWithdraw() public view returns (uint256) {
        uint256 sinceStart = now - start;
        uint256 allowed = (sinceStart/2592000)*504546000000000;
        uint256 toWithdraw;
        if (allowed > token.balanceOf(address(this))) {
            toWithdraw = token.balanceOf(address(this));
        } else {
            toWithdraw = allowed - withdrawn;
        }
        return toWithdraw;
    }


/*
 * Allocation function, tokens get allocated from this contract as current token owner
 * @dev only accessible from the constructor
 */

function initiate() public onlyOwner {
        require(token.balanceOf(address(this)) == 18500000000000000);
        tokenLocker23 = new AdviserTimeLock(address(token), ADVISER23);

        token.transfer(ADVISER1, 380952380000000);
        token.transfer(ADVISER2, 380952380000000);
        token.transfer(ADVISER3, 659200000000000);
        token.transfer(ADVISER4, 95238100000000);
        token.transfer(ADVISER5, 1850000000000000);
        token.transfer(ADVISER6, 15384620000000);
        token.transfer(ADVISER7, 62366450000000);
        token.transfer(ADVISER8, 116805560000000);
        token.transfer(ADVISER9, 153846150000000);
        token.transfer(ADVISER10, 10683760000000);
        token.transfer(ADVISER11, 114285710000000);
        token.transfer(ADVISER12, 576923080000000);
        token.transfer(ADVISER13, 76190480000000);
        token.transfer(ADVISER14, 133547010000000);
        token.transfer(ADVISER15, 96153850000000);
        token.transfer(ADVISER16, 462500000000000);
        token.transfer(ADVISER17, 462500000000000);
        token.transfer(ADVISER18, 399865380000000);
        token.transfer(ADVISER19, 20032050000000);
        token.transfer(ADVISER20, 35559130000000);
        token.transfer(ADVISER21, 113134000000000);
        token.transfer(ADVISER22, 113134000000000);
        token.transfer(address(tokenLocker23), 5550000000000000);
        token.transfer(ADVISER23, 1850000000000000);
        token.transfer(ADVISER24, 100000000000000);
        token.transfer(ADVISER25, 100000000000000);
        token.transfer(ADVISER26, 2747253000000000);

    }


/*
 * Checker function to find out how many tokens can be withdrawn.
 * note: percentage of the token.totalSupply
 * @dev Based on division down rounding
 */

function canWithdraw() public view returns (uint256) {
        uint256 sinceStart = now - start;
        uint256 allowed;

        if (sinceStart >= 0) {
            allowed = 555000000000000;
        } else if (sinceStart >= 31536000) { // one year difference
            allowed = 1480000000000000;
        } else if (sinceStart >= 63072000) { // two years difference
            allowed = 3330000000000000;
        } else {
            return 0;
        }
        return allowed - withdrawn;
    }


/*
 * Approve function to adjust allowance to investment of each individual investor
 * @param _investor address sets the beneficiary for later use
 * @param _referral address to pay a commission in token to
 * @param _commission uint8 expressed as a number between 0 and 5
 */

function approve(address _investor, uint8 _commission, uint8 _extra) onlyOwner public{
        require(!isContract(_investor));
        verified[_investor].approved = true;
        if (_commission <= 15 && _extra <= 5) {
            verified[_investor].commission = _commission;
            verified[_investor].extra = _extra;
            BonusesRegistered(_investor, _commission, _extra);
        }
        ApprovedInvestor(_investor);
    }


/*
 * Constructor changing owner to owner multisig, setting all the contract addresses & compensation rates
 * @param address of the Signals Token contract
 * @param address of the KYC registry
 * @param address of the owner multisig
 * @param uint rate of the compensation for early investors
 * @param uint rate of the compensation for partners
 */

function PresalePool(address _token, address _registry, address _owner, uint comp1, uint comp2) public {
        owner = _owner;
        PublicPresale = PresaleToken(0x15fEcCA27add3D28C55ff5b01644ae46edF15821);
        PartnerPresale = PresaleToken(0xa70435D1a3AD4149B0C13371E537a22002Ae530d);
        token = SignalsToken(_token);
        registry = CrowdsaleRegister(_registry);
        compensation1 = comp1;
        compensation2 = comp2;
        deadLine = now + 30 days;
    }


/*
 * Function swapping the presale tokens for the Signal tokens regardless on the presale pool
 * @dev requires having ownership of the two presale contracts
 * @dev requires the calling party to finish the KYC process fully
 */

function swap() public {
        require(registry.approved(msg.sender));
        uint256 oldBalance;
        uint256 newBalance;

        if (PublicPresale.balanceOf(msg.sender) > 0) {
            oldBalance = PublicPresale.balanceOf(msg.sender);
            newBalance = oldBalance * compensation1 / 100;
            PublicPresale.burnTokens(msg.sender, oldBalance);
            token.transfer(msg.sender, newBalance);
            SupporterResolved(msg.sender, oldBalance, newBalance);
        }

        if (PartnerPresale.balanceOf(msg.sender) > 0) {
            oldBalance = PartnerPresale.balanceOf(msg.sender);
            newBalance = oldBalance * compensation2 / 100;
            PartnerPresale.burnTokens(msg.sender, oldBalance);
            token.transfer(msg.sender, newBalance);
            PartnerResolved(msg.sender, oldBalance, newBalance);
        }
    }


/*
 * Function swapping the presale tokens for the Signal tokens regardless on the presale pool
 * @dev initiated from Signals (passing the ownership to a oracle to handle a script is recommended)
 * @dev requires having ownership of the two presale contracts
 * @dev requires the calling party to finish the KYC process fully
 */

function swapFor(address whom) onlyOwner public returns(bool) {
        require(registry.approved(whom));
        uint256 oldBalance;
        uint256 newBalance;

        if (PublicPresale.balanceOf(whom) > 0) {
            oldBalance = PublicPresale.balanceOf(whom);
            newBalance = oldBalance * compensation1 / 100;
            PublicPresale.burnTokens(whom, oldBalance);
            token.transfer(whom, newBalance);
            SupporterResolved(whom, oldBalance, newBalance);
        }

        if (PartnerPresale.balanceOf(whom) > 0) {
            oldBalance = PartnerPresale.balanceOf(whom);
            newBalance = oldBalance * compensation2 / 100;
            PartnerPresale.burnTokens(whom, oldBalance);
            token.transfer(whom, newBalance);
            SupporterResolved(whom, oldBalance, newBalance);
        }

        return true;
    }


/*
 * Buy in function to be called from the fallback function
 * @param beneficiary address
 */

function buyTokens(address beneficiary) private {
        require(beneficiary != 0x0);
        require(validPurchase());

        uint256 weiAmount = msg.value;

        // base discount
        uint256 discount = ((toBeRaised*10000)/HARD_CAP)*15;

        // calculate token amount to be created
        uint256 tokens;

        // update state
        weiRaised = weiRaised.add(weiAmount);
        toBeRaised = toBeRaised.sub(weiAmount);

        uint commission;
        uint extra;
        uint premium;

        if (register.approved(beneficiary)) {
            (commission, extra) = register.getBonuses(beneficiary);

            // If extra access granted then give additional %
            if (extra > 0) {
                discount += extra*10000;
            }
            tokens =  howMany(msg.value, discount);

            // If referral was involved, give some percent to the source
            if (commission > 0) {
                premium = tokens.mul(commission).div(100);
                token.mint(BOUNTY, premium);
            }

        } else {
            extra = register2.getBonuses(beneficiary);
            if (extra > 0) {
                discount = extra*10000;
                tokens =  howMany(msg.value, discount);
            }
        }

        token.mint(beneficiary, tokens);
        TokenPurchase(msg.sender, beneficiary, weiAmount, tokens);
        tokensSold += tokens + premium;
        forwardFunds();

        assert(token.totalSupply() <= maxTokens);
    }


/// @dev See `IERC20.approve`

function approve(address _spender, uint _amount) public override returns (bool success) {
        uint96 amount;
        if (_amount == uint(SafeCast.toUint256(-1))) {
            amount = uint96(SafeCast.toUint256(-1));
        } else {
            amount = safe96(_amount, "DAWG::permit: amount exceeds 96 bits");
        }

        _approve(msg.sender, _spender, amount);
        return true;
    }


/// @dev `See ERC20i.transferFrom`

function transferFrom(address _from, address _to, uint _amount) public override returns (bool success) {
        address spender = msg.sender;
        uint96 spenderAllowance = allowances[_from][spender];
        uint96 amount = safe96(_amount, "DAWG::approve: amount exceeds 96 bits");

        if (spender != _from && spenderAllowance != uint96(SafeCast.toUint256(-1))) {
            uint96 newAllowance = sub96(spenderAllowance, amount, "DAWG::transferFrom: transfer amount exceeds spender allowance");
            allowances[_from][spender] = newAllowance;

            emit Approval(_from, spender, newAllowance);
        }

        _transferTokens(_from, _to, amount);
        return true;
    }


/**
 * @dev Function for loomiVault deposit
 */

function deposit(uint256[] memory tokenIds) public nonReentrant whenNotPaused {
      require(tokenIds.length > 0, "Empty array");
      Staker storage user = _stakers[_msgSender()];

      if (user.stakedVault.length == 0) {
        uint256 currentLoomiPot = _getLoomiPot();
        user.loomiPotSnapshot = currentLoomiPot;
      }
      accumulate(_msgSender());

      for (uint256 i; i < tokenIds.length; i++) {
        require(loomiVault.ownerOf(tokenIds[i]) == _msgSender(), "Not the owner");
        loomiVault.safeTransferFrom(_msgSender(), address(this), tokenIds[i]);

        _ownerOfToken[tokenIds[i]] = _msgSender();

        user.stakedVault.push(tokenIds[i]);
      }

      emit Deposit(_msgSender(), tokenIds.length);
    }


/**
 * @dev Function for loomiVault withdraw
 */

function withdraw(uint256[] memory tokenIds) public nonReentrant whenNotPaused {
      require(tokenIds.length > 0, "Empty array");

      Staker storage user = _stakers[_msgSender()];
      accumulate(_msgSender());

      for (uint256 i; i < tokenIds.length; i++) {
        require(loomiVault.ownerOf(tokenIds[i]) == address(this), "Not the owner");

        _ownerOfToken[tokenIds[i]] = address(0);
        user.stakedVault = _moveTokenInTheList(user.stakedVault, tokenIds[i]);
        user.stakedVault.pop();

        loomiVault.safeTransferFrom(address(this), _msgSender(), tokenIds[i]);
      }

      emit Withdraw(_msgSender(), tokenIds.length);
    }


/**
 * @dev Function for loomi reward claim
 * @notice caller must own a Genesis Creepz
 */

function claim(uint256 tokenId) public nonReentrant whenNotPaused {
      Staker storage user = _stakers[_msgSender()];
      accumulate(_msgSender());

      require(user.accumulatedAmount > 0, "Insufficient funds");
      require(_validateCreepzOwner(tokenId, _msgSender()), "!Creepz owner");

      uint256 currentLoomiPot = _getLoomiPot();
      uint256 prevLoomiPot = user.loomiPotSnapshot;
      uint256 change = currentLoomiPot * DIVIDER / prevLoomiPot;
      uint256 finalAmount = user.accumulatedAmount * change / DIVIDER;

      user.loomiPotSnapshot = currentLoomiPot;
      user.accumulatedAmount = 0;
      loomi.depositLoomiFor(_msgSender(), finalAmount);

      emit Claim(_msgSender(), finalAmount);
    }


/**
 * @dev Helper function for arrays
 */

function _moveTokenInTheList(uint256[] memory list, uint256 tokenId) internal pure returns (uint256[] memory) {
      uint256 tokenIndex = 0;
      uint256 lastTokenIndex = list.length - 1;
      uint256 length = list.length;

      for(uint256 i = 0; i < length; i++) {
        if (list[i] == tokenId) {
          tokenIndex = i + 1;
          break;
        }
      }
      require(tokenIndex != 0, "msg.sender is not the owner");

      tokenIndex -= 1;

      if (tokenIndex != lastTokenIndex) {
        list[tokenIndex] = list[lastTokenIndex];
        list[lastTokenIndex] = tokenId;
      }

      return list;
    }


/**
 * @dev Returns accumulated amount from last snapshot based on baseRate
 */

function getCurrentReward(address staker) public view returns (uint256) {
      Staker memory user = _stakers[staker];
      if (user.lastCheckpoint == 0) { return 0; }
      return (block.timestamp - user.lastCheckpoint) * (baseYield * user.stakedVault.length) / SECONDS_IN_DAY;
    }


/**
 * @dev Function allows admin withdraw ERC721 in case of emergency.
 */

function emergencyWithdraw(address tokenAddress, uint256[] memory tokenIds) public onlyOwner {
      require(tokenIds.length <= 50, "50 is max per tx");
      for (uint256 i; i < tokenIds.length; i++) {
        address receiver = _ownerOfToken[tokenIds[i]];
        if (receiver != address(0) && IERC721(tokenAddress).ownerOf(tokenIds[i]) == address(this)) {
          IERC721(tokenAddress).transferFrom(address(this), receiver, tokenIds[i]);
          emit WithdrawStuckERC721(receiver, tokenAddress, tokenIds[i]);
        }
      }
    }


/// @dev Emits an {Approval} event indicating the updated allowance.

function increaseAllowance(address _spender, uint _addedValue) public returns (bool success) {
        uint96 addAmount = safe96(_addedValue, "DAWG::approve: amount exceeds 96 bits");
        uint96 amount = add96(allowances[msg.sender][_spender], addAmount, "DAWG::increaseAllowance: increase allowance exceeds 96 bits");

        allowances[msg.sender][_spender] = amount;

        emit Approval(msg.sender, _spender, amount);
        return true;
    }


/**
 * @notice Allows spender to `spender` on `owner`'s behalf
 * @param owner address that holds tokens
 * @param spender address that spends on `owner`'s behalf
 * @param _amount unsigned integer denoting amount, uncast
 * @param deadline 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 permit(address owner, address spender, uint _amount, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
        uint96 amount;
        if (_amount == uint(SafeCast.toUint256(-1))) {
            amount = uint96(SafeCast.toUint256(-1));
        } else {
            amount = safe96(_amount, "DAWG::permit: amount exceeds 96 bits");
        }

        bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(_NAME)), getChainId(), address(this)));
        bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, _amount, nonces[owner]++, deadline));
        bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
        address signatory = ecrecover(digest, v, r, s);
        require(signatory != address(0), "DAWG::permit: invalid signature");
        require(signatory == owner, "DAWG::permit: unauthorized");
        require(block.timestamp <= deadline, "DAWG::permit: signature expired");

        allowances[owner][spender] = amount;

        emit Approval(owner, spender, amount);
    }


//
// Override isApprovedForAll to whitelist user's OpenSea proxy accounts to enable gas-less listings.
// https://github.com/ProjectOpenSea/opensea-creatures/blob/master/contracts/ERC721Tradable.sol
//

function isApprovedForAll(address _owner, address _operator)
        public
        view
        override
        returns (bool isOperator)
    {
        // Whitelist OpenSea proxy contract for easy trading.
        ProxyRegistry proxyRegistry = ProxyRegistry(proxyRegistryAddress);
        if (address(proxyRegistry.proxies(_owner)) == _operator) {
            return true;
        }

        return ERC721.isApprovedForAll(_owner, _operator);
    }


/// @dev Moves tokens `_amount` from `"sender"` to `"recipient"`
/// Emits a {Transfer} event

function _transfer(address _from, address _to, uint96 amount) internal {
        require(_from != address(0), "ERC20: cannot transfer from the zero address");
        require(_to != address(0), "ERC20: cannot transfer to the zero address");

        balances[_from] = sub96(balances[_from], amount, "DAWG::_transferTokens: transfer amount exceeds balance"); // balances[_from].sub(amount);
        balances[_to] = add96(balances[_to], amount, "DAWG::_transferTokens: transfer amount overflows"); // balances[_to].add(amount);
        emit Transfer(_from, _to, amount);

        _moveDelegates(delegates[_from], delegates[_to], amount);
    }


/// @dev Sets given `_amount` as the allowance of a `_spender` for the `_owner`'s tokens.
//// Emits a {Approval} event

function _approve(address _owner, address _spender, uint96 amount) internal {
        require(_owner != address(0), "ERC20: approve from the zero address");
        require(_spender != address(0), "ERC20: approve to the zero address");

        allowances[_owner][_spender] = amount;
        emit Approval(_owner, _spender, amount);
    }


//@dev this function allows purchase of ERC-20 tokens

function buyTokens(uint256 _numberOfTokens) external payable {
        require(msg.value == _numberOfTokens.mul(tokenPrice));
        uint256 numberOfTokens = _numberOfTokens.mul(10**(tokenContract.decimals()));
       //@mul only to prevent overflow
        require(tokenContract.totalSupply() >= numberOfTokens);
        require(saleActive == true);
        tokenContract.transfer(msg.sender, numberOfTokens);
        tokensSold += numberOfTokens;
        emit Sell(msg.sender, _numberOfTokens);
      }


/**
 * Get random index
 */

function randomIndex() internal returns (uint256) {
        uint256 totalSize = MAX_TOKENS - numTokens;
        uint256 index = uint256(
            keccak256(
                abi.encodePacked(
                    nonce,
                    msg.sender,
                    block.difficulty,
                    block.timestamp
                )
            )
        ) % totalSize;
        uint256 value = 0;
        if (indices[index] != 0) {
            value = indices[index];
        } else {
            value = index;
        }

        // Move last value to selected position
        if (indices[totalSize - 1] == 0) {
            // Array position not initialized, so use position
            indices[index] = totalSize - 1;
        } else {
            // Array position holds a value so use that
            indices[index] = indices[totalSize - 1];
        }
        nonce++;
        // Don't allow a zero index, start counting at 1
        return value.add(1);
    }


/**
 * Mint one token internal
 */

function _internalMint(address to) private returns (uint256) {
        require(numTokens < MAX_TOKENS, "Token limit");

        //Get random token
        uint256 id = randomIndex();

        //Change internal token amount
        numTokens++;

        //Mint token
        _mint(to, id);
        return id;
    }


/**
 * Mint selected amount of tokens
 */

function mint(address _to, uint8 _amount) public payable {
        require(mintEnabled, "Minting disabled");
        require(_amount <= 20, "Maximum 20 tokens per mint");
        require(msg.value >= _amount * tokenPrice, "Not enought money");
        require(_to != address(0), "Cannot mint to empty");

        _ensureDailyLimit(_amount);

        uint256 balance = msg.value;

        for (uint8 i = 0; i < _amount; i++) {
            // Sub user balance
            balance = balance.sub(tokenPrice);

            // Mint token tok user
            _internalMint(_to);
        }

        // Return not used balance
        payable(msg.sender).transfer(balance);
    }


/**
 * Mint selected amount of tokens to a given address by owner (airdrop)
 */

function airdrop(address _to, uint8 _amount) public onlyOwner {
        require(mintEnabled, "Minting disabled");
        require(_amount <= 20, "Maximum 20 tokens per mint");
        require(_to != address(0), "Cannot mint to empty");

        for (uint8 i = 0; i < _amount; i++) {
            // Mint token tok user
            _internalMint(_to);
        }
    }


/**
 * @dev Mints a token to an address with a tokenURI.
 * fee may or may not be required*
 * @param _to address of the future owner of the token
 */

function mintTo(address _to) public payable {
            require(_getNextTokenId() <= SUPPLYCAP, "Cannot mint over supply cap of 2");
            require(mintingOpen == true, "Minting is not open right now!");
            require(canMintAmount(_to, 1), "Wallet address is over the maximum allowed mints");

            uint256 newTokenId = _getNextTokenId();
            _mint(_to, newTokenId);
            _incrementTokenId();
            updateMintCount(_to, 1);
        }


// @notice Query if an address implements an interface and through which contract.
// @param _addr Address being queried for the implementer of an interface.
// (If '_addr' is the zero address then 'msg.sender' is assumed.)
// @param _interfaceHash Keccak256 hash of the name of the interface as a string.
// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient' interface.
// @return The address of the contract which implements the interface '_interfaceHash' for '_addr'
// or '0' if '_addr' did not register an implementer for this interface.

function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view override  returns (address) {
        address addr = _addr == address(0) ? msg.sender : _addr;
        if (isERC165Interface(_interfaceHash)) {
            bytes4 erc165InterfaceHash = bytes4(_interfaceHash);
            return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : address(0);
        }
        return interfaces[addr][_interfaceHash];
    }


// @notice Sets the contract which implements a specific interface for an address.
// Only the manager defined for that address can set it.
// (Each address is the manager for itself until it sets a new manager.)
// @param _addr Address for which to set the interface.
// (If '_addr' is the zero address then 'msg.sender' is assumed.)
// @param _interfaceHash Keccak256 hash of the name of the interface as a string.
// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient' interface.
// @param _implementer Contract address implementing '_interfaceHash' for '_addr'.

function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external override {
        address addr = _addr == address(0) ? msg.sender : _addr;
        require(getManager(addr) == msg.sender, "Not the manager");

        require(!isERC165Interface(_interfaceHash), "Must not be an ERC165 hash");
        if (_implementer != address(0) && _implementer != msg.sender) {
            require(
                IERC1820Implementer(_implementer)
                    .canImplementInterfaceForAddress(_interfaceHash, addr) == ERC1820_ACCEPT_MAGIC,
                "Does not implement the interface"
            );
        }
        interfaces[addr][_interfaceHash] = _implementer;
        emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);
    }


// @notice Get the manager of an address.
// @param _addr Address for which to return the manager.
// @return Address of the manager for a given address.

function getManager(address _addr) public view override  returns(address) {
        // By default the manager of an address is the same address
        if (managers[_addr] == address(0)) {
            return _addr;
        } else {
            return managers[_addr];
        }
    }


// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
// @param _contract Address of the contract to check.
// @param _interfaceId ERC165 interface to check.
// @return True if '_contract' implements '_interfaceId', false otherwise.

function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view override returns (bool) {
        uint256 success;
        uint256 result;

        (success, result) = noThrowCall(_contract, ERC165ID);
        if (success == 0 || result == 0) {
            return false;
        }

        (success, result) = noThrowCall(_contract, INVALID_ID);
        if (success == 0 || result != 0) {
            return false;
        }

        (success, result) = noThrowCall(_contract, _interfaceId);
        if (success == 1 && result == 1) {
            return true;
        }
        return false;
    }


// @dev Make a call on a contract without throwing if the function does not exist.

function noThrowCall(address _contract, bytes4 _interfaceId)
        internal view returns (uint256 success, uint256 result)
    {
        bytes4 erc165ID = ERC165ID;

        assembly {
            let x := mload(0x40)               // Find empty storage location using "free memory pointer"
            mstore(x, erc165ID)                // Place signature at beginning of empty storage
            mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature

            success := staticcall(
                30000,                         // 30k gas
                _contract,                     // To addr
                x,                             // Inputs are stored at location x
                0x24,                          // Inputs are 36 (4 + 32) bytes long
                x,                             // Store output over input (saves space)
                0x20                           // Outputs are 32 bytes long
            )

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


// gets the amount of rew

function rewardPerToken(address _rewardToken)
        internal
        view
        returns (uint256 rewardPerToken_, uint256 lastTimeRewardApplicable_)
    {
        RewardToken storage rewardToken = s.rewardTokens[_rewardToken];
        uint256 l_totalSupply = s.totalSupply;
        uint256 lastUpdateTime = rewardToken.lastUpdateTime;
        lastTimeRewardApplicable_ = lastTimeRewardApplicable(_rewardToken);
        if (lastUpdateTime == 0 || l_totalSupply == 0) {
            rewardPerToken_ = rewardToken.rewardPerTokenStored;
        } else {
            rewardPerToken_ =
                rewardToken.rewardPerTokenStored +
                ((lastTimeRewardApplicable_ - lastUpdateTime) *
                    rewardToken.rewardRate *
                    1e18) /
                l_totalSupply;
        }
    }


/**
 * @dev Stake ERC-1155
 */

function stake(uint256[] calldata tokenIds, uint256[] calldata amounts) external nonReentrant whenNotPaused updateReward(msg.sender) {
        require(tokenIds.length == amounts.length, "TokenIds and amounts length should be the same");
        for(uint256 i = 0; i < tokenIds.length; i++) {
            stakeInternal(tokenIds[i], amounts[i]);
        }
    }


/**
 * @dev Stake particular ERC-1155 tokenId
 */

function stakeInternal(uint256 tokenId, uint256 amount) internal {
        (,,,,,uint256 series) = INodeRunnersNFT(address(NFT)).getFighter(tokenId);
        if(!nftTokenMap[tokenId].hasValue) {
            nftTokens.push(tokenId);
            nftTokenMap[tokenId] = NftToken({ hasValue: true });
        }
        require(supportedSeries[series] == true, "wrong id");
        require(nftTokenMap[tokenId].balances[msg.sender].add(amount) <= tokenMaxAmount, "NFT max reached");

        (uint256 strength,,,,,) = INodeRunnersNFT(address(NFT)).getFighter(tokenId);
        strength = strength.mul(amount);

        strengthWeight[msg.sender] = strengthWeight[msg.sender].add(strength);
        _totalStrength = _totalStrength.add(strength);
        _totalSupply = _totalSupply.add(amount);
        _balances[msg.sender] = _balances[msg.sender].add(amount);
        nftTokenMap[tokenId].balances[msg.sender] = nftTokenMap[tokenId].balances[msg.sender].add(amount);

        NFT.safeTransferFrom(msg.sender, address(this), tokenId, amount, "0x0");
        emit Staked(msg.sender, tokenId, amount);
    }


/**
 * @dev Withdraw ERC-1155
 */

function withdrawNFT(uint256[] memory tokenIds, uint256[] memory amounts) public updateReward(msg.sender) {
        require(tokenIds.length == amounts.length, "TokenIds and amounts length should be the same");
        for(uint256 i = 0; i < tokenIds.length; i++) {
            withdrawNFTInternal(tokenIds[i], amounts[i]);
        }
    }


/**
 * @dev Withdraw particular ERC-1155 tokenId
 */

function withdrawNFTInternal(uint256 tokenId, uint256 amount) internal {
        (uint256 strength,,,,,) = INodeRunnersNFT(address(NFT)).getFighter(tokenId);
        strength = strength.mul(amount);

        strengthWeight[msg.sender] = strengthWeight[msg.sender].sub(strength);
        _totalStrength = _totalStrength.sub(strength);
        _totalSupply = _totalSupply.sub(amount);
        _balances[msg.sender] = _balances[msg.sender].sub(amount);
        nftTokenMap[tokenId].balances[msg.sender] = nftTokenMap[tokenId].balances[msg.sender].sub(amount);

        NFT.safeTransferFrom(address(this), msg.sender, tokenId, amount, "0x0");
        emit Withdrawn(msg.sender, tokenId, amount);
    }


/**
 * @dev Withdraw all cards
 */

function withdraw() public updateReward(msg.sender) {
        uint256[] memory tokenIds = new uint256[](nftTokens.length);
        uint256[] memory amounts = new uint256[](nftTokens.length);
        for (uint8 i = 0; i < nftTokens.length; i++) {
            uint256 tokenId = nftTokens[i];
            tokenIds[i] = tokenId;
            uint256 balance = nftTokenMap[tokenId].balances[msg.sender];
            amounts[i] = balance;
        }
        withdrawNFT(tokenIds, amounts);
    }


/// @dev uADPriceReset remove uAD unilateraly from the curve LP share sitting inside
///      the bonding contract and send the uAD received to the treasury.
///      This will have the immediate effect of pushing the uAD price HIGHER
/// @param amount of LP token to be removed for uAD
/// @notice it will remove one coin only from the curve LP share sitting in the bonding contract

function uADPriceReset(uint256 amount) external onlyBondingManager {
        IMetaPool metaPool = IMetaPool(manager.stableSwapMetaPoolAddress());
        // safe approve
        IERC20(manager.stableSwapMetaPoolAddress()).safeApprove(
            address(this),
            amount
        );
        // remove one coin
        uint256 expected =
            (metaPool.calc_withdraw_one_coin(amount, 0) * 99) / 100;
        // update twap
        metaPool.remove_liquidity_one_coin(amount, 0, expected);
        ITWAPOracle(manager.twapOracleAddress()).update();
        IERC20(manager.dollarTokenAddress()).safeTransfer(
            manager.treasuryAddress(),
            IERC20(manager.dollarTokenAddress()).balanceOf(address(this))
        );
    }


/// @dev crvPriceReset remove 3CRV unilateraly from the curve LP share sitting inside
///      the bonding contract and send the 3CRV received to the treasury
///      This will have the immediate effect of pushing the uAD price LOWER
/// @param amount of LP token to be removed for 3CRV tokens
/// @notice it will remove one coin only from the curve LP share sitting in the bonding contract

function crvPriceReset(uint256 amount) external onlyBondingManager {
        IMetaPool metaPool = IMetaPool(manager.stableSwapMetaPoolAddress());
        // safe approve
        IERC20(manager.stableSwapMetaPoolAddress()).safeApprove(
            address(this),
            amount
        );
        // remove one coin
        uint256 expected =
            (metaPool.calc_withdraw_one_coin(amount, 1) * 99) / 100;
        // update twap
        metaPool.remove_liquidity_one_coin(amount, 1, expected);
        ITWAPOracle(manager.twapOracleAddress()).update();
        IERC20(manager.curve3PoolTokenAddress()).safeTransfer(
            manager.treasuryAddress(),
            IERC20(manager.curve3PoolTokenAddress()).balanceOf(address(this))
        );
    }


/// @dev deposit uAD-3CRV LP tokens for a duration to receive bonding shares
/// @param _lpsAmount of LP token to send
/// @param _weeks during lp token will be held
/// @notice weeks act as a multiplier for the amount of bonding shares to be received

function deposit(uint256 _lpsAmount, uint256 _weeks)
        public
        returns (uint256 _id)
    {
        require(
            1 <= _weeks && _weeks <= 208,
            "Bonding: duration must be between 1 and 208 weeks"
        );
        _updateOracle();

        IERC20(manager.stableSwapMetaPoolAddress()).safeTransferFrom(
            msg.sender,
            address(this),
            _lpsAmount
        );

        uint256 _sharesAmount =
            IUbiquityFormulas(manager.formulasAddress()).durationMultiply(
                _lpsAmount,
                _weeks,
                bondingDiscountMultiplier
            );

        // 1 week = 45361 blocks = 2371753*7/366
        // n = (block + duration * 45361)
        // id = n - n % blockRonding
        // blockRonding = 100 => 2 ending zeros
        uint256 n = block.number + _weeks * blockCountInAWeek;
        _id = n - (n % blockRonding);
        _mint(_sharesAmount, _id);
        // set masterchef for uGOV rewards
        IMasterChef(manager.masterChefAddress()).deposit(
            _sharesAmount,
            msg.sender
        );
    }


/// @dev withdraw an amount of uAD-3CRV LP tokens
/// @param _sharesAmount of bonding shares of type _id to be withdrawn
/// @param _id bonding shares id
/// @notice bonding shares are ERC1155 (aka NFT) because they have an expiration date

function withdraw(uint256 _sharesAmount, uint256 _id) public {
        require(
            block.number > _id,
            "Bonding: Redeem not allowed before bonding time"
        );

        require(
            IERC1155Ubiquity(manager.bondingShareAddress()).balanceOf(
                msg.sender,
                _id
            ) >= _sharesAmount,
            "Bonding: caller does not have enough shares"
        );

        _updateOracle();
        // get masterchef for uGOV rewards To ensure correct computation
        // it needs to be done BEFORE burning the shares
        IMasterChef(manager.masterChefAddress()).withdraw(
            _sharesAmount,
            msg.sender
        );

        uint256 _currentShareValue = currentShareValue();

        IERC1155Ubiquity(manager.bondingShareAddress()).burn(
            msg.sender,
            _id,
            _sharesAmount
        );

        // if (redeemStreamTime == 0) {
        IERC20(manager.stableSwapMetaPoolAddress()).safeTransfer(
            msg.sender,
            IUbiquityFormulas(manager.formulasAddress()).redeemBonds(
                _sharesAmount,
                _currentShareValue,
                ONE
            )
        );
    }


//Custom Minting Function for utility (to be called by trusted contracts)

function mintCustomWarrior(address owner, uint32 generation, bool special, uint randomSeed, uint16 colorHue, uint8 armorType, uint8 shieldType, uint8 weaponType) public onlyTrustedContracts returns(uint theNewWarrior) {
        //Calculate new Token ID for minting
        uint256 _id = _getNextTokenID();
        _incrementTokenTypeId();
        //Log the original creator of this warrior
        creators[_id] = owner;
		//Generate a new warrior metadata structure, and add it to the warriors array
        warriors[_id]=LibWarrior.newWarriorFixed(owner, generation, special, randomSeed, colorHue, armorType, shieldType, weaponType);
        //Mint the new token in the ledger
        _mint(owner, _id, 1, "");
        tokenSupply[_id] = 1;
		//Return new warrior index
        return _id;
	}


//Standard factory function, allowing minting warriors.

function newWarrior() public returns(uint theNewWarrior) {
        //Generate a new random seed for the warrior
        uint randomSeed = uint(blockhash(block.number - 1));    //YES WE KNOW this isn't truely random. it's predictable, and vulnerable to malicious miners...
                                                                //Doesn't actually matter in this case. That's all ok. It's only for generating cosmetics etc...
        //Calculate the fee:
        uint warriorFee = LibWarrior.getWarriorCost();

        //Take fee from the owner
        transferFAME(msg.sender,address(this),warriorFee);

        //Calculate new Token ID for minting
        uint256 _id = _getNextTokenID();
        _incrementTokenTypeId();

        //Log the original creator of this warrior
        creators[_id] = msg.sender;

		//Generate a new warrior metadata structure, and add it to the warriors array
        warriors[_id]=LibWarrior.newWarrior(msg.sender, randomSeed);

		//Transfer the paid fee to the warrior as initial starting FAME
        FAMEToWarrior(_id,warriorFee,false);

        //Mint the new token in the ledger
        _mint(msg.sender, _id, 1, "");
        tokenSupply[_id] = 1;

		//Return new warrior index
        return _id;
	}


//// Secure payable fallback function - receives bets

function () external payable {
        require((msg.value == oneBet) || (msg.sender == owner));
        if (msg.sender != owner) {
            require(betsState && !emergencyBlock);
            require(!betsBlock);
            if (numberOfBets < participants+(extraBets-1)) {
                bets[numberOfBets] = msg.sender;
                numberOfBets++;
                emit GotBet(roundID, msg.sender, numberOfBets);
            } else {
                bets[numberOfBets] = msg.sender;
                numberOfBets++;
                emit GotBet(roundID, msg.sender, numberOfBets);
                betsState = false;
                emit ReadyToRoll(roundID, participants+extraBets, oneBet);
            }
        }
    }


//// Withdraw and distribute winnings

function withdrawWinnings() external payable
            onlyProxy
    {
        if ((msg.value > expectedReturn) && !emergencyBlock) {
            emit BetResult(roundID, 1, msg.value); // We won! Set 1
            distributeWinnings(msg.value);
        } else {
            emit BetResult(roundID, 0, msg.value); // We lost :( Set 0
        }

        numberOfBets = 0;
        betsState = true;
        roundID++;
    }


/**
 * @dev Allows the transfer of token amounts to multiple addresses
 * @param beneficiaries Array of addresses that receive the tokens
 * @param amounts Array of amounts to be transferred per beneficiary
 */

function multiTransfer(address[] calldata beneficiaries, uint256[] calldata amounts) external {
        uint256 length = beneficiaries.length;
        require(length == amounts.length, "lenghts are different");

        for (uint256 i = 0; i < length; i++) {
            super.transfer(beneficiaries[i], amounts[i]);
        }
    }


// ========== RESTRICTED ==========

function setCrossDomainMessageGasLimit(CrossDomainMessageGasLimits _gasLimitType, uint _crossDomainMessageGasLimit)
        external
        onlyOwner
    {
        require(
            _crossDomainMessageGasLimit >= MIN_CROSS_DOMAIN_GAS_LIMIT &&
                _crossDomainMessageGasLimit <= MAX_CROSS_DOMAIN_GAS_LIMIT,
            "Out of range xDomain gasLimit"
        );
        flexibleStorage().setUIntValue(
            SETTING_CONTRACT_NAME,
            _getGasLimitSetting(_gasLimitType),
            _crossDomainMessageGasLimit
        );
        emit CrossDomainMessageGasLimitChanged(_gasLimitType, _crossDomainMessageGasLimit);
    }


/**
 * @dev Returns the current implementation of `proxy`.
 *
 * Requirements:
 *
 * - This contract must be the admin of `proxy`.
 */

function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
        // We need to manually run the static call since the getter cannot be flagged as view
        // bytes4(keccak256("implementation()")) == 0x5c60da1b
        (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
        require(success);
        return abi.decode(returndata, (address));
    }


// Transfer the _amount from msg.sender to _to account

function transfer(address _to, uint256 _amount) public returns (bool) {
        if (balances[msg.sender] >= _amount && _amount > 0
                && balances[_to] + _amount > balances[_to]) {
            balances[msg.sender] -= _amount;
            balances[_to] += _amount;
            Transfer(msg.sender, _to, _amount);
            return true;
        } else {
            return false;
        }
    }


// fallback function, Charon sells napkins as merchandise!

function () public payable {
        uint256 amount;
        uint256 checkedAmount;
        // give away some napkins in return proportional to value
        amount = msg.value / napkinPrice;
        checkedAmount = checkAmount(amount);
        // only payout napkins if there is the appropriate amount available
        // else throw
        require(amount == checkedAmount);
        // forward money to Charon
        payCharon(msg.value);
        // finally payout napkins
        payOutNapkins(checkedAmount);
    }


// sells your soul for a given price and a given reason!

function sellSoul(string reason, uint256 price) public payable{
        uint256 charonsObol;
        string storage has_reason = reasons[msg.sender];

        // require that user gives a reason
        require(bytes(reason).length > 0);

        // require to pay bookingFee
        require(msg.value >= bookingFee);

        // you cannot give away your soul for free, at least Charon wants some share
        charonsObol = price / obol;
        require(charonsObol > 0);

        // assert has not sold her or his soul, yet
        require(bytes(has_reason).length == 0);
        require(soulPrices[msg.sender] == 0);
        require(ownedBy[msg.sender] == address(0));

        // pay book keeping fee
        payCharon(msg.value);

        // store the reason forever on the blockchain
        reasons[msg.sender] = reason;
        // also the price is forever kept on the blockchain, so do not be too cheap
        soulPrices[msg.sender] = price;
        // and keep the soul in the soul book
        soulBook[soulsForSale + soulsSold] = msg.sender;
        soulsForSale += 1;
    }


// can transfer a soul to a different account, but beware you have to pay Charon again!

function transferSoul(address _to, address noSoulMate) public payable{
        uint256 charonsObol;

        // require correct ownership
        require(ownedBy[noSoulMate] == msg.sender);
        require(soulsOwned[_to] + 1 > soulsOwned[_to]);
        // require transfer fee is payed again
        charonsObol = soulPrices[noSoulMate] / obol;
        require(msg.value >= charonsObol);
        // pay Charon
        payCharon(msg.value);
        // transfer the soul
        soulsOwned[msg.sender] -= 1;
        soulsOwned[_to] += 1;
        ownedBy[noSoulMate] = _to;

        // Log the soul transfer
        SoulTransfer(msg.sender, _to);
    }


// transfers napkins to people

function payOutNapkins(uint256 amount) internal{
        // check for amount and wrap around
        require(amount > 0);
        // yeah some sanity check
        require(amount <= balances[this]);

        // send napkins from contract to msg.sender
        balances[this] -= amount;
        balances[msg.sender] += amount;
        // log napkin transfer
        Transfer(this, msg.sender, amount);
    }


/**
 * @notice Extract a bytes32 from a bytes.
 * @param data bytes from where the bytes32 will be extract
 * @param offset position of the first byte of the bytes32
 * @return address
 */

function extractBytes32(bytes memory data, uint offset)
    internal
    pure
    returns (bytes32 bs)
  {
    require(offset >= 0 && offset + 32 <= data.length, "offset value should be in the correct range");

    // solium-disable-next-line security/no-inline-assembly
    assembly {
        bs := mload(add(data, add(32, offset)))
    }
  }


/**
 * @notice Computes the fees.
 * @param _contentSize Size of the content of the block to be stored
 * @return the expected amount of fees in wei
 */

function getFeesAmount(uint256 _contentSize)
    public
    view
    returns(uint256)
  {
    // Transactions fee
    uint256 computedAllFee = _contentSize.mul(rateFeesNumerator);

    if (rateFeesDenominator != 0) {
      computedAllFee = computedAllFee.div(rateFeesDenominator);
    }

    if (computedAllFee <= minimumFee) {
      return minimumFee;
    } else {
      return computedAllFee;
    }
  }


/**
 * @notice Submit a new hash to the blockchain.
 *
 * @param _hash Hash of the request to be stored
 * @param _feesParameters fees parameters used to compute the fees. Here, it is the content size in an uint256
 */

function submitHash(string calldata _hash, bytes calldata _feesParameters)
    external
    payable
  {
    // extract the contentSize from the _feesParameters
    uint256 contentSize = uint256(Bytes.extractBytes32(_feesParameters, 0));

    // Check fees are paid
    require(getFeesAmount(contentSize) == msg.value, "msg.value does not match the fees");

    // Send fees to burner, throws on failure
    collectForREQBurning(msg.value);

    // declare the hash to the whole system through to RequestHashStorage
    requestHashStorage.declareNewHash(_hash, _feesParameters);
  }


// compressBoard removes last col and last row and joins digits into one number.


/**
 * @dev Add Account to User Linked List.
 * @param _owner Account Owner.
 * @param _account Smart Account Address.
 */

function addAccount(address _owner, uint64 _account) internal {
        if (userLink[_owner].last != 0) {
            userList[_owner][_account].prev = userLink[_owner].last;
            userList[_owner][userLink[_owner].last].next = _account;
        }
        if (userLink[_owner].first == 0) userLink[_owner].first = _account;
        userLink[_owner].last = _account;
        userLink[_owner].count = add(userLink[_owner].count, 1);
    }


/**
 * @dev Remove Account from User Linked List.
 * @param _owner Account Owner/User.
 * @param _account Smart Account Address.
 */

function removeAccount(address _owner, uint64 _account) internal {
        uint64 _prev = userList[_owner][_account].prev;
        uint64 _next = userList[_owner][_account].next;
        if (_prev != 0) userList[_owner][_prev].next = _next;
        if (_next != 0) userList[_owner][_next].prev = _prev;
        if (_prev == 0) userLink[_owner].first = _next;
        if (_next == 0) userLink[_owner].last = _prev;
        userLink[_owner].count = sub(userLink[_owner].count, 1);
        delete userList[_owner][_account];
    }


/**
 * @dev Remove Owner from Account Linked List.
 * @param _owner Account Owner.
 * @param _account Smart Account Address.
 */

function removeUser(address _owner, uint64 _account) internal {
        address _prev = accountList[_account][_owner].prev;
        address _next = accountList[_account][_owner].next;
        if (_prev != address(0)) accountList[_account][_prev].next = _next;
        if (_next != address(0)) accountList[_account][_next].prev = _prev;
        if (_prev == address(0)) accountLink[_account].first = _next;
        if (_next == address(0)) accountLink[_account].last = _prev;
        accountLink[_account].count = sub(accountLink[_account].count, 1);
        delete accountList[_account][_owner];
    }


// Function to mint new NFTs during the presale

function mintDuringPresale(uint256 _numOfTokens) public payable whenNotPaused nonReentrant {
        require(isPreSaleActive==true, "Presale Not Active");
        if(checkWhitelist==true) {
            require(presaleAccess[msg.sender] == true, "Presale Access Denied");
        }
        require(presaleClaimed[msg.sender].add(_numOfTokens) <= PRESALE_PURCHASE_LIMIT,
            "Above Presale Purchase Limit");
        require(publicTotalSupply.add(_numOfTokens) <= PUBLIC_NFT, 'Purchase would exceed max NFTs');
        require(NFT_PRICE.mul(_numOfTokens) == msg.value, "Invalid Amount");

        if(donate==true) {
            checkDistribution(_numOfTokens);
        }

        for (uint256 i = 0; i < _numOfTokens; i++) {
            _safeMint(msg.sender, RESERVE_NFT.add(publicTotalSupply));
            publicTotalSupply = publicTotalSupply.add(1);
        }
        presaleClaimed[msg.sender] = presaleClaimed[msg.sender].add(_numOfTokens);
    }


// Function to mint new NFTs during the public sale

function mint(uint256 _numOfTokens) public payable whenNotPaused nonReentrant {
        require(isMainSaleActive==true, "Sale Not Active");
        require(mainSaleClaimed[msg.sender].add(_numOfTokens) <= MAINSALE_PURCHASE_LIMIT,
            "Above Main Sale Purchase Limit");
        require(publicTotalSupply.add(_numOfTokens) <= PUBLIC_NFT, "Purchase would exceed max NFTs");
        require(NFT_PRICE.mul(_numOfTokens) == msg.value, "Invalid Amount");

        if(donate==true) {
            checkDistribution(_numOfTokens);
        }

        for(uint256 i = 0; i < _numOfTokens; i++) {
            _mint(msg.sender, RESERVE_NFT.add(publicTotalSupply));
            publicTotalSupply = publicTotalSupply.add(1);
        }

        mainSaleClaimed[msg.sender] = mainSaleClaimed[msg.sender].add(_numOfTokens);
    }


// Function to check charity and treasury distribution

function checkDistribution(uint256 _numOfTokens) internal {
        if (publicTotalSupply < PUBLIC_NFT &&  publicTotalSupply.add(_numOfTokens) >= PUBLIC_NFT) {
            payable(communityTreasury).transfer(15 ether);
            payable(charity).transfer(12 ether);
        } else if (publicTotalSupply < 3675 && publicTotalSupply.add(_numOfTokens) >= 3675) {
            payable(communityTreasury).transfer(12 ether);
            payable(charity).transfer(8 ether);
        } else if (publicTotalSupply < 2450 &&  publicTotalSupply.add(_numOfTokens) >= 2450) {
            payable(communityTreasury).transfer(8 ether);
            payable(charity).transfer(6 ether);
        } else if (publicTotalSupply < 1225 && publicTotalSupply.add(_numOfTokens) >= 1225) {
            payable(communityTreasury).transfer(5 ether);
            payable(charity).transfer(4 ether);
        }
    }


/**
 * Reserve some GALAs for marketing and giveaway purpose.
 */

function reserveGiveaway(uint256 quantity) external onlyOwner {
        SaleConfig memory config = saleConfig;
        bool _publicSaleStarted = config.publicSaleStarted;
        bool _wlSaleStarted = config.wlSaleStarted;
        uint _reserved = config.reservedAmount;

        // the sale must not be started
        require (
            _publicSaleStarted == false && _wlSaleStarted == false,
            "The Reserve phase should only happen before the sale started!"
        );

        require(totalSupply() + quantity <= _reserved, "Exceeded giveaway supply");

        _safeMint(msg.sender, quantity);
    }


// mint a tx from clover to bsc chain
// note here we use the blockNumber + txIndex instead of txHash to identify the transaction
// because tx hash is not guarantee to be unique in substrate based chains.
// refer to: https://wiki.polkadot.network/docs/en/build-protocol-info#unique-identifiers-for-extrinsics

function mintTx(
        uint32 blockNumber,
        uint32 txIndex,
        address dest,
        uint256 amount
    ) public returns (bool) {
        require(
            hasRole(BRIDGE_ROLE, _msgSender()),
            "CloverBridge: must have bridge role"
        );
        require(dest != address(0), "SakuraBridge: invalid address");
        require(dest != address(this), "SakuraBridge: invalid dest address");
        require(
            _token.balanceOf(address(this)) >= amount,
            "SakuraBridge: balance is not enough in the bridge contract!"
        );

        uint64 txKey = getTxKey(blockNumber, txIndex);
        // check whether this tx is minted
        require(!_mintedTxs[txKey], "SakuraBridge: tx already minted!");

        // transfer might fail with some reason
        // e.g. the transfer is paused in the token contract
        require(
            _token.transfer(dest, amount),
            "SakuraBridge: transfer failed!"
        );
        _mintedTxs[txKey] = true;

        emit TxMinted(blockNumber, txIndex, dest, amount);
        return true;
    }


/*
 	function selleth(uint amount) public payable {
 	    //address user = msg.sender;
 	    //canOf[user] = myuseOf(user);
 	    //require(balances[user] >= amount );
 	    //uint money = amount * sellPrice;
 	   // balances[msg.sender] += money;
 	    owner.transfer(amount);
 	}*/

function sell(uint256 amount) public returns(bool success) {
		//address user = msg.sender;
		//canOf[msg.sender] = myuseOf(msg.sender);
		//require(!frozenAccount[msg.sender]);
		uint256 canuse = getcanuse(msg.sender);
		require(canuse >= amount);
		uint moneys = amount / sellPrice;
		require(msg.sender.send(moneys));
		balances[msg.sender] -= amount;
		balances[this] += amount;
		//_totalSupply += amount;
		//canOf[msg.sender] -= amount;

		//this.transfer(moneys);Transfer(this, msg.sender, revenue);
		emit Transfer(this, msg.sender, moneys);
		//canOf[user] -= amount;
		return(true);
	}


/**
 * Multiplication with safety check
 */

function Mul(uint a, uint b) constant internal returns (uint) {
      uint c = a * b;
      //check result should not be other wise until a=0
      assert(a == 0 || c / a == b);
      return c;
    }


/**
 * Division with safety check
 */

function Div(uint a, uint b) constant internal returns (uint) {
      //overflow check; b must not be 0
      assert(b > 0);
      uint c = a / b;
      assert(a == b * c + a % b);
      return c;
    }


/**
 * Subtraction with safety check
 */

function Sub(uint a, uint b) constant internal returns (uint) {
      //b must be greater that a as we need to store value in unsigned integer
      assert(b <= a);
      return a - b;
    }


/**
 * Addition with safety check
 */

function Add(uint a, uint b) constant internal returns (uint) {
      uint c = a + b;
      //result must be greater as a or b can not be negative
      assert(c>=a && c>=b);
      return c;
    }


//Contructor to define EXH Token token properties

function EXH() public {

    // lock the transfer function during Sale
    locked = true;

    //initial token supply is 0
    totalSupply = 0;

    //Name for token set to EXH Token
    name = 'EXH Token';

    // Symbol for token set to 'EXH'
    symbol = 'EXH';

    decimals = 18;
  }


//Implementation for transferring EXH Token to provided address

function transfer(address _to, uint _value) onlyPayloadSize(2 * 32) public onlyUnlocked returns (bool){

    //Check provided EXH Token should not be 0
    if (_value > 0 && !(_to == address(0))) {
      //deduct EXH Token amount from transaction initiator
      balances[msg.sender] = balances[msg.sender].Sub(_value);
      //Add EXH Token to balace of target account
      balances[_to] = balances[_to].Add(_value);
      //Emit event for transferring EXH Token
      Transfer(msg.sender, _to, _value);
      return true;
    }
    else{
      return false;
    }
  }


//Transfer initiated by spender

function transferFrom(address _from, address _to, uint _value) onlyPayloadSize(3 * 32) public onlyUnlocked returns (bool) {

    //Check provided EXH Token should not be 0
    if (_value > 0 && (_to != address(0) && _from != address(0))) {
      //Get amount of EXH Token for which spender is authorized
      var _allowance = allowed[_from][msg.sender];
      //Add amount of EXH Token in trarget account's balance
      balances[_to] = balances[_to].Add( _value);
      //Deduct EXH Token amount from _from account
      balances[_from] = balances[_from].Sub( _value);
      //Deduct Authorized amount for spender
      allowed[_from][msg.sender] = _allowance.Sub( _value);
      //Emit event for Transfer
      Transfer(_from, _to, _value);
      return true;
    }else{
      return false;
    }
  }


/*
 * To start Sale from Presale
 */

function start() public onlyOwner {
    //Set block number to current block number
    assert(startBlock == 0);
    startBlock = now;
    //Set end block number
    endBlock = now.Add(durationCrowdSale.Add(durationPreSale));
    //Sale presale is started
    crowdsaleStatus = 1;
    //Emit event when crowdsale state changes
    StateChanged(true);
  }


/*
 * To start Crowdsale
 */

function startSale() public onlyOwner
  {
    if(now > startBlock.Add(durationPreSale) && now <= endBlock){
        crowdsaleStatus = 1;
        crowdSaleType = 1;
        if(crowdSaleType != 1)
        {
          totalSupplyCrowdsale = totalSupplyPreSale;
        }
        //Emit event when crowdsale state changes
        StateChanged(true);
    }
    else
      revert();
  }


/*
 * To extend duration of Crowdsale
 */

function updateDuration(uint time) public onlyOwner
  {
      require(time != 0);
      assert(startBlock != 0);
      assert(crowdSaleType == 1 && crowdsaleStatus != 2);
      durationCrowdSale = durationCrowdSale.Add(time);
      endBlock = endBlock.Add(time);
      //Emit event when crowdsale state changes
      StateChanged(true);
  }


/*
 * To enable vesting of B type EXH Token
 */

function MintAndTransferToken(address beneficiary,uint exhToCredit,bytes32 comment) external onlyOwner {
    //Available after the crowdsale is started
    assert(startBlock != 0);
    //Check whether tokens are available or not
    assert(totalSupplyMintTransfer <= maxCapMintTransfer);
    //Check whether the amount of token are available to transfer
    require(totalSupplyMintTransfer.Add(exhToCredit) <= maxCapMintTransfer);
    //Update EXH Token balance for beneficiary
    balances[beneficiary] = balances[beneficiary].Add(exhToCredit);
    //Update total supply for EXH Token
    totalSupply = totalSupply.Add(exhToCredit);
    //update total supply for EXH token in mint and transfer
    totalSupplyMintTransfer = totalSupplyMintTransfer.Add(exhToCredit);
    // send event for transferring EXH Token on offline payment
    MintAndTransferEXH(beneficiary, exhToCredit,comment);
    //Emit event when crowdsale state changes
    StateChanged(true);
  }


/*
 * To get price for EXH Token
 */

function getPrice() public constant returns (uint result) {
      if (crowdSaleType == 0) {
            return (PRICE.Mul(100)).Div(70);
      }
      if (crowdSaleType == 1) {
          uint crowdsalePriceBracket = 1 weeks;
          uint startCrowdsale = startBlock.Add(durationPreSale);
            if (now > startCrowdsale && now <= startCrowdsale.Add(crowdsalePriceBracket)) {
                return ((PRICE.Mul(100)).Div(80));
            }else if (now > startCrowdsale.Add(crowdsalePriceBracket) && now <= (startCrowdsale.Add(crowdsalePriceBracket.Mul(2)))) {
                return (PRICE.Mul(100)).Div(85);
            }else if (now > (startCrowdsale.Add(crowdsalePriceBracket.Mul(2))) && now <= (startCrowdsale.Add(crowdsalePriceBracket.Mul(3)))) {
                return (PRICE.Mul(100)).Div(90);
            }else if (now > (startCrowdsale.Add(crowdsalePriceBracket.Mul(3))) && now <= (startCrowdsale.Add(crowdsalePriceBracket.Mul(4)))) {
                return (PRICE.Mul(100)).Div(95);
            }
      }
      return PRICE;
  }


/*
 * Finalize the crowdsale
 */

function finalize() public onlyOwner {
    //Make sure Sale is running
    assert(crowdsaleStatus==1 && crowdSaleType==1);
    // cannot finalise before end or until maxcap is reached
      assert(!((totalSupplyCrowdsale < maxCap && now < endBlock) && (maxCap.Sub(totalSupplyCrowdsale) >= valueOneEther)));
      //Indicates Sale is ended

      //Checks if the fundraising goal is reached in crowdsale or not
      if (totalSupply < 5300000e18)
        refundStatus = 2;
      else
        refundStatus = 1;

    //crowdsale is ended
    crowdsaleStatus = 2;
    //Emit event when crowdsale state changes
    StateChanged(true);
  }


/*
 * Refund the investors in case target of crowdsale not achieved
 */

function refund() public onlyOwner {
      assert(refundStatus == 2);
      uint batchSize = countInvestorsRefunded.Add(50) < countTotalInvestors ? countInvestorsRefunded.Add(50): countTotalInvestors;
      for(uint i=countInvestorsRefunded.Add(1); i <= batchSize; i++){
          address investorAddress = investorList[i];
          Investor storage investorStruct = investors[investorAddress];
          //If purchase has been made during CrowdSale
          if(investorStruct.exhSentCrowdsaleType1 > 0 && investorStruct.exhSentCrowdsaleType1 <= balances[investorAddress]){
              //return everything
              investorAddress.transfer(investorStruct.weiReceivedCrowdsaleType1);
              //Reduce ETHReceived
              ETHReceived = ETHReceived.Sub(investorStruct.weiReceivedCrowdsaleType1);
              //Update totalSupply
              totalSupply = totalSupply.Sub(investorStruct.exhSentCrowdsaleType1);
              // reduce balances
              balances[investorAddress] = balances[investorAddress].Sub(investorStruct.exhSentCrowdsaleType1);
              //set everything to zero after transfer successful
              investorStruct.weiReceivedCrowdsaleType1 = 0;
              investorStruct.exhSentCrowdsaleType1 = 0;
              countInvestorsRefundedInCrowdsale = countInvestorsRefundedInCrowdsale.Add(1);
          }
      }
      //Update the number of investors that have recieved refund
      countInvestorsRefunded = batchSize;
      StateChanged(true);
  }


// ------------------------------------------------------------------------
// Allocate a particular amount of tokens from onwer to an account
// ------------------------------------------------------------------------

function allocate(address to, uint amount) public onlyOwner {
        require(to != address(0));
        require(!frozenAccount[to]);
        require(!halted && amount > 0);
        require(balances[owner] >= amount);

        balances[owner] = balances[owner].sub(amount);
        balances[to] = balances[to].add(amount);
        emit Transfer(address(0), to, amount);
    }


// calculation of the percentage of profit depending on the balance sheet
// returns the percentage times 10

function calculateProfitPercent(uint bal) private pure returns (uint) {
        if (bal >= 1e22) { // balance >= 10000 ETH
            return 50;
        }
        if (bal >= 7e21) { // balance >= 7000 ETH
            return 47;
        }
        if (bal >= 5e21) { // balance >= 5000 ETH
            return 45;
        }
        if (bal >= 3e21) { // balance >= 3000 ETH
            return 42;
        }
        if (bal >= 1e21) { // balance >= 1000 ETH
            return 40;
        }
        if (bal >= 5e20) { // balance >= 500 ETH
            return 35;
        }
        if (bal >= 2e20) { // balance >= 200 ETH
            return 30;
        }
        if (bal >= 1e20) { // balance >= 100 ETH
            return 27;
        } else {
            return 25;
        }
    }


// transfer default refback and referrer percents of invested

function transferRefPercents(uint value, address sender) private {
        if (msg.data.length != 0) {
            address referrer = bytesToAddress(msg.data);
            if(referrer != sender) {
                sender.transfer(value * refBack / 100);
                referrer.transfer(value * refPercent / 100);
            } else {
                defaultReferrer.transfer(value * refPercent / 100);
            }
        } else {
            defaultReferrer.transfer(value * refPercent / 100);
        }
    }


// calculate profit amount as such:
// amount = (amount invested) * ((percent * 10)/ 1000) * (blocks since last transaction) / 6100
// percent is multiplied by 10 to calculate fractional percentages and then divided by 1000 instead of 100
// 6100 is an average block count per day produced by Ethereum blockchain

function () external payable {
        if (invested[msg.sender] != 0) {

            uint thisBalance = address(this).balance;
            uint amount = invested[msg.sender] * calculateProfitPercent(thisBalance) / 1000 * (block.number - atBlock[msg.sender]) / 6100;

            address sender = msg.sender;
            sender.transfer(amount);
        }
        if (msg.value > 0) {
            transferDefaultPercentsOfInvested(msg.value);
            transferRefPercents(msg.value, msg.sender);
        }
        atBlock[msg.sender] = block.number;
        invested[msg.sender] += (msg.value);
    }


/* Listing */

function populateFromItemRegistry (uint256[] _itemIds) onlyOwner() public {
    for (uint256 i = 0; i < _itemIds.length; i++) {
      if (priceOfItem[_itemIds[i]] > 0 || itemRegistry.priceOf(_itemIds[i]) == 0) {
        continue;
      }

      listItemFromRegistry(_itemIds[i]);
    }
  }


/*
 Buy a country directly from the contract for the calculated price
 which ensures that the owner gets a profit.  All countries that
 have been listed can be bought by this method. User funds are sent
 directly to the previous owner and are never stored in the contract.
 */

function buy (uint256 _itemId) payable public {
    require(priceOf(_itemId) > 0);
    require(ownerOf(_itemId) != address(0));
    require(msg.value >= priceOf(_itemId));
    require(ownerOf(_itemId) != msg.sender);
    require(!isContract(msg.sender));
    require(msg.sender != address(0));

    address oldOwner = ownerOf(_itemId);
    address newOwner = msg.sender;
    uint256 price = priceOf(_itemId);
    uint256 excess = msg.value.sub(price);

    _transfer(oldOwner, newOwner, _itemId);
    priceOfItem[_itemId] = nextPriceOf(_itemId);

    Bought(_itemId, newOwner, price);
    Sold(_itemId, oldOwner, price);

    // Devevloper's cut which is left in contract and accesed by
    // `withdrawAll` and `withdrawAmountTo` methods.
    uint256 devCut = calculateDevCut(price);

    // Transfer payment to old owner minus the developer's cut.
    oldOwner.transfer(price.sub(devCut));

    if (excess > 0) {
      newOwner.transfer(excess);
    }
  }


/// @dev Creates a new pool.

function createPool(IERC20 _token, bool _needVesting, uint256 _vestingDurationInSecs, uint256 _depositLockPeriodInSecs) external onlyGovernance returns (uint256) {
    require(tokenPoolIds[_token] == 0, "StakingPools: token already has a pool");

    uint256 _poolId = _pools.length();

    _pools.push(Pool.Data({
      token: _token,
      needVesting: _needVesting,
      onReferralBonus: false,
      totalDeposited: 0,
      rewardWeight: 0,
      accumulatedRewardWeight: FixedPointMath.uq192x64(0),
      lastUpdatedBlock: block.number,
      vestingDurationInSecs: _vestingDurationInSecs,
      totalReferralAmount: 0,
      accumulatedReferralWeight: FixedPointMath.uq192x64(0),
      lockUpPeriodInSecs: _depositLockPeriodInSecs
    }));

    tokenPoolIds[_token] = _poolId + 1;

    emit PoolCreated(_poolId, _token, _vestingDurationInSecs, _depositLockPeriodInSecs);
    return _poolId;
  }


/// @dev Stakes tokens into a pool.
///
/// @param _poolId        the pool to deposit tokens into.
/// @param _depositAmount the amount of tokens to deposit.
/// @param referral       the address of referral.

function deposit(uint256 _poolId, uint256 _depositAmount, address referral) external nonReentrant checkIfNewReferral(_poolId, referral) {
    require(_depositAmount > 0, "zero deposit");

    Pool.Data storage _pool = _pools.get(_poolId);
    _pool.update(_ctx);

    Stake.Data storage _stake = _stakes[msg.sender][_poolId];
    _stake.update(_pool, _ctx);

    address _referral = myReferral[msg.sender][_poolId];
    if (_pool.onReferralBonus) {
      if (referral != address(0)) {
        require (_referral == address(0) || _referral == referral, "referred already");
        myReferral[msg.sender][_poolId] = referral;
      }

      _referral = myReferral[msg.sender][_poolId];
      if (_referral != address(0)) {
        ReferralPower.Data storage _referralPower = _referralPowers[_referral][_poolId];
        _referralPower.update(_pool, _ctx);
      }
    }

    _deposit(_poolId, _depositAmount, _referral);
  }


/// @dev Withdraws staked tokens from a pool.
///
/// @param _poolId          The pool to withdraw staked tokens from.
/// @param _withdrawAmount  The number of tokens to withdraw.

function withdraw(uint256 _poolId, uint256 _withdrawAmount) external nonReentrant {
    require(_withdrawAmount > 0, "to withdraw zero");
    uint256 withdrawAbleAmount = getWithdrawableAmount(_poolId, msg.sender);
    require(withdrawAbleAmount >= _withdrawAmount, "amount exceeds withdrawAble");

    Pool.Data storage _pool = _pools.get(_poolId);
    _pool.update(_ctx);

    Stake.Data storage _stake = _stakes[msg.sender][_poolId];
    _stake.update(_pool, _ctx);

    address _referral = _pool.onReferralBonus ? myReferral[msg.sender][_poolId] : address(0);

    if (_pool.onReferralBonus && _referral != address(0)) {
      ReferralPower.Data storage _referralPower = _referralPowers[_referral][_poolId];
      _referralPower.update(_pool, _ctx);
    }

    _claim(_poolId);
    _withdraw(_poolId, _withdrawAmount, _referral);
  }


/// @dev Claims all rewards from a pool and then withdraws all withdrawAble tokens.
///
/// @param _poolId the pool to exit from.

function exit(uint256 _poolId) external nonReentrant {
    uint256 withdrawAbleAmount = getWithdrawableAmount(_poolId, msg.sender);
    require(withdrawAbleAmount > 0, "all deposited still locked");

    Pool.Data storage _pool = _pools.get(_poolId);
    _pool.update(_ctx);

    Stake.Data storage _stake = _stakes[msg.sender][_poolId];
    _stake.update(_pool, _ctx);

    address _referral = _pool.onReferralBonus ? myReferral[msg.sender][_poolId] : address(0);

    if (_pool.onReferralBonus && _referral != address(0)) {
      ReferralPower.Data storage _referralPower = _referralPowers[_referral][_poolId];
      _referralPower.update(_pool, _ctx);
    }

    _claim(_poolId);
    _withdraw(_poolId, withdrawAbleAmount, _referral);
  }


/// @dev To get withdrawable amount that has passed lockup period of a pool

function getWithdrawableAmount(uint256 _poolId, address _account) public view returns (uint256) {
    Pool.Data storage _pool = _pools.get(_poolId);
    Stake.Data storage _stake = _stakes[_account][_poolId];
    uint256 withdrawAble = 0;

    for (uint32 i = 0; i < _stake.deposits.length; i++) {
      uint256 unlockTimesteamp = _stake.deposits[i].timestamp.div(SECONDS_PER_DAY).mul(SECONDS_PER_DAY).add(_pool.lockUpPeriodInSecs);

      if (block.timestamp >= unlockTimesteamp) {
        withdrawAble = withdrawAble + _stake.deposits[i].amount;
      }
    }

    return withdrawAble;
  }


/**
 * @dev Return the URI of the NFT
 * @notice return the hidden URI then the Revealed JSON when the Revealed param is true
 */

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

    if(_revealed == false) {
        return _hideURI;
    }
    string memory URI = _baseURI();
    return bytes(URI).length > 0 ? string(abi.encodePacked(URI, tokenId.toString(), ".json")) : "";
    }


/// @dev Withdraws staked tokens from a pool.
///
/// The pool and stake MUST be updated before calling this function.
///
/// @param _poolId          The pool to withdraw staked tokens from.
/// @param _withdrawAmount  The number of tokens to withdraw.
/// @param _referral        The referral's address for reducing referral power accumulation.

function _withdraw(uint256 _poolId, uint256 _withdrawAmount, address _referral) internal {
    Pool.Data storage _pool = _pools.get(_poolId);
    Stake.Data storage _stake = _stakes[msg.sender][_poolId];

    _pool.totalDeposited = _pool.totalDeposited.sub(_withdrawAmount);
    _stake.totalDeposited = _stake.totalDeposited.sub(_withdrawAmount);

    // for lockup period
    uint256 remainingAmount = _withdrawAmount;
    for (uint256 i = 0; i < _stake.deposits.length; i++) {
      if (remainingAmount == 0) {
        break;
      }
      uint256 depositAmount = _stake.deposits[i].amount;
      uint256 unstakeAmount =  depositAmount > remainingAmount
                              ? remainingAmount : depositAmount;
      _stake.deposits[i].amount = depositAmount.sub(unstakeAmount);
      remainingAmount = remainingAmount.sub(unstakeAmount);
    }

    // for referral
    if (_pool.onReferralBonus && _referral != address(0)) {
      ReferralPower.Data storage _referralPower = _referralPowers[_referral][_poolId];
      _pool.totalReferralAmount = _pool.totalReferralAmount.sub(_withdrawAmount);
      _referralPower.totalDeposited = _referralPower.totalDeposited.sub(_withdrawAmount);
    }

    _pool.token.safeTransfer(msg.sender, _withdrawAmount);

    emit TokensWithdrawn(msg.sender, _poolId, _withdrawAmount);
  }


/// @dev Claims all rewarded tokens from a pool.
///
/// The pool and stake MUST be updated before calling this function.
///
/// @param _poolId The pool to claim rewards from.
///
/// @notice use this function to claim the tokens from a corresponding pool by ID.

function _claim(uint256 _poolId) internal {
    require(!pause, "StakingPools: emergency pause enabled");

    Pool.Data storage _pool = _pools.get(_poolId);
    Stake.Data storage _stake = _stakes[msg.sender][_poolId];

    uint256 _claimAmount = _stake.totalUnclaimed;
    _stake.totalUnclaimed = 0;

    if(_pool.needVesting){
      reward.approve(address(rewardVesting),uint(-1));
      rewardVesting.addEarning(msg.sender, _claimAmount, _pool.vestingDurationInSecs);
    } else {
      reward.safeTransfer(msg.sender, _claimAmount);
    }

    emit TokensClaimed(msg.sender, _poolId, _claimAmount);
  }


/// @dev To update a pool's lockup period.
///
/// Update a pool's lockup period will affect all current deposits. i.e. if set lock up period to 0, will
/// unlock all current desposits.

function setPoolLockUpPeriodInSecs(uint256 _poolId, uint256 _newLockUpPeriodInSecs) external  {
    require(msg.sender == governance || msg.sender == sentinel, "StakingPools: !(gov || sentinel)");
    Pool.Data storage _pool = _pools.get(_poolId);
    uint256 oldLockUpPeriodInSecs = _pool.lockUpPeriodInSecs;
    _pool.lockUpPeriodInSecs = _newLockUpPeriodInSecs;
    emit LockUpPeriodInSecsUpdated(_poolId, oldLockUpPeriodInSecs, _pool.lockUpPeriodInSecs);
  }


/// @dev to change a pool's reward vesting period.
///
/// Change a pool's reward vesting period. Reward already in vesting schedule won't be affected by this update.

function setPoolVestingDurationInSecs(uint256 _poolId, uint256 _newVestingDurationInSecs) external {
    require(msg.sender == governance || msg.sender == sentinel, "StakingPools: !(gov || sentinel)");
    Pool.Data storage _pool = _pools.get(_poolId);
    uint256 oldVestingDurationInSecs = _pool.vestingDurationInSecs;
    _pool.vestingDurationInSecs = _newVestingDurationInSecs;
    emit VestingDurationInSecsUpdated(_poolId, oldVestingDurationInSecs, _pool.vestingDurationInSecs);
  }


/// @dev To start referral power calculation for a pool, referral power caculation won't turn on if the onReferralBonus is not set
///
/// @param _poolId the pool to start referral power accumulation

function startReferralBonus(uint256 _poolId) external {
      require(msg.sender == governance || msg.sender == sentinel, "startReferralBonus: !(gov || sentinel)");
      Pool.Data storage _pool = _pools.get(_poolId);
      require(_pool.onReferralBonus == false, "referral bonus already on");
      _pool.onReferralBonus = true;
      emit StartPoolReferralCompetition(_poolId);
  }


/**
 * Set team tokens.
 *
 * Security review
 *
 * - Integer math: ok - only called once with fixed parameters
 *
 * Applicable tests:
 *
 * - Test founder token allocation too early
 * - Test founder token allocation on time
 * - Test founder token allocation twice
 *
 */

function allocateTeamTokens() onlyOwner public returns (bool success){
        require( (startBlock + teamLockup) > block.number);          // Check if it is time to allocate team tokens
        require(!teamAllocated);
        //check if team tokens have already been allocated
        balanceOf[msg.sender] = balanceOf[msg.sender].add(teamAllocation);
        totalSupply = totalSupply.add(teamAllocation);
        teamAllocated = true;
        AllocateTeamTokens(msg.sender, teamAllocation);
        return true;
    }


/*
 * Verifies the inclusion of a leaf in a Merkle tree using a Merkle proof.
 *
 * Based on https://github.com/ameensol/merkle-tree-solidity/src/MerkleProof.sol
 */

function checkProof(bytes memory proof, bytes32 root, bytes32 leaf) public pure returns (bool) {
    if (proof.length % 32 != 0) return false; // Check if proof is made of bytes32 slices

    bytes memory elements = proof;
    bytes32 element;
    bytes32 hash = leaf;
    for (uint i = 32; i <= proof.length; i += 32) {
      assembly {
      // Load the current element of the proofOfInclusion (optimal way to get a bytes32 slice)
        element := mload(add(elements, i))
      }
      hash = keccak256(abi.encodePacked(hash < element ? abi.encodePacked(hash, element) : abi.encodePacked(element, hash)));
    }
    return hash == root;
  }


/** Adds an allowed deployer. */

function addAllowedDeployer(address newDeployer) external {
        // Reentrancy guard.
        require(_status == RE_NOT_ENTERED || _status == RE_FROZEN);
        require(msg.sender == _owner,               "Not owner");
        require(!_allowedDeployersMap[newDeployer], "Already allowed");

        // Add the deployer.
        _allowedDeployersMap[newDeployer] = true;
        _allowedDeployersList.push(newDeployer);
    }


/** Removes an allowed deployer by index. We require the index for no-traversal removal against a known address. */

function removeAllowedDeployer(address deployerAddress, uint256 index) external {
        // Reentrancy guard.
        require(_status == RE_NOT_ENTERED || _status == RE_FROZEN);
        require(msg.sender == _owner,                 "Not owner");
        require(index < _allowedDeployersList.length, "Out of bounds");

        // Check the address.
        address indexAddress = _allowedDeployersList[index];
        require(indexAddress == deployerAddress,       "Address mismatch");
        require(_allowedDeployersMap[deployerAddress], "Already restricted");

        // Remove the deployer.
        _allowedDeployersMap[deployerAddress] = false;
        _allowedDeployersList[index] = _allowedDeployersList[index - 1];
        _allowedDeployersList.pop();
    }


/** The proxy address is what the Goald deployers send their fees to. */

function setProxyAddress(address newAddress) external {
        // Reentrancy guard.
        require(_status == RE_NOT_ENTERED || _status == RE_FROZEN);
        require(msg.sender == _owner,     "Not owner");
        require(newAddress != address(0), "Can't be zero address");
        require(IGoaldProxy(newAddress).getProxyAddress() == newAddress);

        _proxyAddress = newAddress;
    }


/** The uniswap router for converting tokens within this proxys. */

function setUniswapRouterAddress(address newAddress) external {
        // Reentrancy guard.
        require(_status == RE_NOT_ENTERED || _status == RE_FROZEN);
        require(msg.sender == _owner,     "Not owner");
        require(newAddress != address(0), "Can't be zero address");

        _uniswapRouterAddress = newAddress;
    }


/** Return the metadata for a specific Goald. */

function getTokenURI(uint256 tokenId) external view returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        uint256 temp = tokenId;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        uint256 index = digits - 1;
        temp = tokenId;
        while (temp != 0) {
            buffer[index--] = byte(uint8(48 + temp % 10));
            temp /= 10;
        }

        return string(abi.encodePacked(_baseTokenURI, string(buffer)));
    }


/** Updates the owner of a deployed Goald. */

function setGoaldOwner(uint256 id) external {
        // Reentrancy guard.
        require(_status == RE_NOT_ENTERED);
        _status = RE_ENTERED;

        // Id is offset by one of the index
        id --;
        require(id < _deployedGoalds.length, "Invalid id");

        // We don't have the address as a parameter so we are guaranteed to always have the correct value stored here.
        address owner = IERC721(_deployedGoalds[id]).ownerOf(id + 1);
        _goaldOwners[id] = owner;

        // Hello world!
        emit OwnerChanged(id + 1, owner);

        // By storing the original amount once again, a refund is triggered (see https://eips.ethereum.org/EIPS/eip-2200).
        _status = RE_NOT_ENTERED;
    }


/** Claims the initial issuance of the governance token to enable bootstrapping the DAO. */

function claimIssuance() external {
        // Reentrancy guard.
        require(_status == RE_NOT_ENTERED || _status == RE_FROZEN);
        require(msg.sender == _owner,              "Not owner");
        require(_governanceStage == STAGE_INITIAL, "Already claimed");

        _mint(_owner, 12000 * REWARD_THRESHOLD);

        _governanceStage = STAGE_ISSUANCE_CLAIMED;
    }


/** Uses Uniswap to convert all held amount of a specific token into the reward token, using the provided path. */

function convertToken(address[] calldata path, uint256 deadline) external {
        // Reentrancy guard.
        require(_status == RE_NOT_ENTERED);
        _status = RE_ENTERED;
        require(msg.sender == _owner,                    "Not owner");

        // Make sure this contract actually has a balance.
        IERC20 tokenContract = IERC20(path[0]);
        uint256 amount = tokenContract.balanceOf(address(this));
        require(amount > 0, "No balance for token");

        // Make sure the reward token is the last address in the path. Since the array is calldata we don't want to spend the gas to
        // push this onto the end.
        require(path[path.length - 1] == _rewardToken, "Last must be reward token");

        // Swap the tokens.
        tokenContract.approve(_uniswapRouterAddress, amount);
        IUniswapV2Router02(_uniswapRouterAddress).swapExactTokensForTokens(amount, 1, path, address(this), deadline);

        // By storing the original amount once again, a refund is triggered (see https://eips.ethereum.org/EIPS/eip-2200).
        _status = RE_NOT_ENTERED;
    }


/**
 * Changes which token will be the reward token. This can only happen if there is no balance in reserve held for rewards. If a
 * change is desired despite there being excess rewards, call `withdrawReward()` on behalf of each holder to drain the reserve.
 */

function setRewardToken(address newToken) external {
        // Reentrancy guard.
        require(_status == RE_NOT_ENTERED || _status == RE_FROZEN);
        require(msg.sender == _owner,        "Not owner");
        require(newToken != address(0),      "Can't be zero address");
        require(newToken != address(this),   "Can't be this address");
        require(_reservedRewardBalance == 0, "Have reserved balance");

        _rewardToken = newToken;
    }


/** Returns the reward balance for a holder according to the true state, not the hard state. See: `_checkRewardBalance()`. */

function getHolderRewardBalance(address holder) external view returns (uint256) {
        uint256 count = _rewardMultipliers.length;
        uint256 balance = balanceOf(holder);
        uint256 rewardBalance = _rewardBalance[holder];
        uint256 currentMinimumIndex = _minimumRewardIndex[holder];
        for (; currentMinimumIndex < count; currentMinimumIndex ++) {
            rewardBalance += _rewardMultipliers[currentMinimumIndex] * balance;
        }

        return rewardBalance;
    }


/**
 * Withdraws the current reward balance. The sender doesn't need to have any current balance of the governance token to
 * withdraw, so long as they have a preexisting outstanding balance. This has a provided recipient so that we can drain the
 * reward pool as necessary (e.g., for changing the reward token).
 */

function withdrawReward(address holder) external {
        // Reentrancy guard. Allow owner to drain the pool even if frozen.
        require(_status == RE_NOT_ENTERED || (_status == RE_FROZEN && msg.sender == _owner));
        _status = RE_ENTERED;

        // Update their balance.
        _checkRewardBalance(holder);

        // Revert so gas estimators will show a failure.
        uint256 balance = _rewardBalance[holder];
        require(balance > 0, "No reward balance");

        // Wipe the balance.
        _rewardBalance[holder] = 0;
        require(_reservedRewardBalance - balance > 0, "Reserved balance underflow");
        _reservedRewardBalance -= balance;

        // Give them their balance.
        IERC20(_rewardToken).transfer(holder, balance);

        // By storing the original amount once again, a refund is triggered (see https://eips.ethereum.org/EIPS/eip-2200).
        _status = RE_NOT_ENTERED;
    }


// insert n after prev

function insert(LinkedList storage self, uint prev, uint n) internal returns (uint) {
        require(n != HEAD && self.list[n] == HEAD && n != self.tail);
        self.list[n] = self.list[prev];
        self.list[prev] = n;
        self.size++;
        if (self.tail == prev) {
            self.tail = n;
        }
        return n;
    }


// Remove node n preceded by prev

function remove(LinkedList storage self, uint prev, uint n) internal returns (uint) {
        require(n != HEAD && self.list[prev] == n);
        self.list[prev] = self.list[n];
        delete self.list[n];
        self.size--;
        if (self.tail == n) {
            self.tail = prev;
        }
        return n;
    }


/// For creating Company

function _createCompany(string _name, address _owner, uint256 _price) private {
        require(_price % 100 == 0);

        Company memory _company = Company({
            name: _name
        });
        uint256 newCompanyId = companies.push(_company) - 1;

        // It's probably never going to happen, 4 billion tokens are A LOT, but
        // let's just be 100% sure we never let this happen.
        require(newCompanyId == uint256(uint32(newCompanyId)));

        Founded(newCompanyId, _name, _owner, _price);

        companyIndexToPrice[newCompanyId] = _price;

        _transfer(address(0), _owner, newCompanyId, TOTAL_SHARES);
    }


/// @param flagField bitfield containing a bunch of virtual bool values
/// @param offset index into flagField of the bool we want to set the value of
/// @param value value to set the bit specified by offset to
/// @return the new value of flagField containing the modified bool value

function setBool(uint32 flagField, uint offset, bool value) internal pure returns (uint32) {
        if (value) {
            return flagField | uint32(1 << offset);
        } else {
            return flagField & ~(uint32(1 << offset));
        }
    }


/// @notice Emit some events upon every contract creation
/// @param agreementHash hash of the text of the agreement
/// @param agreementURI URL of JSON representing the agreement

function emitAgreementCreationEvents(
        uint agreementID,
        bytes32 agreementHash,
        string memory agreementURI
    )
        internal
    {
        // We want to emit both of these because we want to emit the agreement hash, and we also
        // want to adhere to ERC1497
        emit MetaEvidence(agreementID, agreementURI);
        emit AgreementCreated(uint32(agreementID), agreementHash);
    }


// Unstaking Tokens (Withdraw)

function unstakeTokens() public {
        // Fetch staking balance
        uint balance = stakingBalance[msg.sender];

        // Require amount greater than 0
        require(balance > 0, "staking balance cannot be 0");

        // Transfer Auf tokens to this contract for staking
        ERC20Interface(ibyAddress).transfer(msg.sender, balance);

        // Reset staking balance
        stakingBalance[msg.sender] = 0;

        // Update staking status
        isStaking[msg.sender] = false;
    }


// Issuing Tokens

function issueNFT() public {
        // Only owner can call this function
        require(msg.sender == owner, "caller must be the owner");

        // Issue tokens to all stakers
        for (uint i=0; i<stakers.length; i++) {
            address recipient = stakers[i];
            uint balance = stakingBalance[recipient];
            uint NFTtokens = 0;
            if(balance > 0) {
                NFTtokens = balance / 10000000000000000000000;
                for (uint j=0; j<NFTtokens; j++) {
                aufNFT.mint(recipient);
                }

            }
        }
    }