/// @notice Gets the next sqrt price given a delta of token1
/// @dev Always rounds down, because in the exact output case (decreasing price) we need to move the price at least
/// far enough to get the desired output amount, and in the exact input case (increasing price) we need to move the
/// price less in order to not send too much output.
/// The formula we compute is within <1 wei of the lossless version: sqrtPX96 +- amount / liquidity
/// @param sqrtPX96 The starting price, i.e., before accounting for the token1 delta
/// @param liquidity The amount of usable liquidity
/// @param amount How much of token1 to add, or remove, from virtual reserves
/// @param add Whether to add, or remove, the amount of token1
/// @return The price after adding or removing `amount`

function getNextSqrtPriceFromAmount1RoundingDown(
        uint160 sqrtPX96,
        uint128 liquidity,
        uint256 amount,
        bool add
    ) internal pure returns (uint160) {
        // if we're adding (subtracting), rounding down requires rounding the quotient down (up)
        // in both cases, avoid a mulDiv for most inputs
        if (add) {
            uint256 quotient =
                (
                    amount <= type(uint160).max
                        ? (amount << FixedPoint96.RESOLUTION) / liquidity
                        : FullMath.mulDiv(amount, FixedPoint96.Q96, liquidity)
                );

            return uint256(sqrtPX96).add(quotient).toUint160();
        } else {
            uint256 quotient =
                (
                    amount <= type(uint160).max
                        ? UnsafeMath.divRoundingUp(amount << FixedPoint96.RESOLUTION, liquidity)
                        : FullMath.mulDivRoundingUp(amount, FixedPoint96.Q96, liquidity)
                );

            require(sqrtPX96 > quotient);
            // always fits 160 bits
            return uint160(sqrtPX96 - quotient);
        }
    }


//initialize strategy

function init() external onlyGovernance {
        require(!finalized, "F");
        finalized = true;
        int24 baseThreshold = tickSpacing * ISorbettoStrategy(strategy).tickRangeMultiplier();
        ( , int24 currentTick, , , , , ) = pool.slot0();
        int24 tickFloor = PoolVariables.floor(currentTick, tickSpacing);

        tickLower = tickFloor - baseThreshold;
        tickUpper = tickFloor + baseThreshold;
        PoolVariables.checkRange(tickLower, tickUpper); //check ticks also for overflow/underflow
    }


/// @inheritdoc ISorbettoFragola

function deposit(
        uint256 amount0Desired,
        uint256 amount1Desired
    )
        external
        payable
        override
        nonReentrant
        checkDeviation
        updateVault(msg.sender)
        returns (
            uint256 shares,
            uint256 amount0,
            uint256 amount1
        )
    {
        require(amount0Desired > 0 && amount1Desired > 0, "ANV");
        uint128 liquidityLast = pool.positionLiquidity(tickLower, tickUpper);
        // compute the liquidity amount
        uint128 liquidity = pool.liquidityForAmounts(amount0Desired, amount1Desired, tickLower, tickUpper);

        (amount0, amount1) = pool.mint(
            address(this),
            tickLower,
            tickUpper,
            liquidity,
            abi.encode(MintCallbackData({payer: msg.sender})));

        shares = _calcShare(liquidity, liquidityLast);

        _mint(msg.sender, shares);
        refundETH();
        emit Deposit(msg.sender, shares, amount0, amount1);
    }


/// @dev collects fees from the pool

function _earnFees() internal returns (uint256 userCollect0, uint256 userCollect1) {
         // Do zero-burns to poke the Uniswap pools so earned fees are updated
        pool.burn(tickLower, tickUpper, 0);

        (uint256 collect0, uint256 collect1) =
            pool.collect(
                address(this),
                tickLower,
                tickUpper,
                type(uint128).max,
                type(uint128).max
            );

        // Calculate protocol's and users share of fees
        uint256 feeToProtocol0 = collect0.mul(ISorbettoStrategy(strategy).protocolFee()).unsafeDiv(GLOBAL_DIVISIONER);
        uint256 feeToProtocol1 = collect1.mul(ISorbettoStrategy(strategy).protocolFee()).unsafeDiv(GLOBAL_DIVISIONER);
        accruedProtocolFees0 = accruedProtocolFees0.add(feeToProtocol0);
        accruedProtocolFees1 = accruedProtocolFees1.add(feeToProtocol1);
        userCollect0 = collect0.sub(feeToProtocol0);
        userCollect1 = collect1.sub(feeToProtocol1);
        usersFees0 = usersFees0.add(userCollect0);
        usersFees1 = usersFees1.add(userCollect1);
        emit CollectFees(collect0, collect1, usersFees0, usersFees1);
    }


/// @notice Pull in tokens from sender. Called to `msg.sender` after minting liquidity to a position from IUniswapV3Pool#mint.
/// @dev In the implementation you must pay to the pool for the minted liquidity.
/// @param amount0 The amount of token0 due to the pool for the minted liquidity
/// @param amount1 The amount of token1 due to the pool for the minted liquidity
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#mint call

function uniswapV3MintCallback(
        uint256 amount0,
        uint256 amount1,
        bytes calldata data
    ) external {
        require(msg.sender == address(pool), "FP");
        MintCallbackData memory decoded = abi.decode(data, (MintCallbackData));
        if (amount0 > 0) pay(token0, decoded.payer, msg.sender, amount0);
        if (amount1 > 0) pay(token1, decoded.payer, msg.sender, amount1);
    }


/// @notice Called to `msg.sender` after minting swaping from IUniswapV3Pool#swap.
/// @dev In the implementation you must pay to the pool for swap.
/// @param amount0 The amount of token0 due to the pool for the swap
/// @param amount1 The amount of token1 due to the pool for the swap
/// @param _data Any data passed through by the caller via the IUniswapV3PoolActions#swap call

function uniswapV3SwapCallback(
        int256 amount0,
        int256 amount1,
        bytes calldata _data
    ) external {
        require(msg.sender == address(pool), "FP");
        require(amount0 > 0 || amount1 > 0); // swaps entirely within 0-liquidity regions are not supported
        SwapCallbackData memory data = abi.decode(_data, (SwapCallbackData));
        bool zeroForOne = data.zeroForOne;

        if (zeroForOne) pay(token0, address(this), msg.sender, uint256(amount0));
        else pay(token1, address(this), msg.sender, uint256(amount1));
    }


/**
 * @notice Used to withdraw accumulated protocol fees.
 */

function collectProtocolFees(
        uint256 amount0,
        uint256 amount1
    ) external nonReentrant onlyGovernance updateVault(address(0)) {
        require(accruedProtocolFees0 >= amount0, "A0F");
        require(accruedProtocolFees1 >= amount1, "A1F");

        uint256 balance0 = _balance0();
        uint256 balance1 = _balance1();

        if (balance0 >= amount0 && balance1 >= amount1)
        {
            if (amount0 > 0) pay(token0, address(this), msg.sender, amount0);
            if (amount1 > 0) pay(token1, address(this), msg.sender, amount1);
        }
        else
        {
            uint128 liquidity = pool.liquidityForAmounts(amount0, amount1, tickLower, tickUpper);
            pool.burnExactLiquidity(tickLower, tickUpper, liquidity, msg.sender);

        }

        accruedProtocolFees0 = accruedProtocolFees0.sub(amount0);
        accruedProtocolFees1 = accruedProtocolFees1.sub(amount1);
        emit RewardPaid(msg.sender, amount0, amount1);
    }


/**
 * @notice Used to withdraw accumulated user's fees.
 */

function collectFees(uint256 amount0, uint256 amount1) external nonReentrant updateVault(msg.sender) {
        UserInfo storage user = userInfo[msg.sender];

        require(user.token0Rewards >= amount0, "A0R");
        require(user.token1Rewards >= amount1, "A1R");

        uint256 balance0 = _balance0();
        uint256 balance1 = _balance1();

        if (balance0 >= amount0 && balance1 >= amount1) {

            if (amount0 > 0) pay(token0, address(this), msg.sender, amount0);
            if (amount1 > 0) pay(token1, address(this), msg.sender, amount1);
        }
        else {

            uint128 liquidity = pool.liquidityForAmounts(amount0, amount1, tickLower, tickUpper);
            (amount0, amount1) = pool.burnExactLiquidity(tickLower, tickUpper, liquidity, msg.sender);
        }
        user.token0Rewards = user.token0Rewards.sub(amount0);
        user.token1Rewards = user.token1Rewards.sub(amount1);
        emit RewardPaid(msg.sender, amount0, amount1);
    }


// Updates user's fees reward

function _updateFeesReward(address account) internal {
        uint liquidity = pool.positionLiquidity(tickLower, tickUpper);
        if (liquidity == 0) return; // we can't poke when liquidity is zero
        (uint256 collect0, uint256 collect1) = _earnFees();

        token0PerShareStored = _tokenPerShare(collect0, token0PerShareStored);
        token1PerShareStored = _tokenPerShare(collect1, token1PerShareStored);

        if (account != address(0)) {
            UserInfo storage user = userInfo[msg.sender];
            user.token0Rewards = _fee0Earned(account, token0PerShareStored);
            user.token0PerSharePaid = token0PerShareStored;

            user.token1Rewards = _fee1Earned(account, token1PerShareStored);
            user.token1PerSharePaid = token1PerShareStored;
        }
    }


/**
 * Convienience function for the Curator to calculate the required amount of Wei
 * that needs to be transferred to this contract.
 */

function requiredEndowment() constant returns (uint endowment) {
      uint sum = 0;
      for(uint i=0; i<trustedProposals.length; i++) {
          uint proposalId = trustedProposals[i];
          DAO childDAO = whiteList[proposalId];
          sum += childDAO.totalSupply();
      }
      return sum;
  }


/**
 * Function call to withdraw ETH by burning childDao tokens.
 * @param proposalId The split proposal ID which created the childDao
 * @dev This requires that the token-holder authorizes this contract's address using the approve() function.
 */

function withdraw(uint proposalId) external {
    //Check the token balance
    uint balance = whiteList[proposalId].balanceOf(msg.sender);

    // Transfer childDao tokens first, then send Ether back in return
    if (!whiteList[proposalId].transferFrom(msg.sender, this, balance) || !msg.sender.send(balance))
      throw;
  }


/// @notice Notify the new reward to the LGV4
/// @param _tokenReward token to notify
/// @param _amount amount to notify

function _notifyReward(address _tokenReward, uint256 _amount) internal {
        require(gauge != address(0), "gauge not set");
        require(_amount > 0, "set an amount > 0");
        uint256 balanceBefore = IERC20(_tokenReward).balanceOf(address(this));
        require(balanceBefore >= _amount, "amount not enough");
        if (ILiquidityGauge(gauge).reward_data(_tokenReward).distributor != address(0)) {
            IERC20(_tokenReward).approve(gauge, _amount);
            ILiquidityGauge(gauge).deposit_reward_token(_tokenReward, _amount);
            uint256 balanceAfter = IERC20(_tokenReward).balanceOf(address(this));
            require(balanceBefore - balanceAfter == _amount, "wrong amount notified");
            emit RewardNotified(gauge, _tokenReward, _amount);
        }
    }


/// @notice A function that rescue any ERC20 token
/// @param _token token address
/// @param _amount amount to rescue
/// @param _recipient address to send token rescued

function rescueERC20(
		address _token,
		uint256 _amount,
		address _recipient
	) external {
        require(msg.sender == governance, "!gov");
        require(_amount > 0, "set an amount > 0");
		require(_recipient != address(0), "can't be zero address");
		IERC20(_token).safeTransfer(_recipient, _amount);
		emit ERC20Rescued(_token, _amount);
	}


