/// @notice Use token address ETH_TOKEN_ADDRESS for ether
/// @dev Do one trade with each reserve in reservesData, verifying network balance
///    as expected to ensure reserves take correct src amount
/// @param src Source token
/// @param dest Destination token
/// @param destAddress Address to send tokens to
/// @param reservesData reservesData to trade
/// @param expectedDestAmount Amount to be transferred to destAddress
/// @param srcDecimals Decimals of source token
/// @param destDecimals Decimals of destination token

function doReserveTrades(
        IERC20 src,
        IERC20 dest,
        address payable destAddress,
        ReservesData memory reservesData,
        uint256 expectedDestAmount,
        uint256 srcDecimals,
        uint256 destDecimals
    ) internal virtual {

        if (src == dest) {
            // eth -> eth, need not do anything except for token -> eth: transfer eth to destAddress
            if (destAddress != (address(this))) {
                (bool success, ) = destAddress.call{value: expectedDestAmount}("");
                require(success, "send dest qty failed");
            }
            return;
        }

        tradeAndVerifyNetworkBalance(
            reservesData,
            src,
            dest,
            srcDecimals,
            destDecimals
        );

        if (destAddress != address(this)) {
            // for eth -> token / token -> token, transfer tokens to destAddress
            dest.safeTransfer(destAddress, expectedDestAmount);
        }
    }


/// @notice If user maxDestAmount < actual dest amount, actualSrcAmount will be < srcAmount
/// Calculate the change, and send it back to the user

function handleChange(
        IERC20 src,
        uint256 srcAmount,
        uint256 requiredSrcAmount,
        address payable trader
    ) internal {
        if (requiredSrcAmount < srcAmount) {
            // if there is "change" send back to trader
            if (src == ETH_TOKEN_ADDRESS) {
                (bool success, ) = trader.call{value: (srcAmount - requiredSrcAmount)}("");
                require(success, "Send change failed");
            } else {
                src.safeTransfer(trader, (srcAmount - requiredSrcAmount));
            }
        }
    }


/// @notice This function does all calculations to find trade dest amount without accounting
///        for maxDestAmount. Part of this process includes:
///        - Call kyberMatchingEngine to parse hint and get an optional reserve list to trade.
///        - Query reserve rates and call kyberMatchingEngine to use best reserve.
///        - Calculate trade values and fee values.
///     This function should set all TradeData information so that it can be later used without
///         any ambiguity
/// @param tradeData Main trade data object for trade info to be stored
/// @param hint Advanced user instructions for the trade

function calcRatesAndAmounts(TradeData memory tradeData, bytes memory hint)
        internal
        view
        returns (uint256 destAmount, uint256 rateWithNetworkFee)
    {
        validateFeeInput(tradeData.input, tradeData.networkFeeBps);

        // token -> eth: find best reserves match and calculate wei amount
        tradeData.tradeWei = calcDestQtyAndMatchReserves(
            tradeData.input.src,
            ETH_TOKEN_ADDRESS,
            tradeData.input.srcAmount,
            tradeData,
            tradeData.tokenToEth,
            hint
        );

        require(tradeData.tradeWei <= MAX_QTY, "Trade wei > MAX_QTY");
        if (tradeData.tradeWei == 0) {
            return (0, 0);
        }

        // calculate fees
        tradeData.platformFeeWei = (tradeData.tradeWei * tradeData.input.platformFeeBps) / BPS;
        tradeData.networkFeeWei =
            (((tradeData.tradeWei * tradeData.networkFeeBps) / BPS) * tradeData.feeAccountedBps) /
            BPS;

        assert(tradeData.tradeWei >= (tradeData.networkFeeWei + tradeData.platformFeeWei));

        // eth -> token: find best reserves match and calculate trade dest amount
        uint256 actualSrcWei = tradeData.tradeWei -
            tradeData.networkFeeWei -
            tradeData.platformFeeWei;

        destAmount = calcDestQtyAndMatchReserves(
            ETH_TOKEN_ADDRESS,
            tradeData.input.dest,
            actualSrcWei,
            tradeData,
            tradeData.ethToToken,
            hint
        );

        tradeData.networkFeeWei =
            (((tradeData.tradeWei * tradeData.networkFeeBps) / BPS) * tradeData.feeAccountedBps) /
            BPS;

        rateWithNetworkFee = calcRateFromQty(
            tradeData.input.srcAmount * (BPS - tradeData.input.platformFeeBps) / BPS,
            destAmount,
            tradeData.tokenToEth.decimals,
            tradeData.ethToToken.decimals
        );
    }


/// @dev Checks a trade input validity, including correct src amounts
/// @param input Trade input structure

function validateTradeInput(TradeInput memory input) internal view
    {
        require(isEnabled, "network disabled");
        require(kyberProxyContracts[msg.sender], "bad sender");
        require(tx.gasprice <= maxGasPriceValue, "gas price");
        require(input.srcAmount <= MAX_QTY, "srcAmt > MAX_QTY");
        require(input.srcAmount != 0, "0 srcAmt");
        require(input.destAddress != address(0), "dest add 0");
        require(input.src != input.dest, "src = dest");

        if (input.src == ETH_TOKEN_ADDRESS) {
            require(msg.value == input.srcAmount); // kyberProxy issues message here
        } else {
            require(msg.value == 0); // kyberProxy issues message here
            // funds should have been moved to this contract already.
            require(input.src.balanceOf(address(this)) >= input.srcAmount, "no tokens");
        }
    }


/// @notice Update reserve data with selected reserves from kyberMatchingEngine

function updateReservesList(ReservesData memory reservesData, uint256[] memory selectedIndexes)
        internal
        pure
    {
        uint256 numReserves = selectedIndexes.length;

        require(numReserves <= reservesData.addresses.length, "doMatch: too many reserves");

        IKyberReserve[] memory reserveAddresses = new IKyberReserve[](numReserves);
        bytes32[] memory reserveIds = new bytes32[](numReserves);
        uint256[] memory splitsBps = new uint256[](numReserves);
        bool[] memory isFeeAccountedFlags = new bool[](numReserves);
        bool[] memory isEntitledRebateFlags = new bool[](numReserves);
        uint256[] memory srcAmounts = new uint256[](numReserves);
        uint256[] memory rates = new uint256[](numReserves);

        // update participating resevres and all data (rates, srcAmounts, feeAcounted etc.)
        for (uint256 i = 0; i < numReserves; i++) {
            reserveAddresses[i] = reservesData.addresses[selectedIndexes[i]];
            reserveIds[i] = reservesData.ids[selectedIndexes[i]];
            splitsBps[i] = reservesData.splitsBps[selectedIndexes[i]];
            isFeeAccountedFlags[i] = reservesData.isFeeAccountedFlags[selectedIndexes[i]];
            isEntitledRebateFlags[i] = reservesData.isEntitledRebateFlags[selectedIndexes[i]];
            srcAmounts[i] = reservesData.srcAmounts[selectedIndexes[i]];
            rates[i] = reservesData.rates[selectedIndexes[i]];
        }

        // update values
        reservesData.addresses = reserveAddresses;
        reservesData.ids = reserveIds;
        reservesData.splitsBps = splitsBps;
        reservesData.isFeeAccountedFlags = isFeeAccountedFlags;
        reservesData.isEntitledRebateFlags = isEntitledRebateFlags;
        reservesData.rates = rates;
        reservesData.srcAmounts = srcAmounts;
    }


/// @notice Verify split values bps and reserve ids,
///     then calculate the destQty from srcAmounts and rates
/// @dev Each split bps must be in range (0, BPS]
/// @dev Total split bps must be 100%
/// @dev Reserve ids must be increasing

function validateTradeCalcDestQtyAndFeeData(
        IERC20 src,
        ReservesData memory reservesData,
        TradeData memory tradeData
    ) internal pure returns (uint256 totalDestAmount) {
        uint256 totalBps;
        uint256 srcDecimals = (src == ETH_TOKEN_ADDRESS) ? ETH_DECIMALS : reservesData.decimals;
        uint256 destDecimals = (src == ETH_TOKEN_ADDRESS) ? reservesData.decimals : ETH_DECIMALS;

        for (uint256 i = 0; i < reservesData.addresses.length; i++) {
            if (i > 0 && (uint256(reservesData.ids[i]) <= uint256(reservesData.ids[i - 1]))) {
                return 0; // ids are not in increasing order
            }
            totalBps += reservesData.splitsBps[i];

            uint256 destAmount = calcDstQty(
                reservesData.srcAmounts[i],
                srcDecimals,
                destDecimals,
                reservesData.rates[i]
            );
            if (destAmount == 0) {
                return 0;
            }
            totalDestAmount += destAmount;

            if (reservesData.isFeeAccountedFlags[i]) {
                tradeData.feeAccountedBps += reservesData.splitsBps[i];

                if (reservesData.isEntitledRebateFlags[i]) {
                    tradeData.numEntitledRebateReserves++;
                }
            }
        }

        if (totalBps != BPS) {
            return 0;
        }
    }


/// @notice Recalculates tradeWei, network and platform fees, and actual source amount needed for the trade
/// in the event actualDestAmount > maxDestAmount

function calcTradeSrcAmountFromDest(TradeData memory tradeData)
        internal
        pure
        virtual
        returns (uint256 actualSrcAmount)
    {
        uint256 weiAfterDeductingFees;
        if (tradeData.input.dest != ETH_TOKEN_ADDRESS) {
            weiAfterDeductingFees = calcTradeSrcAmount(
                tradeData.tradeWei - tradeData.platformFeeWei - tradeData.networkFeeWei,
                ETH_DECIMALS,
                tradeData.ethToToken.decimals,
                tradeData.input.maxDestAmount,
                tradeData.ethToToken
            );
        } else {
            weiAfterDeductingFees = tradeData.input.maxDestAmount;
        }

        // reverse calculation, because we are working backwards
        uint256 newTradeWei =
            (weiAfterDeductingFees * BPS * BPS) /
            ((BPS * BPS) -
                (tradeData.networkFeeBps *
                tradeData.feeAccountedBps +
                tradeData.input.platformFeeBps *
                BPS));
        tradeData.tradeWei = minOf(newTradeWei, tradeData.tradeWei);
        // recalculate network and platform fees based on tradeWei
        tradeData.networkFeeWei =
            (((tradeData.tradeWei * tradeData.networkFeeBps) / BPS) * tradeData.feeAccountedBps) /
            BPS;
        tradeData.platformFeeWei = (tradeData.tradeWei * tradeData.input.platformFeeBps) / BPS;

        if (tradeData.input.src != ETH_TOKEN_ADDRESS) {
            actualSrcAmount = calcTradeSrcAmount(
                tradeData.input.srcAmount,
                tradeData.tokenToEth.decimals,
                ETH_DECIMALS,
                tradeData.tradeWei,
                tradeData.tokenToEth
            );
        } else {
            actualSrcAmount = tradeData.tradeWei;
        }

        assert(actualSrcAmount <= tradeData.input.srcAmount);
    }


/// @notice Recalculates srcAmounts and stores into tradingReserves, given the new destAmount.
///     Uses the original proportion of srcAmounts and rates to determine new split destAmounts,
///     then calculate the respective srcAmounts
/// @dev Due to small rounding errors, will fallback to current src amounts if new src amount is greater

