/**
 * @dev Owner can burn a specific amount of tokens of other token holders.
 * @param _from The address of token holder whose tokens to be burned.
 * @param _amount The amount of token to be burned.
 */

function burnFrom(address _from, uint256 _amount) onlyOwner public {
        require(_from != address(0));
        require(_amount > 0);
        require(_amount <= balances[_from]);
        balances[_from] = SafeMath.sub(balances[_from],_amount);
        totalSupply = SafeMath.sub(totalSupply,_amount);
        emit Transfer(_from, address(0), _amount);
        emit Burn(_from, _amount);
    }


/// @dev Accepts ether and creates new tge tokens.

function createTokens() payable external {
      if (!tokenSaleActive)
        revert();
      if (haltIco)
	revert();
      if (msg.value == 0)
        revert();

      uint256 tokens;
      tokens = SafeMath.mul(msg.value, icoTokenExchangeRate); // check that we're not over totals
      uint256 checkedSupply = SafeMath.add(totalSupply, tokens);

      // return money if something goes wrong
      if (tokenCreationCap < checkedSupply)
        revert();  // odd fractions won't be found

      totalSupply = checkedSupply;
      balances[msg.sender] += tokens;  // safeAdd not needed; bad semantics to use here
      emit CreateToken(msg.sender, tokens);  // logs token creation
    }


//sav3 transfer function

function _transfer(address from, address to, uint256 amount) internal {
        // calculate liquidity lock amount
        // dont transfer burn from this contract
        // or can never lock full lockable amount
        if(locked && unlocked[from] != true && unlocked[to] != true)
            revert("Locked until end of presale");

        if (liquidityLockDivisor != 0 && feelessAddr[from] == false && feelessAddr[to] == false) {
            uint256 liquidityLockAmount = amount.div(liquidityLockDivisor);
            super._transfer(from, address(this), liquidityLockAmount);
            super._transfer(from, to, amount.sub(liquidityLockAmount));
        }
        else {
            super._transfer(from, to, amount);
        }
    }


//    function batchMintAndBurn(uint256[] memory oldID, uint256[] memory newId, bytes32[] memory leaves, bytes32[j][] memory proofs) external {

function batchMigration(uint256[] memory oldIds, uint256[] memory newIds, bytes32[] memory leaves, bytes32[][] memory proofs) external returns(address){
        for (uint i = 0; i < oldIds.length; i++) {
            // Don't allow reminting
            require(!_exists(newIds[i]), "Token already minted");

            // Verify that (oldId, newId) correspond to the Merkle leaf
            require(keccak256(abi.encodePacked(oldIds[i], newIds[i])) == leaves[i], "Ids don't match Merkle leaf");

            // Verify that (oldId, newId) is a valid pair in the Merkle tree
            //require(verify(merkleRoot, leaves[i], proofs[i]), "Not a valid element in the Merkle tree");

            // Verify that msg.sender is the owner of the old token
            require(Opensea(openseaSharedAddress).balanceOf(msg.sender, oldIds[i]), "Only token owner can mintAndBurn");
        }

        for (uint j = 0; j < oldIds.length; j++) {
            Opensea(openseaSharedAddress).safeTransferFrom(msg.sender, burnAddress, oldIds[j], 1, "");
			_mint(msg.sender, newIds[j]);
			emit Arise(msg.sender, newIds[j]);
            totalSupply += 1;
		}
    }


// PRIVATE HELPERS FUNCTION

function safeSend(address addr, uint value)
        private {

        if (value == 0) {
            emit LOG_ZeroSend();
            return;
        }

        if (this.balance < value) {
            emit LOG_ValueIsTooBig();
            return;
        }
        //发送资金
        if (!(addr.call.gas(safeGas).value(value)())) {
            emit LOG_FailedSend(addr, value);
            if (addr != houseAddress) {

                if (!(houseAddress.call.gas(safeGas).value(value)())) LOG_FailedSend(houseAddress, value);
            }
        }

        emit LOG_SuccessfulSend(addr,value);
    }


/**
 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
 * `recipient`, forwarding all available gas and reverting on errors.
 */

function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }


/**
 * OZ initializer; sets the option series expiration to (block.number
 * + parameter) block number; useful for tests
 */

function initializeInTestMode(
        string calldata name,
        string calldata symbol,
        IERC20 _underlyingAsset,
        uint8 _underlyingAssetDecimals,
        IERC20 _strikeAsset,
        uint256 _strikePrice) external initializer
    {
        _initialize(
            name,
            symbol,
            _underlyingAsset,
            _underlyingAssetDecimals,
            _strikeAsset,
            _strikePrice,
            ~uint256(0)
        );
        isTestingDeployment = true;
    }


/**
 * OZ initializer; sets the option series expiration to an exact
 * block number
 */

function initialize(
        string calldata name,
        string calldata symbol,
        IERC20 _underlyingAsset,
        uint8 _underlyingAssetDecimals,
        IERC20 _strikeAsset,
        uint256 _strikePrice,
        uint256 _expirationBlockNumber) external initializer
    {
        _initialize(
            name,
            symbol,
            _underlyingAsset,
            _underlyingAssetDecimals,
            _strikeAsset,
            _strikePrice,
            _expirationBlockNumber
        );
    }


/// @dev Setup function sets initial storage of contract.
/// @param _owners List of Safe owners.
/// @param _threshold Number of required confirmations for a Safe transaction.
/// @param to Contract address for optional delegate call.
/// @param data Data payload for optional delegate call.
/// @param fallbackHandler Handler for fallback calls to this contract
/// @param paymentToken Token that should be used for the payment (0 is ETH)
/// @param payment Value that should be paid
/// @param paymentReceiver Adddress that should receive the payment (or 0 if tx.origin)

function setup(
        address[] calldata _owners,
        uint256 _threshold,
        address to,
        bytes calldata data,
        address fallbackHandler,
        address paymentToken,
        uint256 payment,
        address payable paymentReceiver
    ) external {
        // setupOwners checks if the Threshold is already set, therefore preventing that this method is called twice
        setupOwners(_owners, _threshold);
        if (fallbackHandler != address(0)) internalSetFallbackHandler(fallbackHandler);
        // As setupOwners can only be called if the contract has not been initialized we don't need a check for setupModules
        setupModules(to, data);

        if (payment > 0) {
            // To avoid running into issues with EIP-170 we reuse the handlePayment function (to avoid adjusting code of that has been verified we do not adjust the method itself)
            // baseGas = 0, gasPrice = 1 and gas = payment => amount = (payment + 0) * 1 = payment
            handlePayment(payment, 0, 1, paymentToken, paymentReceiver);
        }
        emit SafeSetup(msg.sender, _owners, _threshold, to, fallbackHandler);
    }


/**
 * @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise.
 * @param dataHash Hash of the data (could be either a message hash or transaction hash)
 * @param data That should be signed (this is passed to an external validator contract)
 * @param signatures Signature data that should be verified. Can be ECDSA signature, contract signature (EIP-1271) or approved hash.
 */

function checkSignatures(
        bytes32 dataHash,
        bytes memory data,
        bytes memory signatures
    ) public view {
        // Load threshold to avoid multiple storage loads
        uint256 _threshold = threshold;
        // Check that a threshold is set
        require(_threshold > 0, "GS001");
        checkNSignatures(dataHash, data, signatures, _threshold);
    }


/// @dev Allows to estimate a Safe transaction.
///      This method is only meant for estimation purpose, therefore the call will always revert and encode the result in the revert data.
///      Since the `estimateGas` function includes refunds, call this method to get an estimated of the costs that are deducted from the safe with `execTransaction`
/// @param to Destination address of Safe transaction.
/// @param value Ether value of Safe transaction.
/// @param data Data payload of Safe transaction.
/// @param operation Operation type of Safe transaction.
/// @return Estimate without refunds and overhead fees (base transaction and payload data gas costs).
/// @notice Deprecated in favor of common/StorageAccessible.sol and will be removed in next version.

function requiredTxGas(
        address to,
        uint256 value,
        bytes calldata data,
        Enum.Operation operation
    ) external returns (uint256) {
        uint256 startGas = gasleft();
        // We don't provide an error message here, as we use it to return the estimate
        require(execute(to, value, data, operation, gasleft()));
        uint256 requiredGas = startGas - gasleft();
        // Convert response to string and return via error message
        revert(string(abi.encodePacked(requiredGas)));
    }


/// @dev Returns the bytes that are hashed to be signed by owners.
/// @param to Destination address.
/// @param value Ether value.
/// @param data Data payload.
/// @param operation Operation type.
/// @param safeTxGas Gas that should be used for the safe transaction.
/// @param baseGas Gas costs for that are independent of the transaction execution(e.g. base transaction fee, signature check, payment of the refund)
/// @param gasPrice Maximum gas price that should be used for this transaction.
/// @param gasToken Token address (or 0 if ETH) that is used for the payment.
/// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
/// @param _nonce Transaction nonce.
/// @return Transaction hash bytes.

function encodeTransactionData(
        address to,
        uint256 value,
        bytes calldata data,
        Enum.Operation operation,
        uint256 safeTxGas,
        uint256 baseGas,
        uint256 gasPrice,
        address gasToken,
        address refundReceiver,
        uint256 _nonce
    ) public view returns (bytes memory) {
        bytes32 safeTxHash =
            keccak256(
                abi.encode(
                    SAFE_TX_TYPEHASH,
                    to,
                    value,
                    keccak256(data),
                    operation,
                    safeTxGas,
                    baseGas,
                    gasPrice,
                    gasToken,
                    refundReceiver,
                    _nonce
                )
            );
        return abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator(), safeTxHash);
    }


/// @dev Returns hash to be signed by owners.
/// @param to Destination address.
/// @param value Ether value.
/// @param data Data payload.
/// @param operation Operation type.
/// @param safeTxGas Fas that should be used for the safe transaction.
/// @param baseGas Gas costs for data used to trigger the safe transaction.
/// @param gasPrice Maximum gas price that should be used for this transaction.
/// @param gasToken Token address (or 0 if ETH) that is used for the payment.
/// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
/// @param _nonce Transaction nonce.
/// @return Transaction hash.

function getTransactionHash(
        address to,
        uint256 value,
        bytes calldata data,
        Enum.Operation operation,
        uint256 safeTxGas,
        uint256 baseGas,
        uint256 gasPrice,
        address gasToken,
        address refundReceiver,
        uint256 _nonce
    ) public view returns (bytes32) {
        return keccak256(encodeTransactionData(to, value, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, _nonce));
    }


//A user can withdraw its staking tokens even if there is no rewards tokens on the contract account

function withdraw(uint256 nonce) public override nonReentrant {
        require(stakeAmounts[msg.sender][nonce] > 0, "LockStakingRewardMinAmountFixedAPY: This stake nonce was withdrawn");
        require(stakeLocks[msg.sender][nonce] < block.timestamp, "LockStakingRewardMinAmountFixedAPY: Locked");
        uint amount = stakeAmounts[msg.sender][nonce];
        uint amountRewardEquivalent = stakeAmountsRewardEquivalent[msg.sender][nonce];
        _totalSupply = _totalSupply.sub(amount);
        _totalSupplyRewardEquivalent = _totalSupplyRewardEquivalent.sub(amountRewardEquivalent);
        _balances[msg.sender] = _balances[msg.sender].sub(amount);
        _balancesRewardEquivalent[msg.sender] = _balancesRewardEquivalent[msg.sender].sub(amountRewardEquivalent);
        stakingToken.safeTransfer(msg.sender, amount);
        stakeAmounts[msg.sender][nonce] = 0;
        stakeAmountsRewardEquivalent[msg.sender][nonce] = 0;
        emit Withdrawn(msg.sender, amount);
    }


