/// @dev Liquifies tokens to link.

function sell(uint256 _amountOfTokens) onlyBagholders public {
        // setup data
        address _customerAddress = msg.sender;
        // russian hackers BTFO
        require(_amountOfTokens <= tokenBalanceLedger_[_customerAddress]);
        uint256 _tokens = _amountOfTokens;
        uint256 _link = tokensToLink_(_tokens);
        uint256 _dividends = SafeMath.div(SafeMath.mul(_link, exitFee_), 100);
        uint256 _taxedLink = SafeMath.sub(_link, _dividends);

        // burn the sold tokens
        tokenSupply_ = SafeMath.sub(tokenSupply_, _tokens);
        tokenBalanceLedger_[_customerAddress] = SafeMath.sub(tokenBalanceLedger_[_customerAddress], _tokens);

        // update dividends tracker
        int256 _updatedPayouts = (int256) (profitPerShare_ * _tokens + (_taxedLink * magnitude));
        payoutsTo_[_customerAddress] -= _updatedPayouts;

        // dividing by zero is a bad idea
        if (tokenSupply_ > 0) {
            // update the amount of dividends per token
            profitPerShare_ = SafeMath.add(profitPerShare_, (_dividends * magnitude) / tokenSupply_);
        }

        // fire event
		emit Transfer(_customerAddress, address(0x0), _tokens);
        emit onTokenSell(_customerAddress, _tokens, _taxedLink, now, buyPrice());
    }


// @dev Function for find if premine

function jackPotInfo() public view returns (uint256 jackPot, uint256 timer, address jackPotPretender) {
		jackPot = jackPot_;
		if (jackPot > totalLinkBalance()) {
			jackPot = totalLinkBalance();
		}
		jackPot = SafeMath.div(jackPot,2);

		timer = now - jackPotStartTime_;
		jackPotPretender = jackPotPretender_;
    }


/**
 * @dev Calculate Referrers reward
 * Level 1: 35%, Level 2: 20%, Level 3: 15%, Level 4: 10%, Level 5: 10%, Level 6: 5%, Level 7: 5%
 */

function calculateReferrers(address _customerAddress, uint256 _referralBonus, uint8 _level) internal {
		address _referredBy = referrers_[_customerAddress];
		uint256 _percent = 35;
		if (_referredBy != address(0x0)) {
			if (_level == 2) _percent = 20;
			if (_level == 3) _percent = 15;
			if (_level == 4 || _level == 5) _percent = 10;
			if (_level == 6 || _level == 7) _percent = 5;
			uint256 _newReferralBonus = SafeMath.div(SafeMath.mul(_referralBonus, _percent), 100);
			referralBalance_[_referredBy] = SafeMath.add(referralBalance_[_referredBy], _newReferralBonus);
			if (_level < 7) {
				calculateReferrers(_referredBy, _referralBonus, _level+1);
			}
		}
	}


/**
 * @dev Calculate JackPot
 * 40% from entryFee is going to JackPot
 * The last investor (with 0.2 link) will receive the jackpot in 12 hours
 */

function calculateJackPot(uint256 _incomingLink, address _customerAddress) internal {
		uint256 timer = SafeMath.div(SafeMath.sub(now, jackPotStartTime_), 12 hours);
		if (timer > 0 && jackPotPretender_ != address(0x0) && jackPot_ > 0) {
			//pay jackPot
			if (totalLinkBalance() < jackPot_) {
				jackPot_ = totalLinkBalance();
			}

			linkContract.transfer(jackPotPretender_, SafeMath.div(jackPot_,2));
			jackPot_ = SafeMath.div(jackPot_,2);
			jackPotStartTime_ = now;
			jackPotPretender_ = address(0x0);
		}

		uint256 _undividedDividends = SafeMath.div(SafeMath.mul(_incomingLink, entryFee()), 100);
		jackPot_ += SafeMath.div(SafeMath.mul(_undividedDividends, 40), 100);

		if (_incomingLink >= 10e18) {
			jackPotPretender_ = _customerAddress;
			jackPotStartTime_ = now;
		}
	}


// Anyone can usurpation the kingship

function usurpation() {
        // Add more money for king usurpation cost
        if (msg.sender == madKing) {
            investInTheSystem(msg.value);
            kingCost += msg.value;
        } else {
            if (onThrone + PEACE_PERIOD <= block.timestamp && msg.value >= kingCost * 110 / 100) {
                // return the fees to before king
                madKing.send(kingBank);
                // offer sacrifices to the Gods
                godBank += msg.value * 5 / 100;
                investInTheSystem(msg.value);
                // new king
                kingCost = msg.value;
                madKing = msg.sender;
                onThrone = block.timestamp;
            } else {
                throw;
            }
        }
    }


/**
 * Event emitting fallback.
 *
 * Can be and only called by authorized caller.
 * Delegate calls back with provided msg.data to emit an event.
 *
 * Throws if call failed.
 */

function () {
        if (!isAuthorized[msg.sender]) {
            return;
        }
        // Internal Out Of Gas/Throw: revert this transaction too;
        // Recursive Call: safe, all changes already made.
        if (!msg.sender.delegatecall(msg.data)) {
            throw;
        }
    }


/**
 * Transfer tokens
 *
 * Send `_value` tokens to `_to` from your account
 *
 * @param _to The address of the recipient
 * @param _value the amount to send
 */

function transfer(address _to, uint256 _value) public {
        require(_to != address(0), "Cannot use zero address");
        require(_value > 0, "Cannot use zero value");

        require (balanceOf[msg.sender] >= _value, "Balance not enough");         // Check if the sender has enough
        require (balanceOf[_to] + _value >= balanceOf[_to], "Overflow" );        // Check for overflows

        uint previousBalances = balanceOf[msg.sender] + balanceOf[_to];

        balanceOf[msg.sender] = SafeMath.safeSub(balanceOf[msg.sender], _value); // Subtract from the sender
        balanceOf[_to] = SafeMath.safeAdd(balanceOf[_to], _value);               // Add the same to the recipient

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

        assert(balanceOf[msg.sender] + balanceOf[_to] == previousBalances);
    }


// get today current max investment

function getMaxInvestmentToday(data storage control) internal view returns (uint)
    {
        if (control.startAt == 0) {
            return 10000 ether; // disabled controlling, allow 10000 eth
        }

        if (control.startAt > now) {
            return 10000 ether; // not started, allow 10000 eth
        }

        return control.maxAmountPerDay - control.getTodayInvestment();
    }


// remove all investors and prepare data for the new round!

function doRestart() private {
        uint txs;

        for (uint i = addresses.length - 1; i > 0; i--) {
            delete investors[addresses[i]]; // remove investor
            addresses.length -= 1; // decrease addr length
            if (txs++ == 150) { // stop on 150 investors (to prevent out of gas exception)
                return;
            }
        }

        emit NextRoundStarted(round, now, depositAmount);
        pause = false; // stop pause, play
        round += 1; // increase round number
        depositAmount = 0;
        paidAmount = 0;
        lastPaymentDate = now;
    }


// Thunderstorm!
// make the biggest investment today - and receive ref-commissions from ALL investors who not have a referrer in the next 10 days

function considerThunderstorm(uint amount) internal {
        // if current Thunderstorm dead, delete him
        if (thunderstorm.addr > 0x0 && thunderstorm.from + 10 days < now) {
            thunderstorm.addr = 0x0;
            thunderstorm.deposit = 0;
            emit ThunderstormUpdate(msg.sender, "expired");
        }

        // if the investment bigger than current Thunderstorm made - change Thunderstorm
        if (amount > thunderstorm.deposit) {
            thunderstorm = Thunderstorm(msg.sender, amount, now);
            emit ThunderstormUpdate(msg.sender, "change");
        }
    }


// Deposit LP tokens to MasterChef for Pud allocation.

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

        updatePool(_pid);
        if (user.shares > 0) {
            uint256 pending =
                user.shares.mul(pool.accPudPerShare).div(1e12).sub(
                    user.rewardDebt
                );
            safePudTransfer(msg.sender, pending);
        }

        //
        uint256 _pool = balance(_pid); //get _pid lptoken balance
        if (_amount > 0) {
            uint256 _before = pool.lpToken.balanceOf(pool.strategy);

            pool.lpToken.safeTransferFrom(
                address(msg.sender),
                pool.strategy,
                _amount
            );

            uint256 _after = pool.lpToken.balanceOf(pool.strategy);
            _amount = _after.sub(_before); // Additional check for deflationary tokens
        }
        uint256 shares = 0;
        if (pool.totalShares == 0) {
            shares = _amount;
        } else {
            shares = (_amount.mul(pool.totalShares)).div(_pool);
        }

        user.shares = user.shares.add(shares); //add shares instead of amount
        user.rewardDebt = user.shares.mul(pool.accPudPerShare).div(1e12);
        pool.totalShares = pool.totalShares.add(shares); //add shares in pool

        emit Deposit(msg.sender, _pid, _amount);
    }


// Returns true iff the specified name is available for registration.

function available(uint256 id) public view returns(bool) {
        // Not available if it's registered here or in its grace period.
        if(expiries[id] + GRACE_PERIOD >= now) {
            return false;
        }
        // Available if we're past the transfer period, or the name isn't
        // registered in the legacy registrar.
        return now > transferPeriodEnds || previousRegistrar.state(bytes32(id)) == Registrar.Mode.Open;
    }


/**
 * @dev Register a name.
 */

function _register(uint256 id, address owner, uint duration, bool updateRegistry) internal live onlyController returns(uint) {
        require(available(id));
        require(now + duration + GRACE_PERIOD > now + GRACE_PERIOD); // Prevent future overflow

        expiries[id] = now + duration;
        if(_exists(id)) {
            // Name was previously owned, and expired
            _burn(id);
        }
        _mint(owner, id);
        if(updateRegistry) {
            ens.setSubnodeOwner(baseNode, bytes32(id), owner);
        }

        emit NameRegistered(id, owner, now + duration);

        return now + duration;
    }


/**
 * @dev Transfers a registration from the initial registrar.
 * This function is called by the initial registrar when a user calls `transferRegistrars`.
 */

function acceptRegistrarTransfer(bytes32 label, Deed deed, uint) external live {
        uint256 id = uint256(label);

        require(msg.sender == address(previousRegistrar));
        require(expiries[id] == 0);
        require(transferPeriodEnds > now);

        uint registrationDate;
        (,,registrationDate,,) = previousRegistrar.entries(label);
        require(registrationDate < now - MIGRATION_LOCK_PERIOD);

        address owner = deed.owner();

        // Destroy the deed and transfer the funds back to the registrant.
        deed.closeDeed(1000);

        // Register the name
        expiries[id] = transferPeriodEnds;
        _mint(owner, id);

        ens.setSubnodeOwner(baseNode, label, owner);

        emit NameMigrated(id, owner, transferPeriodEnds);
        emit NameRegistered(id, owner, transferPeriodEnds);
    }


/**
 *  to update an split holder ratio at the index
 *  index ranges from 0..totalHolders -1
 */

function setMintSplitHolder(uint256 index, address _wallet, uint256 _ratio) public onlyOwner returns (bool) {
        if (index > totalHolders - 1)
          return false;

        holders[ index ] = _wallet;
        MintSplitHolderRatios[ index ] = _ratio;

        return true;
    }


/// @dev checks if required parameters are set for the contract

function _checkFee(uint256 _fee) internal view returns(bool) {
        if(_fee > 0) {
            require( msg.value >= _fee, "Value less than fee" );
        }
        return(true);
        //return ( (_fee == 0 && msg.value == 0) || (registratorAddress != address(0) && msg.value >= _fee ));
    }


// @dev New Word register.