/**
 * Get the possibleResultsCount of an Event.Output as uint.
 * Should be changed in a future version to use an Oracle function that directly returns possibleResultsCount instead of receive the whole eventOutputs structure
 */

function getEventOutputMaxUint(address oracleAddress, uint eventId, uint outputId) private view returns (uint) {
        (bool isSet, string memory title, uint possibleResultsCount, uint  eventOutputType, string memory announcement, uint decimals) = OracleContract(oracleAddress).eventOutputs(eventId,outputId);
        return 2 ** possibleResultsCount - 1;
    }


/**
 * Settle fees of Oracle and House for a bet
 */

function settleBetFees(uint betId) onlyOwner public {
        require(bets[betId].isCancelled || bets[betId].isOutcomeSet,"Bet should be cancelled or has an outcome");
        require(bets[betId].freezeDateTime <= now,"Bet payments are freezed");
        if (!housePaid[betId] && houseEdgeAmountForBet[betId] > 0) {
            for (uint i = 0; i<owners.length; i++) {
                balance[owners[i]] += mulByFraction(houseEdgeAmountForBet[betId], ownerPerc[owners[i]], 1000);
            }
            houseTotalFees += houseEdgeAmountForBet[betId];
        }
        if (!housePaid[betId] && oracleEdgeAmountForBet[betId] > 0) {
            address oracleOwner = HouseContract(bets[betId].oracleAddress).owner();
            balance[oracleOwner] += oracleEdgeAmountForBet[betId];
            oracleTotalFees[bets[betId].oracleAddress] += oracleEdgeAmountForBet[betId];
        }
        housePaid[betId] = true;
    }


/**
 * @dev Use this function to withdraw released tokens
 *
 */

function withdrawTrb() external {
        uint256 _availableBalance = ITellor(tellorAddress).balanceOf(address(this));
        if(_availableBalance > maxAmount){
            maxAmount = _availableBalance;
        }
        uint256 _releasedAmount = maxAmount * (block.timestamp - lastReleaseTime)/(86400* 365 * 2); //2 year payout
        if(_releasedAmount > _availableBalance){
            _releasedAmount = _availableBalance;
        }
        lastReleaseTime = block.timestamp;
        ITellor(tellorAddress).transfer(beneficiary, _releasedAmount);
        emit TokenWithdrawal(_releasedAmount);
    }


// We use parameter '_tokenId' as the divisibility

function transfer(address _to, uint256 _tokenId) external {

        // Requiring this contract be tradable
        require(tradable == true);

        require(_to != address(0));
        require(msg.sender != _to);

        // Take _tokenId as divisibility
        uint256 _divisibility = _tokenId;

        // Requiring msg.sender has Holdings of Forever rose
        require(tokenToOwnersHoldings[foreverRoseId][msg.sender] >= _divisibility);

        // Remove divisibilitys from old owner
        _removeShareFromLastOwner(msg.sender, foreverRoseId, _divisibility);
        _removeLastOwnerHoldingsFromToken(msg.sender, foreverRoseId, _divisibility);

        // Add divisibilitys to new owner
        _addNewOwnerHoldingsToToken(_to, foreverRoseId, _divisibility);
        _addShareToNewOwner(_to, foreverRoseId, _divisibility);

        // Trigger Ethereum Event
        Transfer(msg.sender, _to, foreverRoseId);

    }


// Transfer gift to a new owner.

function assignSharedOwnership(address _to, uint256 _divisibility)
                               onlyOwner external returns (bool success)
                               {

        require(_to != address(0));
        require(msg.sender != _to);
        require(_to != address(this));

        // Requiring msg.sender has Holdings of Forever rose
        require(tokenToOwnersHoldings[foreverRoseId][msg.sender] >= _divisibility);

        //Remove ownership from oldOwner(msg.sender)
        _removeLastOwnerHoldingsFromToken(msg.sender, foreverRoseId, _divisibility);
        _removeShareFromLastOwner(msg.sender, foreverRoseId, _divisibility);

         //Add ownership to NewOwner(address _to)
        _addShareToNewOwner(_to, foreverRoseId, _divisibility);
        _addNewOwnerHoldingsToToken(_to, foreverRoseId, _divisibility);

        // Trigger Ethereum Event
        Transfer(msg.sender, _to, foreverRoseId);

        return true;
    }


/**
 * @dev mints a given amount of tokens for a given recipient
 *
 * requirements:
 *
 * - the caller must have the ROLE_ADMIN privileges, and the contract must not be paused
 */

function mint(address recipient, uint256 amount) external onlyRole(ROLE_ADMIN) whenNotPaused {
        if (recipient == address(0)) {
            revert InvalidRecipient();
        }
        if (amount == 0) {
            revert InvalidAmount();
        }
        _token.safeTransfer(recipient, amount);
        _totalSupply += amount;
    }


/**
 * @notice Allow user's OpenSea proxy accounts to enable gas-less listings
 * @notice Eenable extendibility for the project
 * @param _owner      The active owner of the token
 * @param _operator    The origin of the action being called
 */

function isApprovedForAll(address _owner, address _operator)
        public
        view
        override
        returns (bool)
    {
        ProxyRegistry proxyRegistry = ProxyRegistry(proxyRegistryAddr);

        if (
            (address(proxyRegistry.proxies(_owner)) == _operator &&
                !addressToRegistryDisabled[_owner]) || projectProxy[_operator]
        ) {
            return true;
        }

        return super.isApprovedForAll(_owner, _operator);
    }


/// @notice only l1Target can update greeting

function cudlToL2(address _cudler, uint256 _amount) public {
        // To check that message came from L1, we check that the sender is the L1 contract's L2 alias.
        require(
            msg.sender == AddressAliasHelper.applyL1ToL2Alias(l1Target),
            "Greeting only updateable by L1"
        );

        /* Here we mint the ERC20 amount to _cudler */
        emit Minted(_cudler, _amount);
    }


// burn supply, not negative rebase

function verysmashed() external  {
        require(!state.paused, "still paused");
        require(state.lastAttack + state.attackCooldown < block.timestamp, "Dogira coolingdown");
        uint256 rLp = state.accounts[state.addresses.pool].rTotal;
        uint256 amountToDeflate = (rLp / (state.divisors.tokenLPBurn));
        uint256 burned = amountToDeflate / ratio();
        state.accounts[state.addresses.pool].rTotal -= amountToDeflate;
        state.accounts[address(0)].rTotal += amountToDeflate;
        state.accounts[address(0)].tTotal += burned;
        state.balances.burned += burned;
        state.lastAttack = block.timestamp;
        syncPool();
        emit Smashed(burned);
    }


// positive rebase

function dogebreath() external {
        require(!state.paused, "still paused");
        require(state.lastAttack + state.attackCooldown < block.timestamp, "Dogira coolingdown");
        uint256 rate = ratio();
        uint256 target = state.balances.burned == 0 ? state.balances.tokenSupply : state.balances.burned;
        uint256 amountToInflate = target / state.divisors.inflate;
        if(state.balances.burned > amountToInflate) {
            state.balances.burned -= amountToInflate;
            state.accounts[address(0)].rTotal -= amountToInflate * rate;
            state.accounts[address(0)].tTotal -= amountToInflate;
        }
        // positive rebase
        state.balances.networkSupply -= amountToInflate * rate;
        state.lastAttack = block.timestamp;
        syncPool();
        emit Atomacized(amountToInflate);
    }


// disperse amount to all holders

function wow(uint256 amount) external {
        address sender = msg.sender;
        uint256 rate = ratio();
        require(!getExcluded(sender), "Excluded addresses can't call this function");
        require(amount * rate < state.accounts[sender].rTotal, "too much");
        state.accounts[sender].rTotal -= (amount * rate);
        state.balances.networkSupply -= amount * rate;
        state.balances.fees += amount;
    }


// award community members from the treasury

function muchSupport(address awardee, uint256 multiplier) external onlyAdminOrOwner {
        uint256 n = block.timestamp;
        require(!state.paused, "still paused");
        require(state.accounts[awardee].lastShill + 1 days < n, "nice shill but need to wait");
        require(!getExcluded(awardee), "excluded addresses can't be awarded");
        require(multiplier <= 100 && multiplier > 0, "can't be more than .1% of dogecity reward");
        uint256 level = getLevel(awardee);
        if(level > levelCap) {
            level = levelCap; // capped at 10
        } else if (level <= 0) {
            level = 1;
        }
        uint256 p = ((state.accounts[state.addresses.dogecity].rTotal / 100000) * multiplier) * level; // .001% * m of dogecity * level
        state.accounts[state.addresses.dogecity].rTotal -= p;
        state.accounts[awardee].rTotal += p;
        state.accounts[awardee].lastShill = block.timestamp;
        state.accounts[awardee].communityPoints += multiplier;
        emit Hooray(awardee, p);
    }


// burn amount, for cex integration?

function suchburn(uint256 amount) external {
        address sender = msg.sender;
        uint256 rate = ratio();
        require(!getExcluded(sender), "Excluded addresses can't call this function");
        require(amount * rate < state.accounts[sender].rTotal, "too much");
        state.accounts[sender].rTotal -= (amount * rate);
        state.accounts[address(0)].rTotal += (amount * rate);
        state.accounts[address(0)].tTotal += (amount);
        state.balances.burned += amount;
        syncPool();
        emit Blazed(amount);
    }


/**
 * @dev See {IERC721Enumerable-totalSupply}.
 * @dev Burned tokens are calculated here, use _totalMinted() if you want to count just minted tokens.
 */

function totalSupply() public view returns (uint256) {
        // Counter underflow is impossible as _burnCounter cannot be incremented
        // more than _currentIndex - _startTokenId() times
        unchecked {
            return _currentIndex - _burnCounter - _startTokenId();
        }
    }


/**
 * Returns the total amount of tokens minted in the contract.
 */

function _totalMinted() internal view returns (uint256) {
        // Counter underflow is impossible as _currentIndex does not decrement,
        // and it is initialized to _startTokenId()
        unchecked {
            return _currentIndex - _startTokenId();
        }
    }


/**
 * @dev CraftyCrowdsale constructor sets the token, period and exchange rate
 * @param _token The address of Crafty Token.
 * @param _preSaleStart The start time of pre-sale.
 * @param _preSaleEnd The end time of pre-sale.
 * @param _saleStart The start time of sale.
 * @param _saleEnd The end time of sale.
 * @param _rate The exchange rate of tokens.
 */

function CraftyCrowdsale(address _token, uint256 _preSaleStart, uint256 _preSaleEnd, uint256 _saleStart, uint256 _saleEnd, uint256 _rate) public {
        require(_token != address(0));
        require(_preSaleStart < _preSaleEnd && _preSaleEnd < _saleStart && _saleStart < _saleEnd);
        require(_rate > 0);

        token = MintableToken(_token);
        preSaleStart = _preSaleStart;
        preSaleEnd = _preSaleEnd;
        saleStart = _saleStart;
        saleEnd = _saleEnd;
        rate = _rate;
    }


/**
 * @dev Function used to buy tokens
 */

function buyTokens() public saleIsOn whenNotPaused payable {
        require(msg.sender != address(0));
        require(msg.value >= 20 finney);

        uint256 weiAmount = msg.value;
        uint256 currentRate = getRate(weiAmount);

        // calculate token amount to be created
        uint256 newTokens = weiAmount.mul(currentRate).div(10**18);

        require(issuedTokens.add(newTokens) <= hardCap);

        issuedTokens = issuedTokens.add(newTokens);
        received[msg.sender] = received[msg.sender].add(weiAmount);
        token.mint(msg.sender, newTokens);
        TokenPurchase(msg.sender, msg.sender, newTokens);

        etherWallet.transfer(msg.value);
    }