function calcTradeSrcAmount(
        uint256 srcAmount,
        uint256 srcDecimals,
        uint256 destDecimals,
        uint256 destAmount,
        ReservesData memory reservesData
    ) internal pure returns (uint256 newSrcAmount) {
        uint256 totalWeightedDestAmount;
        for (uint256 i = 0; i < reservesData.srcAmounts.length; i++) {
            totalWeightedDestAmount += reservesData.srcAmounts[i] * reservesData.rates[i];
        }

        uint256[] memory newSrcAmounts = new uint256[](reservesData.srcAmounts.length);
        uint256 destAmountSoFar;
        uint256 currentSrcAmount;
        uint256 destAmountSplit;

        for (uint256 i = 0; i < reservesData.srcAmounts.length; i++) {
            currentSrcAmount = reservesData.srcAmounts[i];
            require(destAmount * currentSrcAmount * reservesData.rates[i] / destAmount ==
                    currentSrcAmount * reservesData.rates[i],
                "multiplication overflow");
            destAmountSplit = i == (reservesData.srcAmounts.length - 1)
                ? (destAmount - destAmountSoFar)
                : (destAmount * currentSrcAmount * reservesData.rates[i]) /
                    totalWeightedDestAmount;
            destAmountSoFar += destAmountSplit;

            newSrcAmounts[i] = calcSrcQty(
                destAmountSplit,
                srcDecimals,
                destDecimals,
                reservesData.rates[i]
            );
            if (newSrcAmounts[i] > currentSrcAmount) {
                // revert back to use current src amounts
                return srcAmount;
            }

            newSrcAmount += newSrcAmounts[i];
        }
        // new src amounts are used only when all of them aren't greater then current srcAmounts
        reservesData.srcAmounts = newSrcAmounts;
    }


/*
 * Buy in function to be called mostly from the fallback function
 * @dev kept public in order to buy for someone else
 * @param beneficiary address
 */

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

        // Check the register if the investor was approved
        require(register.approved(beneficiary));

        uint256 weiAmount = msg.value;

        // calculate token amount to be created
        uint256 toGet = howMany(msg.value);

        require((toGet > 0) && (toGet.add(tokensSold) <= toBeSold));

        // update state
        weiRaised = weiRaised.add(weiAmount);
        tokensSold = tokensSold.add(toGet);

        token.mint(beneficiary, toGet);
        TokenPurchase(msg.sender, beneficiary, weiAmount, toGet);

        forwardFunds();

    }


/**
 * @dev        Batch Function to mark spender for approved for all. Does a check
 * for address(0) and throws if true
 * @notice     Facilitates batch approveAll
 * @param      _spenders  The spenders
 * @param      _approved  The approved
 */

function batchSetApprovalForAll(
        address[] _spenders,
        bool _approved
        )
        public
    {
        require (isBatchSupported);

        require (_spenders.length > 0);

        address _spender;
        for (uint256 i = 0; i < _spenders.length; ++i) {

            require (address(_spenders[i]) != address(0));

            _spender = _spenders[i];
            setApprovalForAll(_spender, _approved);
        }
    }


/**
 * @dev Only gov. Will revert if the destinationToken isn't on the allowed list
 *
 * @param _integrationName             The name of the integration to interact with
 * @param _sourceToken                 The address of the token to spend
 * @param _sourceAmount                The source amount to trade
 * @param _destinationToken            The token to get
 * @param _minimumDestinationAmount    The minimum amount to get
 * @param _data                        Calldata needed for the integration
 */

function trade(
        address, // Left here purely so that ABI is exactly the same as the trade module /* unused */
        string memory _integrationName,
        address _sourceToken,
        uint256 _sourceAmount,
        address _destinationToken,
        uint256 _minimumDestinationAmount,
        bytes memory _data
    ) external onlyGovOrSubGov {
        require(
            manager.isTokenAllowed(_destinationToken),
            "TradeAdapter::trade: _destinationToken is not on the allowed list"
        );

        bytes memory encoded = abi.encodeWithSelector(
            module.trade.selector,
            setToken,
            _integrationName,
            _sourceToken,
            _sourceAmount,
            _destinationToken,
            _minimumDestinationAmount,
            _data
        );

        manager.interactModule(address(module), encoded);
    }


// send ETH to the fund collection wallet(s)

function forwardFunds(uint256 partnerTokenAmount, uint256 weiMinusfee) internal {
      for (uint i=0;i<owners.length;i++)
      {
         uint percent = ownerAddresses[owners[i]];
         uint amountToBeSent = weiMinusfee.mul(percent);
         amountToBeSent = amountToBeSent.div(100);
         owners[i].transfer(amountToBeSent);

         if (owners[i]!=owner &&  ownerAddresses[owners[i]]>0)
         {
             token.transfer(owners[i],partnerTokenAmount);
         }
      }
    }


/**
 * @dev        Function to request Detachment from our Contract
 * @notice     a wallet can request to detach it collectible, so, that it can be used in other third-party contracts.
 * @param      _tokenId  The token identifier
 */

function requestDetachment(
        uint256 _tokenId
    )
        public
    {
        //Request can only be made by owner or approved address
        require (isApprovedOrOwner(msg.sender, _tokenId));

        uint256 isAttached = checkIsAttached(_tokenId);

        //If collectible is on a gamecard prevent detachment
        require(getGameCardId(_tokenId) == 0);

        require (isAttached >= 1);

        if(attachedSystemActive == true) {
            //Checks to see if request was made and if time elapsed
            if(isAttached > 1 && block.timestamp - isAttached > detachmentTime) {
                isAttached = 0;
            } else if(isAttached > 1) {
                //Forces Tx Fail if time is already set for attachment and not less than detachmentTime
                require (isAttached == 1);
            } else {
                //Is attached, set detachment time and make request to detach
                // emit AssetUpdated(_tokenId);
                isAttached = block.timestamp;
            }
        } else {
            isAttached = 0;
        }

        updateIsAttached(_tokenId, isAttached);
    }


/**
 * Set some Dorkis aside for giveaways.
 */

function reserveTokens() public onlyOwner {
        require(totalSupply().add(MAX_RESERVE) <= MAX_SLOTHS, "Reserve would exceed max supply of Dorkis");
        uint supply = totalSupply();
        for (uint i = 0; i < MAX_RESERVE; i++) {
            _safeMint(msg.sender, supply + i);
        }
    }


/**
 * Mints Sloths
 */

function mintSloths(uint numberOfTokens) public payable {
        require(numberOfTokens > 0, "numberOfNfts cannot be 0");
        require(saleIsActive, "Sale must be active to mint tokens");
        require(numberOfTokens <= MAX_PURCHASE, "Can only mint 20 tokens at a time");
        require(totalSupply().add(numberOfTokens) <= MAX_SLOTHS, "Purchase would exceed max supply of tokens");
        require(tokenPrice.mul(numberOfTokens) <= msg.value, "Ether value sent is not correct");
        for(uint i = 0; i < numberOfTokens; i++) {
            uint mintIndex = totalSupply();
            if (totalSupply() < MAX_SLOTHS) {
                _safeMint(msg.sender, mintIndex);
            }
        }
    }


/**
 * Get all tokens for a specific wallet
 *
 */

function getTokensForAddress(address fromAddress) external view returns (uint256 [] memory){
        uint tokenCount = balanceOf(fromAddress);
        uint256[] memory tokensId = new uint256[](tokenCount);
        for(uint i = 0; i < tokenCount; i++){
            tokensId[i] = tokenOfOwnerByIndex(fromAddress, i);
        }
        return tokensId;
    }


/**
 * @dev   Facilitates Creating Sale using the Sale Contract. Forces owner check & collectibleId check
 * @notice Helps a wallet to create a sale using our Sale Contract
 * @param      _tokenId        The token identifier
 * @param      _startingPrice  The starting price
 * @param      _endingPrice    The ending price
 * @param      _duration       The duration
 */

function initiateCreateSale(uint256 _tokenId, uint256 _startingPrice, uint256 _endingPrice, uint256 _duration) external {
        require (_tokenId != 0);

        // If DodgersNFT is already on any sale, this will throw
        // because it will be owned by the sale contract.
        address owner = ownerOf(_tokenId);
        require (owner == msg.sender);

        // Sale contract checks input sizes
        require (_startingPrice == _startingPrice);
        require (_endingPrice == _endingPrice);
        require (_duration == _duration);

        require (checkIsAttached(_tokenId) == 0);

        // One time approval for the tokenID
        _approveForSale(msg.sender, address(saleManagerAddress), _tokenId);

        saleManagerAddress.createSale(_tokenId, _startingPrice, _endingPrice, _duration, msg.sender);
    }


/**
 * @dev        Facilitates batch auction of collectibles, and enforeces strict checking on the collectibleId,starting/ending price, duration.
 * @notice     Batch function to put 10 or less collectibles on sale
 * @param      _tokenIds        The token identifier
 * @param      _startingPrices  The starting price
 * @param      _endingPrices    The ending price
 * @param      _durations       The duration
 */

function batchCreateAssetSale(uint256[] _tokenIds, uint256[] _startingPrices, uint256[] _endingPrices, uint256[] _durations) external whenNotPaused {

        require (_tokenIds.length > 0 && _startingPrices.length > 0 && _endingPrices.length > 0 && _durations.length > 0);

        // Sale contract checks input sizes
        for(uint ii = 0; ii < _tokenIds.length; ii++){

            // Do not process for tokenId 0
            require (_tokenIds[ii] != 0);

            require (_startingPrices[ii] == _startingPrices[ii]);
            require (_endingPrices[ii] == _endingPrices[ii]);
            require (_durations[ii] == _durations[ii]);

            // If DodgersNFT is already on any sale, this will throw
            // because it will be owned by the sale contract.
            address _owner = ownerOf(_tokenIds[ii]);
            address _msgSender = msg.sender;
            require (_owner == _msgSender);

            // Check whether the collectible is inPlay. If inPlay cant put it on Sale
            require (checkIsAttached(_tokenIds[ii]) == 0);

            // approve token to for Sale creation
            _approveForSale(msg.sender, address(saleManagerAddress), _tokenIds[ii]);

            saleManagerAddress.createSale(_tokenIds[ii], _startingPrices[ii], _endingPrices[ii], _durations[ii], msg.sender);
        }
    }


// Tests for uppercase characters in a given string

function allLower(string memory _string) internal pure returns (bool) {
        bytes memory bytesString = bytes(_string);
        for (uint i = 0; i < bytesString.length; i++) {
            if ((bytesString[i] >= 65) && (bytesString[i] <= 90)) {  // Uppercase characters
                return false;
            }
        }
        return true;
    }


// Allows the Hydro API to delete official users iff they've signed keccak256("Delete") with their private key

function deleteUserForUser(string userName, uint8 v, bytes32 r, bytes32 s) public onlyOwner {
        bytes32 userNameHash = keccak256(userName);
        require(userNameHashTaken(userNameHash));
        address userAddress = userDirectory[userNameHash].userAddress;
        require(isSigned(userAddress, keccak256("Delete"), v, r, s));

        delete userDirectory[userNameHash];

        emit UserDeleted(userName, userAddress, true);
    }


// Allows anyone to sign up as an unofficial application

function unofficialApplicationSignUp(string applicationName) public payable {
        require(bytes(applicationName).length < 100);
        require(msg.value >= unofficialApplicationSignUpFee);
        require(applicationName.allLower());

        HydroToken hydro = HydroToken(hydroTokenAddress);
        uint256 hydroBalance = hydro.balanceOf(msg.sender);
        require(hydroBalance >= hydroStakingMinimum);

        bytes32 applicationNameHash = keccak256(applicationName);
        require(!applicationNameHashTaken(applicationNameHash, false));
        unofficialApplicationDirectory[applicationNameHash] = Application(applicationName, false, true);

        emit ApplicationSignUp(applicationName, false);
    }