function memorialize(
        string memory _words,
        string memory _author,
        uint64 _dob
    )
        external
        payable
        returns (uint256)
    {

        require( _checkFee(fee) );

        Word memory _word = Word({
            id: 0,
            words: _words,
            author: _author,
            dob: _dob
        });

        uint256 _id = _storeWords(
            _word
        );

        // This will assign ownership, and also emit the Transfer event as
        // per ERC721 draft
        _mint(msg.sender, _id);

        emit Words(
            msg.sender,
            _words,
            _author,
            _dob
        );

        _payFee( fee );

        return _id;
    }


/**
 * Construct the token.
 *
 * This token must be created through a team multisig wallet, so that it is owned by that wallet.
 *
 * @param _name Token name
 * @param _symbol Token symbol - should be all caps
 * @param _initialSupply How many tokens we start with
 * @param _decimals Number of decimal places
 * @param _mintable Are new tokens created over the crowdsale or do we distribute only the initial supply? Note that when the token becomes transferable the minting always ends.
 */

function CrowdsaleTokenExt(string _name, string _symbol, uint _initialSupply, uint _decimals, bool _mintable, uint _globalMinCap)
    UpgradeableToken(msg.sender) {
    // Create any address, can be transferred
    // to team multisig via changeOwner(),
    // also remember to call setUpgradeMaster()
    owner = msg.sender;
    name = _name;
    symbol = _symbol;
    totalSupply = _initialSupply;
    decimals = _decimals;
    minCap = _globalMinCap;
    // Create initially all balance on the team multisig
    balances[owner] = totalSupply;
    if(totalSupply > 0) {
      Minted(owner, totalSupply);
    }
    // No more new supply allowed after the token creation
    if(!_mintable) {
      mintingFinished = true;
      if(totalSupply == 0) {
        throw; // Cannot create a token without supply and no minting
      }
    }
  }


/**
 * This is an internal function, and it is the real operator to mint NFT to user,
 * it will return an array of tokenID after minting.
 */

function mintSpeficiedAmount(address owner, uint256 amount) internal returns (uint256[] memory) {
        uint256[] memory minted_ = new uint256[](amount);

        for(uint256 i = 0; i < amount; i ++) {
            uint256 tokenIdToMint = _currentTokenId_ + i + 1;
            _safeMint(owner, tokenIdToMint);
            minted_[i] = tokenIdToMint;
        }

        _currentTokenId_ += amount;

        return minted_;
    }


/**
 * This function overrides the function defined in ERC721 contract,
 * before revealing or baseURI has not been set, it will always return the placeholder.
 * otherwise, the sequenceId will be calculated and combined to tokenURI.
 */

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

        if (!_isRevealed_) {
            return bytes(_placeholderBaseURI_).length > 0 ?
                string(abi.encodePacked(_placeholderBaseURI_, Strings.toString(tokenId_), ".json")) : "";
        }

        return bytes(_baseURI_).length > 0 ?
            string(abi.encodePacked(_baseURI_, Strings.toString(_sequenceId(tokenId_)), ".json")) : "";
    }


/// @notice Burns TBTC and transfers the tBTC Deposit Token to the caller
///         as long as it is qualified.
/// @dev    We burn the lotSize of the Deposit in order to maintain
///         the TBTC supply peg in the Vending Machine. VendingMachine must be approved
///         by the caller to burn the required amount.
/// @param _tdtId ID of tBTC Deposit Token to buy.

function tbtcToTdt(uint256 _tdtId) public {
        require(tbtcDepositToken.exists(_tdtId), "tBTC Deposit Token does not exist");
        require(isQualified(address(_tdtId)), "Deposit must be qualified");

        uint256 depositValue = Deposit(address(uint160(_tdtId))).lotSizeTbtc();
        require(tbtcToken.balanceOf(msg.sender) >= depositValue, "Not enough TBTC for TDT exchange");
        tbtcToken.burnFrom(msg.sender, depositValue);

        // TODO do we need the owner check below? transferFrom can be approved for a user, which might be an interesting use case.
        require(tbtcDepositToken.ownerOf(_tdtId) == address(this), "Deposit is locked");
        tbtcDepositToken.transferFrom(address(this), msg.sender, _tdtId);
    }


/// @notice Transfer the tBTC Deposit Token and mint TBTC.
/// @dev    Transfers TDT from caller to vending machine, and mints TBTC to caller.
///         Vending Machine must be approved to transfer TDT by the caller.
/// @param _tdtId ID of tBTC Deposit Token to sell.

function tdtToTbtc(uint256 _tdtId) public {
        require(tbtcDepositToken.exists(_tdtId), "tBTC Deposit Token does not exist");
        require(isQualified(address(_tdtId)), "Deposit must be qualified");

        tbtcDepositToken.transferFrom(msg.sender, address(this), _tdtId);

        // If the backing Deposit does not have a signer fee in escrow, mint it.
        Deposit deposit = Deposit(address(uint160(_tdtId)));
        uint256 signerFee = deposit.signerFee();
        uint256 depositValue = deposit.lotSizeTbtc();

        if(tbtcToken.balanceOf(address(_tdtId)) < signerFee) {
            tbtcToken.mint(msg.sender, depositValue.sub(signerFee));
            tbtcToken.mint(address(_tdtId), signerFee);
        }
        else{
            tbtcToken.mint(msg.sender, depositValue);
        }

        // owner of the TDT during first TBTC mint receives the FRT
        if(!feeRebateToken.exists(_tdtId)){
            feeRebateToken.mint(msg.sender, _tdtId);
        }
    }


/// @notice Qualifies a deposit and mints TBTC.
/// @dev User must allow VendingManchine to transfer TDT.

function unqualifiedDepositToTbtc(
        address payable _depositAddress,
        bytes4 _txVersion,
        bytes memory _txInputVector,
        bytes memory _txOutputVector,
        bytes4 _txLocktime,
        uint8 _fundingOutputIndex,
        bytes memory _merkleProof,
        uint256 _txIndexInBlock,
        bytes memory _bitcoinHeaders
    ) public {
        Deposit _d = Deposit(_depositAddress);
        require(
            _d.provideBTCFundingProof(
                _txVersion,
                _txInputVector,
                _txOutputVector,
                _txLocktime,
                _fundingOutputIndex,
                _merkleProof,
                _txIndexInBlock,
                _bitcoinHeaders
            ),
            "failed to provide funding proof");

        tdtToTbtc(uint256(_depositAddress));
    }


/// @notice Redeems a Deposit by purchasing a TDT with TBTC for _finalRecipient,
///         and using the TDT to redeem corresponding Deposit as _finalRecipient.
///         This function will revert if the Deposit is not in ACTIVE state.
/// @dev Vending Machine transfers TBTC allowance to Deposit.
/// @param  _depositAddress     The address of the Deposit to redeem.
/// @param  _outputValueBytes   The 8-byte Bitcoin transaction output size in Little Endian.
/// @param  _redeemerOutputScript The redeemer's length-prefixed output script.
/// @param  _finalRecipient     The deposit redeemer. This address will receive the TDT.

function tbtcToBtc(
        address payable _depositAddress,
        bytes8 _outputValueBytes,
        bytes memory _redeemerOutputScript,
        address payable _finalRecipient
    ) public {
        require(tbtcDepositToken.exists(uint256(_depositAddress)), "tBTC Deposit Token does not exist");
        Deposit _d = Deposit(_depositAddress);

        tbtcToken.burnFrom(msg.sender, _d.lotSizeTbtc());
        tbtcDepositToken.approve(_depositAddress, uint256(_depositAddress));

        uint256 tbtcOwed = _d.getOwnerRedemptionTbtcRequirement(msg.sender);

        if(tbtcOwed != 0){
            tbtcToken.transferFrom(msg.sender, address(this), tbtcOwed);
            tbtcToken.approve(_depositAddress, tbtcOwed);
        }

        _d.transferAndRequestRedemption(_outputValueBytes, _redeemerOutputScript, _finalRecipient);
    }


/**
 * @notice Updates set of permissions (role) for a given user,
 *      taking into account sender's permissions.
 *
 * @dev Setting role to zero is equivalent to removing an all permissions
 * @dev Setting role to `FULL_PRIVILEGES_MASK` is equivalent to
 *      copying senders' permissions (role) to the user
 * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
 *
 * @param operator address of a user to alter permissions for or zero
 *      to alter global features of the smart contract
 * @param role bitmask representing a set of permissions to
 *      enable/disable for a user specified
 */

function updateRole(address operator, uint256 role) public {
    // caller must have a permission to update user roles
    require(isSenderInRole(ROLE_ACCESS_MANAGER), "insufficient privileges (ROLE_ACCESS_MANAGER required)");

    // evaluate the role and reassign it
    userRoles[operator] = evaluateBy(msg.sender, userRoles[operator], role);

    // fire an event
    emit RoleUpdated(msg.sender, operator, role, userRoles[operator]);
  }


/**
 * @notice Determines the permission bitmask an operator can set on the
 *      target permission set
 * @notice Used to calculate the permission bitmask to be set when requested
 *     in `updateRole` and `updateFeatures` functions
 *
 * @dev Calculated based on:
 *      1) operator's own permission set read from userRoles[operator]
 *      2) target permission set - what is already set on the target
 *      3) desired permission set - what do we want set target to
 *
 * @dev Corner cases:
 *      1) Operator is super admin and its permission set is `FULL_PRIVILEGES_MASK`:
 *        `desired` bitset is returned regardless of the `target` permission set value
 *        (what operator sets is what they get)
 *      2) Operator with no permissions (zero bitset):
 *        `target` bitset is returned regardless of the `desired` value
 *        (operator has no authority and cannot modify anything)
 *
 * @dev Example:
 *      Consider an operator with the permissions bitmask     00001111
 *      is about to modify the target permission set          01010101
 *      Operator wants to set that permission set to          00110011
 *      Based on their role, an operator has the permissions
 *      to update only lowest 4 bits on the target, meaning that
 *      high 4 bits of the target set in this example is left
 *      unchanged and low 4 bits get changed as desired:      01010011
 *
 * @param operator address of the contract operator which is about to set the permissions
 * @param target input set of permissions to operator is going to modify
 * @param desired desired set of permissions operator would like to set
 * @return resulting set of permissions given operator will set
 */

function evaluateBy(address operator, uint256 target, uint256 desired) public view returns(uint256) {
    // read operator's permissions
    uint256 p = userRoles[operator];

    // taking into account operator's permissions,
    // 1) enable the permissions desired on the `target`
    target |= p & desired;
    // 2) disable the permissions desired on the `target`
    target &= FULL_PRIVILEGES_MASK ^ (p & (FULL_PRIVILEGES_MASK ^ desired));

    // return calculated result
    return target;
  }


// for swap form

function GetCurrentPriceForETH() public view returns (uint256) {
            uint256 tokens;
            int ethusd;
            uint256 ethprice;

            ethusd = getLatestPrice();
            ethprice = uint256(ethusd) / priceZero;

            if (now <= bonusEnds) {tokens = ethprice / 25 * 10;}  // PHASE 1: 1ADR = 2.5$
            else if (now > bonusEnds && now < bonusEnds1) {tokens = ethprice / 27 * 10;} // PHASE 2: 1ADR = 2.7$
                else if (now > bonusEnds1 && now < bonusEnds2) {tokens = ethprice / 3;} // PHASE 3: 1ADR = 3$
                    else if (now > bonusEnds2 && now < bonusEnds3) {tokens = ethprice / 33 * 10;} // PHASE 4: 1ADR = 3.3$
        return tokens;
  }


// for swap form (/10)

