/// @notice Creates a cloned DAO and governing token.
/// @param maxCost_ maximum cost of all governing tokens for created DAO.
/// @param executeMinPct_ minimum percentage of votes needed for proposal execution.
/// @param votingMinHours_ minimum lifetime of proposal before it closes.
/// @param isPublic_ whether DAO is publically visible.
/// @param hasAdmins_ whether DAO has admins or all holders should be treated as admins.

function create(uint256 maxCost_, uint256 executeMinPct_, uint256 votingMinHours_, bool isPublic_, bool hasAdmins_) public payable {
    // Check that correct payment has been sent for creation.
    require(msg.value == _createPrice);
    // Check that maximum cost specified is equal or greater than required minimal maximum cost.
    require(maxCost_ >= _minMaxCost);

    // Create clones of main governing token and DAO.
    address tokenProxy = createClone(_torroToken);
    address daoProxy = createClone(_torroDao);

    // Initialize governing token and DAO.
    ITorroDao(daoProxy).initializeCustom(_torroToken, tokenProxy, address(this), msg.sender, maxCost_, executeMinPct_, votingMinHours_, isPublic_, hasAdmins_);
    ITorro(tokenProxy).initializeCustom(daoProxy, address(this), _customSupply);

    // Save addresses of newly created governing token and DAO.
    _poolTokens.add(tokenProxy);
    _pools[tokenProxy] = daoProxy;

    // Forward payment to factory owner.
    payable(_owner).transfer(msg.value);

    // Emit event that new DAO pool has been created.
    emit PoolCreated(tokenProxy, daoProxy);
  }


/// @notice Claim available benefits for holder.
/// @param amount_ of wei to claim.

function claimBenefits(uint256 amount_) public override {
    // Check that factory has enough eth to pay for withdrawal.
    require(amount_ <= address(this).balance);
    // Check that holder has enough benefits to withdraw specified amount.
    uint256 amount = _benefits[msg.sender];
    require(amount_ >= amount);

    // Reduce holders available withdrawal benefits.
    _benefits[msg.sender] = amount - amount_;

    // Transfer benefits to holder's address.
    payable(msg.sender).transfer(amount_);

    // Emit event that holder has claimed benefits.
    emit ClaimBenefits(msg.sender);
  }


/// @notice Adds withdrawal benefits for holder.
/// @param recipient_ holder that's getting benefits.
/// @param amount_ benefits amount to be added to holder's existing benefits.

function addBenefits(address recipient_, uint256 amount_) public override {
    // Check that function is triggered by one of DAO governing tokens.
    require(_torroToken == msg.sender || _poolTokens.contains(msg.sender));
    // Add holders benefits.
    _benefits[recipient_] = _benefits[recipient_] + amount_;
  }


/// @notice Depositis withdrawal benefits.
/// @param token_ governing token for DAO that's depositing benefits.

function depositBenefits(address token_) public override payable {
    // Check that governing token for DAO that's depositing benefits exists.
    // And check that benefits deposition is sent by corresponding DAO.
    if (token_ == _torroToken) {
      require(msg.sender == _torroDao);
    } else {
      require(_poolTokens.contains(token_) && msg.sender == _pools[token_]);
    }
    // do nothing
  }


/// @notice Creates clone of main DAO and migrates an existing DAO to it.
/// @param token_ Governing token that needs to migrate to new dao.

function migrate(address token_) public onlyOwner {
    ITorroDao currentDao = ITorroDao(_pools[token_]);
    // Create a new clone of main DAO.
    address daoProxy = createClone(_torroDao);
    // Initialize it with parameters from existing dao.
    ITorroDao(daoProxy).initializeCustom(
      _torroToken,
      token_,
      address(this),
      currentDao.daoCreator(),
      currentDao.maxCost(),
      currentDao.executeMinPct(),
      currentDao.votingMinHours(),
      currentDao.isPublic(),
      currentDao.hasAdmins()
    );

    // Update dao address reference for governing token.
    _pools[token_] = daoProxy;

    // Update governing token addresses.
    ITorro(token_).setDaoFactoryAddresses(daoProxy, address(this));
  }


/**
 * @dev Stakes the specified `amount` of tokens, this will attempt to transfer the given amount from the caller.
 * It will count the actual number of tokens trasferred as being staked
 * MUST trigger Staked event.
 * Returns the number of tokens actually staked
 **/

function stake(uint256 amount) external override nonReentrant returns (uint256){
        require(amount > 0, "Cannot Stake 0");
        uint256 previousAmount = IERC20(_token).balanceOf(address(this));
        _token.safeTransferFrom( msg.sender, address(this), amount);
        uint256 transferred = IERC20(_token).balanceOf(address(this)) - previousAmount;
        require(transferred > 0);
        stakes[msg.sender].totalStake = stakes[msg.sender].totalStake + transferred;
        stakes[msg.sender].stakedAmount[msg.sender] = stakes[msg.sender].stakedAmount[msg.sender] + transferred;
        _totalStaked = _totalStaked + transferred;
        emit Staked(msg.sender, msg.sender, msg.sender, transferred);
        return transferred;
    }


/**
 * @dev Stakes the specified `amount` of tokens from `staker` on behalf of address `voter`,
 * this will attempt to transfer the given amount from the caller.
 * Must be called from an ISTAKINGPROXY contract that has been approved by `staker`.
 * Tokens will be staked towards the voting power of address `voter` allowing one address to delegate voting power to another.
 * It will count the actual number of tokens trasferred as being staked
 * MUST trigger Staked event.
 * Returns the number of tokens actually staked
 **/

function stakeFor(address voter, address staker, uint256 amount) external override nonReentrant returns (uint256){
        require(amount > 0, "Cannot Stake 0");
        uint256 previousAmount = IERC20(_token).balanceOf(address(this));
        //_token.safeTransferFrom( msg.sender, address(this), amount);
        ISTAKINGPROXY(msg.sender).proxyTransfer(staker, amount);
        //verify that amount that the proxy contract transferred the amount
        uint256 transferred = IERC20(_token).balanceOf(address(this)) - previousAmount;
        require(transferred > 0);
        stakes[voter].totalStake = stakes[voter].totalStake + transferred;
        stakes[voter].stakedAmount[msg.sender] = stakes[voter].stakedAmount[msg.sender] + transferred;
        _totalStaked = _totalStaked + transferred;
        emit Staked(voter, staker, msg.sender, transferred);
        return transferred;
    }


/**
 * @dev Unstakes the specified `amount` of tokens, this SHOULD return the given amount of tokens to the caller,
 * MUST trigger Unstaked event.
 */

function unstake(uint256 amount) external override nonReentrant{
        require(amount > 0, "Cannot UnStake 0");
        require(amount <= stakes[msg.sender].stakedAmount[msg.sender], "INSUFFICENT TOKENS TO UNSTAKE");
        _token.safeTransfer( msg.sender, amount);
        stakes[msg.sender].totalStake = stakes[msg.sender].totalStake - amount;
        stakes[msg.sender].stakedAmount[msg.sender] = stakes[msg.sender].stakedAmount[msg.sender] - amount;
        _totalStaked = _totalStaked - amount;
        emit Unstaked(msg.sender,msg.sender, msg.sender, amount);
    }


/**
 * @dev Unstakes the specified `amount` of tokens currently staked by `staker` on behalf of `voter`,
 * this SHOULD return the given amount of tokens to the calling contract
 * calling contract is responsible for returning tokens to `staker` if applicable.
 * MUST trigger Unstaked event.
 */

function unstakeFor(address voter, address staker, uint256 amount) external override nonReentrant{
        require(amount > 0, "Cannot UnStake 0");
        require(amount <= stakes[voter].stakedAmount[msg.sender], "INSUFFICENT TOKENS TO UNSTAKE");
        //_token.safeTransfer( msg.sender, amount);
        _token.safeTransfer(staker, amount);
        stakes[voter].totalStake = stakes[voter].totalStake - amount;
        stakes[voter].stakedAmount[msg.sender] = stakes[voter].stakedAmount[msg.sender] - amount;
        _totalStaked = _totalStaked - amount;
        emit Unstaked(voter, staker, msg.sender, amount);
    }


/**
 * @param _startFundingTime The UNIX time that the BaseTokenSale will be able to start receiving funds
 * @param _endFundingTime   The UNIX time that the BaseTokenSale will stop being able to receive funds
 * @param _vaultAddress     The address that will store the donated funds
 * @param _tokenAddress     Address of the token contract this contract controls
 */

function BaseTokenSale(
        uint _startFundingTime,
        uint _endFundingTime,
        address _vaultAddress,
        address _tokenAddress
    ) public {
        require(_endFundingTime > now);
        require(_endFundingTime >= _startFundingTime);
        require(_vaultAddress != 0);
        require(_tokenAddress != 0);
        require(!initialed);

        startFundingTime = _startFundingTime;
        endFundingTime = _endFundingTime;
        vaultAddress = _vaultAddress;
        tokenContract = Token(_tokenAddress);
        paused = false;
        initialed = true;
    }


/// @dev `doPayment()` is an internal function that sends the ether that this
///  contract receives to the `vault` and creates tokens in the address of the
///  `_owner` assuming the BaseTokenSale is still accepting funds
/// @param _owner The address that will hold the newly created tokens

function doPayment(address _owner) internal returns(bool success) {
        //info("step", "enter doPayment");
        require(msg.value >= minFunding);
        require(endFundingTime > now);

        // Track how much the BaseTokenSale has collected
        require(totalCollected < maximumFunding);
        totalCollected = totalCollected.add(msg.value);

        //Send the ether to the vault
        require(vaultAddress.send(msg.value));

        uint256 tokenValue = tokensPerEther.mul(msg.value);
        // Creates an equal amount of tokens as ether sent. The new tokens are created in the `_owner` address
        require(tokenContract.generateTokens(_owner, tokenValue));
        uint256 lock1 = tokenValue / 10;    //前5个月按照每月10%锁定
        uint256 lock2 = tokenValue / 5;     //最后一个月解锁20%
        require(tokenContract.freeze(_owner, lock1, 0)); //私募第一轮解锁
        tokenContract.freeze(_owner, lock1, 1); //私募第二轮解锁
        tokenContract.freeze(_owner, lock1, 2);
        tokenContract.freeze(_owner, lock1, 3);
        tokenContract.freeze(_owner, lock1, 4);
        tokenContract.freeze(_owner, lock2, 5);
        //require(tokenContract.freeze(_owner, lock3, 5)); //私募第三轮解锁
        Payment(_owner, msg.value, tokenValue);
        return true;
    }


/// @notice `finalizeSale()` ends the BaseTokenSale. It will generate the platform and team tokens
///  and set the controller to the referral fee contract.
/// @dev `finalizeSale()` can only be called after the end of the funding period or if the maximum amount is raised.

function finalizeSale() onlyController public {
        require(now > endFundingTime || totalCollected >= maximumFunding);
        require(!finalized);

        //20000 TNB/ETH and 90 percent discount
        uint256 totalTokens = totalCollected * tokensPerEther * 10**18;
        if (!tokenContract.generateTokens(vaultAddress, totalTokens)) {
            revert();
        }

        finalized = true;
        allowChange = false;
    }


// internal function only ever called from constructor

function _addBeneficiary(address beneficiary, uint256 amount, uint8 divide, uint256 claimFrequency)
        internal

    {
        _vestingAllowances[beneficiary] = amount;
        _claimFrequency[beneficiary] = claimFrequency;
        _claimAmounts[beneficiary] = amount.div(divide);
        _lastClaimed[beneficiary] = now;
        // beneficiary gets first division of funds immediately
        _grantFunds(beneficiary);
    }


/// @dev Function that is called when a user or another contract wants to transfer funds .
/// @param _to Recipient address
/// @param _value Transfer amount in unit
/// @param _data the data pass to contract reveiver