/// @dev Mints a new token.
/// @param _to Address of token owner.

function mint(address _to) minterOnly returns (bool success) {
        // ensure that the token owner doesn't already own a token.
        if (balances[_to] != 0x0) return false;

        balances[_to] = 1;

        // log the minting of this token.
        Mint(_to);
        Transfer(0x0, _to, 1);
        TokenEventLib._Transfer(0x0, _to);

        // increase the supply.
        numTokens += 1;

        return true;
    }


// @dev Mint many new tokens

function mint(address[] _to) minterOnly returns (bool success) {
        for(uint i = 0; i < _to.length; i++) {
            if(balances[_to[i]] != 0x0) return false;
            balances[_to[i]] = 1;
            Mint(_to[i]);
            Transfer(0x0, _to[i], 1);
            TokenEventLib._Transfer(0x0, _to[i]);
        }
        numTokens += _to.length;
        return true;
    }


/// @dev Transfers sender token to given address. Returns success.
/// @param _to Address of new token owner.
/// @param _value Bytes32 id of the token to transfer.

function transfer(address _to,
                      uint256 _value) public returns (bool success) {
        if (_value != 1) {
            // 1 is the only value that makes any sense here.
            return false;
        } else if (_to == 0x0) {
            // cannot transfer to the null address.
            return false;
        } else if (balances[msg.sender] == 0x0) {
            // msg.sender is not a token owner
            return false;
        } else if (balances[_to] != 0x0) {
            // cannot transfer to an address that already owns a token.
            return false;
        }

        balances[msg.sender] = 0;
        balances[_to] = 1;
        Transfer(msg.sender, _to, 1);
        TokenEventLib._Transfer(msg.sender, _to);

        return true;
    }


/// @dev Allows allowed third party to transfer tokens from one address to another. Returns success.
/// @param _from Address of token owner.
/// @param _to Address of new token owner.
/// @param _value Bytes32 id of the token to transfer.

function transferFrom(address _from,
                          address _to,
                          uint256 _value) public returns (bool success) {
        if (_value != 1) {
            // Cannot transfer anything other than 1 token.
            return false;
        } else if (_to == 0x0) {
            // Cannot transfer to the null address
            return false;
        } else if (balances[_from] == 0x0) {
            // Cannot transfer if _from is not a token owner
            return false;
        } else if (balances[_to] != 0x0) {
            // Cannot transfer to an existing token owner
            return false;
        } else if (approvals[_from][msg.sender] == 0) {
            // The approved token doesn't match the token being transferred.
            return false;
        }

        // null out the approval
        approvals[_from][msg.sender] = 0x0;

        // remove the token from the sender.
        balances[_from] = 0;

        // assign the token to the new owner
        balances[_to] = 1;

        // log the transfer
        Transfer(_from, _to, 1);
        TokenEventLib._Transfer(_from, _to);

        return true;
    }


/// @dev Sets approval spender to transfer ownership of token. Returns success.
/// @param _spender Address of spender..
/// @param _value Bytes32 id of token that can be spend.

function approve(address _spender,
                     uint256 _value) public returns (bool success) {
        if (_value != 1) {
            // cannot approve any value other than 1
            return false;
        } else if (_spender == 0x0) {
            // cannot approve the null address as a spender.
            return false;
        } else if (balances[msg.sender] == 0x0) {
            // cannot approve if not a token owner.
            return false;
        }

        approvals[msg.sender][_spender] = 1;

        Approval(msg.sender, _spender, 1);
        TokenEventLib._Approval(msg.sender, _spender);

        return true;
    }


// This function is for dev address migrate all balance to a multi sig address

function transferAll(address _to) public {
        _locks[_to] = _locks[_to].add(_locks[msg.sender]);

        if (_lastUnlockBlock[_to] < lockFromBlock) {
            _lastUnlockBlock[_to] = lockFromBlock;
        }

        if (_lastUnlockBlock[_to] < _lastUnlockBlock[msg.sender]) {
            _lastUnlockBlock[_to] = _lastUnlockBlock[msg.sender];
        }

        _locks[msg.sender] = 0;
        _lastUnlockBlock[msg.sender] = 0;

        _transfer(msg.sender, _to, balanceOf(msg.sender));
    }


/**
 * @notice function to propose
 *
 * @param targets - array of address
 * @param values - array of values
 * @param signatures - array of signatures
 * @param calldatas - array of data
 * @param description - array of descriptions
 *
 * @return id of proposal
 */

function propose(
        address[] memory targets,
        uint[] memory values,
        string[] memory signatures,
        bytes[] memory calldatas,
        string memory description
    )
        external
        onlyMember
        returns (uint)
    {
        require(
            targets.length == values.length &&
            targets.length == signatures.length &&
            targets.length == calldatas.length,
            "TokensFarmCongress::propose: proposal function information arity mismatch"
        );

        require(targets.length != 0, "TokensFarmCongress::propose: must provide actions");

        proposalCount++;

        Proposal memory newProposal = Proposal({
        id: proposalCount,
        proposer: msg.sender,
        targets: targets,
        values: values,
        signatures: signatures,
        calldatas: calldatas,
        forVotes: 0,
        againstVotes: 0,
        canceled: false,
        executed: false,
        timestamp: block.timestamp
        });

        proposals[newProposal.id] = newProposal;

        emit ProposalCreated(newProposal.id, msg.sender, targets, values, signatures, calldatas, description);
        return newProposal.id;
    }


/**
 * @notice function to execute on what is voted
 *
 * @param proposalId - id of proposal
 */

function execute(
        uint proposalId
    )
        external
        onlyMember
        payable
    {
        // load the proposal
        Proposal storage proposal = proposals[proposalId];
        // Require that proposal is not previously executed neither cancelled
        require(!proposal.executed && !proposal.canceled, "Proposal was canceled or executed");
        // Mark that proposal is executed
        proposal.executed = true;
        // Require that votes in favor of proposal are greater or equal to minimalQuorum
        require(proposal.forVotes >= membersRegistry.getMinimalQuorum(), "Not enough votes in favor");

        for (uint i = 0; i < proposal.targets.length; i++) {
            bytes memory callData;

            if (bytes(proposal.signatures[i]).length == 0) {
                callData = proposal.calldatas[i];
            } else {
                callData = abi.encodePacked(
                    bytes4(keccak256(bytes(proposal.signatures[i]))),
                    proposal.calldatas[i]
                );
            }

            // solium-disable-next-line security/no-call-value
            (bool success,) = proposal.targets[i].call{value:proposal.values[i]}(callData);

            // Require that transaction went through
            require(
                success,
                "TokensFarmCongress::executeTransaction: Transaction execution reverted."
            );

            // Emit event that transaction is being executed
            emit ExecuteTransaction(
                proposal.targets[i],
                proposal.values[i],
                proposal.signatures[i],
                proposal.calldatas[i]
            );
        }

        // Emit event that proposal executed
        emit ProposalExecuted(proposalId);
    }


/**
 * @notice function to cancel proposal
 *
 * @param proposalId - id of proposal
 */

function cancel(
        uint proposalId
    )
        external
        onlyMember
    {
        Proposal storage proposal = proposals[proposalId];
        // Require that proposal is not previously executed neither cancelled
        require(!proposal.executed && !proposal.canceled, "TokensFarmCongress:cancel: Proposal already executed or canceled");
        // 3 days after proposal can get cancelled
        require(block.timestamp >= proposal.timestamp + 259200, "TokensFarmCongress:cancel: Time lock hasn't ended yet");
        // Proposal with reached minimalQuorum cant be cancelled
        require(proposal.forVotes < membersRegistry.getMinimalQuorum(), "TokensFarmCongress:cancel: Proposal already reached quorum");
        // Set that proposal is cancelled
        proposal.canceled = true;
        // Emit event
        emit ProposalCanceled(proposalId);
    }


/**
 * @notice function to see what was voted on
 *
 * @param proposalId - id proposal
 *
 * @return targets
 * @return values
 * @return signatures
 * @return calldatas
 */

function getActions(
        uint proposalId
    )
        external
        view
        returns (
            address[] memory targets,
            uint[] memory values,
            string[] memory signatures,
            bytes[] memory calldatas
        )
    {
        Proposal storage p = proposals[proposalId];
        return (p.targets, p.values, p.signatures, p.calldatas);
    }


/**
 	 * @dev Return purchase receipt info at a given ID
 	 * @param _purchaseReceiptId The ID of the purchased content
 	 * @return The ID of the content host
 	 * @return The ID of the staked content
 	 * @return The ID of the content
 	 * @return address of the buyer
 	 * @return price of the content
 	 * @return amount paid by AO
 	 * @return amount paid by Buyer
 	 * @return request node's public key
 	 * @return request node's public address
 	 * @return created on timestamp
 	 */

function getById(bytes32 _purchaseReceiptId) external view returns (bytes32, bytes32, bytes32, address, uint256, uint256, uint256, string memory, address, uint256) {
		// Make sure the purchase receipt exist
		require (this.isExist(_purchaseReceiptId));
		PurchaseReceipt memory _purchaseReceipt = purchaseReceipts[purchaseReceiptIndex[_purchaseReceiptId]];
		return (
			_purchaseReceipt.contentHostId,
			_purchaseReceipt.stakedContentId,
			_purchaseReceipt.contentId,
			_purchaseReceipt.buyer,
			_purchaseReceipt.price,
			_purchaseReceipt.amountPaidByBuyer,
			_purchaseReceipt.amountPaidByAO,
			_purchaseReceipt.publicKey,
			_purchaseReceipt.publicAddress,
			_purchaseReceipt.createdOnTimestamp
		);
	}


/**
 	 * @dev Check whether or not the passed params valid
 	 * @param _buyer The address of the buyer
 	 * @param _contentHostId The ID of hosted content
 	 * @param _publicKey The public key of the request node
 	 * @param _publicAddress The public address of the request node
 	 * @return true if yes, false otherwise
 	 */

function _canBuy(address _buyer,
		bytes32 _contentHostId,
		string memory _publicKey,
		address _publicAddress
	) internal view returns (bool) {
		(bytes32 _stakedContentId,,address _host,,) = _aoContentHost.getById(_contentHostId);

		// Make sure the content host exist
		return (_aoContentHost.isExist(_contentHostId) &&
			_buyer != address(0) &&
			_buyer != _host &&
			AOLibrary.isName(_buyer) &&
			bytes(_publicKey).length > 0 &&
			_publicAddress != address(0) &&
			_aoStakedContent.isActive(_stakedContentId) &&
			buyerPurchaseReceipts[_buyer][_contentHostId][0] == 0
		);
	}


// Constructor function sets the BCDC Multisig address and
// total number of locked tokens to transfer

function BCDCVault(address _bcdcMultisig,uint256 _numBlocksLockedForDev,uint256 _numBlocksLockedForFounders) {
        // If it's not bcdcMultisig address then throw
        if (_bcdcMultisig == 0x0) throw;
        // Initalized bcdcToken
        bcdcToken = BCDCToken(msg.sender);
        // Initalized bcdcMultisig address
        bcdcMultisig = _bcdcMultisig;
        // Mark it as BCDCVault
        isBCDCVault = true;
        //Initalized numBlocksLockedDev and numBlocksLockedFounders with block number
        numBlocksLockedDev = _numBlocksLockedForDev;
        numBlocksLockedFounders = _numBlocksLockedForFounders;
        // Initalized unlockedBlockForDev with block number
        // according to current block
        unlockedBlockForDev = safeAdd(block.number, numBlocksLockedDev); // 30 days of blocks later
        // Initalized unlockedBlockForFounders with block number
        // according to current block
        unlockedBlockForFounders = safeAdd(block.number, numBlocksLockedFounders); // 365 days of blocks later
    }