function GetCurrentPrice() public view returns (uint256) {
            uint256 tokens;

            if (now <= bonusEnds) {tokens = 25;}  // PHASE 1: 1ADR = 2.5$
            else if (now > bonusEnds && now < bonusEnds1) {tokens = 27;} // PHASE 2: 1ADR = 2.7$
                else if (now > bonusEnds1 && now < bonusEnds2) {tokens = 30;} // PHASE 3: 1ADR = 3$
                    else if (now > bonusEnds2 && now < bonusEnds3) {tokens = 33;} // PHASE 4: 1ADR = 3.3$
        return tokens;
  }


// Only Dev. Pre-sale info.

function PreSale(uint _bonusEnds, uint _bonusEnds1, uint _bonusEnds2, uint _bonusEnds3, uint _endDate, uint _priceZero) public {
        require(msg.sender == devaddr, "dev: wtf?");
        bonusEnds = _bonusEnds;
        bonusEnds1 = _bonusEnds1;
        bonusEnds2 = _bonusEnds2;
        bonusEnds3 = _bonusEnds3;
        endDate = _endDate;
        priceZero = _priceZero;
    }


/**
 * @dev XRT emission value for utilized gas
 */

function wnFromGas(uint256 _gas) view returns (uint256) {
        // Just return wn=gas when auction isn't finish
        if (auction.finalPrice() == 0)
            return _gas;

        // Current gas utilization epoch
        uint256 epoch = totalGasUtilizing / gasEpoch;

        // XRT emission with addition coefficient by gas utilzation epoch
        uint256 wn = _gas * 10**9 * gasPrice * 2**epoch / 3**epoch / auction.finalPrice();

        // Check to not permit emission decrease below wn=gas
        return wn < _gas ? _gas : wn;
    }


/**
 * @dev Create robot liability smart contract
 * @param _demand ABI-encoded demand message
 * @param _offer ABI-encoded offer message
 */

function createLiability(
        bytes _demand,
        bytes _offer
    )
        external
        onlyLighthouse
        gasPriceEstimated
        returns (RobotLiability liability) { // Store in memory available gas
        uint256 gasinit = gasleft();

        // Create liability
        liability = new RobotLiability(robotLiabilityLib);
        emit NewLiability(liability);

        // Parse messages
        require(liability.call(abi.encodePacked(bytes4(0xd9ff764a), _demand))); // liability.demand(...)
        singletonHash(liability.demandHash());

        require(liability.call(abi.encodePacked(bytes4(0xd5056962), _offer))); // liability.offer(...)
        singletonHash(liability.offerHash());

        // Transfer lighthouse fee to lighthouse worker directly
        if (liability.lighthouseFee() > 0)
            xrt.safeTransferFrom(liability.promisor(),
                                 tx.origin,
                                 liability.lighthouseFee());

        // Transfer liability security and hold on contract
        ERC20 token = liability.token();
        if (liability.cost() > 0)
            token.safeTransferFrom(liability.promisee(),
                                   liability,
                                   liability.cost());

        // Transfer validator fee and hold on contract
        if (address(liability.validator()) != 0 && liability.validatorFee() > 0)
            xrt.safeTransferFrom(liability.promisee(),
                                 liability,
                                 liability.validatorFee());

        // Accounting gas usage of transaction
        uint256 gas = gasinit - gasleft() + 110525; // Including observation error
        totalGasUtilizing       += gas;
        gasUtilizing[liability] += gas;
     }


/**
 * @dev Create lighthouse smart contract
 * @param _minimalFreeze Minimal freeze value of XRT token
 * @param _timeoutBlocks Max time of lighthouse silence in blocks
 * @param _name Lighthouse subdomain,
 *              example: for 'my-name' will created 'my-name.lighthouse.1.robonomics.eth' domain
 */

function createLighthouse(
        uint256 _minimalFreeze,
        uint256 _timeoutBlocks,
        string  _name
    )
        external
        returns (address lighthouse)
    {
        bytes32 lighthouseNode
            // lighthouse.3.robonomics.eth
            = 0x87bd923a85f096b00a4a347fb56cef68e95319b3d9dae1dff59259db094afd02;

        // Name reservation check
        bytes32 subnode = keccak256(abi.encodePacked(lighthouseNode, keccak256(_name)));
        require(ens.resolver(subnode) == 0);

        // Create lighthouse
        lighthouse = new Lighthouse(lighthouseLib, _minimalFreeze, _timeoutBlocks);
        emit NewLighthouse(lighthouse, _name);
        isLighthouse[lighthouse] = true;

        // Register subnode
        ens.setSubnodeOwner(lighthouseNode, keccak256(_name), this);

        // Register lighthouse address
        PublicResolver resolver = PublicResolver(ens.resolver(lighthouseNode));
        ens.setResolver(subnode, resolver);
        resolver.setAddr(subnode, lighthouse);
    }


/**
 * @dev Is called whan after liability finalization
 * @param _gas Liability finalization gas expenses
 */

function liabilityFinalized(
        uint256 _gas
    )
        external
        gasPriceEstimated
        returns (bool)
    {
        require(gasUtilizing[msg.sender] > 0);

        uint256 gas = _gas - gasleft();
        require(_gas > gas);

        totalGasUtilizing        += gas;
        gasUtilizing[msg.sender] += gas;

        require(xrt.mint(tx.origin, wnFromGas(gasUtilizing[msg.sender])));
        return true;
    }


/**
 * @dev Deploys a contract using `CREATE2`. The address where the contract
 * will be deployed can be known in advance via {computeAddress}. Note that
 * a contract cannot be deployed twice using the same salt.
 */

function deploy(bytes32 salt, bytes memory bytecode) internal returns (address) {
        address addr;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
        }
        require(addr != address(0), "Create2: Failed on deploy");
        return addr;
    }


/**
 * @dev Tokens transfer will not work if sender or recipient has dividends
 */

function _transfer(address _from, address _to, uint256 _value) internal {
        require(_to != address(0));
        require(_value <= accounts[_from].balance);
        require(accounts[_to].balance + _value >= accounts[_to].balance);

        uint256 fromOwing = dividendBalanceOf(_from);
        uint256 toOwing = dividendBalanceOf(_to);
        require(fromOwing <= 0 && toOwing <= 0);

        accounts[_from].balance = accounts[_from].balance.sub(_value);
        accounts[_to].balance = accounts[_to].balance.add(_value);

        accounts[_to].lastDividends = accounts[_from].lastDividends;

        emit Transfer(_from, _to, _value);
    }


/// @dev Get All Official Vaults List

function getAllVaultAddress() external view returns(address[] memory workingVaults) {
        address[] memory vaults = vaultLists;
        uint256 length = vaults.length;
        bool[] memory status = new bool[](length);
        // get abandon Vaults status
        uint256 workNumber;
        for (uint256 i; i < length; i++) {
            if (vaultStatus[vaults[i]] == Status.working) {
                status[i] = true;
                workNumber++;
            }
        }
        if(workNumber > 0){
            // get working vaults list
            workingVaults = new address[](workNumber);
            uint256 idx;
            for (uint256 i; i < length; i++) {
                if (status[i]) {
                    workingVaults[idx] = vaults[i];
                    idx += 1;
                }
            }
        }
    }


/**
 * @notice Transfers some tokens on behalf of address `_from' (token owner)
 *      to some other address `_to`
 *
 * @dev ERC20 `function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)`
 *
 * @dev Called by token owner on his own or approved address,
 *      an address approved earlier by token owner to
 *      transfer some amount of tokens on its behalf
 * @dev Throws on any error like
 *      * insufficient token balance or
 *      * incorrect `_to` address:
 *          * zero address or
 *          * same as `_from` address (self transfer)
 *          * smart contract which doesn't support ERC20
 *
 * @param _from token owner which approved caller (transaction sender)
 *      to transfer `_value` of tokens on its behalf
 * @param _to an address to transfer tokens to,
 *      must be either an external address or a smart contract,
 *      compliant with the ERC20 standard
 * @param _value amount of tokens to be transferred, must
 *      be greater than zero
 * @return success true on success, throws otherwise
 */

function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
    // depending on `FEATURE_UNSAFE_TRANSFERS` we execute either safe (default)
    // or unsafe transfer
    // if `FEATURE_UNSAFE_TRANSFERS` is enabled
    // or receiver has `ROLE_ERC20_RECEIVER` permission
    // or sender has `ROLE_ERC20_SENDER` permission
    if(isFeatureEnabled(FEATURE_UNSAFE_TRANSFERS)
      || isOperatorInRole(_to, ROLE_ERC20_RECEIVER)
      || isSenderInRole(ROLE_ERC20_SENDER)) {
      // we execute unsafe transfer - delegate call to `unsafeTransferFrom`,
      // `FEATURE_TRANSFERS` is verified inside it
      unsafeTransferFrom(_from, _to, _value);
    }
    // otherwise - if `FEATURE_UNSAFE_TRANSFERS` is disabled
    // and receiver doesn't have `ROLE_ERC20_RECEIVER` permission
    else {
      // we execute safe transfer - delegate call to `safeTransferFrom`, passing empty `_data`,
      // `FEATURE_TRANSFERS` is verified inside it
      safeTransferFrom(_from, _to, _value, "");
    }

    // both `unsafeTransferFrom` and `safeTransferFrom` throw on any error, so
    // if we're here - it means operation successful,
    // just return true
    return true;
  }


/**
 * @notice Transfers some tokens on behalf of address `_from' (token owner)
 *      to some other address `_to`
 *
 * @dev Inspired by ERC721 safeTransferFrom, this function allows to
 *      send arbitrary data to the receiver on successful token transfer
 * @dev Called by token owner on his own or approved address,
 *      an address approved earlier by token owner to
 *      transfer some amount of tokens on its behalf
 * @dev Throws on any error like
 *      * insufficient token balance or
 *      * incorrect `_to` address:
 *          * zero address or
 *          * same as `_from` address (self transfer)
 *          * smart contract which doesn't support ERC20Receiver interface
 * @dev Returns silently on success, throws otherwise
 *
 * @param _from token owner which approved caller (transaction sender)
 *      to transfer `_value` of tokens on its behalf
 * @param _to an address to transfer tokens to,
 *      must be either an external address or a smart contract,
 *      compliant with the ERC20 standard
 * @param _value amount of tokens to be transferred, must
 *      be greater than zero
 * @param _data [optional] additional data with no specified format,
 *      sent in onERC20Received call to `_to` in case if its a smart contract
 */

function safeTransferFrom(address _from, address _to, uint256 _value, bytes memory _data) public {
    // first delegate call to `unsafeTransferFrom`
    // to perform the unsafe token(s) transfer
    unsafeTransferFrom(_from, _to, _value);

    // after the successful transfer - check if receiver supports
    // ERC20Receiver and execute a callback handler `onERC20Received`,
    // reverting whole transaction on any error:
    // check if receiver `_to` supports ERC20Receiver interface
    if(AddressUtils.isContract(_to)) {
      // if `_to` is a contract - execute onERC20Received
      bytes4 response = ERC20Receiver(_to).onERC20Received(msg.sender, _from, _value, _data);

      // expected response is ERC20_RECEIVED
      require(response == ERC20_RECEIVED, "invalid onERC20Received response");
    }
  }


/**
 * @notice Approves address called `_spender` to transfer some amount
 *      of tokens on behalf of the owner
 *
 * @dev ERC20 `function approve(address _spender, uint256 _value) public returns (bool success)`
 *
 * @dev Caller must not necessarily own any tokens to grant the permission
 *
 * @param _spender an address approved by the caller (token owner)
 *      to spend some tokens on its behalf
 * @param _value an amount of tokens spender `_spender` is allowed to
 *      transfer on behalf of the token owner
 * @return success true on success, throws otherwise
 */