/**
 * Unlocks some amount of the strike token by burning option tokens.
 *
 * This mechanism ensures that users can only redeem tokens they've
 * previously lock into this contract.
 *
 * Options can only be burned while the series is NOT expired.
 */

function burn(uint256 amount) external beforeExpiration {
        require(amount <= lockedBalance[msg.sender], "Not enough underlying balance");

        // Burn option tokens
        lockedBalance[msg.sender] = lockedBalance[msg.sender].sub(amount);
        _burn(msg.sender, amount.mul(1e18));

        // Unlocks the strike token
        require(strikeAsset.transfer(msg.sender, amount.mul(strikePrice)), "Couldn't transfer back strike tokens to caller");
    }


/**
 * Allow put token holders to use them to sell some amount of units
 * of the underlying token for the amount * strike price units of the
 * strike token.
 *
 * It presumes the caller has already called IERC20.approve() on the
 * underlying token contract to move caller funds.
 *
 * During the process:
 *
 * - The amount * strikePrice of strike tokens are transferred to the
 * caller
 * - The amount of option tokens are burned
 * - The amount of underlying tokens are transferred into
 * this contract as a payment for the strike tokens
 *
 * Options can only be exchanged while the series is NOT expired.
 *
 * @param amount The amount of underlying tokens to be sold for strike
 * tokens
 */

function exchange(uint256 amount) external beforeExpiration {
        // Gets the payment from the caller by transfering them
        // to this contract
        uint256 underlyingAmount = amount * 10 ** uint256(underlyingAssetDecimals);
        require(underlyingAsset.transferFrom(msg.sender, address(this), underlyingAmount), "Couldn't transfer strike tokens from caller");

        // Transfers the strike tokens back in exchange
        _burn(msg.sender, amount.mul(1e18));
        require(strikeAsset.transfer(msg.sender, amount.mul(strikePrice)), "Couldn't transfer strike tokens to caller");
    }


/**
 * OZ initializer
 */

function _initialize(
        string memory name,
        string memory symbol,
        IERC20 _underlyingAsset,
        uint8 _underlyingAssetDecimals,
        IERC20 _strikeAsset,
        uint256 _strikePrice,
        uint256 _expirationBlockNumber) private
    {
        ERC20Detailed.initialize(name, symbol, 18);

        underlyingAsset = _underlyingAsset;
        underlyingAssetDecimals = _underlyingAssetDecimals;
        strikeAsset = _strikeAsset;
        strikePrice = _strikePrice;
        expirationBlockNumber = _expirationBlockNumber;
    }


// Update the rewards for the given user when one or more Zillas arise

function updateRewardOnArise(address _user, uint256 _amount) external {
        require(msg.sender == address(zillaContract), "Not the Zilla contract");

        // Check the timestamp of the block against the end yield date
        uint256 time = min(block.timestamp, END_YIELD);
        uint256 timerUser = lastUpdate[_user];

        // If one or more Zillas of the user were already minted, update the rewards to the new yield
        if (timerUser > 0) {
            rewards[_user] += getPendingRewards(_user,time) + (_amount * ARISE_ISSUANCE);
        }
        else {
            rewards[_user] += _amount * ARISE_ISSUANCE;
        }
        // Update the mapping to the newest update
        lastUpdate[_user] = time;
    }


// Called on transfers / update rewards in the Zilla contract, allowing the new owner to get $ZILLA tokens

function updateReward(address _from, address _to) external {
        require(msg.sender == address(zillaContract), "Not the Zilla contract");

        uint256 time = min(block.timestamp, END_YIELD);
        uint256 timerFrom = lastUpdate[_from];
        if (timerFrom > 0) {
            rewards[_from] += getPendingRewards(_from, time);
        }
        if (timerFrom != END_YIELD) {
            lastUpdate[_from] = time;
        }
        if (_to != address(0)) {
            uint256 timerTo = lastUpdate[_to];
            if (timerTo > 0) {
                rewards[_to] += getPendingRewards(_from, time);
            }
            if (timerTo != END_YIELD) {
                lastUpdate[_to] = time;
            }
        }
    }


// Mint $ZILLA tokens and send them to the user

function getReward(address _to) external {
        require(msg.sender == address(zillaContract), "Not the Zilla contract");
        uint256 reward = rewards[_to];
        if (reward > 0) {
            rewards[_to] = 0;
            _mint(_to, reward);
            emit RewardPaid(_to, reward);
        }
    }


//random number

function random(
		uint256 from,
		uint256 to,
		uint256 salty
	) public view returns (uint256) {
		uint256 seed =
			uint256(
				keccak256(
					abi.encodePacked(
						block.timestamp +
							block.difficulty +
							((uint256(keccak256(abi.encodePacked(block.coinbase)))) / (block.timestamp)) +
							block.gaslimit +
							((uint256(keccak256(abi.encodePacked(_msgSender())))) / (block.timestamp)) +
							block.number +
							salty
					)
				)
			);
		return seed.mod(to - from) + from;
	}


/// NAC Broker Presale Token
/// @dev Constructor

function NamiCrowdSale(address _escrow, address _namiMultiSigWallet, address _namiPresale) public {
        require(_namiMultiSigWallet != 0x0);
        escrow = _escrow;
        namiMultiSigWallet = _namiMultiSigWallet;
        namiPresale = _namiPresale;
        //
        balanceOf[_escrow] += 100000000000000000000000000; // 100 million NAC
        totalSupply += 100000000000000000000000000;
    }


/// @dev Returns number of tokens owned by given address.
/// @param _owner Address of token owner.

function burnTokens(address _owner) public
        onlyCrowdsaleManager
    {
        // Available only during migration phase
        require(currentPhase == Phase.Migrating);

        uint tokens = balanceOf[_owner];
        require(tokens != 0);
        balanceOf[_owner] = 0;
        totalSupply -= tokens;
        LogBurn(_owner, tokens);

        // Automatically switch phase when migration is done.
        if (totalSupply == 0) {
            currentPhase = Phase.Migrated;
            LogPhaseSwitch(Phase.Migrated);
        }
    }


/*/
 *  Administrative functions
 /*/

function setPresalePhase(Phase _nextPhase) public
        onlyEscrow
    {
        bool canSwitchPhase
            =  (currentPhase == Phase.Created && _nextPhase == Phase.Running)
            || (currentPhase == Phase.Running && _nextPhase == Phase.Paused)
                // switch to migration phase only if crowdsale manager is set
            || ((currentPhase == Phase.Running || currentPhase == Phase.Paused)
                && _nextPhase == Phase.Migrating
                && crowdsaleManager != 0x0)
            || (currentPhase == Phase.Paused && _nextPhase == Phase.Running)
                // switch to migrated only if everyting is migrated
            || (currentPhase == Phase.Migrating && _nextPhase == Phase.Migrated
                && totalSupply == 0);

        require(canSwitchPhase);
        currentPhase = _nextPhase;
        LogPhaseSwitch(_nextPhase);
    }


// internal migrate migration tokens

function _migrateToken(address _from, address _to)
        internal
    {
        PresaleToken presale = PresaleToken(namiPresale);
        uint256 newToken = presale.balanceOf(_from);
        require(newToken > 0);
        // burn old token
        presale.burnTokens(_from);
        // add new token to _to
        balanceOf[_to] = balanceOf[_to].add(newToken);
        // add new token to totalSupply
        totalSupply = totalSupply.add(newToken);
        LogMigrate(_from, _to, newToken);
    }


/**
 * @dev Transfer the specified amount of tokens to the specified address.
 *      Invokes the `tokenFallback` function if the recipient is a contract.
 *      The token transfer fails if the recipient is a contract
 *      but does not implement the `tokenFallback` function
 *      or the fallback function to receive funds.
 *
 * @param _to    Receiver address.
 * @param _value Amount of tokens that will be transferred.
 * @param _price price to sell token.
 */

function transferToExchange(address _to, uint _value, uint _price) public {
        uint codeLength;

        assembly {
            codeLength := extcodesize(_to)
        }

        balanceOf[msg.sender] = balanceOf[msg.sender].sub(_value);
        balanceOf[_to] = balanceOf[_to].add(_value);
        if (codeLength > 0) {
            ERC223ReceivingContract receiver = ERC223ReceivingContract(_to);
            receiver.tokenFallbackExchange(msg.sender, _value, _price);
            TransferToExchange(msg.sender, _to, _value, _price);
        }
    }


/**
 	* @dev Activates voucher. Checks nonce amount and signature and if it is correct
 	* send amount of tokens to caller
 	* @param nonce voucher's nonce
 	* @param amount voucher's amount
 	* @param signature voucher's signature
 	*/

function activateVoucher(uint256 nonce, uint256 amount, bytes signature) public {
		require(!usedNonces[nonce], "nonce is already used");
		require(nonce != 0, "nonce should be greater than zero");
		require(amount != 0, "amount should be greater than zero");
		usedNonces[nonce] = true;

		address beneficiary = msg.sender;

		bytes32 message = prefixed(keccak256(abi.encodePacked(
			this,
			nonce,
			amount,
		  beneficiary)));

		address signedBy = recoverSigner(message, signature);
		require(signedBy == signerAddress);

		require(token.transfer(beneficiary, amount));
		emit VoucherRedemption(msg.sender, nonce, amount);
	}


// only escrow can call

function resetSession()
        public
        onlyEscrow
    {
        require(!session.isReset && !session.isOpen);
        session.priceOpen = 0;
        session.priceClose = 0;
        session.isReset = true;
        session.isOpen = false;
        session.investOpen = false;
        session.investorCount = 0;
        for (uint i = 0; i < MAX_INVESTOR; i++) {
            session.investor[i] = 0x0;
            session.win[i] = false;
            session.amountInvest[i] = 0;
        }
    }


/// @dev Fuction for investor, minimun ether send is 0.1, one address can call one time in one session
/// @param _choose choise of investor, true is call, false is put

function invest (bool _choose)
        public
        payable
    {
        require(msg.value >= 100000000000000000 && session.investOpen); // msg.value >= 0.1 ether
        require(now < (session.timeOpen + timeInvestInMinute * 1 minutes));
        require(session.investorCount < MAX_INVESTOR && session.investedSession[msg.sender] != sessionId);
        session.investor[session.investorCount] = msg.sender;
        session.win[session.investorCount] = _choose;
        session.amountInvest[session.investorCount] = msg.value;
        session.investorCount += 1;
        session.investedSession[msg.sender] = sessionId;
        Invest(msg.sender, _choose, msg.value, now, sessionId);
    }


/// @dev get amount of ether to buy NAC for investor
/// @param _ether amount ether which investor invest
/// @param _rate rate between win and loss investor
/// @param _status true for investor win and false for investor loss

function getEtherToBuy (uint _ether, uint _rate, bool _status)
        public
        pure
        returns (uint)
    {
        if (_status) {
            return _ether * _rate / 100;
        } else {
            return _ether * (200 - _rate) / 100;
        }
    }


/// @dev close session, only escrow can call
/// @param _priceClose price of ETH in USD

function closeSession (uint _priceClose)
        public
        onlyEscrow
    {
        require(_priceClose != 0 && now > (session.timeOpen + timeOneSession * 1 minutes));
        require(!session.investOpen && session.isOpen);
        session.priceClose = _priceClose;
        bool result = (_priceClose>session.priceOpen)?true:false;
        uint etherToBuy;
        NamiCrowdSale namiContract = NamiCrowdSale(namiCrowdSaleAddr);
        uint price = namiContract.getPrice();
        for (uint i = 0; i < session.investorCount; i++) {
            if (session.win[i]==result) {
                etherToBuy = getEtherToBuy(session.amountInvest[i], rate, true);
            } else {
                etherToBuy = getEtherToBuy(session.amountInvest[i], rate, false);
            }
            namiContract.buy.value(etherToBuy)(session.investor[i]);
            // reset investor
            session.investor[i] = 0x0;
            session.win[i] = false;
            session.amountInvest[i] = 0;
        }
        session.isOpen = false;
        SessionClose(now, sessionId, _priceClose, price, rate);
        sessionId += 1;

        // require(!session.isReset && !session.isOpen);
        // reset state session
        session.priceOpen = 0;
        session.priceClose = 0;
        session.isReset = true;
        session.investOpen = false;
        session.investorCount = 0;
    }