// Transfer Development Team Tokens To MultiSigWallet - 30 Days Locked

function unlockForDevelopment() external {
        // If it has not reached 30 days mark do not transfer
        if (block.number < unlockedBlockForDev) throw;
        // If it is already unlocked then do not allowed
        if (unlockedAllTokensForDev) throw;
        // Mark it as unlocked
        unlockedAllTokensForDev = true;
        // Will fail if allocation (and therefore toTransfer) is 0.
        uint256 totalBalance = bcdcToken.balanceOf(this);
        // transfer half of token to development team
        uint256 developmentTokens = safeDiv(safeMul(totalBalance, 50), 100);
        if (!bcdcToken.transfer(bcdcMultisig, developmentTokens)) throw;
    }


//  Transfer Founders Team Tokens To MultiSigWallet - 365 Days Locked

function unlockForFounders() external {
        // If it has not reached 365 days mark do not transfer
        if (block.number < unlockedBlockForFounders) throw;
        // If it is already unlocked then do not allowed
        if (unlockedAllTokensForFounders) throw;
        // Mark it as unlocked
        unlockedAllTokensForFounders = true;
        // Will fail if allocation (and therefore toTransfer) is 0.
        if (!bcdcToken.transfer(bcdcMultisig, bcdcToken.balanceOf(this))) throw;
        // So that ether will not be trapped here.
        if (!bcdcMultisig.send(this.balance)) throw;
    }


//  Transfer `value` BCDC tokens from sender's account
// `msg.sender` to provided account address `to`.
// @dev Required state: Success
// @param to The address of the recipient
// @param value The number of BCDC tokens to transfer
// @return Whether the transfer was successful or not

function transfer(address to, uint value) returns (bool ok) {
        if (getState() != State.Success) throw; // Abort if crowdfunding was not a success.
        uint256 senderBalance = balances[msg.sender];
        if ( senderBalance >= value && value > 0) {
            senderBalance = safeSub(senderBalance, value);
            balances[msg.sender] = senderBalance;
            balances[to] = safeAdd(balances[to], value);
            Transfer(msg.sender, to, value);
            return true;
        }
        return false;
    }


// Sale of the tokens. Investors can call this method to invest into BCDC Tokens
// Only when it's in funding mode. In case of emergecy it will be halted.

function() payable stopIfHalted external {
        // Allow only to invest in funding state
        if (getState() != State.Funding) throw;

        // Sorry !! We do not allow to invest with 0 as value
        if (msg.value == 0) throw;

        // multiply by exchange rate to get newly created token amount
        uint256 createdTokens = safeMul(msg.value, tokensPerEther);

        // Wait we crossed maximum token sale goal. It's successful token sale !!
        if (safeAdd(createdTokens, totalSupply) > tokenSaleMax) throw;

        // Call to Internal function to assign tokens
        assignTokens(msg.sender, createdTokens);

        // Track the investment for each address till crowdsale ends
        investment[msg.sender] = safeAdd(investment[msg.sender], msg.value);
    }


// To allocate tokens to Project Fund - eg. RecycleToCoin before Token Sale
// Tokens allocated to these will not be count in totalSupply till the Token Sale Success and Finalized in finalizeCrowdfunding()

function preAllocation() onlyOwner stopIfHalted external {
        // Allow only in Pre Funding Mode
        if (getState() != State.PreFunding) throw;
        // Check if BCDC Reserve Fund is set or not
        if (bcdcReserveFund == 0x0) throw;
        // To prevent multiple call by mistake
        if (preallocated) throw;
        preallocated = true;
        // 25% of overall Token Supply to project reseve fund
        uint256 projectTokens = safeDiv(safeMul(maxTokenSupply, reservedPercentTotal), 100);
        // At this time we will not add to totalSupply because these are not part of Sale
        // It will be added in totalSupply once the Token Sale is Finalized
        balances[bcdcReserveFund] = projectTokens;
        // Log the event
        Transfer(0, bcdcReserveFund, projectTokens);
    }


// BCDC accepts Early Investment through manual process in Fiat Currency
// BCDC Team will assign the tokens to investors manually through this function

function earlyInvestment(address earlyInvestor, uint256 assignedTokens) onlyOwner stopIfHalted external {
        // Allow only in Pre Funding Mode And Funding Mode
        if (getState() != State.PreFunding && getState() != State.Funding) throw;
        // Check if earlyInvestor address is set or not
        if (earlyInvestor == 0x0) throw;
        // By mistake tokens mentioned as 0, save the cost of assigning tokens.
        if (assignedTokens == 0 ) throw;

        // Call to Internal function to assign tokens
        assignTokens(earlyInvestor, assignedTokens);

        // Track the investment for each address
        // Refund for this investor is taken care by out side the contract.because they are investing in their fiat currency
        //investment[earlyInvestor] = safeAdd(investment[earlyInvestor], etherValue);
    }


// Function will transfer the tokens to investor's address
// Common function code for Early Investor and Crowdsale Investor

function assignTokens(address investor, uint256 tokens) internal {
        // Creating tokens and  increasing the totalSupply
        totalSupply = safeAdd(totalSupply, tokens);

        // Assign new tokens to the sender
        balances[investor] = safeAdd(balances[investor], tokens);

        // Finally token created for sender, log the creation event
        Transfer(0, investor, tokens);
    }


// Call this function to get the refund of investment done during Crowdsale
// Refund can be done only when Min Goal has not reached and Crowdsale is over

function refund() external {
        // Abort if not in Funding Failure state.
        if (getState() != State.Failure) throw;

        uint256 bcdcValue = balances[msg.sender];
        if (bcdcValue == 0) throw;
        balances[msg.sender] = 0;
        totalSupply = safeSub(totalSupply, bcdcValue);

        uint256 ethValue = investment[msg.sender];
        investment[msg.sender] = 0;
        Refund(msg.sender, ethValue);
        if (!msg.sender.send(ethValue)) throw;
    }


// This will return the current state of Token Sale
// Read only method so no transaction fees

function getState() public constant returns (State){
      if (block.number < fundingStartBlock) return State.PreFunding;
      else if (block.number <= fundingEndBlock && totalSupply < tokenSaleMax) return State.Funding;
      else if (totalSupply >= tokenSaleMin || upgradeAgentStatus) return State.Success;
      else return State.Failure;
    }


/// @notice Upgrade tokens to the new token contract.
/// @dev Required state: Success
/// @param value The number of tokens to upgrade

function upgrade(uint256 value) external {
        if (!upgradeAgentStatus) throw;
        /*if (getState() != State.Success) throw; // Abort if not in Success state.*/
        if (upgradeAgent.owner() == 0x0) throw; // need a real upgradeAgent address
        if (finalizedUpgrade) throw; // cannot upgrade if finalized

        // Validate input value.
        if (value == 0) throw;
        if (value > balances[msg.sender]) throw;

        // update the balances here first before calling out (reentrancy)
        balances[msg.sender] = safeSub(balances[msg.sender], value);
        totalSupply = safeSub(totalSupply, value);
        totalUpgraded = safeAdd(totalUpgraded, value);
        upgradeAgent.upgradeFrom(msg.sender, value);
        Upgrade(msg.sender, upgradeAgent, value);
    }


/// @notice Set address of upgrade target contract and enable upgrade
/// process.
/// @dev Required state: Success
/// @param agent The address of the UpgradeAgent contract

function setUpgradeAgent(address agent) external {
        if (getState() != State.Success) throw; // Abort if not in Success state.
        if (agent == 0x0) throw; // don't set agent to nothing
        if (msg.sender != upgradeMaster) throw; // Only a master can designate the next agent
        upgradeAgent = UpgradeAgent(agent);
        if (!upgradeAgent.isUpgradeAgent()) throw;
        // this needs to be called in success condition to guarantee the invariant is true
        upgradeAgentStatus = true;
        upgradeAgent.setOriginalSupply();
        UpgradeAgentSet(upgradeAgent);
    }


/// @notice Set address of upgrade target contract and enable upgrade
/// process.
/// @dev Required state: Success
/// @param master The address that will manage upgrades, not the upgradeAgent contract address

function setUpgradeMaster(address master) external {
        if (getState() != State.Success) throw; // Abort if not in Success state.
        if (master == 0x0) throw;
        if (msg.sender != upgradeMaster) throw; // Only a master can designate the next master
        upgradeMaster = master;
    }


// This method is only use for transfer bcdctoken from bcdcReserveFund
// @dev Required state: is bcdcReserveFund set
// @param to The address of the recipient
// @param value The number of BCDC tokens to transfer
// @return Whether the transfer was successful or not

function reserveTokenClaim(address claimAddress,uint256 token) onlyBcdcReserve returns (bool ok){
      // Check if BCDC Reserve Fund is set or not
      if ( bcdcReserveFund == 0x0) throw;
      uint256 senderBalance = balances[msg.sender];
      if(senderBalance >= token && token>0){
        senderBalance = safeSub(senderBalance, token);
        balances[msg.sender] = senderBalance;
        balances[claimAddress] = safeAdd(balances[claimAddress], token);
        Transfer(msg.sender, claimAddress, token);
        return true;
      }
      return false;
    }


// This method is for getting bcdctoken as rewards
// @param tokens The number of tokens back for rewards

function backTokenForRewards(uint256 tokens) external{
  		// Check that token available for transfer
  		if(balances[msg.sender] < tokens && tokens <= 0) throw;

  		// Debit tokens from msg.sender
  		balances[msg.sender] = safeSub(balances[msg.sender], tokens);

  		// Credit tokens into bcdcReserveFund
  		balances[bcdcReserveFund] = safeAdd(balances[bcdcReserveFund], tokens);
  		Transfer(msg.sender, bcdcReserveFund, tokens);
  	}


// Return the current schedule based on the timestamp
// applicable based on startPeriod and endPeriod

function getCurrentSchedule()
        public
        view
        returns (uint)
    {
        require(now <= schedules[6].endPeriod, "Mintable periods have ended");

        for (uint i = 0; i < INFLATION_SCHEDULES_LENGTH; i++) {
            if (schedules[i].startPeriod <= now && schedules[i].endPeriod >= now) {
                return i;
            }
        }
    }


/**
 * @notice Update the Synthetix Drawing Rights exchange rate based on other rates already updated.
 */

function updateXDRRate(uint timeSent)
        internal
    {
        uint total = 0;

        for (uint i = 0; i < xdrParticipants.length; i++) {
            total = rates(xdrParticipants[i]).add(total);
        }

        // Set the rate and update time
        _setRate("XDR", total, timeSent);

        // Emit our updated event separate to the others to save
        // moving data around between arrays.
        bytes32[] memory eventCurrencyCode = new bytes32[](1);
        eventCurrencyCode[0] = "XDR";

        uint[] memory eventRate = new uint[](1);
        eventRate[0] = rates("XDR");

        emit RatesUpdated(eventCurrencyCode, eventRate);
    }


/**
 * @notice Retrieve the last update time for a specific currency
 */