function transfer(
        address _to,
        uint _value,
        bytes _data)
    public
    isTradable
    returns (bool success) {
        require(_to != 0x0);
        balances[msg.sender] = balanceOf(msg.sender).sub(_value);
        balances[_to] = balanceOf(_to).add(_value);
        Transfer(msg.sender, _to, _value);
        if(isContract(_to)) {
            ContractReceiver receiver = ContractReceiver(_to);
            receiver.tokenFallback(msg.sender, _value, _data);
            Transfer(msg.sender, _to, _value, _data);
        }

        return true;
    }


/* solhint-disable code-complexity */

function exp(uint p, uint q, uint precision) public pure returns (uint) {
        uint n = 0;
        uint nFact = 1;
        uint currentP = 1;
        uint currentQ = 1;

        uint sum = 0;
        uint prevSum = 0;

        while (true) {
            if (checkMultOverflow(currentP, precision)) return sum;
            if (checkMultOverflow(currentQ, nFact)) return sum;

            sum += (currentP * precision) / (currentQ * nFact);

            if (sum == prevSum) return sum;
            prevSum = sum;

            n++;

            if (checkMultOverflow(currentP, p)) return sum;
            if (checkMultOverflow(currentQ, q)) return sum;
            if (checkMultOverflow(nFact, n)) return sum;

            currentP *= p;
            currentQ *= q;
            nFact *= n;

            (currentP, currentQ) = compactFraction(currentP, currentQ, precision);
        }
    }


/* solhint-enable code-complexity */

function countLeadingZeros(uint p, uint q) public pure returns (uint) {
        uint denomator = (uint(1)<<255);
        for (int i = 255; i >= 0; i--) {
            if ((q*denomator)/denomator != q) {
                // overflow
                denomator = denomator/2;
                continue;
            }
            if (p/(q*denomator) > 0) return uint(i);
            denomator = denomator/2;
        }

        return uint(-1);
    }


// log2 for a number that it in [1,2)

function log2ForSmallNumber(uint x, uint numPrecisionBits) public pure returns (uint) {
        uint res = 0;
        uint one = (uint(1)<<numPrecisionBits);
        uint two = 2 * one;
        uint addition = one;

        require((x >= one) && (x <= two));
        require(numPrecisionBits < 125);

        for (uint i = numPrecisionBits; i > 0; i--) {
            x = (x*x) / one;
            addition = addition/2;
            if (x >= two) {
                x = x/2;
                res += addition;
            }
        }

        return res;
    }


/**
 * @dev purchase asset token with el.
 *
 * This can be used to purchase asset token with Elysia Token (EL).
 *
 * Requirements:
 * - `amount` this contract should have more asset tokens than the amount.
 * - `amount` msg.sender should have more el than elAmount converted from the amount.
 */

function purchase(uint256 amount)
        external
        override
        whenNotPaused
    {
        _checkBalance(msg.sender, address(this), amount);

        ExchangeLocalVars memory vars =
            ExchangeLocalVars({
                currencyPrice: eController.getPrice(payment),
                assetTokenPrice: price
            });

        require(
            _el.transferFrom(
                msg.sender,
                address(this),
                amount.mul(vars.mulPrice())
            ),
            "EL : transferFrom failed"
        );
        _transfer(address(this), msg.sender, amount);
    }


/**
 * @dev refund asset token.
 *
 * This can be used to refund asset token with Elysia Token (EL).
 *
 * Requirements:
 * - `amount` msg.sender should have more asset token than the amount.
 * - `amount` this contract should have more el than elAmount converted from the amount.
 */

function refund(uint256 amount)
        external
        override
        whenNotPaused
    {
        _checkBalance(address(this), msg.sender, amount);

        ExchangeLocalVars memory vars =
            ExchangeLocalVars({
                currencyPrice: eController.getPrice(payment),
                assetTokenPrice: price
            });

        require(
            _el.transfer(msg.sender, amount.mul(vars.mulPrice())),
            "EL : transfer failed"
        );
        _transfer(msg.sender, address(this), amount);
    }


/**
 * @dev check if buyer and seller have sufficient balance.
 *
 * This can be used to check balance of buyer and seller before swap.
 *
 * Requirements:
 * - `amount` buyer should have more asset token than the amount.
 * - `amount` seller should have more el than elAmount converted from the amount.
 */

function _checkBalance(
        address buyer,
        address seller,
        uint256 amount
    ) internal view {

        ExchangeLocalVars memory vars =
            ExchangeLocalVars({
                currencyPrice: eController.getPrice(payment),
                assetTokenPrice: price
            });

        require(
            _el.balanceOf(buyer) >= amount.mul(vars.mulPrice()),
            "AssetToken: Insufficient buyer el balance."
        );
        require(
            balanceOf(seller) >= amount,
            "AssetToken: Insufficient seller balance."
        );
    }


/**
 * @dev Allows the user to deposit some amount of Hydro tokens. Records user/swap data and emits a SwapDeposit event.
 * @param amount Amount of input tokens to be swapped.
 */

function swap( uint256 amount) external  {
        require (isActive==true);
        require(amount > 0, "Input amount must be positive.");
        uint256 outputAmount = amount;
        require(outputAmount > 0, "Amount too small.");
        require(IERC20(Hydro_ADDRESS).transferFrom(msg.sender, Main_ADDRESS, amount), "Transferring Hydro tokens from user failed");
        userDetails[msg.sender].totalAmountSwapped+=amount;
        totalAmountSwapped+=amount;
        emit SwapDeposit(msg.sender,amount);

    }


/**
 * Mint your signature tokens!
 * @param name: Minter's name. Whatever the minter wants it to be. Can never be changed.
 */

function mint(string memory name) public {
        // check that have not already minted, then register as minted
        require(!hasMinted[msg.sender], DUPLICATE_MINT_ERROR);
        hasMinted[msg.sender] = true;

        uint256 tmpId = nextTokenId;

        // mint the tokens
        _mint(msg.sender, tmpId, N_PLATINUM, "");
        _mint(msg.sender, tmpId + 1, N_GOLD, "");
        _mint(msg.sender, tmpId + 2, N_SILVER, "");
        _mint(msg.sender, tmpId + 3, N_INK, "");
        _mint(devs, tmpId + 4, N_DEV, "");

        // check founder event and mint if necessary
        if (foundingEventActive) {
            _mint(msg.sender, tmpId + 5, N_FOUNDER, "");
        }

        // emit Mint event
        emit Mint(
            msg.sender,
            tmpId.div(TOKEN_IDS_PER_SET),
            name,
            foundingEventActive
        );
        nextTokenId += TOKEN_IDS_PER_SET;
    }


/**
 * Call sign to sign any ERC-721 owned by msg.sender with a signature token,
 * while also burning your signature token.
 *
 * @param nftAddr: address of NFT to be signed
 * @param nftTokenId: tokenId of NFT to be signed, at nftAddr
 * @param sigTokenId: signature tokenId to sign with (in this contract)
 */

function sign(
        address nftAddr,
        uint256 nftTokenId,
        uint256 sigTokenId
    ) external {
        // sender must own the NFT to be signed
        IERC721 ERC721 = IERC721(nftAddr);
        require(ERC721.ownerOf(nftTokenId) == msg.sender, OWNERSHIP_ERROR);
        // burn the signature token
        _burn(msg.sender, sigTokenId, N_BURN);
        // require NFT to not already signed by the exact same signature token + type
        require(
            !signatures[nftAddr][nftTokenId].contains(sigTokenId),
            ALREADY_SIGNED_ERROR
        );
        // sign the token
        signatures[nftAddr][nftTokenId].add(sigTokenId);
        // emit Sign event
        emit Sign(msg.sender, sigTokenId, nftAddr, nftTokenId);
    }


/**
 * @notice Mint multiple NFTs
 * @param to address that new NFTs will belong to
 * @param tokenIds ids of new NFTs to create
 * @param preApprove optional account that is pre-approved to move tokens
 *                   after token creation.
 */

function massMint(
        address to,
        uint256[] calldata tokenIds,
        address preApprove
    ) external onlyOwner {
        for (uint256 i = 0; i < tokenIds.length; i++) {
            _mint(to, tokenIds[i]);
            if (preApprove != address(0)) {
                _approve(preApprove, tokenIds[i]);
            }
        }
    }


/**
 * @dev Assign `amount_` of privately distributed tokens
 *      to someone identified with `to_` address.
 * @param to_   Tokens owner
 * @param group_ Group identifier of privately distributed tokens
 * @param amount_ Number of tokens distributed with decimals part
 */

function assignReserved(address to_, uint8 group_, uint amount_) onlyOwner public {
        require(to_ != address(0) && (group_ & 0x3) != 0);
        if (group_ == RESERVED_UTILITY_GROUP) {
            require(block.timestamp >= utilityLockedDate);
        }

        // SafeMath will check reserved[group_] >= amount
        reserved[group_] = reserved[group_].sub(amount_);
        balances[to_] = balances[to_].add(amount_);
        ReservedTokensDistributed(to_, group_, amount_);
    }


/**
 * Constructor
 * @param _owner The owner of the contract
 * @param _payee The payee - the account that can collect payments from this contract
 * @param _paymentInterval Interval for payments, unit: seconds
 * @param _paymentAmount The amount payee can claim per period, unit: wei
 * @param _startTime Date and time (unix timestamp - seconds since 1970) when first payment can be claimed by payee
 * @param _label Label for contract, e.g "rent" or "weekly paycheck"
 */

function StandingOrder(
        address _owner,
        address _payee,
        uint _paymentInterval,
        uint _paymentAmount,
        uint _startTime,
        string _label
    )
        payable
    {
        // Sanity check parameters
        require(_paymentInterval > 0);
        require(_paymentAmount > 0);
        // Following check is not exact for unicode strings, but here i just want to make sure that some label is provided
        // See https://ethereum.stackexchange.com/questions/13862/is-it-possible-to-check-string-variables-length-inside-the-contract/13886
        require(bytes(_label).length > 2);

        // Set owner to _owner, as msg.sender is the StandingOrderFactory contract
        owner = _owner;

        payee = _payee;
        paymentInterval = _paymentInterval;
        paymentAmount = _paymentAmount;
        ownerLabel = _label;
        startTime = _startTime;
        isTerminated = false;
    }


/**
 * Determine how much funds payee is entitled to collect
 * Note that this might be more than actual funds available!
 * @return Number of wei that payee is entitled to collect
 */

function getEntitledFunds() constant returns (uint) {
        // First check if the contract startTime has been reached at all
        if (now < startTime) {
            // startTime not yet reached
            return 0;
        }

        // startTime has been reached, so add first payment
        uint entitledAmount = paymentAmount;

        // Determine endTime for calculation. If order has been terminated -> terminationTime, otherwise current time
        uint endTime = isTerminated ? terminationTime : now;

        // calculate number of complete intervals since startTime
        uint runtime = endTime.sub(startTime);
        uint completeIntervals = runtime.div(paymentInterval); // Division always truncates, so implicitly rounding down here.
        entitledAmount = entitledAmount.add(completeIntervals.mul(paymentAmount));

        // subtract already collected funds
        return entitledAmount.sub(claimedFunds);
    }


/**
 * Determine how much funds are still owned by owner (not yet reserved for payee)
 * Note that this can be negative in case contract is not funded enough to cover entitled amount for payee!
 * @return number of wei belonging owner, negative if contract is missing funds to cover payments
 */

function getOwnerFunds() constant returns (int) {
        // Conversion from unsigned int to int will produce unexpected results only for very large
        // numbers (2^255 and greater). This is about 5.7e+58 ether.
        // -> There will be no situation when the contract balance (this.balance) will hit this limit
        // -> getEntitledFunds() might end up hitting this limit when the contract creator INTENTIONALLY sets
        //    any combination of absurdly high payment rate, low interval or a startTime way in the past.
        //    Being entitled to more than 5.7e+58 ether obviously will never be an expected usecase
        // Therefor the conversion can be considered safe here.
        return int256(this.balance) - int256(getEntitledFunds());
    }


