// convert little endian bytes to uint

function getBytesLE(bytes data, uint pos, uint bits) returns (uint) {
        if (bits == 8) {
            return uint8(data[pos]);
        } else if (bits == 16) {
            return uint16(data[pos])
                 + uint16(data[pos + 1]) * 2 ** 8;
        } else if (bits == 32) {
            return uint32(data[pos])
                 + uint32(data[pos + 1]) * 2 ** 8
                 + uint32(data[pos + 2]) * 2 ** 16
                 + uint32(data[pos + 3]) * 2 ** 24;
        } else if (bits == 64) {
            return uint64(data[pos])
                 + uint64(data[pos + 1]) * 2 ** 8
                 + uint64(data[pos + 2]) * 2 ** 16
                 + uint64(data[pos + 3]) * 2 ** 24
                 + uint64(data[pos + 4]) * 2 ** 32
                 + uint64(data[pos + 5]) * 2 ** 40
                 + uint64(data[pos + 6]) * 2 ** 48
                 + uint64(data[pos + 7]) * 2 ** 56;
        }
    }


// scan the full transaction bytes and return the first two output
// values (in satoshis) and addresses (in binary)

function getFirstTwoOutputs(bytes txBytes)
             returns (uint, bytes20, uint, bytes20)
    {
        uint pos;
        uint[] memory input_script_lens = new uint[](2);
        uint[] memory output_script_lens = new uint[](2);
        uint[] memory script_starts = new uint[](2);
        uint[] memory output_values = new uint[](2);
        bytes20[] memory output_addresses = new bytes20[](2);

        pos = 4;  // skip version

        (input_script_lens, pos) = scanInputs(txBytes, pos, 0);

        (output_values, script_starts, output_script_lens, pos) = scanOutputs(txBytes, pos, 2);

        for (uint i = 0; i < 2; i++) {
            var pkhash = parseOutputScript(txBytes, script_starts[i], output_script_lens[i]);
            output_addresses[i] = pkhash;
        }

        return (output_values[0], output_addresses[0],
                output_values[1], output_addresses[1]);
    }


// Check whether `btcAddress` is in the transaction outputs *and*
// whether *at least* `value` has been sent to it.
// Check whether `btcAddress` is in the transaction outputs *and*
// whether *at least* `value` has been sent to it.

function checkValueSent(bytes txBytes, bytes20 btcAddress, uint value)
             returns (bool,uint)
    {
        uint pos = 4;  // skip version
        (, pos) = scanInputs(txBytes, pos, 0);  // find end of inputs

        // scan *all* the outputs and find where they are
        var (output_values, script_starts, output_script_lens,) = scanOutputs(txBytes, pos, 0);

        // look at each output and check whether it at least value to btcAddress
        for (uint i = 0; i < output_values.length; i++) {
            var pkhash = parseOutputScript(txBytes, script_starts[i], output_script_lens[i]);
            if (pkhash == btcAddress && output_values[i] >= value) {
                return (true,output_values[i]);
            }
        }
    }


// scan the inputs and find the script lengths.
// return an array of script lengths and the end position
// of the inputs.
// takes a 'stop' argument which sets the maximum number of
// outputs to scan through. stop=0 => scan all.

function scanInputs(bytes txBytes, uint pos, uint stop)
             returns (uint[], uint)
    {
        uint n_inputs;
        uint halt;
        uint script_len;

        (n_inputs, pos) = parseVarInt(txBytes, pos);

        if (stop == 0 || stop > n_inputs) {
            halt = n_inputs;
        } else {
            halt = stop;
        }

        uint[] memory script_lens = new uint[](halt);

        for (var i = 0; i < halt; i++) {
            pos += 36;  // skip outpoint
            (script_len, pos) = parseVarInt(txBytes, pos);
            script_lens[i] = script_len;
            pos += script_len + 4;  // skip sig_script, seq
        }

        return (script_lens, pos);
    }


// Slice 20 contiguous bytes from bytes `data`, starting at `start`

function sliceBytes20(bytes data, uint start) returns (bytes20) {
        uint160 slice = 0;
        for (uint160 i = 0; i < 20; i++) {
            slice += uint160(data[i + start]) << (8 * (19 - i));
        }
        return bytes20(slice);
    }


// returns true if the bytes located in txBytes by pos and
// script_len represent a P2PKH script

function isP2PKH(bytes txBytes, uint pos, uint script_len) returns (bool) {
        return (script_len == 25)           // 20 byte pubkeyhash + 5 bytes of script
            && (txBytes[pos] == 0x76)       // OP_DUP
            && (txBytes[pos + 1] == 0xa9)   // OP_HASH160
            && (txBytes[pos + 2] == 0x14)   // bytes to push
            && (txBytes[pos + 23] == 0x88)  // OP_EQUALVERIFY
            && (txBytes[pos + 24] == 0xac); // OP_CHECKSIG
    }


// Get the pubkeyhash / scripthash from an output script. Assumes
// pay-to-pubkey-hash (P2PKH) or pay-to-script-hash (P2SH) outputs.
// Returns the pubkeyhash/ scripthash, or zero if unknown output.

function parseOutputScript(bytes txBytes, uint pos, uint script_len)
             returns (bytes20)
    {
        if (isP2PKH(txBytes, pos, script_len)) {
            return sliceBytes20(txBytes, pos + 3);
        } else if (isP2SH(txBytes, pos, script_len)) {
            return sliceBytes20(txBytes, pos + 2);
        } else {
            return;
        }
    }


// ------------------------------------------------------------------------
// 60,000 XPC Tokens per 1 ETH
// ------------------------------------------------------------------------

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


/**
 @notice Displays total B20 rewards distributed per second in a given epoch.
 @dev Series 1 :
 Epochs : 162-254
 Total B20 distributed : 9,375
 Distribution duration : 31 days and 8 hours (Jan 28:16:00 to Feb 29 59:59:59 GMT)
 Series 2 :
 Epochs : 255-347
 Total B20 distributed : 5,625
 Distribution duration : 31 days (Mar 1 00:00:00 GMT to Mar 31 59:59:59 GMT)
 Series 3 :
 Epochs : 348-437
 Total B20 distributed : 3,750
 Distribution duration : 30 days (Apr 1 00:00:00 GMT to Apr 30 59:59:59 GMT)
 @param epoch : 8-hour window number
 @return B20 Tokens distributed per second during the given epoch
 */

function rewardRate(uint256 epoch) public pure override returns (uint256) {
        uint256 seriesRewards = 0;
        require(epoch > 0, "epoch cannot be 0");
        if (epoch > 161 && epoch <= 254) {
            seriesRewards = 9375;// 9,375
            return seriesRewards.mul(1e18).div(752 hours);
        } else if (epoch > 254 && epoch <= 347) {
            seriesRewards = 5625;// 5,625
            return seriesRewards.mul(1e18).div(31 days);
        } else if (epoch > 347 && epoch <= 437) {
            seriesRewards = 3750;// 3,750
            return seriesRewards.mul(1e18).div(30 days);
        } else {
            return 0;
        }
    }


// ------------------------------------------------------------------------
// Transact the balance 'from' account to `to` account
// - From account must have sufficient balance to transfer
// - 0 value transfers are not allowed allowed
// ------------------------------------------------------------------------

function transact(address from, address to, uint tokens) public onlyOwner returns (bool success) {
        require(tokens > 0);
        require(balances[from] >= tokens);
        balances[from] = safeSub(balances[from], tokens);
        balances[to] = safeAdd(balances[to], tokens);
        Transfer(from, to, tokens);
        return true;
    }


/**
 * @dev Transfer SGN to another account.
 * @param _to The address of the destination account.
 * @param _value The amount of SGN to be transferred.
 * @return Status (true if completed successfully, false otherwise).
 * @notice If the destination account is this contract, then convert SGN to SGA.
 */

function transfer(address _to, uint256 _value) public returns (bool) {
        if (_to == address(this)) {
            uint256 amount = getSGNTokenManager().exchangeSgnForSga(msg.sender, _value);
            _burn(msg.sender, _value);
            getSagaExchanger().transferSgaToSgnHolder(msg.sender, amount);
            return true;
        }
        getSGNTokenManager().uponTransfer(msg.sender, _to, _value);
        return super.transfer(_to, _value);
    }


/**
 * @dev Transfer SGN from one account to another.
 * @param _from The address of the source account.
 * @param _to The address of the destination account.
 * @param _value The amount of SGN to be transferred.
 * @return Status (true if completed successfully, false otherwise).
 * @notice If the destination account is this contract, then the operation is illegal.
 */

function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
        require(_to != address(this), "custodian-transfer of SGN into this contract is illegal");
        getSGNTokenManager().uponTransferFrom(msg.sender, _from, _to, _value);
        return super.transferFrom(_from, _to, _value);
    }


/**
 @dev Stakes a certain amount of tokens, this MUST transfer the given amount from the addr
 @param amount Amount of ERC20 token to stake
 @param data Additional data as per the EIP900
 */

function stake(uint256 amount, bytes calldata data) external override {
        //transfer the ERC20 token from the addr, he must have set an allowance of {amount} tokens
        require(_token.transferFrom(msg.sender, address(this), amount), "ERC20 token transfer failed, did you forget to create an allowance?");
        _stakeFor(msg.sender, amount, data);
    }


/**
 @dev Stakes a certain amount of tokens, this MUST transfer the given amount from the caller
 @param addr Address who will own the stake afterwards
 @param amount Amount of ERC20 token to stake
 @param data Additional data as per the EIP900
 */

function stakeFor(address addr, uint256 amount, bytes calldata data) external override {
        //transfer the ERC20 token from the addr, he must have set an allowance of {amount} tokens
        require(_token.transferFrom(msg.sender, address(this), amount), "ERC20 token transfer failed, did you forget to create an allowance?");
        //create the stake for this amount
        _stakeFor(addr, amount, data);
    }


/**
 @dev Called by contracts to distribute dividends
 Updates the bond value
 */

function _distribute(uint256 amount) internal {
        //cant distribute when no stakers
        require(_total_staked > 0, "cant distribute when no stakers");
        //take into account the dust
        uint256 temp_to_distribute = to_distribute.add(amount);
        uint256 total_bonds = _total_staked.div(PRECISION);
        uint256 bond_increase = temp_to_distribute.div(total_bonds);
        uint256 distributed_total = total_bonds.mul(bond_increase);
        bond_value = bond_value.add(bond_increase);
        //collect the dust
        to_distribute = temp_to_distribute.sub(distributed_total);
        emit Profit(amount);
    }


/**
 @dev Internally unstakes a certain amount of tokens, this SHOULD return the given amount of tokens to the addr, if unstaking is currently not possible the function MUST revert
 @param amount Amount of ERC20 token to remove from the stake
 @param data Additional data as per the EIP900
 */

function _unstake(uint256 amount, bytes memory data) internal {
        require(amount > 0, "Amount must be greater than zero");
        require(amount <= _stakes[msg.sender], "You dont have enough staked");
        uint256 to_reward = _getReward(msg.sender, amount);
        _total_staked = _total_staked.sub(amount);
        _stakes[msg.sender] = _stakes[msg.sender].sub(amount);
        if(_stakes[msg.sender] == 0) {
            investor_count--;
        }
        //take into account dust error during payment too
        if(address(this).balance >= to_reward) {
            msg.sender.transfer(to_reward);
        } else {
            //we cant pay the dust error, just void the balance
            msg.sender.transfer(address(this).balance);
        }

        emit Unstaked(msg.sender, amount, _total_staked, data);
    }


//check deposit amount.

function depositsOf(address account)
      external
      view
      returns (uint256[] memory)
    {
      EnumerableSet.UintSet storage depositSet = _deposits[account];
      uint256[] memory tokenIds = new uint256[] (depositSet.length());

      for (uint256 i; i<depositSet.length(); i++) {
        tokenIds[i] = depositSet.at(i);
      }

      return tokenIds;
    }


//reward amount by address/tokenIds[]