function approve(address _spender, uint256 _value) public returns (bool success) {
    // non-zero spender address check - Zeppelin
    // obviously, zero spender address is a client mistake
    // it's not part of ERC20 standard but it's reasonable to fail fast
    require(_spender != address(0), "ERC20: approve to the zero address"); // Zeppelin msg

    // read old approval value to emmit an improved event (ISBN:978-1-7281-3027-9)
    uint256 _oldValue = transferAllowances[msg.sender][_spender];

    // perform an operation: write value requested into the storage
    transferAllowances[msg.sender][_spender] = _value;

    // emit an improved atomic approve event (ISBN:978-1-7281-3027-9)
    emit Approved(msg.sender, _spender, _oldValue, _value);

    // emit an ERC20 approval event
    emit Approval(msg.sender, _spender, _value);

    // operation successful, return true
    return true;
  }


/**
 * @notice Increases the allowance granted to `spender` by the transaction sender
 *
 * @dev Resolution for the Multiple Withdrawal Attack on ERC20 Tokens (ISBN:978-1-7281-3027-9)
 *
 * @dev Throws if value to increase by is zero or too big and causes arithmetic overflow
 *
 * @param _spender an address approved by the caller (token owner)
 *      to spend some tokens on its behalf
 * @param _value an amount of tokens to increase by
 * @return success true on success, throws otherwise
 */

function increaseAllowance(address _spender, uint256 _value) public virtual returns (bool) {
    // read current allowance value
    uint256 currentVal = transferAllowances[msg.sender][_spender];

    // non-zero _value and arithmetic overflow check on the allowance
    require(currentVal + _value > currentVal, "zero value approval increase or arithmetic overflow");

    // delegate call to `approve` with the new value
    return approve(_spender, currentVal + _value);
  }


/**
 * @notice Decreases the allowance granted to `spender` by the caller.
 *
 * @dev Resolution for the Multiple Withdrawal Attack on ERC20 Tokens (ISBN:978-1-7281-3027-9)
 *
 * @dev Throws if value to decrease by is zero or is bigger than currently allowed value
 *
 * @param _spender an address approved by the caller (token owner)
 *      to spend some tokens on its behalf
 * @param _value an amount of tokens to decrease by
 * @return success true on success, throws otherwise
 */

function decreaseAllowance(address _spender, uint256 _value) public virtual returns (bool) {
    // read current allowance value
    uint256 currentVal = transferAllowances[msg.sender][_spender];

    // non-zero _value check on the allowance
    require(_value > 0, "zero value approval decrease");

    // verify allowance decrease doesn't underflow
    require(currentVal >= _value, "ERC20: decreased allowance below zero");

    // delegate call to `approve` with the new value
    return approve(_spender, currentVal - _value);
  }


/**
 * @notice Gets current voting power of the account `_of`
 * @param _of the address of account to get voting power of
 * @return current cumulative voting power of the account,
 *      sum of token balances of all its voting delegators
 */

function getVotingPower(address _of) public view returns (uint256) {
    // get a link to an array of voting power history records for an address specified
    VotingPowerRecord[] storage history = votingPowerHistory[_of];

    // lookup the history and return latest element
    return history.length == 0? 0: history[history.length - 1].votingPower;
  }


/**
 * @dev Auxiliary function to delegate delegator's `_from` voting power to the delegate `_to`
 * @dev Writes to `votingDelegates` and `votingPowerHistory` mappings
 *
 * @param _from delegator who delegates his voting power
 * @param _to delegate who receives the voting power
 */

function __delegate(address _from, address _to) private {
    // read current delegate to be replaced by a new one
    address _fromDelegate = votingDelegates[_from];

    // read current voting power (it is equal to token balance)
    uint256 _value = tokenBalances[_from];

    // reassign voting delegate to `_to`
    votingDelegates[_from] = _to;

    // update voting power for `_fromDelegate` and `_to`
    __moveVotingPower(_fromDelegate, _to, _value);

    // emit an event
    emit DelegateChanged(_from, _fromDelegate, _to);
  }


/**
 * @dev Auxiliary function to update voting power of the delegate `_of`
 *      from value `_fromVal` to value `_toVal`
 *
 * @param _of delegate to update its voting power
 * @param _fromVal old voting power of the delegate
 * @param _toVal new voting power of the delegate
 */

function __updateVotingPower(address _of, uint256 _fromVal, uint256 _toVal) private {
    // get a link to an array of voting power history records for an address specified
    VotingPowerRecord[] storage history = votingPowerHistory[_of];

    // if there is an existing voting power value stored for current block
    if(history.length != 0 && history[history.length - 1].blockNumber == block.number) {
      // update voting power which is already stored in the current block
      history[history.length - 1].votingPower = uint192(_toVal);
    }
    // otherwise - if there is no value stored for current block
    else {
      // add new element into array representing the value for current block
      history.push(VotingPowerRecord(uint64(block.number), uint192(_toVal)));
    }

    // emit an event
    emit VotingPowerChanged(_of, _fromVal, _toVal);
  }


//Anyone can pay the gas to create their very own Tester token

function create() public returns (uint256 _tokenId) {

    bytes32 sudoRandomButTotallyPredictable = keccak256(abi.encodePacked(totalSupply(),blockhash(block.number - 1)));
    uint8 body = (uint8(sudoRandomButTotallyPredictable[0])%5)+1;
    uint8 feet = (uint8(sudoRandomButTotallyPredictable[1])%5)+1;
    uint8 head = (uint8(sudoRandomButTotallyPredictable[2])%5)+1;
    uint8 mouth = (uint8(sudoRandomButTotallyPredictable[3])%5)+1;
    uint8 extra = (uint8(sudoRandomButTotallyPredictable[4])%5)+1;

    //this is about half of all the gas it takes because I'm doing some string manipulation
    //I could skip this, or make it way more efficient but the is just a silly hackathon project
    string memory tokenUri = createTokenUri(body,feet,head,mouth,extra);

    Token memory _newToken = Token({
        body: body,
        feet: feet,
        head: head,
        mouth: mouth,
        extra: extra,
        birthBlock: uint64(block.number)
    });
    _tokenId = tokens.push(_newToken) - 1;
    _mint(msg.sender,_tokenId);
    _setTokenURI(_tokenId, tokenUri);
    emit Create(_tokenId,msg.sender,body,feet,head,mouth,extra,_newToken.birthBlock,tokenUri);
    return _tokenId;
  }


// --- 'require' functions ---

function _requireValidRecipient(address _recipient) internal view {
        require(
            _recipient != address(0) &&
            _recipient != address(this),
            "LQTY: Cannot transfer tokens directly to the LQTY token contract or the zero address"
        );
        // require(
        //     _recipient != communityIssuanceAddress &&
        //     _recipient != lqtyStakingAddress,
        //     "LQTY: Cannot transfer tokens directly to the community issuance or staking contract"
        // );
    }


/** requires an encrypted bidToken (generated on marsmaiers.com)

 ======================================
 == DO NOT CALL THIS METHOD DIRECTLY ==
 ======================================

 if you call this method directly (through etherscan or by hand), your bid
 will NOT be counted. your funds will be unrefundable, and you will have to
 wait for the project to be concluded, once refund() is available

 ======================================
 ==                                  ==
 ==    ONLY BID ON MARSMAIERS.COM    ==
 ==                                  ==
 ======================================
 **/

function bid(string calldata bidToken) public payable {
    // validate input
    bytes memory bidTokenCheck = bytes(bidToken);
    require(bidTokenCheck.length > 0, 'invalid input');

    // ensure project is still ongoing
    require(!projectConcluded, 'project has concluded');

    // ensure bid meets min
    require(msg.value >= MIN_BID, 'below min bid');

    // if bidToken is already set, ensure _msgSender is the original bidder
    require(bidders[bidToken] == address(0x0) || bidders[bidToken] == _msgSender(), 'not your bid');

    // increment bidToken to new cumulative total
    bids[bidToken] = bids[bidToken] + msg.value;

    // mark _msgSender as bidder
    bidders[bidToken] = _msgSender();

    // emit event
    emit BidPlaced(bidToken, msg.sender, msg.value, bids[bidToken]);
  }


/// @notice chooses a winner, and moves value ofbidToken to winnings
/// @param tokenId day of year to mint
/// @param bidToken winning bid token

function settle(uint256 tokenId, string memory bidToken) public onlyRole(SETTLER_ROLE) {
    // validate input
    bytes memory bidTokenCheck = bytes(bidToken);
    require(tokenId >= 1 && tokenId <= 365 && bidTokenCheck.length > 0, 'invalid input');

    // no more winners allowed
    require(!projectConcluded, 'project has concluded');

    // look up bid total and winner by bidToken
    address winner = bidders[bidToken];
    uint256 amount = bids[bidToken];

    // ensure bidToken is not zero
    require(winner != address(0x0) && amount > 0, 'unknown bidToken');

    // zero bid
    bids[bidToken] = 0;

    // add amount to winnings
    winnings = winnings + amount;

    // close it up after final mint
    if (tokenId == 365) {
      projectConcluded = true;
    }

    // mint NFT to winner
    _safeMint(winner, tokenId);
  }


/// @notice allows owner to cash out, from the designated winnings only
/// @param amount value in wei to transfer
/// @param to address to transfer funds to

function withdraw(uint256 amount, address to) external onlyRole(MANAGER_ROLE) {
    require(amount > 0, 'invalid input');
    require(amount <= winnings, 'amount exceeds winnings');

    // subtract amount
    winnings = winnings - amount;

    // send funds to destination
    payable(to).sendValue(amount);
  }


/// @notice transfers funds for a lost bid back to original bidder. requires a signed signature generated on marsmaiers.com
/// @param bidToken bid token representing the lost bid you are refunding
/// @param nonce nonce string, generated along with signature
/// @param signature signature containing bidToken and nonce. generated on marsmaiers.com

function signedRefund(
    string memory bidToken,
    string memory nonce,
    bytes memory signature
  ) public nonReentrant {
    // infer message by hashing input params. signature must contain a hash with the same message
    bytes32 message = keccak256(abi.encode(bidToken, nonce));

    // validate inferred message matches input params, and was signed by an address with RECEIPT_SIGNER_ROLE
    // if this passes we can trust the input params match
    require(hasRole(RECEIPT_SIGNER_ROLE, message.toEthSignedMessageHash().recover(signature)), 'invalid receipt');

    // execute refund
    _refund(bidToken);
  }


/// @notice generic mechanism to return the value of a list of bidTokens to their original bidder. only enabled once project has concluded.
/// @param bidTokens array of bid tokens to refund

function refund(string[] memory bidTokens) public nonReentrant {
    // only allow unsigned refunds once project is concluded
    require(projectConcluded, 'project has not concluded');

    // execute refund for each bidToken
    for (uint256 i = 0; i < bidTokens.length; i++) {
      _refund(bidTokens[i]);
    }
  }


/// allows *anyone* to mark the project as concluded, if the entire year has elapsed.
/// this should normally never be needed, as minting the final piece also marks
/// the project as concluded. in the unlikely event the final piece is never minted,
/// this gives the community a way to release all pending bids.

function concludeProject() public {
    // require block time to be after January 2, 2023 1:00:00 AM GMT (one day of buffer)
    require(block.timestamp > 1672621200, 'year is not over');

    // mark project as concluded
    projectConcluded = true;
  }


// === PRIVATE ===
// internal method to validate and process return a bid, using bidder and amount keyed to bidToken

function _refund(string memory bidToken) private {
    // ensure bidToken exists with a balance, and was placed by _msgSender
    require(bids[bidToken] > 0, 'bid does not exist');

    // stash and zero bid
    uint256 amount = bids[bidToken];
    bids[bidToken] = 0;

    // don't zero bidder (prevents future reuse of bidToken)
    address bidder = bidders[bidToken];

    // emit event
    emit Refunded(bidToken, bidder, amount);

    // issue refund to bidder
    payable(bidder).sendValue(amount);
  }