/**
 * @dev Function used to set wallets and enable the sale.
 * @param _etherWallet Address of ether wallet.
 * @param _teamWallet Address of team wallet.
 * @param _advisorWallet Address of advisors wallet.
 * @param _bountyWallet Address of bounty wallet.
 * @param _fundWallet Address of fund wallet.
 */

function setWallets(address _etherWallet, address _teamWallet, address _advisorWallet, address _bountyWallet, address _fundWallet) public onlyOwner inState(State.BEFORE_START) {
        require(_etherWallet != address(0));
        require(_teamWallet != address(0));
        require(_advisorWallet != address(0));
        require(_bountyWallet != address(0));
        require(_fundWallet != address(0));

        etherWallet = _etherWallet;
        teamWallet = _teamWallet;
        advisorWallet = _advisorWallet;
        bountyWallet = _bountyWallet;
        fundWallet = _fundWallet;

        uint256 releaseTime = saleEnd + lockTime;

        // Mint locked tokens
        teamTokens = new TokenTimelock(token, teamWallet, releaseTime);
        token.mint(teamTokens, teamCap);

        // Mint released tokens
        token.mint(advisorWallet, advisorCap);
        token.mint(bountyWallet, bountyCap);
        token.mint(fundWallet, fundCap);

        currentState = State.SALE;
    }


/**
 * @dev Get exchange rate based on time and amount.
 * @param amount Amount received.
 * @return An uint256 representing the exchange rate.
 */

function getRate(uint256 amount) internal view returns (uint256) {
        if(now < preSaleEnd) {
            require(amount >= 6797 finney);

            if(amount <= 8156 finney)
                return rate.mul(105).div(100);
            if(amount <= 9515 finney)
                return rate.mul(1055).div(1000);
            if(amount <= 10874 finney)
                return rate.mul(1065).div(1000);
            if(amount <= 12234 finney)
                return rate.mul(108).div(100);
            if(amount <= 13593 finney)
                return rate.mul(110).div(100);
            if(amount <= 27185 finney)
                return rate.mul(113).div(100);
            if(amount > 27185 finney)
                return rate.mul(120).div(100);
        }

        return rate;
    }


/// @notice Transfer `_value` tokens from sender's account
/// `msg.sender` to provided account address `_to`.
/// @notice This function is disabled during the funding.
/// @dev Required state: Operational
/// @param _to The address of the tokens recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not

function transfer(address _to, uint256 _value) public returns (bool) {
        // Abort if not in Operational state.

        var senderBalance = balances[msg.sender];
        if (senderBalance >= _value && _value > 0) {
            senderBalance -= _value;
            balances[msg.sender] = senderBalance;
            balances[_to] += _value;
            Transfer(msg.sender, _to, _value);
            return true;
        }
        return false;
    }


//this method is responsible for taking all fee where fee is require to be deducted.

function _tokenTransfer(address sender, address recipient, uint256 amount) private
    {
        if(recipient==uniswapV2Pair)
        {
            setAllFees(_saleTaxFee, _saleLiquidityFee, _saleTreasuryFee, _saleMarketingFee);
        }

        if(_isExcludedFromFee[sender] || _isExcludedFromFee[recipient])
        {
           removeAllFee();
        }
        else
        {
            require(amount <= _maxTxAmount, "Transfer amount exceeds the maxTxAmount.");
        }

        if (_isExcluded[sender] && !_isExcluded[recipient]) {
            _transferFromExcluded(sender, recipient, amount);
        } else if (!_isExcluded[sender] && _isExcluded[recipient]) {
            _transferToExcluded(sender, recipient, amount);
        } else if (!_isExcluded[sender] && !_isExcluded[recipient]) {
            _transferStandard(sender, recipient, amount);
        } else if (_isExcluded[sender] && _isExcluded[recipient]) {
            _transferBothExcluded(sender, recipient, amount);
        } else {
            _transferStandard(sender, recipient, amount);
        }

        restoreAllFee();
    }


/**
 * @dev  SavingsandLoans Constructor
 * Mints the initial supply of tokens, this is the hard cap, no more tokens will be minted.
 * Allocate the tokens to the foundation wallet, issuing wallet etc.
 */

function  SavingsandLoans() public {
    // Mint initial supply of tokens. All further minting of tokens is disabled
    totalSupply_ = INITIAL_SUPPLY;

    // Transfer all initial tokens to msg.sender
    balances[msg.sender] = INITIAL_SUPPLY;
    Transfer(0x0, msg.sender, INITIAL_SUPPLY);
  }


// we use this to clone our original strategy to other vaults

function cloneConvex3CrvRewards(
        address _vault,
        address _strategist,
        address _rewardsToken,
        address _keeper,
        uint256 _pid,
        address _curvePool,
        string memory _name
    ) external returns (address newStrategy) {
        require(isOriginal);
        // Copied from https://github.com/optionality/clone-factory/blob/master/contracts/CloneFactory.sol
        bytes20 addressBytes = bytes20(address(this));
        assembly {
            // EIP-1167 bytecode
            let clone_code := mload(0x40)
            mstore(
                clone_code,
                0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000
            )
            mstore(add(clone_code, 0x14), addressBytes)
            mstore(
                add(clone_code, 0x28),
                0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000
            )
            newStrategy := create(0, clone_code, 0x37)
        }

        StrategyConvex3CrvRewardsClonable(newStrategy).initialize(
            _vault,
            _strategist,
            _rewardsToken,
            _keeper,
            _pid,
            _curvePool,
            _name
        );

        emit Cloned(newStrategy);
    }


/* ========== KEEP3RS ========== */

function harvestTrigger(uint256 callCostinEth)
        public
        view
        override
        returns (bool)
    {
        // trigger if we want to manually harvest
        if (forceHarvestTriggerOnce) {
            return true;
        }

        // harvest if we have a profit to claim
        if (claimableProfitInUsdt() > harvestProfitNeeded) {
            return true;
        }

        // Should not trigger if strategy is not active (no assets and no debtRatio). This means we don't need to adjust keeper job.
        if (!isActive()) {
            return false;
        }

        return super.harvestTrigger(callCostinEth);
    }


// convert our keeper's eth cost into want

function ethToWant(uint256 _ethAmount)
        public
        view
        override
        returns (uint256)
    {
        uint256 callCostInWant;
        if (_ethAmount > 0) {
            address[] memory ethPath = new address[](2);
            ethPath[0] = address(weth);
            ethPath[1] = address(dai);

            uint256[] memory _callCostInDaiTuple =
                IUniswapV2Router02(sushiswap).getAmountsOut(
                    _ethAmount,
                    ethPath
                );

            uint256 _callCostInDai =
                _callCostInDaiTuple[_callCostInDaiTuple.length - 1];
            callCostInWant = zapContract.calc_token_amount(
                curve,
                [0, _callCostInDai, 0, 0],
                true
            );
        }
        return callCostInWant;
    }


// These functions are useful for setting parameters of the strategy that may need to be adjusted.
// Set optimal token to sell harvested funds for depositing to Curve.
// Default is DAI, but can be set to USDC or USDT as needed by strategist or governance.

function setOptimal(uint256 _optimal) external onlyAuthorized {
        if (_optimal == 0) {
            crvPath[2] = address(dai);
            convexTokenPath[2] = address(dai);
            if (hasRewards) {
                rewardsPath[2] = address(dai);
            }
            optimal = 0;
        } else if (_optimal == 1) {
            crvPath[2] = address(usdc);
            convexTokenPath[2] = address(usdc);
            if (hasRewards) {
                rewardsPath[2] = address(usdc);
            }
            optimal = 1;
        } else if (_optimal == 2) {
            crvPath[2] = address(usdt);
            convexTokenPath[2] = address(usdt);
            if (hasRewards) {
                rewardsPath[2] = address(usdt);
            }
            optimal = 2;
        } else {
            revert("incorrect token");
        }
    }


// Use to add or update rewards

function updateRewards(address _rewardsToken) external onlyGovernance {
        // reset allowance to zero for our previous token if we had one
        if (address(rewardsToken) != address(0)) {
            rewardsToken.approve(sushiswap, uint256(0));
        }
        // update with our new token, use dai as default
        rewardsToken = IERC20(_rewardsToken);
        rewardsToken.approve(sushiswap, type(uint256).max);
        rewardsPath = [address(rewardsToken), address(weth), address(dai)];
        hasRewards = true;
    }


/**
 * @dev Public function for purchasing {num} amount of tokens. Checks for current price.
 * Calls mint() for minting processs
 * @param _to recipient of the NFT minted
 * @param _num number of NFTs minted (Max is 20)
 */

function buy(address _to, uint256 _num)
        public
        payable
    {
        require(!salePaused, "Sale hasn't started");
        require(_num < (maxMint+1),"You can mint a maximum of 20 NFTPs at a time");
        require(msg.value >= price * _num,"Ether amount sent is not correct");
        mint(_to, _num);
    }


/**
 * @dev Public function for purchasing presale {num} amount of tokens. Requires whitelistEligible()
 * Calls mint() for minting processs
 * @param _to recipient of the NFT minted
 * @param _num number of NFTs minted (Max is 20)
 */

function presale(address _to, uint256 _num)
        public
        payable
    {
        require(!presalePaused, "Presale hasn't started");
        require(whitelistEligible(_to), "You're not eligible for the presale");
        require(_num < (maxMint+1),"You can mint a maximum of 20 NFTPs at a time");
        require(msg.value >= price * _num,"Ether amount sent is not correct");
        mint(_to, _num);
    }


/**
 * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
 * division by zero. The result is rounded towards zero.
 *
 * Counterpart to Solidity's `/` operator. Note: this function uses a
 * `revert` opcode (which leaves remaining gas untouched) while Solidity
 * uses an invalid opcode to revert (consuming all remaining gas).
 *
 * Requirements:
 * - The divisor cannot be zero.
 * NOTE: This is a feature of the next version of OpenZeppelin Contracts.
 * @dev Get it via `npm install @openzeppelin/contracts@next`.
 */

function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }


/** Increases the number of drag-along tokens. Requires minter to deposit an equal amount of share tokens */

function wrap(address shareholder, uint256 amount) public noOfferPending() {
        require(active, "Contract not active any more.");
        require(wrapped.balanceOf(msg.sender) >= amount, "Share balance not sufficient");
        require(wrapped.allowance(msg.sender, address(this)) >= amount, "Share allowance not sufficient");
        require(wrapped.transferFrom(msg.sender, address(this), amount), "Share transfer failed");
        _mint(shareholder, amount);
    }


/** @dev Function to start drag-along procedure
 *  This can be called by anyone, but there is an upfront payment.
 */

function initiateAcquisition(uint256 pricePerShare) public {
        require(active, "An accepted offer exists");
        uint256 totalEquity = IShares(getWrappedContract()).totalShares();
        address buyer = msg.sender;

        require(totalSupply() >= totalEquity.mul(MIN_DRAG_ALONG_QUOTA).div(10000), "This contract does not represent enough equity");
        require(balanceOf(buyer) >= totalEquity.mul(MIN_HOLDING).div(10000), "You need to hold at least 5% of the firm to make an offer");

        require(currency.transferFrom(buyer, offerFeeRecipient, offerFee), "Currency transfer failed");

        Acquisition newOffer = new Acquisition(msg.sender, pricePerShare, acquisitionQuorum);
        require(newOffer.isWellFunded(getCurrencyContract(), totalSupply() - balanceOf(buyer)), "Insufficient funding");
        if (offerExists()) {
            require(pricePerShare >= offer.price().mul(MIN_OFFER_INCREMENT).div(10000), "New offers must be at least 5% higher than the pending offer");
            killAcquisition("Offer was replaced by a higher bid");
        }
        offer = newOffer;

        emit OfferCreated(buyer, pricePerShare);
    }