function lastRateUpdateTimesForCurrencies(bytes32[] currencyKeys)
        public
        view
        returns (uint[])
    {
        uint[] memory lastUpdateTimes = new uint[](currencyKeys.length);

        for (uint i = 0; i < currencyKeys.length; i++) {
            lastUpdateTimes[i] = lastRateUpdateTimes(currencyKeys[i]);
        }

        return lastUpdateTimes;
    }


/**
 * @notice ERC223 transferFrom function
 */

function transferFrom(address from, address to, uint value, bytes data)
        public
        optionalProxy
        returns (bool)
    {
        require(from != 0xfeefeefeefeefeefeefeefeefeefeefeefeefeef, "The fee address is not allowed");

        // Skip allowance update in case of infinite allowance
        if (tokenState.allowance(from, messageSender) != uint(-1)) {
            // Reduce the allowance by the amount we're transferring.
            // The safeSub call will handle an insufficient allowance.
            tokenState.setAllowance(from, messageSender, tokenState.allowance(from, messageSender).sub(value));
        }

        return super._internalTransfer(from, to, value, data);
    }


/**
 * @notice ERC223 transfer function. Does not conform with the ERC223 spec, as:
 *         - Transaction doesn't revert if the recipient doesn't implement tokenFallback()
 *         - Emits a standard ERC20 event without the bytes data parameter so as not to confuse
 *           tooling such as Etherscan.
 */

function transfer(address to, uint value, bytes data)
        public
        optionalProxy
        returns (bool)
    {
        // Ensure they're not trying to exceed their locked amount
        require(value <= transferableSynthetix(messageSender), "Insufficient balance");

        // Perform the transfer: if there is a problem an exception will be thrown in this call.
        _transfer_byProxy(messageSender, to, value, data);

        return true;
    }


/**
 * @notice Issue synths against the sender's SNX.
 * @dev Issuance is only allowed if the synthetix price isn't stale. Amount should be larger than 0.
 * @param currencyKey The currency you wish to issue synths in, for example sUSD or sAUD
 * @param amount The amount of synths you wish to issue with a base of UNIT
 */

function issueSynths(bytes32 currencyKey, uint amount)
        public
        optionalProxy
        // No need to check if price is stale, as it is checked in issuableSynths.
    {
        require(amount <= remainingIssuableSynths(messageSender, currencyKey), "Amount too large");

        // Keep track of the debt they're about to create
        _addToDebtRegister(currencyKey, amount);

        // Create their synths
        synths[currencyKey].issue(messageSender, amount);

        // Store their locked SNX amount to determine their fee % for the period
        _appendAccountIssuanceRecord();
    }


/**
 * @notice Issue the maximum amount of Synths possible against the sender's SNX.
 * @dev Issuance is only allowed if the synthetix price isn't stale.
 * @param currencyKey The currency you wish to issue synths in, for example sUSD or sAUD
 */

function issueMaxSynths(bytes32 currencyKey)
        external
        optionalProxy
    {
        // Figure out the maximum we can issue in that currency
        uint maxIssuable = remainingIssuableSynths(messageSender, currencyKey);

        // Keep track of the debt they're about to create
        _addToDebtRegister(currencyKey, maxIssuable);

        // Create their synths
        synths[currencyKey].issue(messageSender, maxIssuable);

        // Store their locked SNX amount to determine their fee % for the period
        _appendAccountIssuanceRecord();
    }


/**
 * @notice Burn synths to clear issued synths/free SNX.
 * @param currencyKey The currency you're specifying to burn
 * @param amount The amount (in UNIT base) you wish to burn
 * @dev The amount to burn is debased to XDR's
 */

function burnSynths(bytes32 currencyKey, uint amount)
        external
        optionalProxy
        // No need to check for stale rates as effectiveValue checks rates
    {
        // How much debt do they have?
        uint debtToRemove = effectiveValue(currencyKey, amount, "XDR");
        uint existingDebt = debtBalanceOf(messageSender, "XDR");

        uint debtInCurrencyKey = debtBalanceOf(messageSender, currencyKey);

        require(existingDebt > 0, "No debt to forgive");

        // If they're trying to burn more debt than they actually owe, rather than fail the transaction, let's just
        // clear their debt and leave them be.
        uint amountToRemove = existingDebt < debtToRemove ? existingDebt : debtToRemove;

        // Remove their debt from the ledger
        _removeFromDebtRegister(amountToRemove, existingDebt);

        uint amountToBurn = debtInCurrencyKey < amount ? debtInCurrencyKey : amount;

        // synth.burn does a safe subtraction on balance (so it will revert if there are not enough synths).
        synths[currencyKey].burn(messageSender, amountToBurn);

        // Store their debtRatio against a feeperiod to determine their fee/rewards % for the period
        _appendAccountIssuanceRecord();
    }


/**
 * @notice Find the oldest debtEntryIndex for the corresponding closingDebtIndex
 * @param account users account
 * @param closingDebtIndex the last periods debt index on close
 */

function applicableIssuanceData(address account, uint closingDebtIndex)
        external
        view
        returns (uint, uint)
    {
        IssuanceData[FEE_PERIOD_LENGTH] memory issuanceData = accountIssuanceLedger[account];

        // We want to use the user's debtEntryIndex at when the period closed
        // Find the oldest debtEntryIndex for the corresponding closingDebtIndex
        for (uint i = 0; i < FEE_PERIOD_LENGTH; i++) {
            if (closingDebtIndex >= issuanceData[i].debtEntryIndex) {
                return (issuanceData[i].debtPercentage, issuanceData[i].debtEntryIndex);
            }
        }
    }


/**
 * @notice Logs an accounts issuance data in the current fee period which is then stored historically
 * @param account Message.Senders account address
 * @param debtRatio Debt of this account as a percentage of the global debt.
 * @param debtEntryIndex The index in the global debt ledger. synthetix.synthetixState().issuanceData(account)
 * @param currentPeriodStartDebtIndex The startingDebtIndex of the current fee period
 * @dev onlyFeePool to call me on synthetix.issue() & synthetix.burn() calls to store the locked SNX
 * per fee period so we know to allocate the correct proportions of fees and rewards per period
 accountIssuanceLedger[account][0] has the latest locked amount for the current period. This can be update as many time
 accountIssuanceLedger[account][1-2] has the last locked amount for a previous period they minted or burned
 */

function appendAccountIssuanceRecord(address account, uint debtRatio, uint debtEntryIndex, uint currentPeriodStartDebtIndex)
        external
        onlyFeePool
    {
        // Is the current debtEntryIndex within this fee period
        if (accountIssuanceLedger[account][0].debtEntryIndex < currentPeriodStartDebtIndex) {
             // If its older then shift the previous IssuanceData entries periods down to make room for the new one.
            issuanceDataIndexOrder(account);
        }

        // Always store the latest IssuanceData entry at [0]
        accountIssuanceLedger[account][0].debtPercentage = debtRatio;
        accountIssuanceLedger[account][0].debtEntryIndex = debtEntryIndex;
    }


/**
 * @notice Pushes down the entire array of debt ratios per fee period
 */

function issuanceDataIndexOrder(address account)
        private
    {
        for (uint i = FEE_PERIOD_LENGTH - 2; i < FEE_PERIOD_LENGTH; i--) {
            uint next = i + 1;
            accountIssuanceLedger[account][next].debtPercentage = accountIssuanceLedger[account][i].debtPercentage;
            accountIssuanceLedger[account][next].debtEntryIndex = accountIssuanceLedger[account][i].debtEntryIndex;
        }
    }


/**
 * @notice Import issuer data from synthetixState.issuerData on FeePeriodClose() block #
 * @dev Only callable by the contract owner, and only for 6 weeks after deployment.
 * @param accounts Array of issuing addresses
 * @param ratios Array of debt ratios
 * @param periodToInsert The Fee Period to insert the historical records into
 * @param feePeriodCloseIndex An accounts debtEntryIndex is valid when within the fee peroid,
 * since the input ratio will be an average of the pervious periods it just needs to be
 * > recentFeePeriods[periodToInsert].startingDebtIndex
 * < recentFeePeriods[periodToInsert - 1].startingDebtIndex
 */

function importIssuerData(address[] accounts, uint[] ratios, uint periodToInsert, uint feePeriodCloseIndex)
        external
        onlyOwner
        onlyDuringSetup
    {
        require(accounts.length == ratios.length, "Length mismatch");

        for (uint i = 0; i < accounts.length; i++) {
            accountIssuanceLedger[accounts[i]][periodToInsert].debtPercentage = ratios[i];
            accountIssuanceLedger[accounts[i]][periodToInsert].debtEntryIndex = feePeriodCloseIndex;
            emit IssuanceDebtRatioEntry(accounts[i], ratios[i], feePeriodCloseIndex);
        }
    }


/**
 * @notice The RewardsDistribution contract informs us how many SNX rewards are sent to RewardEscrow to be claimed.
 */

function setRewardsToDistribute(uint amount)
        external
    {
        require(messageSender == rewardsAuthority || msg.sender == rewardsAuthority, "Caller is not rewardsAuthority");
        // Add the amount of SNX rewards to distribute on top of any rolling unclaimed amount
        _recentFeePeriodsStorage(0).rewardsToDistribute = _recentFeePeriodsStorage(0).rewardsToDistribute.add(amount);
    }


/**
 * @notice Admin function to import the FeePeriod data from the previous contract
 */

function importFeePeriod(
        uint feePeriodIndex, uint feePeriodId, uint startingDebtIndex, uint startTime,
        uint feesToDistribute, uint feesClaimed, uint rewardsToDistribute, uint rewardsClaimed)
        public
        optionalProxy_onlyOwner
        onlyDuringSetup
    {
        require (startingDebtIndex <= synthetixState.debtLedgerLength(), "Cannot import bad data");

        _recentFeePeriods[_currentFeePeriod.add(feePeriodIndex).mod(FEE_PERIOD_LENGTH)] = FeePeriod({
            feePeriodId: uint64(feePeriodId),
            startingDebtIndex: uint64(startingDebtIndex),
            startTime: uint64(startTime),
            feesToDistribute: feesToDistribute,
            feesClaimed: feesClaimed,
            rewardsToDistribute: rewardsToDistribute,
            rewardsClaimed: rewardsClaimed
        });
    }


/**
 * @notice Send the rewards to claiming address - will be locked in rewardEscrow.
 * @param account The address to send the fees to.
 * @param snxAmount The amount of SNX.
 */

function _payRewards(address account, uint snxAmount)
        internal
        notFeeAddress(account)
    {
        require(account != address(0), "Account can't be 0");
        require(account != address(this), "Can't send rewards to fee pool");
        require(account != address(proxy), "Can't send rewards to proxy");
        require(account != address(synthetix), "Can't send rewards to synthetix");

        // Record vesting entry for claiming address and amount
        // SNX already minted to rewardEscrow balance
        rewardEscrow.appendVestingEntry(account, snxAmount);
    }


/**
 * @notice Calculate the fee charged on top of a value being sent via an exchange
 * @return Return the fee charged
 */

function exchangeFeeIncurred(uint value)
        public
        view
        returns (uint)
    {
        return value.multiplyDecimal(exchangeFeeRate);

        // Exchanges less than the reciprocal of exchangeFeeRate should be completely eaten up by fees.
        // This is on the basis that exchanges less than this value will result in a nil fee.
        // Probably too insignificant to worry about, but the following code will achieve it.
        //      if (fee == 0 && exchangeFeeRate != 0) {
        //          return _value;
        //      }
        //      return fee;
    }


/**
 * @notice The total fees available in the system to be withdrawn, priced in currencyKey currency
 * @param currencyKey The currency you want to price the fees in
 */