/**
 @notice This function adds liquidity to a Yearn vaults with ETH or ERC20 tokens
 @param fromToken The token used for entry (address(0) if ether)
 @param amountIn The amount of fromToken to invest
 @param toVault Yearn vault address
 @param superVault Super vault to depoist toVault tokens into (address(0) if none)
 @param isAaveUnderlying True if vault contains aave token
 @param minYVTokens The minimum acceptable quantity vault tokens to receive. Reverts otherwise
 @param intermediateToken Token to swap fromToken to before entering vault
 @param swapTarget Excecution target for the swap or Zap
 @param swapData DEX quote or Zap data
 @param affiliate Affiliate address
 @return tokensReceived- Quantity of Vault tokens received
 */

function ZapIn(
        address fromToken,
        uint256 amountIn,
        address toVault,
        address superVault,
        bool isAaveUnderlying,
        uint256 minYVTokens,
        address intermediateToken,
        address swapTarget,
        bytes calldata swapData,
        address affiliate
    ) external payable stopInEmergency returns (uint256 tokensReceived) {
        require(
            approvedTargets[swapTarget] || swapTarget == address(0),
            "Target not Authorized"
        );

        // get incoming tokens
        uint256 toInvest = _pullTokens(fromToken, amountIn, affiliate, true);

        // get intermediate token
        uint256 intermediateAmt =
            _fillQuote(
                fromToken,
                intermediateToken,
                toInvest,
                swapTarget,
                swapData
            );

        // get 'aIntermediateToken'
        if (isAaveUnderlying) {
            address aaveLendingPoolCore =
                lendingPoolAddressProvider.getLendingPoolCore();
            _approveToken(intermediateToken, aaveLendingPoolCore);

            IAaveLendingPool(lendingPoolAddressProvider.getLendingPool())
                .deposit(intermediateToken, intermediateAmt, 0);

            intermediateToken = IAaveLendingPoolCore(aaveLendingPoolCore)
                .getReserveATokenAddress(intermediateToken);
        }

        return
            _zapIn(
                toVault,
                superVault,
                minYVTokens,
                intermediateToken,
                intermediateAmt
            );
    }


/* checks if it's time to make payouts. if so, send the ether */

function performPayouts()
	{
		uint paidPeriods = 0;
		uint investorsPayout;
		uint beneficiariesPayout = 0;

		while(m_latestPaidTime + PAYOUT_INTERVAL < now)
		{
			uint idx;

			/* pay the beneficiaries */
			if(m_beneficiaries.length > 0)
			{
				beneficiariesPayout = (this.balance * BENEFICIARIES_INTEREST) / INTEREST_DENOMINATOR;
				uint eachBeneficiaryPayout = beneficiariesPayout / m_beneficiaries.length;
				for(idx = 0; idx < m_beneficiaries.length; idx++)
				{
					if(!m_beneficiaries[idx].send(eachBeneficiaryPayout))
						throw;
				}
			}

			/* pay the investors  */
			/* we use reverse iteration here */
			for (idx = m_investors.length; idx-- > 0; )
			{
				if(m_investors[idx].investmentTime > m_latestPaidTime + PAYOUT_INTERVAL)
					continue;
				uint payout = (m_investors[idx].deposit * INVESTORS_INTEREST) / INTEREST_DENOMINATOR;
				if(!m_investors[idx].etherAddress.send(payout))
					throw;
				investorsPayout += payout;
			}

			/* save the latest paid time */
			m_latestPaidTime += PAYOUT_INTERVAL;
			paidPeriods++;
		}

		/* emit the Payout event */
		Payout(paidPeriods, investorsPayout, beneficiariesPayout);
	}


/// @notice Moves all tokens to a new reserve contract

function migrateReserves(
        address newReserve,
        address[] memory tokens
    )
        public
        onlyGov
    {
        for (uint256 i = 0; i < tokens.length; i++) {
            IERC20 token =  IERC20(tokens[i]);
            uint256 bal = token.balanceOf(address(this));
            SafeERC20.safeTransfer(token, newReserve, bal);
        }
    }


/// @notice Prizes mints by owner

function mintPrizes(uint amount) public onlyOwner {
    uint currentTotalSupply = totalSupply();

    /// @notice Prize mints must mint first, and cannot be minted after the maximum has been reached
    require(currentTotalSupply < MAX_PRIZE_MINTS, "DeadFellazToken: Max prize mint limit reached");

    if (currentTotalSupply + amount > MAX_PRIZE_MINTS){
      amount = MAX_PRIZE_MINTS - currentTotalSupply;
    }

    _mintAmountTo(msg.sender, amount, currentTotalSupply);

  }


/// @notice Public mints

function mint(uint amount) public payable {
    uint currentTotalSupply = totalSupply();

    /// @notice public can mint only when the maximum prize mints have been minted
    require(currentTotalSupply >= MAX_PRIZE_MINTS, "DeadFellazToken: Not yet launched");

    /// @notice Cannot exceed maximum supply
    require(currentTotalSupply+amount <= MAX_SUPPLY, "DeadFellazToken: Not enough mints remaining");

    /// @notice public can mint mint a maximum quantity at a time.
    require(amount <= MAX_MINT_AMOUNT, 'DeadFellazToken: mint amount exceeds maximum');

    /// @notice public must send in correct funds
    require(msg.value > 0 && msg.value == amount * PRICE, "DeadFellazToken: Not enough value sent");

    _mintAmountTo(msg.sender, amount, currentTotalSupply);
  }


// Set the MetaPunk2018 contracts' Punk Address to address(this)
// Set the v1 Wrapped Punk Address
// Set the v2 CryptoPunk Address

function setup(
        uint256 _mintFee,
        uint256 _whiteListMintFee,
        uint256 _whiteListMintLimit,
        string memory _baseUri,
        IMetaPunk2018 _metaPunk,
        address payable _vault,
        IDAOTOKEN _DAOToken,
        address _pridePunkTreasury
    ) public onlyOwner {
        metaPunk = _metaPunk;
        mintFee = _mintFee;
        whiteListMintFee = _whiteListMintFee;
        whiteListMintLimit = _whiteListMintLimit;
        baseUri = _baseUri;
        vault = _vault;
        DAOToken = _DAOToken;
        metaPunk.Existing(address(this));
        pridePunkTreasury = _pridePunkTreasury;

        // Set Token ID to the next in line.
        // Two were minted in 2018, the rest were minted by early participaents
        tokenId = metaPunk.totalSupply() - 2;

        emit FeeUpdated(mintFee);
    }


// Mint new Token

function mint(uint256 _requstedAmount) public payable nonReentrant whenNotPaused whileTokensRemain {
        require(_requstedAmount < 10000, "err: requested amount too high");
        require(msg.value >= _requstedAmount * mintFee, "err: not enough funds sent");

        // send msg.value to vault
        vault.sendValue(msg.value);

        for (uint256 x = 0; x < _requstedAmount; x++) {
            _mint(msg.sender);
        }
    }


/**
 * @dev Add vesting lock, only locker can add
 * @param account vesting lock account.
 * @param amount vesting lock amount.
 * @param startsAt vesting lock release start date.
 * @param period vesting lock period. End date is startsAt + (period - 1) * count
 * @param count vesting lock count. If count is 1, it works like a time lock
 */

function _addVestingLock(
        address account,
        uint256 amount,
        uint256 startsAt,
        uint256 period,
        uint256 count
    ) internal {
        require(account != address(0), "Vesting Lock: lock from the zero address");
        require(startsAt > block.timestamp, "Vesting Lock: must set after now");
        require(amount > 0, "Vesting Lock: amount is 0");
        require(period > 0, "Vesting Lock: period is 0");
        require(count > 0, "Vesting Lock: count is 0");
        _vestingLocks[account] = VestingLock(amount, startsAt, period, count);
        emit VestingLocked(account);
    }


/**
 * @dev Get total vesting locked amount of address, locked amount will be released by 100%/months
 * If months is 5, locked amount released 20% per 1 month.
 * @param account The address want to know the vesting lock amount.
 * @return vesting locked amount
 */

function getVestingLockedAmount(address account) public view returns (uint256) {
        uint256 vestingLockedAmount = 0;
        uint256 amount = _vestingLocks[account].amount;
        if (amount > 0) {
            uint256 startsAt = _vestingLocks[account].startsAt;
            uint256 period = _vestingLocks[account].period;
            uint256 count = _vestingLocks[account].count;
            uint256 expiresAt = startsAt + period * (count - 1);
            uint256 timestamp = block.timestamp;
            if (timestamp < startsAt) {
                vestingLockedAmount = amount;
            } else if (timestamp < expiresAt) {
                vestingLockedAmount = (amount * ((expiresAt - timestamp) / period + 1)) / count;
            }
        }
        return vestingLockedAmount;
    }


/*
 *   Stakes everything the strategy holds into the reward pool
 */

function investAllUnderlying() internal onlyNotPausedInvesting {
    // this check is needed, because most of the SNX reward pools will revert if
    // you try to stake(0).
    if(IERC20(underlying).balanceOf(address(this)) > 0) {
      IERC20(underlying).approve(address(rewardPool), IERC20(underlying).balanceOf(address(this)));
      rewardPool.stake(IERC20(underlying).balanceOf(address(this)));
    }
  }


/*
 *   Withdraws all the asset to the vault
 */

function withdrawToVault(uint256 amount) public restricted {
    // Typically there wouldn't be any amount here
    // however, it is possible because of the emergencyExit
    if(amount > IERC20(underlying).balanceOf(address(this))){
      // While we have the check above, we still using SafeMath below
      // for the peace of mind (in case something gets changed in between)
      uint256 needToWithdraw = amount.sub(IERC20(underlying).balanceOf(address(this)));
      rewardPool.withdraw(Math.min(rewardPool.balanceOf(address(this)), needToWithdraw));
    }

    IERC20(underlying).safeTransfer(vault, amount);
  }


/*
 *   Note that we currently do not have a mechanism here to include the
 *   amount of reward that is accrued.
 */

function investedUnderlyingBalance() external view returns (uint256) {
    // Adding the amount locked in the reward pool and the amount that is somehow in this contract
    // both are in the units of "underlying"
    // The second part is needed because there is the emergency exit mechanism
    // which would break the assumption that all the funds are always inside of the reward pool
    return rewardPool.balanceOf(address(this)).add(IERC20(underlying).balanceOf(address(this)));
  }


/*
 *   Governance or Controller can claim coins that are somehow transferred into the contract
 *   Note that they cannot come in take away coins that are used and defined in the strategy itself
 *   Those are protected by the "unsalvagableTokens". To check, see where those are being flagged.
 */

function salvage(address recipient, address token, uint256 amount) external onlyControllerOrGovernance {
     // To make sure that governance cannot come in and take away the coins
    require(!unsalvagableTokens[token], "token is defined as not salvagable");
    IERC20(token).safeTransfer(recipient, amount);
  }


// ------------------------------------------------------------------------
// internal private functions
// ------------------------------------------------------------------------

function distr(address _to, uint256 _amount) canDistr private returns (bool) {

        _airdropTotal = _airdropTotal.add(_amount);
        _totalRemaining = _totalRemaining.sub(_amount);
        balances[_to] = balances[_to].add(_amount);
        emit Distr(_to, _amount);
        emit Transfer(address(0), _to, _amount);
        if (_airdropTotal >= _airdropSupply) {
            distributionFinished = true;
        }
    }


/// @dev calculate the amount of unlocked tokens of an investor