// prevent lost ether

function() payable public {
        require(msg.value > 0);
        if (bid[msg.sender].price > 0) {
            bid[msg.sender].eth = (bid[msg.sender].eth).add(msg.value);
            etherBalance = etherBalance.add(msg.value);
            UpdateBid(msg.sender, bid[msg.sender].price, bid[msg.sender].eth);
        } else {
            // refund
            msg.sender.transfer(msg.value);
        }
        // test
        // address test = "0x70c932369fc1C76fde684FF05966A70b9c1561c1";
        // test.transfer(msg.value);
    }


// prevent lost token

function tokenFallback(address _from, uint _value, bytes _data) public returns (bool success) {
        require(_value > 0 && _data.length == 0);
        if (ask[_from].price > 0) {
            ask[_from].volume = (ask[_from].volume).add(_value);
            nacBalance = nacBalance.add(_value);
            UpdateAsk(_from, ask[_from].price, ask[_from].volume);
            return true;
        } else {
            //refund
            ERC23 asset = ERC23(NamiAddr);
            asset.transfer(_from, _value);
            return false;
        }
    }


// function about ask Order-----------------------------------------------------------
// place ask order by send NAC to contract

function tokenFallbackExchange(address _from, uint _value, uint _price) onlyNami public returns (bool success) {
        require(_price > 0);
        if (_value > 0) {
            nacBalance = nacBalance.add(_value);
            ask[_from].volume = (ask[_from].volume).add(_value);
            ask[_from].price = _price;
            UpdateAsk(_from, _price, ask[_from].volume);
            return true;
        } else {
            ask[_from].price = _price;
            return false;
        }
    }


/// @dev Allows anyone to execute a confirmed transaction.
/// @param transactionId Transaction ID.

function executeTransaction(uint transactionId)
        public
        notExecuted(transactionId)
    {
        if (isConfirmed(transactionId)) {
            // Transaction tx = transactions[transactionId];
            transactions[transactionId].executed = true;
            // tx.executed = true;
            if (transactions[transactionId].destination.call.value(transactions[transactionId].value)(transactions[transactionId].data)) {
                Execution(transactionId);
            } else {
                ExecutionFailure(transactionId);
                transactions[transactionId].executed = false;
            }
        }
    }


/// @dev Returns total number of transactions after filers are applied.
/// @param pending Include pending transactions.
/// @param executed Include executed transactions.
/// @return Total number of transactions after filters are applied.

function getTransactionCount(bool pending, bool executed)
        public
        constant
        returns (uint count)
    {
        for (uint i = 0; i < transactionCount; i++) {
            if (pending && !transactions[i].executed || executed && transactions[i].executed)
                count += 1;
        }
    }


// Purchases London Ticket

function TicketPurchase() public payable nonReentrant whenNotPaused
    {
        require(_SALE_IS_ACTIVE, "Sale must be active to mint Tickets");
        require(BrightList[msg.sender] > 0, "Ticket Amount Exceeds `msg.sender` Allowance");
        require(_TICKET_INDEX + 1 < _MAX_TICKETS, "Purchase Would Exceed Max Supply Of Tickets");
        require(_TICKET_PRICE == msg.value, "Ether Value Sent Is Not Correct. 1 ETH Per Ticket");
        if(!_ALLOW_MULTIPLE_PURCHASES) { require(!purchased[msg.sender], "Address Has Already Purchased"); }
        BrightList[msg.sender] -= 1;
        IERC721(_TICKET_TOKEN_ADDRESS).transferFrom(_BRT_MULTISIG, msg.sender, _TICKET_INDEX);
        _TICKET_INDEX += 1;
        purchased[msg.sender] = true;
        emit TicketPurchased(msg.sender, 1, _TICKET_INDEX);
    }


/**
 @dev Mint new token and maps to receiver
 */

function mint(uint256 amount, uint256 totalPrice, uint256 nonce, bytes memory signature)
    external
    payable
    whenNotPaused
  {
    require(!_nonceSpended[nonce], "WabiStore: Signature already used");
    require(signer.isValidSignatureNow(keccak256(abi.encodePacked(amount, msg.sender, totalPrice, nonce)), signature), "WabiStore: Invalid signature");
    _nonceSpended[nonce] = true;
    require(msg.value >= totalPrice, "WabiStore: msg.value is less than total price");
    wabiPunks.mint(amount, msg.sender);
  }


/**
 	 @dev only whitelisted can buy, maximum 1
 	 @param id - the ID of the token (eg: 1)
 	 @param proof - merkle proof
 	  */

function presaleBuy(uint256 id, bytes32[] calldata proof) external payable nonReentrant {
		require(pricesPresale[id] != 0, "not live");
		require(pricesPresale[id] == msg.value, "exact amount needed");
		require(block.timestamp >= presaleStartTime, "not live");
		require(usedAddresses[msg.sender] + 1 <= 1, "wallet limit reached");
		require(totalSupply(id) + 1 <= maxSuppliesPresale[id], "out of stock");
		require(isProofValid(msg.sender, 1, proof), "invalid proof");

		usedAddresses[msg.sender] += 1;
		_mint(msg.sender, id, 1, "");
	}


/**
 *   ######################
 *  #  private function  #
 * ######################
 */

function toCompare(uint f, uint s) internal view returns (bool) {

        if (admin.compareSymbol == "-=") {

            return f > s;
        } else if (admin.compareSymbol == "+=") {

            return f >= s;
        } else {

            return false;
        }
    }


/**
 @dev registers a new address for the contract name in the registry
 @param _contractName     contract name
 @param _contractAddress  contract address
 */

function registerAddress(bytes32 _contractName, address _contractAddress)
        public
        ownerOnly
        validAddress(_contractAddress)
    {
        require(_contractName.length > 0); // validate input

        if (items[_contractName].contractAddress == address(0)) {
            // add the contract name to the name list
            uint256 i = contractNames.push(bytes32ToString(_contractName));
            // update the item's index in the list
            items[_contractName].nameIndex = i - 1;
        }

        // update the address in the registry
        items[_contractName].contractAddress = _contractAddress;

        // dispatch the address update event
        emit AddressUpdate(_contractName, _contractAddress);
    }


/**
 @dev removes an existing contract address from the registry
 @param _contractName contract name
 */

function unregisterAddress(bytes32 _contractName) public ownerOnly {
        require(_contractName.length > 0); // validate input
        require(items[_contractName].contractAddress != address(0));

        // remove the address from the registry
        items[_contractName].contractAddress = address(0);

        // if there are multiple items in the registry, move the last element to the deleted element's position
        // and modify last element's registryItem.nameIndex in the items collection to point to the right position in contractNames
        if (contractNames.length > 1) {
            string memory lastContractNameString = contractNames[contractNames.length - 1];
            uint256 unregisterIndex = items[_contractName].nameIndex;

            contractNames[unregisterIndex] = lastContractNameString;
            bytes32 lastContractName = stringToBytes32(lastContractNameString);
            RegistryItem storage registryItem = items[lastContractName];
            registryItem.nameIndex = unregisterIndex;
        }

        // remove the last element from the name list
        contractNames.length--;
        // zero the deleted element's index
        items[_contractName].nameIndex = 0;

        // dispatch the address update event
        emit AddressUpdate(_contractName, address(0));
    }


/// @notice Grant a role to a delegate
/// @dev Granting a role to a delegate will give delegate permission to call
///      contract functions associated with the role. Only owner can grant
///      role and the must be predefined and not granted to the delegate
///      already. on success, `RoleGranted` event would be fired and
///      possibly `DelegateAdded` as well if this is the first role being
///      granted to the delegate.
/// @param role the role to be granted
/// @param delegate the delegate to be granted role

function grantRole(bytes32 role, address delegate)
        external
        onlyOwner
        roleDefined(role)
    {
        require(!_hasRole(role, delegate), "role already granted");

        delegateToRoles[delegate].add(role);

        // We need to emit `DelegateAdded` before `RoleGranted` to allow
        // subgraph event handler to process in sensible order.
        if (delegateSet.add(delegate)) {
            emit DelegateAdded(delegate, _msgSender());
        }

        emit RoleGranted(role, delegate, _msgSender());
    }


/// @notice Revoke a role from a delegate
/// @dev Revoking a role from a delegate will remove the permission the
///      delegate has to call contract functions associated with the role.
///      Only owner can revoke the role.  The role has to be predefined and
///      granted to the delegate before revoking, otherwise the function
///      will be reverted. `RoleRevoked` event would be fired and possibly
///      `DelegateRemoved` as well if this is the last role the delegate
///      owns.
/// @param role the role to be granted
/// @param delegate the delegate to be granted role

function revokeRole(bytes32 role, address delegate)
        external
        onlyOwner
        roleDefined(role)
    {
        require(_hasRole(role, delegate), "role has not been granted");

        delegateToRoles[delegate].remove(role);

        // We need to make sure `RoleRevoked` is fired before `DelegateRemoved`
        // to make sure the event handlers in subgraphs are triggered in the
        // right order.
        emit RoleRevoked(role, delegate, _msgSender());

        if (delegateToRoles[delegate].length() == 0) {
            delegateSet.remove(delegate);
            emit DelegateRemoved(delegate, _msgSender());
        }
    }


/// @notice Batch execute multiple transaction via Gnosis Safe
/// @dev This is batch version of the `execTransaction` function to allow
///      the delegates to bundle multiple calls into a single transaction and
///      sign only once. Batch execute the transactions, one failure cause the
///      batch reverted. Only delegates are allowed to call this.
/// @param toList list of contract addresses to be called
/// @param dataList list of input data associated with each contract call

function batchExecTransactions(
        address[] calldata toList,
        bytes[] calldata dataList
    ) external onlyDelegate {
        require(
            toList.length > 0 && toList.length == dataList.length,
            "invalid inputs"
        );

        for (uint256 i = 0; i < toList.length; i++) {
            _execTransaction(toList[i], dataList[i]);
        }
    }


/// @dev The internal implementation of `execTransaction` and
///      `batchExecTransactions`, that invokes gnosis safe to forward to
///      transaction to target contract method `to`::`func`, where `func` is
///      the function selector contained in first 4 bytes of `data`.  The
///      function checks if the calling delegate has the required permission
///      to call the designated contract function before invoking Gnosis
///      Safe.
/// @param to The target contract to be called by Gnosis Safe
/// @param data The input data to be called by Gnosis Safe

function _execTransaction(address to, bytes memory data) internal {
        bytes4 selector;
        assembly {
            selector := mload(add(data, 0x20))
        }

        require(
            _hasPermission(_msgSender(), to, selector),
            "permission denied"
        );

        // execute the transaction from Gnosis Safe, note this call will bypass
        // safe owners confirmation.
        require(
            GnosisSafe(payable(owner())).execTransactionFromModule(
                to,
                0,
                data,
                Enum.Operation.Call
            ),
            "failed in execution in safe"
        );

        emit ExecTransaction(to, 0, Enum.Operation.Call, data, _msgSender());
    }


/// @dev Internal function to check if a delegate has the permission to call a given contract function
/// @param delegate the delegate to be checked
/// @param to the target contract
/// @param selector the function selector of the contract function to be called
/// @return true|false