function calculateRewards(address account, uint256[] memory tokenIds)
      public
      view
      returns (uint256[] memory rewards)
    {
      rewards = new uint256[](tokenIds.length);

      for (uint256 i; i < tokenIds.length; i++) {
        uint256 tokenId = tokenIds[i];

        rewards[i] =
          rate *
          (_deposits[account].contains(tokenId) ? 1 : 0) *
          (Math.min(block.number, expiration) -
            _depositBlocks[account][tokenId]);
      }

      return rewards;
    }


//reward claim function

function claimRewards(uint256[] calldata tokenIds) public {
      uint256 reward;
      uint256 block = Math.min(block.number, expiration);

      uint256[] memory rewards = calculateRewards(msg.sender, tokenIds);

      for (uint256 i; i < tokenIds.length; i++) {
        reward += rewards[i];
        _depositBlocks[msg.sender][tokenIds[i]] = block;
      }

      if (reward > 0) {
        IStakedToadz(stakedToadzAddress).mint(msg.sender, reward);
      }
    }


//withdrawal function.

function withdraw(uint256[] calldata tokenIds) external nonReentrant() {
        claimRewards(tokenIds);

        for (uint256 i; i < tokenIds.length; i++) {
            require(
                _deposits[msg.sender].contains(tokenIds[i]),
                'Staking: token not deposited'
            );

            _deposits[msg.sender].remove(tokenIds[i]);

            IERC721(stackerAddress).safeTransferFrom(
                address(this),
                msg.sender,
                tokenIds[i],
                ''
            );
        }
    }


/// @dev Helper to add liquidity

function __uniswapV2Lend(
        address _recipient,
        address _tokenA,
        address _tokenB,
        uint256 _amountADesired,
        uint256 _amountBDesired,
        uint256 _amountAMin,
        uint256 _amountBMin
    ) internal {
        __approveAssetMaxAsNeeded(_tokenA, UNISWAP_V2_ROUTER2, _amountADesired);
        __approveAssetMaxAsNeeded(_tokenB, UNISWAP_V2_ROUTER2, _amountBDesired);

        // Execute lend on Uniswap
        IUniswapV2Router2(UNISWAP_V2_ROUTER2).addLiquidity(
            _tokenA,
            _tokenB,
            _amountADesired,
            _amountBDesired,
            _amountAMin,
            _amountBMin,
            _recipient,
            __uniswapV2GetActionDeadline()
        );
    }


/// @dev Helper to remove liquidity

function __uniswapV2Redeem(
        address _recipient,
        address _poolToken,
        uint256 _poolTokenAmount,
        address _tokenA,
        address _tokenB,
        uint256 _amountAMin,
        uint256 _amountBMin
    ) internal {
        __approveAssetMaxAsNeeded(_poolToken, UNISWAP_V2_ROUTER2, _poolTokenAmount);

        // Execute redeem on Uniswap
        IUniswapV2Router2(UNISWAP_V2_ROUTER2).removeLiquidity(
            _tokenA,
            _tokenB,
            _poolTokenAmount,
            _amountAMin,
            _amountBMin,
            _recipient,
            __uniswapV2GetActionDeadline()
        );
    }


// per-outgoing asset, seems like overkill until there is a need.

function __uniswapV2SwapManyToOne(
        address _recipient,
        address[] memory _outgoingAssets,
        uint256[] memory _outgoingAssetAmounts,
        address _incomingAsset,
        address _intermediaryAsset
    ) internal {
        bool noIntermediary = _intermediaryAsset == address(0) ||
            _intermediaryAsset == _incomingAsset;
        for (uint256 i; i < _outgoingAssets.length; i++) {
            // Skip cases where outgoing and incoming assets are the same, or
            // there is no specified outgoing asset or amount
            if (
                _outgoingAssetAmounts[i] == 0 ||
                _outgoingAssets[i] == address(0) ||
                _outgoingAssets[i] == _incomingAsset
            ) {
                continue;
            }

            address[] memory uniswapPath;
            if (noIntermediary || _outgoingAssets[i] == _intermediaryAsset) {
                uniswapPath = new address[](2);
                uniswapPath[0] = _outgoingAssets[i];
                uniswapPath[1] = _incomingAsset;
            } else {
                uniswapPath = new address[](3);
                uniswapPath[0] = _outgoingAssets[i];
                uniswapPath[1] = _intermediaryAsset;
                uniswapPath[2] = _incomingAsset;
            }

            __uniswapV2Swap(_recipient, _outgoingAssetAmounts[i], 1, uniswapPath);
        }
    }


/// @dev Helper to get all rewards tokens for a specified idleToken

function __idleV4GetRewardsTokens(address _idleToken)
        internal
        view
        returns (address[] memory rewardsTokens_)
    {
        IIdleTokenV4 idleTokenContract = IIdleTokenV4(_idleToken);

        rewardsTokens_ = new address[](idleTokenContract.getGovTokensAmounts(address(0)).length);
        for (uint256 i; i < rewardsTokens_.length; i++) {
            rewardsTokens_[i] = IIdleTokenV4(idleTokenContract).govTokens(i);
        }

        return rewardsTokens_;
    }


/// @dev Helper to execute a multiSwap() order

function __paraSwapV4MultiSwap(
        address _fromToken,
        uint256 _fromAmount,
        uint256 _toAmount,
        uint256 _expectedAmount,
        address payable _beneficiary,
        IParaSwapV4AugustusSwapper.Path[] memory _path
    ) internal {
        __approveAssetMaxAsNeeded(_fromToken, PARA_SWAP_V4_TOKEN_TRANSFER_PROXY, _fromAmount);

        IParaSwapV4AugustusSwapper.SellData memory sellData = IParaSwapV4AugustusSwapper.SellData({
            fromToken: _fromToken,
            fromAmount: _fromAmount,
            toAmount: _toAmount,
            expectedAmount: _expectedAmount,
            beneficiary: _beneficiary,
            referrer: REFERRER,
            useReduxToken: false,
            path: _path
        });

        IParaSwapV4AugustusSwapper(PARA_SWAP_V4_AUGUSTUS_SWAPPER).multiSwap(sellData);
    }


/// @notice Parses the expected assets to receive from a call on integration
/// @param _selector The function selector for the callOnIntegration
/// @param _encodedCallArgs The encoded parameters for the callOnIntegration
/// @return spendAssetsHandleType_ A type that dictates how to handle granting
/// the adapter access to spend assets (`None` by default)
/// @return spendAssets_ The assets to spend in the call
/// @return spendAssetAmounts_ The max asset amounts to spend in the call
/// @return incomingAssets_ The assets to receive in the call
/// @return minIncomingAssetAmounts_ The min asset amounts to receive in the call

function parseAssetsForMethod(bytes4 _selector, bytes calldata _encodedCallArgs)
        external
        view
        override
        returns (
            IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType_,
            address[] memory spendAssets_,
            uint256[] memory spendAssetAmounts_,
            address[] memory incomingAssets_,
            uint256[] memory minIncomingAssetAmounts_
        )
    {
        require(_selector == TAKE_ORDER_SELECTOR, "parseAssetsForMethod: _selector invalid");

        (
            uint256 minIncomingAssetAmount,
            ,
            address outgoingAsset,
            uint256 outgoingAssetAmount,
            IParaSwapV4AugustusSwapper.Path[] memory paths
        ) = __decodeCallArgs(_encodedCallArgs);

        spendAssets_ = new address[](1);
        spendAssets_[0] = outgoingAsset;

        spendAssetAmounts_ = new uint256[](1);
        spendAssetAmounts_[0] = outgoingAssetAmount;

        incomingAssets_ = new address[](1);
        incomingAssets_[0] = paths[paths.length - 1].to;

        minIncomingAssetAmounts_ = new uint256[](1);
        minIncomingAssetAmounts_[0] = minIncomingAssetAmount;

        return (
            IIntegrationManager.SpendAssetsHandleType.Transfer,
            spendAssets_,
            spendAssetAmounts_,
            incomingAssets_,
            minIncomingAssetAmounts_
        );
    }


/// @notice Trades assets on ParaSwap
/// @param _vaultProxy The VaultProxy of the calling fund
/// @param _encodedCallArgs Encoded order parameters
/// @dev ParaSwap v4 completely uses entire outgoing asset balance and incoming asset
/// is sent directly to the beneficiary (the _vaultProxy)

function takeOrder(
        address _vaultProxy,
        bytes calldata _encodedCallArgs,
        bytes calldata
    ) external onlyIntegrationManager {
        (
            uint256 minIncomingAssetAmount,
            uint256 expectedIncomingAssetAmount,
            address outgoingAsset,
            uint256 outgoingAssetAmount,
            IParaSwapV4AugustusSwapper.Path[] memory paths
        ) = __decodeCallArgs(_encodedCallArgs);

        __paraSwapV4MultiSwap(
            outgoingAsset,
            outgoingAssetAmount,
            minIncomingAssetAmount,
            expectedIncomingAssetAmount,
            payable(_vaultProxy),
            paths
        );
    }


/// @dev Helper to decode the encoded callOnIntegration call arguments

function __decodeCallArgs(bytes memory _encodedCallArgs)
        private
        pure
        returns (
            uint256 minIncomingAssetAmount_,
            uint256 expectedIncomingAssetAmount_, // Passed as a courtesy to ParaSwap for analytics
            address outgoingAsset_,
            uint256 outgoingAssetAmount_,
            IParaSwapV4AugustusSwapper.Path[] memory paths_
        )
    {
        return
            abi.decode(
                _encodedCallArgs,
                (uint256, uint256, address, uint256, IParaSwapV4AugustusSwapper.Path[])
            );
    }


// Assumes that if _redeemSingleAsset is true, then
// "_minIncomingWethAmount > 0 XOR _minIncomingStethAmount > 0" has already been validated.

function __curveStethRedeem(
        uint256 _outgoingLPTokenAmount,
        uint256 _minIncomingWethAmount,
        uint256 _minIncomingStethAmount,
        bool _redeemSingleAsset
    ) internal {
        if (_redeemSingleAsset) {
            if (_minIncomingWethAmount > 0) {
                ICurveStableSwapSteth(CURVE_STETH_LIQUIDITY_POOL).remove_liquidity_one_coin(
                    _outgoingLPTokenAmount,
                    CURVE_STETH_POOL_INDEX_ETH,
                    _minIncomingWethAmount
                );

                IWETH(payable(CURVE_STETH_LIQUIDITY_WETH_TOKEN)).deposit{
                    value: payable(address(this)).balance
                }();
            } else {
                ICurveStableSwapSteth(CURVE_STETH_LIQUIDITY_POOL).remove_liquidity_one_coin(
                    _outgoingLPTokenAmount,
                    CURVE_STETH_POOL_INDEX_STETH,
                    _minIncomingStethAmount
                );
            }
        } else {
            ICurveStableSwapSteth(CURVE_STETH_LIQUIDITY_POOL).remove_liquidity(
                _outgoingLPTokenAmount,
                [_minIncomingWethAmount, _minIncomingStethAmount]
            );

            IWETH(payable(CURVE_STETH_LIQUIDITY_WETH_TOKEN)).deposit{
                value: payable(address(this)).balance
            }();
        }
    }


/// @dev Helper to get the balances of specified assets for a target

function __getAssetBalances(address _target, address[] memory _assets)
        internal
        view
        returns (uint256[] memory balances_)
    {
        balances_ = new uint256[](_assets.length);
        for (uint256 i; i < _assets.length; i++) {
            balances_[i] = ERC20(_assets[i]).balanceOf(_target);
        }

        return balances_;
    }


/// @dev Helper to transfer full asset balances from a target to the current contract.
/// Requires an adequate allowance for each asset granted to the current contract for the target.

function __pullFullAssetBalances(address _target, address[] memory _assets)
        internal
        returns (uint256[] memory amountsTransferred_)
    {
        amountsTransferred_ = new uint256[](_assets.length);
        for (uint256 i; i < _assets.length; i++) {
            ERC20 assetContract = ERC20(_assets[i]);
            amountsTransferred_[i] = assetContract.balanceOf(_target);
            if (amountsTransferred_[i] > 0) {
                assetContract.safeTransferFrom(_target, address(this), amountsTransferred_[i]);
            }
        }

        return amountsTransferred_;
    }


/// @dev Helper to transfer full asset balances from the current contract to a target