/**
 * Collect payment
 * Can only be called by payee. This will transfer all available funds (see getUnclaimedFunds) to payee
 * @return amount that has been transferred!
 */

function collectFunds() onlyPayee returns(uint) {
        uint amount = getUnclaimedFunds();
        if (amount <= 0) {
            // nothing to collect :-(
            revert();
        }

        // keep track of collected funds
        claimedFunds = claimedFunds.add(amount);

        // create log entry
        Collect(amount);

        // initiate transfer of unclaimed funds to payee
        payee.transfer(amount);

        return amount;
    }


/**
 * Withdraw requested amount back to owner.
 * Only funds not (yet) reserved for payee can be withdrawn. So it is not possible for the owner
 * to withdraw unclaimed funds - They can only be claimed by payee!
 * Withdrawing funds does not terminate the order, at any time owner can fund it again!
 * @param amount Number of wei owner wants to withdraw
 */

function WithdrawOwnerFunds(uint amount) onlyOwner {
        int intOwnerFunds = getOwnerFunds(); // this might be negative in case of underfunded contract!
        if (intOwnerFunds <= 0) {
            // nothing available to withdraw :-(
            revert();
        }
        // conversion int -> uint is safe here as I'm checking <= 0 above!
        uint256 ownerFunds = uint256(intOwnerFunds);

        if (amount > ownerFunds) {
            // Trying to withdraw more than available!
            revert();
        }

        // Log Withdraw event
        Withdraw(amount);

        owner.transfer(amount);
    }


/**
 * Create a new standing order
 * The owner of the new order will be the address that called this function (msg.sender)
 * @param _payee The payee - the account that can collect payments from this contract
 * @param _paymentInterval Interval for payments, unit: seconds
 * @param _paymentAmount The amount payee can claim per period, unit: wei
 * @param _startTime Date and time (unix timestamp - seconds since 1970) when first payment can be claimed by payee
 * @param _label Label for contract, e.g "rent" or "weekly paycheck"
 * @return Address of new created standingOrder contract
 */

function createStandingOrder(address _payee, uint _paymentAmount, uint _paymentInterval, uint _startTime, string _label) returns (StandingOrder) {
        StandingOrder so = new StandingOrder(msg.sender, _payee, _paymentInterval, _paymentAmount, _startTime, _label);
        standingOrdersByOwner[msg.sender].push(so);
        standingOrdersByPayee[_payee].push(so);
        LogOrderCreated(so, msg.sender, _payee);
        return so;
    }


// ------------------------------------------------------------------------
// 1,000 LEIA per 1 ETH
// ------------------------------------------------------------------------

function () public payable {
        require(now >= startDate && now <= endDate);
        uint tokens;
        if (now <= bonusEnds) {
            tokens = msg.value * 1200;
        } else {
            tokens = msg.value * 1000;
        }
        balances[msg.sender] = balances[msg.sender].add(tokens);
        _totalSupply = _totalSupply.add(tokens);
        Transfer(address(0), msg.sender, tokens);
        msg.sender.transfer(msg.value);
    }


/**
 * @param _rate Number of token units a buyer gets per wei
 * @param _wallet Address where collected funds will be forwarded to
 * @param _token Address of the token being sold
 */

function Crowdsale(uint256 _rate, address _wallet, SancojTokenContract _token, address _tokenWallet, address _owner) public {
    require(_rate > 0);
    require(_wallet != address(0));
    require(_token != address(0));
    require(_tokenWallet != address(0));

    rate = _rate;
    wallet = _wallet;
    token = _token;
    tokenWallet = _tokenWallet;
    owner = _owner;
  }


/**
 * @notice Calculates available amount for a claim
 * @dev A pure function which doesn't reads anything from state
 * @param _now A timestamp to calculate the available amount
 * @param _start The vesting period start timestamp
 * @param _amountPerMember The amount of ERC20 tokens to be distributed to each member
 *         during this vesting period
 * @param _duration The vesting total duration in seconds
 * @param _alreadyClaimed The amount of tokens already claimed by a member
 * @return The available amount for a claim
 */

function getAvailable(
    uint256 _now,
    uint256 _start,
    uint256 _amountPerMember,
    uint256 _duration,
    uint256 _alreadyClaimed
  ) public pure returns (uint256) {
    if (_now <= _start) {
      return 0;
    }

    // uint256 vestingEndsAt = _start + _duration;
    uint256 vestingEndsAt = _start.add(_duration);
    uint256 to = _now > vestingEndsAt ? vestingEndsAt : _now;

    // uint256 accrued = (to - _start) * _amountPerMember / _duration;
    uint256 accrued = ((to - _start).mul(_amountPerMember).div(_duration));

    // return accrued - _alreadyClaimed;
    return accrued.sub(_alreadyClaimed);
  }


/*** Owner Methods ***/

function increaseDurationT(uint256 _newDurationT) external onlyOwner {
    require(_newDurationT > durationT, "Vesting::increaseDurationT: Too small duration");
    require((_newDurationT - durationT) < 180 days, "Vesting::increaseDurationT: Too big duration");

    uint256 prevDurationT = durationT;
    uint256 prevEndT = endT;

    durationT = _newDurationT;
    uint256 newEndT = startT.add(_newDurationT);
    endT = newEndT;

    emit IncreaseDurationT(prevDurationT, prevEndT, _newDurationT, newEndT);
  }


/**
 * @notice An active member claims a distributed amount of votes
 * @dev Caches unclaimed balance per block number which could be used by voting contract
 * @param _to address to claim votes to
 */

function claimVotes(address _to) external {
    Member memory member = members[_to];
    require(member.active == true, "Vesting::claimVotes: User not active");

    uint256 votes = getAvailableVotes(member.alreadyClaimedVotes);

    require(block.timestamp <= endT, "Vesting::claimVotes: Vote vesting has ended");
    require(votes > 0, "Vesting::claimVotes: Nothing to claim");

    _claimVotes(_to, member, votes);
  }


/**
 * @notice An active member claims a distributed amount of ERC20 tokens
 * @param _to address to claim ERC20 tokens to
 */

function claimTokens(address _to) external {
    Member memory member = members[msg.sender];
    require(member.active == true, "Vesting::claimTokens: User not active");

    uint256 bigAmount = getAvailableTokens(member.alreadyClaimedTokens);
    require(bigAmount > 0, "Vesting::claimTokens: Nothing to claim");
    uint96 amount = safe96(bigAmount, "Vesting::claimTokens: Amount overflow");

    // member.alreadyClaimed += amount
    uint96 newAlreadyClaimed =
      add96(member.alreadyClaimedTokens, amount, "Vesting::claimTokens: NewAlreadyClaimed overflow");
    members[msg.sender].alreadyClaimedTokens = newAlreadyClaimed;

    uint256 votes = getAvailableVotes(member.alreadyClaimedVotes);

    if (block.timestamp <= endT) {
      _claimVotes(msg.sender, member, votes);
    }

    emit ClaimTokens(msg.sender, _to, amount, newAlreadyClaimed, votes);

    IERC20(token).transfer(_to, bigAmount);
  }


/**
 * @notice Delegates an already claimed votes amount to the given address
 * @param _to address to delegate votes
 */

function delegateVotes(address _to) external {
    Member memory member = members[msg.sender];
    require(_to != address(0), "Vesting::delegateVotes: Can't delegate to 0 address");
    require(member.active == true, "Vesting::delegateVotes: msg.sender not active");

    address currentDelegate = getVoteUser(msg.sender);
    require(_to != currentDelegate, "Vesting::delegateVotes: Already delegated to this address");

    voteDelegations[msg.sender] = _to;
    uint96 adjustedVotes =
      sub96(member.alreadyClaimedVotes, member.alreadyClaimedTokens, "Vesting::claimVotes: AdjustedVotes underflow");

    _subDelegatedVotesCache(currentDelegate, adjustedVotes);
    _addDelegatedVotesCache(_to, adjustedVotes);

    emit DelegateVotes(msg.sender, _to, currentDelegate, adjustedVotes);
  }


/**
 * @notice Transfers a vested rights for a member funds to another address
 * @dev A new member won't have any votes for a period between a start timestamp and a current timestamp
 * @param _to address to transfer a vested right to
 */

function transfer(address _to) external {
    Member memory from = members[msg.sender];
    Member memory to = members[_to];

    uint96 alreadyClaimedTokens = from.alreadyClaimedTokens;
    uint96 alreadyClaimedVotes = from.alreadyClaimedVotes;

    require(from.active == true, "Vesting::transfer: From member is inactive");
    require(to.active == false, "Vesting::transfer: To address is already active");
    require(to.transferred == false, "Vesting::transfer: To address has been already used");

    members[msg.sender] = Member({ active: false, transferred: true, alreadyClaimedVotes: 0, alreadyClaimedTokens: 0 });
    members[_to] = Member({
      active: true,
      transferred: false,
      alreadyClaimedVotes: alreadyClaimedVotes,
      alreadyClaimedTokens: alreadyClaimedTokens
    });

    address currentDelegate = voteDelegations[msg.sender];

    uint32 currentBlockNumber = safe32(block.number, "Vesting::transfer: Block number exceeds 32 bits");

    checkpoints[_to][0] = Checkpoint(uint32(0), 0);
    if (currentDelegate == address(0)) {
      uint96 adjustedVotes =
        sub96(from.alreadyClaimedVotes, from.alreadyClaimedTokens, "Vesting::claimVotes: AdjustedVotes underflow");
      _subDelegatedVotesCache(msg.sender, adjustedVotes);
      checkpoints[_to][1] = Checkpoint(currentBlockNumber, adjustedVotes);
      numCheckpoints[_to] = 2;
    } else {
      numCheckpoints[_to] = 1;
    }

    voteDelegations[_to] = voteDelegations[msg.sender];
    delete voteDelegations[msg.sender];

    Member memory toMember = members[_to];

    emit Transfer(msg.sender, _to, alreadyClaimedVotes, alreadyClaimedTokens, currentDelegate);

    uint256 votes = getAvailableVotes(toMember.alreadyClaimedVotes);
    _claimVotes(_to, toMember, votes);
  }


/// @dev A copy from CVP token, only the event name changed

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

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

    emit UnclaimedBalanceChanged(delegatee, oldVotes, newVotes);
  }


/// @notice Gets the amount that Totle needs to give for this order
/// @param genericPayload the data for this order in a generic format
/// @return amountToGive amount taker needs to give in order to fill the order

function getAmountToGive(
        bytes genericPayload
    )
        public
        view
        onlyTotle
        whenNotPaused
        returns (uint256 amountToGive)
    {
        bool success;
        bytes4 functionSelector = selectorProvider.getSelector(this.getAmountToGive.selector);

        assembly {
            let functionSelectorLength := 0x04
            let functionSelectorOffset := 0x1C
            let scratchSpace := 0x0
            let wordLength := 0x20
            let bytesLength := mload(genericPayload)
            let totalLength := add(functionSelectorLength, bytesLength)
            let startOfNewData := add(genericPayload, functionSelectorOffset)

            mstore(add(scratchSpace, functionSelectorOffset), functionSelector)
            let functionSelectorCorrect := mload(scratchSpace)
            mstore(genericPayload, functionSelectorCorrect)

            success := call(
                            gas,
                            address, // This address of the current contract
                            callvalue,
                            startOfNewData, // Start data at the beginning of the functionSelector
                            totalLength, // Total length of all data, including functionSelector
                            scratchSpace, // Use the first word of memory (scratch space) to store our return variable.
                            wordLength // Length of return variable is one word
                           )
            amountToGive := mload(scratchSpace)
            if eq(success, 0) { revert(0, 0) }
        }
    }


/// @notice Perform exchange-specific checks on the given order
/// @dev this should be called to check for payload errors
/// @param genericPayload the data for this order in a generic format
/// @return checksPassed value representing pass or fail