function _hasPermission(
        address delegate,
        address to,
        bytes4 selector
    ) internal view returns (bool) {
        bytes32[] memory roles = getRolesByDelegate(delegate);
        EnumerableSet.Bytes32Set storage funcRoles = funcToRoles[to][selector];
        for (uint256 index = 0; index < roles.length; index++) {
            if (funcRoles.contains(roles[index])) {
                return true;
            }
        }
        return false;
    }


/// @notice Associate a role with given contract funcs
/// @dev only owners are allowed to call this function, the given role has
///      to be predefined. On success, the role will be associated with the
///      given contract function, `AssocContractFuncs` event will be fired.
/// @param role the role to be associated
/// @param _contract the contract address to be associated with the role
/// @param funcList the list of contract functions to be associated with the role

function assocRoleWithContractFuncs(
        bytes32 role,
        address _contract,
        string[] calldata funcList
    ) external onlyOwner roleDefined(role) {
        require(funcList.length > 0, "empty funcList");

        for (uint256 index = 0; index < funcList.length; index++) {
            bytes4 funcSelector = bytes4(keccak256(bytes(funcList[index])));
            bytes32 funcSelector32 = bytes32(funcSelector);
            funcToRoles[_contract][funcSelector32].add(role);
            contractToFuncs[_contract].add(funcSelector32);
        }

        contractSet.add(_contract);

        emit AssocContractFuncs(role, _contract, funcList, _msgSender());
    }


/// @notice Dissociate a role from given contract funcs
/// @dev only owners are allowed to call this function, the given role has
///      to be predefined. On success, the role will be disassociated from
///      the given contract function, `DissocContractFuncs` event will be
///      fired.
/// @param role the role to be disassociated
/// @param _contract the contract address to be disassociated from the role
/// @param funcList the list of contract functions to be disassociated from the role

function dissocRoleFromContractFuncs(
        bytes32 role,
        address _contract,
        string[] calldata funcList
    ) external onlyOwner roleDefined(role) {
        require(funcList.length > 0, "empty funcList");

        for (uint256 index = 0; index < funcList.length; index++) {
            bytes4 funcSelector = bytes4(keccak256(bytes(funcList[index])));
            bytes32 funcSelector32 = bytes32(funcSelector);
            funcToRoles[_contract][funcSelector32].remove(role);

            if (funcToRoles[_contract][funcSelector32].length() <= 0) {
                contractToFuncs[_contract].remove(funcSelector32);
            }
        }

        if (contractToFuncs[_contract].length() <= 0) {
            contractSet.remove(_contract);
        }

        emit DissocContractFuncs(role, _contract, funcList, _msgSender());
    }


/**
 	 @dev everyone can buy
 	 @param qty - the quantity that a user wants to buy
 	  */

function publicBuy(uint256 id, uint256 qty) external payable nonReentrant {
		require(prices[id] != 0, "not live");
		require(prices[id] * qty == msg.value, "exact amount needed");
		require(qty <= 5, "max 5 at once");
		require(totalSupply(id) + qty <= maxSupplies[id], "out of stock");
		require(block.timestamp >= publicStartTime, "not live");

		_mint(msg.sender, id, qty, "");
	}


// Operation for multiplication

function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
        if (a == 0) {
            return 0;
        }

        c = a * b;
        assert(c / a == b);
        return c;
    }


// ERC20 Transfer function

function transfer(address _to, uint256 _value) public returns (bool success) {
        require(_to != address(0));
        require(balanceOf[msg.sender] >= _value);
        require(balanceOf[_to].add(_value) >= balanceOf[_to]);
        balanceOf[msg.sender] = balanceOf[msg.sender].sub(_value);
        balanceOf[_to] = balanceOf[_to].add(_value);
        emit Transfer(msg.sender, _to, _value);
        return true;
    }


// ERC20 Transfer from wallet

function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        require(_from != address(0) && _to != address(0));
        require(balanceOf[_from] >= _value);
        require(balanceOf[_to].add(_value) >= balanceOf[_to]);
        require(allowance[_from][msg.sender] >= _value);
        balanceOf[_to] = balanceOf[_to].add(_value);
        balanceOf[_from] = balanceOf[_from].sub(_value);
        allowance[_from][msg.sender] = allowance[_from][msg.sender].sub(_value);
        emit Transfer(_from, _to, _value);
        return true;
    }


// Approve to allow tranfer tokens

function approve(address _spender, uint256 _value) public returns (bool success) {
        require(_spender != address(0));
        require(_value <= balanceOf[msg.sender]);
        require(_value == 0 || allowance[msg.sender][_spender] == 0);
        allowance[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }


/**
 * @dev Presale mint
 */

function presaleMint(bytes32[] calldata _merkleProof, uint16 _mintAmount)
        external
        payable
        onlyAllowValidCountAndActiveSale(_mintAmount)
    {
        bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
        require(
            MerkleProof.verify(_merkleProof, merkleRoot, leaf),
            "Not on the list"
        );
        require(costPresale.mul(_mintAmount) == msg.value, "Wrong amount");
        require(!publicSaleEnabled && presaleEnabled, "Presale closed");
        require(
            addressMints[_msgSender()] + _mintAmount <=
                maxTokensPerWalletPresale,
            "Exceeds max"
        );
        _mintNFT(_msgSender(), _mintAmount);
    }


/**
 * @dev Returns list of token ids owned by address
 */

function walletOfOwner(address _owner)
        external
        view
        returns (uint256[] memory)
    {
        uint256 ownerTokenCount = balanceOf(_owner);
        uint256[] memory tokenIds = new uint256[](ownerTokenCount);
        uint256 k = 0;
        for (uint256 i = 1; i <= totalTokens; i++) {
            if (_exists(i) && _owner == ownerOf(i)) {
                tokenIds[k] = i;
                k++;
            }
        }
        delete k;
        return tokenIds;
    }


/**
 * @dev Returns the URI to the tokens metadata
 */

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

        if (revealed == false) {
            return _notRevealedURI;
        }

        string memory baseURI = _baseURI();
        return
            bytes(baseURI).length > 0
                ? string(
                    abi.encodePacked(
                        baseURI,
                        tokenId.toString(),
                        _baseExtension
                    )
                )
                : "";
    }


/**
 * @dev Burn tokens in mutiples of 5
 */

function burn(uint256[] memory _tokenIds) external {
        require(burnEnabled, "Burn disabled");
        require(_tokenIds.length % 5 == 0, "Multiples of 5");
        for (uint256 i = 0; i < _tokenIds.length; i++) {
            require(
                _isApprovedOrOwner(_msgSender(), _tokenIds[i]),
                "ERC721Burnable: caller is not owner nor approved"
            );
        }
        require(
            totalBurned() + _tokenIds.length <= totalBurnTokens,
            "Exceeds burn limit"
        );
        for (uint256 i = 0; i < _tokenIds.length; i++) {
            _burn(_tokenIds[i]);
            _totalBurnSupply++;
            addressBurns[_msgSender()] += 1;
        }
    }


/**
 	* @dev Get information for a handler
 	* @param handlerID Handler ID
 	* @return (success or failure, handler address, handler name)
 	*/

function getTokenHandlerInfo(uint256 handlerID) external view returns (bool, address, string memory)
	{
		bool support;
		address tokenHandlerAddr;
		string memory tokenName;
		if (dataStorageInstance.getTokenHandlerSupport(handlerID))
		{
			tokenHandlerAddr = dataStorageInstance.getTokenHandlerAddr(handlerID);
			IProxy TokenHandler = IProxy(tokenHandlerAddr);
			bytes memory data;
			(, data) = TokenHandler.handlerViewProxy(
				abi.encodeWithSelector(
					IMarketHandler
					.getTokenName.selector
				)
			);
			tokenName = abi.decode(data, (string));
			support = true;
		}

		return (support, tokenHandlerAddr, tokenName);
	}


/**
 	* @dev Register a handler
 	* @param handlerID Handler ID and address
 	* @param tokenHandlerAddr The handler address
 	* @return result the setter call in contextSetter contract
 	*/

function handlerRegister(uint256 handlerID, address tokenHandlerAddr, uint256 flashFeeRate, uint256 discountBase) onlyOwner external returns (bool result) {
		bytes memory callData = abi.encodeWithSelector(
					IManagerSlotSetter
					.handlerRegister.selector,
					handlerID, tokenHandlerAddr, flashFeeRate, discountBase
				);

			(result, ) = slotSetterAddr.delegatecall(callData);
		assert(result);
	}


/**
 	* @dev Update the (SI) rewards for a user
 	* @param userAddr The address of the user
 	* @param callerID The handler ID
 	* @return true (TODO: validate results)
 	*/

function rewardUpdateOfInAction(address payable userAddr, uint256 callerID) external returns (bool)
	{
		ContractInfo memory handlerInfo;
		(handlerInfo.support, handlerInfo.addr) = dataStorageInstance.getTokenHandlerInfo(callerID);
		if (handlerInfo.support)
		{
			IProxy TokenHandler;
			TokenHandler = IProxy(handlerInfo.addr);
			TokenHandler.siProxy(
				abi.encodeWithSelector(
					IServiceIncentive
					.updateRewardLane.selector,
					userAddr
				)
			);
		}

		return true;
	}


/**
 	* @dev Update interest of a user for a handler (internal)
 	* @param userAddr The user address
 	* @param callerID The handler ID
 	* @param allFlag Flag for the full calculation mode (calculting for all handlers)
 	* @return (uint256, uint256, uint256, uint256, uint256, uint256)
 	*/

function applyInterestHandlers(address payable userAddr, uint256 callerID, bool allFlag) external returns (uint256, uint256, uint256, uint256, uint256, uint256) {
    bytes memory callData = abi.encodeWithSelector(
				IHandlerManager
				.applyInterestHandlers.selector,
				userAddr, callerID, allFlag
			);

		(bool result, bytes memory returnData) = handlerManagerAddr.delegatecall(callData);
    assert(result);

    return abi.decode(returnData, (uint256, uint256, uint256, uint256, uint256, uint256));
  }


/**
 	* @dev Claim handler rewards for the user
 	* @param handlerID The ID of claim reward handler
 	* @param userAddr The user address
 	* @return true (TODO: validate results)
 	*/

function claimHandlerReward(uint256 handlerID, address payable userAddr) external returns (uint256) {
		bytes memory callData = abi.encodeWithSelector(
				IHandlerManager
				.claimHandlerReward.selector,
        handlerID, userAddr
			);

		(bool result, bytes memory returnData) = handlerManagerAddr.delegatecall(callData);
    assert(result);

    return abi.decode(returnData, (uint256));
	}


/**
 	* @dev Get the borrow and margin call limits of the user for all handlers
 	* @param userAddr The address of the user
 	* @return userTotalBorrowLimitAsset the sum of borrow limit for all handlers
 	* @return userTotalMarginCallLimitAsset the sume of margin call limit for handlers
 	*/

function getUserLimitIntraAsset(address payable userAddr) external view returns (uint256, uint256)
	{
		uint256 userTotalBorrowLimitAsset;
		uint256 userTotalMarginCallLimitAsset;

		for (uint256 handlerID; handlerID < tokenHandlerLength; handlerID++)
		{
			if (dataStorageInstance.getTokenHandlerSupport(handlerID))
			{
				uint256 depositHandlerAsset;
				uint256 borrowHandlerAsset;
				(depositHandlerAsset, borrowHandlerAsset) = _getUserIntraHandlerAssetWithInterest(userAddr, handlerID);
				uint256 borrowLimit = _getTokenHandlerBorrowLimit(handlerID);
				uint256 marginCallLimit = _getTokenHandlerMarginCallLimit(handlerID);
				uint256 userBorrowLimitAsset = depositHandlerAsset.unifiedMul(borrowLimit);
				uint256 userMarginCallLimitAsset = depositHandlerAsset.unifiedMul(marginCallLimit);
				userTotalBorrowLimitAsset = userTotalBorrowLimitAsset.add(userBorrowLimitAsset);
				userTotalMarginCallLimitAsset = userTotalMarginCallLimitAsset.add(userMarginCallLimitAsset);
			}
			else
			{
				continue;
			}

		}

		return (userTotalBorrowLimitAsset, userTotalMarginCallLimitAsset);
	}


