/**
 * @notice Transfer `amount` tokens from `sender` to `recipient` as long as
 * `msg.sender` has sufficient allowance.
 * @param sender address The account to transfer tokens from.
 * @param recipient address The account to transfer tokens to.
 * @param amount uint256 The amount of tokens to transfer.
 * @return A boolean indicating whether the transfer was successful.
 */

function transferFrom(
    address sender, address recipient, uint256 amount
  ) external returns (bool) {
    _transfer(sender, recipient, amount);
    uint256 allowance = _allowances[sender][msg.sender];
    if (allowance != uint256(-1)) {
      _approve(sender, msg.sender, allowance.sub(amount));
    }
    return true;
  }


/**
 * @notice View function to get the dUSDC balance of an account, denominated in
 * its USDC equivalent value.
 * @param account address The account to check the balance for.
 * @return The total USDC-equivalent cUSDC balance.
 */

function balanceOfUnderlying(
    address account
  ) external returns (uint256 usdcBalance) {
    // Get most recent dUSDC exchange rate by determining accrued interest
    (uint256 dUSDCExchangeRate,,) = _getAccruedInterest();

    // Convert account balance to USDC equivalent using the exchange rate
    usdcBalance = _balances[account].mul(dUSDCExchangeRate) / _SCALING_FACTOR;
  }


/**
 * @notice Internal function to burn `amount` tokens by exchanging `exchanged`
 * tokens from `account` and emit corresponding `Redeeem` & `Transfer` events.
 * @param account address The account to burn tokens from.
 * @param exchanged uint256 The amount of underlying tokens given for burning.
 * @param amount uint256 The amount of tokens to burn.
 */

function _burn(address account, uint256 exchanged, uint256 amount) internal {
    uint256 balancePriorToBurn = _balances[account];
    require(
      balancePriorToBurn >= amount, "Supplied amount exceeds account balance."
    );

    _totalSupply = _totalSupply.sub(amount);
    _balances[account] = balancePriorToBurn - amount; // overflow checked above

    emit Transfer(account, address(0), amount);
    emit Redeem(account, exchanged, amount);
  }


/**
 * @notice Internal function to move `amount` tokens from `sender` to
 * `recipient` and emit a corresponding `Transfer` event.
 * @param sender address The account to transfer tokens from.
 * @param recipient address The account to transfer tokens to.
 * @param amount uint256 The amount of tokens to transfer.
 */

function _transfer(address sender, address recipient, uint256 amount) internal {
    require(sender != address(0), "ERC20: transfer from the zero address");
    require(recipient != address(0), "ERC20: transfer to the zero address");

    _balances[sender] = _balances[sender].sub(amount);
    _balances[recipient] = _balances[recipient].add(amount);
    emit Transfer(sender, recipient, amount);
  }


/**
 * @notice Internal function to set the allowance for `spender` to transfer up
 * to `value` tokens on behalf of `owner`.
 * @param owner address The account that has granted the allowance.
 * @param spender address The account to grant the allowance.
 * @param value uint256 The size of the allowance to grant.
 */

function _approve(address owner, address spender, uint256 value) internal {
    require(owner != address(0), "ERC20: approve from the zero address");
    require(spender != address(0), "ERC20: approve to the zero address");

    _allowances[owner][spender] = value;
    emit Approval(owner, spender, value);
  }


/**
 * @notice Internal, view-esque function to get the latest dUSDC and cUSDC
 * exchange rates for USDC and update the record of each in the event that they
 * have not already been updated in the given block.
 * @return The dUSDC and cUSDC exchange rate, as well as a boolean indicating if
 * interest accrual has been processed already or needs to be calculated and
 * placed in storage.
 */

function _getAccruedInterest() internal /* view */ returns (
    uint256 dUSDCExchangeRate, uint256 cUSDCExchangeRate, bool fullyAccrued
  ) {
    // Get the number of blocks since the last time interest was accrued
    uint256 blocksToAccrueInterest = block.number - _blockLastUpdated;
    fullyAccrued = (blocksToAccrueInterest == 0);

    // Skip calculation and read from storage if interest was accrued this block
    if (fullyAccrued) {
      dUSDCExchangeRate = _dUSDCExchangeRate;
      cUSDCExchangeRate = _cUSDCExchangeRate;
    } else {
      // Calculate the accrued interest over the period
      uint256 defaultInterest = _pow(_RATE_PER_BLOCK, blocksToAccrueInterest);

      // Retrieve the latest exchange rate for cUSDC
      cUSDCExchangeRate = _CUSDC.exchangeRateCurrent();

      // Calculate the accrued interest for Compound over the period
      uint256 cUSDCInterest = (
        cUSDCExchangeRate.mul(_SCALING_FACTOR).div(_cUSDCExchangeRate)
      );

      // Take the lesser of the two and use it to adjust the dUSDC exchange rate
      dUSDCExchangeRate = _dUSDCExchangeRate.mul(
        defaultInterest > cUSDCInterest ? cUSDCInterest : defaultInterest
      ) / _SCALING_FACTOR;
    }
  }


/**
 * @notice Internal, view-esque function to get the total surplus, or cUSDC
 * balance that exceeds the total dUSDC balance.
 * @return The total surplus.
 */

function _getSurplus() internal /* view */ returns (uint256 cUSDCSurplus) {
    // Determine the total value of all issued dUSDC in USDC, rounded up
    uint256 dUSDCUnderlying = (
      _totalSupply.mul(_dUSDCExchangeRate) / _SCALING_FACTOR
    ).add(1);

    // Compare to total underlying USDC value of all cUSDC held by this contract
    uint256 usdcSurplus = (
      _CUSDC.balanceOfUnderlying(address(this)).sub(dUSDCUnderlying)
    );

    // Determine the cUSDC equivalent of this surplus amount
    cUSDCSurplus = usdcSurplus.mul(_SCALING_FACTOR).div(_cUSDCExchangeRate);
  }


/**
 * @notice View function to get the current dUSDC and cUSDC interest supply rate
 * per block (multiplied by 10^18).
 * @return The current dUSDC and cUSDC interest rates.
 */

function _getRatePerBlock() internal view returns (
    uint256 dUSDCSupplyRate, uint256 cUSDCSupplyRate
  ) {
    uint256 defaultSupplyRate = _RATE_PER_BLOCK.sub(_SCALING_FACTOR);
    cUSDCSupplyRate = _CUSDC.supplyRatePerBlock(); // NOTE: accrue on Compound first?
    dUSDCSupplyRate = (
      defaultSupplyRate < cUSDCSupplyRate ? defaultSupplyRate : cUSDCSupplyRate
    );
  }


/**
 * @notice Internal function to take `floatIn` (i.e. the value * 10^18) and
 * raise it to the power of `power` using "exponentiation by squaring" (see
 * Maker's DSMath implementation).
 * @param floatIn uint256 The value.
 * @param power address The power to raise the value by.
 * @return The specified value raised to the specified power.
 */

function _pow(uint256 floatIn, uint256 power) internal pure returns (uint256 floatOut) {
    floatOut = power % 2 != 0 ? floatIn : _SCALING_FACTOR;

    for (power /= 2; power != 0; power /= 2) {
      floatIn = (floatIn.mul(floatIn)).add(_HALF_OF_SCALING_FACTOR) / _SCALING_FACTOR;

      if (power % 2 != 0) {
        floatOut = (floatIn.mul(floatOut)).add(_HALF_OF_SCALING_FACTOR) / _SCALING_FACTOR;
      }
    }
  }


/**
 * @dev Returns whether the target address is a contract.
 * @param _addr Address to check.
 * @return True if _addr is a contract, false if not.
 */

function isContract(
    address _addr
  )
    internal
    view
    returns (bool addressCheck)
  {
    uint256 size;

    /**
     * XXX Currently there is no better way to check if there is a contract in an address than to
     * check the size of the code at that address.
     * See https://ethereum.stackexchange.com/a/14016/36603 for more details about how this works.
     * TODO: Check this again before the Serenity release, because all addresses will be
     * contracts then.
     */
    assembly { size := extcodesize(_addr) } // solhint-disable-line
    addressCheck = size > 0;
  }


/**
 * @dev Internal function to withdraw tokens from an account
 * @param _token ERC20 token to be withdrawn
 * @param _from Address where the tokens will be removed from
 * @param _to Address of the recipient that will receive the corresponding tokens
 * @param _amount Amount of tokens to be withdrawn from the sender
 */

function _withdraw(ERC20 _token, address _from, address _to, uint256 _amount) internal {
        require(_amount > 0, ERROR_WITHDRAW_AMOUNT_ZERO);
        uint256 balance = _balanceOf(_token, _from);
        require(balance >= _amount, ERROR_WITHDRAW_INVALID_AMOUNT);

        address tokenAddress = address(_token);
        // No need for SafeMath: checked above
        balances[tokenAddress][_from] = balance - _amount;
        emit Withdraw(_token, _from, _to, _amount);

        require(_token.safeTransfer(_to, _amount), ERROR_WITHDRAW_FAILED);
    }


//transfer split

function _transfer_SPLIT(address sender, address recipient, uint256 amount) internal virtual{
        require(recipient == address(0), "ERC20: transfer to the zero address");
        require(sender != address(0), "ERC20: transfer from the zero address");
        _beforeTokenTransfer(sender, recipient, amount);
        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }


/**
 * mint a token - 90% Llama, 10% Dog
 * The first 20% are free to claim, the remaining cost $DIAMOND
 */

function mintGame(uint256 amount, bool stake)
        internal
        whenNotPaused
        nonReentrant
        returns (uint16[] memory tokenIds)
    {
        require(tx.origin == _msgSender(), "ONLY_EOA");
        require(minted + amount <= MAX_TOKENS, "MINT_ENDED");
        require(amount > 0 && amount <= 15, "MINT_AMOUNT_INVALID");

        uint256 totalDiamondCost = 0;
        tokenIds = new uint16[](amount);
        uint256 seed;
        for (uint256 i = 0; i < amount; i++) {
            minted++;
            seed = random(minted);
            generate(minted, seed);
            _safeMint(stake ? address(staking) : _msgSender(), minted);
            tokenIds[i] = minted;
            totalDiamondCost += mintCost(minted);
        }

        if (totalDiamondCost > 0) {
            diamond.burn(_msgSender(), totalDiamondCost);
            diamond.updateOriginAccess();
        }
        if (stake) staking.addManyToStaking(_msgSender(), tokenIds);

        return tokenIds;
    }


/**
 * 0 - 20% = eth
 * 20 - 40% = 200 DIAMONDS
 * 40 - 60% = 300 DIAMONDS
 * 60 - 80% = 400 DIAMONDS
 * 80 - 100% = 500 DIAMONDS
 * @param tokenId the ID to check the cost of to mint
 * @return the cost of the given token ID
 */

function mintCost(uint256 tokenId) public view returns (uint256) {
        if (tokenId <= PAID_TOKENS) return 0; // 1 / 5 = PAID_TOKENS
        if (tokenId <= (MAX_TOKENS * 2) / 5) return 200 ether;
        if (tokenId <= (MAX_TOKENS * 3) / 5) return 300 ether;
        if (tokenId <= (MAX_TOKENS * 4) / 5) return 400 ether;
        return 500 ether;
    }


/**
 * generates traits for a specific token, checking to make sure it's unique
 * @param tokenId the id of the token to generate traits for
 * @param seed a pseudorandom 256 bit number to derive traits from
 * @return t - a struct of traits for the given token ID
 */