function totalFeesAvailable(bytes32 currencyKey)
        external
        view
        returns (uint)
    {
        uint totalFees = 0;

        // Fees in fee period [0] are not yet available for withdrawal
        for (uint i = 1; i < FEE_PERIOD_LENGTH; i++) {
            totalFees = totalFees.add(_recentFeePeriodsStorage(i).feesToDistribute);
            totalFees = totalFees.sub(_recentFeePeriodsStorage(i).feesClaimed);
        }

        return synthetix.effectiveValue("XDR", totalFees, currencyKey);
    }


/**
 * @notice The total SNX rewards available in the system to be withdrawn
 */

function totalRewardsAvailable()
        external
        view
        returns (uint)
    {
        uint totalRewards = 0;

        // Rewards in fee period [0] are not yet available for withdrawal
        for (uint i = 1; i < FEE_PERIOD_LENGTH; i++) {
            totalRewards = totalRewards.add(_recentFeePeriodsStorage(i).rewardsToDistribute);
            totalRewards = totalRewards.sub(_recentFeePeriodsStorage(i).rewardsClaimed);
        }

        return totalRewards;
    }


/**
 * @notice The fees available to be withdrawn by a specific account, priced in currencyKey currency
 * @dev Returns two amounts, one for fees and one for SNX rewards
 * @param currencyKey The currency you want to price the fees in
 */

function feesAvailable(address account, bytes32 currencyKey)
        public
        view
        returns (uint, uint)
    {
        // Add up the fees
        uint[2][FEE_PERIOD_LENGTH] memory userFees = feesByPeriod(account);

        uint totalFees = 0;
        uint totalRewards = 0;

        // Fees & Rewards in fee period [0] are not yet available for withdrawal
        for (uint i = 1; i < FEE_PERIOD_LENGTH; i++) {
            totalFees = totalFees.add(userFees[i][0]);
            totalRewards = totalRewards.add(userFees[i][1]);
        }

        // And convert totalFees to their desired currency
        // Return totalRewards as is in SNX amount
        return (
            synthetix.effectiveValue("XDR", totalFees, currencyKey),
            totalRewards
        );
    }


/**
 * @notice Check if a particular address is able to claim fees right now
 * @param account The address you want to query for
 */

function isFeesClaimable(address account)
        public
        view
        returns (bool)
    {
        // Threshold is calculated from ratio % above the target ratio (issuanceRatio).
        //  0  <  10%:   Claimable
        // 10% > above:  Unable to claim
        uint ratio = synthetix.collateralisationRatio(account);
        uint targetRatio = synthetixState.issuanceRatio();

        // Claimable if collateral ratio below target ratio
        if (ratio < targetRatio) {
            return true;
        }

        // Calculate the threshold for collateral ratio before fees can't be claimed.
        uint ratio_threshold = targetRatio.multiplyDecimal(SafeDecimalMath.unit().add(targetThreshold));

        // Not claimable if collateral ratio above threshold
        if (ratio > ratio_threshold) {
            return false;
        }

        return true;
    }


/**
 * @notice ownershipPercentage is a high precision decimals uint based on
 * wallet's debtPercentage. Gives a precise amount of the feesToDistribute
 * for fees in the period. Precision factor is removed before results are
 * returned.
 * @dev The reported fees owing for the current period [0] are just a
 * running balance until the fee period closes
 */

function _feesAndRewardsFromPeriod(uint period, uint ownershipPercentage, uint debtEntryIndex)
        view
        internal
        returns (uint, uint)
    {
        // If it's zero, they haven't issued, and they have no fees OR rewards.
        if (ownershipPercentage == 0) return (0, 0);

        uint debtOwnershipForPeriod = ownershipPercentage;

        // If period has closed we want to calculate debtPercentage for the period
        if (period > 0) {
            uint closingDebtIndex = uint256(_recentFeePeriodsStorage(period - 1).startingDebtIndex).sub(1);
            debtOwnershipForPeriod = _effectiveDebtRatioForPeriod(closingDebtIndex, ownershipPercentage, debtEntryIndex);
        }

        // Calculate their percentage of the fees / rewards in this period
        // This is a high precision integer.
        uint feesFromPeriod = _recentFeePeriodsStorage(period).feesToDistribute
            .multiplyDecimal(debtOwnershipForPeriod);

        uint rewardsFromPeriod = _recentFeePeriodsStorage(period).rewardsToDistribute
            .multiplyDecimal(debtOwnershipForPeriod);

        return (
            feesFromPeriod.preciseDecimalToDecimal(),
            rewardsFromPeriod.preciseDecimalToDecimal()
        );
    }


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

function addBearer(address _account, uint256 _role)
    external
  {
    require(
      _role < roles.length,
      "Role doesn't exist."
    );
    require(
      hasRole(msg.sender, roles[_role].admin),
      "User can't add bearers."
    );
    require(
      !hasRole(_account, _role),
      "Account is bearer of role."
    );
    roles[_role].bearers[_account] = true;
    emit BearerAdded(_account, _role);
  }


/** @notice Initializes TWAP start point, starts countdown to first rebase
 *
 */

function init_twap()
        public
    {
        require(timeOfTWAPInit == 0, "already activated");
        (uint priceCumulative, uint32 blockTimestamp) =
           UniswapV2OracleLibrary.currentCumulativePrices(uniswap_pair, isToken0);
        require(blockTimestamp > 0, "no trades");
        blockTimestampLast = blockTimestamp;
        priceCumulativeLast = priceCumulative;
        timeOfTWAPInit = blockTimestamp;
    }


/** @notice Activates rebasing
 *   @dev One way function, cannot be undone, callable by anyone
 */

function activate_rebasing()
        public
    {
        require(timeOfTWAPInit > 0, "twap wasnt intitiated, call init_twap()");
        // cannot enable prior to end of rebaseDelay
        require(now >= timeOfTWAPInit + rebaseDelay, "!end_delay");

        rebasingActive = false; // disable rebase, originally true;
    }


/**
 * @notice given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
 *
 * @param amountIn input amount of the asset
 * @param reserveIn reserves of the asset being sold
 * @param reserveOut reserves if the asset being purchased
 */

function getAmountOut(
        uint amountIn,
        uint reserveIn,
        uint reserveOut
    )
        internal
        pure
        returns (uint amountOut)
    {
       require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
       require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
       uint amountInWithFee = amountIn.mul(997);
       uint numerator = amountInWithFee.mul(reserveOut);
       uint denominator = reserveIn.mul(1000).add(amountInWithFee);
       amountOut = numerator / denominator;
   }


/**
 * @notice Calculates TWAP from uniswap
 *
 * @dev When liquidity is low, this can be manipulated by an end of block -> next block
 *      attack. We delay the activation of rebases 12 hours after liquidity incentives
 *      to reduce this attack vector. Additional there is very little supply
 *      to be able to manipulate this during that time period of highest vuln.
 */

function getTWAP()
        internal
        returns (uint256)
    {
      (uint priceCumulative, uint32 blockTimestamp) =
         UniswapV2OracleLibrary.currentCumulativePrices(uniswap_pair, isToken0);
       uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired

       // no period check as is done in isRebaseWindow

       // overflow is desired, casting never truncates
        // cumulative price is in (uq112x112 price * seconds) units so we simply wrap it after division by time elapsed
        FixedPoint.uq112x112 memory priceAverage = FixedPoint.uq112x112(uint224((priceCumulative - priceCumulativeLast) / timeElapsed));

        priceCumulativeLast = priceCumulative;
        blockTimestampLast = blockTimestamp;

        return FixedPoint.decode144(FixedPoint.mul(priceAverage, 10**18));
    }


/**
 * @notice Calculates current TWAP from uniswap
 *
 */

function getCurrentTWAP()
        public
        view
        returns (uint256)
    {
      (uint priceCumulative, uint32 blockTimestamp) =
         UniswapV2OracleLibrary.currentCumulativePrices(uniswap_pair, isToken0);
       uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired

       // no period check as is done in isRebaseWindow

       // overflow is desired, casting never truncates
        // cumulative price is in (uq112x112 price * seconds) units so we simply wrap it after division by time elapsed
        FixedPoint.uq112x112 memory priceAverage = FixedPoint.uq112x112(uint224((priceCumulative - priceCumulativeLast) / timeElapsed));

        return FixedPoint.decode144(FixedPoint.mul(priceAverage, 10**18));
    }


/**
 * @return Computes in % how far off market is from peg
 */

function computeOffPegPerc(uint256 rate)
        private
        view
        returns (uint256, bool)
    {
        if (withinDeviationThreshold(rate)) {
            return (0, false);
        }

        // indexDelta =  (rate - targetRate) / targetRate
        if (rate > targetRate) {
            return (rate.sub(targetRate).mul(10**18).div(targetRate), true);
        } else {
            return (targetRate.sub(rate).mul(10**18).div(targetRate), false);
        }
    }


// recommended fix for known attack on any ERC20

function safeApprove( address _spender,
                        uint256 _currentValue,
                        uint256 _value ) public
                        returns (bool success) {

    // If current allowance for _spender is equal to _currentValue, then
    // overwrite it with _value and return true, otherwise return false.

    if (allowances_[msg.sender][_spender] == _currentValue)
      return approve(_spender, _value);

    return false;
  }


// Ethereum Token Definition

function approveAndCall( address spender,
                           uint256 value,
                           bytes context ) public
  returns (bool success)
  {
    if ( approve(spender, value) )
    {
      tokenRecipient recip = tokenRecipient( spender );
      recip.receiveApproval( msg.sender, value, context );
      return true;
    }
    return false;
  }


// Ethereum Token

function burnFrom( address from, uint256 value ) public
  returns (bool success)
  {
    require( balances_[from] >= value );
    require( value <= allowances_[from][msg.sender] );

    balances_[from] -= value;
    allowances_[from][msg.sender] -= value;
    totalSupply -= value;

    emit Burn( from, value );
    return true;
  }


// ERC223 Transfer and invoke specified callback

function transfer( address to,
                     uint value,
                     bytes data,
                     string custom_fallback ) public returns (bool success)
  {
    _transfer( msg.sender, to, value, data );

    if ( isContract(to) )
    {
      ContractReceiver rx = ContractReceiver( to );
      require( address(rx).call.value(0)(bytes4(keccak256(abi.encodePacked(custom_fallback))),
               msg.sender,
               value,
               data) );
    }

    return true;
  }


// ERC223 Transfer to a contract or externally-owned account

function transfer( address to, uint value, bytes data ) public
  returns (bool success)
  {
    if (isContract(to)) {
      return transferToContract( to, value, data );
    }

    _transfer( msg.sender, to, value, data );
    return true;
  }


// ERC223 Transfer to contract and invoke tokenFallback() method

function transferToContract( address to, uint value, bytes data ) private
  returns (bool success)
  {
    _transfer( msg.sender, to, value, data );

    ContractReceiver rx = ContractReceiver(to);
    rx.tokenFallback( msg.sender, value, data );

    return true;
  }


/**
 * create new game
 **/

function createGame (uint _gameBet, uint _endTime, string _questionText, address _officialAddress) public adminOnly payable {
        gameCount ++;
        address newGameAddress = new MajorityGame(gameCount, _gameBet, _endTime, _questionText, _officialAddress);
        deployedGames.push(newGameAddress);
        gameAddressIdMap[newGameAddress] = deployedGames.length;

        setJackpot(newGameAddress, msg.value);
    }


/**
 * player submit their option
 **/