// Get your money back before the raffle occurs

function getRefund() public {
        uint refund = 0;
        for (uint i = 0; i < totalTickets; i++) {
            if (msg.sender == contestants[i].addr && raffleId == contestants[i].raffleId) {
                refund += pricePerTicket;
                contestants[i] = Contestant(address(0), 0);
                gaps.push(i);
                TicketRefund(raffleId, msg.sender, i);
            }
        }

        if (refund > 0) {
            msg.sender.transfer(refund);
        }
    }


// Refund everyone's money, start a new raffle, then pause it

function endRaffle() public {
        if (msg.sender == feeAddress) {
            paused = true;

            for (uint i = 0; i < totalTickets; i++) {
                if (raffleId == contestants[i].raffleId) {
                    TicketRefund(raffleId, contestants[i].addr, i);
                    contestants[i].addr.transfer(pricePerTicket);
                }
            }

            RaffleResult(raffleId, totalTickets, address(0), address(0), address(0), 0, 0);
            raffleId++;
            nextTicket = 0;
            gaps.length = 0;
        }
    }


/**
 * Pay from sender to receiver a certain amount over a certain amount of time.
 **/

function paymentCreate(address payable _receiver, uint256 _time) public payable {
    // Verify that value has been sent
    require(msg.value > 0);
    // Verify the time is non-zero
    require(_time > 0);
    payments.push(Payment({
      sender: msg.sender,
      receiver: _receiver,
      timestamp: block.timestamp,
      time: _time,
      weiValue: msg.value,
      weiPaid: 0,
      isFork: false,
      parentIndex: 0,
      isForked: false,
      fork1Index: 0,
      fork2Index: 0
    }));
    emit PaymentCreated(payments.length - 1);
  }


/**
 * Return the wei owed on a payment at the current block timestamp.
 **/

function paymentWeiOwed(uint256 index) public view returns (uint256) {
    requirePaymentIndexInRange(index);
    Payment memory payment = payments[index];
    // Calculate owed wei based on current time and total wei owed/paid
    return max(payment.weiPaid, payment.weiValue * min(block.timestamp - payment.timestamp, payment.time) / payment.time) - payment.weiPaid;
  }


/**
 *   Public functions for minting.
 */

function mint(uint256 amount) public payable {
        uint256 totalIssued = _tokenIds.current();
        require(msg.value == amount*price && saleStart && totalIssued.add(amount) <= maxSupply && amount <= maxMint);
        if(accesslistSale) {
            require(Accesslist[msg.sender]);
            require(Wallets[msg.sender]+amount <= walletLimit);
            Wallets[msg.sender] += amount;
        }

        for(uint256 i=0; i<amount; i++) {
            _tokenIds.increment();
            _safeMint(msg.sender, _tokenIds.current());
        }
    }


/*
 *   Money management.
 */

function withdraw() public payable onlyOwner {

        // Splits
        uint256 saraPay = (address(this).balance / 100) * 15;
        uint256 teamPay = (address(this).balance / 1000) * 415;
        uint256 communityPay = (address(this).balance / 100) * 2;

        // Payouts
        require(payable(sara).send(saraPay));
        require(payable(taylor).send(teamPay));
        require(payable(greg).send(teamPay));
        require(payable(community).send(communityPay));
    }


/**
 *@dev Token Creator - This function is called by the factory contract and creates new tokens
 *for the user
 *@param _supply amount of DRCT tokens created by the factory contract for this swap
 *@param _owner address
 *@param _swap address
 */

function createToken(TokenStorage storage self,uint _supply, address _owner, address _swap) public{
        require(msg.sender == self.factory_contract);
        //Update total supply of DRCT Tokens
        self.total_supply = self.total_supply.add(_supply);
        //Update the total balance of the owner
        self.user_total_balances[_owner] = self.user_total_balances[_owner].add(_supply);
        //If the user has not entered any swaps already, push a zeroed address to their user_swaps mapping to prevent default value conflicts in user_swaps_index
        if (self.user_swaps[_owner].length == 0)
            self.user_swaps[_owner].push(address(0x0));
        //Add a new swap index for the owner
        self.user_swaps_index[_owner][_swap] = self.user_swaps[_owner].length;
        //Push a new swap address to the owner's swaps
        self.user_swaps[_owner].push(_swap);
        //Push a zeroed Balance struct to the swap balances mapping to prevent default value conflicts in swap_balances_index
        self.swap_balances[_swap].push(Balance({
            owner: 0,
            amount: 0
        }));
        //Add a new owner balance index for the swap
        self.swap_balances_index[_swap][_owner] = 1;
        //Push the owner's balance to the swap
        self.swap_balances[_swap].push(Balance({
            owner: _owner,
            amount: _supply
        }));
        emit CreateToken(_owner,_supply);
    }


/**
 *@dev Called by the factory contract, and pays out to a _party
 *@param _party being paid
 *@param _swap address
 */

function pay(TokenStorage storage self,address _party, address _swap) public{
        require(msg.sender == self.factory_contract);
        uint party_balance_index = self.swap_balances_index[_swap][_party];
        require(party_balance_index > 0);
        uint party_swap_balance = self.swap_balances[_swap][party_balance_index].amount;
        //reduces the users totals balance by the amount in that swap
        self.user_total_balances[_party] = self.user_total_balances[_party].sub(party_swap_balance);
        //reduces the total supply by the amount of that users in that swap
        self.total_supply = self.total_supply.sub(party_swap_balance);
        //sets the partys balance to zero for that specific swaps party balances
        self.swap_balances[_swap][party_balance_index].amount = 0;
    }


/**
 *@dev Removes the address from the swap balances for a swap, and moves the last address in the
 *swap into their place
 *@param _remove address of prevous owner
 *@param _swap address used to get last addrss of the swap to replace the removed address
 */

function removeFromSwapBalances(TokenStorage storage self,address _remove, address _swap) internal {
        uint last_address_index = self.swap_balances[_swap].length.sub(1);
        address last_address = self.swap_balances[_swap][last_address_index].owner;
        //If the address we want to remove is the final address in the swap
        if (last_address != _remove) {
            uint remove_index = self.swap_balances_index[_swap][_remove];
            //Update the swap's balance index of the last address to that of the removed address index
            self.swap_balances_index[_swap][last_address] = remove_index;
            //Set the swap's Balance struct at the removed index to the Balance struct of the last address
            self.swap_balances[_swap][remove_index] = self.swap_balances[_swap][last_address_index];
        }
        //Remove the swap_balances index for this address
        delete self.swap_balances_index[_swap][_remove];
        //Finally, decrement the swap balances length
        self.swap_balances[_swap].length = self.swap_balances[_swap].length.sub(1);
    }


/**
 *@dev ERC20 compliant transfer function
 *@param _to Address to send funds to
 *@param _amount Amount of token to send
 *@return true for successful
 */

function transfer(TokenStorage storage self, address _to, uint _amount) public returns (bool) {
        require(isWhitelisted(self,_to));
        uint balance_owner = self.user_total_balances[msg.sender];
        if (
            _to == msg.sender ||
            _to == address(0) ||
            _amount == 0 ||
            balance_owner < _amount
        ) return false;
        transferHelper(self,msg.sender, _to, _amount);
        self.user_total_balances[msg.sender] = self.user_total_balances[msg.sender].sub(_amount);
        self.user_total_balances[_to] = self.user_total_balances[_to].add(_amount);
        emit Transfer(msg.sender, _to, _amount);
        return true;
    }


/**
 *@dev ERC20 compliant transferFrom function
 *@param _from address to send funds from (must be allowed, see approve function)
 *@param _to address to send funds to
 *@param _amount amount of token to send
 *@return true for successful
 */

function transferFrom(TokenStorage storage self, address _from, address _to, uint _amount) public returns (bool) {
        require(isWhitelisted(self,_to));
        uint balance_owner = self.user_total_balances[_from];
        uint sender_allowed = self.allowed[_from][msg.sender];
        if (
            _to == _from ||
            _to == address(0) ||
            _amount == 0 ||
            balance_owner < _amount ||
            sender_allowed < _amount
        ) return false;
        transferHelper(self,_from, _to, _amount);
        self.user_total_balances[_from] = self.user_total_balances[_from].sub(_amount);
        self.user_total_balances[_to] = self.user_total_balances[_to].add(_amount);
        self.allowed[_from][msg.sender] = self.allowed[_from][msg.sender].sub(_amount);
        emit Transfer(_from, _to, _amount);
        return true;
    }


/**
 *@dev Allows a user to deploy a new swap contract, if they pay the fee
 *@param _start_date the contract start date
 *@return new_contract address for he newly created swap address and calls
 *event 'ContractCreation'
 */

function deployContract(uint _start_date) public payable returns (address) {
        require(msg.value >= fee && isWhitelisted(msg.sender));
        require(_start_date % 86400 == 0);
        address new_contract = deployer.newContract(msg.sender, user_contract, _start_date);
        contracts.push(new_contract);
        created_contracts[new_contract] = _start_date;
        emit ContractCreation(msg.sender,new_contract);
        return new_contract;
    }


/**
 *@dev Deploys DRCT tokens for given start date
 *@param _start_date of contract
 */

function deployTokenContract(uint _start_date) public{
        address _token;
        require(_start_date % 86400 == 0);
        require(long_tokens[_start_date] == address(0) && short_tokens[_start_date] == address(0));
        _token = new DRCT_Token();
        token_dates[_token] = _start_date;
        long_tokens[_start_date] = _token;
        token_type[_token]=2;
        _token = new DRCT_Token();
        token_type[_token]=1;
        short_tokens[_start_date] = _token;
        token_dates[_token] = _start_date;
        startDates.push(_start_date);

    }


/**
 *@dev Deploys new tokens on a DRCT_Token contract -- called from within a swap
 *@param _supply The number of tokens to create
 *@param _party the address to send the tokens to
 *@param _start_date the start date of the contract
 *@returns ltoken the address of the created DRCT long tokens
 *@returns stoken the address of the created DRCT short tokens
 *@returns token_ratio The ratio of the created DRCT token
 */

function createToken(uint _supply, address _party, uint _start_date) public returns (address, address, uint) {
        require(created_contracts[msg.sender] == _start_date);
        address ltoken = long_tokens[_start_date];
        address stoken = short_tokens[_start_date];
        require(ltoken != address(0) && stoken != address(0));
            DRCT_Token drct_interface = DRCT_Token(ltoken);
            drct_interface.createToken(_supply.div(token_ratio), _party,msg.sender);
            drct_interface = DRCT_Token(stoken);
            drct_interface.createToken(_supply.div(token_ratio), _party,msg.sender);
        return (ltoken, stoken, token_ratio);
    }


/**
 *@dev Allows for a transfer of tokens to _to
 *@param _to The address to send tokens to
 *@param _amount The amount of tokens to send
 */

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


/**
 *@dev Allows an address with sufficient spending allowance to send tokens on the behalf of _from
 *@param _from The address to send tokens from
 *@param _to The address to send tokens to
 *@param _amount The amount of tokens to send
 */

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