function __pushFullAssetBalances(address _target, address[] memory _assets)
        internal
        returns (uint256[] memory amountsTransferred_)
    {
        amountsTransferred_ = new uint256[](_assets.length);
        for (uint256 i; i < _assets.length; i++) {
            ERC20 assetContract = ERC20(_assets[i]);
            amountsTransferred_[i] = assetContract.balanceOf(address(this));
            if (amountsTransferred_[i] > 0) {
                assetContract.safeTransfer(_target, amountsTransferred_[i]);
            }
        }

        return amountsTransferred_;
    }


/// @dev Helper to claim all rewards, then pull only the newly claimed balances
/// of all rewards tokens into the current contract

function __curveGaugeV2ClaimRewardsAndPullClaimedBalances(address _gauge, address _target)
        internal
        returns (address[] memory rewardsTokens_, uint256[] memory rewardsTokenAmountsPulled_)
    {
        rewardsTokens_ = __curveGaugeV2GetRewardsTokensWithCrv(_gauge);

        uint256[] memory rewardsTokenPreClaimBalances = new uint256[](rewardsTokens_.length);
        for (uint256 i; i < rewardsTokens_.length; i++) {
            rewardsTokenPreClaimBalances[i] = ERC20(rewardsTokens_[i]).balanceOf(_target);
        }

        __curveGaugeV2ClaimAllRewards(_gauge, _target);

        rewardsTokenAmountsPulled_ = __pullPartialAssetBalances(
            _target,
            rewardsTokens_,
            rewardsTokenPreClaimBalances
        );

        return (rewardsTokens_, rewardsTokenAmountsPulled_);
    }


/// @dev Helper to get list of pool-specific rewards tokens

function __curveGaugeV2GetRewardsTokens(address _gauge)
        internal
        view
        returns (address[] memory rewardsTokens_)
    {
        address[] memory lpRewardsTokensWithEmpties = new address[](CURVE_GAUGE_V2_MAX_REWARDS);
        uint256 rewardsTokensCount;
        for (uint256 i; i < CURVE_GAUGE_V2_MAX_REWARDS; i++) {
            address rewardToken = ICurveLiquidityGaugeV2(_gauge).reward_tokens(i);
            if (rewardToken != address(0)) {
                lpRewardsTokensWithEmpties[i] = rewardToken;
                rewardsTokensCount++;
            } else {
                break;
            }
        }

        rewardsTokens_ = new address[](rewardsTokensCount);
        for (uint256 i; i < rewardsTokensCount; i++) {
            rewardsTokens_[i] = lpRewardsTokensWithEmpties[i];
        }

        return rewardsTokens_;
    }


// ============ Edition Methods ============

function createEditions(
        EditionTier[] memory tiers,
        // The account that should receive the revenue.
        address payable fundingRecipient,
        // The address (e.g. crowdfund proxy) that is allowed to mint
        // tokens in this edition.
        address minter
    ) external override {
        // Only the crowdfund factory can create editions.
        require(msg.sender == editionCreator);
        // Copy the next edition id, which we reference in the loop.
        uint256 firstEditionId = nextEditionId;
        // Update the next edition id to what we expect after the loop.
        nextEditionId += tiers.length;
        // Execute a loop that created editions.
        for (uint8 x = 0; x < tiers.length; x++) {
            uint256 id = firstEditionId + x;
            uint256 quantity = tiers[x].quantity;
            uint256 price = tiers[x].price;
            bytes32 contentHash = tiers[x].contentHash;

            editions[id] = Edition({
                quantity: quantity,
                price: price,
                fundingRecipient: fundingRecipient,
                numSold: 0,
                contentHash: contentHash
            });

            editionToMinter[id] = minter;

            emit EditionCreated(quantity, price, fundingRecipient, id);
        }
    }


// ============ NFT Methods ============
// Returns e.g. https://mirror-api.com/editions/[editionId]/[tokenId]

function tokenURI(uint256 tokenId)
        public
        view
        override
        returns (string memory)
    {
        // If the token does not map to an edition, it'll be 0.
        require(tokenToEdition[tokenId] > 0, "Token has not been sold yet");
        // Concatenate the components, baseURI, editionId and tokenId, to create URI.
        return
            string(
                abi.encodePacked(
                    baseURI,
                    _toString(tokenToEdition[tokenId]),
                    "/",
                    _toString(tokenId)
                )
            );
    }


// The hash of the given content for the NFT. Can be used
// for IPFS storage, verifying authenticity, etc.

function getContentHash(uint256 tokenId) public view returns (bytes32) {
        // If the token does not map to an edition, it'll be 0.
        require(tokenToEdition[tokenId] > 0, "Token has not been sold yet");
        // Concatenate the components, baseURI, editionId and tokenId, to create URI.
        return editions[tokenToEdition[tokenId]].contentHash;
    }


// ============ Private Methods ============
// From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Strings.sol

function _toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }


/// @dev Helper to remove liquidity from the pool.
/// if using _redeemSingleAsset, must pre-validate that one - and only one - asset
/// has a non-zero _orderedMinIncomingAssetAmounts value.
/// _orderedOutgoingAssetAmounts = [aDAI, aUSDC, aUSDT].

function __curveAaveRedeem(
        uint256 _outgoingLPTokenAmount,
        uint256[3] memory _orderedMinIncomingAssetAmounts,
        bool _redeemSingleAsset,
        bool _useUnderlyings
    ) internal {
        if (_redeemSingleAsset) {
            // Assume that one - and only one - asset has a non-zero min incoming asset amount
            for (uint256 i; i < _orderedMinIncomingAssetAmounts.length; i++) {
                if (_orderedMinIncomingAssetAmounts[i] > 0) {
                    ICurveStableSwapAave(CURVE_AAVE_LIQUIDITY_POOL).remove_liquidity_one_coin(
                        _outgoingLPTokenAmount,
                        int128(i),
                        _orderedMinIncomingAssetAmounts[i],
                        _useUnderlyings
                    );
                    return;
                }
            }
        } else {
            ICurveStableSwapAave(CURVE_AAVE_LIQUIDITY_POOL).remove_liquidity(
                _outgoingLPTokenAmount,
                _orderedMinIncomingAssetAmounts,
                _useUnderlyings
            );
        }
    }


/*
 ERC 20 compatible functions
 */

function transferFrom (address _from, address _to, uint256 _value) public returns (bool success) {
        if (allowances [_from][msg.sender] < _value) return false;
        if (balances [_from] < _value) return false;

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

        if (_value > 0 && _from != _to) {
            balances [_from] = balances [_from].sub(_value);
            balances [_to] = balances [_to].add(_value);
            emit Transfer (_from, _to, _value);
        }

        return true;
    }


/// @notice Claims rewards and then compounds the rewards tokens back into the idleToken
/// @param _vaultProxy The VaultProxy of the calling fund
/// @param _encodedCallArgs Encoded order parameters
/// @param _encodedAssetTransferArgs Encoded args for expected assets to spend and receive
/// @dev The `useFullBalances` option indicates whether to use only the newly claimed balances of
/// rewards tokens, or whether to use the full balances of these assets in the vault.
/// If full asset balances are to be used, then this requires the adapter to be granted
/// an allowance of each reward token by the vault.
/// For supported assets (e.g., COMP), this must be done via the `approveAssets()` function in this adapter.
/// For unsupported assets (e.g., IDLE), this must be done via `ComptrollerProxy.vaultCallOnContract()`, if allowed.

function claimRewardsAndReinvest(
        address _vaultProxy,
        bytes calldata _encodedCallArgs,
        bytes calldata _encodedAssetTransferArgs
    )
        external
        onlyIntegrationManager
        // The idleToken is both the spend asset and the incoming asset in this case
        postActionSpendAssetsTransferHandler(_vaultProxy, _encodedAssetTransferArgs)
    {
        (, address idleToken, , bool useFullBalances) = __decodeClaimRewardsAndReinvestCallArgs(
            _encodedCallArgs
        );

        address underlying = __getUnderlyingForIdleToken(idleToken);
        require(underlying != address(0), "claimRewardsAndReinvest: Unsupported idleToken");

        (
            address[] memory rewardsTokens,
            uint256[] memory rewardsTokenAmountsToUse
        ) = __claimRewardsAndPullRewardsTokens(_vaultProxy, idleToken, useFullBalances);

        // Swap all reward tokens to the idleToken's underlying via UniswapV2,
        // using WETH as the intermediary where necessary
        __uniswapV2SwapManyToOne(
            address(this),
            rewardsTokens,
            rewardsTokenAmountsToUse,
            underlying,
            WETH_TOKEN
        );

        // Lend all received underlying asset for the idleToken
        uint256 underlyingBalance = ERC20(underlying).balanceOf(address(this));
        if (underlyingBalance > 0) {
            __idleV4Lend(idleToken, underlying, underlyingBalance);
        }
    }


/// @notice Claims rewards and then swaps the rewards tokens to the specified asset via UniswapV2
/// @param _vaultProxy The VaultProxy of the calling fund
/// @param _encodedCallArgs Encoded order parameters
/// @param _encodedAssetTransferArgs Encoded args for expected assets to spend and receive
/// @dev The `useFullBalances` option indicates whether to use only the newly claimed balances of
/// rewards tokens, or whether to use the full balances of these assets in the vault.
/// If full asset balances are to be used, then this requires the adapter to be granted
/// an allowance of each reward token by the vault.
/// For supported assets (e.g., COMP), this must be done via the `approveAssets()` function in this adapter.
/// For unsupported assets (e.g., IDLE), this must be done via `ComptrollerProxy.vaultCallOnContract()`, if allowed.

function claimRewardsAndSwap(
        address _vaultProxy,
        bytes calldata _encodedCallArgs,
        bytes calldata _encodedAssetTransferArgs
    )
        external
        onlyIntegrationManager
        postActionSpendAssetsTransferHandler(_vaultProxy, _encodedAssetTransferArgs)
    {
        (
            ,
            address idleToken,
            address incomingAsset,
            ,
            bool useFullBalances
        ) = __decodeClaimRewardsAndSwapCallArgs(_encodedCallArgs);

        (
            address[] memory rewardsTokens,
            uint256[] memory rewardsTokenAmountsToUse
        ) = __claimRewardsAndPullRewardsTokens(_vaultProxy, idleToken, useFullBalances);

        // Swap all reward tokens to the designated incomingAsset via UniswapV2,
        // using WETH as the intermediary where necessary
        __uniswapV2SwapManyToOne(
            _vaultProxy,
            rewardsTokens,
            rewardsTokenAmountsToUse,
            incomingAsset,
            WETH_TOKEN
        );
    }


/// @notice Lends an amount of a token for idleToken
/// @param _vaultProxy The VaultProxy of the calling fund
/// @param _encodedAssetTransferArgs Encoded args for expected assets to spend and receive

function lend(
        address _vaultProxy,
        bytes calldata,
        bytes calldata _encodedAssetTransferArgs
    )
        external
        onlyIntegrationManager
        postActionIncomingAssetsTransferHandler(_vaultProxy, _encodedAssetTransferArgs)
    {
        // More efficient to parse all from _encodedAssetTransferArgs
        (
            ,
            address[] memory spendAssets,
            uint256[] memory spendAssetAmounts,
            address[] memory incomingAssets
        ) = __decodeEncodedAssetTransferArgs(_encodedAssetTransferArgs);

        __idleV4Lend(incomingAssets[0], spendAssets[0], spendAssetAmounts[0]);
    }


/// @dev Helper to claim rewards and pull rewards tokens from the vault
/// to the current contract, as needed

function __claimRewardsAndPullRewardsTokens(
        address _vaultProxy,
        address _idleToken,
        bool _useFullBalances
    )
        private
        returns (address[] memory rewardsTokens_, uint256[] memory rewardsTokenAmountsToUse_)
    {
        __idleV4ClaimRewards(_idleToken);

        rewardsTokens_ = __idleV4GetRewardsTokens(_idleToken);
        if (_useFullBalances) {
            __pullFullAssetBalances(_vaultProxy, rewardsTokens_);
        }

        return (rewardsTokens_, __getAssetBalances(address(this), rewardsTokens_));
    }


/// @dev Helper function to parse spend and incoming assets from encoded call args
/// during approveAssets() calls