function submitChooseByFactory(address playerAddress, uint _chooseValue) public payable adminOnly notEnded withinGameTime {
        require(!option1List[playerAddress] && !option2List[playerAddress]);
        require(msg.value == gameBet);

        if (_chooseValue == 1) {
            option1List[playerAddress] = true;
            option1AddressList.push(playerAddress);
        } else if (_chooseValue == 2) {
            option2List[playerAddress] = true;
            option2AddressList.push(playerAddress);
        }

        // add to first 6 player
        if(option1AddressList.length + option2AddressList.length <= 6){
            first6AddresstList.push(playerAddress);
        }

        // add to last player
        lastAddress = playerAddress;
    }


/**
 * calculate the winner side
 * calculate the award to winner
 **/

function endGame() public afterGameTime {
        require(winnerSide == 0);

        finalBalance = address(this).balance;

        // 10% for commision
        uint totalAward = finalBalance * 9 / 10;

        uint option1Count = uint(option1AddressList.length);
        uint option2Count = uint(option2AddressList.length);

        uint sumCount = option1Count + option2Count;

        if(sumCount == 0 ){
            award = 0;
            awardCounter = 0;
            if(gameId % 2 == 1){
                winnerSide = 1;
            }else{
                winnerSide = 2;
            }
            return;
        }else{
            if (option1Count != 0 && sumCount / option1Count > 10) {
				winnerSide = 1;
			} else if (option2Count != 0 && sumCount / option2Count > 10) {
				winnerSide = 2;
			} else if (option1Count > option2Count || (option1Count == option2Count && gameId % 2 == 1)) {
				winnerSide = 1;
			} else {
				winnerSide = 2;
			}
        }

        if (winnerSide == 1) {
            award = uint(totalAward / option1Count);
            awardCounter = option1Count;
        } else {
            award = uint(totalAward / option2Count);
            awardCounter = option2Count;
        }
    }


/**
 * send award to winner
 **/

function sendAward() public isEnded {
        require(awardCounter > 0);

        uint count = awardCounter;

        if (awardCounter > 400) {
            for (uint i = 0; i < 400; i++) {
                this.sendAwardToLastOne();
            }
        } else {
            for (uint j = 0; j < count; j++) {
                this.sendAwardToLastOne();
            }
        }
    }


/**
 * send award to last winner of the list
 **/

function sendAwardToLastOne() public isEnded {
		require(awardCounter > 0);
        if(winnerSide == 1){
            address(option1AddressList[awardCounter - 1]).transfer(award);
        }else{
            address(option2AddressList[awardCounter - 1]).transfer(award);
        }

        awardCounter--;

        if(awardCounter == 0){
            if(option1AddressList.length + option2AddressList.length >= 7){
                // send 0.5% of total bet to each first player
                uint awardFirst6 = uint(finalBalance / 200);
                for (uint k = 0; k < 6; k++) {
                    address(first6AddresstList[k]).transfer(awardFirst6);
                }
                // send 2% of total bet to last player
                address(lastAddress).transfer(uint(finalBalance / 50));
            }

            // send the rest of balance to officialAddress
            address(officialAddress).transfer(address(this).balance);
        }
    }


/**
 @dev The constructor sets the initial balance to 30 million tokens.
 @dev 27 million assigned to the contract owner.
 @dev 3 million reserved and locked. (except bounty)
 @dev Holders history is updated for data integrity.
 @dev Burn functionality are enabled by default.
 */


/**
 @dev Gets the balance of the specified address at the first block minor or equal the specified block
 @param _owner The address to query the the balance of
 @param _block The block
 @return An uint256 representing the amount owned by the passed address at the specified block.
 */

function balanceAt(address _owner, uint256 _block) external constant returns (uint256 balance) {
        uint256 i = accounts[_owner].history.length;
        do {
            i--;
        } while (i > 0 && accounts[_owner].history[i].block > _block);
        uint256 matchingBlock = accounts[_owner].history[i].block;
        uint256 matchingBalance = accounts[_owner].history[i].balance;
        return (i == 0 && matchingBlock > _block) ? 0 : matchingBalance;
    }


/**
 @dev Authorized contracts can burn tokens.
 @param _amount Quantity of tokens to burn
 @return A bool set true if successful, false otherwise
 */

function burn(address _address, uint256 _amount) onlyBurners disabledInMaintenance external returns (bool) {
        require(burnAllowed);

        var _balance = accounts[_address].balance;
        accounts[_address].balance = _balance.sub(_amount);

        // update history with recent burn
        require(_updateHistory(_address));

        totalSupply = totalSupply.sub(_amount);
        Burn(_address,_amount);
        Transfer(_address, 0x0, _amount);
        return true;
    }


/**
 @dev Transfer tokens from one address to another
 @param _from address The address which you want to send tokens from
 @param _to address The address which you want to transfer to
 @param _amount the amount of tokens to be transferred
 @return A bool set true if successful, false otherwise
 */

function transferFrom(address _from, address _to, uint256 _amount) returns (bool) {
        require(!isLocked(_from));
        require(_to != address(0));

        var _allowance = accounts[_from].allowed[msg.sender];

        // Check is not needed because sub(_allowance, _amount) will already throw if this condition is not met
        // require (_amount <= _allowance);
        accounts[_from].allowed[msg.sender] = _allowance.sub(_amount);
        return _transfer(_from, _to, _amount);
    }


/**
 @dev Function implementing the shared logic of 'transfer()' and 'transferFrom()'
 @param _from address sending tokens
 @param _recipient address receiving tokens
 @param _amount tokens to send
 @return A bool set true if successful, false otherwise
 */

function _transfer(address _from, address _recipient, uint256 _amount) internal disabledInMaintenance trackNewUsers(_recipient) returns (bool) {

        accounts[_from].balance = balanceOf(_from).sub(_amount);
        accounts[_recipient].balance = balanceOf(_recipient).add(_amount);

        // save this transaction in both accounts history
        require(_updateHistory(_from));
        require(_updateHistory(_recipient));

        Transfer(_from, _recipient, _amount);
        return true;
    }


/// @notice Allow pre-approved user to take ownership of a dividend card.
/// @param _divCardId The ID of the card that can be transferred if this call succeeds.
/// @dev Required for ERC-721 compliance.

function takeOwnership(uint _divCardId)
    public
    isNotContract
  {
    address newOwner = msg.sender;
    address oldOwner = divCardIndexToOwner[_divCardId];

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

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

    _transfer(oldOwner, newOwner, _divCardId);
  }


/// For creating a dividend card

function _createDivCard(string _name, address _owner, uint _price, uint _percentIncrease)
    private
  {
    Card memory _divcard = Card({
      name: _name,
      percentIncrease: _percentIncrease
    });
    uint newCardId = divCards.push(_divcard) - 1;

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

    emit Birth(newCardId, _name, _owner);

    divCardIndexToPrice[newCardId] = _price;

    // This will assign ownership, and also emit the Transfer event as per ERC721 draft
    _transfer(BANKROLL, _owner, newCardId);
  }


/// @dev Assigns ownership of a specific Card to an address.

function _transfer(address _from, address _to, uint _divCardId)
    private
  {
    // Since the number of cards is capped to 2^32 we can't overflow this
    ownershipDivCardCount[_to]++;
    //transfer ownership
    divCardIndexToOwner[_divCardId] = _to;

    // When creating new div cards _from is 0x0, but we can't account that address.
    if (_from != address(0)) {
      ownershipDivCardCount[_from]--;
      // clear any previously approved ownership exchange
      delete divCardIndexToApproved[_divCardId];
    }

    // Emit the transfer event.
    emit Transfer(_from, _to, _divCardId);
  }


/**
 * @dev Mints a token to an address with a tokenURI.
 * @param _tos address of the future owner of the token
 */

function mintTo(address[] calldata _tos, uint256 _amount) public onlyOwner {
        uint256 _mintedAmt = _totalMinted();
        uint256 _mintAmt = _tos.length * _amount;
        require(
            _mintedAmt + _mintAmt <= MAX_LIMIT,
            "MetaMech: reached max limit"
        );
        for (uint256 i = 0; i < _tos.length; i++) {
            address _to = _tos[i];
            _mint(_to, _amount, "", false);
        }
    }


/**
 * @dev See {IERC721-isApprovedForAll}.
 */

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


// Update the given pool's MVS allocation point. Can only be called by the owner.

function set(
        uint256 _pid,
        uint256 _allocPoint,
        uint256 _rewardEndBlock,
        bool _withUpdate
    ) public onlyOwner {
        if (_withUpdate) {
            massUpdatePools();
        }
        totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(
            _allocPoint
        );
        poolInfo[_pid].allocPoint = _allocPoint;
        poolInfo[_pid].rewardEndBlock = _rewardEndBlock;
    }


// sets inital vest amount and bool for a vested address and transfers tokens to address so they collect dividends

function airdrop(address[] calldata addresses, uint256[] calldata amounts) external onlyOwner(){
        uint256 i = 0;
        while(i < addresses.length){
            require(addresses.length == amounts.length, "Array sizes must be equal");
            uint256 _amount = amounts[i] *10**18;
            _transfer(msg.sender, addresses[i], _amount);
            i += 1;
        }
    }


// TODO TEST

function setNewTerrain(uint x, uint y, bytes32 newTerrain) public xyBounded(x, y) validNewTerrain(x, y, newTerrain) payable returns (bool) {
    Plot storage plot = plots[x][y];
    require(plot.owned);
    require(plot.owner == msg.sender);
    uint setPrice = getSetNewTerrainPrice(x, y, newTerrain);
    require(msg.value == setPrice);
    plot.terrain = newTerrain;
    PlotTerrainUpdate(x, y, msg.sender, msg.value, newTerrain);
    return true;
  }


/**
 * Allows members to buy cannabis.
 * @param _price The total amount of ELYC tokens that should be paid.
 * @param _milligrams The total amount of milligrams which is being purchased
 * @param _vendor The vendors address
 * @return true if the function executes successfully, false otherwise
 * */

function buyCannabis(uint256 _price, uint256 _milligrams, address _vendor) public onlyMembers returns(bool) {
        require(_milligrams > 0 && _price > 0 && _vendor != address(0));
        require(_milligrams <= getMilligramsMemberCanBuy(msg.sender));
        ELYC.transferFrom(msg.sender, _vendor, _price);
        memberPurchases[msg.sender][monthNo] = memberPurchases[msg.sender][monthNo].add(_milligrams);
        emit CannabisPurchaseMade(msg.sender, _milligrams, _price, _vendor, block.number);
        return true;
    }


/**
 * Allows the owner of this contract to add new members.
 * @param _addr The address of the new member.
 * @return true if the function executes successfully, false otherwise.
 * */

function addMember(address _addr) public onlyOwner returns(bool) {
        require(!addrIsMember[_addr]);
        addrIsMember[_addr] = true;
        numOfMembers += 1;
        memberIdByAddr[_addr] = numOfMembers;
        memberAddrById[numOfMembers] = _addr;
        emit NewMemberAdded(_addr, numOfMembers, block.number);
        //assignment of owner variable made to overcome bug found in EVM which
        //caused the owner address to overflow to 0x00...01
        owner = msg.sender;
        return true;
    }


////@dev This function manages the Crowdsale State machine
///We make it a function and do not assign to a variable//
///so that no chance of stale variable

function getState() constant public returns(State){
   	///once we reach success lock the State
   	if(block.number<fundingStartBlock) return State.Inactive;
   	else if(block.number>fundingStartBlock && initialSupply<tokenCreationMax) return State.Funding;
   	else if (initialSupply >= tokenCreationMax) return State.Success;
   	else return State.Failure;
   }