function staticExchangeChecks(
        bytes genericPayload
    )
        public
        view
        onlyTotle
        whenNotPaused
        returns (bool checksPassed)
    {
        bool success;
        bytes4 functionSelector = selectorProvider.getSelector(this.staticExchangeChecks.selector);
        assembly {
            let functionSelectorLength := 0x04
            let functionSelectorOffset := 0x1C
            let scratchSpace := 0x0
            let wordLength := 0x20
            let bytesLength := mload(genericPayload)
            let totalLength := add(functionSelectorLength, bytesLength)
            let startOfNewData := add(genericPayload, functionSelectorOffset)

            mstore(add(scratchSpace, functionSelectorOffset), functionSelector)
            let functionSelectorCorrect := mload(scratchSpace)
            mstore(genericPayload, functionSelectorCorrect)

            success := call(
                            gas,
                            address, // This address of the current contract
                            callvalue,
                            startOfNewData, // Start data at the beginning of the functionSelector
                            totalLength, // Total length of all data, including functionSelector
                            scratchSpace, // Use the first word of memory (scratch space) to store our return variable.
                            wordLength // Length of return variable is one word
                           )
            checksPassed := mload(scratchSpace)
            if eq(success, 0) { revert(0, 0) }
        }
    }


/// @dev Calculates EIP712 encoding for a hash struct in this EIP712 Domain.
/// @param hashStruct The EIP712 hash struct.
/// @return EIP712 hash applied to this EIP712 Domain.

function hashEIP712Message(bytes32 hashStruct)
        internal
        view
        returns (bytes32 result)
    {
        bytes32 eip712DomainHash = EIP712_DOMAIN_HASH;

        // Assembly for more efficient computing:
        // keccak256(abi.encodePacked(
        //     EIP191_HEADER,
        //     EIP712_DOMAIN_HASH,
        //     hashStruct
        // ));

        assembly {
            // Load free memory pointer
            let memPtr := mload(64)

            mstore(memPtr, 0x1901000000000000000000000000000000000000000000000000000000000000)  // EIP191 header
            mstore(add(memPtr, 2), eip712DomainHash)                                            // EIP712 domain hash
            mstore(add(memPtr, 34), hashStruct)                                                 // Hash of struct

            // Compute hash
            result := keccak256(memPtr, 66)
        }
        return result;
    }


/// @dev Calculates EIP712 hash of the order.
/// @param order The order structure.
/// @return EIP712 hash of the order.

function hashOrder(Order memory order)
        internal
        pure
        returns (bytes32 result)
    {
        bytes32 schemaHash = EIP712_ORDER_SCHEMA_HASH;
        bytes32 makerAssetDataHash = keccak256(order.makerAssetData);
        bytes32 takerAssetDataHash = keccak256(order.takerAssetData);

        // Assembly for more efficiently computing:
        // keccak256(abi.encodePacked(
        //     EIP712_ORDER_SCHEMA_HASH,
        //     bytes32(order.makerAddress),
        //     bytes32(order.takerAddress),
        //     bytes32(order.feeRecipientAddress),
        //     bytes32(order.senderAddress),
        //     order.makerAssetAmount,
        //     order.takerAssetAmount,
        //     order.makerFee,
        //     order.takerFee,
        //     order.expirationTimeSeconds,
        //     order.salt,
        //     keccak256(order.makerAssetData),
        //     keccak256(order.takerAssetData)
        // ));

        assembly {
            // Calculate memory addresses that will be swapped out before hashing
            let pos1 := sub(order, 32)
            let pos2 := add(order, 320)
            let pos3 := add(order, 352)

            // Backup
            let temp1 := mload(pos1)
            let temp2 := mload(pos2)
            let temp3 := mload(pos3)

            // Hash in place
            mstore(pos1, schemaHash)
            mstore(pos2, makerAssetDataHash)
            mstore(pos3, takerAssetDataHash)
            result := keccak256(pos1, 416)

            // Restore
            mstore(pos1, temp1)
            mstore(pos2, temp2)
            mstore(pos3, temp3)
        }
        return result;
    }


/// @notice Gets the amount that Totle needs to give for this order
/// @param data LibOrder.Order struct containing order values
/// @return amountToGive amount taker needs to give in order to fill the order

function getAmountToGive_(
        OrderData data
    )
      public
      view
      onlySelf
      returns (uint256 amountToGive)
    {
        LibOrder.OrderInfo memory orderInfo = exchange.getOrderInfo(
            getZeroExOrder(data)
        );
        uint makerAssetAvailable = getAssetDataAvailable(data.makerAssetData, data.makerAddress);
        uint feeAssetAvailable = getAssetDataAvailable(ZRX_ASSET_DATA, data.makerAddress);

        uint maxFromMakerFee = data.makerFee == 0 ? Utils.max_uint() : getPartialAmount(feeAssetAvailable, data.makerFee, data.takerAssetAmount);
        amountToGive = Math.min(Math.min(
            getPartialAmount(makerAssetAvailable, data.makerAssetAmount, data.takerAssetAmount),
            maxFromMakerFee),
            SafeMath.sub(data.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount)
        );
        /* logger.log("Getting amountToGive from ZeroEx arg2: amountToGive", amountToGive); */
    }


/// @notice Perform exchange-specific checks on the given order
/// @dev This should be called to check for payload errors
/// @param data LibOrder.Order struct containing order values
/// @return checksPassed value representing pass or fail

function staticExchangeChecks_(
        OrderData data
    )
        public
        view
        onlySelf
        returns (bool checksPassed)
    {

        // Make sure that:
        //  The order is not expired
        //  Both the maker and taker assets are ERC20 tokens
        //  The taker does not have to pay a fee (we don't support fees yet)
        //  We are permitted to take this order
        //  We are permitted to send this order
        // TODO: Should we check signatures here?
        return (block.timestamp <= data.expirationTimeSeconds &&
                toBytes4(data.takerAssetData, 0) == bytes4(0xf47261b0) &&
                toBytes4(data.makerAssetData, 0) == bytes4(0xf47261b0) &&
                data.takerFee == 0 &&
                (data.takerAddress == address(0x0) || data.takerAddress == address(this)) &&
                (data.senderAddress == address(0x0) || data.senderAddress == address(this))
        );
    }


/// @notice Perform a buy order at the exchange
/// @param data LibOrder.Order struct containing order values
/// @return amountSpentOnOrder the amount that would be spent on the order
/// @return amountReceivedFromOrder the amount that was received from this order

function performBuyOrder_(
        OrderData data
    )
        public
        payable
        onlySelf
        returns (uint256 amountSpentOnOrder, uint256 amountReceivedFromOrder)
    {
        uint256 amountToGiveForOrder = toUint(msg.data, msg.data.length - 32);

        approveAddress(ERC20_ASSET_PROXY, toAddress(data.takerAssetData, 16));

        weth.deposit.value(amountToGiveForOrder)();

        LibFillResults.FillResults memory results = exchange.fillOrder(
            getZeroExOrder(data),
            amountToGiveForOrder,
            data.signature
        );
        require(ERC20SafeTransfer.safeTransfer(toAddress(data.makerAssetData, 16), totlePrimary, results.makerAssetFilledAmount));

        amountSpentOnOrder = results.takerAssetFilledAmount;
        amountReceivedFromOrder = results.makerAssetFilledAmount;
        /* logger.log("Performed buy order on ZeroEx arg2: amountSpentOnOrder, arg3: amountReceivedFromOrder", amountSpentOnOrder, amountReceivedFromOrder); */
    }


// utility functions

function uint2str(uint256 _i) internal pure returns (string memory _uintAsString) {
		if (_i == 0) {
			return "0";
		}
		uint256 j = _i;
		uint256 len;
		while (j != 0) {
			len++;
			j /= 10;
		}
		bytes memory bstr = new bytes(len);
		uint256 k = len;
		while (_i != 0) {
			k = k - 1;
			uint8 temp = (48 + uint8(_i - (_i / 10) * 10));
			bytes1 b1 = bytes1(temp);
			bstr[k] = b1;
			_i /= 10;
		}
		return string(bstr);
	}


/*
 Restricted functions (owner only)
 */

function reject(address applicant)
    public
    onlyOwner
    onlyPendingApplication(applicant)
    {
        applications[applicant].state = ApplicationState.Rejected;

        // protection against function reentry on an overriden transfer() function
        uint contribution = applications[applicant].contribution;
        applications[applicant].contribution = 0;
        applicant.transfer(contribution);

        contributionPending -= contribution;
        contributionRejected += contribution;

        RejectedApplication(applicant, contribution, applications[applicant].id);
    }


/// @notice Add/replace/remove any number of functions and optionally execute
///         a function with delegatecall
/// @param _diamondCut Contains the facet addresses and function selectors
/// @param _init The address of the contract or facet to execute _calldata
/// @param _calldata A function call, including function selector and arguments
///                  _calldata is executed with delegatecall on _init

function diamondCut(
        FacetCut[] calldata _diamondCut,
        address _init,
        bytes calldata _calldata
    ) external override {
        LibDiamond.enforceIsContractOwner();
        LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
        uint256 originalSelectorCount = ds.selectorCount;
        uint256 selectorCount = originalSelectorCount;
        bytes32 selectorSlot;
        // Check if last selector slot is not full
        if (selectorCount % 8 > 0) {
            // get last selectorSlot
            selectorSlot = ds.selectorSlots[selectorCount / 8];
        }
        // loop through diamond cut
        for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) {
            (selectorCount, selectorSlot) = LibDiamond.addReplaceRemoveFacetSelectors(
                selectorCount,
                selectorSlot,
                _diamondCut[facetIndex].facetAddress,
                _diamondCut[facetIndex].action,
                _diamondCut[facetIndex].functionSelectors
            );
        }
        if (selectorCount != originalSelectorCount) {
            ds.selectorCount = uint16(selectorCount);
        }
        // If last selector slot is not full
        if (selectorCount % 8 > 0) {
            ds.selectorSlots[selectorCount / 8] = selectorSlot;
        }
        emit DiamondCut(_diamondCut, _init, _calldata);
        LibDiamond.initializeDiamondCut(_init, _calldata);
    }


/**
 * @param tokenAmount Amount of tokens to swap with bep2
 * @param BNB_Address address of Binance Chain to which to receive the bep2 tokens
 */

function swap(uint tokenAmount, string memory BNB_Address) public returns(bool) {

        bool success = token.transferFrom(msg.sender, owner, tokenAmount);

        if(!success) {
            revert("Transfer of tokens to Swap contract failed.");
        }

        emit Swaped(tokenAmount, BNB_Address);

        return true;

    }


/**
 	* @dev Moves specified amount of tokens from main balance to mining balance
 	* @param _amount An uint256 representing the amount of tokens to transfer to main balance
 	*/

function depositToMiningBalance(uint _amount) public {
		require(balances[msg.sender] >= _amount, "not enough tokens");
		require(getCurrentDayDeposited().add(_amount) <= DAY_MINING_DEPOSIT_LIMIT,
			"Day mining deposit exceeded");
		require(miningTotalDeposited.add(_amount) <= TOTAL_MINING_DEPOSIT_LIMIT,
			"Total mining deposit exceeded");

		balances[msg.sender] = balances[msg.sender].sub(_amount);
		miningBalances[msg.sender] = miningBalances[msg.sender].add(_amount);
		miningTotalDeposited = miningTotalDeposited.add(_amount);
		updateCurrentDayDeposited(_amount);
		lastMiningBalanceUpdateTime[msg.sender] = now;
		emit MiningBalanceUpdated(msg.sender, _amount, true);
	}


/**
 	* @dev Get number of days for reward on mining. Maximum 100 days.
 	* @return An uint256 representing number of days user will get reward for.
 	*/

function getDaysForReward() public view returns (uint rewardDaysNum){
		if(lastMiningBalanceUpdateTime[msg.sender] == 0) {
			return 0;
		} else {
			uint value = (now - lastMiningBalanceUpdateTime[msg.sender]) / (1 days);
			if(value > 100) {
				return 100;
			} else {
				return value;
			}
		}
	}


/**
 	* @dev Calculate current mining reward based on total supply of tokens
 	* @return An uint256 representing reward in percents multiplied by 1000000000
 	*/