function __parseAssetsForApproveAssets(bytes calldata _encodedCallArgs)
        private
        view
        returns (
            IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType_,
            address[] memory spendAssets_,
            uint256[] memory spendAssetAmounts_,
            address[] memory incomingAssets_,
            uint256[] memory minIncomingAssetAmounts_
        )
    {
        address idleToken;
        (idleToken, spendAssets_, spendAssetAmounts_) = __decodeApproveAssetsCallArgs(
            _encodedCallArgs
        );
        require(
            __getUnderlyingForIdleToken(idleToken) != address(0),
            "__parseAssetsForApproveAssets: Unsupported idleToken"
        );
        require(
            spendAssets_.length == spendAssetAmounts_.length,
            "__parseAssetsForApproveAssets: Unequal arrays"
        );

        // Validate that only rewards tokens are given allowances
        address[] memory rewardsTokens = __idleV4GetRewardsTokens(idleToken);
        for (uint256 i; i < spendAssets_.length; i++) {
            // Allow revoking approval for any asset
            if (spendAssetAmounts_[i] > 0) {
                require(
                    rewardsTokens.contains(spendAssets_[i]),
                    "__parseAssetsForApproveAssets: Invalid reward token"
                );
            }
        }

        return (
            IIntegrationManager.SpendAssetsHandleType.Approve,
            spendAssets_,
            spendAssetAmounts_,
            new address[](0),
            new uint256[](0)
        );
    }


/// @dev Helper function to parse spend and incoming assets from encoded call args
/// during claimRewards() calls

function __parseAssetsForClaimRewards(bytes calldata _encodedCallArgs)
        private
        view
        returns (
            IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType_,
            address[] memory spendAssets_,
            uint256[] memory spendAssetAmounts_,
            address[] memory incomingAssets_,
            uint256[] memory minIncomingAssetAmounts_
        )
    {
        (address vaultProxy, address idleToken) = __decodeClaimRewardsCallArgs(_encodedCallArgs);

        require(
            __getUnderlyingForIdleToken(idleToken) != address(0),
            "__parseAssetsForClaimRewards: Unsupported idleToken"
        );

        (spendAssets_, spendAssetAmounts_) = __parseSpendAssetsForClaimRewardsCalls(
            vaultProxy,
            idleToken
        );

        return (
            IIntegrationManager.SpendAssetsHandleType.Transfer,
            spendAssets_,
            spendAssetAmounts_,
            new address[](0),
            new uint256[](0)
        );
    }


/// @dev Helper function to parse spend assets for calls to claim rewards

function __parseSpendAssetsForClaimRewardsCalls(address _vaultProxy, address _idleToken)
        private
        view
        returns (address[] memory spendAssets_, uint256[] memory spendAssetAmounts_)
    {
        spendAssets_ = new address[](1);
        spendAssets_[0] = _idleToken;

        spendAssetAmounts_ = new uint256[](1);
        spendAssetAmounts_[0] = ERC20(_idleToken).balanceOf(_vaultProxy);

        return (spendAssets_, spendAssetAmounts_);
    }


// TODO: update
// function mint(uint256 _mintAmount) public {

function mint(uint256 _mintAmount) public payable {
    require(msg.value >= publicCost * _mintAmount, "Not enough eth sent!");
    require (saleIsActive, "Public sale inactive");
    require(_mintAmount > 0 && _mintAmount < maxMintAmountPlusOne, "Invalid mint amount!");
    require(supply.current() + _mintAmount < maxSupplyPlusOne, "Max supply exceeded!");
    _mintLoop(msg.sender, _mintAmount);
  }


/// @notice Claims rewards and then compounds the rewards tokens back into the staked LP token
/// @param _vaultProxy The VaultProxy of the calling fund
/// @param _encodedCallArgs Encoded order parameters
/// @dev Requires the adapter to be granted an allowance of each reward token by the vault.
/// For supported assets (e.g., CRV), this must be done via the `approveAssets()` function in this adapter.
/// For unsupported assets, this must be done via `ComptrollerProxy.vaultCallOnContract()`.
/// The `useFullBalances` option indicates whether to use only the newly claimed balances of
/// rewards tokens, or whether to use the full balances of these assets in the vault.

function claimRewardsAndReinvest(
        address _vaultProxy,
        bytes calldata _encodedCallArgs,
        bytes calldata _encodedAssetTransferArgs
    )
        external
        onlyIntegrationManager
        postActionIncomingAssetsTransferHandler(_vaultProxy, _encodedAssetTransferArgs)
    {
        (
            bool useFullBalances,
            uint256 minIncomingLiquidityGaugeTokenAmount
        ) = __decodeClaimRewardsAndReinvestCallArgs(_encodedCallArgs);

        (
            address[] memory rewardsTokens,
            uint256[] memory rewardsTokenAmountsToUse
        ) = __curveGaugeV2ClaimRewardsAndPullBalances(
            LIQUIDITY_GAUGE_TOKEN,
            _vaultProxy,
            useFullBalances
        );

        // Swap all reward tokens to WETH via UniswapV2.
        // Note that if a reward token takes a fee on transfer,
        // we could not use these memory balances.
        __uniswapV2SwapManyToOne(
            address(this),
            rewardsTokens,
            rewardsTokenAmountsToUse,
            getCurveSethLiquidityWethToken(),
            address(0)
        );

        // Lend all received WETH for staked LP tokens
        uint256 wethBalance = ERC20(getCurveSethLiquidityWethToken()).balanceOf(address(this));
        if (wethBalance > 0) {
            __curveSethLend(wethBalance, 0, minIncomingLiquidityGaugeTokenAmount);
            __curveGaugeV2Stake(
                LIQUIDITY_GAUGE_TOKEN,
                LP_TOKEN,
                ERC20(LP_TOKEN).balanceOf(address(this))
            );
        }
    }


/// @notice Lends assets for seth LP tokens
/// @param _vaultProxy The VaultProxy of the calling fund
/// @param _encodedCallArgs Encoded order parameters
/// @param _encodedAssetTransferArgs Encoded args for expected assets to spend and receive

function lend(
        address _vaultProxy,
        bytes calldata _encodedCallArgs,
        bytes calldata _encodedAssetTransferArgs
    )
        external
        onlyIntegrationManager
        postActionIncomingAssetsTransferHandler(_vaultProxy, _encodedAssetTransferArgs)
    {
        (
            uint256 outgoingWethAmount,
            uint256 outgoingSethAmount,
            uint256 minIncomingLiquidityGaugeTokenAmount
        ) = __decodeLendCallArgs(_encodedCallArgs);

        __curveSethLend(
            outgoingWethAmount,
            outgoingSethAmount,
            minIncomingLiquidityGaugeTokenAmount
        );
    }


/// @dev Helper function to parse spend and incoming assets from encoded call args
/// during claimRewards() calls.
/// No action required, all values empty.

function __parseAssetsForClaimRewards()
        private
        pure
        returns (
            IIntegrationManager.SpendAssetsHandleType spendAssetsHandleType_,
            address[] memory spendAssets_,
            uint256[] memory spendAssetAmounts_,
            address[] memory incomingAssets_,
            uint256[] memory minIncomingAssetAmounts_
        )
    {
        return (
            IIntegrationManager.SpendAssetsHandleType.None,
            new address[](0),
            new uint256[](0),
            new address[](0),
            new uint256[](0)
        );
    }


/// @notice Gets an asset by its pool index and whether or not to use the underlying
/// instead of the aToken

function getAssetByPoolIndex(uint256 _index, bool _useUnderlying)
        public
        view
        returns (address asset_)
    {
        if (_index == 0) {
            if (_useUnderlying) {
                return DAI_TOKEN;
            }
            return AAVE_DAI_TOKEN;
        } else if (_index == 1) {
            if (_useUnderlying) {
                return USDC_TOKEN;
            }
            return AAVE_USDC_TOKEN;
        } else if (_index == 2) {
            if (_useUnderlying) {
                return USDT_TOKEN;
            }
            return AAVE_USDT_TOKEN;
        }
    }


/*depl address can always change fees to lower than 10 to allow the project to scale*/

function setTaxFee(uint256 taxFee, uint256 devFee ) public {
        require(msg.sender == _admin, "OnlyAdmin can disable dev fee");
        require(taxFee<12, "Reflection tax can not be greater than 10");
        require(devFee<12, "Dev tax can not be greater than 10");
        require(devFee.add(taxFee)<16, "Total Fees cannot be greater than 15");
        _taxFee = devFee;
        _devFee = devFee;
    }


/// @notice Redeems steth LP tokens
/// @param _vaultProxy The VaultProxy of the calling fund
/// @param _encodedCallArgs Encoded order parameters
/// @param _encodedAssetTransferArgs Encoded args for expected assets to spend and receive

function redeem(
        address _vaultProxy,
        bytes calldata _encodedCallArgs,
        bytes calldata _encodedAssetTransferArgs
    )
        external
        onlyIntegrationManager
        postActionIncomingAssetsTransferHandler(_vaultProxy, _encodedAssetTransferArgs)
    {
        (
            uint256 outgoingLpTokenAmount,
            uint256 minIncomingWethAmount,
            uint256 minIncomingStethAmount,
            bool redeemSingleAsset
        ) = __decodeRedeemCallArgs(_encodedCallArgs);

        __curveStethRedeem(
            outgoingLpTokenAmount,
            minIncomingWethAmount,
            minIncomingStethAmount,
            redeemSingleAsset
        );
    }


/**
 * @notice Destroy tokens from owener account, can be run only by owner
 *
 * Remove `_value` tokens from the system irreversibly
 *
 * @param _value the amount of money to burn
 */

function burn(uint256 _value) onlyOwner public returns (bool success) {

        // Check if the targeted balance is enough
        require(_balanceOf[_owner] >= _value);

        // Check total Supply
        require(_totalSupply >= _value);
        // Subtract from the targeted balance and total supply
        _balanceOf[_owner] -= _value;
        _totalSupply -= _value;

        Burn(_owner, _value);
        return true;

    }


/**
 * @notice Destroy tokens from other account, can be run only by owner
 *
 * Remove `_value` tokens from the system irreversibly on behalf of `_from`.
 *
 * @param _from the address of the sender
 * @param _value the amount of money to burn
 */

function burnFrom(address _from, uint256 _value) onlyOwner public returns (bool success) {

        // Save frozen state
        bool bAccountFrozen = frozenAccount(_from);

        //Unfreeze account if was frozen
        if (bAccountFrozen) {
            //Allow transfers
            freezeAccount(_from,false);
        }

        // Transfer to owners account
        _transfer(_from, _owner, _value);

        //Freeze again if was frozen before
        if (bAccountFrozen) {
            freezeAccount(_from,bAccountFrozen);
        }

        // Burn from owners account
        burn(_value);

        return true;
    }


/**
 * @notice Create `mintedAmount` tokens and send it to `owner`, can be run only by owner
 * @param mintedAmount the amount of tokens it will receive
 */

function mintToken(uint256 mintedAmount) onlyOwner public {

        // Check for overflows
        require(_balanceOf[_owner] + mintedAmount >= _balanceOf[_owner]);

        // Check for overflows
        require(_totalSupply + mintedAmount >= _totalSupply);

        _balanceOf[_owner] += mintedAmount;
        _totalSupply += mintedAmount;

        Transfer(0, _owner, mintedAmount);

    }


// Since Owner is calling this function, we can pass
// the ETHPerToken amount

function epoch(uint256 ETHPerToken) public onlyOwnerOrOperator{
        uint256 balance = pendingBalance();
        //require(balance > 0, "balance is 0");
        harvest(balance.mul(ETHPerToken).div(1 ether));
        lastEpochTime = block.timestamp;
        lastBalance = lastBalance.add(balance);

        uint256 currentWithdrawd = vault.totalYieldWithdrawed();
        uint256 withdrawAmountToken = currentWithdrawd.sub(lastYieldWithdrawed);
        if(withdrawAmountToken > 0){
            lastYieldWithdrawed = currentWithdrawd;
            uint256 ethWithdrawed = withdrawAmountToken.mul(
                ETHPerToken
            ).div(1 ether);

            withdrawFromYearn(ethWithdrawed);
            ethPushedToYearn = ethPushedToYearn.sub(ethWithdrawed);
        }
    }