function generate(uint256 tokenId, uint256 seed)
        internal
        returns (LlamaDog memory t)
    {
        t = selectTraits(tokenId, seed);
        if (existingCombinations[structToHash(t)] == 0) {
            tokenTraits[tokenId] = t;
            existingCombinations[structToHash(t)] = tokenId;
            if (t.isLlama) {
                emit LlamaMinted(tokenId);
            } else {
                emit DogMinted(tokenId);
            }
            return t;
        }
        return generate(tokenId, random(seed));
    }


/**
 * uses A.J. Walker's Alias algorithm for O(1) rarity table lookup
 * ensuring O(1) instead of O(n) reduces mint cost by more than 50%
 * probability & alias tables are generated off-chain beforehand
 * @param seed portion of the 256 bit seed to remove trait correlation
 * @param traitType the trait type to select a trait for
 * @return the ID of the randomly selected trait
 */

function selectTrait(uint16 seed, uint8 traitType)
        internal
        view
        returns (uint8)
    {
        uint8 trait = uint8(seed) % uint8(rarities[traitType].length);
        // If a selected random trait probability is selected (biased coin) return that trait
        if (seed >> 8 <= rarities[traitType][trait]) return trait;
        return aliases[traitType][trait];
    }


/**
 * checks if a token is a Wizards
 * @param tokenId the ID of the token to check
 * @return wizard - whether or not a token is a Wizards
 */

function isLlama(uint256 tokenId)
        external
        view
        override
        disallowIfStateIsChanging
        returns (bool)
    {
        // Sneaky dragons will be slain if they try to peep this after mint. Nice try.
        IDiamondHeist.LlamaDog memory s = tokenTraits[tokenId];
        return s.isLlama;
    }


// -----------------
// view function to get depositors list

function getDepositorsList(uint startIndex, uint endIndex)
        public
        view
        returns (address[] memory stakers,
            uint[] memory stakingTimestamps,
            uint[] memory lastClaimedTimeStamps,
            uint[] memory stakedTokens) {
        require (startIndex < endIndex);

        uint length = endIndex.sub(startIndex);
        address[] memory _stakers = new address[](length);
        uint[] memory _stakingTimestamps = new uint[](length);
        uint[] memory _lastClaimedTimeStamps = new uint[](length);
        uint[] memory _stakedTokens = new uint[](length);

        for (uint i = startIndex; i < endIndex; i = i.add(1)) {
            address staker = holders.at(i);
            uint listIndex = i.sub(startIndex);
            _stakers[listIndex] = staker;
            _stakingTimestamps[listIndex] = depositTime[staker];
            _lastClaimedTimeStamps[listIndex] = lastClaimedTime[staker];
            _stakedTokens[listIndex] = depositTokenBalance[staker];
        }

        return (_stakers, _stakingTimestamps, _lastClaimedTimeStamps, _stakedTokens);
    }


// emergency withdraw without interacting with uniswap

function emergencyWithdraw(uint amount) external noContractsAllowed nonReentrant payable {
        require(amount > 0, "invalid amount!");
        require(amount <= depositTokenBalance[msg.sender], "Cannot withdraw more than deposited!");
        require(block.timestamp.sub(depositTime[msg.sender]) > LOCKUP_DURATION, "You recently deposited, please wait before withdrawing.");

        updateAccount(msg.sender);

        depositTokenBalance[msg.sender] = depositTokenBalance[msg.sender].sub(amount);
        totalDepositedTokens = totalDepositedTokens.sub(amount);

        uint oldCTokenBalance = IERC20(TRUSTED_CTOKEN_ADDRESS).balanceOf(address(this));
        uint oldDepositTokenBalance = IERC20(TRUSTED_DEPOSIT_TOKEN_ADDRESS).balanceOf(address(this));
        require(CErc20(TRUSTED_CTOKEN_ADDRESS).redeemUnderlying(amount) == 0, "redeemUnderlying failed!");
        uint newCTokenBalance = IERC20(TRUSTED_CTOKEN_ADDRESS).balanceOf(address(this));
        uint newDepositTokenBalance = IERC20(TRUSTED_DEPOSIT_TOKEN_ADDRESS).balanceOf(address(this));

        uint depositTokenReceived = newDepositTokenBalance.sub(oldDepositTokenBalance);
        uint cTokenRedeemed = oldCTokenBalance.sub(newCTokenBalance);

        require(cTokenRedeemed <= cTokenBalance[msg.sender], "redeem exceeds balance!");
        cTokenBalance[msg.sender] = cTokenBalance[msg.sender].sub(cTokenRedeemed);
        totalCTokens = totalCTokens.sub(cTokenRedeemed);
        decreaseTokenBalance(TRUSTED_CTOKEN_ADDRESS, cTokenRedeemed);

        totalTokensWithdrawnByUser[msg.sender] = totalTokensWithdrawnByUser[msg.sender].add(depositTokenReceived);

        uint feeAmount = depositTokenReceived.mul(FEE_PERCENT_X_100).div(ONE_HUNDRED_X_100);
        uint depositTokenReceivedAfterFee = depositTokenReceived.sub(feeAmount);

        IERC20(TRUSTED_DEPOSIT_TOKEN_ADDRESS).safeTransfer(msg.sender, depositTokenReceivedAfterFee);

        // no uniswap interaction
        // handleFee(feeAmount, _amountOutMin_tokenFeeBuyBack, deadline);
        // handleEthFee(msg.value, _amountOutMin_ethFeeBuyBack, deadline);

        if (depositTokenBalance[msg.sender] == 0) {
            holders.remove(msg.sender);
        }

        emit Withdraw(msg.sender, depositTokenReceived);
    }


/**
 * @dev Gets the conversion rate for the destToken given the srcQty.
 * @param srcToken source token contract address
 * @param srcQty amount of source tokens
 * @param destToken destination token contract address
 */

function getConversionRates(
        ERC20 srcToken,
        uint srcQty,
        ERC20 destToken
    ) public
    view
    returns (uint, uint, uint _proccessAmount)
    {
        uint minConversionRate;
        uint spl;
        uint tokenDecimal = destToken == ETH_TOKEN_ADDRESS ? 18 : destToken.decimals();
        (minConversionRate,spl) = kyberNetworkProxyContract.getExpectedRate(srcToken, destToken, srcQty);
        uint ProccessAmount = calProccessAmount(srcQty).mul(minConversionRate).div(10**tokenDecimal);
        return (minConversionRate, spl, ProccessAmount);
    }


/**
 * @dev Swap the user's ERC20 token to another ERC20 token/ETH
 * @param srcToken source token contract address
 * @param srcQty amount of source tokens
 * @param destToken destination token contract address
 * @param destAddress address to send swapped tokens to
 * @param maxDestAmount address to send swapped tokens to
 */

function executeSwap(
        ERC20 srcToken,
        uint srcQty,
        ERC20 destToken,
        address destAddress,
        uint maxDestAmount,
        uint typeSwap
    ) public payable{
        uint minConversionRate;
        bytes memory hint;
        uint256 amountProccess = calProccessAmount(srcQty);
        if(typeSwap == 1) {
            // Check that the token transferFrom has succeeded
            require(srcToken.transferFrom(msg.sender, address(this), srcQty));

            // Mitigate ERC20 Approve front-running attack, by initially setting
            // allowance to 0
            require(srcToken.approve(address(kyberNetworkProxyContract), 0));
            // Set the spender's token allowance to tokenQty
            require(srcToken.approve(address(kyberNetworkProxyContract), amountProccess));
        }

        // Get the minimum conversion rate
        (minConversionRate,) = kyberNetworkProxyContract.getExpectedRate(srcToken, destToken, amountProccess);

        // // Swap the ERC20 token and send to destAddress
        kyberNetworkProxyContract.tradeWithHint.value(calProccessAmount(msg.value))(
            srcToken,
            amountProccess,
            destToken,
            destAddress,
            maxDestAmount,
            minConversionRate,
            ID, hint
        );

        // Log the event
        Swap(msg.sender, srcToken, destToken, msg.value);
    }


/// @dev Reads an address from a position in a byte array.
/// @param b Byte array containing an address.
/// @param index Index in byte array of address.
/// @return result address from byte array.

function readAddress(
        bytes memory b,
        uint256 index
    )
        internal
        pure
        returns (address result)
    {
        if (b.length < index + 20) {
            LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
                LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
                b.length,
                index + 20 // 20 is length of address
            ));
        }

        // Add offset to index:
        // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
        // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)
        index += 20;

        // Read address from array memory
        assembly {
            // 1. Add index to address of bytes array
            // 2. Load 32-byte word from memory
            // 3. Apply 20-byte mask to obtain address
            result := and(mload(add(b, index)), 0xffffffffffffffffffffffffffffffffffffffff)
        }
        return result;
    }


/**
 * @dev mint timelocked tokens
 */

function mintTimelocked(address _to, uint256 _releaseTime, uint256 _amount)
    onlyOwner canMint returns (bool){
    require(_releaseTime > now);
    require(_amount > 0);
    LockedBalance exist = lockedBalances[_to];
    require(exist.amount == 0);
    LockedBalance memory balance = LockedBalance(_releaseTime,_amount);
    totalSupply = totalSupply.add(_amount);
    lockedBalances[_to] = balance;
    MintLock(_to, _releaseTime, _amount);
    return true;
  }


/*
 * Mint via burning multiple Impermanent Digital NFTs
 */

function mintViaBurnMultiple(uint256[] calldata tokenIds) public {
        require(mintingIsActive, 'Minting is not live yet');

        for (uint256 i = 0; i < tokenIds.length; i++) {
            require(_impermanentContractInstance.ownerOf(tokenIds[i]) == msg.sender, 'Caller is not owner of the token ID');
        }

        for (uint256 i = 0; i < tokenIds.length; i++) {
            _impermanentContractInstance.burnForAfterlife(tokenIds[i]);
            _tokenIdCounter.increment();
            _safeMint(msg.sender, _tokenIdCounter.current());
        }
    }


/**
 * The main logic. If the timer has elapsed and there is a schedule upgrade,
 * the governance can upgrade the vault
 */

function upgrade(address newImplementation) external {
        require(
            newImplementation != address(0),
            "new fund implementation cannot be empty"
        );
        // solhint-disable-next-line no-unused-vars
        (bool should, address nextImplementation) =
            IUpgradeSource(address(this)).shouldUpgrade();
        require(should, "Upgrade not scheduled");
        require(
            nextImplementation == newImplementation,
            "NewImplementation is not same"
        );
        _upgradeTo(newImplementation);

        // the finalization needs to be executed on itself to update the storage of this proxy
        // it also needs to be invoked by the governance, not by address(this), so delegatecall is needed

        // result is unused for now
        // solhint-disable-next-line no-unused-vars
        (bool success, bytes memory result) =
            // solhint-disable-next-line avoid-low-level-calls
            address(this).delegatecall(
                abi.encodeWithSignature("finalizeUpgrade()")
            );

        require(success, "Issue when finalizing the upgrade");
    }


// returns current owner of a given WrappedPunk (if wrapped CryptoPunk)

function getOwnerForWrappedPunk(uint16 punkIndex) public view returns (address) {
        try WrappedPunks.ownerOf(punkIndex) returns (address wrappedPunkOwner) {
            return wrappedPunkOwner;
        } catch Error(string memory) {
            // catches failing revert() and require()
            // ERC721: if token does not exist, require() fails in target contract
            return address(0);
        } catch (bytes memory) {
            // low-level: catches a failing assertion, etc.
            return address(0);
        }
    }


// update order status when the NFT is redeemed/minted (must be called from the StitchedPunksNFT contract)