function getReward(uint _totalSupply) public pure returns (uint rewardPercent){
		uint rewardFactor = 1000000 * (10 ** uint256(decimals));
		uint decreaseFactor = 41666666;

		if(_totalSupply < 23 * rewardFactor) {
			return 2000000000 - (decreaseFactor.mul(_totalSupply.div(rewardFactor)));
		}

		if(_totalSupply < MAX_SUPPLY) {
			return 1041666666;
		} else {
			return 1000000000;
		}
	}


/**
 * @dev Receive payment in ether and return tokens.
 */

function buyTokens() payable public {
        require(msg.value>=getPrice(),'Tx value cannot be lower than price of 1 token');
        uint256 amount = msg.value.div(getPrice());
        ERC20 erc20 = ERC20(token);
        require(erc20.balanceOf(address(this))>=amount,"Sorry, token vendor does not possess enough tokens for this purchase");
        erc20.transfer(msg.sender,amount);
        emit TokensBought(msg.sender,amount);
    }


/**
 * @dev Receive payment in tokens and return ether.
 * Only usable by accounts which allowed the smart contract to transfer tokens from their account.
 */

function sellTokens(uint256 _amount) public {
        require(_amount>0,'You cannot sell 0 tokens');
        uint256 ethToSend = _amount.mul(getPrice());
        require(address(this).balance>=ethToSend,'Sorry, vendor does not possess enough Ether to trade for your tokens');
        ERC20 erc20 = ERC20(token);
        require(erc20.balanceOf(msg.sender)>=_amount,"You cannot sell more tokens than you own on your balance");
        require(erc20.allowance(msg.sender,address(this))>=_amount,"You need to allow this contract to transfer enough tokens from your account");
        erc20.transferFrom(msg.sender,address(this),_amount);
        msg.sender.transfer(ethToSend);
        emit TokensSold(msg.sender,_amount);
    }


/**
 * @notice Update royalty info for collection
 * @param collection address of the NFT contract
 * @param setter address that sets the receiver
 * @param receiver receiver for the royalty fee
 * @param fee fee (500 = 5%, 1,000 = 10%)
 */

function updateRoyaltyInfoForCollection(
        address collection,
        address setter,
        address receiver,
        uint256 fee
    ) external override onlyOwner {
        require(fee <= royaltyFeeLimit, "Registry: Royalty fee too high");
        _royaltyFeeInfoCollection[collection] = FeeInfo({
            setter: setter,
            receiver: receiver,
            fee: fee
        });

        emit RoyaltyFeeUpdate(collection, setter, receiver, fee);
    }


/**
 * @dev Get random number.
 * @dev Random number calculation depends on block timestamp,
 * @dev difficulty, number and hash.
 *
 * @param min Minimal number.
 * @param max Maximum number.
 * @param time Timestamp.
 * @param difficulty Block difficulty.
 * @param number Block number.
 * @param bHash Block hash.
 */

function randomNumber(
        uint min,
        uint max,
        uint time,
        uint difficulty,
        uint number,
        bytes32 bHash
    )
        public
        pure
        returns (uint)
    {
        min ++;
        max ++;

        uint random = uint(keccak256(
            time *
            difficulty *
            number *
            uint(bHash)
        ))%10 + 1;

        uint result = uint(keccak256(random))%(min+max)-min;

        if (result > max) {
            result = max;
        }

        if (result < min) {
            result = min;
        }

        result--;

        return result;
    }


/**
 * @dev Start the new game.
 * @dev Checks ticket price changes, if exists new ticket price the price will be changed.
 * @dev Checks game status changes, if exists request for changing game status game status
 * @dev will be changed.
 */

function startGame() internal {
        require(isActive);

        game = block.number;
        if (newPrice != 0) {
            ticketPrice = newPrice;
            newPrice = 0;
        }
        if (toogleStatus) {
            isActive = !isActive;
            toogleStatus = false;
        }
        emit Game(game, now);
    }


/**
 * @dev Pick the winner.
 * @dev Check game players, depends on player count provides next logic:
 * @dev - if in the game is only one player, by game rules the whole jackpot
 * @dev without commission returns to him.
 * @dev - if more than one player smart contract randomly selects one player,
 * @dev calculates commission and after that jackpot transfers to the winner,
 * @dev commision to founders.
 */

function pickTheWinner() internal {
        uint winner;
        uint toPlayer;
        if (players[game].length == 1) {
            toPlayer = jackpot[game];
            players[game][0].transfer(jackpot[game]);
            winner = 0;
        } else {
            winner = randomNumber(
                0,
                players[game].length - 1,
                block.timestamp,
                block.difficulty,
                block.number,
                blockhash(block.number - 1)
            );

            uint distribute = jackpot[game] * fee / 100;
            toPlayer = jackpot[game] - distribute;
            players[game][winner].transfer(toPlayer);

            transferToPartner(players[game][winner]);

            distribute -= paidToPartners;
            bool result = address(fundsDistributor).call.gas(30000).value(distribute)();
            if (!result) {
                revert();
            }
        }

        paidToPartners = 0;
        stats.newWinner(
            players[game][winner],
            game,
            players[game].length,
            toPlayer,
            gType,
            winner
        );

        allTimeJackpot += toPlayer;
        allTimePlayers += players[game].length;
    }


/**
 * @dev Checks if the player is in referral system.
 * @dev Sending earned ether to partners.
 *
 * @param partner Partner address.
 * @param referral Player address.
 */

function processReferralSystem(address partner, address referral)
        internal
    {
        address partnerRef = referralInstance.getPartnerByReferral(referral);
        if (partner != address(0) || partnerRef != address(0)) {
            if (partnerRef == address(0)) {
                referralInstance.addReferral(partner, referral);
                partnerRef = partner;
            }

            if (players[game].length > 1) {
                transferToPartner(referral);
            }
        }
    }


/**
 * @dev Sending earned ether to partners.
 *
 * @param referral Player address.
 */

function transferToPartner(address referral) internal {
        address partner = referralInstance.getPartnerByReferral(referral);
        if (partner != address(0)) {
            uint sum = getPartnerAmount(partner);
            if (sum != 0) {
                partner.transfer(sum);
                paidToPartners += sum;

                emit ToPartner(partner, referral, sum, now);

                transferToSalesPartner(partner);
            }
        }
    }


/**
 * @dev Sending earned ether to sales partners.
 *
 * @param partner Partner address.
 */

function transferToSalesPartner(address partner) internal {
        address salesPartner = referralInstance.getSalesPartner(partner);
        if (salesPartner != address(0)) {
            uint sum = getSalesPartnerAmount(partner);
            if (sum != 0) {
                salesPartner.transfer(sum);
                paidToPartners += sum;

                emit ToSalesPartner(salesPartner, partner, sum, now);
            }
        }
    }


/**
 * Uses the Curve protocol to convert the wbtc asset into to mixed renwbtc token.
 */

function mixFromWBTC() internal {
    uint256 wbtcBalance = IERC20(wbtc).balanceOf(address(this));
    if (wbtcBalance > 0) {
      IERC20(wbtc).safeApprove(curve, 0);
      IERC20(wbtc).safeApprove(curve, wbtcBalance);
      // we can accept 0 as minimum because this is called only by a trusted role
      uint256 minimum = 0;
      uint256[2] memory coinAmounts = wrapCoinAmount(wbtcBalance);
      ICurveFiWbtc(curve).add_liquidity(
        coinAmounts, minimum
      );
    }
    // now we have the mixed token
  }


/**
 * Uses the Curve protocol to convert the mixed token back into the wbtc asset. If it cannot
 * acquire the limit amount, it will acquire the maximum it can.
 */

function mixToWBTC(uint256 wbtcLimit) internal {
    uint256 mixTokenBalance = IERC20(mixToken).balanceOf(address(this));

    // this is the maximum number of wbtc we can get for our mixed token
    uint256 wbtcMaximumAmount = wbtcValueFromMixToken(mixTokenBalance);
    if (wbtcMaximumAmount == 0) {
      return;
    }

    if (wbtcLimit < wbtcMaximumAmount) {
      // we want less than what we can get, we ask for the exact amount
      // now we can remove the liquidity
      uint256[2] memory tokenAmounts = wrapCoinAmount(wbtcLimit);
      IERC20(mixToken).safeApprove(curve, 0);
      IERC20(mixToken).safeApprove(curve, mixTokenBalance);
      ICurveFiWbtc(curve).remove_liquidity_imbalance(
        tokenAmounts, mixTokenBalance
      );
    } else {
      // we want more than we can get, so we withdraw everything
      IERC20(mixToken).safeApprove(curve, 0);
      IERC20(mixToken).safeApprove(curve, mixTokenBalance);
      ICurveFiWbtc(curve).remove_liquidity_one_coin(mixTokenBalance, int128(tokenIndex), 0);
    }
    // now we have wbtc asset
  }


/**
 * Withdraws an wbtc asset from the strategy to the vault in the specified amount by asking
 * by removing imbalanced liquidity from the Curve protocol. The rest is deposited back to the
 * Curve protocol pool. If the amount requested cannot be obtained, the method will get as much
 * as we have.
 */

function withdrawToVault(uint256 amountWbtc) external restricted {
    // withdraw all from gauge
    Gauge(gauge).withdraw(Gauge(gauge).balanceOf(address(this)));
    // convert the mix to WBTC, but get at most amountWbtc
    mixToWBTC(amountWbtc);
    // we can transfer the asset to the vault
    uint256 actualBalance = IERC20(wbtc).balanceOf(address(this));
    if (actualBalance > 0) {
      IERC20(wbtc).safeTransfer(vault, Math.min(amountWbtc, actualBalance));
    }

    // invest back the rest
    investAllUnderlying();
  }


/**
 * Withdraws all assets from the vault.
 */

function withdrawAllToVault() external restricted {
    // withdraw all from gauge
    Gauge(gauge).withdraw(Gauge(gauge).balanceOf(address(this)));
    // convert the mix to WBTC, we want the entire balance
    mixToWBTC(uint256(~0));
    // we can transfer the asset to the vault
    uint256 actualBalance = IERC20(wbtc).balanceOf(address(this));
    if (actualBalance > 0) {
      IERC20(wbtc).safeTransfer(vault, actualBalance);
    }
  }


/**
 * Invests all wbtc assets into our mixToken vault.
 */

function investAllUnderlying() internal {
    // convert the entire balance not yet invested into mixToken first
    mixFromWBTC();

    // then deposit into the mixToken vault
    uint256 mixTokenBalance = IERC20(mixToken).balanceOf(address(this));
    if (mixTokenBalance > 0) {
      IERC20(mixToken).safeApprove(gauge, 0);
      IERC20(mixToken).safeApprove(gauge, mixTokenBalance);
      Gauge(gauge).deposit(mixTokenBalance);
    }
  }


/**
 * Salvages a token. We cannot salvage mixToken tokens, CRV, or wbtc assets.
 */

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


/**
 * Returns the wbtc invested balance. The is the wbtc amount in this stragey, plus the gauge
 * amount of the mixed token converted back to wbtc.
 */

function investedUnderlyingBalance() public view returns (uint256) {
    uint256 gaugeBalance = Gauge(gauge).balanceOf(address(this));
    uint256 wbtcBalance = IERC20(wbtc).balanceOf(address(this));
    if (gaugeBalance == 0) {
      // !!! if we have 0 balance in gauge, the conversion to wbtc reverts in Curve
      // !!! this if-statement is necessary to avoid transaction reverts
      return wbtcBalance;
    }
    uint256 investedBalance = wbtcValueFromMixToken(gaugeBalance);
    return investedBalance.add(wbtcBalance);
  }


/**
 * Claims the CRV crop, converts it to WBTC/renWBTC on Uniswap
 */