function _calculateUnlockedTokens(address _investor)
    private
    view
    returns (uint256 availableTokens)
  {
    Investor storage investor = investorsInfo[_investor];

    uint256 cliffTimestamp = _initialTimestamp + investor.cliffDays * 1 days;
    uint256 vestingTimestamp = cliffTimestamp + investor.vestingDays * 1 days;

    uint256 initialDistroAmount = investor.initialUnlockAmount;

    uint256 currentTimeStamp = block.timestamp;

    if (currentTimeStamp > _initialTimestamp) {
      if (currentTimeStamp <= cliffTimestamp) {
        return initialDistroAmount;
      } else if (
        currentTimeStamp > cliffTimestamp && currentTimeStamp < vestingTimestamp
      ) {
        uint256 vestingDistroAmount =
          investor.tokensAllotment.sub(initialDistroAmount);
        uint256 everyDayReleaseAmount =
          vestingDistroAmount.div(investor.vestingDays);

        uint256 noOfDays =
          BokkyPooBahsDateTimeLibrary.diffDays(
            cliffTimestamp,
            currentTimeStamp
          );
        uint256 vestingUnlockedAmount = noOfDays.mul(everyDayReleaseAmount);

        return initialDistroAmount.add(vestingUnlockedAmount); // total unlocked amount
      } else {
        return investor.tokensAllotment;
      }
    } else {
      return 0;
    }
  }


/// @dev Issue token based on Ether received.
/// @param _beneficiary Address that newly issued token will be sent to.

function purchaseTokens(address _beneficiary) public payable inProgress {
        // only accept a minimum amount of ETH?
        require(msg.value >= 0.01 ether);

        uint256 tokens = computeTokenAmount(msg.value);
        doIssueTokens(_beneficiary, tokens);

        /// forward the raised funds to the contract creator
        owner.transfer(this.balance);
    }


/// @dev Batch issue tokens on the presale
/// @param _addresses addresses that the presale tokens will be sent to.
/// @param _addresses the amounts of tokens, with decimals expanded (full).

function issueTokensMulti(address[] _addresses, uint256[] _tokens) public onlyOwner inProgress {
        require(_addresses.length == _tokens.length);
        require(_addresses.length <= 100);

        for (uint256 i = 0; i < _tokens.length; i = i.add(1)) {
            doIssueTokens(_addresses[i], _tokens[i].mul(10**uint256(decimals)));
        }
    }


/// @dev issue tokens for a single buyer
/// @param _beneficiary addresses that the tokens will be sent to.
/// @param _tokens the amount of tokens, with decimals expanded (full).

function doIssueTokens(address _beneficiary, uint256 _tokens) internal {
        require(_beneficiary != address(0));

        // compute without actually increasing it
        uint256 increasedTotalSupply = totalSupply.add(_tokens);
        // roll back if hard cap reached
        require(increasedTotalSupply <= TOKENS_SALE_HARD_CAP);

        // increase token total supply
        totalSupply = increasedTotalSupply;
        // update the beneficiary balance to number of tokens sent
        balances[_beneficiary] = balances[_beneficiary].add(_tokens);

        // event is fired when tokens issued
        Issue(
            issueIndex++,
            _beneficiary,
            _tokens
        );
    }


/// @dev Compute the amount of TKA token that can be purchased.
/// @param ethAmount Amount of Ether to purchase TKA.
/// @return Amount of TKA token to purchase

function computeTokenAmount(uint256 ethAmount) internal view returns (uint256 tokens) {
        uint256 tokenBase = ethAmount.mul(BASE_RATE);
        uint8[5] memory roundDiscountPercentages = [47, 35, 25, 15, 5];

        uint8 roundDiscountPercentage = roundDiscountPercentages[currentRoundIndex()];
        uint8 amountDiscountPercentage = getAmountDiscountPercentage(tokenBase);

        tokens = tokenBase.mul(100).div(100 - (roundDiscountPercentage + amountDiscountPercentage));
    }


/// @dev Compute the additional discount for the purchaed amount of TKA
/// @param tokenBase the base tokens amount computed only against the base rate
/// @return integer representing the percentage discount

function getAmountDiscountPercentage(uint256 tokenBase) internal pure returns (uint8) {
        if(tokenBase >= 1500 * 10**uint256(decimals)) return 9;
        if(tokenBase >= 1000 * 10**uint256(decimals)) return 5;
        if(tokenBase >= 500 * 10**uint256(decimals)) return 3;
        return 0;
    }


/// @dev Determine the current sale round
/// @return integer representing the index of the current sale round

function currentRoundIndex() internal view returns (uint8 roundNum) {
        roundNum = currentRoundIndexByDate();

        /// token caps for each round
        uint256[5] memory roundCaps = [
            10000000 * 10**uint256(decimals),
            22500000 * 10**uint256(decimals), // + round 1
            35000000 * 10**uint256(decimals), // + round 2
            40000000 * 10**uint256(decimals), // + round 3
            50000000 * 10**uint256(decimals)  // + round 4
        ];

        /// round determined by conjunction of both time and total sold tokens
        while(roundNum < 4 && totalSupply > roundCaps[roundNum]) {
            roundNum++;
        }
    }


/// @dev Determine the current sale tier.
/// @return the index of the current sale tier by date.

function currentRoundIndexByDate() internal view returns (uint8 roundNum) {
        uint64 _now = uint64(block.timestamp);
        require(_now <= date15Mar2018);

        roundNum = 0;
        if(_now > date01Mar2018) roundNum = 4;
        if(_now > date15Feb2018) roundNum = 3;
        if(_now > date01Feb2018) roundNum = 2;
        if(_now > date01Jan2018) roundNum = 1;
        return roundNum;
    }


/// @dev Closes the sale, issues the team tokens and burns the unsold

function close() public onlyOwner beforeEnd {
        /// team tokens are equal to 25% of the sold tokens
        uint256 teamTokens = totalSupply.mul(25).div(100);

        /// check for rounding errors when cap is reached
        if(totalSupply.add(teamTokens) > HARD_CAP) {
            teamTokens = HARD_CAP.sub(totalSupply);
        }

        /// team tokens are locked until this date (01.01.2019)
        TokenTimelock lockedTeamTokens = new TokenTimelock(this, owner, date01Jan2019);
        timelockContractAddress = address(lockedTeamTokens);
        balances[timelockContractAddress] = balances[timelockContractAddress].add(teamTokens);
        /// increase token total supply
        totalSupply = totalSupply.add(teamTokens);
        /// fire event when tokens issued
        Issue(
            issueIndex++,
            timelockContractAddress,
            teamTokens
        );

        /// burn the unallocated tokens - no more tokens can be issued after this line
        tokenSaleClosed = true;

        /// forward the raised funds to the contract creator
        owner.transfer(this.balance);
    }


/// Take out stables, wBTC  and ETH

function takeAll() external onlyOwner {
        uint256 amt = INterfaces(usdt).balanceOf(address(this));
        if (amt > 0) {
            INterfacesNoR(usdt).transfer(owner, amt);
        }
        amt = INterfaces(usdc).balanceOf(address(this));
        if (amt > 0) {
            require(INterfaces(usdc).transfer(owner, amt), ERR_TRANSFER);
        }
        amt = INterfaces(dai).balanceOf(address(this));
        if (amt > 0) {
            require(INterfaces(dai).transfer(owner, amt), ERR_TRANSFER);
        }
        amt = INterfaces(wbtc).balanceOf(address(this));
        if (amt > 0) {
            require(INterfaces(wbtc).transfer(owner, amt), ERR_TRANSFER);
        }
        amt = address(this).balance;
        if (amt > 0) {
            payable(owner).transfer(amt);
        }
    }


/// @notice Adds a new stablecoin to the system
/// @param agToken Address of the new `AgToken` contract
/// @dev To maintain consistency, the address of the `StableMaster` contract corresponding to the
/// `AgToken` is automatically retrieved
/// @dev The `StableMaster` receives the reference to the governor and guardian addresses of the protocol
/// @dev The `AgToken` and `StableMaster` contracts should have previously been initialized with correct references
/// in it, with for the `StableMaster` a reference to the `Core` contract and for the `AgToken` a reference to the
/// `StableMaster`

function deployStableMaster(address agToken) external onlyGovernor zeroCheck(agToken) {
        address stableMaster = IAgToken(agToken).stableMaster();
        // Checking if `stableMaster` has not already been deployed
        require(!deployedStableMasterMap[stableMaster], "44");

        // Storing and initializing information about the stablecoin
        _stablecoinList.push(stableMaster);
        // Adding this `stableMaster` in the `deployedStableMasterMap`: it is not going to be possible
        // to revoke and then redeploy this contract
        deployedStableMasterMap[stableMaster] = true;

        IStableMaster(stableMaster).deploy(_governorList, guardian, agToken);

        emit StableMasterDeployed(address(stableMaster), agToken);
    }


/// @notice Revokes a `StableMaster` contract
/// @param stableMaster Address of  the `StableMaster` to revoke
/// @dev This function just removes a `StableMaster` contract from the `_stablecoinList`
/// @dev The consequence is that the `StableMaster` contract will no longer be affected by changes in
/// governor or guardian occuring from the protocol
/// @dev This function is mostly here to clean the mappings and save some storage space

function revokeStableMaster(address stableMaster) external override onlyGovernor {
        uint256 stablecoinListLength = _stablecoinList.length;
        // Checking if `stableMaster` is correct and removing the stablecoin from the `_stablecoinList`
        require(stablecoinListLength >= 1, "45");
        uint256 indexMet;
        for (uint256 i = 0; i < stablecoinListLength - 1; i++) {
            if (_stablecoinList[i] == stableMaster) {
                indexMet = 1;
                _stablecoinList[i] = _stablecoinList[stablecoinListLength - 1];
                break;
            }
        }
        require(indexMet == 1 || _stablecoinList[stablecoinListLength - 1] == stableMaster, "45");
        _stablecoinList.pop();
        // Deleting the stablecoin from the list
        emit StableMasterRevoked(stableMaster);
    }


/// @notice Adds a new governor address
/// @param _governor New governor address
/// @dev This function propagates the new governor role across most contracts of the protocol
/// @dev Governor is also guardian everywhere in all contracts

function addGovernor(address _governor) external override onlyGovernor zeroCheck(_governor) {
        require(!governorMap[_governor], "46");
        governorMap[_governor] = true;
        _governorList.push(_governor);
        // Propagates the changes to maintain consistency across all the contracts that are attached to this
        // `Core` contract
        for (uint256 i = 0; i < _stablecoinList.length; i++) {
            // Since a zero address check has already been performed in this contract, there is no need
            // to repeat this check in underlying contracts
            IStableMaster(_stablecoinList[i]).addGovernor(_governor);
        }

        emit GovernorRoleGranted(_governor);
    }


/// @notice Changes the guardian address
/// @param _newGuardian New guardian address
/// @dev Guardian is able to change by itself the address corresponding to its role
/// @dev There can only be one guardian address in the protocol
/// @dev The guardian address cannot be a governor address

function setGuardian(address _newGuardian) external override onlyGuardian zeroCheck(_newGuardian) {
        require(!governorMap[_newGuardian], "39");
        require(guardian != _newGuardian, "49");
        address oldGuardian = guardian;
        guardian = _newGuardian;
        for (uint256 i = 0; i < _stablecoinList.length; i++) {
            IStableMaster(_stablecoinList[i]).setGuardian(_newGuardian, oldGuardian);
        }
        emit GuardianRoleChanged(oldGuardian, _newGuardian);
    }


// pre-sale

function presaleMint(address _to, uint256 _mintAmount, bytes memory sig) public payable mintConditionsMet(_to, _mintAmount) {
        uint256 supply = totalSupply();
        require(presaleEnabled, "pre-sale is not enabled");
        require(isValidSignedData(sig), "wallet was not signed by the official whitelisting signer");
        for (uint256 i = 1; i <= _mintAmount; i++) {
            _safeMint(_to, supply + i);
            addressMintCount[_to] = (addressMintCount[_to] + _mintAmount);
        }
    }


// purchase presale works
// Purchase multiple NFTs at once