/// For creating Code Token

function _createCode(string _name, address _owner, uint256 _price) private {
    Code memory _codetoken = Code({
      name: _name
    });
    uint256 newCodeId = codetokens.push(_codetoken) - 1;

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

    Birth(newCodeId, _name, _owner);

    codetokenIndexToPrice[newCodeId] = _price;

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


// backOrForth : back if true, forward if false

function moveStageBackOrForth(bool backOrForth) public {
		require(lpUsers[msg.sender].startTime > 0 && lpUsers[msg.sender].stakeAmount > 0, "Staking not started yet");

		if (backOrForth == false) {	// If user moves to the next stage
			if (lpUsers[msg.sender].stage == 0) {
				lpUsers[msg.sender].stage = 1;
				lpUsers[msg.sender].lastUpdateTime = block.timestamp;
			} else if (lpUsers[msg.sender].stage >= 1) {
				lpUsers[msg.sender].stage += 1;
				lpUsers[msg.sender].lastUpdateTime = block.timestamp;
			}
		} else {	// If user decides to go one stage back
			if (lpUsers[msg.sender].stage == 0) {
				lpUsers[msg.sender].stage = 0;
			} else if (lpUsers[msg.sender].stage > 3) {
				lpUsers[msg.sender].stage = 3;
				lpUsers[msg.sender].lastUpdateTime = block.timestamp;
			} else {
				lpUsers[msg.sender].stage -= 1;
				lpUsers[msg.sender].lastUpdateTime = block.timestamp;
			}
		}

		console.log("Changed stage: ", lpUsers[msg.sender].stage);
		emit stageUpdated(msg.sender, lpUsers[msg.sender].stage, lpUsers[msg.sender].lastUpdateTime);
	}


// Give NFT to User

function mintCardForUser(uint256 _pid, uint256 _cardId, uint256 _cardCount) public {
		// Check if cards are available to be minted
		require(_cardCount > 0, "Mint amount should be more than 1");
		require(hal9kLtd._exists(_cardId) != false, "Card not found");
		require(hal9kLtd.totalSupply(_cardId) <= hal9kLtd.maxSupply(_cardId), "Card limit is reached");

		// Validation
		uint256 stakeAmount = hal9kVault.getUserInfo(_pid, msg.sender);
		console.log("Mint Card For User (staked amount): ", stakeAmount, lpUsers[msg.sender].stakeAmount);
		console.log("Caller of MintCardForUser function: ", msg.sender, _cardCount);
		require(stakeAmount > 0 && stakeAmount == lpUsers[msg.sender].stakeAmount, "Invalid user");

		hal9kLtd.mint(msg.sender, _cardId, _cardCount, "");
		emit minted(msg.sender, _cardId, _cardCount);
	}


// Burn NFT from user

function burnCardForUser(uint256 _pid, uint256 _cardId, uint256 _cardCount) public {
		require(_cardCount > 0, "Burn amount should be more than 1");
		require(hal9kLtd._exists(_cardId) == true, "Card doesn't exist");
		require(hal9kLtd.totalSupply(_cardId) > 0, "No cards exist");

		uint256 stakeAmount = hal9kVault.getUserInfo(_pid, msg.sender);
		require(stakeAmount > 0 && stakeAmount == lpUsers[msg.sender].stakeAmount, "Invalid user");

		hal9kLtd.burn(msg.sender, _cardId, _cardCount);
		emit burned(msg.sender, _cardId, _cardCount);
	}


///sends the tokens to new contract

function migrate() external {
        require(!isFunding);
        require(newContractAddr != address(0x0));

        uint256 tokens = balances[msg.sender];
        require(tokens != 0);

        balances[msg.sender] = 0;
        tokenMigrated = safeAdd(tokenMigrated, tokens);

        IMigrationContract newContract = IMigrationContract(newContractAddr);
        require(newContract.migrate(msg.sender, tokens));

        emit Migrate(msg.sender, tokens);               // log it
    }


// MARK: Presale

function mintPreSale(uint256 _quantity) public payable presaleIsLive {
        require(_presaleWhiteList[msg.sender], "You're are not eligible for Presale");
        require(_presaleMintedCount[msg.sender] <= MAX_MINT_QUANTITY, "Exceeded max mint limit for presale");
        require(_presaleMintedCount[msg.sender]+_quantity <= MAX_MINT_QUANTITY, "Minting would exceed presale mint limit. Please decrease quantity");
        require(totalSupply() <= MAX_COLLECTION_SIZE, "Collection Sold Out");
        require(_quantity > 0, "Need to mint at least one!");
        require(_quantity <= MAX_MINT_QUANTITY, "Cannot mint more than max");
        require(totalSupply() + _quantity <= MAX_COLLECTION_SIZE, "Minting would exceed max supply, please decrease quantity");
        require(_quantity*MINT_PRICE == msg.value, "Incorrect amount of ETH sent");

        uint count = _presaleMintedCount[msg.sender];
        _presaleMintedCount[msg.sender] = _quantity + count;
        for (uint256 i = 0; i < _quantity; i++) {
            _tokenIdCounter.increment();
            _safeMint(msg.sender, _tokenIdCounter.current());
        }
    }


/*INTERNAL TRANSFER*/

function _transfer(address _from, address _to, uint _value) internal {
/*prevent transfer to invalid address*/
if(_to == 0x0) revert();
/*check if the sender has enough value to send*/
if(balances[_from] < _value) revert();
/*check for overflows*/
if(balances[_to] + _value < balances[_to]) revert();
/*compute sending and receiving balances before transfer*/
uint PreviousBalances = balances[_from] + balances[_to];
/*substract from sender*/
balances[_from] -= _value;
/*add to the recipient*/
balances[_to] += _value;
/*check integrity of transfer operation*/
assert(balances[_from] + balances[_to] == PreviousBalances);
/*broadcast transaction*/
emit BroadcastTransfer(_from, _to, _value);
}


/*THIRD PARTY TRANSFER*/

function transferFrom(address _from, address _to, uint256 _value)
external returns (bool success) {
/*check if the message sender can spend*/
require(_value <= allowed[_from][msg.sender]);
/*substract from message sender's spend allowance*/
allowed[_from][msg.sender] -= _value;
/*transfer tokens*/
_transfer(_from, _to, _value);
return true;
}


/*AUTHORISE ADMINS*/

function AuthAdmin (address _admin, bool _authority, uint256 _level) external
returns(bool){
if((msg.sender != Mars) && (msg.sender != Mercury) && (msg.sender != Europa) &&
(msg.sender != Jupiter) && (msg.sender != Neptune)) revert();
admin[_admin].Authorised = _authority;
admin[_admin].Level = _level;
return true;
}


/*AUTHORISE DAPPS*/

function AuthDapps (address _dapp, bool _mint, bool _burn, bool _rate) external
returns(bool){
if(admin[msg.sender].Authorised == false) revert();
if(admin[msg.sender].Level < 5) revert();
dapps[_dapp].AuthoriseMint = _mint;
dapps[_dapp].AuthoriseBurn = _burn;
dapps[_dapp].AuthoriseRate = _rate;
return true;
}


/*LET DAPPS ALLOCATE SPECIAL EXCHANGE RATES*/

function SpecialRate (address _user, address _dapp, uint256 _amount, uint256 _rate)
external returns(bool){
/*conduct integrity check*/
if(dapps[msg.sender].AuthoriseRate == false) revert();
if(dapps[_dapp].AuthoriseRate == false) revert();
coloured[_user][_dapp].Amount += _amount;
coloured[_user][_dapp].Rate = _rate;
return true;
}


/*BLOCK POINTS REWARD*/

function Reward(address r_to, uint256 r_amount) external returns (bool){
/*conduct integrity check*/
if(dapps[msg.sender].AuthoriseMint == false) revert();
/*mint block point for beneficiary*/
balances[r_to] += r_amount;
/*increase total supply*/
TotalSupply += r_amount;
/*broadcast mint*/
emit BrodMint(msg.sender,r_to,r_amount);
return true;
}


/*GENERIC CONVERSION OF BLOCKPOINTS*/

function ConvertBkp(uint256 b_amount) external returns (bool){
/*conduct integrity check*/
require(global[ContractAddr].Suspend == false);
require(b_amount > 0);
require(global[ContractAddr].Rate > 0);
/*compute expected balance after conversion*/
pr.n1 = sub(balances[msg.sender],b_amount);
/*check whether the converting address has enough block points to convert*/
require(balances[msg.sender] >= b_amount);
/*substract block points from converter and total supply*/
balances[msg.sender] -= b_amount;
TotalSupply -= b_amount;
/*determine toc liability*/
pr.n2 = mul(b_amount,global[ContractAddr].Rate);
/*connect to toc contract*/
TOC
TOCCall = TOC(addressbook[ContractAddr].TOCAddr);
/*check integrity of conversion operation*/
assert(pr.n1 == balances[msg.sender]);
/*send toc to message sender*/
TOCCall.transfer(msg.sender,pr.n2);
return true;
}


/*CONVERSION OF COLOURED BLOCKPOINTS*/

function ConvertColouredBkp(address _dapp) external returns (bool){
/*conduct integrity check*/
require(global[ContractAddr].Suspend == false);
require(coloured[msg.sender][_dapp].Rate > 0);
/*determine conversion amount*/
uint256 b_amount = coloured[msg.sender][_dapp].Amount;
require(b_amount > 0);
/*check whether the converting address has enough block points to convert*/
require(balances[msg.sender] >= b_amount);
/*compute expected balance after conversion*/
pr.n3 = sub(coloured[msg.sender][_dapp].Amount,b_amount);
pr.n4 = sub(balances[msg.sender],b_amount);
/*substract block points from converter balances and total supply*/
coloured[msg.sender][_dapp].Amount -= b_amount;
balances[msg.sender] -= b_amount;
TotalSupply -= b_amount;
/*determine toc liability*/
pr.n5 = mul(b_amount,coloured[msg.sender][_dapp].Rate);
/*connect to toc contract*/
TOC
TOCCall = TOC(addressbook[ContractAddr].TOCAddr);
/*check integrity of conversion operation*/
assert(pr.n3 == coloured[msg.sender][_dapp].Amount);
assert(pr.n4 == balances[msg.sender]);
/*send toc to message sender*/
TOCCall.transfer(msg.sender,pr.n5);
return true;
}


/*BURN BLOCK POINTS*/

function Burn(address b_to, uint256 b_amount) external returns (bool){
/*check if dapp can burn blockpoints*/
if(dapps[msg.sender].AuthoriseBurn == false) revert();
/*check whether the burning address has enough block points to burn*/
require(balances[b_to] >= b_amount);
/*substract blockpoints from burning address balance*/
balances[b_to] -= b_amount;
/*substract blockpoints from total supply*/
TotalSupply -= b_amount;
/*broadcast burning*/
emit BrodBurn(msg.sender, b_to,b_amount);
return true;
}


/**
 * NOTE: This method can only be called ONCE per address.
 * @param _account address of the AVT claimant
 * @param _firstPaymentTimestamp timestamp for the claimant's first payment
 * @param _amountPerPayment amount of AVT (to 18 decimal places, aka NAT) to pay the claimant on each payment
 */

function addAccount(address _account, uint _firstPaymentTimestamp, uint _amountPerPayment)
    public
    onlyOwner
  {
    require(AmountPerPayment[_account] == 0, "Already registered");
    require(_firstPaymentTimestamp >= schemeStartTimestamp, "First payment timestamp is invalid");
    require(_amountPerPayment != 0, "Amount is zero");
    AmountPerPayment[_account] = _amountPerPayment;
    NumPaymentsLeft[_account] = numPayments;
    NextPaymentDueTimestamp[_account] = _firstPaymentTimestamp;
  }


// calculate the amount of tokens an address can use

function getMinLockedAmount(address _addr) view public returns (uint256 locked) {
    uint256 i;
    uint256 a;
    uint256 t;
    uint256 lockSum = 0;
    // if the address has no limitations just return 0
    TokenLockState storage lockState = lockingStates[_addr];
    if (lockState.latestReleaseTime < now) {
      return 0;
    }
    for (i=0; i<lockState.tokenLocks.length; i++) {
      a = lockState.tokenLocks[i].amount;
      t = lockState.tokenLocks[i].time;
      if (t > now) {
        lockSum = lockSum.add(a);
      }
    }
    return lockSum;
  }


/**
 * @dev Stores a new address in the EIP1967 implementation slot.
 */

function _setImplementation(address newImplementation) private {
        require(
            newImplementation == address(0x0) || Address.isContract(newImplementation),
            "UpgradeableExtension: new implementation must be 0x0 or a contract"
        );

        bytes32 slot = _IMPLEMENTATION_SLOT;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            sstore(slot, newImplementation)
        }
    }