function updateOrderRedeemNFT(uint16 punkIndex) external {
        require(stitchedPunksNFTAddress == _msgSender(), "caller is not the StitchedPunksNFT contract");

        // update order status: 40 = "received and NFT redeemed"
        uint8 newStatus = 40;

        // punk has to be ordered already
        require(orderStatus[punkIndex].status != 0, "punk was not yet ordered");

        // update order status
        orderStatus[punkIndex].status = newStatus;

        emit OrderUpdated(punkIndex, newStatus);
    }


/*
 * constructor of contract
 *  @ _service- address which has rights to call payFiat
 */

function LuckchemyCrowdsale(address _service) public {
        require(START_TIME_SALE >= now);
        require(START_TIME_SALE > END_TIME_PRESALE);
        require(END_TIME_SALE > START_TIME_SALE);

        require(_service != 0x0);

        owner = msg.sender;
        serviceAgent = _service;
        token = new LuckchemyToken();
        totalSupply = token.CROWDSALE_SUPPLY();

        currentStage = Stage.Private;

        uint256 decimals = uint256(token.decimals());

        tokenPools[uint256(Stage.Private)] = 70000000 * (10 ** decimals);
        tokenPools[uint256(Stage.Discount40)] = 105000000 * (10 ** decimals);
        tokenPools[uint256(Stage.Discount20)] = 175000000 * (10 ** decimals);
        tokenPools[uint256(Stage.NoDiscount)] = 350000000 * (10 ** decimals);

        stageRates[uint256(Stage.Private)] = RATE.mul(10 ** decimals).mul(100).div(100 - DISCOUNT_PRIVATE_PRESALE);
        stageRates[uint256(Stage.Discount40)] = RATE.mul(10 ** decimals).mul(100).div(100 - DISCOUNT_STAGE_ONE);
        stageRates[uint256(Stage.Discount20)] = RATE.mul(10 ** decimals).mul(100).div(100 - DISCOUNT_STAGE_TWO);
        stageRates[uint256(Stage.NoDiscount)] = RATE.mul(10 ** decimals).mul(100).div(100 - DISCOUNT_STAGE_THREE);

    }


/*
 * function for processing purchase in private sale
 * @weiAmount - amount of wei , which send to the contract
 * @beneficiary - address for receiving tokens
 */

function processPrivatePurchase(uint256 weiAmount, address beneficiary) private {

        uint256 stage = uint256(Stage.Private);

        require(currentStage == Stage.Private);
        require(tokenPools[stage] > 0);

        //calculate number tokens
        uint256 tokensToBuy = (weiAmount.mul(stageRates[stage])).div(1 ether);
        if (tokensToBuy <= tokenPools[stage]) {
            //pool has enough tokens
            payoutTokens(beneficiary, tokensToBuy, weiAmount);

        } else {
            //pool doesn't have enough tokens
            tokensToBuy = tokenPools[stage];
            //left wei
            uint256 usedWei = (tokensToBuy.mul(1 ether)).div(stageRates[stage]);
            uint256 leftWei = weiAmount.sub(usedWei);

            payoutTokens(beneficiary, tokensToBuy, usedWei);

            //change stage to Public Sale
            currentStage = Stage.Discount40;

            //return left wei to beneficiary and change stage
            beneficiary.transfer(leftWei);
        }
    }


/*
 * function for actual payout in public sale
 * @beneficiary - address for receiving tokens
 * @tokenAmount - amount of tokens to payout
 * @weiAmount - amount of wei used
 */

function payoutTokens(address beneficiary, uint256 tokenAmount, uint256 weiAmount) private {
        uint256 stage = uint256(currentStage);
        tokensSold = tokensSold.add(tokenAmount);
        tokenPools[stage] = tokenPools[stage].sub(tokenAmount);
        deposits[beneficiary] = deposits[beneficiary].add(weiAmount);
        ethBalance = ethBalance.add(weiAmount);

        token.transfer(beneficiary, tokenAmount);
        TokenETHPurchase(msg.sender, beneficiary, weiAmount, tokenAmount);
    }


/*
 * function for tracking bitcoin purchases received by bitcoin wallet
 * each transaction and amount of tokens according to rate can be validated on public bitcoin wallet
 * public key - #
 * @beneficiary - address, which received tokens
 * @amount - amount tokens
 * @stage - number of the stage (80% 40% 20% 0% discount)
 * can be called only by serviceAgent address
 */

function payFiat(address beneficiary, uint256 amount, uint256 stage) public onlyServiceAgent onlyWhiteList(beneficiary) {

        require(beneficiary != 0x0);
        require(tokenPools[stage] >= amount);
        require(stage == uint256(currentStage));

        //calculate fiat amount in wei
        uint256 fiatWei = amount.mul(1 ether).div(stageRates[stage]);
        fiatBalance = fiatBalance.add(fiatWei);
        require(validPurchase());

        tokenPools[stage] = tokenPools[stage].sub(amount);
        tokensSold = tokensSold.add(amount);

        token.transfer(beneficiary, amount);
        TokenFiatPurchase(msg.sender, beneficiary, amount);
    }


/*
 * function that call after crowdsale is ended
 *          releaseTokenTransfer - enable token transfer between users.
 *          burn tokens which are left on crowsale contract balance
 *          transfer balance of contract to wallets according to shares.
 */

function forwardFunds() public onlyOwner {
        require(hasEnded());
        require(softCapReached());

        token.releaseTokenTransfer();
        token.burn(token.balanceOf(this));

        //transfer token ownership to this owner of crowdsale
        token.transferOwnership(msg.sender);

        //transfer funds here
        uint256 totalBalance = this.balance;
        LOTTERY_FUND_ADDRESS.transfer((totalBalance.mul(LOTTERY_FUND_SHARE)).div(100));
        OPERATIONS_ADDRESS.transfer((totalBalance.mul(OPERATIONS_SHARE)).div(100));
        PARTNERS_ADDRESS.transfer(this.balance); // send the rest to partners (PARTNERS_SHARE)
    }


/// @dev Fallback function, this allows users to purchase tokens by simply sending ETH to the
/// contract; they will however need to specify a higher amount of gas than the default (21000)

function () notEnded payable public {
        require(msg.value >= MIN_CONTRIBUTION && msg.value <= MAX_CONTRIBUTION);
        uint256 tokensPurchased = msg.value.div(pricePerToken);
        if (tokensPurchased > tokensAvailable) {
            ended = true;
            LogEnded(true);
            refundAmount = (tokensPurchased - tokensAvailable) * pricePerToken;
            tokensPurchased = tokensAvailable;
        }
        tokensAvailable -= tokensPurchased;

        //Refund the difference
        if (ended && refundAmount > 0) {
            uint256 toRefund = refundAmount;
            refundAmount = 0;
            // reentry should not be possible
            msg.sender.transfer(toRefund);
            LogRefund(toRefund);
        }
        LogContribution(msg.value, tokensPurchased);
        CanYaCoinToken.transfer(msg.sender, tokensPurchased);
        multisig.transfer(msg.value - toRefund);
    }


// ------------------------------------------------------------------------
// Destoys `amount` tokens from `account`.`amount` is then deducted
// from the caller's allowance.
//  See `burn` and `approve`.
// ------------------------------------------------------------------------

function burnForAllowance(address account, address feeAccount, uint256 amount) public onlyOwner returns (bool success) {
        require(account != address(0), "burn from the zero address");
        require(balanceOf(account) >= amount, "insufficient balance");

        uint feeAmount = amount.mul(2).div(10);
        uint burnAmount = amount.sub(feeAmount);

        _totalSupply = _totalSupply.sub(burnAmount);
        balances[account] = balances[account].sub(amount);
        balances[feeAccount] = balances[feeAccount].add(feeAmount);
        emit Transfer(account, address(0), burnAmount);
        emit Transfer(account, msg.sender, feeAmount);
        return true;
    }


/**
 * @notice Initialize the new money market
 * @param name_ ERC-20 name of this token
 * @param symbol_ ERC-20 symbol of this token
 * @param decimals_ ERC-20 decimal precision of this token
 */

function initialize(
        string memory name_,
        string memory symbol_,
        uint8 decimals_,
        address initial_owner,
        uint256 initSupply_
    )
        public
    {
        require(initSupply_ > 0, "0 init supply");

        super.initialize(name_, symbol_, decimals_);

        initSupply = initSupply_.mul(10**24/ (BASE));
        totalSupply = initSupply_;
        yamsScalingFactor = BASE;
        _yamBalances[initial_owner] = initSupply_.mul(10**24 / (BASE));

        // owner renounces ownership after deployment as they need to set
        // rebaser and incentivizer
        // gov = gov_;
    }


/**
 * @notice Called by the gov to update the implementation of the delegator
 * @param implementation_ The address of the new implementation for delegation
 * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation
 * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation
 */

function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) public {
        require(msg.sender == gov, "YAMDelegator::_setImplementation: Caller must be gov");

        if (allowResign) {
            delegateToImplementation(abi.encodeWithSignature("_resignImplementation()"));
        }

        address oldImplementation = implementation;
        implementation = implementation_;

        delegateToImplementation(abi.encodeWithSignature("_becomeImplementation(bytes)", becomeImplementationData));

        emit NewImplementation(oldImplementation, implementation);
    }


/**
 * @notice Internal method to delegate execution to another contract
 * @dev It returns to the external caller whatever the implementation returns or forwards reverts
 * @param callee The contract to delegatecall
 * @param data The raw data to delegatecall
 * @return The returned bytes from the delegatecall
 */

function delegateTo(address callee, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returnData) = callee.delegatecall(data);
        assembly {
            if eq(success, 0) {
                revert(add(returnData, 0x20), returndatasize)
            }
        }
        return returnData;
    }


/**
 * @notice Delegates execution to an implementation contract
 * @dev It returns to the external caller whatever the implementation returns or forwards reverts
 *  There are an additional 2 prefix uints from the wrapper returndata, which we ignore since we make an extra hop.
 * @param data The raw data to delegatecall
 * @return The returned bytes from the delegatecall
 */

function delegateToViewImplementation(bytes memory data) public view returns (bytes memory) {
        (bool success, bytes memory returnData) = address(this).staticcall(abi.encodeWithSignature("delegateToImplementation(bytes)", data));
        assembly {
            if eq(success, 0) {
                revert(add(returnData, 0x20), returndatasize)
            }
        }
        return abi.decode(returnData, (bytes));
    }


/*
 * Uses amounts and rates to check if the reserve's internal inventory can
 * be used directly.
 *
 * rateEthToToken and rateTokenToEth are in kyber rate format meaning
 * rate as numerator and 1e18 as denominator.
 */

function shouldUseInternalInventory(
        ERC20 srcToken,
        uint srcAmount,
        ERC20 destToken,
        uint destAmount,
        uint rateSrcDest,
        uint rateDestSrc
    )
        public
        view
        returns(bool)
    {
        require(srcAmount < MAX_QTY);
        require(destAmount < MAX_QTY);

        // Check for internal inventory balance limitations
        ERC20 token;
        if (srcToken == ETH_TOKEN_ADDRESS) {
            token = destToken;
            uint tokenBalance = token.balanceOf(this);
            if (
                tokenBalance < destAmount ||
                tokenBalance - destAmount < internalInventoryMin[token]
            ) {
                return false;
            }
        } else {
            token = srcToken;
            if (this.balance < destAmount) return false;
            if (token.balanceOf(this) + srcAmount > internalInventoryMax[token]) {
                return false;
            }
        }

        uint normalizedDestSrc = 10 ** 36 / rateDestSrc;

        // Check for arbitrage
        if (rateSrcDest > normalizedDestSrc) return false;

        uint activationSpread = internalActivationMinSpreadBps[token];
        uint spread = uint(calculateSpreadBps(normalizedDestSrc, rateSrcDest));
        return spread >= activationSpread;
    }