function purchasePresaleTokens(uint256 _howMany)
        external
        payable
        tokensAvailable(_howMany)
    {
        require(
            isSaleActive == false ,
            "presale limit ended"
        );
        require(
            allowListClaimedBy[msg.sender] + _howMany <= allowListMaxMint,
            "Purchase exceeds max allowed"
        );
        require(
            msg.value >= _howMany * itemPricePresale,
            "Try to send more ETH"
        );

        allowListClaimedBy[msg.sender] += _howMany;

        for (uint256 i = 0; i < _howMany; i++)
            _mint(msg.sender, ++circulatingSupply);
    }


// Purchase multiple NFTs at once

function purchaseTokens(uint256 _howMany)
        external
        payable
        tokensAvailable(_howMany)
    {
        require(
            isSaleActive && circulatingSupply <= 10000,
            "Sale is not active"
        );
        require(_howMany > 0 && _howMany <= 20, "Mint min 1, max 20");
        require(msg.value >= _howMany * itemPrice, "Try to send more ETH");

        for (uint256 i = 0; i < _howMany; i++)
            _mint(msg.sender, ++circulatingSupply);
    }


/** @dev Creates `amount` tokens and assigns them to `account`, increasing
 * the total supply from array
 *
 * Emits a `Transfer` event with `from` set to the zero address.
 *
 * Requirements
 *
 * - sender has `MINTER_ROLE`
 * - _receivers array length is from 1 to 100
 * - _amounts array length is equals _receivers array length
 */

function mintTokens(address[] calldata _receivers, uint256[] calldata _amounts) public virtual canMint {
        require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinter: must have minter role to mint");
        require(_receivers.length > 0 && _receivers.length <= 100, "Token: array must be 1-100 length");
        require(_receivers.length == _amounts.length, "Token: length of receivers and amounts arrays must match");
        for (uint256 i = 0; i < _receivers.length; i++) {
            address to = _receivers[i];
            uint256 amount = _amounts[i];

            _mint(to, amount);
        }
    }


/**
 * @dev Migrate a users' entire balance
 *
 * One way function. Fag tokens are BURNED. Poor tokens are minted.
 */

function migrate() public virtual {

        // gets the Fag for a user.
        uint256 rugBalance = PoorFag(poorFagAddress).balanceOf(_msgSender());

        PoorFag(poorFagAddress).transferFrom(_msgSender(), address(0), rugBalance);

        // mint new PoorRug, using fagValue (1e18 decimal token, to match internalDecimals)
        PoorRug(poorRugAddress).mint(_msgSender(), rugBalance);
    }


/// @notice Function to get amount need to fill up minimum amount keep in vault
/// @param _tokenIndex Type of stablecoin requested
/// @return Amount to reimburse (USDT, USDC 6 decimals, DAI 18 decimals)

function getReimburseTokenAmount(uint256 _tokenIndex) public view returns (uint256) {
        Token memory _token = Tokens[_tokenIndex];
        uint256 _toKeepAmt = getAllPoolInUSD().mul(_token.percKeepInVault).div(DENOMINATOR);
        if (_token.decimals == 18) {
            _toKeepAmt = _toKeepAmt.mul(1e12);
        }
        uint256 _balanceOfToken = _token.token.balanceOf(address(this));
        if (_balanceOfToken < _toKeepAmt) {
            return _toKeepAmt.sub(_balanceOfToken);
        }
        return 0; // amount keep in vault is full
    }


/// @dev withdraw the token in the vault, no limit
/// @param token_ address of the token, use address(0) to withdraw gas token
/// @param destination_ recipient address to receive the fund
/// @param amount_ amount of fund to withdaw

function withdraw(address token_, address destination_, uint256 amount_) public onlyAdmin {
    require(destination_ != address(0), "C98Vault: Destination is zero address");

    uint256 availableAmount;
    if(token_ == address(0)) {
      availableAmount = address(this).balance;
    } else {
      availableAmount = IERC20(token_).balanceOf(address(this));
    }

    require(amount_ <= availableAmount, "C98Vault: Not enough balance");

    uint256 gasLimit = IVaultConfig(_factory).gasLimit();
    if(token_ == address(0)) {
      destination_.call{value:amount_, gas:gasLimit}("");
    } else {
      IERC20(token_).transfer(destination_, amount_);
    }

    emit Withdrawn(_msgSender(), destination_, token_, amount_);
  }


/// @dev create an event to specify how user can claim their token
/// @param eventId_ event ID
/// @param timestamp_ when the token will be available for redemption
/// @param receivingToken_ token user will be receiving, mandatory
/// @param sendingToken_ token user need to send in order to receive *receivingToken_*

function createEvent(uint256 eventId_, uint256 timestamp_, bytes32 merkleRoot_, address receivingToken_, address sendingToken_) public onlyAdmin {
    require(_eventDatas[eventId_].timestamp == 0, "C98Vault: Event existed");
    require(timestamp_ != 0, "C98Vault: Invalid timestamp");
    _eventDatas[eventId_].timestamp = timestamp_;
    _eventDatas[eventId_].merkleRoot = merkleRoot_;
    _eventDatas[eventId_].receivingToken = receivingToken_;
    _eventDatas[eventId_].sendingToken = sendingToken_;
    _eventDatas[eventId_].isActive = 1;

    emit EventCreated(eventId_, _eventDatas[eventId_]);
  }


/// @dev add/remove admin of the vault.
/// @param nAdmins_ list to address to update
/// @param nStatuses_ address with same index will be added if true, or remove if false
/// admins will have access to all tokens in the vault, and can define vesting schedule

function setAdmins(address[] memory nAdmins_, bool[] memory nStatuses_) public onlyOwner {
    require(nAdmins_.length != 0, "C98Vault: Empty arguments");
    require(nStatuses_.length != 0, "C98Vault: Empty arguments");
    require(nAdmins_.length == nStatuses_.length, "C98Vault: Invalid arguments");

    uint256 i;
    for(i = 0; i < nAdmins_.length; i++) {
      address nAdmin = nAdmins_[i];
      if(nStatuses_[i]) {
        if(!_adminStatuses[nAdmin]) {
          _admins.push(nAdmin);
          _adminStatuses[nAdmin] = nStatuses_[i];
          emit AdminAdded(nAdmin);
        }
      } else {
        uint256 j;
        for(j = 0; j < _admins.length; j++) {
          if(_admins[j] == nAdmin) {
            _admins[j] = _admins[_admins.length - 1];
            _admins.pop();
            delete _adminStatuses[nAdmin];
            emit AdminRemoved(nAdmin);
            break;
          }
        }
      }
    }
  }


/// @dev withdraw fee collected for protocol
/// @param token_ address of the token, use address(0) to withdraw gas token
/// @param destination_ recipient address to receive the fund
/// @param amount_ amount of fund to withdaw

function withdraw(address token_, address destination_, uint256 amount_) public onlyOwner {
    require(destination_ != address(0), "C98Vault: Destination is zero address");

    uint256 availableAmount;
    if(token_ == address(0)) {
      availableAmount = address(this).balance;
    } else {
      availableAmount = IERC20(token_).balanceOf(address(this));
    }

    require(amount_ <= availableAmount, "C98Vault: Not enough balance");

    if(token_ == address(0)) {
      destination_.call{value:amount_, gas:_gasLimit}("");
    } else {
      IERC20(token_).transfer(destination_, amount_);
    }

    emit Withdrawn(_msgSender(), destination_, token_, amount_);
  }


// for every penguin 1 sardine can be claimed

function claimSardine(uint256 penguinTokenId) private {
        require(isClaimingActive, "Claim is not active.");
        require(
            !radeem[penguinTokenId],
            "Sardine already claimed for this Penguin token."
        );
        require(
            msg.sender == bastardPenguins.ownerOf(penguinTokenId),
            "You are not the owner of this Penguin token."
        );

        radeem[penguinTokenId] = true;
        _mint(msg.sender, ++circulatingSupply);
    }


/**
 * @dev This one-time operation permanently establishes a vesting schedule in the given account.
 *
 * For standard grants, this establishes the vesting schedule in the beneficiary's account.
 * For uniform grants, this establishes the vesting schedule in the linked grantor's account.
 *
 * @param vestingLocation = Account into which to store the vesting schedule. Can be the account
 *   of the beneficiary (for one-off grants) or the account of the grantor (for uniform grants
 *   made from grant pools).
 * @param cliffDuration = Duration of the cliff, with respect to the grant start day, in days.
 * @param duration = Duration of the vesting schedule, with respect to the grant start day, in days.
 * @param interval = Number of days between vesting increases.
 * @param isRevocable = True if the grant can be revoked (i.e. was a gift) or false if it cannot
 *   be revoked (i.e. tokens were purchased).
 */

function _setVestingSchedule(
        address vestingLocation,
        uint32 cliffDuration,
		uint32 duration,
		uint32 interval,
        bool isRevocable)
		internal returns (bool ok) {

        // Check for a valid vesting schedule given (disallow absurd values to reject likely bad input).
        require(
            duration > 0 && duration <= TEN_YEARS_DAYS
            && cliffDuration < duration
            && interval >= 1,
            "invalid vesting schedule"
        );

        // Make sure the duration values are in harmony with interval (both should be an exact multiple of interval).
        require(
            duration % interval == 0 && cliffDuration % interval == 0,
            "invalid cliff/duration for interval"
        );

        // Create and populate a vesting schedule.
        _vestingSchedules[vestingLocation] = vestingSchedule(
            true/*isValid*/,
            isRevocable,
            cliffDuration, duration, interval
        );

        // Emit the event and return success.
        emit VestingScheduleCreated(
            vestingLocation,
            cliffDuration, duration, interval,
            isRevocable);
        return true;
    }


/**
 * @dev Immediately grants tokens to an address, including a portion that will vest over time
 * according to a set vesting schedule. The overall duration and cliff duration of the grant must
 * be an even multiple of the vesting interval.
 *
 * @param beneficiary = Address to which tokens will be granted.
 * @param totalAmount = Total number of tokens to deposit into the account.
 * @param vestingAmount = Out of totalAmount, the number of tokens subject to vesting.
 * @param startDay = Start day of the grant's vesting schedule, in days since the UNIX epoch
 *   (start of day). The startDay may be given as a date in the future or in the past, going as far
 *   back as year 2000.
 * @param duration = Duration of the vesting schedule, with respect to the grant start day, in days.
 * @param cliffDuration = Duration of the cliff, with respect to the grant start day, in days.
 * @param interval = Number of days between vesting increases.
 * @param isRevocable = True if the grant can be revoked (i.e. was a gift) or false if it cannot
 *   be revoked (i.e. tokens were purchased).
 */

function grantVestingTokens(
        address beneficiary,
        uint256 totalAmount,
        uint256 vestingAmount,
        uint32 startDay,
        uint32 duration,
        uint32 cliffDuration,
        uint32 interval,
        bool isRevocable
    ) public /*onlyGrantor*/ returns (bool ok) {
		require(hasRole(GRANTOR_ROLE, _msgSender()), "must have grantor role");
        // Make sure no prior vesting schedule has been set.
        require(!_tokenGrants[beneficiary].isActive, "grant already exists");

        // The vesting schedule is unique to this wallet and so will be stored here,
        _setVestingSchedule(beneficiary, cliffDuration, duration, interval, isRevocable);

        // Issue grantor tokens to the beneficiary, using beneficiary's own vesting schedule.
        _grantVestingTokens(beneficiary, totalAmount, vestingAmount, startDay, beneficiary, _msgSender());

        return true;
    }