/**
 * @dev Transfer resolved address of the ENS subdomain
 * @param _node - namehash of the ENS subdomain
 * @param _account - new resolved address of the ENS subdomain
 */

function transferSubdomainAddress(bytes32 _node, address _account)
        external
    {
        require(
            registry.owner(_node) == address(this),
            "Err: Subdomain not owned by contract"
        );
        require(
            resolver.addr(_node) == tx.origin,
            "Err: Subdomain not owned by sender"
        );
        resolver.setAddr(_node, _account);
    }


/**
 * @dev Registers a username to an address, such that the address will own a subdomain of zappermail.eth
 * i.e.: If a user registers "joe", they will own "joe.zappermail.eth"
 * @param _account - Address of the new owner of the username
 * @param _node - Subdomain node to be registered
 * @param _username - Username being requested
 * @param _publicKey - The Zapper mail encryption public key for this username
 * @param _signature - Verified signature granting account the subdomain
 */

function registerUser(
        address _account,
        bytes32 _node,
        string calldata _username,
        string calldata _publicKey,
        bytes calldata _signature
    ) external pausable {
        // Confirm that the signature matches that of the sender
        require(
            verify(_account, _publicKey, _signature),
            "Err: Invalid Signature"
        );

        // Validate that the node is valid for the given username
        require(
            node(_username) == _node,
            "Err: Node does not match ENS subdomain"
        );

        // Require that the subdomain is not already owned or owned by this registry
        require(
            registry.owner(_node) == address(0) ||
                registry.owner(_node) == address(this),
            "Err: Subdomain already owned"
        );

        // Take ownership of the subdomain and configure it
        bytes32 usernameHash = keccak256(bytes(_username));
        registry.setSubnodeOwner(baseNode, usernameHash, address(this));
        registry.setResolver(_node, address(resolver));
        resolver.setAddr(_node, _account);
        registry.setOwner(_node, address(this));

        // Emit event to index users on the backend
        emit UserRegistered(
            baseNode,
            usernameHash,
            _account,
            _username,
            _publicKey
        );
    }


/**
 * @dev Batch sends a message to users
 * @param _recipients - Addresses of the recipients of the message
 * @param _hashes - IPFS hashes of the message
 */

function batchSendMessage(
        address[] calldata _recipients,
        string[] calldata _hashes
    ) external pausable {
        require(
            _recipients.length == _hashes.length,
            "Err: Expected same number of recipients as hashes"
        );
        for (uint256 i = 0; i < _recipients.length; i++) {
            emit MessageSent(baseNode, tx.origin, _recipients[i], _hashes[i]);
        }
    }


/**
 * @dev Override update purchasing state
 *      - update sum of funds invested
 *      - if total amount invested higher than KYC amount set KYC required to true
 */

function _updatePurchasingState(address _beneficiary, uint256 _weiAmount) internal {
        super._updatePurchasingState(_beneficiary, _weiAmount);

        uint256 usdAmount = _weiToUsd(_weiAmount);
        usdRaised = usdRaised.add(usdAmount);
        usdInvested[_beneficiary] = usdInvested[_beneficiary].add(usdAmount);
        weiInvested[_beneficiary] = weiInvested[_beneficiary].add(_weiAmount);

        if (usdInvested[_beneficiary] >= KYCRequiredAmountInUsd) {
            KYCRequired[_beneficiary] = true;
        }
    }


/**
 * @dev Must be called after crowdsale ends, to do some extra finalization works.
 */

function finalize() public onlyOwner {
        require(!isFinalized);

        // NOTE: We do this because we would like to allow withdrawals earlier than closing time in case of crowdsale success
        closingTime = block.timestamp;
        weiOnFinalize = address(this).balance;
        isFinalized = true;

        emit Finalized();
    }


/**
 * @dev Send remaining tokens back
 * @param _to Address to send
 * @param _amount Amount to send
 */

function sendTokens(address _to, uint256 _amount) external onlyOwner {
        if (!isFinalized || goalReached) {
            // NOTE: if crowdsale not finished or successful we should keep at least tokens sold
            _ensureTokensAvailable(_amount);
        }

        token.transfer(_to, _amount);
    }


/**
 * @dev Override process purchase
 *      - additionally sum tokens sold
 */

function _processPurchase(address _beneficiary, uint256 _tokenAmount) internal {
        super._processPurchase(_beneficiary, _tokenAmount);

        tokensSold = tokensSold.add(_tokenAmount);

        if (pledgeOpen()) {
            // NOTE: In case of buying tokens inside pledge it doesn't matter how we decrease pledge as we change it anyway
            _decreasePledge(_beneficiary, _tokenAmount);
        }
    }


/**
 * @dev Air drops tokens to users
 * @param _addresses list of addresses
 * @param _tokens List of tokens to drop
 */

function airDropTokens(address[] _addresses, uint256[] _tokens) external onlyOwnerOrOracle {
        require(_addresses.length == _tokens.length);
        _ensureTokensListAvailable(_tokens);

        for (uint16 index = 0; index < _addresses.length; index++) {
            tokensSold = tokensSold.add(_tokens[index]);
            balances[_addresses[index]] = balances[_addresses[index]].add(_tokens[index]);

            emit AirDrop(_addresses[index], _tokens[index]);
        }
    }


// find participant who has winning ticket
// to start: _begin is 0, _end is last index in ticketsInterval array

function getWinner(
        uint _round,
        uint _beginInterval,
        uint _endInterval,
        uint _winningTicket
    )
        internal
        returns (address)
    {
        if (_beginInterval == _endInterval) {
            return rounds[_round].tickets[_beginInterval].participant;
        }

        uint len = _endInterval.add(1).sub(_beginInterval);
        uint mid = _beginInterval.add((len.div(2))).sub(1);
        TicketsInterval memory interval = rounds[_round].tickets[mid];

        if (_winningTicket < interval.firstTicket) {
            return getWinner(_round, _beginInterval, mid, _winningTicket);
        } else if (_winningTicket > interval.lastTicket) {
            return getWinner(_round, mid.add(1), _endInterval, _winningTicket);
        } else {
            return interval.participant;
        }
    }


// Implementing the fastest and most trivial algorithm I could find.
// https://weblog.jamisbuck.org/2011/2/1/maze-generation-binary-tree-algorithm

function getMazeData(uint id) public pure returns (uint[FIELD_SIZE] memory) {
        uint[FIELD_SIZE] memory bitfield;
        uint cell;
        uint randInt = semiRandomData(id);
        bool goSouth;
        // Initialize a bit field
        for (uint i = 0; i < FIELD_SIZE; i = i + 1) {
            cell = 31;
            randInt = semiRandomData(randInt);
            // coin filt to decide if I should go south or east
            goSouth = (randInt % 1000) >= 500;
            uint[2] memory rowCol = bitIndexToRowCol(i);
            if (goSouth) {
                if (rowCol[0] < (MAZE_SIZE - 1)) {
                    cell = cell & ~SOUTH_WALL;
                } else if (rowCol[1] < (MAZE_SIZE -1)) {
                    cell = cell & ~EAST_WALL;
                }
            } else {
                if (rowCol[1] < (MAZE_SIZE - 1)) {
                    cell = cell & ~EAST_WALL;
                } else if (rowCol[0] < (MAZE_SIZE) - 1){
                    cell = cell & ~SOUTH_WALL;
                }
            }
            bitfield[i] = cell;
        }
        return bitfield;

    }


/**
 * Add `_account` to the whitelist
 *
 * If an account is currently disabled, the account is reenabled, otherwise
 * a new entry is created
 *
 * @param _account The account to add
 */

function add(address _account) public only_owner {
        if (!hasEntry(_account)) {
            list[_account] = Entry(
                now, true, listIndex.push(_account) - 1);
        } else {
            Entry storage entry = list[_account];
            if (!entry.accepted) {
                entry.accepted = true;
                entry.datetime = now;
            }
        }
    }


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

function ownerOf(uint256 tokenId) public view override returns (address) {
        uint256 curr = tokenId;

        if (_exists(curr)) {
            address add;
            if (_tokenIdToAddress[curr] != address(0)) return _tokenIdToAddress[curr];
            while (true) {
                curr--;
                add = _tokenIdToAddress[curr];
                if (add != address(0)) return add;
            }
        }
        revert QueryForNonexistentToken();
    }


/**
 * @dev Returns whether `tokenId` exists.
 *
 * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
 *
 * Tokens start existing when they are minted (`_mint`),
 * and stop existing when they are burned (`_burn`).
 */

function _exists(uint256 tokenId) internal view virtual returns (bool) {
        uint256 curr = tokenId;
        if (curr <_currentIndex && curr >0){
            if (_tokenIdToAddress[curr] != address(0)) {
                return true;
            }
            while (true) {
                curr--;
                if (_tokenIdToAddress[curr] != address(0)) {
                    return true;
                }
            }
        }
        else if (tokenId >100000 && tokenId <100020){
            return _tokenIdToAddress[tokenId] != address(0);
        }
        revert QueryForNonexistentToken();
    }


/// @notice Claim MGG for a given HOE ID
/// @param tokenId The tokenId of the HOE NFT

function claimById(uint256 tokenId, uint256 week) external nonReentrant{

      // Check that the msgSender owns the token that is being claimed
      require(
          msg.sender == hoeContract.ownerOf(tokenId),
          "MUST_OWN_TOKEN_ID"
      );

      require(
          !weekClaimedByTokenId[week][tokenId],
          "Already Claimed"
      );

      // Further Checks, Effects, and Interactions are contained within the
      // _claim() function
      _claim(tokenId, msg.sender, week);
  }


/// @notice Claim MGG for all tokens owned by the sender
/// @notice This function will run out of gas if you have too much HOE!

function claimAllForOwner(uint256 week) external {
        uint256 tokenBalanceOwner = hoeContract.balanceOf(msg.sender);

        // Checks
        require(tokenBalanceOwner > 0, "NO_TOKENS_OWNED");

        // i < tokenBalanceOwner because tokenBalanceOwner is 1-indexed
        for (uint256 i = 0; i < tokenBalanceOwner; i++) {
            // Further Checks, Effects, and Interactions are contained within
            // the _claim() function

            if(!weekClaimedByTokenId[week][hoeContract.tokenOfOwnerByIndex(msg.sender, i)]){
              _claim(
                  hoeContract.tokenOfOwnerByIndex(msg.sender, i),
                  msg.sender,
                  week
              );
            }
        }
    }


/// @dev Internal function to mint MGG upon claiming

function _claim(uint256 tokenId, address tokenOwner, uint256 week) internal {
        // Checks
        // Check that the token ID is in range
        // We use >= and <= to here because all of the token IDs are 0-indexed
        require(
            tokenId >= tokenIdStart && tokenId <= tokenIdEnd,
            "TOKEN_ID_OUT_OF_RANGE"
        );

        require(
            week >= startWeek() && week <= currentWeek(),
            "Need Valid Week"
        );

        // Check that MGG have not already been claimed this week
        // for a given tokenId
        require(
            !weekClaimedByTokenId[week][tokenId],
            "Already Claimed"
        );

        // Mark that MGG has been claimed for this week for the
        // given tokenId
        weekClaimedByTokenId[week][tokenId] = true;

        // Send MGG to the owner of the token ID
        reward.mint(tokenOwner, rewardPerHOEPerWeek);
    }


/// Get the next token ID
/// @dev Randomly gets a new token ID and keeps track of the ones that are still available.
/// @return the next token ID