/// @inheritdoc	IRaribleSecondarySales

function getFeeRecipients(uint256 tokenId)
        public
        view
        override
        returns (address payable[] memory recipients)
    {
        // using ERC2981 implementation to get the recipient & amount
        (address recipient, uint256 amount) = royaltyInfo(tokenId, 10000);
        if (amount != 0) {
            recipients = new address payable[](1);
            recipients[0] = payable(recipient);
        }
    }


/**
 * @notice batch received
 */

function onERC1155BatchReceived(address _operator, address _from, uint256[] calldata _ids, uint256[] calldata _amounts, bytes calldata _data) external nonReentrant returns(bytes4) {
        require(msg.sender == address(nft), "DrawCard: only accept self nft");
        require(_ids.length == _amounts.length,"DrawCard: id length must equal amount length");

        for (uint256 i = 0; i < _ids.length; i++) {
            _receiveCards(_from,_ids[i],_amounts[i],_data);
        }

        _operator;

        return ERC1155_BATCH_RECEIVED_VALUE;
    }


/**
 * @notice when card is ready for init
 */

function init() public onlyOperator{
        require(!isInit,"DrawCard: already init!");

        for (uint256 i = 2; i <= 6; i++) {
            /* token 2 - 5 is 1000 piece */
            require(nft.balanceOf(address(this),i) == INIT_BALANCE_B,"DrawCard: cards value not right!");
        }
        /* token 1 is 500 piece */
        require(nft.balanceOf(address(this),1) == INIT_BALANCE_A,"DrawCard: cards value not right!");

        isInit = true;
    }


/**
 * @notice draw card functions
 * @param time draw card time
 * @param seed seed for produce a random number
 */

function drawCard(uint256 time, uint256 seed) public whenNotPaused nonReentrant {
        require(isInit,"DrawCard: not init yet!");
        require(time > 0 && time <= MAX_DRAW_COUNT,"DrawCard: draw card time not good!");
        require(time <= countCard(),"DrawCard: not enough card!");

        uint256 costMee = DRAW_CARD_FEE.mul(time);
        require(mee.transferFrom(msg.sender,dealAddress,costMee),"DrawCard: failed to transfer token!");

        if(randomFlag){
          _getRandomNumber(seed);
          seed = linkRandomResult;
        }

        uint256[] memory ids = new uint256[](5);

        for(uint256 i = 0; i < time; i++) {
          uint256 id = _drawCard(seed);
          nft.safeTransferFrom(address(this),msg.sender,id,1,"");
          ids[id.sub(2)] = ids[id.sub(2)].add(1);
          randomNonce = randomNonce.add(1);
        }

        emit DrawMyCards(msg.sender,ids);
    }


/**
 * @notice transfer cards to deal address for exchange id 1 card
 */

function exchangeCard1(uint256 number) public whenNotPaused nonReentrant{
       require(number > 0, "DrawCard: can not exchange 1 card 0 piece!");
       require(nft.balanceOf(address(this),1) >= number,"DrawCard: not enought card 1 for exchange!");

       uint256[] memory ids = new uint256[](5);
       uint256 value = number.mul(2);
       uint256[] memory values = new uint256[](5);

       for(uint256 i = 0; i < 5; i++) {
           ids[i] = i.add(2);
           values[i] = value;
       }

       nft.safeBatchTransferFrom(msg.sender,dealAddress,ids,values,"");

       /* transfer card 1 for user */
       nft.safeTransferFrom(address(this), msg.sender, 1, number, "");

       emit ExchangeCard1(msg.sender,number);
    }


/**
 * Transfers `amount` tokens from `from` address to `to`
 * the sender needs to have allowance for this operation
 * @param  from - address to take tokens from
 * @param  to - address to send tokens to
 * @param  amount - amount of tokens to send
 * @return success - `true` if the transfer was succesful, `false` otherwise
 */

function transferFrom (address from, address to, uint256 amount)   returns (bool success) {
        if (balances[from] < amount)
            return false;

        if(allowed[from][msg.sender] < amount)
            return false;

        if(amount == 0)
            return false;

        if(balances[to] + amount <= balances[to])
            return false;

        balances[from] -= amount;
        allowed[from][msg.sender] -= amount;
        balances[to] += amount;
        Transfer(from, to, amount);
        return true;
    }


/// @inheritdoc Variety

function plant(address, bytes32[] memory)
        external
        view
        override
        onlySower
        returns (uint256)
    {
        // this ensure that noone, even Sower, can directly mint tokens on this contract
        // they can only be created through the wrapping method
        revert('No direct planting, only wrapping.');
    }


/// @notice Slugify a name (tolower and replace all non 0-9az by -)
/// @param str the string to keyIfy
/// @return the key

function slugify(string memory str) public pure returns (string memory) {
        bytes memory strBytes = bytes(str);
        bytes memory lowerCase = new bytes(strBytes.length);
        uint8 charCode;
        bytes1 char;
        for (uint256 i; i < strBytes.length; i++) {
            char = strBytes[i];
            charCode = uint8(char);

            // if 0-9, a-z use the character
            if (
                (charCode >= 48 && charCode <= 57) ||
                (charCode >= 97 && charCode <= 122)
            ) {
                lowerCase[i] = char;
            } else if (charCode >= 65 && charCode <= 90) {
                // if A-Z, use lowercase
                lowerCase[i] = bytes1(charCode + 32);
            } else {
                // for all others, return a -
                lowerCase[i] = 0x2D;
            }
        }

        return string(lowerCase);
    }


/// @dev allows to set a name internally.
///      checks that the name is valid and not used, else throws
/// @param tokenId the token to name
/// @param seedlingName the name

function _setName(uint256 tokenId, string memory seedlingName) internal {
        bytes32 slugBytes;

        // if the name is not empty, require that it's valid and not used
        if (bytes(seedlingName).length > 0) {
            require(isNameValid(seedlingName) == true, 'Invalid name.');

            // also requires the name is not already used
            slugBytes = keccak256(bytes(slugify(seedlingName)));
            require(usedNames[slugBytes] == false, 'Name already used.');

            // set as used
            usedNames[slugBytes] = true;
        }

        // if it already has a name, mark the old name as unused
        string memory oldName = names[tokenId];
        if (bytes(oldName).length > 0) {
            slugBytes = keccak256(bytes(slugify(oldName)));
            usedNames[slugBytes] = false;
        }

        names[tokenId] = seedlingName;
    }


//Starts a new calculation epoch
// Because averge since start will not be accurate

function startNewEpoch() public {
        for (uint256 _pid = 0; _pid < poolInfo.length; _pid++) {
            require(
                poolInfo[_pid].epochCalculationStartBlock + 50000 <
                    block.number,
                "New epoch not ready yet"
            ); // About a week
            poolInfo[_pid].epochRewards[epoch] = poolInfo[_pid]
                .rewardsInThisEpoch;
            poolInfo[_pid].cumulativeRewardsSinceStart = poolInfo[_pid]
                .cumulativeRewardsSinceStart
                .add(poolInfo[_pid].rewardsInThisEpoch);
            poolInfo[_pid].rewardsInThisEpoch = 0;
            poolInfo[_pid].epochCalculationStartBlock = block.number;
            ++epoch;
        }
    }


// Add a new token pool. Can only be called by the owner.
// Note contract owner is meant to be a governance contract allowing NERD governance consensus

function add(
        uint256 _allocPoint,
        IERC20 _token,
        bool _withUpdate
    ) public onlyOwner {
        require(
            isTokenPairValid(address(_token)),
            "One of the paired tokens must be NERD"
        );
        if (_withUpdate) {
            massUpdatePools();
        }

        uint256 length = poolInfo.length;
        for (uint256 pid = 0; pid < length; ++pid) {
            require(poolInfo[pid].token != _token, "Error pool already added");
        }

        totalAllocPoint = totalAllocPoint.add(_allocPoint);

        poolInfo.push(
            PoolInfo({
                token: _token,
                allocPoint: _allocPoint,
                accNerdPerShare: 0,
                lockedPeriod: LP_LOCKED_PERIOD_WEEKS.mul(LP_RELEASE_TRUNK),
                emergencyWithdrawable: false,
                rewardsInThisEpoch: 0,
                cumulativeRewardsSinceStart: 0,
                startBlock: block.number,
                epochCalculationStartBlock: block.number
            })
        );
    }


//return value is /1000

function getPenaltyFactorForEarlyUnlockers(uint256 _pid, address _addr)
        public
        view
        returns (uint256)
    {
        uint256 lpReleaseStart = getLpReleaseStart(_pid, _addr);
        if (lpReleaseStart == 0 || block.timestamp < lpReleaseStart)
            return 1000;
        uint256 weeksTilNow = weeksSinceLPReleaseTilNow(_pid, _addr);
        uint256 numReleaseWeeks = poolInfo[_pid].lockedPeriod.div(
            LP_RELEASE_TRUNK
        ); //10
        if (weeksTilNow >= numReleaseWeeks) return 0;
        uint256 remainingWeeks = numReleaseWeeks.sub(weeksTilNow);
        //week 1: 45/1000 = 4.5%
        //week 2: 40/1000 = 4%
        uint256 ret = remainingWeeks.mul(50000).div(1000).div(numReleaseWeeks);
        return ret > 1000 ? 1000 : ret;
    }


// Update reward vairables for all pools. Be careful of gas spending!

function massUpdatePools() public {
        console.log("Mass Updating Pools");
        uint256 length = poolInfo.length;
        uint256 allRewards;
        for (uint256 pid = 0; pid < length; ++pid) {
            allRewards = allRewards.add(updatePool(pid));
        }

        pendingRewards = pendingRewards.sub(allRewards);
    }


// ----
// Function that adds pending rewards, called by the NERD token.
// ----

function updatePendingRewards() public {
        uint256 newRewards = nerd.balanceOf(address(this)).sub(nerdBalance);

        if (newRewards > 0) {
            nerdBalance = nerd.balanceOf(address(this)); // If there is no change the balance didn't change
            pendingRewards = pendingRewards.add(newRewards);
        }
    }


// Deposit  tokens to NerdVault for NERD allocation.

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

        massUpdatePools();

        // Transfer pending tokens
        // to user
        updateAndPayOutPending(_pid, msg.sender);

        uint256 lpAccumulationFee = _originAmount.mul(LP_ACCUMULATION_FEE).div(
            1000
        );
        uint256 _amount = _originAmount.sub(lpAccumulationFee);

        //Transfer in the amounts from user
        // save gas
        if (_amount > 0) {
            pool.token.safeTransferFrom(
                address(msg.sender),
                ADDRESS_LOCKED_LP_ACCUMULATION,
                lpAccumulationFee
            );
            pool.token.safeTransferFrom(
                address(msg.sender),
                address(this),
                _amount
            );
            updateDepositTime(_pid, msg.sender, _amount);
            user.amount = user.amount.add(_amount);
        }

        user.rewardDebt = user.amount.mul(pool.accNerdPerShare).div(1e18);
        emit Deposit(msg.sender, _pid, _amount);
    }


// Test coverage
// [x] Does user get the deposited amounts?
// [x] Does user that its deposited for update correcty?
// [x] Does the depositor get their tokens decreased