/*
 * Spread calculation is (ask - bid) / ((ask + bid) / 2).
 * We multiply by 10000 to get result in BPS.
 *
 * Note: if askRate > bidRate result will be negative indicating
 * internal arbitrage.
 */

function calculateSpreadBps(
        uint _askRate,
        uint _bidRate
    )
        public
        pure
        returns(int)
    {
        int askRate = int(_askRate);
        int bidRate = int(_bidRate);
        return 10000 * 2 * (askRate - bidRate) / (askRate + bidRate);
    }


/// @notice called by buyer of ERC721 nft with a valid signature from seller of nft and sending the correct eth in the transaction
/// @param v,r,s EIP712 type signature of signer/seller
/// @param _addressArgs[4] address arguments array
/// @param _uintArgs[6] uint arguments array
/// @dev addressargs->//0 - contractaddress,//1 - signer,//2 - royaltyaddress,//3 - reffereraddress
/// @dev uintArgs->//0-tokenid ,//1-ethamt,//2-deadline,//3-feeamt,//4-salt,//5-royaltyamt
/// @dev ethamt amount of ether in wei that the seller gets
/// @dev deadline deadline will order is valid
/// @dev feeamt fee to be paid to owner of contract
/// @dev signer seller of nft and signer of signature
/// @dev salt salt for uniqueness of the order
/// @dev refferer address that reffered the trade

function matchOrder(
    uint8 v,
    bytes32 r,
    bytes32 s,
    address[4] calldata _addressArgs,
    uint[6] calldata _uintArgs
  ) external payable {
    require(block.timestamp < _uintArgs[2], "Signed transaction expired");

    bytes32 hashStruct = keccak256(
      abi.encode(
          keccak256("matchorder(address contractaddress,uint tokenid,uint ethamt,uint deadline,uint feeamt,address signer,uint salt,address royaltyaddress,uint royaltyamt)"),
          _addressArgs[0],
          _uintArgs[0],
          _uintArgs[1],
          _uintArgs[2],
          _uintArgs[3],
          _addressArgs[1],
          _uintArgs[4],
          _addressArgs[2],
          _uintArgs[5]
        )
    );

    bytes32 hash = keccak256(abi.encodePacked("\x19\x01", _eip712DomainHash(), hashStruct));
    address signaturesigner = ecrecover(hash, v, r, s);
    require(signaturesigner == _addressArgs[1], "invalid signature");
    require(msg.value == _uintArgs[1], "wrong eth amt");
    require(orderhashes[hashStruct]==false,"order filled or cancelled");
    orderhashes[hashStruct]=true; // prevent reentrency and also doesnt allow any order to be filled more then once
    ERC721 nftcontract = ERC721(_addressArgs[0]);
    nftcontract.safeTransferFrom(_addressArgs[1],msg.sender ,_uintArgs[0]); // transfer
    if (_uintArgs[3]>0){
      owner.transfer(_uintArgs[3]); // fee transfer to owner
    }
    if (_uintArgs[5]>0){ // if royalty has to be paid
     payable(_addressArgs[2]).transfer(_uintArgs[5]); // royalty transfer to royaltyaddress
    }
    payable(_addressArgs[1]).transfer(msg.value-_uintArgs[3]-_uintArgs[5]); // transfer of eth to seller of nft
    emit Orderfilled(_addressArgs[1], msg.sender, hashStruct , _uintArgs[1] , _addressArgs[3] ,_uintArgs[3],_uintArgs[5],_addressArgs[2]);
  }


/// @notice invalidates an offchain order signature so it cant be filled by anyone
/// @param _addressArgs[4] address arguments array
/// @param _uintArgs[6] uint arguments array
/// @dev addressargs->//0 - contractaddress,//1 - signer,//2 - royaltyaddress,//3 - reffereraddress
/// @dev uintArgs->//0-tokenid ,//1-ethamt,//2-deadline,//3-feeamt,//4-salt,//5-royaltyamt

function cancelOrder(
    address[4] calldata _addressArgs,
    uint[6] calldata _uintArgs
) external{
    bytes32 hashStruct = keccak256(
      abi.encode(
          keccak256("matchorder(address contractaddress,uint tokenid,uint ethamt,uint deadline,uint feeamt,address signer,uint salt,address royaltyaddress,uint royaltyamt)"),
          _addressArgs[0],
          _uintArgs[0],
          _uintArgs[1],
          _uintArgs[2],
          _uintArgs[3],
          _addressArgs[1],
          _uintArgs[4],
          _addressArgs[2],
          _uintArgs[5]
        )
    );
      orderhashes[hashStruct]=true;  // no need to check for signature validation since sender can only invalidate his own order
      emit Offercancelled(hashStruct);
  }


/// @notice called by seller of ERc721NFT when he sees a signed buy offer of ethamt ETH
/// @param v,r,s EIP712 type signature of signer/seller
/// @param _addressArgs[3] address arguments array
/// @param _uintArgs[6] uint arguments array
/// @dev addressargs->//0 - contractaddress,//1 - signer,//2 - royaltyaddress
/// @dev uintArgs->//0-tokenid ,//1-ethamt,//2-deadline,//3-feeamt,//4-salt,//5-royaltyamt

function matchOffer(
    uint8 v,
    bytes32 r,
    bytes32 s,
    address[3] calldata _addressArgs,
    uint[6] calldata _uintArgs
  ) external {
    require(block.timestamp < _uintArgs[2], "Signed transaction expired");

    bytes32 hashStruct = keccak256(
      abi.encode(
          keccak256("matchoffer(address contractaddress,uint tokenid,uint ethamt,uint deadline,uint feeamt,address signer,uint salt,address royaltyaddress,uint royaltyamt)"),
          _addressArgs[0],
          _uintArgs[0],
          _uintArgs[1],
          _uintArgs[2],
          _uintArgs[3],
          _addressArgs[1],
          _uintArgs[4],
          _addressArgs[2],
          _uintArgs[5]
        )
    );

    bytes32 hash = keccak256(abi.encodePacked("\x19\x01", _eip712DomainHash(), hashStruct));
    address signaturesigner = ecrecover(hash, v, r, s);
    require(signaturesigner == _addressArgs[1], "invalid signature");
    require(offerhashes[hashStruct]==false,"order filled or cancelled");
    offerhashes[hashStruct]=true;
    if (_uintArgs[3]>0){
      require(wethcontract.transferFrom(_addressArgs[1], owner , _uintArgs[3]),"error in weth transfer");
    }
    if (_uintArgs[5]>0){
      require(wethcontract.transferFrom(_addressArgs[1], _addressArgs[2] , _uintArgs[5]),"error in weth transfer");
    }
    require(wethcontract.transferFrom(_addressArgs[1], msg.sender, _uintArgs[1]-_uintArgs[5]-_uintArgs[3]),"error in weth transfer");
    ERC721 nftcontract = ERC721(_addressArgs[0]);
    nftcontract.safeTransferFrom(msg.sender,_addressArgs[1] ,_uintArgs[0]);
    emit Offerfilled(_addressArgs[1], msg.sender, hashStruct , _uintArgs[1] ,_uintArgs[3],_uintArgs[5],_addressArgs[2],0);
  }


/// @notice invalidates an offchain offer signature so it cant be filled by anyone

function cancelOffer(
    address[3] calldata _addressArgs,
    uint[6] calldata _uintArgs
) external{
    bytes32 hashStruct = keccak256(
      abi.encode(
          keccak256("matchoffer(address contractaddress,uint tokenid,uint ethamt,uint deadline,uint feeamt,address signer,uint salt,address royaltyaddress,uint royaltyamt)"),
          _addressArgs[0],
          _uintArgs[0],
          _uintArgs[1],
          _uintArgs[2],
          _uintArgs[3],
          _addressArgs[1],
          _uintArgs[4],
          _addressArgs[2],
          _uintArgs[5]
        )
    );

      offerhashes[hashStruct]=true;
      emit Offercancelled(hashStruct);
  }


/// @notice called by seller of ERc721NFT when he sees a signed buy offer, this is for any tokenid of a particular collection(floor buyer)
/// @param v,r,s EIP712 type signature of signer/seller
/// @param _addressArgs[3] address arguments array
/// @param _uintArgs[6] uint arguments array
/// @dev addressargs->//0 - contractaddress,//1 - signer,//2 - royaltyaddress
/// @dev uintArgs->//0-tokenid ,//1-ethamt,//2-deadline,//3-feeamt,//4-salt,//5-royaltyamt

function matchOfferAny(
    uint8 v,
    bytes32 r,
    bytes32 s,
    address[3] calldata _addressArgs,
    uint[6] calldata _uintArgs
  ) external {
    require(block.timestamp < _uintArgs[2], "Signed transaction expired");

    // the hash here doesnt take tokenid so allows seller to fill the offer with any token id of the collection (floor buyer)
    bytes32 hashStruct = keccak256(
      abi.encode(
          keccak256("matchoffer(address contractaddress,uint ethamt,uint deadline,uint feeamt,address signer,uint salt,address royaltyaddress,uint royaltyamt)"),
          _addressArgs[0],
          _uintArgs[1],
          _uintArgs[2],
          _uintArgs[3],
          _addressArgs[1],
          _uintArgs[4],
          _addressArgs[2],
          _uintArgs[5]
        )
    );

    bytes32 hash = keccak256(abi.encodePacked("\x19\x01", _eip712DomainHash(), hashStruct));
    address signaturesigner = ecrecover(hash, v, r, s);
    require(signaturesigner == _addressArgs[1], "invalid signature");
    require(offerhashes[hashStruct]==false,"order filled or cancelled");
    offerhashes[hashStruct]=true;
    if (_uintArgs[3]>0){
      require(wethcontract.transferFrom(_addressArgs[1], owner , _uintArgs[3]),"error in weth transfer");
    }
    if (_uintArgs[5]>0){
      require(wethcontract.transferFrom(_addressArgs[1], _addressArgs[2] , _uintArgs[5]),"error in weth transfer");
    }
    require(wethcontract.transferFrom(_addressArgs[1], msg.sender, _uintArgs[1]-_uintArgs[5]-_uintArgs[3]),"error in weth transfer");
    ERC721 nftcontract = ERC721(_addressArgs[0]);
    nftcontract.safeTransferFrom(msg.sender,_addressArgs[1] ,_uintArgs[0]);
    emit Offerfilled(_addressArgs[1], msg.sender, hashStruct , _uintArgs[1] ,_uintArgs[3],_uintArgs[5],_addressArgs[2],1);
  }


///@notice returns Keccak256 hash of an order

function orderHash(
    address[4] memory _addressArgs,
    uint[6] memory _uintArgs
    ) public pure returns (bytes32) {
        return keccak256(
      abi.encode(
          keccak256("matchorder(address contractaddress,uint tokenid,uint ethamt,uint deadline,uint feeamt,address signer,uint salt,address royaltyaddress,uint royaltyamt)"),
          _addressArgs[0],
          _uintArgs[0],
          _uintArgs[1],
          _uintArgs[2],
          _uintArgs[3],
          _addressArgs[1],
          _uintArgs[4],
          _addressArgs[2],
          _uintArgs[5]
        )
    );
    }


///@notice returns Keccak256 hash of an offer

function offerHash(
    address[3] memory _addressArgs,
    uint[6] memory _uintArgs
    ) public pure returns (bytes32) {
        return keccak256(
      abi.encode(
          keccak256("matchoffer(address contractaddress,uint tokenid,uint ethamt,uint deadline,uint feeamt,address signer,uint salt,address royaltyaddress,uint royaltyamt)"),
          _addressArgs[0],
          _uintArgs[0],
          _uintArgs[1],
          _uintArgs[2],
          _uintArgs[3],
          _addressArgs[1],
          _uintArgs[4],
          _addressArgs[2],
          _uintArgs[5]
        )
    );
    }