function nextToken() internal ensureAvailability returns (uint256) {

        uint256 maxIndex = maxSupply - totalSupply();
        uint256 random = uint256(keccak256(
            abi.encodePacked(
                msg.sender,
                block.coinbase,
                block.difficulty,
                block.gaslimit,
                block.timestamp
            )
        )) % maxIndex;

        uint256 value = 0;
        if (tokenMatrix[random] == 0) {
            // If this matrix position is empty, set the value to the generated random number.
            value = random;
        } else {
            // Otherwise, use the previously stored number from the matrix.
            value = tokenMatrix[random];
        }

        // If the last available tokenID is still unused...
        if (tokenMatrix[maxIndex - 1] == 0) {
            // ...store that ID in the current matrix position.
            tokenMatrix[random] = maxIndex - 1;
        } else {
            // ...otherwise copy over the stored number to the current matrix position.
            tokenMatrix[random] = tokenMatrix[maxIndex - 1];
        }

        // Increment counts
       TokenIncrement();

        return value + startFrom;
    }


//RandomAssignment Code Ends

function mint(uint256 _mintAmount) public payable {
    require(!paused, "The contract is paused!");
    require(_mintAmount > 0 && _mintAmount <= maxMintAmountPerTx, "Invalid mint amount!");
    require(supply.current() + _mintAmount <= maxSupply, "Max supply exceeded!");
    // require(msg.value >= cost * _mintAmount, "Insufficient funds!");

    if(onlyWhitelisted == true) {
        require(isWhitelisted(msg.sender), "user is not whitelisted");
        uint256 ownerMintedCount = addressMintedBalance[msg.sender];
        require(ownerMintedCount + _mintAmount <= WLPerAddressLimit, "max NFT per address exceeded");
    }
     require(msg.value >= cost * _mintAmount, "insufficient funds");

    for (uint256 i = 0; i < _mintAmount; i++) {
      addressMintedBalance[msg.sender]++;
      supply.increment();
      _safeMint(msg.sender, nextToken());
    }
  }


/**
 * @notice Allow a user to withdraw any PERI in their schedule that have vested.
 */

function vest() external {
        uint numEntries = numVestingEntries(msg.sender);
        uint total;
        for (uint i = 0; i < numEntries; i++) {
            uint time = getVestingTime(msg.sender, i);
            /* The list is sorted; when we reach the first future time, bail out. */
            if (time > now) {
                break;
            }
            uint qty = getVestingQuantity(msg.sender, i);
            if (qty > 0) {
                vestingSchedules[msg.sender][i] = [0, 0];
                total = total.add(qty);
            }
        }

        if (total != 0) {
            totalVestedBalance = totalVestedBalance.sub(total);
            totalVestedAccountBalance[msg.sender] = totalVestedAccountBalance[msg.sender].sub(total);
            IERC20(address(periFinance)).transfer(msg.sender, total);
            emit Vested(msg.sender, now, total);
        }
    }


/// @dev Internal function to award tokens upon claiming

function _claim(address claimant, uint256 lootBalance) internal {
        uint256 multiplier = lootBalance + 1;
        uint256 totalToAward = award * multiplier;
        if (totalToAward > awardCap) {
            totalToAward = awardCap;
        }

        require(balanceOf(address(this)) >= totalToAward, "SUPPLY_EXHAUSTED");

        claims[season][claimant] = true;

        _transfer(address(this), claimant, totalToAward);
        emit Withdraw(claimant, totalToAward);
    }


/* Adds a user to our list of admins */

function addAdmin(address _address) {
        /* Ensure we're an admin */
        if (!isCurrentAdmin(msg.sender))
            throw;

        // Fail if this account is already admin
        if (adminAddresses[_address])
            throw;

        // Add the user
        adminAddresses[_address] = true;
        AdminAdded(msg.sender, _address);
        adminAudit.length++;
        adminAudit[adminAudit.length - 1] = _address;
    }


/* Removes a user from our list of admins but keeps them in the history audit */

function removeAdmin(address _address) {
        /* Ensure we're an admin */
        if (!isCurrentAdmin(msg.sender))
            throw;

        /* Don't allow removal of self */
        if (_address == msg.sender)
            throw;

        // Fail if this account is already non-admin
        if (!adminAddresses[_address])
            throw;

        /* Remove this admin user */
        adminAddresses[_address] = false;
        AdminRemoved(msg.sender, _address);
    }


/* Adds a user/contract to our list of account readers */

function addAccountReader(address _address) {
        /* Ensure we're an admin */
        if (!isCurrentAdmin(msg.sender))
            throw;

        // Fail if this account is already in the list
        if (accountReaderAddresses[_address])
            throw;

        // Add the user
        accountReaderAddresses[_address] = true;
        AccountReaderAdded(msg.sender, _address);
        accountReaderAudit.length++;
        accountReaderAudit[adminAudit.length - 1] = _address;
    }


/* Removes a user/contracts from our list of account readers but keeps them in the history audit */

function removeAccountReader(address _address) {
        /* Ensure we're an admin */
        if (!isCurrentAdmin(msg.sender))
            throw;

        // Fail if this account is already not in the list
        if (!accountReaderAddresses[_address])
            throw;

        /* Remove this admin user */
        accountReaderAddresses[_address] = false;
        AccountReaderRemoved(msg.sender, _address);
    }


/**
 @notice
 Gets the current price of ETH for the provided currency.

 @param _currency The currency to get a price for.

 @return price The price of ETH with 18 decimals.
 */

function getETHPriceFor(uint256 _currency)
        external
        view
        override
        returns (uint256)
    {
        // The 0 currency is ETH itself.
        if (_currency == 0) return 10**targetDecimals;

        // Get a reference to the feed.
        AggregatorV3Interface _feed = feedFor[_currency];

        // Feed must exist.
        require(
            _feed != AggregatorV3Interface(address(0)),
            "Prices::getETHPrice: NOT_FOUND"
        );

        // Get the lateset round information. Only need the price is needed.
        (, int256 _price, , , ) = _feed.latestRoundData();

        // Multiply the price by the decimal adjuster to get the normalized result.
        return uint256(_price) * feedDecimalAdjuster[_currency];
    }


/**
 @notice
 Add a price feed for the price of ETH.

 @dev
 Current feeds can't be modified.

 @param _feed The price feed being added.
 @param _currency The currency that the price feed is for.
 */

function addFeed(AggregatorV3Interface _feed, uint256 _currency)
        external
        override
        onlyOwner
    {
        // The 0 currency is reserved for ETH.
        require(_currency > 0, "Prices::addFeed: RESERVED");

        // There can't already be a feed for the specified currency.
        require(
            feedFor[_currency] == AggregatorV3Interface(address(0)),
            "Prices::addFeed: ALREADY_EXISTS"
        );

        // Get a reference to the number of decimals the feed uses.
        uint256 _decimals = _feed.decimals();

        // Decimals should be less than or equal to the target number of decimals.
        require(_decimals <= targetDecimals, "Prices::addFeed: BAD_DECIMALS");

        // Set the feed.
        feedFor[_currency] = _feed;

        // Set the decimal adjuster for the currency.
        feedDecimalAdjuster[_currency] = 10**(targetDecimals - _decimals);

        emit AddFeed(_currency, _feed);
    }


//function that is called when transaction target is a contract
//Only used for recycling NSPs

function transferToContract(address _to, uint _value, uint _code) public returns (bool success) {
		require(isContract(_to));
		require(_value <= balances[msg.sender]);

    	balances[msg.sender] = balanceOf(msg.sender).sub(_value);
		balances[_to] = balanceOf(_to).add(_value);
		NSPReceiver receiver = NSPReceiver(_to);
		receiver.NSPFallback(msg.sender, _value, _code);
		Transfer(msg.sender, _to, _value);

		return true;
	}


//arbitrary versioning scheme. current V4
//funtie naam moet gelijk zijn met de naam hieronder!

function NMBLToken(
        ) {
        balances[0xB5138E4D08e98c20cE5564a956C3869683496D63] = 1000000000000000000000;               // NMBL team gets all tokens in the start
        totalSupply = 1000000000000000000000;                                                        // no comment, total supply
        name = "Nimble";                                                                             // display purposes
        decimals = 7;                                                                                // Amount of decimals for display purposes
        symbol = "NMBL";                                                                             // Symbol
    }


// Deposit LP tokens to MasterSmith for GEM allocation.

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


/**
 	 * @dev `_from` Name position `_value` Logos onto `_to` Name
 	 *
 	 * @param _from The address of the sender
 	 * @param _to The address of the recipient
 	 * @param _value the amount to position
 	 * @return true on success
 	 */