function depositFor(
        address _depositFor,
        uint256 _pid,
        uint256 _originAmount
    ) public {
        claimLPTokensToFarmingPool(_depositFor);
        // requires no allowances
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][_depositFor];

        massUpdatePools();

        // Transfer pending tokens
        // to user
        updateAndPayOutPending(_pid, _depositFor); // Update the balances of person that amount is being deposited for
        uint256 lpAccumulationFee = _originAmount.mul(LP_ACCUMULATION_FEE).div(
            1000
        );
        uint256 _amount = _originAmount.sub(lpAccumulationFee);

        if (_amount > 0) {
            pool.token.safeTransferFrom(
                address(msg.sender),
                ADDRESS_LOCKED_LP_ACCUMULATION,
                lpAccumulationFee
            );
            pool.token.safeTransferFrom(
                address(msg.sender),
                address(this),
                _amount
            );
            updateDepositTime(_pid, _depositFor, _amount);
            user.amount = user.amount.add(_amount); // This is depositedFor address
        }

        user.rewardDebt = user.amount.mul(pool.accNerdPerShare).div(1e18); /// This is deposited for address
        emit Deposit(_depositFor, _pid, _amount);
    }


// Test coverage
// [x] Does allowance decrease?
// [x] Do oyu need allowance
// [x] Withdraws to correct address

function withdrawFrom(
        address owner,
        uint256 _pid,
        uint256 _amount
    ) public {
        claimLPTokensToFarmingPool(owner);
        PoolInfo storage pool = poolInfo[_pid];
        require(
            pool.allowance[owner][msg.sender] >= _amount,
            "withdraw: insufficient allowance"
        );
        pool.allowance[owner][msg.sender] = pool.allowance[owner][msg.sender]
            .sub(_amount);
        _withdraw(_pid, _amount, owner, msg.sender);
    }


// Low level withdraw function

function _withdraw(
        uint256 _pid,
        uint256 _amount,
        address from,
        address to
    ) internal {
        PoolInfo storage pool = poolInfo[_pid];
        //require(pool.withdrawable, "Withdrawing from this pool is disabled");
        UserInfo storage user = userInfo[_pid][from];
        (
            uint256 withdrawnableAmount,
            uint256 penaltyAmount
        ) = computeReleasableLPWithPenalty(_pid, from);
        require(withdrawnableAmount >= _amount, "withdraw: not good");

        massUpdatePools();
        updateAndPayOutPending(_pid, from); // Update balances of from this is not withdrawal but claiming NERD farmed

        if (_amount > 0) {
            uint256 _actualWithdrawn = _amount;
            uint256 _actualPenalty = _actualWithdrawn.mul(penaltyAmount).div(
                withdrawnableAmount
            );
            user.amount = user.amount.sub(_actualWithdrawn.add(_actualPenalty));

            pool.token.safeTransfer(address(to), _actualWithdrawn);

            if (_actualPenalty > 0) {
                //withdraw liquidtity for _actualPenalty
                uint256 otherTokenAmount = removeLiquidity(
                    address(pool.token),
                    _actualPenalty
                );
                swapTokenForNerd(address(pool.token), otherTokenAmount);

                updatePendingRewards();
            }
        }
        user.rewardDebt = user.amount.mul(pool.accNerdPerShare).div(1e18);

        emit Withdraw(to, _pid, _amount);
    }


// function that lets owner/governance contract
// approve allowance for any token inside this contract
// This means all future UNI like airdrops are covered
// And at the same time allows us to give allowance to strategy contracts.
// Upcoming cYFI etc vaults strategy contracts will  se this function to manage and farm yield on value locked

function setStrategyContractOrDistributionContractAllowance(
        address tokenAddress,
        uint256 _amount,
        address contractAddress
    ) public onlySuperAdmin {
        require(
            isContract(contractAddress),
            "Recipent is not a smart contract, BAD"
        );
        require(
            block.number > contractStartBlock.add(95_000),
            "Governance setup grace period not over"
        );
        IERC20(tokenAddress).approve(contractAddress, _amount);
    }


/**
 * Accepts money deposit and makes record
 * minimum deposit is MINIMUM_DEPOSIT
 * @param beneficiar - the address to receive Tokens
 * @param countryCode - 3-digit country code
 * @dev if `msg.value < MINIMUM_DEPOSIT` throws
 */

function deposit (address beneficiar, uint16 countryCode)   payable  {
        require(msg.value >= MINIMUM_DEPOSIT);
        require(state == State.Sale || state == State.Presale);

        /* this should end any finished period before starting any operations */
        tick();

        /* check if have enough tokens for the current period
         * if not, the call fails until tokens are deposited to the contract
         */
        require(isActive());

        uint256 tokensBought = msg.value / getTokenPrice();

        if(periods[currentPeriod].tokensSold + tokensBought >= tokensForPeriod(currentPeriod)) {
            tokensBought = tokensForPeriod(currentPeriod) - periods[currentPeriod].tokensSold;
        }

        uint256 moneySpent = getTokenPrice() * tokensBought;

        investmentsByCountries[countryCode] += moneySpent;

        if(tokensBought > 0) {
            assert(moneySpent <= msg.value);

            /* return the rest */
            if(msg.value > moneySpent) {
                msg.sender.transfer(msg.value - moneySpent);
            }

            periods[currentPeriod].tokensSold += tokensBought;
            unclaimedTokensForInvestor[beneficiar] += tokensBought;
            totalUnclaimedTokens += tokensBought;
            totalTokensSold += tokensBought;
            Deposited(msg.sender, moneySpent, tokensBought);
        }

        /* if all tokens are sold, get to the next period */
        tick();
    }


/**
 * Set verification of a users challenge
 * @param tokenId - the tokenId of the challenge
 * @param completed - true if the challenge has been verified
 * @return success - true if successful
 */

function setVerify(uint256 tokenId, bool completed)
        public
        returns (bool success)
    {
        require(!verificationPaused, "Verification is paused");
        require(
            tokenIdVerificationComplete[tokenId] == false,
            "Token has already been verified"
        );
        require(
            tokenIdToAccountabilityPartner[tokenId] == msg.sender,
            "Only the accountability partner can verify"
        );

        tokenIdVerificationComplete[tokenId] = true;
        tokenIdVerificationValue[tokenId] = completed;

        emit Verified(
            tokenId,
            completed,
            ownerOf(tokenId),
            accountabilityPartnerOf(tokenId)
        );

        return true;
    }


/**
 * Remove the verification status from the challenge
 * @param tokenId - the tokenId of the challenge
 * @return success - true if successful
 */

function unverify(uint256 tokenId) public returns (bool success) {
        require(!verificationPaused, "Verification is paused");
        require(
            tokenIdVerificationComplete[tokenId] == true,
            "Token has not been verified"
        );
        require(
            tokenIdToAccountabilityPartner[tokenId] == msg.sender,
            "Only the accountability partner can unverify"
        );

        tokenIdVerificationComplete[tokenId] = false;
        tokenIdVerificationValue[tokenId] = false;

        emit Unverified(
            tokenId,
            ownerOf(tokenId),
            accountabilityPartnerOf(tokenId)
        );

        return true;
    }


/**
 * Withdraw the balance of the contract
 * @dev owner only
 * @param _to - the address to send the balance to
 * @param _amount - the amount to send
 * @return sent - true if successful
 * @return data - data from the call
 */

function withdraw(address payable _to, uint256 _amount)
        external
        onlyOwner
        returns (bool sent, bytes memory data)
    {
        require(_amount < address(this).balance, "Not enough balance");
        require(_amount > 0, "Amount must be greater than 0");

        (sent, data) = _to.call{value: _amount}("");

        return (sent, data);
    }


/**
 * Get the URI of a token
 * @param tokenId - the tokenId of the token
 * @return tokenURI - the uri of the token
 */

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

        string memory _tokenURI = tokenIdToIpfsHash[tokenId];
        string memory base = _baseURI();

        // If there is no base URI, return the token URI.
        if (bytes(base).length == 0) {
            return _tokenURI;
        }
        // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
        if (bytes(_tokenURI).length > 0) {
            return string(abi.encodePacked(base, _tokenURI));
        }
        // If there is a baseURI but no tokenURI, return the base because this shouldn't happen
        return string(abi.encodePacked(base));
    }


/**
 * Holder can withdraw the ETH when price >= box.openPrice or now >= box.openTime.
 * After breaking the box, the box will be burned.
 */

function breakBox(uint _boxId) public {
    require(_isApprovedOrOwner(_msgSender(), _boxId), "ERC721: transfer caller is not owner nor approved");
    uint ether_price_now = getEthPrice();
    MoonBox storage box = moonBoxs[_boxId];
    require(box.etherNumber > 0, "This box has been opened");
    if(box.openPrice <= ether_price_now || box.openTime <= now) {
      super._burn(_boxId);
      msg.sender.transfer(box.etherNumber);
      emit BreakBox(_boxId, now);
    } else {
      revert("The break condition is not yet satisfied.");
    }
  }


/**
 * Holder can withdraw the ETH when price >= box.openPrice or now >= box.openTime.
 * After open the box, you still have the box.
 * Developers will receive a fee of.1%.
 */

function openBox(uint _boxId) public {
    require(_isApprovedOrOwner(_msgSender(), _boxId), "ERC721: transfer caller is not owner nor approved");
    uint ether_price_now = getEthPrice();
    MoonBox storage box = moonBoxs[_boxId];
    require(box.etherNumber > 0, "This box has been opened");
    if(box.openPrice <= ether_price_now || box.openTime <= now) {
      uint fee = box.etherNumber / tipRatio;
      uint payout = box.etherNumber.sub(fee);
      box.etherNumber = 0;
      msg.sender.transfer(payout);
      owner.transfer(fee);
      emit OpenBox(_boxId, now);
    } else {
      revert("The open condition is not yet satisfied.");
    }
  }


/**
 * @dev Gets the list of token IDs of the requested owner.
 * @param _owner address owning the tokens
 * @return uint256[] List of token IDs owned by the requested address
 */

function getMoonBoxsByOwner(address _owner) external view returns(uint[] memory) {
    uint boxNum = super.balanceOf(_owner);
    uint[] memory result = new uint[](boxNum);
    uint counter = 0;
    for (uint i = 0; i < moonBoxs.length; i++) {
      if (super._exists(i) && super._isApprovedOrOwner(_owner, i)) {
        result[counter] = i;
        counter++;
      }
    }
    return result;
  }


/**
 * when developer changed the address of the oracle.
 * Users have one week to opt out safely.
 */

function quit(uint _boxId) external {
    require(_isApprovedOrOwner(_msgSender(), _boxId), "ERC721: transfer caller is not owner nor approved");
    MoonBox storage box = moonBoxs[_boxId];
    require(box.etherNumber > 0, "This box has been opened");
    if(now < new_uniswap_address_effective_time) {
      super._burn(_boxId);
      msg.sender.transfer(box.etherNumber);
      emit Quit(_boxId, now);
    } else {
      revert("The quit condition is not yet satisfied.");
    }
  }


///@dev Deposits alTokens into the transmuter
///
///@param amount the amount of alTokens to stake

function stake(uint256 amount)
        public
        noContractAllowed()
        ensureUserActionDelay()
        runPhasedDistribution()
        updateAccount(msg.sender)
        checkIfNewUser()
    {
        require(!pause, "emergency pause enabled");

        // requires approval of AlToken first
        address sender = msg.sender;
        //require tokens transferred in;
        IERC20Burnable(alToken).safeTransferFrom(sender, address(this), amount);
        totalSupplyAltokens = totalSupplyAltokens.add(amount);
        depositedAlTokens[sender] = depositedAlTokens[sender].add(amount);
        emit AlUsdStaked(sender, amount);
    }


/// @dev Sets the keeper list
///
/// This function reverts if the caller is not governance
///
/// @param _keepers the accounts to set states for.
/// @param _states the accounts states.