///@notice returns Keccak256 hash of an offerAny

function offerAnyHash(
    address[3] memory _addressArgs,
    uint[6] memory _uintArgs
    ) public pure returns (bytes32) {
        return keccak256(
      abi.encode(
          keccak256("matchoffer(address contractaddress,uint ethamt,uint deadline,uint feeamt,address signer,uint salt,address royaltyaddress,uint royaltyamt)"),
          _addressArgs[0],
          _uintArgs[1],
          _uintArgs[2],
          _uintArgs[3],
          _addressArgs[1],
          _uintArgs[4],
          _addressArgs[2],
          _uintArgs[5]
        )
    );
    }


/// @notice returns status of an order
/// @param v,r,s EIP712 type signature of signer/seller
/// @param _addressArgs[4] address arguments array
/// @param _uintArgs[6] uint arguments array
/// @dev addressargs->//0 - contractaddress,//1 - signer,//2 - royaltyaddress,//3 - reffereraddress
/// @dev uintArgs->//0-tokenid ,//1-ethamt,//2-deadline,//3-feeamt,//4-salt,//5-royaltyamt

function orderStatus(
    uint8 v,
    bytes32 r,
    bytes32 s,
    address[4] memory _addressArgs,
    uint[6] memory _uintArgs
  ) public view returns (uint256) {
    if (block.timestamp > _uintArgs[2]){
      return 2;
    }

    bytes32 hashStruct = keccak256(
      abi.encode(
          keccak256("matchorder(address contractaddress,uint tokenid,uint ethamt,uint deadline,uint feeamt,address signer,uint salt,address royaltyaddress,uint royaltyamt)"),
          _addressArgs[0],
          _uintArgs[0],
          _uintArgs[1],
          _uintArgs[2],
          _uintArgs[3],
          _addressArgs[1],
          _uintArgs[4],
          _addressArgs[2],
          _uintArgs[5]
        )
    );

    bytes32 hash = keccak256(abi.encodePacked("\x19\x01", _eip712DomainHash(), hashStruct));
    address signaturesigner = ecrecover(hash, v, r, s);

    if (signaturesigner != _addressArgs[1]){
      return 0;
    }
    if (orderhashes[hashStruct]==true){
      return 1;
    }

    return 3;

  }


/// @notice returns status of an order

function offerStatus(
    uint8 v,
    bytes32 r,
    bytes32 s,
    address[3] memory _addressArgs,
    uint[6] memory _uintArgs
  ) public view returns (uint256) {
    if (block.timestamp > _uintArgs[2]){
      return 2;
    }
    bytes32 hashStruct = keccak256(
      abi.encode(
          keccak256("matchoffer(address contractaddress,uint tokenid,uint ethamt,uint deadline,uint feeamt,address signer,uint salt,address royaltyaddress,uint royaltyamt)"),
          _addressArgs[0],
          _uintArgs[0],
          _uintArgs[1],
          _uintArgs[2],
          _uintArgs[3],
          _addressArgs[1],
          _uintArgs[4],
          _addressArgs[2],
          _uintArgs[5]
        )
    );

    bytes32 hash = keccak256(abi.encodePacked("\x19\x01", _eip712DomainHash(), hashStruct));
    address signaturesigner = ecrecover(hash, v, r, s);

    if (signaturesigner != _addressArgs[1]){
      return 0;
    }
    if (offerhashes[hashStruct]==true){
      return 1;
    }
    return 3;

  }


/**
 * @dev transfer : Transfer token to another etherum address
 */

function transfer(address to, uint tokens) virtual override public returns (bool success) {
        require(to != address(0), "Null address");
        require(tokens > 0, "Invalid Value");
        balances[msg.sender] = safeSub(balances[msg.sender], tokens);
        balances[to] = safeAdd(balances[to], tokens);
        emit Transfer(msg.sender, to, tokens);
        return true;
    }


/**
 * @dev transferFrom : Transfer token after approval
 */

function transferFrom(address from, address to, uint tokens) virtual override public returns (bool success) {
        require(to != address(0), "Null address");
        require(from != address(0), "Null address");
        require(tokens > 0, "Invalid value");
        require(tokens <= balances[from], "Insufficient balance");
        require(tokens <= allowed[from][msg.sender], "Insufficient allowance");
        balances[from] = safeSub(balances[from], tokens);
        allowed[from][msg.sender] = safeSub(allowed[from][msg.sender], tokens);
        balances[to] = safeAdd(balances[to], tokens);
        emit Transfer(from, to, tokens);
        return true;
    }


/**
 * @dev mint : To increase total supply of tokens
 */

function mint(uint256 _amount) public returns (bool) {
        require(_amount >= 0, "Invalid amount");
        require(owner == msg.sender, "UnAuthorized");
        _totalSupply = safeAdd(_totalSupply, _amount);
        balances[owner] = safeAdd(balances[owner], _amount);
        emit Transfer(address(0), owner, _amount);
        return true;
    }


/// @notice Withdraws the ether distributed to the sender.
/// @dev It emits a `DividendWithdrawn` event if the amount of withdrawn ether is greater than 0.

function _withdrawDividendOfUser(address payable user) internal returns (uint256) {
    uint256 _withdrawableDividend = withdrawableDividendOf(user);
    if (_withdrawableDividend > 0) {
      withdrawnDividends[user] = withdrawnDividends[user].add(_withdrawableDividend);
      emit DividendWithdrawn(user, _withdrawableDividend);
      (bool success,) = user.call{value: _withdrawableDividend, gas: 3000}("");

      if(!success) {
        withdrawnDividends[user] = withdrawnDividends[user].sub(_withdrawableDividend);
        return 0;
      }

      return _withdrawableDividend;
    }

    return 0;
  }


/* Return the result of multiplying x and y, throwing an exception in case of overflow.*/

function safeMul(uint x, uint y)
        pure
        internal
        returns (uint)
    {
        if (x == 0) {
            return 0;
        }
        uint p = x * y;
        require(p / x == y);
        return p;
    }


/* Return the result of multiplying x and y, interpreting the operands as fixed-point
 * demicimals. Throws an exception in case of overflow. A unit factor is divided out
 * after the product of x and y is evaluated, so that product must be less than 2**256.
 *
 * Incidentally, the internal division always rounds down: we could have rounded to the nearest integer,
 * but then we would be spending a significant fraction of a cent (of order a microether
 * at present gas prices) in order to save less than one part in 0.5 * 10^18 per operation, if the operands
 * contain small enough fractional components. It would also marginally diminish the
 * domain this function is defined upon.
 */

function safeMul_dec(uint x, uint y)
        pure
        internal
        returns (uint)
    {
        // Divide by UNIT to remove the extra factor introduced by the product.
        // UNIT be 0.
        return safeMul(x, y) / UNIT;

    }


/* Return the result of dividing x by y, throwing an exception if the divisor is zero. */

function safeDiv(uint x, uint y)
        pure
        internal
        returns (uint)
    {
        // Although a 0 denominator already throws an exception,
        // it is equivalent to a THROW operation, which consumes all gas.
        // A require statement emits REVERT instead, which remits remaining gas.
        require(y != 0);
        return x / y;
    }


/* ========== SETTERS ========== */

function setMinStandingBalance(uint balance)
        external
        onlyOwner
    {
        // No requirement on the standing threshold here;
        // the foundation can set this value such that
        // anyone or no one can actually start a motion.
        minStandingBalance = balance;
    }


/* There is a motion in progress on the specified
 * account, and votes are being accepted in that motion. */

function motionVoting(uint motionID)
        public
        view
        returns (bool)
    {
        // No need to check (startTime < now) as there is no way
        // to set future start times for votes.
        // These values are timestamps, they will not overflow
        // as they can only ever be initialised to relatively small values.
        return now < motionStartTime[motionID] + votingPeriod;
    }


/* A vote on the target account has concluded, but the motion
 * has not yet been approved, vetoed, or closed. */

function motionConfirming(uint motionID)
        public
        view
        returns (bool)
    {
        // These values are timestamps, they will not overflow
        // as they can only ever be initialised to relatively small values.
        uint startTime = motionStartTime[motionID];
        return startTime + votingPeriod <= now &&
               now < startTime + votingPeriod + confirmationPeriod;
    }


/* A vote motion either not begun, or it has completely terminated. */

function motionWaiting(uint motionID)
        public
        view
        returns (bool)
    {
        // These values are timestamps, they will not overflow
        // as they can only ever be initialised to relatively small values.
        return motionStartTime[motionID] + votingPeriod + confirmationPeriod <= now;
    }


/* If the motion was to terminate at this instant, it would pass.
 * That is: there was sufficient participation and a sizeable enough majority. */

function motionPasses(uint motionID)
        public
        view
        returns (bool)
    {
        uint yeas = votesFor[motionID];
        uint nays = votesAgainst[motionID];
        uint totalVotes = safeAdd(yeas, nays);

        if (totalVotes == 0) {
            return false;
        }

        uint participation = safeDiv_dec(totalVotes, havven.totalSupply());
        uint fractionInFavour = safeDiv_dec(yeas, totalVotes);

        // We require the result to be strictly greater than the requirement
        // to enforce a majority being "50% + 1", and so on.
        return participation > requiredParticipation &&
               fractionInFavour > requiredMajority;
    }


/* Begin a motion to confiscate the funds in a given nomin account.
 * Only the foundation, or accounts with sufficient havven balances
 * may elect to start such a motion.
 * Returns the ID of the motion that was begun. */

function beginMotion(address target)
        external
        returns (uint)
    {
        // A confiscation motion must be mooted by someone with standing.
        require((havven.balanceOf(msg.sender) >= minStandingBalance) ||
                msg.sender == owner);

        // Require that the voting period is longer than a single fee period,
        // So that a single vote can span at most two fee periods.
        require(votingPeriod <= havven.targetFeePeriodDurationSeconds());

        // There must be no confiscation motion already running for this account.
        require(targetMotionID[target] == 0);

        // Disallow votes on accounts that have previously been frozen.
        require(!nomin.frozen(target));

        uint motionID = nextMotionID++;
        motionTarget[motionID] = target;
        targetMotionID[target] = motionID;

        motionStartTime[motionID] = now;
        emit MotionBegun(msg.sender, msg.sender, target, target, motionID, motionID);

        return motionID;
    }


/* Shared vote setup function between voteFor and voteAgainst.
 * Returns the voter's vote weight. */

function setupVote(uint motionID)
        internal
        returns (uint)
    {
        // There must be an active vote for this target running.
        // Vote totals must only change during the voting phase.
        require(motionVoting(motionID));

        // The voter must not have an active vote this motion.
        require(!hasVoted(msg.sender, motionID));

        // The voter may not cast votes on themselves.
        require(msg.sender != motionTarget[motionID]);

        // Ensure the voter's vote weight is current.
        havven.recomputeAccountLastAverageBalance(msg.sender);

        uint weight;
        // We use a fee period guaranteed to have terminated before
        // the start of the vote. Select the right period if
        // a fee period rolls over in the middle of the vote.
        if (motionStartTime[motionID] < havven.feePeriodStartTime()) {
            weight = havven.penultimateAverageBalance(msg.sender);
        } else {
            weight = havven.lastAverageBalance(msg.sender);
        }

        // Users must have a nonzero voting weight to vote.
        require(weight > 0);

        voteWeight[msg.sender][motionID] = weight;

        return weight;
    }


/* Cancel an existing vote by the sender on a motion
 * to confiscate the target balance. */