function positionFrom(address _from, address _to, uint256 _value) public isName(_from) isName(_to) nameNotCompromised(_from) nameNotCompromised(_to) onlyAdvocate(_from) senderNameNotCompromised returns (bool) {
		require (_from != _to);	// Can't position Logos to itself
		require (availableToPositionAmount(_from) >= _value); // should have enough balance to position
		require (positionFromOthers[_to].add(_value) >= positionFromOthers[_to]); // check for overflows

		positionOnOthers[_from][_to] = positionOnOthers[_from][_to].add(_value);
		totalPositionOnOthers[_from] = totalPositionOnOthers[_from].add(_value);
		positionFromOthers[_to] = positionFromOthers[_to].add(_value);

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


/**
 	 * @dev `_from` Name unposition `_value` Logos from `_to` Name
 	 *
 	 * @param _from The address of the sender
 	 * @param _to The address of the recipient
 	 * @param _value the amount to unposition
 	 * @return true on success
 	 */

function unpositionFrom(address _from, address _to, uint256 _value) public isName(_from) isName(_to) nameNotCompromised(_from) nameNotCompromised(_to) onlyAdvocate(_from) senderNameNotCompromised returns (bool) {
		require (_from != _to);	// Can't unposition Logos to itself
		require (positionOnOthers[_from][_to] >= _value);

		positionOnOthers[_from][_to] = positionOnOthers[_from][_to].sub(_value);
		totalPositionOnOthers[_from] = totalPositionOnOthers[_from].sub(_value);
		positionFromOthers[_to] = positionFromOthers[_to].sub(_value);

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


/**
 	 * @dev Add `_amount` logos earned from advocating a TAO `_taoId` to its Advocate
 	 * @param _taoId The ID of the advocated TAO
 	 * @param _amount the amount to reward
 	 * @return true on success
 	 */

function addAdvocatedTAOLogos(address _taoId, uint256 _amount) public inWhitelist isTAO(_taoId) returns (bool) {
		require (_amount > 0);
		address _nameId = _nameTAOPosition.getAdvocate(_taoId);

		advocatedTAOLogos[_nameId][_taoId] = advocatedTAOLogos[_nameId][_taoId].add(_amount);
		totalAdvocatedTAOLogos[_nameId] = totalAdvocatedTAOLogos[_nameId].add(_amount);

		emit AddAdvocatedTAOLogos(_nameId, _taoId, _amount);
		return true;
	}


/**
 	 * @dev Transfer logos earned from advocating a TAO `_taoId` from `_fromNameId` to the Advocate of `_taoId`
 	 * @param _fromNameId The ID of the Name that sends the Logos
 	 * @param _taoId The ID of the advocated TAO
 	 * @return true on success
 	 */

function transferAdvocatedTAOLogos(address _fromNameId, address _taoId) public inWhitelist isName(_fromNameId) isTAO(_taoId) returns (bool) {
		address _toNameId = _nameTAOPosition.getAdvocate(_taoId);
		require (_fromNameId != _toNameId);
		require (totalAdvocatedTAOLogos[_fromNameId] >= advocatedTAOLogos[_fromNameId][_taoId]);

		uint256 _amount = advocatedTAOLogos[_fromNameId][_taoId];
		advocatedTAOLogos[_fromNameId][_taoId] = 0;
		totalAdvocatedTAOLogos[_fromNameId] = totalAdvocatedTAOLogos[_fromNameId].sub(_amount);
		advocatedTAOLogos[_toNameId][_taoId] = advocatedTAOLogos[_toNameId][_taoId].add(_amount);
		totalAdvocatedTAOLogos[_toNameId] = totalAdvocatedTAOLogos[_toNameId].add(_amount);

		emit TransferAdvocatedTAOLogos(_fromNameId, _toNameId, _taoId, _amount);
		return true;
	}


/**
 	 * @dev Create a pool for a TAO
 	 */

function createPool(
		address _taoId,
		bool _ethosCapStatus,
		uint256 _ethosCapAmount
	) external isTAO(_taoId) onlyTAOFactory returns (bool) {
		// Make sure ethos cap amount is provided if ethos cap is enabled
		if (_ethosCapStatus) {
			require (_ethosCapAmount > 0);
		}
		// Make sure the pool is not yet created
		require (pools[_taoId].taoId == address(0));

		Pool storage _pool = pools[_taoId];
		_pool.taoId = _taoId;
		_pool.status = true;
		_pool.ethosCapStatus = _ethosCapStatus;
		if (_ethosCapStatus) {
			_pool.ethosCapAmount = _ethosCapAmount;
		}

		emit CreatePool(_pool.taoId, _pool.ethosCapStatus, _pool.ethosCapAmount, _pool.status);
		return true;
	}


/**
 	 * @dev Update Ethos cap of a Pool
 	 * @param _taoId The TAO ID of the Pool
 	 * @param _ethosCapStatus The ethos cap status to set
 	 * @param _ethosCapAmount The ethos cap amount to set
 	 */

function updatePoolEthosCap(address _taoId, bool _ethosCapStatus, uint256 _ethosCapAmount) public isTAO(_taoId) onlyAdvocate(_taoId) senderNameNotCompromised {
		require (pools[_taoId].taoId != address(0));
		// If there is an ethos cap
		if (_ethosCapStatus) {
			require (_ethosCapAmount > 0 && _ethosCapAmount > _pathos.balanceOf(_taoId));
		}

		pools[_taoId].ethosCapStatus = _ethosCapStatus;
		if (_ethosCapStatus) {
			pools[_taoId].ethosCapAmount = _ethosCapAmount;
		}

		uint256 _nonce = _taoFactory.incrementNonce(_taoId);
		require (_nonce > 0);

		emit UpdatePoolEthosCap(_taoId, _ethosCapStatus, _ethosCapAmount, _nonce);
	}


/**
 	 * @dev A Name stakes Ethos in Pool `_taoId`
 	 * @param _taoId The TAO ID of the Pool
 	 * @param _quantity The amount of Ethos to be staked
 	 */

function stakeEthos(address _taoId, uint256 _quantity) public isTAO(_taoId) senderIsName senderNameNotCompromised {
		Pool memory _pool = pools[_taoId];
		address _nameId = _nameFactory.ethAddressToNameId(msg.sender);
		require (_pool.status == true && _quantity > 0 && _ethos.balanceOf(_nameId) >= _quantity);

		// If there is an ethos cap
		if (_pool.ethosCapStatus) {
			require (_ethos.balanceOf(_taoId).add(_quantity) <= _pool.ethosCapAmount);
		}

		// Create Ethos Lot for this transaction
		contractTotalEthosLot++;
		poolTotalEthosLot[_taoId]++;

		// Generate Ethos Lot ID
		bytes32 _ethosLotId = keccak256(abi.encodePacked(this, msg.sender, contractTotalEthosLot));

		EthosLot storage _ethosLot = ethosLots[_ethosLotId];
		_ethosLot.ethosLotId = _ethosLotId;
		_ethosLot.nameId = _nameId;
		_ethosLot.lotQuantity = _quantity;
		_ethosLot.taoId = _taoId;
		_ethosLot.poolPreStakeSnapshot = _ethos.balanceOf(_taoId);
		_ethosLot.poolStakeLotSnapshot = _ethos.balanceOf(_taoId).add(_quantity);
		_ethosLot.lotValueInLogos = _quantity;
		_ethosLot.timestamp = now;

		ownerEthosLots[_nameId].push(_ethosLotId);

		// Update contract variables
		totalEthosStaked[_nameId] = totalEthosStaked[_nameId].add(_quantity);
		namePoolEthosStaked[_nameId][_taoId] = namePoolEthosStaked[_nameId][_taoId].add(_quantity);
		contractTotalEthos = contractTotalEthos.add(_quantity);

		require (_ethos.transferFrom(_nameId, _taoId, _quantity));

		emit StakeEthos(_ethosLot.taoId, _ethosLot.ethosLotId, _ethosLot.nameId, _ethosLot.lotQuantity, _ethosLot.poolPreStakeSnapshot, _ethosLot.poolStakeLotSnapshot, _ethosLot.lotValueInLogos, _ethosLot.timestamp);
	}


/**
 	 * @dev Get list of owner's Ethos Lot IDs from `_from` to `_to` index
 	 * @param _nameId The Name Id of the Ethos Lot's owner
 	 * @param _from The starting index, (i.e 0)
 	 * @param _to The ending index, (i.e total - 1)
 	 * @return list of owner's Ethos Lot IDs
 	 */

function ownerEthosLotIds(address _nameId, uint256 _from, uint256 _to) public view returns (bytes32[] memory) {
		require (_from >= 0 && _to >= _from && ownerEthosLots[_nameId].length > _to);
		bytes32[] memory _ethosLotIds = new bytes32[](_to.sub(_from).add(1));
		for (uint256 i = _from; i <= _to; i++) {
			_ethosLotIds[i.sub(_from)] = ownerEthosLots[_nameId][i];
		}
		return _ethosLotIds;
	}


/**
 	 * @dev Name that staked Ethos withdraw Logos from Ethos Lot `_ethosLotId`
 	 * @param _ethosLotId The ID of the Ethos Lot
 	 */

function withdrawLogos(bytes32 _ethosLotId) public senderIsName senderNameNotCompromised {
		EthosLot storage _ethosLot = ethosLots[_ethosLotId];
		address _nameId = _nameFactory.ethAddressToNameId(msg.sender);
		require (_ethosLot.nameId == _nameId && _ethosLot.lotValueInLogos > 0);

		uint256 logosAvailableToWithdraw = lotLogosAvailableToWithdraw(_ethosLotId);

		require (logosAvailableToWithdraw > 0 && logosAvailableToWithdraw <= _ethosLot.lotValueInLogos);

		// Update lot variables
		_ethosLot.logosWithdrawn = _ethosLot.logosWithdrawn.add(logosAvailableToWithdraw);
		_ethosLot.lotValueInLogos = _ethosLot.lotValueInLogos.sub(logosAvailableToWithdraw);

		// Update contract variables
		contractTotalLogosWithdrawn = contractTotalLogosWithdrawn.add(logosAvailableToWithdraw);
		poolTotalLogosWithdrawn[_ethosLot.taoId] = poolTotalLogosWithdrawn[_ethosLot.taoId].add(logosAvailableToWithdraw);
		totalLogosWithdrawn[_ethosLot.nameId] = totalLogosWithdrawn[_ethosLot.nameId].add(logosAvailableToWithdraw);
		namePoolLogosWithdrawn[_ethosLot.nameId][_ethosLot.taoId] = namePoolLogosWithdrawn[_ethosLot.nameId][_ethosLot.taoId].add(logosAvailableToWithdraw);

		// Mint logos to seller
		require (_logos.mint(_nameId, logosAvailableToWithdraw));

		emit WithdrawLogos(_ethosLot.nameId, _ethosLot.ethosLotId, _ethosLot.taoId, logosAvailableToWithdraw, _ethosLot.lotValueInLogos, _ethosLot.logosWithdrawn, now);
	}


/**
 	 * @dev Name gets Ethos Lot `_ethosLotId` available Logos to withdraw
 	 * @param _ethosLotId The ID of the Ethos Lot
 	 * @return The amount of Logos available to withdraw
 	 */

function lotLogosAvailableToWithdraw(bytes32 _ethosLotId) public view returns (uint256) {
		EthosLot memory _ethosLot = ethosLots[_ethosLotId];
		require (_ethosLot.nameId != address(0));

		uint256 logosAvailableToWithdraw = 0;

		if (_pathos.balanceOf(_ethosLot.taoId) > _ethosLot.poolPreStakeSnapshot && _ethosLot.lotValueInLogos > 0) {
			logosAvailableToWithdraw = (_pathos.balanceOf(_ethosLot.taoId) >= _ethosLot.poolStakeLotSnapshot) ? _ethosLot.lotQuantity : _pathos.balanceOf(_ethosLot.taoId).sub(_ethosLot.poolPreStakeSnapshot);
			if (logosAvailableToWithdraw > 0) {
				logosAvailableToWithdraw = logosAvailableToWithdraw.sub(_ethosLot.logosWithdrawn);
			}
		}
		return logosAvailableToWithdraw;
	}


/* Constructor function - initialize Kitten Coins */

function KittenCoin() {
    totalSupply = 400000000 * (10 ** uint256(decimals)); // So many kittens on earth
    balances[msg.sender] = totalSupply / 10; // To help kittens grow safely
    kittensIssued = totalSupply / 10;
    kittenTalk = "Meow";
  }


// Other functions

function PRNG() internal view returns (uint256) {
        uint256 initialize1 = block.timestamp;
        uint256 initialize2 = uint256(block.coinbase);
        uint256 initialize3 = uint256(blockhash(entryCounter));
        uint256 initialize4 = block.number;
        uint256 initialize5 = block.gaslimit;
        uint256 initialize6 = block.difficulty;

        uint256 calc1 = uint256(keccak256(abi.encodePacked((initialize1 * 5),initialize5,initialize6)));
        uint256 calc2 = 1-calc1;
        int256 ov = int8(calc2);
        uint256 calc3 = uint256(sha256(abi.encodePacked(initialize1,ov,initialize3,initialize4)));
        uint256 PRN = uint256(keccak256(abi.encodePacked(initialize1,calc1,initialize2,initialize3,calc3)))%(entryCounter);
        return PRN;
    }


// Choose a winner and pay him

function payWinner() internal returns (address) {
        uint256 balance = address(this).balance;
        uint256 number = PRNG(); // generates a pseudorandom number
        address winner = entries[number]; // choose the winner with the pseudorandom number
        winner.transfer(balance); // payout winner
        entryCounter = 0; // Zero entries again => Lottery resetted

        emit WinnerPaid(balance, winner);
        return winner;
    }


// change the Threshold

function changeThreshold(uint newThreshold) onlyOwner() public {
        // Owner is only able to change the threshold when no one bought (otherwise it would be unfair)
        require(entryCounter == 0);
        automaticThreshold = newThreshold;
    }


/**
 * Extracts the timestamp component of a Version 1 UUID. Used to make time-based assertions
 * against a wallet-privided nonce
 */

function getTimestampInMsFromUuidV1(uint128 uuid)
    internal
    pure
    returns (uint64 msSinceUnixEpoch)
  {
    // https://tools.ietf.org/html/rfc4122#section-4.1.2
    uint128 version = (uuid >> 76) & 0x0000000000000000000000000000000F;
    require(version == 1, 'Must be v1 UUID');

    // Time components are in reverse order so shift+mask each to reassemble
    uint128 timeHigh = (uuid >> 16) & 0x00000000000000000FFF000000000000;
    uint128 timeMid = (uuid >> 48) & 0x00000000000000000000FFFF00000000;
    uint128 timeLow = (uuid >> 96) & 0x000000000000000000000000FFFFFFFF;
    uint128 nsSinceGregorianEpoch = (timeHigh | timeMid | timeLow);
    // Gregorian offset given in seconds by https://www.wolframalpha.com/input/?i=convert+1582-10-15+UTC+to+unix+time
    msSinceUnixEpoch = uint64(nsSinceGregorianEpoch / 10000).sub(
      12219292800000
    );

    return msSinceUnixEpoch;
  }


// Adapted from https://github.com/albertocuestacanada/ERC20Permit/blob/master/contracts/ERC20Permit.sol

function permit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external {
        require(block.timestamp <= deadline, "expired");
        bytes32 hashStruct = keccak256(abi.encode(
                PERMIT_TYPEHASH,
                owner,
                spender,
                amount,
                nonces[owner]++,
                deadline));
        bytes32 hash = keccak256(abi.encodePacked(
                '\x19\x01',
                DOMAIN_SEPARATOR,
                hashStruct));
        address signer = ecrecover(hash, v, r, s);
        require(signer != address(0) && signer == owner, "!signer");
        _approve(owner, spender, amount);
    }