function setKeepers(address[] calldata _keepers, bool[] calldata _states) external onlyGov() {
        uint256 n = _keepers.length;
        for(uint256 i = 0; i < n; i++) {
            keepers[_keepers[i]] = _states[i];
        }
        emit KeepersSet(_keepers, _states);
    }


/// @dev Updates the active vault.
///
/// This function reverts if the vault adapter is the zero address, if the token that the vault adapter accepts
/// is not the token that this contract defines as the parent asset, or if the contract has not yet been initialized.
///
/// @param _adapter the adapter for the new active vault.

function _updateActiveVault(YearnVaultAdapterWithIndirection _adapter) internal {
        require(_adapter != YearnVaultAdapterWithIndirection(ZERO_ADDRESS), "Transmuter: active vault address cannot be 0x0.");
        require(address(_adapter.token()) == token, "Transmuter.vault: token mismatch.");
        require(!adapters[_adapter], "Adapter already in use");
        adapters[_adapter] = true;
        _vaults.push(VaultWithIndirection.Data({
            adapter: _adapter,
            totalDeposited: 0
        }));

        emit ActiveVaultUpdated(_adapter);
    }


/// @dev Recalls funds from active vault if less than amt exist locally
///
/// @param amt amount of funds that need to exist locally to fulfill pending request

function ensureSufficientFundsExistLocally(uint256 amt) internal {
        uint256 currentBal = IERC20Burnable(token).balanceOf(address(this));
        if (currentBal < amt) {
            uint256 diff = amt - currentBal;
            // get enough funds from active vault to replenish local holdings & fulfill claim request
            _recallExcessFundsFromActiveVault(plantableThreshold.add(diff));
        }
    }


/// @dev Plants or recalls funds from the active vault
///
/// This function plants excess funds in an external vault, or recalls them from the external vault
/// Should only be called as part of distribute()

function _plantOrRecallExcessFunds() internal {
        // check if the transmuter holds more funds than plantableThreshold
        uint256 bal = IERC20Burnable(token).balanceOf(address(this));
        uint256 marginVal = plantableThreshold.mul(plantableMargin).div(100);
        if (bal > plantableThreshold.add(marginVal)) {
            uint256 plantAmt = bal - plantableThreshold;
            // if total funds above threshold, send funds to vault
            VaultWithIndirection.Data storage _activeVault = _vaults.last();
            _activeVault.deposit(plantAmt);
        } else if (bal < plantableThreshold.sub(marginVal)) {
            // if total funds below threshold, recall funds from vault
            // first check that there are enough funds in vault
            uint256 harvestAmt = plantableThreshold - bal;
            _recallExcessFundsFromActiveVault(harvestAmt);
        }
    }


/// @dev Recalls up to the harvestAmt from the active vault
///
/// This function will recall less than harvestAmt if only less is available
///
/// @param _recallAmt the amount to harvest from the active vault

function _recallExcessFundsFromActiveVault(uint256 _recallAmt) internal {
        VaultWithIndirection.Data storage _activeVault = _vaults.last();
        uint256 activeVaultVal = _activeVault.totalValue();
        if (activeVaultVal < _recallAmt) {
            _recallAmt = activeVaultVal;
        }
        if (_recallAmt > 0) {
            _recallFundsFromActiveVault(_recallAmt);
        }
    }


/// @dev Sets the rewards contract.
///
/// This function reverts if the new rewards contract is the zero address or the caller is not the current governance.
///
/// @param _rewards the new rewards contract.

function setRewards(address _rewards) external onlyGov() {
        // Check that the rewards address is not the zero address. Setting the rewards to the zero address would break
        // transfers to the address because of `safeTransfer` checks.
        require(_rewards != ZERO_ADDRESS, "Transmuter: rewards address cannot be 0x0.");

        rewards = _rewards;

        emit RewardsUpdated(_rewards);
    }


/// @dev Migrates transmuter funds to a new transmuter
///
/// @param migrateTo address of the new transmuter

function migrateFunds(address migrateTo) external onlyGov() {
        require(migrateTo != address(0), "cannot migrate to 0x0");
        require(pause, "migrate: set emergency exit first");

        // leave enough funds to service any pending transmutations
        uint256 totalFunds = IERC20Burnable(token).balanceOf(address(this));
        uint256 migratableFunds = totalFunds.sub(totalSupplyAltokens, "not enough funds to service stakes");
        IERC20Burnable(token).approve(migrateTo, migratableFunds);
        ITransmuter(migrateTo).distribute(address(this), migratableFunds);
        emit MigrationComplete(migrateTo, migratableFunds);
    }


/* acceptor mint one baby, left another baby for proposer to claim */

function breed(
        MatingRequest calldata matingRequest,
        uint16 acceptorTokenId,
        bytes calldata sig
    ) external callerIsUser isAtSalePhase(SalePhase.Breed) {
        validateMatingRequest(matingRequest, sig);

        uint16 punkId;
        uint16 baycId;
        address punkOwnerAddress;
        address baycOwnerAddress;

        if (matingRequest.isProposerPunk) {
            punkId = matingRequest.proposerTokenId;
            punkOwnerAddress = matingRequest.proposerAddress;
            baycId = acceptorTokenId;
            baycOwnerAddress = msg.sender;
        } else {
            punkId = acceptorTokenId;
            punkOwnerAddress = msg.sender;
            baycId = matingRequest.proposerTokenId;
            baycOwnerAddress = matingRequest.proposerAddress;
        }

        require(!punkBred(punkId), 'Punk already bred a baby.');
        require(!baycBred(baycId), 'Bayc already bred a baby.');

        bred.set(punkId2Index(punkId));
        bred.set(baycId2Index(baycId));

        // check ownership
        verifyPunkOwnership(punkId, punkOwnerAddress);
        verifyBaycOwnership(baycId, baycOwnerAddress);

        uint16 babyTokenId = uint16(_currentIndex);

        babyParents[babyTokenId] = Parents(
            punkId,
            baycId,
            matingRequest.isProposerPunk,
            false,
            true
        );

        emit Breed(
            matingRequest.proposerTokenId,
            matingRequest.isProposerPunk,
            acceptorTokenId,
            msg.sender,
            babyTokenId
        );

        // mint baby token for acceptor, proposer should claim the twin baby later.
        _mint(msg.sender, 1, '', false);
    }


/* For proposer to claim the baby. */

function claimBaby(uint16 siblingId)
        external
        callerIsUser
        isAtSalePhase(SalePhase.Breed)
    {
        Parents storage parentsInfo = babyParents[siblingId];
        require(parentsInfo.hasParents, 'No baby to be claimed.');
        if (parentsInfo.isProposerPunk) {
            verifyPunkOwnership(parentsInfo.punkTokenId, msg.sender);
        } else {
            verifyBaycOwnership(parentsInfo.baycTokenId, msg.sender);
        }
        require(!parentsInfo.proposerClaimed, 'Baby already claimed.');

        parentsInfo.proposerClaimed = true;

        uint16 babyTokenId = uint16(_currentIndex);
        babyParents[babyTokenId] = parentsInfo;

        emit Claim(babyTokenId, siblingId, msg.sender);
        _mint(msg.sender, 1, '', false);
    }


/**
 * @notice Buy a license
 * @dev Requires value to be equal to the price of the license.
 *      The _owner must not already own a license.
 */

function _buyFrom(address _licenseOwner) internal returns(uint) {
        require(licenseDetails[_licenseOwner].creationTime == 0, "License already bought");

        licenseDetails[_licenseOwner] = LicenseDetails({
            price: price,
            creationTime: block.timestamp
        });

        uint idx = licenseOwners.push(_licenseOwner);
        idxLicenseOwners[_licenseOwner] = idx;

        emit Bought(_licenseOwner, price);

        require(_safeTransferFrom(token, _licenseOwner, burnAddress, price), "Unsuccessful token transfer");

        return idx;
    }


/**
 * @notice Support for "approveAndCall". Callable only by `token()`.
 * @param _from Who approved.
 * @param _amount Amount being approved, need to be equal `price()`.
 * @param _token Token being approved, need to be equal `token()`.
 * @param _data Abi encoded data with selector of `buy(and)`.
 */

function receiveApproval(address _from, uint256 _amount, address _token, bytes memory _data) public {
        require(_amount == price, "Wrong value");
        require(_token == address(token), "Wrong token");
        require(_token == address(msg.sender), "Wrong call");
        require(_data.length == 4, "Wrong data length");

        require(_abiDecodeBuy(_data) == bytes4(0xa6f2ae3a), "Wrong method selector"); //bytes4(keccak256("buy()"))

        _buyFrom(_from);
    }


/**
 * @notice Change acceptAny parameter for arbitrator
 * @param _acceptAny indicates does arbitrator allow to accept any seller/choose sellers
 */

function changeAcceptAny(bool _acceptAny) public {
        require(isLicenseOwner(msg.sender), "Message sender should have a valid arbitrator license");
        require(arbitratorlicenseDetails[msg.sender].acceptAny != _acceptAny,
                "Message sender should pass parameter different from the current one");

        arbitratorlicenseDetails[msg.sender].acceptAny = _acceptAny;
    }


/**
 * @notice Allows arbitrator to accept a seller
 * @param _arbitrator address of a licensed arbitrator
 */

function requestArbitrator(address _arbitrator) public {
       require(isLicenseOwner(_arbitrator), "Arbitrator should have a valid license");
       require(!arbitratorlicenseDetails[_arbitrator].acceptAny, "Arbitrator already accepts all cases");

       bytes32 _id = keccak256(abi.encodePacked(_arbitrator, msg.sender));
       RequestStatus _status = requests[_id].status;
       require(_status != RequestStatus.AWAIT && _status != RequestStatus.ACCEPTED, "Invalid request status");

       if(_status == RequestStatus.REJECTED || _status == RequestStatus.CLOSED){
           require(requests[_id].date + 3 days < block.timestamp,
            "Must wait 3 days before requesting the arbitrator again");
       }

       requests[_id] = Request({
            seller: msg.sender,
            arbitrator: _arbitrator,
            status: RequestStatus.AWAIT,
            date: block.timestamp
       });

       emit ArbitratorRequested(_id, msg.sender, _arbitrator);
    }


/**
 * @notice Allows arbitrator to accept a seller's request
 * @param _id request id
 */

function acceptRequest(bytes32 _id) public {
        require(isLicenseOwner(msg.sender), "Arbitrator should have a valid license");
        require(requests[_id].status == RequestStatus.AWAIT, "This request is not pending");
        require(!arbitratorlicenseDetails[msg.sender].acceptAny, "Arbitrator already accepts all cases");
        require(requests[_id].arbitrator == msg.sender, "Invalid arbitrator");

        requests[_id].status = RequestStatus.ACCEPTED;

        address _seller = requests[_id].seller;
        permissions[msg.sender][_seller] = true;

        emit RequestAccepted(_id, msg.sender, requests[_id].seller);
    }


/**
 * @notice Allows arbitrator to reject a request
 * @param _id request id
 */

function rejectRequest(bytes32 _id) public {
        require(isLicenseOwner(msg.sender), "Arbitrator should have a valid license");
        require(requests[_id].status == RequestStatus.AWAIT || requests[_id].status == RequestStatus.ACCEPTED,
            "Invalid request status");
        require(!arbitratorlicenseDetails[msg.sender].acceptAny, "Arbitrator accepts all cases");
        require(requests[_id].arbitrator == msg.sender, "Invalid arbitrator");

        requests[_id].status = RequestStatus.REJECTED;
        requests[_id].date = block.timestamp;

        address _seller = requests[_id].seller;
        permissions[msg.sender][_seller] = false;

        emit RequestRejected(_id, msg.sender, requests[_id].seller);
    }