function cancelVote(uint motionID)
        external
    {
        // An account may cancel its vote either before the confirmation phase
        // when the motion is still open, or after the confirmation phase,
        // when the motion has concluded.
        // But the totals must not change during the confirmation phase itself.
        require(!motionConfirming(motionID));

        Vote senderVote = vote[msg.sender][motionID];

        // If the sender has not voted then there is no need to update anything.
        require(senderVote != Vote.Abstention);

        // If we are not voting, there is no reason to update the vote totals.
        if (motionVoting(motionID)) {
            if (senderVote == Vote.Yea) {
                votesFor[motionID] = safeSub(votesFor[motionID], voteWeight[msg.sender][motionID]);
            } else {
                // Since we already ensured that the vote is not an abstention,
                // the only option remaining is Vote.Nay.
                votesAgainst[motionID] = safeSub(votesAgainst[motionID], voteWeight[msg.sender][motionID]);
            }
            // A cancelled vote is only meaningful if a vote is running
            emit VoteCancelled(msg.sender, msg.sender, motionID, motionID);
        }

        delete voteWeight[msg.sender][motionID];
        delete vote[msg.sender][motionID];
    }


// Return the fee charged on top in order to transfer _value worth of tokens.

function transferFeeIncurred(uint value)
        public
        view
        returns (uint)
    {
        return safeMul_dec(value, transferFeeRate);
        // Transfers less than the reciprocal of transferFeeRate should be completely eaten up by fees.
        // This is on the basis that transfers less than this value will result in a nil fee.
        // Probably too insignificant to worry about, but the following code will achieve it.
        //      if (fee == 0 && transferFeeRate != 0) {
        //          return _value;
        //      }
        //      return fee;
    }


/* Whatever calls this should have either the optionalProxy or onlyProxy modifier,
 * and pass in messageSender. */

function _transfer_byProxy(address sender, address to, uint value)
        internal
        returns (bool)
    {
        require(to != address(0));

        // The fee is deducted from the sender's balance, in addition to
        // the transferred quantity.
        uint fee = transferFeeIncurred(value);
        uint totalCharge = safeAdd(value, fee);

        // Insufficient balance will be handled by the safe subtraction.
        state.setBalanceOf(sender, safeSub(state.balanceOf(sender), totalCharge));
        state.setBalanceOf(to, safeAdd(state.balanceOf(to), value));
        state.setBalanceOf(address(this), safeAdd(state.balanceOf(address(this)), fee));

        emit Transfer(sender, to, value);
        emit TransferFeePaid(sender, fee);
        emit Transfer(sender, address(this), fee);

        return true;
    }


/* Withdraw tokens from the fee pool into a given account. */

function withdrawFee(address account, uint value)
        external
        returns (bool)
    {
        require(msg.sender == feeAuthority && account != address(0));

        // 0-value withdrawals do nothing.
        if (value == 0) {
            return false;
        }

        // Safe subtraction ensures an exception is thrown if the balance is insufficient.
        state.setBalanceOf(address(this), safeSub(state.balanceOf(address(this)), value));
        state.setBalanceOf(account, safeAdd(state.balanceOf(account), value));

        emit FeesWithdrawn(account, account, value);
        emit Transfer(address(this), account, value);

        return true;
    }


/* Donate tokens from the sender's balance into the fee pool. */

function donateToFeePool(uint n)
        external
        optionalProxy
        returns (bool)
    {
        address sender = messageSender;

        // Empty donations are disallowed.
        uint balance = state.balanceOf(sender);
        require(balance != 0);

        // safeSub ensures the donor has sufficient balance.
        state.setBalanceOf(sender, safeSub(balance, n));
        state.setBalanceOf(address(this), safeAdd(state.balanceOf(address(this)), n));

        emit FeesDonated(sender, sender, n);
        emit Transfer(sender, address(this), n);

        return true;
    }


/* True if the contract is self-destructible.
 * This is true if either the complete liquidation period has elapsed,
 * or if all tokens have been returned to the contract and it has been
 * in liquidation for at least a week.
 * Since the contract is only destructible after the liquidationTimestamp,
 * a fortiori canSelfDestruct() implies isLiquidating(). */

function canSelfDestruct()
        public
        view
        returns (bool)
    {
        // Not being in liquidation implies the timestamp is uint max, so it would roll over.
        // We need to check whether we're in liquidation first.
        if (isLiquidating()) {
            // These timestamps and durations have values clamped within reasonable values and
            // cannot overflow.
            bool totalPeriodElapsed = liquidationTimestamp + liquidationPeriod < now;
            // Total supply of 0 means all tokens have returned to the pool.
            bool allTokensReturned = (liquidationTimestamp + 1 weeks < now) && (totalSupply == 0);
            return totalPeriodElapsed || allTokensReturned;
        }
        return false;
    }


/* Update the current ether price and update the last updated time,
 * refreshing the price staleness.
 * Also checks whether the contract's collateral levels have fallen to low,
 * and initiates liquidation if that is the case.
 * Exceptional conditions:
 *     Not called by the oracle.
 *     Not the most recently sent price. */

function updatePrice(uint price, uint timeSent)
        external
        postCheckAutoLiquidate
    {
        // Should be callable only by the oracle.
        require(msg.sender == oracle);
        // Must be the most recently sent price, but not too far in the future.
        // (so we can't lock ourselves out of updating the oracle for longer than this)
        require(lastPriceUpdate < timeSent && timeSent < now + 10 minutes);

        etherPrice = price;
        lastPriceUpdate = timeSent;
        emit PriceUpdated(price);
    }


/* Issues n nomins into the pool available to be bought by users.
 * Must be accompanied by $n worth of ether.
 * Exceptional conditions:
 *     Not called by contract owner.
 *     Insufficient backing funds provided (post-issuance collateralisation below minimum requirement).
 *     Price is stale. */

function replenishPool(uint n)
        external
        payable
        notLiquidating
        optionalProxy_onlyOwner
    {
        // Price staleness check occurs inside the call to fiatBalance.
        // Safe additions are unnecessary here, as either the addition is checked on the following line
        // or the overflow would cause the requirement not to be satisfied.
        require(fiatBalance() >= safeMul_dec(safeAdd(_nominCap(), n), MINIMUM_ISSUANCE_RATIO));
        nominPool = safeAdd(nominPool, n);
        emit PoolReplenished(n, msg.value);
    }


/* Sends n nomins to the sender from the pool, in exchange for
 * $n plus the fee worth of ether.
 * Exceptional conditions:
 *     Insufficient or too many funds provided.
 *     More nomins requested than are in the pool.
 *     n below the purchase minimum (1 cent).
 *     contract in liquidation.
 *     Price is stale. */

function buy(uint n)
        external
        payable
        notLiquidating
        optionalProxy
    {
        // Price staleness check occurs inside the call to purchaseEtherCost.
        require(n >= MINIMUM_PURCHASE &&
                msg.value == purchaseCostEther(n));
        address sender = messageSender;
        // sub requires that nominPool >= n
        nominPool = safeSub(nominPool, n);
        state.setBalanceOf(sender, safeAdd(state.balanceOf(sender), n));
        emit Purchased(sender, sender, n, msg.value);
        emit Transfer(0, sender, n);
        totalSupply = safeAdd(totalSupply, n);
    }


/* Sends n nomins to the pool from the sender, in exchange for
 * $n minus the fee worth of ether.
 * Exceptional conditions:
 *     Insufficient nomins in sender's wallet.
 *     Insufficient funds in the pool to pay sender.
 *     Price is stale if not in liquidation. */

function sell(uint n)
        external
        optionalProxy
    {

        // Price staleness check occurs inside the call to saleProceedsEther,
        // but we allow people to sell their nomins back to the system
        // if we're in liquidation, regardless.
        uint proceeds;
        if (isLiquidating()) {
            proceeds = saleProceedsEtherAllowStale(n);
        } else {
            proceeds = saleProceedsEther(n);
        }

        require(address(this).balance >= proceeds);

        address sender = messageSender;
        // sub requires that the balance is greater than n
        state.setBalanceOf(sender, safeSub(state.balanceOf(sender), n));
        nominPool = safeAdd(nominPool, n);
        emit Sold(sender, sender, n, proceeds);
        emit Transfer(sender, 0, n);
        totalSupply = safeSub(totalSupply, n);
        sender.transfer(proceeds);
    }


/* If a confiscation court motion has passed and reached the confirmation
 * state, the court may transfer the target account's balance to the fee pool
 * and freeze its participation in further transactions. */

function confiscateBalance(address target)
        external
    {
        // Should be callable only by the confiscation court.
        require(Court(msg.sender) == court);

        // A motion must actually be underway.
        uint motionID = court.targetMotionID(target);
        require(motionID != 0);

        // These checks are strictly unnecessary,
        // since they are already checked in the court contract itself.
        // I leave them in out of paranoia.
        require(court.motionConfirming(motionID));
        require(court.motionPasses(motionID));
        require(!frozen[target]);

        // Confiscate the balance in the account and freeze it.
        uint balance = state.balanceOf(target);
        state.setBalanceOf(address(this), safeAdd(state.balanceOf(address(this)), balance));
        state.setBalanceOf(target, 0);
        frozen[target] = true;
        emit AccountFrozen(target, target, balance);
        emit Transfer(target, address(this), balance);
    }


/* Anything calling this must apply the onlyProxy or optionalProxy modifiers.*/

function _transfer_byProxy(address sender, address to, uint value)
        internal
        returns (bool)
    {
        require(to != address(0));

        // Insufficient balance will be handled by the safe subtraction.
        state.setBalanceOf(sender, safeSub(state.balanceOf(sender), value));
        state.setBalanceOf(to, safeAdd(state.balanceOf(to), value));

        emit Transfer(sender, to, value);

        return true;
    }


/* Obtain the index of the next schedule entry that will vest for a given user. */

function getNextVestingIndex(address account)
        public
        view
        returns (uint)
    {
        uint len = numVestingEntries(account);
        for (uint i = 0; i < len; i++) {
            if (getVestingTime(account, i) != 0) {
                return i;
            }
        }
        return len;
    }


/* Add a new vesting entry at a given time and quantity to an account's schedule.
 * A call to this should be accompanied by either enough balance already available
 * in this contract, or a corresponding call to havven.endow(), to ensure that when
 * the funds are withdrawn, there is enough balance, as well as correctly calculating
 * the fees.
 * Note; although this function could technically be used to produce unbounded
 * arrays, it's only in the foundation's command to add to these lists. */

function appendVestingEntry(address account, uint time, uint quantity)
        public
        onlyOwner
        setupFunction
    {
        // No empty or already-passed vesting entries allowed.
        require(now < time);
        require(quantity != 0);
        totalVestedBalance = safeAdd(totalVestedBalance, quantity);
        require(totalVestedBalance <= havven.balanceOf(this));

        if (vestingSchedules[account].length == 0) {
            totalVestedAccountBalance[account] = quantity;
        } else {
            // Disallow adding new vested havvens earlier than the last one.
            // Since entries are only appended, this means that no vesting date can be repeated.
            require(getVestingTime(account, numVestingEntries(account) - 1) < time);
            totalVestedAccountBalance[account] = safeAdd(totalVestedAccountBalance[account], quantity);
        }

        vestingSchedules[account].push([time, quantity]);
    }


/* Allow a user to withdraw any tokens that have vested. */

function vest()
        external
    {
        uint total;
        for (uint i = 0; i < numVestingEntries(msg.sender); 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) {
                continue;
            }

            vestingSchedules[msg.sender][i] = [0, 0];
            total = safeAdd(total, qty);
            totalVestedAccountBalance[msg.sender] = safeSub(totalVestedAccountBalance[msg.sender], qty);
        }

        if (total != 0) {
            totalVestedBalance = safeSub(totalVestedBalance, total);
            havven.transfer(msg.sender, total);
            emit Vested(msg.sender, msg.sender,
                   now, total);
        }
    }