function claimAndLiquidateCrv() internal {
    if (!sell) {
      // Profits can be disabled for possible simplified and rapid exit
      emit ProfitsNotCollected();
      return;
    }
    Mintr(mintr).mint(gauge);
    // claiming rewards and liquidating them
    uint256 crvBalance = IERC20(crv).balanceOf(address(this));
    emit Liquidating(crvBalance);
    if (crvBalance > sellFloor) {
      uint256 wbtcBalanceBefore = IERC20(wbtc).balanceOf(address(this));
      IERC20(crv).safeApprove(uni, 0);
      IERC20(crv).safeApprove(uni, crvBalance);
      // we can accept 1 as the minimum because this will be called only by a trusted worker
      IUniswapV2Router02(uni).swapExactTokensForTokens(
        crvBalance, 1, uniswap_CRV2WBTC, address(this), block.timestamp
      );

      // now we have WBTC
      notifyProfit(wbtcBalanceBefore, IERC20(wbtc).balanceOf(address(this)));
    }
  }


/**
 * Allows user to destroy a specified token
 * This would allow a user to claim his prize for the destroyed token
 * @param _tokenId uint256 ID of the token
 */

function claimReward(uint256 _tokenId) public {
        require(winningTokenId > 0, "The is not winner yet!");
        require(_tokenId == winningTokenId, "This token is not the winner!");

        ensureAddressIsTokenOwner(msg.sender, _tokenId);

        winnerAddress = msg.sender;

        emit RewardIsClaimed(msg.sender, _tokenId);
    }


/**
 * Allows the buyer of at least the number of WLC tokens, specified in WLCRewardAmount
 * to receive a DCC as a bonus.
 * This can only be called by the deployed WLC contract, by the address specified in WLCAdress
 * @param _boughtWLCAmount uint256 the number of bought WLC tokens
 * @param _owner address the address of the buyer
 */

function getWLCReward(uint256 _boughtWLCAmount, address _owner) public returns (uint256 _remaining) {
        if (WLCAdress != address(0) && WLCRewardAmount > 0 && _boughtWLCAmount >= WLCRewardAmount) {
            require(WLCAdress == msg.sender, "You cannot invoke this function directly!");

            uint256 DCCAmount = scalePurchaseTokenAmountToMatchRemainingTokens(_boughtWLCAmount / WLCRewardAmount);

            if (DCCAmount > 0) {
                _addTokensToAddress(_owner, DCCAmount);

                emit Buy(_owner, DCCAmount, nextTokenId - DCCAmount, nextTokenId - 1);

                chooseWinner();

                return _boughtWLCAmount - (DCCAmount * WLCRewardAmount);
            }
        }

        return _boughtWLCAmount;
    }


/**
 * Allows an onwer of WLC token to excange it for DCC token
 * This can only be called by the deployed WLC contract, by the address specified in WLCAdress
 * @param _owner address the address of the exchanger
 */

function getForWLC(address _owner) public {
        require(WLCAdress == msg.sender, "You cannot invoke this function directly!");

        require(nextTokenId <= totalTokenSupply, "Not enough tokens are available for purchase!");

        _addTokensToAddress(_owner, 1);

        emit Buy(_owner, 1, nextTokenId - 1, nextTokenId - 1);

        chooseWinner();
    }


/**
 * @dev apply BDR exchange Abc
 * @param _value uint256 amount of BDR to exchange
 * @return uint256 the sequence of exchange request
 */

function applyExchangeToken(uint256 _value) public whenNotPaused returns (uint256) {
        uint256 trxSeq = applyCounts;
        require(exchangeTrx[trxSeq].from == address(0),"trxSeq already exist");
        require(balances[msg.sender] >= _value);
        exchangeTrx[trxSeq].executed = false;
        exchangeTrx[trxSeq].from = msg.sender;
        exchangeTrx[trxSeq].value = _value;
        applyCounts = applyCounts.add(1);
        balances[address(this)] = balances[address(this)].add(_value);
        balances[exchangeTrx[trxSeq].from] = balances[exchangeTrx[trxSeq].from].sub(_value);
        exchangeLock[exchangeTrx[trxSeq].from] = exchangeLock[exchangeTrx[trxSeq].from].add(_value);
        emit ApplyExchangeToken(exchangeTrx[trxSeq].from,exchangeTrx[trxSeq].value,trxSeq);
        emit Transfer(msg.sender,address(this),_value);
        return trxSeq;
    }


/**
 * @dev signer confirms one exchange request
 * @param _trxSeq uint256 the Sequence of exchange request
 */

function confirmExchangeTrx(uint256 _trxSeq) public onlySigner {
        require(exchangeTrx[_trxSeq].from != address(0),"_trxSeq not exist");
        require(exchangeTrx[_trxSeq].signers.length < requestSigners,"trx already has enough signers");
        require(exchangeTrx[_trxSeq].executed == false,"trx already executed");
        require(isConfirmer(_trxSeq, msg.sender) == false,"signer already confirmed");
        exchangeTrx[_trxSeq].signers.push(msg.sender);
        emit ConfirmTrx(msg.sender, _trxSeq);
    }


/**
 * @dev signer cancel confirmed exchange request
 * @param _trxSeq uint256 the Sequence of exchange request
 */

function cancelConfirm(uint256 _trxSeq) public onlySigner {
        require(exchangeTrx[_trxSeq].from != address(0),"_trxSeq not exist");
        require(isConfirmer(_trxSeq, msg.sender),"Signer didn't confirm");
        require(exchangeTrx[_trxSeq].executed == false,"trx already executed");
        uint256 len = exchangeTrx[_trxSeq].signers.length;
        for(uint256 i = 0;i < len;i++){
            if(exchangeTrx[_trxSeq].signers[i] == msg.sender){
                exchangeTrx[_trxSeq].signers[i] = exchangeTrx[_trxSeq].signers[len.sub(1)] ;
                exchangeTrx[_trxSeq].signers.length --;
                break;
            }
        }
        emit CancleConfirmTrx(msg.sender,_trxSeq);
    }


/**
 * @dev execute exchange request which confirmed by enough signers
 * @param _trxSeq uint256 the Sequence of exchange request
 */

function executeExchangeTrx(uint256 _trxSeq) public whenNotPaused{
        address from = exchangeTrx[_trxSeq].from;
        uint256 value = exchangeTrx[_trxSeq].value;
        require(from != address(0),"trxSeq not exist");
        require(exchangeTrx[_trxSeq].executed == false,"trxSeq has executed");
        require(exchangeTrx[_trxSeq].signers.length >= requestSigners);
        require(from == msg.sender|| isSigner[msg.sender]);
        require(value <= balances[address(this)]);
        _burn(address(this), value);
        exchangeLock[from] = exchangeLock[from].sub(value);
        exchangeTrx[_trxSeq].executed = true;
        AbcInterface(AbcInstance).tokenFallback(from,value,bytes(""));
        emit TokenExchange(exchangeTrx[_trxSeq].from,exchangeTrx[_trxSeq].value,false);
    }


/**
 *  @dev Contract initialization
 *  @param _ownerAddress address Token owner address
 *  @param _startTime uint256 Crowdsale end time
 *
 */

function SmartCityToken(address _ownerAddress, uint256 _startTime) public {
        owner = _ownerAddress; // token Owner
        startTime = _startTime; // token Start Time
        unlockOwnerDate = startTime + 2 years;
        balances[owner] = totalSupply; // all tokens are initially allocated to token owner
    }


/**
 *  @dev Workaround for vulnerability described here: https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM
 */

function _approve(address _spender, uint256 _value) internal returns(bool success) {
        require((_value == 0) || (allowances[msg.sender][_spender] == 0));

        allowances[msg.sender][_spender] = _value; // Set spender allowance

        Approval(msg.sender, _spender, _value); // Trigger Approval event
        return true;
    }


/**
 *  @dev Burns all the tokens which has not been sold during ICO
 */

function burn() public {
        if (!burned && now > startTime) {
            uint256 diff = balances[owner].sub(amountReserved); // Get the amount of unsold tokens

            balances[owner] = amountReserved;
            totalSupply = totalSupply.sub(diff); // Reduce total provision number

            burned = true;
            Burned(diff); // Trigger Burned event
        }
    }


/**
 * @notice Deploys token
 * @param _symbol Token symbol
 * @param _name Token name
 * @param _decimals Token decimals
 * @param _totalSupply Token total supply
 * @param _tokenOwner Owner of the deployed token
 * @param _tokenRegistry Token Registry
 */

function deployToken(
        string calldata _symbol,
        string calldata _name,
        uint8 _decimals,
        uint256 _totalSupply,
        address _tokenOwner,
        address _tokenRegistry,
        bool _saveHoldersHistory,
        address _allowable,
        address _getter
    )
        onlyOwner //owner - TokenRegistry
        external
        returns (address)
    {
        address tokenAddress = address(new InveniamToken(
            _symbol,
            _name,
            _decimals,
            _totalSupply * uint256(10)**_decimals,
            _tokenOwner,
            _tokenRegistry,
            _saveHoldersHistory,
            _allowable,
            _getter
        ));
        Allowable(tokenAddress).transferOwnership(_tokenOwner);
        return tokenAddress;
    }


// Stake SYRUP tokens to StakingPool

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

        // require (_amount.add(user.amount) <= maxStaking, 'exceed max stake');

        updatePool(0);
        if (user.amount > 0) {
            uint256 pending = user.amount.mul(pool.accCakePerShare).div(1e12).sub(user.rewardDebt);
            if(pending > 0) {
                rewardToken.safeTransfer(address(msg.sender), pending);
            }
        }
        if(_amount > 0) {
            pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
            user.amount = user.amount.add(_amount);
            totalStaked = totalStaked.add(_amount);
        }
        user.rewardDebt = user.amount.mul(pool.accCakePerShare).div(1e12);

        emit Deposit(msg.sender, _amount);
    }


/// @notice Reserved for owner to mint

function ownerMint(address to, uint amount) public onlyOwner {

    uint mintsRemaining = ownerMintsRemaining;

    /// @notice Owner mints cannot be minted after the maximum has been reached
    require(mintsRemaining > 0, "Groupies: Max owner mint limit reached");

    if (amount > mintsRemaining){
      amount = mintsRemaining;
    }

    uint currentTotalSupply = totalSupply;

    _mintAmountTo(to, amount, currentTotalSupply);

    ownerMintsRemaining = mintsRemaining - amount;

    totalSupply = currentTotalSupply + amount;
  }


/**
 * @notice Adds given role to the user
 * @param _user Address of user wallet
 * @param _role Role name
 * param _withApproval Flag whether we need an approval
 */

function addRole(
        address _user,
        bytes32 _role,
        bool _withApproval,
        bool _withSameRole,
        bytes32[] storage roleNames,
        mapping(bytes32 => mapping(address => AddressList.Data)) storage roleUserData,
        mapping(bytes32 => address[]) storage roleUsers,
        mapping(address => mapping(bytes32 => address)) storage addRoleInitiators
    )
        public
    {
        if (_withApproval) {
            _checkRoleLevel(_role, _withSameRole, roleUserData);
            _checkInitiator(addRoleInitiators[_user][_role]);
        }
        require(isExists(_role, roleNames), ERROR_ROLE_NOT_FOUND);
        _user.addTo(roleUserData[_role], roleUsers[_role]);
        emit RoleAdded(_user, _role);
        if (_withApproval) {
            delete addRoleInitiators[_user][_role];
        }
    }


/**
 * @notice Requests to add given role to the user
 * @param _user Address of user wallet
 * @param _role Role name
 */

function addRoleRequest(
        address _user,
        bytes32 _role,
        bool _withSameRole,
        mapping(bytes32 => mapping(address => AddressList.Data)) storage roleUserData,
        mapping(address => mapping(bytes32 => address)) storage addRoleInitiators
    )
        public
    {
        _checkRoleLevel(_role, _withSameRole, roleUserData);
        addRoleInitiators[_user][_role] = msg.sender;
        emit RoleAddingRequested(_user, _role);
    }


/**
 * @notice Removes given role from the user
 * @param _user Address of user wallet
 * @param _role Role name
 */