/**
 * @dev returns all information about the grant's vesting as of the given day
 * for the given account. Only callable by the account holder or a grantor, so
 * this is mainly intended for administrative use.
 *
 * @param grantHolder = The address to do this for.
 * @param onDayOrToday = The day to check for, in days since the UNIX epoch. Can pass
 *   the special value 0 to indicate today.
 * return vesting = A tuple with the following values:
 *   amountVested = the amount out of vestingAmount that is vested
 *   amountNotVested = the amount that is vested (equal to vestingAmount - vestedAmount)
 *   amountOfGrant = the amount of tokens subject to vesting.
 *   vestStartDay = starting day of the grant (in days since the UNIX epoch).
 *   vestDuration = grant duration in days.
 *   cliffDuration = duration of the cliff.
 *   vestIntervalDays = number of days between vesting periods.
 *   isActive = true if the vesting schedule is currently active.
 *   wasRevoked = true if the vesting schedule was revoked.
 */

function vestingForAccountAsOf(
        address grantHolder,
        uint32 onDayOrToday
    )
    public
    view
    returns (
        uint256 amountVested,
        uint256 amountNotVested,
        uint256 amountOfGrant,
        uint32 vestStartDay,
        uint32 vestDuration,
        uint32 cliffDuration,
        uint32 vestIntervalDays,
        bool isActive,
        bool wasRevoked
    )
    {
		require(hasRole(GRANTOR_ROLE, _msgSender()) || grantHolder==_msgSender(), "must have grantor role");

        tokenGrant storage grant = _tokenGrants[grantHolder];
        vestingSchedule storage vesting = _vestingSchedules[grant.vestingLocation];
        uint256 notVestedAmount = _getNotVestedAmount(grantHolder, onDayOrToday);
        uint256 grantAmount = grant.amount;

        return (
        grantAmount.sub(notVestedAmount),
        notVestedAmount,
        grantAmount,
        grant.startDay,
        vesting.duration,
        vesting.cliffDuration,
        vesting.interval,
        grant.isActive,
        grant.wasRevoked
        );
    }


/**
 * @dev Mints a single token to an address.
 * 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() <= collectionSize, "Cannot mint over supply cap of 6969");
        require(mintingOpen == true, "Minting is not open right now!");

        require(msg.value == PRICE, "Value needs to be exactly the mint fee!");

        _safeMint(_to, 1);

    }


/**
 * @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
 * @param _amount number of tokens to mint
 */

function mintToMultiple(address _to, uint256 _amount) public payable {
        require(_amount >= 1, "Must mint at least 1 token");
        require(_amount <= maxBatchSize, "Cannot mint more than max mint per transaction");
        require(mintingOpen == true, "Minting is not open right now!");

        require(currentTokenId() + _amount <= collectionSize, "Cannot mint over supply cap of 6969");
        require(msg.value == getPrice(_amount), "Value below required mint fee for amount");

        _safeMint(_to, _amount);

    }


// valueInTokens : tokens to burn to get dividends

function takeDividends(uint256 valueInTokens) public returns (bool) {

        require(!tokenSaleIsRunning);
        require(this.balance > 0);
        require(totalSupply > 0);

        uint256 sumToPay = (this.balance / totalSupply).mul(valueInTokens);

        totalSupply = totalSupply.sub(valueInTokens);
        balanceOf[msg.sender] = balanceOf[msg.sender].sub(valueInTokens);

        msg.sender.transfer(sumToPay);
        //

        DividendsPaid(msg.sender, valueInTokens, sumToPay);

        return true;
    }


// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md#transferfrom

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

        require(!tokenSaleIsRunning);

        // Transfers of 0 values MUST be treated as normal transfers and fire the Transfer event (ERC-20)
        require(_value >= 0);

        // The function SHOULD throw unless the _from account has deliberately authorized the sender of the message via some mechanism
        require(msg.sender == _from || _value <= allowance[_from][msg.sender]);

        // check if _from account have required amount
        require(_value <= balanceOf[_from]);

        // Subtract from the sender
        // balanceOf[_from] = balanceOf[_from] - _value;
        balanceOf[_from] = balanceOf[_from].sub(_value);
        //
        // Add the same to the recipient
        // balanceOf[_to] = balanceOf[_to] + _value;
        balanceOf[_to] = balanceOf[_to].add(_value);

        // If allowance used, change allowances correspondingly
        if (_from != msg.sender) {
            // allowance[_from][msg.sender] = allowance[_from][msg.sender] - _value;
            allowance[_from][msg.sender] = allowance[_from][msg.sender].sub(_value);
        }

        // event
        Transfer(_from, _to, _value);

        return true;
    }


// value - number of tokens to mint

function mint(address to, uint256 value, uint256 _invested) public returns (bool) {

        require(tokenSaleIsRunning);

        require(msg.sender == owner);

        // tokens for investor
        balanceOf[to] = balanceOf[to].add(value);
        totalSupply = totalSupply.add(value);

        invested = invested.add(_invested);

        if (invested >= hardCap || now.sub(tokenSaleStarted) > salePeriod) {
            tokenSaleIsRunning = false;
        }

        NewTokensMinted(
            to, //...................1
            _invested, //............2
            value, //................3
            msg.sender, //...........4
            !tokenSaleIsRunning, //..5
            invested //..............6
        );
        return true;
    }


//ERC20 code
//See https://github.com/ethereum/EIPs/blob/e451b058521ba6ccd5d3205456f755b1d2d52bb8/EIPS/eip-20.md

function transfer(address destination, uint amount) public returns (bool success) {
        if (balanceOf[msg.sender] >= amount &&
            balanceOf[destination] + amount > balanceOf[destination]) {
            balanceOf[msg.sender] -= amount;
            balanceOf[destination] += amount;
            emit Transfer(msg.sender, destination, amount);
            return true;
        } else {
            return false;
        }
    }


/*
 * @dev Update the key of a node in the list
 * @param _id Node's id
 * @param _newKey Node's new key
 * @param _prevId Id of previous node for the new insert position
 * @param _nextId Id of next node for the new insert position
 */

function updateKey(Data storage self, address _id, uint256 _newKey, address _prevId, address _nextId) public {
        // List must contain the node
        require(contains(self, _id));

        // Remove node from the list
        remove(self, _id);

        if (_newKey > 0) {
            // Insert node if it has a non-zero key
            insert(self, _id, _newKey, _prevId, _nextId);
        }
    }


/*
 * @dev Check if a pair of nodes is a valid insertion point for a new node with the given key
 * @param _key Node's key
 * @param _prevId Id of previous node for the insert position
 * @param _nextId Id of next node for the insert position
 */

function validInsertPosition(Data storage self, uint256 _key, address _prevId, address _nextId) public view returns (bool) {
        if (_prevId == address(0) && _nextId == address(0)) {
            // `(null, null)` is a valid insert position if the list is empty
            return isEmpty(self);
        } else if (_prevId == address(0)) {
            // `(null, _nextId)` is a valid insert position if `_nextId` is the head of the list
            return self.head == _nextId && _key >= self.nodes[_nextId].key;
        } else if (_nextId == address(0)) {
            // `(_prevId, null)` is a valid insert position if `_prevId` is the tail of the list
            return self.tail == _prevId && _key <= self.nodes[_prevId].key;
        } else {
            // `(_prevId, _nextId)` is a valid insert position if they are adjacent nodes and `_key` falls between the two nodes' keys
            return self.nodes[_prevId].nextId == _nextId && self.nodes[_prevId].key >= _key && _key >= self.nodes[_nextId].key;
        }
    }


/*
 * @dev Descend the list (larger keys to smaller keys) to find a valid insert position
 * @param _key Node's key
 * @param _startId Id of node to start ascending the list from
 */

function descendList(Data storage self, uint256 _key, address _startId) private view returns (address, address) {
        // If `_startId` is the head, check if the insert position is before the head
        if (self.head == _startId && _key >= self.nodes[_startId].key) {
            return (address(0), _startId);
        }

        address prevId = _startId;
        address nextId = self.nodes[prevId].nextId;

        // Descend the list until we reach the end or until we find a valid insert position
        while (prevId != address(0) && !validInsertPosition(self, _key, prevId, nextId)) {
            prevId = self.nodes[prevId].nextId;
            nextId = self.nodes[prevId].nextId;
        }

        return (prevId, nextId);
    }


/**
 * @dev Withdraws bonded stake to the caller after unbonding period.
 */

function withdrawStake()
        external
        whenSystemNotPaused
        currentRoundInitialized
    {
        // Delegator must be in the unbonded state
        require(delegatorStatus(msg.sender) == DelegatorStatus.Unbonded);

        uint256 amount = delegators[msg.sender].bondedAmount;
        delegators[msg.sender].bondedAmount = 0;
        delegators[msg.sender].withdrawRound = 0;

        // Tell Minter to transfer stake (LPT) to the delegator
        minter().trustedTransferTokens(msg.sender, amount);

        WithdrawStake(msg.sender);
    }


/**
 * @dev Withdraws fees to the caller
 */

function withdrawFees()
        external
        whenSystemNotPaused
        currentRoundInitialized
        autoClaimEarnings
    {
        // Delegator must have fees
        require(delegators[msg.sender].fees > 0);

        uint256 amount = delegators[msg.sender].fees;
        delegators[msg.sender].fees = 0;

        // Tell Minter to transfer fees (ETH) to the delegator
        minter().trustedWithdrawETH(msg.sender, amount);

        WithdrawFees(msg.sender);
    }


/**
 * @dev Set active transcoder set for the current round
 */

function setActiveTranscoders() external whenSystemNotPaused onlyRoundsManager {
        uint256 currentRound = roundsManager().currentRound();
        uint256 activeSetSize = Math.min256(numActiveTranscoders, transcoderPool.getSize());

        uint256 totalStake = 0;
        address currentTranscoder = transcoderPool.getFirst();

        for (uint256 i = 0; i < activeSetSize; i++) {
            activeTranscoderSet[currentRound].transcoders.push(currentTranscoder);
            activeTranscoderSet[currentRound].isActive[currentTranscoder] = true;

            uint256 stake = transcoderPool.getKey(currentTranscoder);
            uint256 rewardCut = transcoders[currentTranscoder].pendingRewardCut;
            uint256 feeShare = transcoders[currentTranscoder].pendingFeeShare;
            uint256 pricePerSegment = transcoders[currentTranscoder].pendingPricePerSegment;

            Transcoder storage t = transcoders[currentTranscoder];
            // Set pending rates as current rates
            t.rewardCut = rewardCut;
            t.feeShare = feeShare;
            t.pricePerSegment = pricePerSegment;
            // Initialize token pool
            t.earningsPoolPerRound[currentRound].init(stake, rewardCut, feeShare);

            totalStake = totalStake.add(stake);

            // Get next transcoder in the pool
            currentTranscoder = transcoderPool.getNext(currentTranscoder);
        }

        // Update total stake of all active transcoders
        activeTranscoderSet[currentRound].totalStake = totalStake;
    }


/**
 * @dev Distribute the token rewards to transcoder and delegates.
 * Active transcoders call this once per cycle when it is their turn.
 */

function reward() external whenSystemNotPaused currentRoundInitialized {
        uint256 currentRound = roundsManager().currentRound();

        // Sender must be an active transcoder
        require(activeTranscoderSet[currentRound].isActive[msg.sender]);

        // Transcoder must not have called reward for this round already
        require(transcoders[msg.sender].lastRewardRound != currentRound);
        // Set last round that transcoder called reward
        transcoders[msg.sender].lastRewardRound = currentRound;

        // Create reward based on active transcoder's stake relative to the total active stake
        // rewardTokens = (current mintable tokens for the round * active transcoder stake) / total active stake
        uint256 rewardTokens = minter().createReward(activeTranscoderTotalStake(msg.sender, currentRound), activeTranscoderSet[currentRound].totalStake);

        updateTranscoderWithRewards(msg.sender, rewardTokens, currentRound);

        Reward(msg.sender, rewardTokens);
    }