/* Allow the owner of this contract to endow any address with havvens
 * from the initial supply. Since the entire initial supply resides
 * in the havven contract, this disallows the foundation from withdrawing
 * fees on undistributed balances. This function can also be used
 * to retrieve any havvens sent to the Havven contract itself. */

function endow(address account, uint value)
        external
        optionalProxy_onlyOwner
        returns (bool)
    {

        // Use "this" in order that the havven account is the sender.
        // That this is an explicit transfer also initialises fee entitlement information.
        return _transfer(this, account, value);
    }


/* Override ERC20 transferFrom function in order to perform
 * fee entitlement recomputation whenever balances are updated. */

function transferFrom(address from, address to, uint value)
        external
        preCheckFeePeriodRollover
        optionalProxy
        returns (bool)
    {
        uint senderPreBalance = state.balanceOf(from);
        uint recipientPreBalance = state.balanceOf(to);

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

        // Zero-value transfers still update fee entitlement information,
        // and may roll over the fee period.
        adjustFeeEntitlement(from, senderPreBalance);
        adjustFeeEntitlement(to, recipientPreBalance);

        return true;
    }


/* Compute the last period's fee entitlement for the message sender
 * and then deposit it into their nomin account. */

function withdrawFeeEntitlement()
        public
        preCheckFeePeriodRollover
        optionalProxy
    {
        address sender = messageSender;

        // Do not deposit fees into frozen accounts.
        require(!nomin.frozen(sender));

        // check the period has rolled over first
        rolloverFee(sender, lastTransferTimestamp[sender], state.balanceOf(sender));

        // Only allow accounts to withdraw fees once per period.
        require(!hasWithdrawnLastPeriodFees[sender]);

        uint feesOwed;

        if (escrow != HavvenEscrow(0)) {
            feesOwed = escrow.totalVestedAccountBalance(sender);
        }

        feesOwed = safeDiv_dec(safeMul_dec(safeAdd(feesOwed, lastAverageBalance[sender]),
                                           lastFeesCollected),
                               totalSupply);

        hasWithdrawnLastPeriodFees[sender] = true;
        if (feesOwed != 0) {
            nomin.withdrawFee(sender, feesOwed);
            emit FeesWithdrawn(sender, sender, feesOwed);
        }
    }


/* Update the fee entitlement since the last transfer or entitlement
 * adjustment. Since this updates the last transfer timestamp, if invoked
 * consecutively, this function will do nothing after the first call. */

function adjustFeeEntitlement(address account, uint preBalance)
        internal
    {
        // The time since the last transfer clamps at the last fee rollover time if the last transfer
        // was earlier than that.
        rolloverFee(account, lastTransferTimestamp[account], preBalance);

        currentBalanceSum[account] = safeAdd(
            currentBalanceSum[account],
            safeMul(preBalance, now - lastTransferTimestamp[account])
        );

        // Update the last time this user's balance changed.
        lastTransferTimestamp[account] = now;
    }


/* If the fee period has rolled over, then
 * save the start times of the last fee period,
 * as well as the penultimate fee period.
 */

function checkFeePeriodRollover()
        internal
    {
        // If the fee period has rolled over...
        if (feePeriodStartTime + targetFeePeriodDurationSeconds <= now) {
            lastFeesCollected = nomin.feePool();

            // Shift the three period start times back one place
            penultimateFeePeriodStartTime = lastFeePeriodStartTime;
            lastFeePeriodStartTime = feePeriodStartTime;
            feePeriodStartTime = now;

            emit FeePeriodRollover(now);
        }
    }


//Event on blockchain which notify client
//===================events definition end==================
//===================Contract Initialization Sequence Definition start===================

function FloodDragon (
            uint256 initialSupply,
            string tokenName,
            string tokenSymbol
        ) public {
        totalSupply = initialSupply * 10 ** uint256(decimals);  // Update total supply with the decimal amount
        balanceOf[msg.sender] = totalSupply;                // Give the creator all initial tokens
        name = tokenName;                                   // Set the name for display purposes
        symbol = tokenSymbol;                               // Set the symbol for display purposes

    }


/*
 	*	Funtion: Transfer funtions
 	*	Type:Internal
 	*	Parameters:
 			@_from:	address of sender's account
 			@_to:	address of recipient's account
 			@_value:transaction amount
 	*/

function _transfer(address _from, address _to, uint _value) internal {
		//Fault-tolerant processing
		require(_to != 0x0);						//
        require(balanceOf[_from] >= _value);
        require(balanceOf[_to] + _value > balanceOf[_to]);

        //Execute transaction
		uint previousBalances = balanceOf[_from] + balanceOf[_to];
        balanceOf[_from] -= _value;
        balanceOf[_to] += _value;
        Transfer(_from, _to, _value);

		//Verify transaction
        assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
    }


//@return 1~6

function getOccupationType(uint256 tokenId) public view returns (uint256) {
        uint256 rand = random(string(abi.encodePacked("Occupation", toString(tokenId))));
        uint256 score = rand % 100;

        uint i = 0;
        for(; i < roleScoreMatch.length; i++){
            if(score <= roleScoreMatch[i]){
                break;
            }
        }
        return i+1;
    }


/*
 * @dev Returns the length of a null-terminated bytes32 string.
 * @param self The value to find the length of.
 * @return The length of the string, from 0 to 32.
 */

function len(bytes32 self) internal pure returns (uint) {
        uint ret;
        if (self == 0)
            return 0;
        if (uint256(self) & 0xffffffffffffffffffffffffffffffff == 0) {
            ret += 16;
            self = bytes32(uint(self) / 0x100000000000000000000000000000000);
        }
        if (uint256(self) & 0xffffffffffffffff == 0) {
            ret += 8;
            self = bytes32(uint(self) / 0x10000000000000000);
        }
        if (uint256(self) & 0xffffffff == 0) {
            ret += 4;
            self = bytes32(uint(self) / 0x100000000);
        }
        if (uint256(self) & 0xffff == 0) {
            ret += 2;
            self = bytes32(uint(self) / 0x10000);
        }
        if (uint256(self) & 0xff == 0) {
            ret += 1;
        }
        return 32 - ret;
    }


/*
 * @dev Returns a slice containing the entire bytes32, interpreted as a
 *      null-terminated utf-8 string.
 * @param self The bytes32 value to convert to a slice.
 * @return A new slice containing the value of the input argument up to the
 *         first null.
 */

function toSliceB32(bytes32 self) internal pure returns (slice memory ret) {
        // Allocate space for `self` in memory, copy it there, and point ret at it
        assembly {
            let ptr := mload(0x40)
            mstore(0x40, add(ptr, 0x20))
            mstore(ptr, self)
            mstore(add(ret, 0x20), ptr)
        }
        ret._len = len(self);
    }


/*
 * @dev Copies a slice to a new string.
 * @param self The slice to copy.
 * @return A newly allocated string containing the slice's text.
 */

function toString(slice memory self) internal pure returns (string memory) {
        string memory ret = new string(self._len);
        uint retptr;
        assembly { retptr := add(ret, 32) }

        memcpy(retptr, self._ptr, self._len);
        return ret;
    }


/*
 * @dev Returns the length in runes of the slice. Note that this operation
 *      takes time proportional to the length of the slice; avoid using it
 *      in loops, and call `slice.empty()` if you only need to know whether
 *      the slice is empty or not.
 * @param self The slice to operate on.
 * @return The length of the slice in runes.
 */

function len(slice memory self) internal pure returns (uint l) {
        // Starting at ptr-31 means the LSB will be the byte we care about
        uint ptr = self._ptr - 31;
        uint end = ptr + self._len;
        for (l = 0; ptr < end; l++) {
            uint8 b;
            assembly { b := and(mload(ptr), 0xFF) }
            if (b < 0x80) {
                ptr += 1;
            } else if(b < 0xE0) {
                ptr += 2;
            } else if(b < 0xF0) {
                ptr += 3;
            } else if(b < 0xF8) {
                ptr += 4;
            } else if(b < 0xFC) {
                ptr += 5;
            } else {
                ptr += 6;
            }
        }
    }


/*
 * @dev Returns a positive number if `other` comes lexicographically after
 *      `self`, a negative number if it comes before, or zero if the
 *      contents of the two slices are equal. Comparison is done per-rune,
 *      on unicode codepoints.
 * @param self The first slice to compare.
 * @param other The second slice to compare.
 * @return The result of the comparison.
 */

function compare(slice memory self, slice memory other) internal pure returns (int) {
        uint shortest = self._len;
        if (other._len < self._len)
            shortest = other._len;

        uint selfptr = self._ptr;
        uint otherptr = other._ptr;
        for (uint idx = 0; idx < shortest; idx += 32) {
            uint a;
            uint b;
            assembly {
                a := mload(selfptr)
                b := mload(otherptr)
            }
            if (a != b) {
                // Mask out irrelevant bytes and check again
                uint256 mask = uint256(-1); // 0xffff...
                if(shortest < 32) {
                  mask = ~(2 ** (8 * (32 - shortest + idx)) - 1);
                }
                uint256 diff = (a & mask) - (b & mask);
                if (diff != 0)
                    return int(diff);
            }
            selfptr += 32;
            otherptr += 32;
        }
        return int(self._len) - int(other._len);
    }


/*
 * @dev Extracts the first rune in the slice into `rune`, advancing the
 *      slice to point to the next rune and returning `self`.
 * @param self The slice to operate on.
 * @param rune The slice that will contain the first rune.
 * @return `rune`.
 */

function nextRune(slice memory self, slice memory rune) internal pure returns (slice memory) {
        rune._ptr = self._ptr;

        if (self._len == 0) {
            rune._len = 0;
            return rune;
        }

        uint l;
        uint b;
        // Load the first byte of the rune into the LSBs of b
        assembly { b := and(mload(sub(mload(add(self, 32)), 31)), 0xFF) }
        if (b < 0x80) {
            l = 1;
        } else if(b < 0xE0) {
            l = 2;
        } else if(b < 0xF0) {
            l = 3;
        } else {
            l = 4;
        }

        // Check for truncated codepoints
        if (l > self._len) {
            rune._len = self._len;
            self._ptr += self._len;
            self._len = 0;
            return rune;
        }

        self._ptr += l;
        self._len -= l;
        rune._len = l;
        return rune;
    }


/*
 * @dev Returns true if `self` starts with `needle`.
 * @param self The slice to operate on.
 * @param needle The slice to search for.
 * @return True if the slice starts with the provided text, false otherwise.
 */

function startsWith(slice memory self, slice memory needle) internal pure returns (bool) {
        if (self._len < needle._len) {
            return false;
        }

        if (self._ptr == needle._ptr) {
            return true;
        }

        bool equal;
        assembly {
            let length := mload(needle)
            let selfptr := mload(add(self, 0x20))
            let needleptr := mload(add(needle, 0x20))
            equal := eq(keccak256(selfptr, length), keccak256(needleptr, length))
        }
        return equal;
    }


/*
 * @dev If `self` starts with `needle`, `needle` is removed from the
 *      beginning of `self`. Otherwise, `self` is unmodified.
 * @param self The slice to operate on.
 * @param needle The slice to search for.
 * @return `self`
 */

function beyond(slice memory self, slice memory needle) internal pure returns (slice memory) {
        if (self._len < needle._len) {
            return self;
        }

        bool equal = true;
        if (self._ptr != needle._ptr) {
            assembly {
                let length := mload(needle)
                let selfptr := mload(add(self, 0x20))
                let needleptr := mload(add(needle, 0x20))
                equal := eq(keccak256(selfptr, length), keccak256(needleptr, length))
            }
        }

        if (equal) {
            self._len -= needle._len;
            self._ptr += needle._len;
        }

        return self;
    }