function removeRole(
        address _user,
        bytes32 _role,
        bool _withApproval,
        bool _withSameRole,
        bytes32[] storage roleNames,
        mapping(bytes32 => mapping(address => AddressList.Data)) storage roleUserData,
        mapping(bytes32 => address[]) storage roleUsers,
        mapping(address => mapping(bytes32 => address)) storage removeRoleInitiators
    )
        public
    {
        if (_withApproval) {
            _checkRoleLevel(_role, _withSameRole, roleUserData);
            _checkInitiator(removeRoleInitiators[_user][_role]);
        }
        require(isExists(_role, roleNames), ERROR_ROLE_NOT_FOUND);
        _user.removeFrom(roleUserData[_role], roleUsers[_role]);
        emit RoleRemoved(_user, _role);
        if (_withApproval) {
            delete removeRoleInitiators[_user][_role];
        }
    }


/**
 * @notice Requests to remove given role to the user
 * @param _user Address of user wallet
 * @param _role Role name
 */

function removeRoleRequest(
        address _user,
        bytes32 _role,
        bool _withSameRole,
        mapping(bytes32 => mapping(address => AddressList.Data)) storage roleUserData,
        mapping(address => mapping(bytes32 => address)) storage removeRoleInitiators
    )
        public
    {
        _checkRoleLevel(_role, _withSameRole, roleUserData);
        removeRoleInitiators[_user][_role] = msg.sender;
        emit RoleRemovingRequested(_user, _role);
    }


/**
 * @notice Checks whether the role has been already added
 * @param _role Role name
 */

function isExists(
        bytes32 _role,
        bytes32[] storage roleNames
    )
        private
        view
        returns (bool)
    {
        for (uint i = 0; i < roleNames.length; i++) {
            if (_role == roleNames[i]) {
                return true;
            }
        }
        return false;
    }


/**
 * @dev Override the base isApprovedForAll(address owner, address operator) to allow us to
 *      specifically allow the opense operator to transfer tokens
 *
 * @param owner_ owner address
 * @param operator_ operator address
 * @return bool if the operator is approved for all
 */

function isApprovedForAll(
        address owner_,
        address operator_
    ) public view override returns (bool) {
        // Whitelist OpenSea proxy contract for easy trading.
        ProxyRegistry proxyRegistry = ProxyRegistry(_proxyAddress);
        if (address(proxyRegistry.proxies(owner_)) == operator_) {
            return true;
        }
        return ERC1155.isApprovedForAll(owner_, operator_);
    }


// View function to see pending JINs on frontend.

function pendingJin(uint256 _pid, address _user)
        external
        view
        returns (uint256)
    {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][_user];
        uint256 accJinPerShare = pool.accJinPerShare;
        uint256 lpSupply = pool.lpToken.balanceOf(address(this));
        if (block.number > pool.lastRewardBlock && lpSupply != 0) {
            uint256 multiplier = getMultiplier(
                pool.lastRewardBlock,
                block.number
            );
            uint256 jinReward = multiplier
                .mul(jinPerBlock)
                .mul(pool.allocPoint)
                .div(totalAllocPoint);
            accJinPerShare = accJinPerShare.add(
                jinReward.mul(1e12).div(lpSupply)
            );
        }
        return
            user.amount.mul(accJinPerShare).div(1e12).sub(user.rewardDebt);
    }


/// @notice Withdraws the tokens that have been deposited
/// Only `withdrawer` can call this.
/// @param _to The address where the withdrawn tokens should go

function withdraw(address payable _to) external {
        uint256 balance = token.balanceOf(address(this));
        require(msg.sender == withdrawer, "the caller is not the withdrawer");
        require(block.timestamp >= release_at || service_registry.deprecated(), "deposit not released yet");
        require(balance > 0, "nothing to withdraw");
        require(token.transfer(_to, balance), "token didn't transfer");
        selfdestruct(_to); // The contract can disappear.
    }


/**
 * Set fee parameters.
 *
 * @param _fixedFee fixed fee in token units
 * @param _minVariableFee minimum variable fee in token units
 * @param _maxVariableFee maximum variable fee in token units
 * @param _variableFeeNumerator variable fee numerator
 */

function setFeeParameters (
    uint256 _fixedFee,
    uint256 _minVariableFee,
    uint256 _maxVariableFee,
    uint256 _variableFeeNumerator) public delegatable payable {
    require (msg.sender == owner);

    require (_minVariableFee <= _maxVariableFee);
    require (_variableFeeNumerator <= MAX_FEE_NUMERATOR);

    fixedFee = _fixedFee;
    minVariableFee = _minVariableFee;
    maxVariableFee = _maxVariableFee;
    variableFeeNumerator = _variableFeeNumerator;

    FeeChange (
      _fixedFee, _minVariableFee, _maxVariableFee, _variableFeeNumerator);
  }


/**
 * Calculate fee for transfer of given number of tokens.
 *
 * @param _amount transfer amount to calculate fee for
 * @return fee for transfer of given amount
 */

function calculateFee (uint256 _amount)
    public delegatable view returns (uint256 _fee) {
    require (_amount <= MAX_TOKENS_COUNT);

    _fee = safeMul (_amount, variableFeeNumerator) / FEE_DENOMINATOR;
    if (_fee < minVariableFee) _fee = minVariableFee;
    if (_fee > maxVariableFee) _fee = maxVariableFee;
    _fee = safeAdd (_fee, fixedFee);
  }


/**
 * @dev Validation of an incoming purchase. Use require statements to revert state when conditions are not met.
 * @param beneficiary Address performing the token purchase
 * @param weiAmount Value in wei involved in the purchase
 */

function _preValidatePurchase(address beneficiary, uint256 weiAmount) internal {
        require(beneficiary != address(0), "beneficiary is the zero address");
        require(weiAmount != 0, "weiAmount is 0");
        require(weiAmount >= minDeposit && weiAmount <= maxDeposit, "Amount should be within 0.05 eth and 1 eth");
        require(weiRaised().add(weiAmount) <= fundingTarget, "Crowdsale goal reached");

        uint256 _existingbalance = balances[beneficiary];
        uint256 _newBalance = _existingbalance.add(weiAmount);
        require(_newBalance <= maxDeposit, "Maximum deposit exceeded!!!");

        balances[beneficiary] = _newBalance;
    }


/**
 *
 * @dev Kickstart the ICO by setting the opning time and manually seting the closing time to a week after the opening time.
 */

function startIco( uint256 _openingTime) external onlyAdmin {
        require(_openingTime >= block.timestamp, "JPEGVaultICO: opening time is before current block timestamp");
        require(openingTime == 0 || hasClosed(), "You can't restart ICO in the middle of sales period");
        openingTime = _openingTime;
        closingTime = openingTime + 1 weeks;
     }


// This is used to prevent stack too deep errors

function calculateFee(
        int128 _gLiq,
        int128[] memory _bals,
        Storage.Curve storage curve,
        int128[] memory _weights
    ) internal view returns (int128 psi_) {
        int128 _beta = curve.beta;
        int128 _delta = curve.delta;

        psi_ = calculateFee(_gLiq, _bals, _beta, _delta, _weights);
    }


// Admin calls this function.

function setPoolInfo(
        uint256 _poolId,
        address _vault,
        IERC20 _token,
        uint256 _allocPoint,
        bool _withUpdate
    ) external onlyOwner {
        if (_withUpdate) {
            massUpdatePools();
        }

        if (_poolId >= poolLength) {
            poolLength = _poolId + 1;
        }

        vaultMap[_poolId] = _vault;

        totalAllocPoint = totalAllocPoint.sub(poolMap[_vault].allocPoint).add(_allocPoint);
        poolMap[_vault].allocPoint = _allocPoint;

        _token.approve(sodaMaster.pool(), type(uint256).max);
    }


// ===== Admin functions ===== //

function setFeeSchedule(
        uint _flatFee,
        uint _contractFee,
        uint _exerciseFee,
        uint _settlementFee
    ) public auth {
        flatFee = _flatFee;
        contractFee = _contractFee;
        exerciseFee = _exerciseFee;
        settlementFee = _settlementFee;

        require(contractFee < 5 ether);
        require(flatFee < 6.95 ether);
        require(exerciseFee < 20 ether);
        require(settlementFee < 20 ether);
    }


// ========== PUT OPTIONS EXCHANGE ========== //

function putBtoWithSto(
        uint    amount,
        uint    expiration,
        bytes32 nonce,
        uint    price,
        uint    size,
        uint    strike,
        uint    validUntil,
        bytes32 r,
        bytes32 s,
        uint8   v
    ) public hasFee(amount) {
        bytes32 h = keccak256(Action.SellPutToOpen, expiration, nonce, price, size, strike, validUntil, this);
        address maker = _getMaker(h, v, r, s);

        _validateOrder(amount, expiration, h, maker, price, validUntil, size, strike);
        _buyPutToOpen(amount, expiration, price, strike, msg.sender);
        _sellPutToOpen(amount, expiration, price, strike, maker);
    }


// Deposit tokens to Pool for Token allocation.

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

        updatePool(_pid);
        if (user.amount > 0) {
            uint256 pending = user.amount.mul(pool.accTokenPerShare).div(1e12).sub(user.rewardDebt);
            safeTokenTransfer(msg.sender, pending);
        }
        if(_amount > 0) { //kevin
            pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
            user.amount = user.amount.add(_amount);
        }
        user.rewardDebt = user.amount.mul(pool.accTokenPerShare).div(1e12);
        emit Deposit(msg.sender, _pid, _amount);
    }


// DepositFor tokens to Pool for Token allocation.

function depositFor(address _beneficiary, uint256 _pid, uint256 _amount) public {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][_beneficiary];
        updatePool(_pid);
        if (user.amount > 0) {
            uint256 pending = user.amount.mul(pool.accTokenPerShare).div(1e12).sub(user.rewardDebt);
            safeTokenTransfer(_beneficiary, pending);
        }
        if(_amount > 0) { //kevin
            pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
            user.amount = user.amount.add(_amount);
        }
        user.rewardDebt = user.amount.mul(pool.accTokenPerShare).div(1e12);
        emit Deposit(_beneficiary, _pid, _amount);
    }


/**
 	 * @dev Store the PublicKey info for a Name
 	 * @param _id The ID of the Name
 	 * @param _defaultKey The default public key for this Name
 	 * @param _writerKey The writer public key for this Name
 	 * @return true on success
 	 */

function initialize(address _id, address _defaultKey, address _writerKey)
		external
		isName(_id)
		keyNotTaken(_defaultKey)
		keyNotTaken(_writerKey)
		onlyFactory returns (bool) {
		require (!isExist(_id));

		keyToNameId[_defaultKey] = _id;
		if (_defaultKey != _writerKey) {
			keyToNameId[_writerKey] = _id;
		}
		PublicKey storage _publicKey = publicKeys[_id];
		_publicKey.created = true;
		_publicKey.defaultKey = _defaultKey;
		_publicKey.writerKey = _writerKey;
		_publicKey.keys.push(_defaultKey);
		if (_defaultKey != _writerKey) {
			_publicKey.keys.push(_writerKey);
		}
		return true;
	}


/**
 	 * @dev Add publicKey to list for a Name
 	 * @param _id The ID of the Name
 	 * @param _key The publicKey to be added
 	 * @param _nonce The signed uint256 nonce (should be Name's current nonce + 1)
 	 * @param _signatureV The V part of the signature
 	 * @param _signatureR The R part of the signature
 	 * @param _signatureS The S part of the signature
 	 */

function addKey(address _id,
		address _key,
		uint256 _nonce,
		uint8 _signatureV,
		bytes32 _signatureR,
		bytes32 _signatureS
	) public isName(_id) onlyAdvocate(_id) keyNotTaken(_key) senderNameNotCompromised {
		require (_nonce == _nameFactory.nonces(_id).add(1));
		bytes32 _hash = keccak256(abi.encodePacked(address(this), _id, _key, _nonce));
		require (ecrecover(_hash, _signatureV, _signatureR, _signatureS) == _key);
		require (_addKey(_id, _key));
	}