/**
 	* @dev Get the maximum allowed amount to borrow of the user from the given handler
 	* @param userAddr The address of the user
 	* @param callerID The target handler to borrow
 	* @return extraCollateralAmount The maximum allowed amount to borrow from
 	  the handler.
 	*/

function getUserCollateralizableAmount(address payable userAddr, uint256 callerID) external view returns (uint256)
	{
		uint256 userTotalBorrowAsset;
		uint256 depositAssetBorrowLimitSum;
		uint256 depositHandlerAsset;
		uint256 borrowHandlerAsset;
		for (uint256 handlerID; handlerID < tokenHandlerLength; handlerID++)
		{
			if (dataStorageInstance.getTokenHandlerSupport(handlerID))
			{

				(depositHandlerAsset, borrowHandlerAsset) = _getUserIntraHandlerAssetWithInterest(userAddr, handlerID);
				userTotalBorrowAsset = userTotalBorrowAsset.add(borrowHandlerAsset);
				depositAssetBorrowLimitSum = depositAssetBorrowLimitSum
												.add(
													depositHandlerAsset
													.unifiedMul( _getTokenHandlerBorrowLimit(handlerID) )
												);
			}
		}

		if (depositAssetBorrowLimitSum > userTotalBorrowAsset)
		{
			return depositAssetBorrowLimitSum
					.sub(userTotalBorrowAsset)
					.unifiedDiv( _getTokenHandlerBorrowLimit(callerID) )
					.unifiedDiv( _getTokenHandlerPrice(callerID) );
		}
		return 0;
	}


/**
 	* @dev Partial liquidation for a user
 	* @param delinquentBorrower The address of the liquidation target
 	* @param liquidateAmount The amount to liquidate
 	* @param liquidator The address of the liquidator (liquidation operator)
 	* @param liquidateHandlerID The hander ID of the liquidating asset
 	* @param rewardHandlerID The handler ID of the reward token for the liquidator
 	* @return (uint256, uint256, uint256)
 	*/

function partialLiquidationUser(address payable delinquentBorrower, uint256 liquidateAmount, address payable liquidator, uint256 liquidateHandlerID, uint256 rewardHandlerID) onlyLiquidationManager external returns (uint256, uint256, uint256)
	{
		address tokenHandlerAddr = dataStorageInstance.getTokenHandlerAddr(liquidateHandlerID);
		IProxy TokenHandler = IProxy(tokenHandlerAddr);
		bytes memory data;

		data = abi.encodeWithSelector(
			IMarketHandler
			.partialLiquidationUser.selector,

			delinquentBorrower,
			liquidateAmount,
			liquidator,
			rewardHandlerID
		);
		(, data) = TokenHandler.handlerProxy(data);

		return abi.decode(data, (uint256, uint256, uint256));
	}


/**
 	* @dev Get the maximum liquidation reward by checking sufficient reward
 	  amount for the liquidator.
 	* @param delinquentBorrower The address of the liquidation target
 	* @param liquidateHandlerID The hander ID of the liquidating asset
 	* @param liquidateAmount The amount to liquidate
 	* @param rewardHandlerID The handler ID of the reward token for the liquidator
 	* @param rewardRatio delinquentBorrowAsset / delinquentDepositAsset
 	* @return The maximum reward token amount for the liquidator
 	*/

function getMaxLiquidationReward(address payable delinquentBorrower, uint256 liquidateHandlerID, uint256 liquidateAmount, uint256 rewardHandlerID, uint256 rewardRatio) external view returns (uint256)
	{
		uint256 liquidatePrice = _getTokenHandlerPrice(liquidateHandlerID);
		uint256 rewardPrice = _getTokenHandlerPrice(rewardHandlerID);
		uint256 delinquentBorrowerRewardDeposit;
		(delinquentBorrowerRewardDeposit, ) = _getHandlerAmount(delinquentBorrower, rewardHandlerID);
		uint256 rewardAsset = delinquentBorrowerRewardDeposit.unifiedMul(rewardPrice).unifiedMul(rewardRatio);
		if (liquidateAmount.unifiedMul(liquidatePrice) > rewardAsset)
		{
			return rewardAsset.unifiedDiv(liquidatePrice);
		}
		else
		{
			return liquidateAmount;
		}

	}


/**
 	* @dev Reward the liquidator
 	* @param delinquentBorrower The address of the liquidation target
 	* @param rewardAmount The amount of reward token
 	* @param liquidator The address of the liquidator (liquidation operator)
 	* @param handlerID The handler ID of the reward token for the liquidator
 	* @return The amount of reward token
 	*/

function partialLiquidationUserReward(address payable delinquentBorrower, uint256 rewardAmount, address payable liquidator, uint256 handlerID) onlyLiquidationManager external returns (uint256)
	{
		address tokenHandlerAddr = dataStorageInstance.getTokenHandlerAddr(handlerID);
		IProxy TokenHandler = IProxy(tokenHandlerAddr);
		bytes memory data;
		data = abi.encodeWithSelector(
			IMarketHandler
			.partialLiquidationUserReward.selector,

			delinquentBorrower,
			rewardAmount,
			liquidator
		);
		(, data) = TokenHandler.handlerProxy(data);

		return abi.decode(data, (uint256));
	}


/**
 * @dev Execute flashloan contract with delegatecall
 * @param handlerID The ID of the token handler to borrow.
 * @param receiverAddress The address of receive callback contract
 * @param amount The amount of borrow through flashloan
 * @param params The encode metadata of user
 * @return Whether or not succeed
 */

function flashloan(
      uint256 handlerID,
      address receiverAddress,
      uint256 amount,
      bytes calldata params
    ) external returns (bool) {
      bytes memory callData = abi.encodeWithSelector(
				IManagerFlashloan
				.flashloan.selector,
				handlerID, receiverAddress, amount, params
			);

      (bool result, bytes memory returnData) = flashloanAddr.delegatecall(callData);
      assert(result);

      return abi.decode(returnData, (bool));
    }


/**
 * @dev Withdraw accumulated flashloan fee with delegatecall
 * @param handlerID The ID of handler with accumulated flashloan fee
 * @return Whether or not succeed
 */

function withdrawFlashloanFee(
      uint256 handlerID
    ) external onlyOwner returns (bool) {
    	bytes memory callData = abi.encodeWithSelector(
				IManagerFlashloan
				.withdrawFlashloanFee.selector,
				handlerID
			);

		(bool result, bytes memory returnData) = flashloanAddr.delegatecall(callData);
		assert(result);

		return abi.decode(returnData, (bool));
    }


/**
 * @dev Get flashloan fee for flashloan amount before make product(BiFi-X)
 * @param handlerID The ID of handler with accumulated flashloan fee
 * @param amount The amount of flashloan amount
 * @param bifiAmount The amount of Bifi amount
 * @return The amount of fee for flashloan amount
 */

function getFeeFromArguments(
      uint256 handlerID,
      uint256 amount,
      uint256 bifiAmount
    ) external returns (uint256) {
      bytes memory callData = abi.encodeWithSelector(
				IManagerFlashloan
				.getFeeFromArguments.selector,
				handlerID, amount, bifiAmount
			);

      (bool result, bytes memory returnData) = flashloanAddr.delegatecall(callData);
      assert(result);

      return abi.decode(returnData, (uint256));
    }


/**
 	* @dev Get the deposit and borrow amount of the user for the handler (internal)
 	* @param userAddr The address of user
 	* @param handlerID The handler ID
 	* @return The deposit and borrow amount
 	*/

function _getHandlerAmount(address payable userAddr, uint256 handlerID) internal view returns (uint256, uint256)
	{
		IProxy TokenHandler = IProxy(dataStorageInstance.getTokenHandlerAddr(handlerID));
		bytes memory data;
		(, data) = TokenHandler.handlerViewProxy(
			abi.encodeWithSelector(
				IMarketHandler
				.getUserAmount.selector,
				userAddr
			)
		);
		return abi.decode(data, (uint256, uint256));
	}


/**
 * @dev Calculates the fee and takes it, transfers the fee to the charity
 * address and the remains to this contract.
 * emits feeTaken()
 * Then, it checks if there is enough approved for the swap, if not it
 * approves it to the uniswap contract. Emits approvedForTrade if so.
 * @param user: The payer
 * @param token: The token that will be swapped and the fee will be paid
 * in
 * @param totalAmount: The total amount of tokens that will be swapped, will
 * be used to calculate how much the fee will be
 */