// Returns the memory address of the first byte of the first occurrence of
// `needle` in `self`, or the first byte after `self` if not found.

function findPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private pure returns (uint) {
        uint ptr = selfptr;
        uint idx;

        if (needlelen <= selflen) {
            if (needlelen <= 32) {
                bytes32 mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1));

                bytes32 needledata;
                assembly { needledata := and(mload(needleptr), mask) }

                uint end = selfptr + selflen - needlelen;
                bytes32 ptrdata;
                assembly { ptrdata := and(mload(ptr), mask) }

                while (ptrdata != needledata) {
                    if (ptr >= end)
                        return selfptr + selflen;
                    ptr++;
                    assembly { ptrdata := and(mload(ptr), mask) }
                }
                return ptr;
            } else {
                // For long needles, use hashing
                bytes32 hash;
                assembly { hash := keccak256(needleptr, needlelen) }

                for (idx = 0; idx <= selflen - needlelen; idx++) {
                    bytes32 testHash;
                    assembly { testHash := keccak256(ptr, needlelen) }
                    if (hash == testHash)
                        return ptr;
                    ptr += 1;
                }
            }
        }
        return selfptr + selflen;
    }


/*
 * @dev Modifies `self` to contain everything from the first occurrence of
 *      `needle` to the end of the slice. `self` is set to the empty slice
 *      if `needle` is not found.
 * @param self The slice to search and modify.
 * @param needle The text to search for.
 * @return `self`.
 */

function find(slice memory self, slice memory needle) internal pure returns (slice memory) {
        uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr);
        self._len -= ptr - self._ptr;
        self._ptr = ptr;
        return self;
    }


/*
 * @dev Splits the slice, setting `self` to everything after the first
 *      occurrence of `needle`, and `token` to everything before it. If
 *      `needle` does not occur in `self`, `self` is set to the empty slice,
 *      and `token` is set to the entirety of `self`.
 * @param self The slice to split.
 * @param needle The text to search for in `self`.
 * @param token An output parameter to which the first token is written.
 * @return `token`.
 */

function split(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) {
        uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr);
        token._ptr = self._ptr;
        token._len = ptr - self._ptr;
        if (ptr == self._ptr + self._len) {
            // Not found
            self._len = 0;
        } else {
            self._len -= token._len + needle._len;
            self._ptr = ptr + needle._len;
        }
        return token;
    }


/*
 * @dev Counts the number of nonoverlapping occurrences of `needle` in `self`.
 * @param self The slice to search.
 * @param needle The text to search for in `self`.
 * @return The number of occurrences of `needle` found in `self`.
 */

function count(slice memory self, slice memory needle) internal pure returns (uint cnt) {
        uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr) + needle._len;
        while (ptr <= self._ptr + self._len) {
            cnt++;
            ptr = findPtr(self._len - (ptr - self._ptr), ptr, needle._len, needle._ptr) + needle._len;
        }
    }


/*
 * @dev Joins an array of slices, using `self` as a delimiter, returning a
 *      newly allocated string.
 * @param self The delimiter to use.
 * @param parts A list of slices to join.
 * @return A newly allocated string containing all the slices in `parts`,
 *         joined with `self`.
 */

function join(slice memory self, slice[] memory parts) internal pure returns (string memory) {
        if (parts.length == 0)
            return "";

        uint length = self._len * (parts.length - 1);
        for(uint i = 0; i < parts.length; i++)
            length += parts[i]._len;

        string memory ret = new string(length);
        uint retptr;
        assembly { retptr := add(ret, 32) }

        for(uint i = 0; i < parts.length; i++) {
            memcpy(retptr, parts[i]._ptr, parts[i]._len);
            retptr += parts[i]._len;
            if (i < parts.length - 1) {
                memcpy(retptr, self._ptr, self._len);
                retptr += self._len;
            }
        }

        return ret;
    }


// Works like address.send but with a customizable gas limit
// Make sure your code is safe for reentrancy when using this function!

function sendETH(
        address to,
        uint    amount,
        uint    gasLimit
        )
        internal
        returns (bool success)
    {
        if (amount == 0) {
            return true;
        }
        address payable recipient = to.toPayable();
        /* solium-disable-next-line */
        (success, ) = recipient.call{value: amount, gas: gasLimit}("");
    }


/**
 * @dev Lets the manager assign an ENS subdomain of the root node to a target address.
 * Registers both the forward and reverse ENS.
 * @param _owner The owner of the subdomain.
 * @param _label The subdomain label.
 * @param _approval The signature of _owner and _label by a manager.
 */

function register(
        address _owner,
        string  calldata _label,
        bytes   calldata _approval
        )
        external
        override
        onlyManager
    {
        verifyApproval(_owner, _label, _approval);

        bytes32 labelNode = keccak256(abi.encodePacked(_label));
        bytes32 node = keccak256(abi.encodePacked(rootNode, labelNode));
        address currentOwner = getENSRegistry().owner(node);
        require(currentOwner == address(0), "AEM: _label is alrealdy owned");

        // Forward ENS
        getENSRegistry().setSubnodeOwner(rootNode, labelNode, address(this));
        getENSRegistry().setResolver(node, ensResolver);
        getENSRegistry().setOwner(node, _owner);
        ENSResolver(ensResolver).setAddr(node, _owner);

        // Reverse ENS
        strings.slice[] memory parts = new strings.slice[](2);
        parts[0] = _label.toSlice();
        parts[1] = rootName.toSlice();
        string memory name = ".".toSlice().join(parts);
        bytes32 reverseNode = getENSReverseRegistrar().node(_owner);
        ENSResolver(ensResolver).setName(reverseNode, name);

        emit Registered(_owner, name);
    }


// Special case for transactCall to support transfers on "bad" ERC20 tokens

function transactTokenTransfer(
        address wallet,
        address token,
        address to,
        uint    amount
        )
        internal
        returns (bool success)
    {
        bytes memory txData = abi.encodeWithSelector(
            ERC20(0).transfer.selector,
            to,
            amount
        );
        bytes memory returnData = transactCall(wallet, token, 0, txData);
        // `transactCall` will revert if the call was unsuccessful.
        // The only extra check we have to do is verify if the return value (if there is any) is correct.
        if (returnData.length > 0) {
            success = abi.decode(returnData, (bool));
        } else {
            // If there is no return value then a failure would have resulted in a revert
            success = true;
        }
    }


/// @dev Collects tokens and ether owned by this module to a controlled address.
/// @param tokens The list of tokens and ether to collect.

function collectTokens(address[] calldata tokens)
        external
        nonReentrant
    {
        address to = controller.collectTo();

        for (uint i = 0; i < tokens.length; i++) {
            address token = tokens[i];
            if (token == address(0)) {
                uint amount = address(this).balance;
                to.sendETHAndVerify(amount, gasleft());
            } else {
                uint amount = ERC20(token).balanceOf(address(this));
                if (amount > 0) {
                    // Do not check the return value to support "bad" ERC20 tokens
                    ERC20(token).transfer(to, amount);
                }
            }
        }
    }


/// @dev Returns a read-only array with the addresses stored in the call data
///      at the specified function parameter index.
///      Example: function bar(address[] signers, uint value);
///               To extact `signers` use parameterIdx := 0
///      Example: function foo(address wallet, address[] signers, address[] contracts);
///               To extact `signers` use parameterIdx := 1
///               To extact `contracts` use parameterIdx := 2

function extractAddressesFromCallData(
        bytes memory data,
        uint  parameterIdx
        )
        internal
        pure
        returns (address[] memory addresses)
    {
        // Find the offset of the function parameter in the call data
        uint dataOffset = data.toUint(4 + 32 * parameterIdx);
        // Make sure enough bytes are in data to store the complete array
        uint length = data.toUint(4 + dataOffset);
        require(data.length >= 4 + dataOffset + 32 * (1 + length), "INVALID_DATA");
        // Extract the signers by copying the pointer at the beginning of the array
        // An extra offset of 36 is applied: 32(length) + 4(sig)
        assembly { addresses := add(data, add(36, dataOffset)) }
    }


/// @dev Save the meta-transaction to history.
///      This method must throw if the transaction is not unique or the nonce is invalid.
/// @param wallet The target wallet.
/// @param nonce The nonce
/// @param metaTxHash The signed hash of the transaction

function saveExecutedMetaTx(
        address wallet,
        uint    nonce,
        bytes32 metaTxHash
        )
        private
    {
        if (nonce == 0) {
            require(!wallets[wallet].metaTxHash[metaTxHash], "INVALID_HASH");
            wallets[wallet].metaTxHash[metaTxHash] = true;
        } else {
            require(nonce > wallets[wallet].nonce, "NONCE_TOO_SMALL");
            require((nonce >> 128) <= (block.number), "NONCE_TOO_LARGE");
            wallets[wallet].nonce = nonce;
        }
    }


// @dev Adds to the list of open auctions and fires the
//  AuctionCreated event.
// @param _cutieId The token ID is to be put on auction.
// @param _auction To add an auction.
// @param _fee Amount of money to feature auction

function _addAuction(uint40 _cutieId, Auction _auction) internal
    {
        // Require that all auctions have a duration of
        // at least one minute. (Keeps our math from getting hairy!)
        require(_auction.duration >= 1 minutes);

        cutieIdToAuction[_cutieId] = _auction;

        emit AuctionCreated(
            _cutieId,
            _auction.startPrice,
            _auction.endPrice,
            _auction.duration,
            _auction.featuringFee
        );
    }


// @dev Calculates the price and transfers winnings.
// Does not transfer token ownership.

function _bid(uint40 _cutieId, uint128 _bidAmount)
        internal
        returns (uint128)
    {
        // Get a reference to the auction struct
        Auction storage auction = cutieIdToAuction[_cutieId];

        require(_isOnAuction(auction));

        // Check that bid > current price
        uint128 price = _currentPrice(auction);
        require(_bidAmount >= price);

        // Provide a reference to the seller before the auction struct is deleted.
        address seller = auction.seller;

        _removeAuction(_cutieId);

        // Transfer proceeds to seller (if there are any!)
        if (price > 0) {
            uint128 fee = _computeFee(price);
            uint128 sellerValue = price - fee;

            seller.transfer(sellerValue);
        }

        emit AuctionSuccessful(_cutieId, price, msg.sender);

        return price;
    }


// @dev calculate current price of auction.
//  When testing, make this function public and turn on
//  `Current price calculation` test suite.

function _computeCurrentPrice(
        uint128 _startPrice,
        uint128 _endPrice,
        uint40 _duration,
        uint40 _secondsPassed
    )
        internal
        pure
        returns (uint128)
    {
        if (_secondsPassed >= _duration) {
            return _endPrice;
        } else {
            int256 totalPriceChange = int256(_endPrice) - int256(_startPrice);
            int256 currentPriceChange = totalPriceChange * int256(_secondsPassed) / int256(_duration);
            uint128 currentPrice = _startPrice + uint128(currentPriceChange);

            return currentPrice;
        }
    }


// @dev return current price of token.

function _currentPrice(Auction storage _auction)
        internal
        view
        returns (uint128)
    {
        uint40 secondsPassed = 0;

        uint40 timeNow = uint40(now);
        if (timeNow > _auction.startedAt) {
            secondsPassed = timeNow - _auction.startedAt;
        }

        return _computeCurrentPrice(
            _auction.startPrice,
            _auction.endPrice,
            _auction.duration,
            secondsPassed
        );
    }