/**
 	 * @dev Get list of publicKeys of a Name
 	 * @param _id The ID of the Name
 	 * @param _from The starting index
 	 * @param _to The ending index
 	 * @return list of publicKeys
 	 */

function getKeys(address _id, uint256 _from, uint256 _to) public isName(_id) view returns (address[] memory) {
		require (isExist(_id));
		require (_from >= 0 && _to >= _from);

		PublicKey memory _publicKey = publicKeys[_id];
		require (_publicKey.keys.length > 0);

		if (_to >  _publicKey.keys.length.sub(1)) {
			_to = _publicKey.keys.length.sub(1);
		}
		address[] memory _keys = new address[](_to.sub(_from).add(1));

		for (uint256 i = _from; i <= _to; i++) {
			_keys[i.sub(_from)] = _publicKey.keys[i];
		}
		return _keys;
	}


/**
 	 * @dev Remove publicKey from the list
 	 * @param _id The ID of the Name
 	 * @param _key The publicKey to be removed
 	 */

function removeKey(address _id, address _key) public isName(_id) onlyAdvocate(_id) senderNameNotCompromised {
		require (this.isKeyExist(_id, _key));

		PublicKey storage _publicKey = publicKeys[_id];

		// Can't remove default key
		require (_key != _publicKey.defaultKey);
		// Can't remove writer key
		require (_key != _publicKey.writerKey);
		// Has to have at least defaultKey/writerKey
		require (_publicKey.keys.length > 1);

		keyToNameId[_key] = address(0);

		uint256 index;
		for (uint256 i = 0; i < _publicKey.keys.length; i++) {
			if (_publicKey.keys[i] == _key) {
				index = i;
				break;
			}
		}

		for (uint256 i = index; i < _publicKey.keys.length.sub(1); i++) {
			_publicKey.keys[i] = _publicKey.keys[i+1];
		}
		_publicKey.keys.length--;

		uint256 _nonce = _nameFactory.incrementNonce(_id);
		require (_nonce > 0);

		emit RemoveKey(_id, _key, _nonce);
	}


/**
 	 * @dev Set a publicKey as the default for a Name
 	 * @param _id The ID of the Name
 	 * @param _defaultKey The defaultKey to be set
 	 * @param _signatureV The V part of the signature for this update
 	 * @param _signatureR The R part of the signature for this update
 	 * @param _signatureS The S part of the signature for this update
 	 */

function setDefaultKey(address _id, address _defaultKey, uint8 _signatureV, bytes32 _signatureR, bytes32 _signatureS) public isName(_id) onlyAdvocate(_id) senderNameNotCompromised {
		require (this.isKeyExist(_id, _defaultKey));

		bytes32 _hash = keccak256(abi.encodePacked(address(this), _id, _defaultKey));
		require (ecrecover(_hash, _signatureV, _signatureR, _signatureS) == msg.sender);

		PublicKey storage _publicKey = publicKeys[_id];
		_publicKey.defaultKey = _defaultKey;

		uint256 _nonce = _nameFactory.incrementNonce(_id);
		require (_nonce > 0);
		emit SetDefaultKey(_id, _defaultKey, _nonce);
	}


/**
 	 * @dev Set a publicKey as the writer for a Name
 	 * @param _id The ID of the Name
 	 * @param _writerKey The writerKey to be set
 	 * @param _signatureV The V part of the signature for this update
 	 * @param _signatureR The R part of the signature for this update
 	 * @param _signatureS The S part of the signature for this update
 	 */

function setWriterKey(address _id, address _writerKey, uint8 _signatureV, bytes32 _signatureR, bytes32 _signatureS) public isName(_id) onlyAdvocate(_id) senderNameNotCompromised {
		bytes32 _hash = keccak256(abi.encodePacked(address(this), _id, _writerKey));
		require (ecrecover(_hash, _signatureV, _signatureR, _signatureS) == msg.sender);
		require (_setWriterKey(_id, _writerKey));
	}


/**
 	 * @dev Actual adding the publicKey to list for a Name
 	 * @param _id The ID of the Name
 	 * @param _key The publicKey to be added
 	 * @return true on success
 	 */

function _addKey(address _id, address _key) internal returns (bool) {
		require (!this.isKeyExist(_id, _key));

		keyToNameId[_key] = _id;

		PublicKey storage _publicKey = publicKeys[_id];
		_publicKey.keys.push(_key);

		uint256 _nonce = _nameFactory.incrementNonce(_id);
		require (_nonce > 0);

		emit AddKey(_id, _key, _nonce);
		return true;
	}


/**
 	 * @dev Actual setting the writerKey for a Name
 	 * @param _id The ID of the Name
 	 * @param _writerKey The writerKey to be set
 	 * @return true on success
 	 */

function _setWriterKey(address _id, address _writerKey) internal returns (bool) {
		require (this.isKeyExist(_id, _writerKey));

		PublicKey storage _publicKey = publicKeys[_id];
		_publicKey.writerKey = _writerKey;

		uint256 _nonce = _nameFactory.incrementNonce(_id);
		require (_nonce > 0);
		emit SetWriterKey(_id, _writerKey, _nonce);
		return true;
	}


/**
 *  @notice initializes bond parameters
 *  @param _controlVariable uint
 *  @param _vestingTerm uint
 *  @param _minimumPrice uint
 *  @param _maxPayout uint
 *  @param _fee uint
 *  @param _maxDebt uint
 *  @param _initialDebt uint
 */

function initializeBondTerms(
        uint _controlVariable,
        uint _vestingTerm,
        uint _minimumPrice,
        uint _maxPayout,
        uint _fee,
        uint _maxDebt,
        uint _initialDebt
    ) external onlyPolicy() {
        require( terms.controlVariable == 0, "Bonds must be initialized from 0" );
        terms = Terms ({
            controlVariable: _controlVariable,
            vestingTerm: _vestingTerm,
            minimumPrice: _minimumPrice,
            maxPayout: _maxPayout,
            fee: _fee,
            maxDebt: _maxDebt
        });
        totalDebt = _initialDebt;
        lastDecay = block.number;
    }


/**
 *  @notice set parameters for new bonds
 *  @param _parameter PARAMETER
 *  @param _input uint
 */

function setBondTerms ( PARAMETER _parameter, uint _input ) external onlyPolicy() {
        if ( _parameter == PARAMETER.VESTING ) { // 0
            require( _input >= 10000, "Vesting must be longer than 36 hours" );
            terms.vestingTerm = _input;
        } else if ( _parameter == PARAMETER.PAYOUT ) { // 1
            require( _input <= 1000, "Payout cannot be above 1 percent" );
            terms.maxPayout = _input;
        } else if ( _parameter == PARAMETER.FEE ) { // 2
            require( _input <= 10000, "DAO fee cannot exceed payout" );
            terms.fee = _input;
        } else if ( _parameter == PARAMETER.DEBT ) { // 3
            terms.maxDebt = _input;
        }
    }


/**
 *  @notice set control variable adjustment
 *  @param _addition bool
 *  @param _increment uint
 *  @param _target uint
 *  @param _buffer uint
 */

function setAdjustment (
        bool _addition,
        uint _increment,
        uint _target,
        uint _buffer
    ) external onlyPolicy() {
        require( _increment <= terms.controlVariable.mul( 25 ).div( 1000 ), "Increment too large" );

        adjustment = Adjust({
            add: _addition,
            rate: _increment,
            target: _target,
            buffer: _buffer,
            lastBlock: block.number
        });
    }


/**
 *  @notice set contract for auto stake
 *  @param _staking address
 *  @param _helper bool
 */

function setStaking( address _staking, bool _helper ) external onlyPolicy() {
        require( _staking != address(0) );
        if ( _helper ) {
            useHelper = true;
            stakingHelper = _staking;
        } else {
            useHelper = false;
            staking = _staking;
        }
    }


/**
 *  @notice redeem bond for user
 *  @param _recipient address
 *  @param _stake bool
 *  @return uint
 */

function redeem( address _recipient, bool _stake ) external returns ( uint ) {
        Bond memory info = bondInfo[ _recipient ];
        uint percentVested = percentVestedFor( _recipient ); // (blocks since last interaction / vesting term remaining)

        if ( percentVested >= 10000 ) { // if fully vested
            delete bondInfo[ _recipient ]; // delete user info
            emit BondRedeemed( _recipient, info.payout, 0 ); // emit bond data
            return stakeOrSend( _recipient, _stake, info.payout ); // pay user everything due

        } else { // if unfinished
            // calculate payout vested
            uint payout = info.payout.mul( percentVested ).div( 10000 );

            // store updated deposit info
            bondInfo[ _recipient ] = Bond({
                payout: info.payout.sub( payout ),
                vesting: info.vesting.sub( block.number.sub( info.lastBlock ) ),
                lastBlock: block.number,
                pricePaid: info.pricePaid
            });

            emit BondRedeemed( _recipient, payout, bondInfo[ _recipient ].payout );
            return stakeOrSend( _recipient, _stake, payout );
        }
    }


/**
 *  @notice allow user to stake payout automatically
 *  @param _stake bool
 *  @param _amount uint
 *  @return uint
 */

function stakeOrSend( address _recipient, bool _stake, uint _amount ) internal returns ( uint ) {
        if ( !_stake ) { // if user does not want to stake
            IERC20( OHM ).transfer( _recipient, _amount ); // send payout
        } else { // if user wants to stake
            if ( useHelper ) { // use if staking warmup is 0
                IERC20( OHM ).approve( stakingHelper, _amount );
                IStakingHelper( stakingHelper ).stake( _amount, _recipient );
            } else {
                IERC20( OHM ).approve( staking, _amount );
                IStaking( staking ).stake( _amount, _recipient );
            }
        }
        return _amount;
    }


/**
 *  @notice makes incremental adjustment to control variable
 */

function adjust() internal {
        uint blockCanAdjust = adjustment.lastBlock.add( adjustment.buffer );
        if( adjustment.rate != 0 && block.number >= blockCanAdjust ) {
            uint initial = terms.controlVariable;
            if ( adjustment.add ) {
                terms.controlVariable = terms.controlVariable.add( adjustment.rate );
                if ( terms.controlVariable >= adjustment.target ) {
                    adjustment.rate = 0;
                    terms.controlVariable = adjustment.target;
                }
            } else {
                terms.controlVariable = terms.controlVariable.sub( adjustment.rate );
                if ( terms.controlVariable <= adjustment.target ) {
                    adjustment.rate = 0;
                    terms.controlVariable = adjustment.target;
                }
            }
            adjustment.lastBlock = block.number;
            emit ControlVariableAdjustment( initial, terms.controlVariable, adjustment.rate, adjustment.add );
        }
    }


/**
 *  @notice calculate current bond premium
 *  @return price_ uint
 */

function bondPrice() public view returns ( uint price_ ) {
        price_ = terms.controlVariable.mul( debtRatio() ).add( 1000000000 ).div( 1e7 );
        if ( price_ < terms.minimumPrice ) {
            price_ = terms.minimumPrice;
        }
    }


/**
 *  @notice calculate current bond price and remove floor if above
 *  @return price_ uint
 */

function _bondPrice() internal returns ( uint price_ ) {
        price_ = terms.controlVariable.mul( debtRatio() ).add( 1000000000 ).div( 1e7 );
        if ( price_ < terms.minimumPrice ) {
            price_ = terms.minimumPrice;
        } else if ( terms.minimumPrice != 0 ) {
            terms.minimumPrice = 0;
        }
    }


/**
 *  @notice converts bond price to DAI value
 *  @return price_ uint
 */

function bondPriceInUSD() public view returns ( uint price_ ) {
        if( isLiquidityBond ) {
            price_ = bondPrice().mul( IBondCalculator( bondCalculator ).markdown( principle ) ).div( 100 );
        } else {
            price_ = bondPrice().mul( 10 ** IERC20( principle ).decimals() ).div( 100 );
        }
    }