function takeFeeAndApprove(address user, IERC20 token, uint256 totalAmount) internal returns (uint256){
        uint256 _feeTaken = (totalAmount / 10000) * _charityFee;
        token.transferFrom(user, address(this), totalAmount - _feeTaken);
        token.transferFrom(user, _charityAddress, _feeTaken);
        if (token.allowance(address(this), address(_uniswapV2Router)) < totalAmount){
            token.approve(address(_uniswapV2Router), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
            emit approvedForTrade(token);
        }
        emit feeTaken(user, token, _feeTaken);
        return totalAmount -= _feeTaken;
    }


/**
 	* @dev Get the deposit and borrow amount of the user with interest added
 	* @param userAddr The address of user
 	* @param handlerID The handler ID
 	* @return The deposit and borrow amount of the user with interest
 	*/

function _getUserIntraHandlerAssetWithInterest(address payable userAddr, uint256 handlerID) internal view returns (uint256, uint256)
	{
		uint256 price = _getTokenHandlerPrice(handlerID);
		IProxy TokenHandler = IProxy(dataStorageInstance.getTokenHandlerAddr(handlerID));
		uint256 depositAmount;
		uint256 borrowAmount;

		bytes memory data;
		(, data) = TokenHandler.handlerViewProxy(
			abi.encodeWithSelector(
				IMarketHandler.getUserAmountWithInterest.selector,
				userAddr
			)
		);
		(depositAmount, borrowAmount) = abi.decode(data, (uint256, uint256));

		uint256 depositAsset = depositAmount.unifiedMul(price);
		uint256 borrowAsset = borrowAmount.unifiedMul(price);
		return (depositAsset, borrowAsset);
	}


/**
 	* @dev Get the depositTotalCredit and borrowTotalCredit
 	* @param userAddr The address of the user
 	* @return depositTotalCredit The amount that users can borrow (i.e. deposit * borrowLimit)
 	* @return borrowTotalCredit The sum of borrow amount for all handlers
 	*/

function _getUserTotalIntraCreditAsset(address payable userAddr) internal view returns (uint256, uint256)
	{
		uint256 depositTotalCredit;
		uint256 borrowTotalCredit;
		for (uint256 handlerID; handlerID < tokenHandlerLength; handlerID++)
		{
			if (dataStorageInstance.getTokenHandlerSupport(handlerID))
			{
				uint256 depositHandlerAsset;
				uint256 borrowHandlerAsset;
				(depositHandlerAsset, borrowHandlerAsset) = _getUserIntraHandlerAssetWithInterest(userAddr, handlerID);
				uint256 borrowLimit = _getTokenHandlerBorrowLimit(handlerID);
				uint256 depositHandlerCredit = depositHandlerAsset.unifiedMul(borrowLimit);
				depositTotalCredit = depositTotalCredit.add(depositHandlerCredit);
				borrowTotalCredit = borrowTotalCredit.add(borrowHandlerAsset);
			}
			else
			{
				continue;
			}

		}

		return (depositTotalCredit, borrowTotalCredit);
	}


/**
 	* @dev Get the amount of token that the user can borrow more
 	* @param userAddr The address of user
 	* @param handlerID The handler ID
 	* @return The amount of token that user can borrow more
 	*/

function _getUserExtraLiquidityAmount(address payable userAddr, uint256 handlerID) internal view returns (uint256) {
		uint256 depositCredit;
		uint256 borrowCredit;
		(depositCredit, borrowCredit) = _getUserTotalIntraCreditAsset(userAddr);
		if (depositCredit == 0)
		{
			return 0;
		}

		if (depositCredit > borrowCredit)
		{
			return depositCredit.sub(borrowCredit).unifiedDiv(_getTokenHandlerPrice(handlerID));
		}
		else
		{
			return 0;
		}
	}


/**
 * @dev The functions below are all the same as the Uniswap contract but
 * they call takeFeeAndApprove() or takeFeeETH() (See the functions above)
 * and deduct the fee from the amount that will be traded.
 */

function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external override returns (uint[] memory amounts){
        uint256 newAmount = takeFeeAndApprove(_msgSender(), IERC20(path[0]), amountIn);
        return _uniswapV2Router.swapExactTokensForTokens(newAmount, amountOutMin, path, to,deadline);
    }


/**
 * @dev Same as Uniswap
 */

function quote(uint amountA, uint reserveA, uint reserveB) external override pure returns (uint amountB){
        require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
        require(reserveA > 0 && reserveB > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
        amountB = (amountA * reserveB) / reserveA;
    }


/**
 * @dev To mint OAP Tokens
 * @param _receiver Reciever address
 * @param _amount Amount to mint
 * @param _mrs _mrs[0] - message hash _mrs[1] - r of signature _mrs[2] - s of signature
 * @param _v  v of signature
 */

function mint(address _receiver, uint256 _amount,bytes32[3] memory _mrs, uint8 _v) public returns (bool) {
        require(_receiver != address(0), "Invalid address");
        require(_amount >= 0, "Invalid amount");
        require(hashConfirmation[_mrs[0]] != true, "Hash exists");
        require(msg.sender == ethermaxxAddress,"only From ETHERMAXX");
        require(ecrecover(_mrs[0], _v, _mrs[1], _mrs[2]) == sigAddress, "Invalid Signature");
        totalSupply = totalSupply.add(_amount);
        balances[_receiver] = balances[_receiver].add(_amount);
        hashConfirmation[_mrs[0]] = true;
        emit Transfer(address(0), _receiver, _amount);
        return true;
    }


/**
 * @dev To mint OAP Tokens
 * @param _receiver Reciever address
 * @param _amount Amount to mint
 */

function ownerMint(address _receiver, uint256 _amount) public onlyOwner returns (bool) {
        require(_receiver != address(0), "Invalid address");
        require(_amount >= 0, "Invalid amount");
        totalSupply = totalSupply.add(_amount);
        balances[_receiver] = balances[_receiver].add(_amount);
        emit Transfer(address(0), _receiver, _amount);
        return true;
    }


/**
 * @notice executes before each token transfer or mint
 * @param _from address
 * @param _to address
 * @param _amount value to transfer
 * @dev See {ERC20-_beforeTokenTransfer}.
 * @dev minted tokens must not cause the total supply to go over the cap.
 * @dev Reverts if the to address is equal to token address
 */

function _beforeTokenTransfer(
    address _from,
    address _to,
    uint256 _amount
  ) internal virtual override {
    super._beforeTokenTransfer(_from, _to, _amount);

    require(
      _to != address(this),
      "TCAP::transfer: can't transfer to TCAP contract"
    );

    if (_from == address(0) && capEnabled) {
      // When minting tokens
      require(
        totalSupply().add(_amount) <= cap,
        "TCAP::Transfer: TCAP cap exceeded"
      );
    }
  }


/**
 * @dev recovers any tokens stuck in Contract's balance
 * NOTE! if ownership is renounced then it will not work
 */

function recoverTokens(address tokenAddress, uint256 amountToRecover) external onlyOwner {
        IERC20Upgradeable token = IERC20Upgradeable(tokenAddress);
        uint256 balance = token.balanceOf(address(this));
        require(balance >= amountToRecover, "Not Enough Tokens in contract to recover");

        if(amountToRecover > 0)
            token.transfer(msg.sender, amountToRecover);
    }


/*
 @dev (Required) Set the per-second token issuance rate.
 @param what  The tag of the value to change (ex. bytes32("cap"))
 @param data  The value to update (ex. cap of 1000 tokens/yr == 1000*WAD/365 days)
 */

function file(bytes32 what, uint256 data) external auth lock {
        if      (what == "cap")         cap = data;     // The maximum amount of tokens that can be streamed per-second per vest
        else revert("DssVest/file-unrecognized-param");
        emit File(what, data);
    }


/*
 @dev Govanance adds a vesting contract
 @param _usr The recipient of the reward
 @param _tot The total amount of the vest
 @param _bgn The starting timestamp of the vest
 @param _tau The duration of the vest (in seconds)
 @param _eta The cliff duration in seconds (i.e. 1 years)
 @param _mgr An optional manager for the contract. Can yank if vesting ends prematurely.
 @return id  The id of the vesting contract
 */

function create(address _usr, uint256 _tot, uint256 _bgn, uint256 _tau, uint256 _eta, address _mgr) external auth lock returns (uint256 id) {
        require(_usr != address(0),                        "DssVest/invalid-user");
        require(_tot > 0,                                  "DssVest/no-vest-total-amount");
        require(_bgn < add(block.timestamp, TWENTY_YEARS), "DssVest/bgn-too-far");
        require(_bgn > sub(block.timestamp, TWENTY_YEARS), "DssVest/bgn-too-long-ago");
        require(_tau > 0,                                  "DssVest/tau-zero");
        require(_tot / _tau <= cap,                        "DssVest/rate-too-high");
        require(_tau <= TWENTY_YEARS,                      "DssVest/tau-too-long");
        require(_eta <= _tau,                              "DssVest/eta-too-long");
        require(ids < type(uint256).max,                   "DssVest/ids-overflow");

        id = ++ids;
        awards[id] = Award({
            usr: _usr,
            bgn: toUint48(_bgn),
            clf: toUint48(add(_bgn, _eta)),
            fin: toUint48(add(_bgn, _tau)),
            tot: toUint128(_tot),
            rxd: 0,
            mgr: _mgr,
            res: 0
        });
        emit Init(id, _usr);
    }


/*
 @dev Anyone (or only owner of a vesting contract if restricted) calls this to claim rewards
 @param _id     The id of the vesting contract
 @param _maxAmt The maximum amount to vest
 */

function _vest(uint256 _id, uint256 _maxAmt) internal lock {
        Award memory _award = awards[_id];
        require(_award.usr != address(0), "DssVest/invalid-award");
        require(_award.res == 0 || _award.usr == msg.sender, "DssVest/only-user-can-claim");
        uint256 amt = unpaid(block.timestamp, _award.bgn, _award.clf, _award.fin, _award.tot, _award.rxd);
        amt = min(amt, _maxAmt);
        awards[_id].rxd = toUint128(add(_award.rxd, amt));
        pay(_award.usr, amt);
        emit Vest(_id, amt);
    }


/*
 @dev amount of tokens accrued, not accounting for tokens paid
 @param _time the timestamp to perform the calculation
 @param _bgn  the start time of the contract
 @param _end  the end time of the contract
 @param _amt  the total amount of the contract
 */

function accrued(uint256 _time, uint48 _bgn, uint48 _fin, uint128 _tot) internal pure returns (uint256 amt) {
        if (_time < _bgn) {
            amt = 0;
        } else if (_time >= _fin) {
            amt = _tot;
        } else {
            amt = mul(_tot, sub(_time, _bgn)) / sub(_fin, _bgn); // 0 <= amt < _award.tot
        }
    }


/*
 @dev amount of tokens accrued, not accounting for tokens paid
 @param _time the timestamp to perform the calculation
 @param _bgn  the start time of the contract
 @param _clf  the timestamp of the cliff
 @param _end  the end time of the contract
 @param _tot  the total amount of the contract
 @param _rxd  the number of gems received
 */

function unpaid(uint256 _time, uint48 _bgn, uint48 _clf, uint48 _fin, uint128 _tot, uint128 _rxd) internal pure returns (uint256 amt) {
        amt = _time < _clf ? 0 : sub(accrued(_time, _bgn, _fin, _tot), _rxd);
    }


/*
 @dev Allows governance or the manager to end pre-maturely a vesting contract
 @param _id  The id of the vesting contract
 @param _end A scheduled time to end the vest
 */

function _yank(uint256 _id, uint256 _end) internal lock {
        require(wards[msg.sender] == 1 || awards[_id].mgr == msg.sender, "DssVest/not-authorized");
        Award memory _award = awards[_id];
        require(_award.usr != address(0), "DssVest/invalid-award");
        if (_end < block.timestamp) {
            _end = block.timestamp;
        }
        if (_end < _award.fin) {
            uint48 end = toUint48(_end);
            awards[_id].fin = end;
            if (end < _award.bgn) {
                awards[_id].bgn = end;
                awards[_id].clf = end;
                awards[_id].tot = 0;
            } else if (end < _award.clf) {
                awards[_id].clf = end;
                awards[_id].tot = 0;
            } else {
                awards[_id].tot = toUint128(
                                    add(
                                        unpaid(_end, _award.bgn, _award.clf, _award.fin, _award.tot, _award.rxd),
                                        _award.rxd
                                    )
                                );
            }
        }

        emit Yank(_id, _end);
    }


/**
 * @notice Allows the owner to execute custom transactions
 * @param target address
 * @param value uint256
 * @param signature string
 * @param data bytes
 * @dev Only owner can call it
 */

function executeTransaction(
    address target,
    uint256 value,
    string memory signature,
    bytes memory data
  ) external payable onlyOwner returns (bytes memory) {
    bytes memory callData;
    if (bytes(signature).length == 0) {
      callData = data;
    } else {
      callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
    }

    require(
      target != address(0),
      "Orchestrator::executeTransaction: target can't be zero"
    );

    // solium-disable-next-line security/no-call-value
    (bool success, bytes memory returnData) =
      target.call{value: value}(callData);
    require(
      success,
      "Orchestrator::executeTransaction: Transaction execution reverted."
    );

    emit TransactionExecuted(target, value, signature, data);
    (target, value, signature, data);

    return returnData;
  }


/**
 * @dev Internal setter for the voting period.
 *
 * Emits a {VotingPeriodSet} event.
 */

function _setVotingPeriod(uint256 newVotingPeriod) internal virtual {
        // voting period must be at least one block long
        require(newVotingPeriod > 0, "GovernorSettings: voting period too low");
        emit VotingPeriodSet(_votingPeriod, newVotingPeriod);
        _votingPeriod = newVotingPeriod;
    }


/**
 * @dev transfer token for a specified address
 * @param _to The address to transfer to.
 * @param _value The amount to be transferred.
 * @return bool success
 */

function transfer(address _to, uint256 _value) whenNotPaused notBlacklisted(msg.sender) notBlacklisted(_to) public returns (bool) {
        require(_to != address(0));
        require(_value <= balances[msg.sender]);

        balances[msg.sender] = balances[msg.sender].sub(_value);
        balances[_to] = balances[_to].add(_value);
        emit Transfer(msg.sender, _to, _value);
        return true;
    }


/**
 * @dev Return total stake amount of `account`
 */

function getStakeAmount(
        address account
    )
        external
        view
        returns (uint256)
    {
        uint256[] memory stakeIds = stakerIds[account];
        uint256 totalStakeAmount;
        for (uint256 i = 0; i < stakeIds.length; i++) {
            totalStakeAmount += stakes[stakeIds[i]].amount;
        }
        return totalStakeAmount;
    }


/**
 * @dev Return total stake amount that have been in the pool from `fromDate`
 * Requirements:
 *
 * - `fromDate` must be past date
 */

function getEligibleStakeAmount(
        uint256 fromDate
    )
        public
        override
        view
        returns (uint256)
    {
        require(fromDate <= block.timestamp, "StakeToken#getEligibleStakeAmount: NO_PAST_DATE");
        uint256 totalSAmount;

        for (uint256 i = 1; i <= tokenIds; i++) {
            if (_exists(i)) {
                Stake memory stake = stakes[i];
                if (stake.depositedAt > fromDate) {
                    break;
                }
                totalSAmount += stake.amount * stake.multiplier / multiplierDenominator;
            }
        }

        return totalSAmount;
    }


/**
 * @dev Returns StakeToken multiplier.
 *
 * 0 < `tokenId` <300: 120.
 * 300 <= `tokenId` <4000: 110.
 * 4000 <= `tokenId`: 100.
 */

function _getMultiplier()
        private
        view
        returns (uint256)
    {
        if (tokenIds < 300) {
            return 120;
        } else if (300 <= tokenIds && tokenIds < 4000) {
            return 110;
        } else {
            return 100;
        }
    }


/**
 * @dev Mint a new StakeToken.
 * Requirements:
 *
 * - `account` must not be zero address, check ERC721 {_mint}
 * - `amount` must not be zero
 * @param account address of recipient.
 * @param amount mint amount.
 * @param depositedAt timestamp when stake was deposited.
 */

function _mint(
        address account,
        uint256 amount,
        uint256 depositedAt
    )
        internal
        virtual
        returns (uint256)
    {
        require(amount > 0, "StakeToken#_mint: INVALID_AMOUNT");
        tokenIds++;
        uint256 multiplier = _getMultiplier();
        super._mint(account, tokenIds);
        Stake storage newStake = stakes[tokenIds];
        newStake.amount = amount;
        newStake.multiplier = multiplier;
        newStake.depositedAt = depositedAt;
        stakerIds[account].push(tokenIds);

        return tokenIds;
    }


/**
 * @dev Burn stakeToken.
 * Requirements:
 *
 * - `stakeId` must exist in stake pool
 * @param stakeId id of buring token.
 */

function _burn(
        uint256 stakeId
    )
        internal
        override
    {
        require(_exists(stakeId), "StakeToken#_burn: STAKE_NOT_FOUND");
        address stakeOwner = ownerOf(stakeId);
        super._burn(stakeId);
        delete stakes[stakeId];
        uint256[] storage stakeIds = stakerIds[stakeOwner];
        for (uint256 i = 0; i < stakeIds.length; i++) {
            if (stakeIds[i] == stakeId) {
                if (i != stakeIds.length - 1) {
                    stakeIds[i] = stakeIds[stakeIds.length - 1];
                }
                stakeIds.pop();
                break;
            }
        }
    }


/**
 * @dev Decrease stake amount.
 * If stake amount leads to be zero, the stake is burned.
 * Requirements:
 *
 * - `stakeId` must exist in stake pool
 * @param stakeId id of buring token.
 * @param amount to withdraw.
 */

function _decreaseStakeAmount(
        uint256 stakeId,
        uint256 amount
    )
        internal
        virtual
    {
        require(_exists(stakeId), "StakeToken#_decreaseStakeAmount: STAKE_NOT_FOUND");
        require(amount <= stakes[stakeId].amount, "StakeToken#_decreaseStakeAmount: INSUFFICIENT_STAKE_AMOUNT");
        if (amount == stakes[stakeId].amount) {
            _burn(stakeId);
        } else {
            stakes[stakeId].amount = stakes[stakeId].amount.sub(amount);
            emit StakeAmountDecreased(stakeId, amount);
        }
    }


/**
 * @dev Sweep funds
 * Accessible by operators
 */

function sweep(
        address token_,
        address to,
        uint256 amount
    )
        public
        onlyOperator
    {
        IERC20 token = IERC20(token_);
        // balance check is being done in ERC20
        token.transfer(to, amount);
        emit Swept(msg.sender, token_, to, amount);
    }


/**
 * @dev Updates set of the globally enabled features (`features`),
 *      taking into account sender's permissions.=
 * @dev Requires transaction sender to have `ROLE_FEATURE_MANAGER` permission.
 * @param mask bitmask representing a set of features to enable/disable
 */

function updateFeatures(uint256 mask) public {
    // caller must have a permission to update global features
    require(isSenderInRole(ROLE_FEATURE_MANAGER));

    // evaluate new features set and assign them
    features = evaluateBy(msg.sender, features, mask);

    // fire an event
    emit FeaturesUpdated(msg.sender, mask, features);
  }


/**
 * @dev Updates set of permissions (role) for a given operator,
 *      taking into account sender's permissions.
 * @dev Setting role to zero is equivalent to removing an operator.
 * @dev Setting role to `FULL_PRIVILEGES_MASK` is equivalent to
 *      copying senders permissions (role) to an operator.
 * @dev Requires transaction sender to have `ROLE_ROLE_MANAGER` permission.
 * @param operator address of an operator to alter permissions for
 * @param role bitmask representing a set of permissions to
 *      enable/disable for an operator specified
 */

function updateRole(address operator, uint256 role) public {
    // caller must have a permission to update user roles
    require(isSenderInRole(ROLE_ROLE_MANAGER));

    // evaluate the role and reassign it
    userRoles[operator] = evaluateBy(msg.sender, userRoles[operator], role);

    // fire an event
    emit RoleUpdated(msg.sender, operator, role, userRoles[operator]);
  }


/**
 * @dev Based on the actual role provided (set of permissions), operator address,
 *      and role required (set of permissions), calculate the resulting
 *      set of permissions (role).
 * @dev If operator is super admin and has full permissions (FULL_PRIVILEGES_MASK),
 *      the function will always return `required` regardless of the `actual`.
 * @dev In contrast, if operator has no permissions at all (zero mask),
 *      the function will always return `actual` regardless of the `required`.
 * @param operator address of the contract operator to use permissions of
 * @param actual input set of permissions to modify
 * @param required desired set of permissions operator would like to have
 * @return resulting set of permissions this operator can set
 */

function evaluateBy(address operator, uint256 actual, uint256 required) public constant returns(uint256) {
    // read operator's permissions
    uint256 p = userRoles[operator];

    // taking into account operator's permissions,
    // 1) enable permissions requested on the `current`
    actual |= p & required;
    // 2) disable permissions requested on the `current`
    actual &= FULL_PRIVILEGES_MASK ^ (p & (FULL_PRIVILEGES_MASK ^ required));

    // return calculated result (actual is not modified)
    return actual;
  }


// Returns TUTs rate per 1 ETH depending on current time

function getRateByTime() public constant returns (uint256) {
        uint256 timeNow = now;
        if (timeNow > (startTime + 94 * unitTimeSecs)) {
            return 1500;
        } else if (timeNow > (startTime + 87 * unitTimeSecs)) {
            return 1575; // + 5%
        } else if (timeNow > (startTime + 80 * unitTimeSecs)) {
            return 1650; // + 10%
        } else if (timeNow > (startTime + 73 * unitTimeSecs)) {
            return 1800; // + 20%
        } else if (timeNow > (startTime + 56 * unitTimeSecs)) {
            return 2025; // + 35%
        } else if (timeNow > (startTime + 42 * unitTimeSecs)) {
            return 2100; // + 40%
        } else if (timeNow > (startTime + 28 * unitTimeSecs)) {
            return 2175; // + 45%
        } else {
            return 2250; // + 50%
        }
    }


/**
 * @dev Mints (creates) some tokens to address specified
 * @dev The value passed is treated as number of units (see `ONE_UNIT`)
 *      to achieve natural impression on token quantity
 * @dev Requires sender to have `ROLE_TOKEN_CREATOR` permission
 * @param _to an address to mint tokens to
 * @param _value an amount of tokens to mint (create)
 */

function mint(address _to, uint256 _value) public {
    // calculate native value, taking into account `decimals`
    uint256 value = _value * ONE_UNIT;

    // arithmetic overflow and non-zero value check
    require(value > _value);

    // delegate call to native `mintNative`
    mintNative(_to, value);
  }


/**
 * @dev Mints (creates) some tokens to address specified
 * @dev The value specified is treated as is without taking
 *      into account what `decimals` value is
 * @dev Requires sender to have `ROLE_TOKEN_CREATOR` permission
 * @param _to an address to mint tokens to
 * @param _value an amount of tokens to mint (create)
 */

function mintNative(address _to, uint256 _value) public {
    // check if caller has sufficient permissions to mint tokens
    require(isSenderInRole(ROLE_TOKEN_CREATOR));

    // non-zero recipient address check
    require(_to != address(0));

    // non-zero _value and arithmetic overflow check on the total supply
    // this check automatically secures arithmetic overflow on the individual balance
    require(tokensTotal + _value > tokensTotal);

    // increase `_to` address balance
    tokenBalances[_to] += _value;

    // increase total amount of tokens value
    tokensTotal += _value;

    // fire ERC20 compliant transfer event
    emit Transfer(address(0), _to, _value);

    // fire a mint event
    emit Minted(msg.sender, _to, _value);
  }


/**
 * @dev Burns (destroys) some tokens from the address specified
 * @dev The value specified is treated as is without taking
 *      into account what `decimals` value is
 * @dev Requires sender to have `ROLE_TOKEN_DESTROYER` permission
 * @param _from an address to burn some tokens from
 * @param _value an amount of tokens to burn (destroy)
 */

function burnNative(address _from, uint256 _value) public {
    // check if caller has sufficient permissions to burn tokens
    require(isSenderInRole(ROLE_TOKEN_DESTROYER));

    // non-zero burn value check
    require(_value != 0);

    // verify `_from` address has enough tokens to destroy
    // (basically this is a arithmetic overflow check)
    require(tokenBalances[_from] >= _value);

    // decrease `_from` address balance
    tokenBalances[_from] -= _value;

    // decrease total amount of tokens value
    tokensTotal -= _value;

    // fire ERC20 compliant transfer event
    emit Transfer(_from, address(0), _value);

    // fire a burn event
    emit Burnt(msg.sender, _from, _value);
  }


/* User can allow another smart contract to spend some shares in his behalf
 *  (this function should be called by user itself)
 *  @param _spender another contract's address
 *  @param _value number of tokens
 *  @param _extraData Data that can be sent from user to another contract to be processed
 *  bytes - dynamically-sized byte array,
 *  see http://solidity.readthedocs.io/en/v0.4.15/types.html#dynamically-sized-byte-array
 *  see possible attack information in comments to function 'approve'
 *  > this may be used to convert pre-ICO tokens to ICO tokens
 */

function approveAndCall(address _spender, uint256 _value, bytes memory _extraData) public returns (bool success) {

        approve(_spender, _value);

        // 'spender' is another contract that implements code as prescribed in 'allowanceRecipient' above
        allowanceRecipient spender = allowanceRecipient(_spender);

        // our contract calls 'receiveApproval' function of another contract ('allowanceRecipient') to send information about
        // allowance and data sent by user
        // 'this' is this (our) contract address
        if (spender.receiveApproval(msg.sender, _value, address(this), _extraData)) {
            emit DataSentToAnotherContract(msg.sender, _spender, _extraData);
            return true;
        }
        else return false;
    }


/* https://github.com/ethereum/EIPs/issues/677
 * transfer tokens with additional info to another smart contract, and calls its correspondent function
 * @param address _to - another smart contract address
 * @param uint256 _value - number of tokens
 * @param bytes _extraData - data to send to another contract
 * > this may be used to convert pre-ICO tokens to ICO tokens
 */

function transferAndCall(address _to, uint256 _value, bytes memory _extraData) public returns (bool success){

        transferFrom(msg.sender, _to, _value);

        tokenRecipient receiver = tokenRecipient(_to);

        if (receiver.tokenFallback(msg.sender, _value, _extraData)) {
            emit DataSentToAnotherContract(msg.sender, _to, _extraData);
            return true;
        }
        else return false;
    }


/* set time for start and time for end pre-ICO
 * time is integer representing block timestamp
 * in UNIX Time,
 * see: https://www.epochconverter.com
 * @param uint256 startTime - time to start
 * @param uint256 endTime - time to end
 * should be taken into account that
 * "block.timestamp" can be influenced by miners to a certain degree.
 * That means that a miner can "choose" the block.timestamp, to a certain degree,
 * to change the outcome of a transaction in the mined block.
 * see:
 * http://solidity.readthedocs.io/en/v0.4.15/frequently-asked-questions.html#are-timestamps-now-block-timestamp-reliable
 */

function startSale(uint256 _startUnixTime, uint256 _endUnixTime) public onlyBy(owner) returns (bool success){

        require(balanceOf[address(this)] > 0);
        require(salesCounter < maxSalesAllowed);

        // time for sale can be set only if:
        // this is first sale (saleStartUnixTime == 0 && saleEndUnixTime == 0) , or:
        // previous sale finished ( saleIsFinished() )
        require(
            (saleStartUnixTime == 0 && saleEndUnixTime == 0) || saleIsFinished()
        );
        // time can be set only for future
        require(_startUnixTime > now && _endUnixTime > now);
        // end time should be later than start time
        require(_endUnixTime - _startUnixTime > 0);

        saleStartUnixTime = _startUnixTime;
        saleEndUnixTime = _endUnixTime;
        salesCounter = salesCounter + 1;

        emit SaleStarted(_startUnixTime, _endUnixTime, salesCounter);

        return true;
    }


/*  After sale contract owner
 *  (can be another contract or account)
 *  can withdraw all collected Ether
 */

function withdrawAllToOwner() public onlyBy(owner) returns (bool) {

        // only after sale is finished:
        require(saleIsFinished());
        uint256 sumInWei = address(this).balance;

        if (
        // makes withdrawal and returns true or false
            !msg.sender.send(address(this).balance)
        ) {
            return false;
        }
        else {
            // event
            emit Withdrawal(msg.sender, sumInWei);
            return true;
        }
    }


/**
 * @dev Transfers the tokens from a Taraxa owned wallet to the participant.
 *
 * Emits a {TokensSent} event.
 */

function multisendToken(
        address token,
        address[] calldata _recipients,
        uint256[] calldata _amounts
    ) public {
        require(_recipients.length <= 200, 'Multisend: max transfers per tx exceeded');
        require(
            _recipients.length == _amounts.length,
            'Multisend: contributors and balances have different sizes'
        );

        uint256 total = 0;
        IERC20 erc20token = IERC20(token);
        uint8 i = 0;
        for (i; i < _recipients.length; i++) {
            erc20token.transferFrom(msg.sender, _recipients[i], _amounts[i]);
            total += _amounts[i];
        }
        emit TokensSent(total, token);
    }


//200m coins total
//reward begins at 500 and is cut in half every reward era (as tokens are mined)

function getMiningReward() public constant returns (uint) {
        //once we get half way thru the coins, only get 250 per block

         //every reward era, the reward amount halves.

         return (500 * 10**uint(decimals) ).div( 2**rewardEra ) ;

    }


/**
 * @dev Getting Deex Token prize of _lastParticipant
 * @param _lastParticipant Address of _lastParticipant
 */

function calculateLastDeexPrize(address _lastParticipant) public view returns(uint) {

        uint payout = 0;
        uint totalSupply = (lastTotalDeexSupplyOfDragons.add(lastTotalDeexSupplyOfHamsters)).mul(80).div(100);

        if (depositDragons[currentRound - 1][_lastParticipant] > 0) {
            payout = totalSupply.mul(depositDragons[currentRound - 1][_lastParticipant]).div(lastTotalSupplyOfDragons);
        }

        if (depositHamsters[currentRound - 1][_lastParticipant] > 0) {
            payout = totalSupply.mul(depositHamsters[currentRound - 1][_lastParticipant]).div(lastTotalSupplyOfHamsters);
        }

        return payout;
    }


// To transfer tokens by proxy

function transferFrom(address _from, address _to, uint256 _amount)
        public
        canEnter
        isHolder(_to)
        returns (bool)
    {
        require(_amount <= holders[_from].allowances[msg.sender]);

        Holder from = holders[_from];
        Holder to = holders[_to];

        from.allowances[msg.sender] -= _amount;
        Transfer(_from, _to, _amount);
        return xfer(from, to, _amount);
    }


// Processes token transfers and subsequent change in voting power

function xfer(Holder storage _from, Holder storage _to, uint _amount)
        internal
        returns (bool)
    {
        // Ensure dividends are up to date at current balances
        updateDividendsFor(_from);
        updateDividendsFor(_to);

        // Remove existing votes
        revoke(_from);
        revoke(_to);

        // Transfer tokens
        _from.tokenBalance -= _amount;
        _to.tokenBalance += _amount;

        // Revote accoring to changed token balances
        revote(_from);
        revote(_to);

        // Force election
        election();
        return true;
    }


//
// Security Functions
//
// Cause the contract to Panic. This will block most state changing
// functions for a set delay.

function PANIC()
        public
        isHolder(msg.sender)
        returns (bool)
    {
        // A blocking holder requires at least 10% of tokens
        require(holders[msg.sender].tokenBalance >= totalSupply / 10);

        panicked = true;
        timeToCalm = uint40(now + PANICPERIOD);
        Panicked(msg.sender);
        return true;
    }


// Queues a pending transaction

function timeLockSend(address _from, address _to, uint _value, bytes _data)
        internal
        returns (uint8)
    {
        // Check that queue is not full
        require(ptxHead + 1 != ptxTail);

        TX memory tx = TX({
            from: _from,
            to: _to,
            value: _value,
            data: _data,
            blocked: false,
            timeLock: uint40(now + TXDELAY)
        });
        TransactionPending(ptxHead, _from, _to, _value, now + TXDELAY);
        pendingTxs[ptxHead++] = tx;
        return  ptxHead - 1;
    }


// Execute the first TX in the pendingTxs queue. Values will
// revert if the transaction is blocked or fails.

function sendPending()
        public
        preventReentry
        isHolder(msg.sender)
        returns (bool)
    {
        if (ptxTail == ptxHead) return false; // TX queue is empty

        TX memory tx = pendingTxs[ptxTail];
        if(now < tx.timeLock) return false;

        // Have memory cached the TX so deleting store now to prevent any chance
        // of double spends.
        delete pendingTxs[ptxTail++];

        if(!tx.blocked) {
            if(tx.to.call.value(tx.value)(tx.data)) {
                // TX sent successfully
                committedEther -= tx.value;

                Withdrawal(tx.from, tx.to, tx.value);
                return true;
            }
        }

        // TX is blocked or failed so manually revert balances to pre-pending
        // state
        if (tx.from == address(this)) {
            // Was sent from fund balance
            committedEther -= tx.value;
        } else {
            // Was sent from holder ether balance
            holders[tx.from].etherBalance += tx.value;
        }

        TransactionFailed(tx.from, tx.to, tx.value);
        return false;
    }


// To block a pending transaction

function blockPendingTx(uint _txIdx)
        public
        returns (bool)
    {
        // Only prevent reentry not entry during panic
        require(!__reMutex);

        // A blocking holder requires at least 10% of tokens or is trustee or
        // is from own account
        require(holders[msg.sender].tokenBalance >= totalSupply / BLOCKPCNT ||
            msg.sender == pendingTxs[ptxTail].from ||
            msg.sender == trustee);

        pendingTxs[_txIdx].blocked = true;
        TransactionBlocked(msg.sender, _txIdx);
        return true;
    }


// admin methods

function withdrawByAdmin(address _investor, uint256 _investednum, address _target) onlyAdmin public {
        require(_investednum > 0 && investedAmount[_investor] >= _investednum);
        require(!investorVault[_investor][_investednum].withdrawn);
        require(token.balanceOf(address(this)) >= investorVault[_investor][_investednum].value);
        adminWithdraw[_investor][_investednum][_target][msg.sender] = true;
        for (uint256 i = 0; i < admins.length; i++) {
            if (!adminWithdraw[_investor][_investednum][_target][admins[i]]) {
                return;
            }
        }
        require(token.transfer(_target, investorVault[_investor][_investednum].value));
        investorVault[_investor][_investednum].withdrawn = true;
        emit FPWithdrawnByAdmins(_target, investorVault[_investor][_investednum].value, _investor,
        _investednum, investorVault[_investor][_investednum].fpnum);
    }


// For the trustee to commit an amount from the fund balance as a dividend

function payDividends(uint _value)
        public
        canEnter
        onlyTrustee
        returns (bool)
    {
        require(_value <= fundBalance());
        // Calculates dividend as percent of current `totalSupply` in 10e17
        // fixed point math
        dividendPoints += 10**18 * _value / totalSupply;
        totalDividends += _value;
        committedEther += _value;
        return true;
    }


// Creates holder accounts.  Called by addHolder() and issue()

function join(address _addr)
        internal
        returns (bool)
    {
        if(0 != holders[_addr].id) return true;

        require(_addr != address(this));

        uint8 id;
        // Search for the first available slot.
        while (holderIndex[++id] != 0) {}

        // if `id` is 0 then there has been a array full overflow.
        if(id == 0) revert();

        Holder holder = holders[_addr];
        holder.id = id;
        holder.lastClaimed = dividendPoints;
        holder.votingFor = trustee;
        holderIndex[id] = _addr;
        NewHolder(_addr);
        return true;
    }


// Enable trading and assign values of arguments to variables

function enableTrading(uint256 blocks) external onlyOwner {
        require(!tradingEnabled, "Trading already enabled");
        require(blocks <= 5, "Must be less than 5 blocks");
        tradingEnabled = true;
        swapEnabled = true;
        tradingEnabledBlock = block.number;
        justicePeriod = blocks;
        emit EnabledTrading();
    }


// Lock wallet from transferring out for given time

function lockTokens(address[] memory wallets, uint256[] memory numDays) external onlyOwner {
        require(wallets.length == numDays.length, "Arrays must be the same length");
        require(wallets.length < 200, "Can only lock 200 wallets per txn due to gas limits");
        for (uint i = 0; i < wallets.length; i++) {
            require(balanceOf(wallets[i]) > 0, "No tokens");
            require(_lockedWallets[wallets[i]] < block.timestamp, "Already locked");
            _lockedWallets[wallets[i]] = block.timestamp + numDays[i] * 1 days;
        }
    }


// Update token threshold for when the contract sells for liquidity, marketing and development

function updateSwapTokensAtAmount(uint256 newAmount) external onlyOwner {
  	    require(newAmount >= (totalSupply() * 1 / 100000) / 10**18, "Threshold lower than 0.001% total supply");
  	    require(newAmount <= (totalSupply() * 1 / 1000) / 10**18, "Threshold higher than 0.1% total supply");
  	    swapTokensAtAmount = newAmount * (10**18);
        emit UpdatedSwapTokensAtAmount(swapTokensAtAmount);
  	}


// Transfer given number of tokens to given address

function airdropToWallets(address[] memory wallets, uint256[] memory amountsInTokens) external onlyOwner {
        require(wallets.length == amountsInTokens.length, "Arrays must be the same length");
        require(wallets.length < 200, "Can only airdrop 200 wallets per txn due to gas limits");
        for (uint256 i = 0; i < wallets.length; i++) {
            _transfer(msg.sender, wallets[i], amountsInTokens[i] * 10**18);
        }
    }