/**
 * @notice Allows seller to cancel a request
 * @param _id request id
 */

function cancelRequest(bytes32 _id) public {
        require(requests[_id].seller == msg.sender,  "This request id does not belong to the message sender");
        require(requests[_id].status == RequestStatus.AWAIT || requests[_id].status == RequestStatus.ACCEPTED, "Invalid request status");

        address arbitrator = requests[_id].arbitrator;

        requests[_id].status = RequestStatus.CLOSED;
        requests[_id].date = block.timestamp;

        address _arbitrator = requests[_id].arbitrator;
        permissions[_arbitrator][msg.sender] = false;

        emit RequestCanceled(_id, arbitrator, requests[_id].seller);
    }


/**
 * @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`
 * @param _signature Signature string
 */

function signatureSplit(bytes memory _signature)
        internal
        pure
        returns (uint8 v, bytes32 r, bytes32 s)
    {
        require(_signature.length == 65, "Bad signature length");
        // The signature format is a compact form of:
        //   {bytes32 r}{bytes32 s}{uint8 v}
        // Compact means, uint8 is not padded to 32 bytes.
        assembly {
            r := mload(add(_signature, 32))
            s := mload(add(_signature, 64))
            // Here we are loading the last 32 bytes, including 31 bytes
            // of 's'. There is no 'mload8' to do this.
            //
            // 'byte' is not working due to the Solidity parser, so lets
            // use the second best option, 'and'
            v := and(mload(add(_signature, 65)), 0xff)
        }
        if (v < 27) {
            v += 27;
        }
        require(v == 27 || v == 28, "Bad signature version");
    }


/**
 * @dev Adds or updates user information
 * @param _user User address to update
 * @param _contactData Contact Data   ContactType:UserId
 * @param _location New location
 * @param _username New status username
 */

function _addOrUpdateUser(
        address _user,
        string memory _contactData,
        string memory _location,
        string memory _username
    ) internal {
        User storage u = users[_user];
        u.contactData = _contactData;
        u.location = _location;
        u.username = _username;
    }


/**
 * @notice Adds or updates user information via signature
 * @param _signature Signature
 * @param _contactData Contact Data   ContactType:UserId
 * @param _location New location
 * @param _username New status username
 * @return Signing user address
 */

function addOrUpdateUser(
        bytes calldata _signature,
        string calldata _contactData,
        string calldata _location,
        string calldata _username,
        uint _nonce
    ) external returns(address payable _user) {
        _user = address(uint160(_getSigner(_username, _contactData, _nonce, _signature)));

        require(_nonce == user_nonce[_user], "Invalid nonce");

        user_nonce[_user]++;
        _addOrUpdateUser(_user, _contactData, _location, _username);

        return _user;
    }


/**
 * @dev Add a new offer with a new user if needed to the list
 * @param _asset The address of the erc20 to exchange, pass 0x0 for Eth
 * @param _contactData Contact Data   ContactType:UserId
 * @param _location The location on earth
 * @param _currency The currency the user want to receive (USD, EUR...)
 * @param _username The username of the user
 * @param _paymentMethods The list of the payment methods the user accept
 * @param _limitL Lower limit accepted
 * @param _limitU Upper limit accepted
 * @param _margin The margin for the user
 * @param _arbitrator The arbitrator used by the offer
 */

function addOffer(
        address _asset,
        string memory _contactData,
        string memory _location,
        string memory _currency,
        string memory _username,
        uint[] memory _paymentMethods,
        uint _limitL,
        uint _limitU,
        int16 _margin,
        address payable _arbitrator
    ) public payable {
        //require(sellingLicenses.isLicenseOwner(msg.sender), "Not a license owner");
        // @TODO: limit number of offers if the sender is unlicensed?

        require(arbitrationLicenses.isAllowed(msg.sender, _arbitrator), "Arbitrator does not allow this transaction");

        require(_limitL <= _limitU, "Invalid limits");
        require(msg.sender != _arbitrator, "Cannot arbitrate own offers");

        _addOrUpdateUser(
            msg.sender,
            _contactData,
            _location,
            _username
        );

        Offer memory newOffer = Offer(
            _margin,
            _paymentMethods,
            _limitL,
            _limitU,
            _asset,
            _currency,
            msg.sender,
            _arbitrator,
            false
        );

        uint256 offerId = offers.push(newOffer) - 1;
        offerWhitelist[msg.sender][offerId] = true;
        addressToOffers[msg.sender].push(offerId);

        emit OfferAdded(
            msg.sender,
            offerId,
            _asset,
            _location,
            _currency,
            _username,
            _paymentMethods,
            _limitL,
            _limitU,
            _margin);

        _stake(offerId, msg.sender, _asset);
    }


/**
 * @notice Get the offer by Id
 * @dev normally we'd access the offers array, but it would not return the payment methods
 * @param _id Offer id
 * @return Offer data (see Offer struct)
 */

function offer(uint256 _id) external view returns (
        address asset,
        string memory currency,
        int16 margin,
        uint[] memory paymentMethods,
        uint limitL,
        uint limitH,
        address payable owner,
        address payable arbitrator,
        bool deleted
    ) {
        Offer memory theOffer = offers[_id];

        // In case arbitrator rejects the seller
        address payable offerArbitrator = theOffer.arbitrator;
        if(!arbitrationLicenses.isAllowed(theOffer.owner, offerArbitrator)){
            offerArbitrator = address(0);
        }

        return (
            theOffer.asset,
            theOffer.currency,
            theOffer.margin,
            theOffer.paymentMethods,
            theOffer.limitL,
            theOffer.limitU,
            theOffer.owner,
            offerArbitrator,
            theOffer.deleted
        );
    }


//-------------------------------------------------------------------------------------
//from StandardToken

function super_transfer(address _to, uint _value) /*public*/ internal returns (bool success) {

        require(!isSendingLocked[msg.sender]);
        require(_value <= oneTransferLimit);
        require(balances[msg.sender] >= _value);

        if(msg.sender == contrInitiator) {
            //no restricton
        } else {
            require(!isAllTransfersLocked);
            require(safeAdd(getLast24hSendingValue(msg.sender), _value) <= oneDayTransferLimit);
        }

        balances[msg.sender] = safeSub(balances[msg.sender], _value);
        balances[_to] = safeAdd(balances[_to], _value);

        uint tc=transferInfo[msg.sender].tc;
        transferInfo[msg.sender].ti[tc].value = _value;
        transferInfo[msg.sender].ti[tc].time = now;
        transferInfo[msg.sender].tc = safeAdd(transferInfo[msg.sender].tc, 1);

        emit Transfer(msg.sender, _to, _value);
        return true;
    }


/*
 function getTransferInfoValue(address _from, uint index) public view returns (uint value) {
 return transferInfo[_from].ti[index].value;
 }
 */

function getLast24hSendingValue(address _from) public view returns (uint totVal) {

        totVal = 0;  //declared above;
        uint tc = transferInfo[_from].tc;

        if(tc > 0) {
            for(uint i = tc-1 ; i >= 0 ; i--) {
//              if(now - transferInfo[_from].ti[i].time < 10 minutes) {
//              if(now - transferInfo[_from].ti[i].time < 1 hours) {
                if(now - transferInfo[_from].ti[i].time < 1 days) {
                    totVal = safeAdd(totVal, transferInfo[_from].ti[i].value );
                } else {
                    break;
                }
            }
        }
    }


// Issue a new amount of tokens
// these tokens are deposited into the owner address
//
// @param _amount Number of tokens to be issued

function mint(address _to, uint256 amount) public {

        if (msg.sender != owner()) {
          require(AssociatedContracts[msg.sender] == true, "Function is just for contract owner...");
        }

        require(canMint == true, "Mint Function is Disabled!");
        require(totalSupply() + amount > totalSupply());
        require(totalSupply() + amount < (MaxSupply * (10 ** _decimals)));
        require(balanceOf(msg.sender)  + amount > balanceOf(msg.sender));

        _mint(_to, amount);
        emit Minted(_to, amount);
    }


// Redeem tokens.
// These tokens are withdrawn from the owner address
// if the balance must be enough to cover the redeem
// or the call will fail.
// @param _amount Number of tokens to be issued

function redeem(uint amount) public {

      if (msg.sender != owner()) {
          require(AssociatedContracts[msg.sender] == true, "Function is just for contract owner...");
        }
        require(totalSupply() >= amount);
        require(balanceOf(msg.sender) >= amount);

        _burn(msg.sender, amount);
        emit Redeem(amount);
    }


/*
 * @notice Get the SF reward that can be sent to a function caller right now
 */

function getCallerReward(uint256 timeOfLastUpdate, uint256 defaultDelayBetweenCalls) public view returns (uint256) {
        bool nullRewards = (baseUpdateCallerReward == 0 && maxUpdateCallerReward == 0);
        if (either(timeOfLastUpdate >= now, nullRewards)) return 0;
        uint256 timeElapsed = (timeOfLastUpdate == 0) ? defaultDelayBetweenCalls : subtract(now, timeOfLastUpdate);
        if (either(timeElapsed < defaultDelayBetweenCalls, baseUpdateCallerReward == 0)) {
            return 0;
        }
        uint256 adjustedTime      = subtract(timeElapsed, defaultDelayBetweenCalls);
        uint256 maxPossibleReward = minimum(maxUpdateCallerReward, treasuryAllowance() / RAY);
        if (adjustedTime > maxRewardIncreaseDelay) {
            return maxPossibleReward;
        }
        uint256 calculatedReward = baseUpdateCallerReward;
        if (adjustedTime > 0) {
            calculatedReward = rmultiply(rpower(perSecondCallerRewardIncrease, adjustedTime, RAY), calculatedReward);
        }
        if (calculatedReward > maxPossibleReward) {
            calculatedReward = maxPossibleReward;
        }
        return calculatedReward;
    }


/**
 * @notice Send a stability fee reward to an address
 * @param proposedFeeReceiver The SF receiver
 * @param reward The system coin amount to send
 **/

function rewardCaller(address proposedFeeReceiver, uint256 reward) internal {
        if (address(treasury) == proposedFeeReceiver) return;
        if (either(address(treasury) == address(0), reward == 0)) return;
        address finalFeeReceiver = (proposedFeeReceiver == address(0)) ? msg.sender : proposedFeeReceiver;
        try treasury.pullFunds(finalFeeReceiver, treasury.systemCoin(), reward) {}
        catch(bytes memory revertReason) {
            emit FailRewardCaller(revertReason, finalFeeReceiver, reward);
        }
    }


// --- Management ---

function modifyParameters(bytes32 parameter, address addr) external isAuthorized {
        require(contractEnabled == 1, "RateSetter/contract-not-enabled");
        if (parameter == "orcl") orcl = OracleLike(addr);
        else if (parameter == "oracleRelayer") oracleRelayer = OracleRelayerLike(addr);
        else if (parameter == "treasury") {
          require(StabilityFeeTreasuryLike(addr).systemCoin() != address(0), "RateSetter/treasury-coin-not-set");
          treasury = StabilityFeeTreasuryLike(addr);
        }
        else if (parameter == "pidCalculator") {
          pidCalculator = PIDCalculator(addr);
        }
        else revert("RateSetter/modify-unrecognized-param");
        emit ModifyParameters(
          parameter,
          addr
        );
    }