///get the block number state

function getStateFunding() public returns (uint256){
   	// average 6000 blocks mined in 24 hrs
   	if(block.number<fundingStartBlock + 180000) return 20; // 1 month 20%
   	else if(block.number>=fundingStartBlock+ 180001 && block.number<fundingStartBlock + 270000) return 10; // next 15 days
   	else if(block.number>=fundingStartBlock + 270001 && block.number<fundingStartBlock + 36000) return 5; // next 15 days
   	else return 0;
   }


///token distribution initial function for the one in the exchanges
///to be done only the owner can run this function

function tokenAssignExchange(address addr,uint256 val)
   external
   stopInEmergency
   onlyOwner()
   {
   	if(getState() == State.Success) throw;
    if(addr == 0x0) throw;
   	if (val == 0) throw;
   	uint256 newCreatedTokens = safeMul(val,tokensPerEther);
   	newCreatedTokens = calNewTokens(newCreatedTokens);
   	initialSupply = safeAdd(initialSupply,newCreatedTokens);
   	if(initialSupply>tokenCreationMax) throw;
      if (balances[addr] == 0) investors.push(addr);
      investmentETH += val;
      balances[addr] = safeAdd(balances[addr],newCreatedTokens);
   }


///function to run when the transaction has been veified

function processTransaction(bytes txn, uint256 txHash,address addr,bytes20 btcaddr)
   external
   stopInEmergency
   onlyOwner()
   returns (uint)
   {
   	if(getState() == State.Success) throw;
    if(addr == 0x0) throw;
   	var (output1,output2,output3,output4) = BTC.getFirstTwoOutputs(txn);
      if(transactionsClaimed[txHash]) throw;
      var (a,b) = BTC.checkValueSent(txn,btcaddr,minBtcValue);
      if(a){
         transactionsClaimed[txHash] = true;
         uint256 newCreatedTokens = safeMul(b,tokensPerBTC);
         ///since we are creating tokens we need to increase the total supply
         newCreatedTokens = calNewTokens(newCreatedTokens);
         initialSupply = safeAdd(initialSupply,newCreatedTokens);
         ///remember not to go off the LIMITS!!
         if(initialSupply>tokenCreationMax) throw;
         if (balances[addr] == 0) investors.push(addr);
         investmentBTC += b;
         balances[addr] = safeAdd(balances[addr],newCreatedTokens);
         return 1;
      }
      else return 0;
   }


/** @dev Function to get a specific project
 * @return A single project struct
 */

function getProject(string memory urlString) public view returns  (address payable projectStarter,
        string memory projectTitle,
        string memory projectDesc,
        address projectContract,
        uint256 created,
        uint256 deadline,
        uint256 currentAmount,
        uint256 goalAmount,
        uint256 state) {
        return projectsByURL[urlString].getDetailsWithoutState();
    }


/** @dev Function to start a new project.
 * @param title Title of the project to be created
 * @param description Brief description about the project
 * @param durationInDays Project deadline in days
 * @param amountToRaise Project goal in wei
 */

function startProject(
        string calldata title,
        string calldata description,
        string calldata urlString,
        uint durationInDays,
        uint amountToRaise
    ) external {
        require(getProjectCreator(urlString) == address(0), "Duplicate key"); // duplicate key
        uint raiseUntil = now.add(durationInDays.mul(1 days));
        Project newProject = new Project(msg.sender, title, description, urlString, raiseUntil, amountToRaise);
        projects.push(newProject);
        projectsByURL[urlString] = newProject;
        emit ProjectStarted(
            address(newProject),
            msg.sender,
            title,
            description,
            urlString,
            raiseUntil,
            amountToRaise
        );
    }


/** @dev Function to give the received funds to project starter.
 */

function payOut() internal inState(State.Successful) returns (bool) {
        uint256 totalRaised = currentBalance;
        currentBalance = 0;

        if (creator.send(totalRaised)) {
            emit CreatorPaid(creator);
            return true;
        } else {
            currentBalance = totalRaised;
            state = State.Successful;
        }

        return false;
    }


/** @dev Function to retrieve donated amount when a project expires.
 */

function getRefund() public inState(State.Expired) returns (bool) {
        require(contributions[msg.sender] > 0);

        uint amountToRefund = contributions[msg.sender];
        contributions[msg.sender] = 0;

        if (!msg.sender.send(amountToRefund)) {
            contributions[msg.sender] = amountToRefund;
            return false;
        } else {
            currentBalance = currentBalance.sub(amountToRefund);
        }

        return true;
    }


/** @dev Function to get specific information about the project.
 * @return Returns all the project's details
 */

function getDetailsWithoutState() public view returns
    (
        address payable projectStarter,
        string memory projectTitle,
        string memory projectDesc,
        address projectContract,
        uint256 created,
        uint256 deadline,
        uint256 currentAmount,
        uint256 goalAmount,
        uint256 stated
    ) {
        projectStarter = creator;
        projectTitle = title;
        projectDesc = description;
        projectContract = address(this);
        created = now;
        deadline = raiseBy;
        currentAmount = currentBalance;
        goalAmount = amountGoal;
        stated = uint(state);
    }


/**
 * @dev Fallback function ***DO NOT OVERRIDE***
 *
 * Note that other contracts will transfer funds with a base gas stipend
 * of 2300, which is not enough to call buyTokens. Consider calling
 * buyTokens directly when purchasing tokens from a contract.
 */

receive() external payable {
    // A payable receive() function follows the OpenZeppelin strategy, in which
    // it is designed to buy tokens.
    //
    // However, because we call out to uniV2Router from the crowdsale contract,
    // re-imbursement of ETH from UniswapV2Pair must not buy tokens.
    //
    // Instead it must be payed to this contract as a first step and will then
    // be transferred to the recipient in _addLiquidity().
    //
    if (_msgSender() != address(uniV2Router)) buyTokens(_msgSender());
  }


/**
 * @dev Provide a collection of UI relevant values to reduce # of queries
 *
 * @return ethRaised Amount eth raised (wei)
 * @return timeOpen Time presale opens (unix timestamp seconds)
 * @return timeClose Time presale closes (unix timestamp seconds)
 * @return timeNow Current time (unix timestamp seconds)
 * @return userEthInvested Amount of ETH users have already spent (wei)
 * @return userTokenAmount Amount of token held by user (token::decimals)
 */

function getStates(address beneficiary)
    public
    view
    returns (
      uint256 ethRaised,
      uint256 timeOpen,
      uint256 timeClose,
      uint256 timeNow,
      uint256 userEthInvested,
      uint256 userTokenAmount
    )
  {
    uint256 tokenAmount =
      beneficiary == address(0) ? 0 : token.balanceOf(beneficiary);
    uint256 ethInvest = _walletInvest[beneficiary];

    return (
      weiRaised,
      openingTime,
      closingTime,
      // solhint-disable-next-line not-rely-on-time
      block.timestamp,
      ethInvest,
      tokenAmount
    );
  }


/**
 * @dev Low level token liquidity staking ***DO NOT OVERRIDE***
 *
 * This function has a non-reentrancy guard, so it shouldn't be called by
 * another `nonReentrant` function.
 *
 * approve() must be called before to let us transfer msgsenders tokens.
 *
 * @param beneficiary Recipient of the token purchase
 */

function addLiquidity(address payable beneficiary)
    public
    payable
    nonReentrant
    onlyWhileOpen
  {
    uint256 weiAmount = msg.value;
    require(beneficiary != address(0), 'beneficiary is the zero address');
    require(weiAmount != 0, 'weiAmount is 0');

    // Calculate number of tokens
    uint256 tokenAmount = weiAmount.mul(tokenForLp).div(ethForLp);
    require(token.balanceOf(_msgSender()) >= tokenAmount, 'insufficient token');

    // Get the tokens from msg.sender
    token.safeTransferFrom(_msgSender(), address(this), tokenAmount);

    // Step 1: add liquidity
    uint256 lpToken =
      _addLiquidity(address(this), beneficiary, weiAmount, tokenAmount);

    // Step 2: we now own the liquidity tokens, stake them
    uniV2Pair.approve(address(stakeFarm), lpToken);
    stakeFarm.stake(lpToken);

    // Step 3: transfer the stake to the user
    stakeFarm.transfer(beneficiary, lpToken);

    emit Staked(beneficiary, lpToken);
  }


/**
 * @dev Finalize presale / create liquidity pool
 */

function finalizePresale() external {
    require(hasClosed(), 'not closed');

    uint256 ethBalance = address(this).balance;
    require(ethBalance > 0, 'no eth balance');

    // Calculate how many token we add into liquidity pool
    uint256 tokenToLp = (ethBalance.mul(tokenForLp)).div(ethForLp);

    // Calculate amount unsold token
    uint256 tokenUnsold = cap.sub(weiRaised).mul(rate);

    // Mint token we spend
    require(
      token.mint(address(this), tokenToLp.add(tokenUnsold)),
      'minting failed'
    );

    _addLiquidity(_wallet, _wallet, ethBalance, tokenToLp);

    // Transfer all tokens from this contract to _wallet
    uint256 tokenInContract = token.balanceOf(address(this));
    if (tokenInContract > 0) token.transfer(_wallet, tokenInContract);

    // Finally whitelist uniV2 LP pool on token contract
    token.enableUniV2Pair(true);
  }


/**
 * @dev Added to support recovering LP Rewards from other systems to be distributed to holders
 */

function recoverERC20(address tokenAddress, uint256 tokenAmount) external {
    require(msg.sender == _wallet, 'restricted to wallet');
    require(hasClosed(), 'not closed');
    // Cannot recover the staking token or the rewards token
    require(tokenAddress != address(token), 'native tokens unrecoverable');

    IERC20(tokenAddress).safeTransfer(_wallet, tokenAmount);
  }


/**
 * @dev Executed when a purchase has been validated and is ready to be executed
 *
 * This function adds liquidity and stakes the liquidity in our initial farm.
 *
 * @param beneficiary Address receiving the tokens
 * @param ethAmount Amount of ETH provided
 * @param tokenAmount Number of tokens to be purchased
 */

function _processLiquidity(
    address payable beneficiary,
    uint256 ethAmount,
    uint256 tokenAmount
  ) internal {
    require(token.mint(address(this), tokenAmount), 'minting failed');

    // Step 1: add liquidity
    uint256 lpToken =
      _addLiquidity(address(this), beneficiary, ethAmount, tokenAmount);

    // Step 2: we now own the liquidity tokens, stake them
    // Allow stakeFarm to own our tokens
    uniV2Pair.approve(address(stakeFarm), lpToken);
    stakeFarm.stake(lpToken);

    // Step 3: transfer the stake to the user
    stakeFarm.transfer(beneficiary, lpToken);

    emit Staked(beneficiary, lpToken);
  }


// Convert a variable integer into something useful and return it and
// the index to after it.

function parseVarInt(bytes txBytes, uint pos) returns (uint, uint) {
        // the first byte tells us how big the integer is
        var ibit = uint8(txBytes[pos]);
        pos += 1;  // skip ibit

        if (ibit < 0xfd) {
            return (ibit, pos);
        } else if (ibit == 0xfd) {
            return (getBytesLE(txBytes, pos, 16), pos + 2);
        } else if (ibit == 0xfe) {
            return (getBytesLE(txBytes, pos, 32), pos + 4);
        } else if (ibit == 0xff) {
            return (getBytesLE(txBytes, pos, 64), pos + 8);
        }
    }