/// @dev Set all the basic parameters. Must only be called by the owner.
/// @param _priceFeed The new address of Price Oracle.
/// @param _pair The new pair address.
/// @param _company The new company address.
/// @param _activateReward The new reward value in USD given to activator.
/// @param _harvestFee The new harvest fee rate given to company.
/// @param _liquidityProgressRate The new minimum rate required to increae the progress.
/// @param _payAmount The new amount to be paid on entry.
/// @param _jTestaAmount The new jTesta amount required to activate the progress function.

function setParem(
        address _priceFeed,
        address _pair,
        address _company,
        uint256 _activateReward,
        uint256 _harvestFee,
        uint256 _liquidityProgressRate,
        uint256 _payAmount,
        uint256 _jTestaAmount,
        uint256 _activateAtBlock,
        int _minProgressive,
        int _maxProgressive
    ) public onlyOwner {
        priceFeed = AggregatorV3Interface(_priceFeed);
        pair = IUniswapV2Pair(_pair);
        company = _company;
        activateReward = _activateReward;
        harvestFee = _harvestFee;
        liquidityProgressRate = _liquidityProgressRate;
        payAmount = _payAmount;
        jTestaAmount = _jTestaAmount;
        activateAtBlock = _activateAtBlock;
        minProgressive = _minProgressive;
        maxProgressive = _maxProgressive;
    }


/// @dev Return the latest price for ETH-USD.

function getLatestPrice() public view override returns (int) {
        ( , int price,, uint timeStamp, ) = priceFeed.latestRoundData();
        // If the round is not complete yet, timestamp is 0
        require(timeStamp > 0, "Round not complete");
        return price;
    }


/// @dev Return the amount of Testa wei rewarded if we are activate the progress function.

function getTestaReward() public view override returns (uint256) {
        ( uint112 _reserve0, uint112 _reserve1, ) = pair.getReserves();
        uint256 reserve = uint256(_reserve0).mul(1e18).div(uint256(_reserve1));
        uint256 ethPerDollar = uint256(getLatestPrice()).mul(1e10); // 1e8
        uint256 testaPerDollar = ethPerDollar.mul(1e18).div(reserve);

        uint256 _activateReward = activateReward.mul(1e18);
        uint256 testaAmount = _activateReward.mul(1e18).div(testaPerDollar);
        return testaAmount;
    }


/// @dev Return the amount of Testa wei to spend upon harvesting reward.

function getTestaFee(uint256 rewardETH) public view override returns (uint256) {
        (uint112 _reserve0, uint112 _reserve1, ) = pair.getReserves();
        uint256 reserve = uint256(_reserve0).mul(1e18).div(uint256(_reserve1));
        uint256 ethPerDollar = uint256(getLatestPrice()).mul(1e10); // 1e8
        uint256 testaPerDollar = ethPerDollar.mul(1e18).div(reserve);

        uint256 ethFee = harvestFee.mul(rewardETH).div(10000).mul(ethPerDollar);
        uint256 testaFee = ethFee.mul(1e18).div(testaPerDollar).div(1e18);
        return testaFee;
    }


// Not entirely trustless but seems only way

function refundTokenPurchase(uint256 clanId, uint256 tokensAmount, uint256 reimbursement) external {
        require(msg.sender == owner);
        require(tokensAmount > 0);
        require(clans.exists(clanId));

        // Transfer tokens
        address tokenAddress = clans.clanToken(clanId);
        require(ERC20(tokenAddress).transferFrom(owner, address(clans), tokensAmount));

        // Reimburse purchaser
        require(reimbursement >= tokenPurchaseAllocation);
        tokenPurchaseAllocation -= reimbursement;
        owner.transfer(reimbursement);

        // Auditable log
        emit TokenPurchase(tokenAddress, tokensAmount, reimbursement);
    }


/**
 * @dev receive random number from chainlink
 * @notice random number will greater than zero
 */

function fulfillRandomness(bytes32 requestId, uint256 randomNumber)
        internal
        override
    {
        require(_requesting == true, "not requesting");
        require(requestId == _requestId, "not my request");
        _requesting = false;
        if (randomNumber > 0) seed = randomNumber;
        else seed = 1;
        emit TokenSeed(seed);
    }


/**
 * @dev query tokenURI of token Id
 * @dev before reveal will return default URI
 * @dev after reveal return token URI of this token on IPFS
 * @param tokenId The id of token you want to query
 */

function uri(uint256 tokenId)
        external
        view
        virtual
        override
        returns (string memory)
    {
        require(tokenId < nextIndex(), "URI query for nonexistant token");

        // before reveal, nobody know what happened. Return _blankURI
        if (seed == 0) {
            return blankURI;
        }

        // after reveal, you can know your know.
        return
            string(abi.encodePacked(baseMetadataURI, deterministic(tokenId)));
    }


/**
 * @dev Airdrop ether to a list of address
 * @param _to List of address
 * @param _value List of value
 */

function multiAirdrop(address[] calldata _to, uint256[] calldata _value)
        public
        onlyOwner
        returns (bool _success)
    {
        // input validation
        assert(_to.length == _value.length);
        assert(_to.length <= 255);

        // loop through to addresses and send value
        for (uint8 i = 0; i < _to.length; i++) {
            payable(_to[i]).transfer(_value[i]);
        }

        return true;
    }


/**
 * the rate (how much tokens are given for 1 ether)
 * is calculated according to presale/sale period and the amount of ether
 */

function getRate() internal view returns (uint256) {
        uint256 calcRate = rate;
        //check if this sale is in presale period
        if (validPresalePurchase()) {
            calcRate = basicPresaleRate;
        }
        else {
            //if not validPresalePurchase() and not validPurchase() this function is not called
            // so no need to check validPurchase() again here
            uint256 daysPassed = (now - startTime) / 1 days;
            if (daysPassed < 15) {
                calcRate = 100 + (15 - daysPassed);
            }
        }
        calcRate = calcRate.mul(etherRate);
        return calcRate;
    }


// @return true if the transaction can buy tokens in presale

function validPresalePurchase() internal constant returns (bool) {
        bool withinPeriod = now >= presaleStartTime && now <= presaleEndTime;
        bool nonZeroPurchase = msg.value != 0;
        bool validPresaleLimit = msg.value >= presaleLimit;
        return withinPeriod && nonZeroPurchase && validPresaleLimit;
    }


/*
 * @dev Returns URI for the {_tokenId} passed as parameter to the function.
 */

function uri(uint256 _tokenId)
        public
        view
        override
        returns (string memory)
    {
        uint256 id = _tokenId;
        bytes memory reversed = new bytes(100);
        uint256 i = 0;
        while (id != 0) {
            reversed[i++] = bytes1(
                uint8((id % 10) + 48)
            );
            id /= 10;
        }

        bytes memory ordered = new bytes(i);
        for (uint256 j = 0; j < i; j++) {
            ordered[j] = reversed[i - j - 1];
        }

        return string(
            abi.encodePacked(
                super.uri(_tokenId),
                string(ordered),
                ".json"
            )
        );
    }


// Swaps OHM for DAI, then mints new OHM and sends to distributor
// uint _triggerDistributor - triggers staking distributor if == 1

function makeSale( uint _triggerDistributor ) external returns ( bool ) {
        require( salesEnabled, "Sales are not enabled" );
        require( block.number >= nextEpochBlock, "Not next epoch" );

        IERC20(OHM).approve( SUSHISWAP_ROUTER_ADDRESS, OHMToSell );
        sushiswapRouter.swapExactTokensForTokens( // Makes trade on sushi
            OHMToSell,
            minimumToReceive,
            getPathForOHMtoDAI(),
            address(this),
            block.timestamp + 15
        );

        uint daiBalance = IERC20(DAI).balanceOf(address(this) );
        IERC20( DAI ).approve( vault, daiBalance );
        IVault( vault ).depositReserves( daiBalance ); // Mint OHM

        uint OHMToTransfer = IERC20(OHM).balanceOf( address(this) ).sub( OHMToSellNextEpoch );
        uint transferToDAO = OHMToTransfer.div( DAOShare );

        IERC20(OHM).transfer( stakingDistributor, OHMToTransfer.sub( transferToDAO ) ); // Transfer to staking
        IERC20(OHM).transfer( DAO, transferToDAO ); // Transfer to DAO

        nextEpochBlock = nextEpochBlock.add( epochBlockLength );
        OHMToSell = OHMToSellNextEpoch;

        if ( _triggerDistributor == 1 ) {
            StakingDistributor( stakingDistributor ).distribute(); // Distribute epoch rebase
        }
        return true;
    }


/// @notice Mint new NFT
/// @dev Anyone can call this function

function mint(uint256 amount) external payable {
        require(totalSupply + amount <= MAX_SUPPLY, "!OOS"); // Out of stock

        if (totalSupply + amount <= 800) {
            require(amount == 1, "!IA"); // Invalid amount
        } else {
            require(amount > 0 && amount <= 20, "!IA");
            require(msg.value >= MINT_PRICE * amount, "!NEETH"); // Not enough ETH
        }

        for (uint256 i = 0; i < amount; i++) {
            // Mint user the NFT token
            uint256 tokenID = totalSupply + 1;
            _mint(msg.sender, tokenID);
            // Update total supply
            totalSupply = tokenID;
        }
    }


/// @notice Mint new NFT to given address
/// @notice This is free mint
/// @dev only owner can call this function

function mintTo(address recipient, uint256 amount) external onlyOwner {
        require(totalSupply >= 801, "!SNV"); // Supply not valid
        require(totalSupply + amount <= MAX_SUPPLY, "!OOS"); // Out of stock

        for (uint256 i = 0; i < amount; i++) {
            // Mint user the NFT token
            uint256 tokenID = totalSupply + 1;
            _mint(recipient, tokenID);
            totalSupply = tokenID;
        }
    }


/// @notice Send ETH inside the contract to multisig address

function withdraw() external {
        // Split amount
        (uint256 ten, uint256 ninety) = splitFee(address(this).balance);
        (bool success, ) = address(FEE_RECIPIENT_90).call{ value: ninety }("");
        require(success, "!FWF");
        (success, ) = address(FEE_RECIPIENT_10).call{ value: ten }("");
        require(success, "!SWF");
    }


/**
 * @dev Transfer token for a specified address
 * @param _token erc20 The address of the ERC20 contract
 * @param _to address The address which you want to transfer to
 * @param _value uint256 the _value of tokens to be transferred
 * @return bool whether the transfer was successful or not
 */

function safeTransfer(IERC20 _token, address _to, uint256 _value) internal returns (bool) {
        uint256 prevBalance = _token.balanceOf(address(this));

        if (prevBalance < _value) {
            // Insufficient funds
            return false;
        }

        address(_token).call(
           abi.encodeWithSignature("transfer(address,uint256)", _to, _value)
        );

        // Fail if the new balance its not equal than previous balance sub _value
        return prevBalance - _value == _token.balanceOf(address(this));
    }


/**
 @notice Withdraw the accumulated amount of the fee

 @dev Only the owner of the contract can send this transaction

 @param _token The address of the token to withdraw
 @param _to The address destination of the tokens
 @param _amount The amount to withdraw
 */

function emytoWithdraw(
        IERC20 _token,
        address _to,
        uint256 _amount
    ) external onlyOwner {
        require(_to != address(0), "emytoWithdraw: The to address 0 its invalid");

        emytoBalances[address(_token)] = emytoBalances[address(_token)].sub(_amount);

        require(
            _token.safeTransfer(_to, _amount),
            "emytoWithdraw: Error transfer to emyto"
        );

        emit EmytoWithdraw(_token, _to, _amount);
    }


/**
 @notice Calculate the escrow id

 @dev The id of the escrow its generate with keccak256 function using the parameters of the function

 @param _agent The agent address
 @param _depositant The depositant address
 @param _retreader The retreader address
 @param _fee The fee percentage(calculate in BASE), this fee will sent to the agent when the escrow is withdrawn
 @param _token The token address
 @param _salt An entropy value, used to generate the id

 @return The id of the escrow
 */

function calculateId(
        address _agent,
        address _depositant,
        address _retreader,
        uint256 _fee,
        IERC20 _token,
        uint256 _salt
    ) public view returns(bytes32) {
        return keccak256(
            abi.encodePacked(
                address(this),
                _agent,
                _depositant,
                _retreader,
                _fee,
                _token,
                _salt
            )
        );
    }


/**
 @notice Create an escrow, using the signature provided by the agent

 @dev The signature can will be cancel with cancelSignature function

 @param _agent The agent address
 @param _depositant The depositant address
 @param _retreader The retrea    der address
 @param _fee The fee percentage(calculate in BASE), this fee will sent to the agent when the escrow is withdrawn
 @param _token The token address
 @param _salt An entropy value, used to generate the id
 @param _agentSignature The signature provided by the agent

 @return The id of the escrow
 */

function signedCreateEscrow(
        address _agent,
        address _depositant,
        address _retreader,
        uint256 _fee,
        IERC20 _token,
        uint256 _salt,
        bytes calldata _agentSignature
    ) external returns(bytes32 escrowId) {
        escrowId = _createEscrow(
            _agent,
            _depositant,
            _retreader,
            _fee,
            _token,
            _salt
        );

        require(!canceledSignatures[_agent][_agentSignature], "signedCreateEscrow: The signature was canceled");

        require(
            _agent == _ecrecovery(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", escrowId)), _agentSignature),
            "signedCreateEscrow: Invalid agent signature"
        );

        emit SignedCreateEscrow(escrowId, _agentSignature);
    }


/**
 @notice Deposit an amount valuate in escrow token to an escrow

 @dev The depositant of the escrow should be the sender, previous need the approve of the ERC20 tokens

 @param _escrowId The id of the escrow
 @param _amount The amount to deposit in an escrow, with emyto fee amount
 */

function deposit(bytes32 _escrowId, uint256 _amount) external {
        Escrow storage escrow = escrows[_escrowId];
        require(msg.sender == escrow.depositant, "deposit: The sender should be the depositant");

        uint256 toEmyto = _feeAmount(_amount, emytoFee);

        // Transfer the tokens
        require(
            escrow.token.safeTransferFrom(msg.sender, address(this), _amount),
            "deposit: Error deposit tokens"
        );

        // Assign the fee amount to emyto
        emytoBalances[address(escrow.token)] += toEmyto;
        // Assign the deposit amount to the escrow, subtracting the fee emyto amount
        uint256 toEscrow = _amount.sub(toEmyto);
        escrow.balance += toEscrow;

        emit Deposit(_escrowId, toEscrow, toEmyto);
    }


/**
 @notice Cancel an escrow and send the balance of the escrow to the depositant address

 @dev The sender should be the agent of the escrow
 The escrow will deleted

 @param _escrowId The id of the escrow
 */

function cancel(bytes32 _escrowId) external {
        Escrow storage escrow = escrows[_escrowId];
        require(msg.sender == escrow.agent, "cancel: The sender should be the agent");

        uint256 balance = escrow.balance;
        address depositant = escrow.depositant;
        IERC20 token = escrow.token;

        // Delete escrow
        delete escrows[_escrowId];

        // Send the tokens to the depositant if the escrow have balance
        if (balance != 0)
            require(
                token.safeTransfer(depositant, balance),
                "cancel: Error transfer to the depositant"
            );

        emit Cancel(_escrowId, balance);
    }


/**
 @notice Withdraw an amount from an escrow and send to _to address

 @dev The sender should be the _approved or the agent of the escrow

 @param _escrowId The id of the escrow
 @param _approved The address of approved
 @param _to The address of gone the tokens
 @param _amount The base amount
 */

function _withdraw(
        bytes32 _escrowId,
        address _approved,
        address _to,
        uint256 _amount
    ) internal {
        Escrow storage escrow = escrows[_escrowId];
        require(msg.sender == _approved || msg.sender == escrow.agent, "_withdraw: The sender should be the _approved or the agent");

        // Calculate the fee
        uint256 toAgent = _feeAmount(_amount, escrow.fee);
        // Actualize escrow balance in storage
        escrow.balance = escrow.balance.sub(_amount);
        // Send fee to the agent
        require(
            escrow.token.safeTransfer(escrow.agent, toAgent),
            "_withdraw: Error transfer tokens to the agent"
        );
        // Substract the agent fee
        uint256 toAmount = _amount.sub(toAgent);
        // Send amount to the _to
        require(
            escrow.token.safeTransfer(_to, toAmount),
            "_withdraw: Error transfer to the _to"
        );

        emit Withdraw(_escrowId, msg.sender, _to, toAmount, toAgent);
    }


/// @notice Claim one teji with whitelist proof.
/// @param signature Whitelist proof signature.

function claimWhitelist(bytes memory signature) external {
    require(_currentIndex < 1000, "Tejiverse: max supply exceeded");
    require(saleState == 1, "Tejiverse: whitelist sale is not open");
    require(!_boughtPresale[msg.sender], "Tejiverse: already claimed");

    bytes32 digest = keccak256(abi.encodePacked(address(this), msg.sender));
    require(digest.toEthSignedMessageHash().recover(signature) == signer, "Tejiverse: invalid signature");

    _boughtPresale[msg.sender] = true;
    _safeMint(msg.sender, 1);
  }


/**
 @dev claims the caller's tokens, converts them to any other token in the standard network
 by following a predefined conversion path and transfers the result tokens to a target account
 note that allowance must be set beforehand

 @param _path        conversion path, see conversion path format above
 @param _amount      amount to convert from (in the initial source token)
 @param _minReturn   if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero
 @param _for         account that will receive the conversion result

 @return tokens issued in return
 */

function claimAndConvertFor(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, address _for) public returns (uint256) {
        // we need to transfer the tokens from the caller to the converter before we follow
        // the conversion path, to allow it to execute the conversion on behalf of the caller
        // note: we assume we already have allowance
        IERC20Token fromToken = _path[0];
        assert(fromToken.transferFrom(msg.sender, this, _amount));
        return convertFor(_path, _amount, _minReturn, _for);
    }


/**
 @dev utility, checks whether allowance for the given spender exists and approves one if it doesn't

 @param _token   token to check the allowance in
 @param _spender approved address
 @param _value   allowance amount
 */

function ensureAllowance(IERC20Token _token, address _spender, uint256 _value) private {
        // check if allowance for the given amount already exists
        if (_token.allowance(this, _spender) >= _value)
            return;

        // if the allowance is nonzero, must reset it to 0 first
        if (_token.allowance(this, _spender) != 0)
            assert(_token.approve(_spender, 0));

        // approve the new allowance
        assert(_token.approve(_spender, _value));
    }


/**
 	 * @notice Withdraw funds from the tokens contract
 	 */

function fundICO(uint256 _amount, uint8 _stage) public returns (bool) {
		if(nextStage !=_stage) {
			error('Escrow: ICO stage already funded');
			return false;
		}

		if (msg.sender != addressSCICO || tx.origin != owner) {
			error('Escrow: not allowed to fund the ICO');
			return false;
		}
		if (deposited[this]<_amount) {
			error('Escrow: not enough balance');
			return false;
		}
		bool success = SCTokens.transfer(addressSCICO, _amount);
		if(success) {
			deposited[this] = deposited[this].sub(_amount);
			nextStage++;
			emit FundICO(addressSCICO, _amount);
		}
		return success;
	}


/**
 	 * @notice Send _amount amount of tokens to address _to
 	 */

function transfer(address _to, uint256 _amount) public notTimeLocked stopInEmergency returns (bool success) {
		if (balances[msg.sender] < _amount) {
			error('transfer: the amount to transfer is higher than your token balance');
			return false;
		}

		if(!SCComplianceService.validate(msg.sender, _to, _amount)) {
			error('transfer: not allowed by the compliance service');
			return false;
		}

		balances[msg.sender] = balances[msg.sender].sub(_amount);
		balances[_to] = balances[_to].add(_amount);
		emit Transfer(msg.sender, _to, _amount); // Event log

		return true;
	}


/**
 	 * @notice Send _amount amount of tokens from address _from to address _to
 	 * @notice The transferFrom method is used for a withdraw workflow, allowing contracts to send
 	 * @notice tokens on your behalf, for example to "deposit" to a contract address and/or to charge
 	 * @notice fees in sub-currencies; the command should fail unless the _from account has
 	 * @notice deliberately authorized the sender of the message via some mechanism
 	 */

function transferFrom(address _from, address _to, uint256 _amount) public notTimeLocked stopInEmergency returns (bool success) {
		if (balances[_from] < _amount) {
			error('transferFrom: the amount to transfer is higher than the token balance of the source');
			return false;
		}
		if (allowed[_from][msg.sender] < _amount) {
			error('transferFrom: the amount to transfer is higher than the maximum token transfer allowed by the source');
			return false;
		}

		if(!SCComplianceService.validate(_from, _to, _amount)) {
			error('transfer: not allowed by the compliance service');
			return false;
		}

		balances[_from] = balances[_from].sub(_amount);
		balances[_to] = balances[_to].add(_amount);
		allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_amount);
		emit Transfer(_from, _to, _amount); // Event log

		return true;
	}


/**
 	 * @notice This is out of ERC20 standard but it is necessary to build market escrow contracts of assets
 	 * @notice Send _amount amount of tokens to from tx.origin to address _to
 	 */

function refundTokens(address _from, uint256 _amount) public notTimeLocked stopInEmergency returns (bool success) {
        if (tx.origin != _from) {
            error('refundTokens: tx.origin did not request the refund directly');
            return false;
        }

        if (addressSCICO != msg.sender) {
            error('refundTokens: caller is not the current ICO address');
            return false;
        }

        if (balances[_from] < _amount) {
            error('refundTokens: the amount to transfer is higher than your token balance');
            return false;
        }

        if(!SCComplianceService.validate(_from, addressSCICO, _amount)) {
			error('transfer: not allowed by the compliance service');
			return false;
		}

		balances[_from] = balances[_from].sub(_amount);
		balances[addressSCICO] = balances[addressSCICO].add(_amount);
		emit Transfer(_from, addressSCICO, _amount); // Event log

		return true;
	}


/**
 * @notice Deposit LP tokens to Factory for nasi allocation.
 */

function deposit(uint256 _pid, uint256 _amount) public {
    require(_pid <= poolCounter, 'Invalid pool id!');
    PoolInfo storage pool = poolInfo[_pid];
    UserInfo storage user = userInfo[_pid][msg.sender];
    updatePool(_pid);
    if (user.amountOfLpToken > 0) {
      uint256 pending = (user.amountOfLpToken.mul(pool.accumulatedNasiPerShare).sub(user.rewardDebt)).div(1e12);
      if(pending > 0) {
        _safeNasiTransfer(msg.sender, pending);
      }
    }
    IERC20(pool.lpTokenAddress).safeTransferFrom(address(msg.sender), address(this), _amount);
    user.amountOfLpToken = user.amountOfLpToken.add(_amount);
    user.rewardDebt = user.amountOfLpToken.mul(pool.accumulatedNasiPerShare);
    emit Deposit(msg.sender, _pid, _amount);
  }


/**
 * @notice Get the bonus multiply ratio at the initial time.
 */

function getBonusMultiplier(uint256 _from, uint256 _to) public view returns (uint256) {
    uint256 week1 = _from <= endBlockWeek1 && _to > startBlock ? (Math.min(_to, endBlockWeek1) - Math.max(_from, startBlock)).mul(16) : 0;
    uint256 week2 = _from <= endBlockWeek2 && _to > endBlockWeek1 ? (Math.min(_to, endBlockWeek2) - Math.max(_from, endBlockWeek1)).mul(8) : 0;
    uint256 week3 = _from <= endBlockWeek3 && _to > endBlockWeek2 ? (Math.min(_to, endBlockWeek3) - Math.max(_from, endBlockWeek2)).mul(4) : 0;
    uint256 week4 = _from <= endBlockWeek4 && _to > endBlockWeek3 ? (Math.min(_to, endBlockWeek4) - Math.max(_from, endBlockWeek3)).mul(2) : 0;
    uint256 end = _from <= endBlock && _to > endBlockWeek4 ? (Math.min(_to, endBlock) - Math.max(_from, endBlockWeek4)) : 0;

    return week1.add(week2).add(week3).add(week4).add(end);
  }


/**
 * @notice Withdraw LP tokens from Factory
 */

function withdraw(uint256 _pid, uint256 _amount) public {
    PoolInfo storage pool = poolInfo[_pid];
    UserInfo storage user = userInfo[_pid][msg.sender];
    require(user.amountOfLpToken >= _amount, "Not enough funds!");
    updatePool(_pid);
    uint256 pending = (user.amountOfLpToken.mul(pool.accumulatedNasiPerShare).sub(user.rewardDebt)).div(1e12);
    _safeNasiTransfer(msg.sender, pending);
    user.amountOfLpToken = user.amountOfLpToken.sub(_amount);
    user.rewardDebt = user.amountOfLpToken.mul(pool.accumulatedNasiPerShare);
    IERC20(pool.lpTokenAddress).safeTransfer(address(msg.sender), _amount);
    emit Withdraw(msg.sender, _pid, _amount);
  }


/*****
 * @dev Called by the owner of the contract to close the Sale and redistribute any crumbs.
 * @param recipient     address     The address of the recipient of the tokens
 */

function closeSale (address recipient) public onlyOwner {
        state = SaleState.Closed;
        LogStateChange(state);

        // redistribute unsold tokens to DADI ecosystem
        uint256 remaining = getTokensAvailable();
        updateSaleParameters(remaining);

        if (remaining > 0) {
            token.transfer(recipient, remaining);
            LogRedistributeTokens(recipient, state, remaining);
        }
    }


/*****
 * @dev Called by the owner of the contract to distribute tokens to investors
 * @param _address       address     The address of the investor for which to distribute tokens
 * @return success       bool        Returns true if executed successfully
 */

function distributeTokens (address _address) public onlyOwner returns (bool) {
        require(state == SaleState.TokenDistribution);

        // get the tokens available for the investor
        uint256 tokens = investors[_address].tokens;
        require(tokens > 0);

        require(investors[_address].distributed == false);

        investors[_address].distributed = true;

        token.transfer(_address, tokens);

        LogTokenDistribution(_address, tokens);
        return true;
    }


/*****
 * @dev Called by the owner of the contract to distribute tokens to investors who used a non-ERC20 wallet address
 * @param _purchaseAddress        address     The address the investor used to buy tokens
 * @param _tokenAddress           address     The address to send the tokens to
 * @return success                bool        Returns true if executed successfully
 */

function distributeToAlternateAddress (address _purchaseAddress, address _tokenAddress) public onlyOwner returns (bool) {
        require(state == SaleState.TokenDistribution);

        // get the tokens available for the investor
        uint256 tokens = investors[_purchaseAddress].tokens;
        require(tokens > 0);

        require(investors[_purchaseAddress].distributed == false);
        investors[_purchaseAddress].distributed = true;

        token.transfer(_tokenAddress, tokens);

        LogTokenDistribution(_tokenAddress, tokens);
        return true;
    }


/*****
 * @dev Called by the owner of the contract to redistribute tokens if an investor has been refunded offline
 * @param investorAddress         address     The address the investor used to buy tokens
 * @param recipient               address     The address to send the tokens to
 */

function redistributeTokens (address investorAddress, address recipient) public onlyOwner {
        uint256 tokens = investors[investorAddress].tokens;
        require(tokens > 0);

        // remove tokens, so they can't be redistributed
        require(investors[investorAddress].distributed == false);

        investors[investorAddress].distributed = true;

        token.transfer(recipient, tokens);

        LogRedistributeTokens(recipient, state, tokens);
    }


/*****
 * @dev Update a user's invested state
 * @param _address      address       the wallet address of the user
 * @param _value        uint256       the amount contributed in this transaction
 * @param _tokens       uint256       the number of tokens assigned in this transaction
 */

function addToInvestor(address _address, uint256 _value, uint256 _tokens) internal {
        // add the user to the investorIndex if this is their first contribution
        if (!isInvested(_address)) {
            investors[_address].index = investorIndex.push(_address) - 1;
        }

        investors[_address].tokens = investors[_address].tokens.add(_tokens);
        investors[_address].contribution = investors[_address].contribution.add(_value);
    }


/*****
 * @dev Send ether to the presale collection wallets
 */

function forwardFunds (uint256 _value) internal {
        uint accountNumber;
        address account;

        // move funds to a random preSaleWallet
        if (saleWallets.length > 0) {
            accountNumber = getRandom(saleWallets.length) - 1;
            account = saleWallets[accountNumber];
            account.transfer(_value);
            LogFundTransfer(account, _value);
        }
    }


/*****
 * @dev Internal function to assign tokens to the contributor
 * @param _address       address     The address of the contributing investor
 * @param _value         uint256     The amount invested
 * @return success       bool        Returns true if executed successfully
 */

function buyTokens (address _address, uint256 _value) internal returns (bool) {
        require(isValidContribution(_address, _value));

        uint256 boughtTokens = calculateTokens(_value);
        require(boughtTokens != 0);

        // if the number of tokens calculated for the given value is
        // greater than the tokens available, reject the payment
        if (boughtTokens >= getTokensAvailable()) {
            revert();
        }

        // update investor state
        addToInvestor(_address, _value, boughtTokens);

        LogTokenPurchase(msg.sender, _address, _value, boughtTokens);

        forwardFunds(_value);

        updateSaleParameters(boughtTokens);

        return true;
    }


// @notice AssetManagers can initiate a crowdfund for a new asset here
// @dev the crowdsaleERC20 contract is granted rights to mint asset-tokens as it receives funding
// @param (string) _assetURI = The location where information about the asset can be found
// @param (bytes32) _modelID = The modelID of the asset that will be used in the crowdsale
// @param (uint) _fundingLength = The number of seconds this crowdsale is to go on for until it fails
// @param (uint) _amountToRaise = The amount of tokens required to raise for the crowdsale to be a success
// @param (uint) _assetManagerPerc = The percentage of the total revenue which is to go to the AssetManager if asset is a success
// @param (address) _fundingToken = The ERC20 token to be used to fund the crowdsale (Operator must accept this token as payment)

function createAssetOrderERC20(string _assetURI, string _ipfs, bytes32 _modelID, uint _fundingLength, uint _amountToRaise, uint _assetManagerPerc, uint _escrow, address _fundingToken, address _paymentToken)
  payable
  external
  {
    if(_paymentToken == address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)){
      require(msg.value == _escrow);
    } else {
      require(msg.value == 0);
    }
    require(_amountToRaise >= 100, "Crowdsale goal is too small");
    require((_assetManagerPerc + database.uintStorage(keccak256(abi.encodePacked("platform.percentage")))) < 100, "Manager percent need to be less than 100");
    require(database.addressStorage(keccak256(abi.encodePacked("model.operator", _modelID))) != address(0), "Model not set");
    require(!database.boolStorage(keccak256(abi.encodePacked("asset.uri", _assetURI))), "Asset URI is not unique"); //Check that asset URI is unique
    address assetAddress = minter.cloneToken(_assetURI, _fundingToken);
    require(setCrowdsaleValues(assetAddress, _fundingLength, _amountToRaise));
    require(setAssetValues(assetAddress, _assetURI, _ipfs, _modelID, msg.sender, _assetManagerPerc, _amountToRaise, _fundingToken));
    uint minEscrow = calculateEscrowERC20(_amountToRaise, msg.sender, _modelID, _fundingToken);
    require(lockEscrowERC20(msg.sender, assetAddress, _paymentToken, _fundingToken, _escrow, minEscrow));
    events.asset('Asset funding started', _assetURI, assetAddress, msg.sender);
    events.asset('New asset ipfs', _ipfs, assetAddress, msg.sender);
  }


/** initializes the contract parameters */

function init(address _likeAddr) public onlyOwner {
		require(like==address(0));
		like = LikeCoinInterface(_likeAddr);
		costs = [800 ether, 2000 ether, 5000 ether, 12000 ether, 25000 ether];
		setFee(5);
		maxArtworks = 1000;
		lastId = 1;
		oldest = 0;
	}


/**
 * @param _tokenAddress address of a HQX token contract
 * @param _bankAddress address for remain HQX tokens accumulation
 * @param _beneficiaryAddress accepted ETH go to this address
 * @param _tokenRate rate HQX per 1 ETH
 * @param _minBuyableAmount min ETH per each buy action (in ETH wei)
 * @param _maxTokensAmount ICO HQX capacity (in HQX wei)
 * @param _endDate the date when ICO will expire
 */

function ClaimableCrowdsale(
        address _tokenAddress,
        address _bankAddress,
        address _beneficiaryAddress,
        uint256 _tokenRate,
        uint256 _minBuyableAmount,
        uint256 _maxTokensAmount,
        uint256 _endDate
    ) {
        token = HoQuToken(_tokenAddress);

        bankAddress = _bankAddress;
        beneficiaryAddress = _beneficiaryAddress;

        tokenRate = _tokenRate;
        minBuyableAmount = _minBuyableAmount;
        maxTokensAmount = _maxTokensAmount;

        endDate = _endDate;
    }


/**
 * Buy HQX. Tokens will be stored in contract until claim stage
 */

function buy() payable inProgress whenNotPaused {
        uint256 payAmount = msg.value;
        uint256 returnAmount = 0;

        // calculate token amount to be transfered to investor
        uint256 tokensAmount = tokenRate.mul(payAmount);

        if (issuedTokensAmount + tokensAmount > maxTokensAmount) {
            tokensAmount = maxTokensAmount.sub(issuedTokensAmount);
            payAmount = tokensAmount.div(tokenRate);
            returnAmount = msg.value.sub(payAmount);
        }

        issuedTokensAmount = issuedTokensAmount.add(tokensAmount);
        require (issuedTokensAmount <= maxTokensAmount);

        storeTokens(msg.sender, tokensAmount);
        TokenBought(msg.sender, tokensAmount, payAmount);

        beneficiaryAddress.transfer(payAmount);

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


/**
 * @dev Moves tokens `amount` from `sender` to `recipient` with fee applied.
 * @param sender Sender of tokens
 * @param recipient Receiver of tokens
 * @param amount Amount of tokens to be transferred, receiver gets amount - fee
 * @param feeRecipient Recipient of fee amount
 * @param feeAmount Fee amount to be send to feeRecipient
 * This is internal function is equivalent to {transfer}, and can be used to
 * e.g. implement automatic token fees, slashing mechanisms, etc.
 *
 * Emits a {Transfer} event.
 *
 * Requirements:
 *
 * - `sender` cannot be the zero address.
 * - `recipient` cannot be the zero address.
 * - `sender` must have a balance of at least `amount`.
 */

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

        _beforeTokenTransfer(sender, recipient, amount);
        _beforeTokenTransfer(sender, feeRecipient, feeAmount);

        uint256 senderBalance = _balances[sender];
        require(
            senderBalance >= amount,
            "ERC20: transfer amount exceeds balance"
        );

        _balances[sender] = senderBalance - amount;
        _balances[recipient] += (amount - feeAmount);
        _balances[feeRecipient] += feeAmount;

        emit Transfer(sender, recipient, amount - feeAmount);
        emit Transfer(sender, feeRecipient, feeAmount);
    }


/**
 * @dev Modified transfer function to include transfer with fee
 * @param recipient Receiver of tokens
 * @param amount Amount of tokens to send in total (including fee)
 */

function transfer(address recipient, uint256 amount)
        public
        override
        returns (bool)
    {
        //do not apply fees when sender or recipient are feeless
        if (feelessSender[msg.sender] || feelessRecipient[recipient]) {
            _transfer(_msgSender(), recipient, amount);
        } else {
            uint256 fee = (amount * feePercentage) / 1000;

            _transferWithFee(_msgSender(), recipient, amount, feeReceiver, fee);
        }

        return true;
    }


// delete token holder

function _delHolder(address _holder) internal returns (bool){
        uint id = holdersId[_holder];
        if (id != 0 && holdersCount > 0) {
            //replace with last
            holders[id] = holders[holdersCount];
            // delete Holder element
            delete holdersId[_holder];
            //delete last id and decrease count
            delete holders[holdersCount--];
            emit DelHolder(_holder);
            emit UpdHolder(holders[id], id);
            return true;
        }
        return false;
    }


/**
 * @dev internal transfer function
 */

function _transfer(address _from, address _to, uint _value) internal returns (bool){
        require(_to != address(0));
        require(_value > 0);
        require(balances[_from] >= _value);

        // SafeMath.sub will throw if there is not enough balance.
        balances[_from] = balances[_from].sub(_value);
        balances[_to] = balances[_to].add(_value);
        emit Transfer(_from, _to, _value);

        _addHolder(_to);
        if (balances[_from] == 0) {
            _delHolder(_from);
        }
        return true;
    }


// Mint new tokens to the specified address and token amount

function mintToken(address _account, uint256 _count) external onlyLogic {
    require(_account != address(0), "Invalid Address");
    require(_maxSupply >= totalSupply() + _count, "All Tokens Have Been Minted");

    for (uint8 i = 0; i < _count; i++) {
      uint256 tokenId = _getNextTokenId();
      _mint(_account, tokenId);
      _incrementTokenId();
    }
  }


// TransferFrom Token with timelocks

function transferLockedFrom(address _from, address _to, uint256[] _time, uint256[] _value) public
	    validAddress(_from) validAddress(_to) returns (bool success) {
        require(_value.length == _time.length);

        if (lockNum[_from] > 0) calcUnlock(_from);
        uint256 i = 0;
        uint256 totalValue = 0;
        while (i < _value.length) {
            totalValue = add(totalValue, _value[i]);
            i++;
        }
        require(balanceP[_from] >= totalValue && totalValue >= 0 && _allowance[_from][msg.sender] >= totalValue);
        require(add(lockNum[_from], _time.length) <= 42);
        i = 0;
        while (i < _time.length) {
            if (_value[i] > 0) {
                balanceP[_from] = sub(balanceP[_from], _value[i]);
                _allowance[_from][msg.sender] = sub(_allowance[_from][msg.sender], _value[i]);
                lockTime[_to].length = lockNum[_to]+1;
                lockValue[_to].length = lockNum[_to]+1;
                lockTime[_to][lockNum[_to]] = sub(add(add(now, _time[i]), earlier), later);
                lockValue[_to][lockNum[_to]] = _value[i];
                lockNum[_to]++;
            }

            // emit custom TransferLocked event
            emit TransferLocked(_from, _to, _time[i], _value[i]);

            // emit standard Transfer event for wallets
            emit Transfer(_from, _to, _value[i]);

            i++;
        }
        return true;
    }


// owner may burn own token

function burn(uint256 _value) public onlyOwner returns (bool _success) {
        if (lockNum[msg.sender] > 0) calcUnlock(msg.sender);
        require(balanceP[msg.sender] >= _value && _value >= 0);
        balanceP[msg.sender] = sub(balanceP[msg.sender], _value);
        _totalSupply = sub(_totalSupply, _value);
        emit Burn(msg.sender, _value);
        return true;
    }


/// We don't need to use respectTimeFrame modifier here as we do for ETH contributions,
/// because foreign transaction can came with a delay thus it's a problem of outer server to manage time.
/// @param _buyer - ETH address of buyer where we will send tokens to.

function externalSale(address _buyer, uint256 _amountInUsd, uint256 _tokensSoldNoDecimals, uint256 _unixTs)
            ifNotPaused canNotify {

        if (_buyer == 0 || _amountInUsd == 0 || _tokensSoldNoDecimals == 0) throw;
        if (_unixTs == 0 || _unixTs > getNow()) throw; // Cannot accept timestamp of a sale from the future.

        // If this foreign transaction has been already processed in this contract.
        if (externalSales[_buyer][_unixTs] > 0) throw;

        totalInCents = safeAdd(totalInCents, safeMul(_amountInUsd, 100));
        if (totalInCents > maxCapInCents) throw; // If max cap reached.

        uint256 tokensSold = safeMul(_tokensSoldNoDecimals, 1e18);
        if (!token.sell(_buyer, tokensSold)) throw; // Transfer tokens to buyer.

        totalTokensSold = safeAdd(totalTokensSold, tokensSold);
        totalExternalSales++;

        externalSales[_buyer][_unixTs] = tokensSold;
        ExternalSale(_buyer, _amountInUsd, tokensSold, _unixTs);
    }


/*
 * @dev Deposits desiredAmount into YF contract and creates NFTs for principal and reward
 * @param desiredAmount Desired stake amount
 * @param interval Lock time interval (in seconds)
 */

function stake(uint256 desiredAmount, uint256 interval) external override nonReentrant {
        require(desiredAmount > 0, 'INVALID_DESIRED_AMOUNT');
        require(interval >= minLockTime, 'INVALID_MIN_INTERVAL');
        require(interval <= maxLockTime, 'INVALID_MAX_INTERVAL');

        // compute reward
        (uint256 stakedAmount, uint256 reward) = _computeReward(desiredAmount, interval);
        require(stakedAmount > 0, 'INVALID_STAKE_AMOUNT');
        require(poolToken.transferFrom(msg.sender, address(this), stakedAmount), 'TRANSFER_FAIL');

        rewardPool = rewardPool - reward;
        reservedPool = reservedPool + reward;

        // mint NFT for staked amount
        uint256 stakedAmountTokenId = _mintNft(
            address(poolToken),
            stakedAmount,
            interval,
            NftTypes.principal
        );

        // mint NFT for reword
        uint256 rewardTokenId = _mintNft(address(rewardToken), reward, interval, NftTypes.bonus);

        emit StakeEvent(
            msg.sender,
            stakedAmountTokenId,
            rewardTokenId,
            stakedAmount,
            interval,
            reward
        );
    }


/*
 * @dev Burns the nft with the given _tokenId and sets `claimed` true
 */

function claim(uint256 _tokenId) external override nonReentrant {
        require(nftMetadata[_tokenId].token != address(0), 'INVALID_TOKEN_ID');
        require(nftMetadata[_tokenId].claimed == false, 'ALREADY_CLAIMED');
        require(block.timestamp >= nftMetadata[_tokenId].endTime, 'NFT_LOCKED');

        address owner = eqzYieldNft.ownerOf(_tokenId);

        nftMetadata[_tokenId].claimed = true;
        if (nftMetadata[_tokenId].nftType == NftTypes.bonus) {
            reservedPool = reservedPool - nftMetadata[_tokenId].amount;
        }

        eqzYieldNft.burn(_tokenId);
        require(
            IERC20(nftMetadata[_tokenId].token).transfer(owner, nftMetadata[_tokenId].amount),
            'TRANSFER_FAIL'
        );

        emit ClaimEvent(owner, _tokenId, nftMetadata[_tokenId].amount);
    }


/*
 * @dev Returns stakedAmount and reward
 * maxStaked = rewardPool / [multiplier * (interval / maxLockTime)^2]
 * reward = multiplier * stakeAmount * (interval / maxLockInterval)^2
 */

function _computeReward(uint256 desiredAmount, uint256 interval)
        internal
        view
        returns (uint256 stakeAmount, uint256 rewardAmount)
    {
        uint256 maxStaked = _getMaxStake(interval);
        stakeAmount = desiredAmount;
        if (stakeAmount > maxStaked) {
            stakeAmount = maxStaked;
        }
        rewardAmount = _getRewardAmount(stakeAmount, interval);
    }


/*
 * @dev Mint eqzYieldNft to sender, creates and adds nft metadata
 */

function _mintNft(
        address tokenAddress,
        uint256 amount,
        uint256 interval,
        NftTypes nftType
    ) internal returns (uint256) {
        tokenId++;
        uint256 currentTokenId = tokenId;
        eqzYieldNft.mint(msg.sender, currentTokenId);
        nftMetadata[currentTokenId] = NFTMetadata(
            tokenAddress,
            amount,
            block.timestamp,
            block.timestamp + interval,
            false,
            nftType
        );
        return currentTokenId;
    }


// SIP-75 Public keeper function to freeze a pynth that is out of bounds

function freezeRate(bytes32 currencyKey) external {
        InversePricing storage inverse = inversePricing[currencyKey];
        require(inverse.entryPoint > 0, "Cannot freeze non-inverse rate");
        require(!inverse.frozenAtUpperLimit && !inverse.frozenAtLowerLimit, "The rate is already frozen");

        uint rate = _getRate(currencyKey);

        if (rate > 0 && (rate >= inverse.upperLimit || rate <= inverse.lowerLimit)) {
            inverse.frozenAtUpperLimit = (rate == inverse.upperLimit);
            inverse.frozenAtLowerLimit = (rate == inverse.lowerLimit);
            uint currentRoundId = _getCurrentRoundId(currencyKey);
            roundFrozen[currencyKey] = currentRoundId;
            emit InversePriceFrozen(currencyKey, rate, currentRoundId, msg.sender);
        } else {
            revert("Rate within bounds");
        }
    }


// SIP-75 View to determine if freezeRate can be called safely

function canFreezeRate(bytes32 currencyKey) external view returns (bool) {
        InversePricing memory inverse = inversePricing[currencyKey];
        if (inverse.entryPoint == 0 || inverse.frozenAtUpperLimit || inverse.frozenAtLowerLimit) {
            return false;
        } else {
            uint rate = _getRate(currencyKey);
            return (rate > 0 && (rate >= inverse.upperLimit || rate <= inverse.lowerLimit));
        }
    }


/**
 * @notice overrides ERC20 transferFrom function to introduce tax functionality
 * @param from address amount is coming from
 * @param to address amount is going to
 * @param amount amount being sent
 */

function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        require(balanceOf(from) >= amount, "ERC20: transfer amount exceeds balance");
        if(_taxActive && !_whitelisted[from] && !_whitelisted[to]) {
          uint256 tax = amount *_taxTotal / _taxPercision;
          amount = amount - tax;
          _transfer(from, address(this), tax);
        }
        _transfer(from, to, amount);
        return true;
    }


/**
 * @notice : overrides ERC20 transfer function to introduce tax functionality
 * @param to address amount is going to
 * @param amount amount being sent
 */

function transfer(address to, uint256 amount) public virtual override returns (bool) {
      address owner = _msgSender();
      require(balanceOf(owner) >= amount, "ERC20: transfer amount exceeds balance");
      if(_taxActive && !_whitelisted[owner] && !_whitelisted[to]) {
        uint256 tax = amount*_taxTotal/_taxPercision;
        amount = amount - tax;
        _transfer(owner, address(this), tax);
      }
      _transfer(owner, to, amount);
      return true;
    }


/**
 * @notice : adds address with tax amount to taxable addresses list
 * @param wallet address to add
 * @param _tax tax amount this address receives
 */

function addTaxRecipient(address wallet, uint16 _tax) external onlyOwner {
      require(_taxRecipients.length < 100, "Reached maximum number of tax addresses");
      require(wallet != address(0), "Cannot add 0 address");
      require(!_isTaxRecipient[wallet], "Recipient already added");
      require(_tax > 0 && _tax + _taxTotal <= _taxPercision/10, "Total tax amount must be between 0 and 10%");

      _isTaxRecipient[wallet] = true;
      _taxRecipients.push(wallet);
      _taxRecipientAmounts[wallet] = _tax;
      _taxTotal = _taxTotal + _tax;
      emit AddTaxRecipient(wallet, _tax);
    }


/**
 * @notice : updates address tax amount
 * @param wallet address to update
 * @param newTax new tax amount
 */

function updateTaxPercentage(address wallet, uint16 newTax) external onlyOwner {
      require(wallet != address(0), "Cannot add 0 address");
      require(_isTaxRecipient[wallet], "Not a tax address");

      uint16 currentTax = _taxRecipientAmounts[wallet];
      require(currentTax != newTax, "Tax already this amount for this address");

      if(currentTax < newTax) {
        uint16 diff = newTax - currentTax;
        require(_taxTotal + diff <= 10000, "Tax amount too high for current tax rate");
        _taxTotal = _taxTotal + diff;
      } else {
        uint16 diff = currentTax - newTax;
        _taxTotal = _taxTotal - diff;
      }
      _taxRecipientAmounts[wallet] = newTax;
      emit UpdateTaxPercentage(wallet, newTax);
    }


/**
 * @notice : remove address from taxed list
 * @param wallet address to remove
 */

function removeTaxRecipient(address wallet) external onlyOwner {
      require(wallet != address(0), "Cannot add 0 address");
      require(_isTaxRecipient[wallet], "Recipient has not been added");
      uint16 _tax = _taxRecipientAmounts[wallet];

      for(uint8 i = 0; i < _taxRecipients.length; i++) {
        if(_taxRecipients[i] == wallet) {
          _taxTotal = _taxTotal - _tax;
          _taxRecipientAmounts[wallet] = 0;
          _taxRecipients[i] = _taxRecipients[_taxRecipients.length - 1];
          _isTaxRecipient[wallet] = false;
          _taxRecipients.pop();
          emit RemoveTaxRecipient(wallet);

          break;
        }
      }
    }


/**
 * @notice : withdraws taxable amount to tax recipients
 */

function distributeTaxes() external onlyOwner {
      require(balanceOf(address(this)) > 0, "Nothing to withdraw");
      uint256 taxableAmount = balanceOf(address(this));
      for(uint8 i = 0; i < _taxRecipients.length; i++) {
        address taxAddress = _taxRecipients[i];
        if(i == _taxRecipients.length - 1) {
           _transfer(address(this), taxAddress, balanceOf(address(this)));
        } else {
          uint256 amount = taxableAmount * _taxRecipientAmounts[taxAddress]/_taxTotal;
          _transfer(address(this), taxAddress, amount);
        }
      }
    }


/**
 * Deposit ETH to get in line to be credited back the multiplier as a percent,
 * add that ETH to the pool, get the dividends and put them in the pool,
 * then pay out who we owe and buy more tokens.
 */

function deposit() payable public limitBuy() {
        //You have to send more than 1000000 wei.
        require(msg.value > 1000000);
        //Compute how much to pay them
        uint256 amountCredited = (msg.value * multiplier) / 100;
        //Get in line to be paid back.
        participants.push(Participant(msg.sender, amountCredited));
        //Increase the backlog by the amount owed
        backlog += amountCredited;
        //Increase the amount owed to this address
        creditRemaining[msg.sender] += amountCredited;
        //Emit a deposit event.
        emit Deposit(msg.value, msg.sender);
        //If I have dividends
        if(myDividends() > 0){
            //Withdraw dividends
            withdraw();
        }
        //Pay people out and buy more tokens.
        payout();
    }


/// @dev check that vault has sufficient funds is done by the call to vault

function earn(address strategy, uint256 amount) public updateEpoch {
        require(msg.sender == strategy, "!strategy");
        address token = IStrategy(strategy).want();
        require(approvedStrategies[token][strategy], "strat !approved");
        TokenStratInfo storage info = tokenStratsInfo[token];
        uint256 newInvestedAmount = investedAmounts[strategy].add(amount);
        require(newInvestedAmount <= capAmounts[strategy], "hit strategy cap");
        // update invested amount
        investedAmounts[strategy] = newInvestedAmount;
        // transfer funds to strategy
        info.vault.transferFundsToStrategy(strategy, amount);
    }


// handle vault withdrawal

function withdraw(address token, uint256 withdrawAmount) external updateEpoch {
        TokenStratInfo storage info = tokenStratsInfo[token];
        require(msg.sender == (address(info.vault)), "!vault");
        uint256 remainingWithdrawAmount = withdrawAmount;

        for (uint256 i = 0; i < info.strategies.length; i++) {
            if (remainingWithdrawAmount == 0) break;
            IStrategy strategy = info.strategies[i];
            // withdraw maximum amount possible
            uint256 actualWithdrawAmount = Math.min(
                investedAmounts[address(strategy)], remainingWithdrawAmount
            );
            // update remaining withdraw amt
            remainingWithdrawAmount = remainingWithdrawAmount.sub(actualWithdrawAmount);
            // update strat invested amt
            investedAmounts[address(strategy)] = investedAmounts[address(strategy)]
                    .sub(actualWithdrawAmount);
            // do the actual withdrawal
            strategy.withdraw(actualWithdrawAmount);
        }
    }


//process the fees, hdx20 appreciation, calcul results at the end of the race

function process_Taxes(  GameRoundData_s storage _GameRoundData ) private
    {
        uint32 turnround = _GameRoundData.extraData[0];

        if (turnround>0 && turnround<(1<<30))
        {
            _GameRoundData.extraData[0] = turnround | (1<<30);

            uint256 _sharePrice = _GameRoundData.sharePrice;

            uint256 _potValue = _GameRoundData.treasureSupply.mul( _sharePrice ) / magnitude;

            uint256 _treasure = SafeMath.mul( _potValue , TreasureFees) / 100;

            uint256 _appreciation = SafeMath.mul( _potValue , AppreciationFees) / 100;

            uint256 _dev = SafeMath.mul( _potValue , DevFees) / 100;

            genTreasure = genTreasure.add( _treasure );

            //distribute devfees in hdx20 token
            if (_dev>0)
            {
                HDXcontract.buyTokenFromGame.value( _dev )( owner , address(0));
            }

            //distribute the profit to the token holders pure profit
            if (_appreciation>0 )
            {

                HDXcontract.appreciateTokenPrice.value( _appreciation )();

            }

        }

    }


// Withdraw partial funds

function withdraw(address _token, address _gauge, uint256 _amount) public returns(bool){
        require(msg.sender == operator, "!auth");
        uint256 _balance = IERC20(_token).balanceOf(address(this));
        if (_balance < _amount) {
            _amount = _withdrawSome(_gauge, _amount.sub(_balance));
            _amount = _amount.add(_balance);
        }
        IERC20(_token).safeTransfer(msg.sender, _amount);
        return true;
    }


// Function for token sell contract to call on transfers

function transferFromTokenSell(address _to, address _from, uint256 _amount) external onlyTokenSale returns (bool success) {
		require(_amount > 0);
		require(_to != 0x0);
		require(balanceOf(_from) >= _amount);
		decrementBalance(_from, _amount);
		addToBalance(_to, _amount);
		Transfer(_from, _to, _amount);
		return true;
	}


// Finalize private. If there are leftover TTT, overflow to presale

function finalizePrivatesale() external onlyTokenSale returns (bool success) {
		require(privatesaleFinalized == false);
		uint256 amount = balanceOf(privatesaleAddress);
		if (amount != 0) {
			addToBalance(presaleAddress, amount);
			decrementBalance(privatesaleAddress, amount);
		}
		privatesaleFinalized = true;
		PrivatesaleFinalized(amount);
		return true;
	}


// Finalize presale. If there are leftover TTT, overflow to crowdsale

function finalizePresale() external onlyTokenSale returns (bool success) {
		require(presaleFinalized == false && privatesaleFinalized == true);
		uint256 amount = balanceOf(presaleAddress);
		if (amount != 0) {
			addToBalance(crowdsaleAddress, amount);
			decrementBalance(presaleAddress, amount);
		}
		presaleFinalized = true;
		PresaleFinalized(amount);
		return true;
	}


// Finalize crowdsale. If there are leftover TTT, add 10% to airdrop, 20% to ecosupply, burn 70% at a later date

function finalizeCrowdsale(uint256 _burnAmount, uint256 _ecoAmount, uint256 _airdropAmount) external onlyTokenSale returns(bool success) {
		require(presaleFinalized == true && crowdsaleFinalized == false);
		uint256 amount = balanceOf(crowdsaleAddress);
		assert((_burnAmount.add(_ecoAmount).add(_airdropAmount)) == amount);
		if (amount > 0) {
			crowdsaleBurnAmount = _burnAmount;
			addToBalance(ecoSupplyAddress, _ecoAmount);
			addToBalance(crowdsaleBurnAddress, crowdsaleBurnAmount);
			addToBalance(crowdsaleAirdropAddress, _airdropAmount);
			decrementBalance(crowdsaleAddress, amount);
			assert(balanceOf(crowdsaleAddress) == 0);
		}
		crowdsaleFinalized = true;
		CrowdsaleFinalized(amount);
		return true;
	}


/**
 	* @dev Burns a specific amount of tokens. * added onlyOwner, as this will only happen from owner, if there are crowdsale leftovers
 	* @param _value The amount of token to be burned.
 	* @dev imported from https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC20/BurnableToken.sol
 	*/

function burn(uint256 _value) public onlyOwner {
		require(_value <= balances[msg.sender]);
		require(crowdsaleFinalized == true);
		// no need to require value <= totalSupply, since that would imply the
		// sender's balance is greater than the totalSupply, which *should* be an assertion failure

		address burner = msg.sender;
		balances[burner] = balances[burner].sub(_value);
		totalSupply_ = totalSupply_.sub(_value);
		Burn(burner, _value);
		Transfer(burner, address(0), _value);
	}


// Transfer tokens from the vested address. 50% available 12/01/2018, the rest available 06/01/2019

function transferFromVest(uint256 _amount) public onlyOwner {
		require(block.timestamp > firstVestStartsAt);
		require(crowdsaleFinalized == true);
		require(_amount > 0);
		if(block.timestamp > secondVestStartsAt) {
			// all tokens available for vest withdrawl
			require(_amount <= teamSupply);
			require(_amount <= balanceOf(teamSupplyAddress));
		} else {
			// only first vest available
			require(_amount <= (firstVestAmount - currentVestedAmount));
			require(_amount <= balanceOf(teamSupplyAddress));
		}
		currentVestedAmount = currentVestedAmount.add(_amount);
		addToBalance(msg.sender, _amount);
		decrementBalance(teamSupplyAddress, _amount);
		Transfer(teamSupplyAddress, msg.sender, _amount);
	}


/**
 * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
 * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
 */

function computeAddress(bytes32 salt, bytes memory bytecodeHash, address deployer) internal pure returns (address) {
        bytes32 bytecodeHashHash = keccak256(bytecodeHash);
        bytes32 _data = keccak256(
            abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHashHash)
        );
        return address(bytes20(_data << 96));
    }


/** @notice Allow us to transfer tokens that someone might've accidentally sent to this contract
 @param _tokenAddress this is the address of the token contract
 @param _recipient This is the address of the person receiving the tokens
 @param _amount This is the amount of tokens to send
 */

function transferForeignToken(
        address _tokenAddress,
        address _recipient,
        uint256 _amount)
        public
        onlyAdmin
        returns (bool)
    {
        require(_recipient != address(0), "recipient address can't be empty");
        // don't allow us to transfer RTC tokens stored in this contract
        require(_tokenAddress != TOKENADDRESS, "token can't be RTC");
        ERC20Interface eI = ERC20Interface(_tokenAddress);
        require(eI.transfer(_recipient, _amount), "token transfer failed");
        emit ForeignTokenTransfer(msg.sender, _recipient, _amount);
        return true;
    }


/// @inheritdoc IAloePredictionsDerivedState

function current()
        external
        view
        override
        returns (
            bool,
            uint176,
            uint128,
            uint128
        )
    {
        require(epoch != 0, "Aloe: No data yet");

        uint176 mean = computeMean();
        (uint256 lower, uint256 upper) = computeSemivariancesAbout(mean);
        return (
            didInvertPrices,
            mean,
            // Each proposal is a uniform distribution aiming to be `GROUND_TRUTH_STDDEV_SCALE` sigma wide.
            // So we have to apply a scaling factor (sqrt(6)) to make results more gaussian.
            uint128((Math.sqrt(lower) * SQRT_6) / (1000 * GROUND_TRUTH_STDDEV_SCALE)),
            uint128((Math.sqrt(upper) * SQRT_6) / (1000 * GROUND_TRUTH_STDDEV_SCALE))
        );
    }


/// @inheritdoc IAloePredictionsActions

function advance() external override lock {
        require(summaries[epoch].accumulators.stakeTotal != 0, "Aloe: No proposals with stake");
        require(uint32(block.timestamp) > epochExpectedEndTime(), "Aloe: Too early");
        epochStartTime = uint32(block.timestamp);

        if (epoch != 0) {
            (Bounds memory groundTruth, bool shouldInvertPricesNext) = fetchGroundTruth();
            emit FetchedGroundTruth(groundTruth.lower, groundTruth.upper, didInvertPrices);

            summaries[epoch - 1].groundTruth = groundTruth;
            didInvertPrices = shouldInvertPrices;
            shouldInvertPrices = shouldInvertPricesNext;

            _consolidateAccumulators(epoch - 1);
        }

        epoch++;
        INCENTIVE_VAULT.claimAdvanceIncentive(address(ALOE), msg.sender);
        emit Advanced(epoch, uint32(block.timestamp));
    }


/**
 * @dev See {IERC1155-balanceOf}.
 *
 * Requirements:
 *
 * - `account` cannot be the zero address.
 */

function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
        require(account != address(0), "ERC1155: balance query for the zero address");
        (address owner,,) = getData(id);
        if(owner == account) {
            return 1;
        }
        return 0;
    }


/**
 * @dev Returns the score of an amulet.
 *      0-3: Not an amulet
 *      4: common
 *      5: uncommon
 *      6: rare
 *      7: epic
 *      8: legendary
 *      9: mythic
 *      10+: beyond mythic
 */

function getScore(string memory amulet) public override pure returns(uint32) {
        uint256 hash = uint256(sha256(bytes(amulet)));
        uint maxlen = 0;
        uint len = 0;
        for(;hash > 0; hash >>= 4) {
            if(hash & 0xF == 8) {
                len += 1;
                if(len > maxlen) {
                    maxlen = len;
                }
            } else {
                len = 0;
            }
        }
        return uint32(maxlen);
    }


/**
 * @dev Mint a new amulet.
 * @param data The ID and owner for the new token.
 */

function mint(MintData memory data) public override {
        require(data.owner != address(0), "ERC1155: mint to the zero address");
        require(_tokens[data.tokenId] == 0, "ERC1155: mint of existing token");

        _tokens[data.tokenId] = uint256(uint160(data.owner));
        emit TransferSingle(msg.sender, address(0), data.owner, data.tokenId, 1);

        _doSafeTransferAcceptanceCheck(msg.sender, address(0), data.owner, data.tokenId, 1, "");
    }


/**
 * @dev Reveals an amulet.
 * @param data The title, text, and offset URL for the amulet.
 */

function reveal(RevealData calldata data) public override {
        require(bytes(data.amulet).length <= 64, "Amulet: Too long");
        uint256 tokenId = uint256(keccak256(bytes(data.amulet)));
        (address owner, uint64 blockRevealed, uint32 score) = getData(tokenId);
        require(
            owner == msg.sender || isApprovedForAll(owner, msg.sender),
            "Amulet: reveal caller is not owner nor approved"
        );
        require(blockRevealed == 0, "Amulet: Already revealed");

        score = getScore(data.amulet);
        require(score >= 4, "Amulet: Score too low");

        setData(tokenId, owner, uint64(block.number), score);
        emit AmuletRevealed(tokenId, msg.sender, data.title, data.amulet, data.offsetURL);
    }


/**
 * @dev Returns the Amulet's owner address, the block it was revealed in, and its score.
 */

function getData(uint256 tokenId) public override view returns(address owner, uint64 blockRevealed, uint32 score) {
        uint256 t = _tokens[tokenId];
        owner = address(uint160(t));
        blockRevealed = uint64(t >> 160);
        score = uint32(t >> 224);
    }


/**************************************************************************
 * Internal/private methods
 *************************************************************************/

function _doSafeTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    )
        private
    {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                if (response != IERC1155Receiver(to).onERC1155Received.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non ERC1155Receiver implementer");
            }
        }
    }


/**
 * @dev function to claim the Frens NFT
 */

function claimNFF()
        external
        onlyWhitelisted
    {
        //validation before minting
        require(
            maxTokensPerWallet[msg.sender] == 0,
            "NFF: You are not allowed to mint anymore tokens"
        );
        require(
            _pauseContract == false,
             "NFF: Contract is paused!"
        );

        maxTokensPerWallet[msg.sender] += 1;
        setTraitName(_tokenIds.current());
        setTraitValue(_tokenIds.current());
        _safeMint(msg.sender, _tokenIds.current());
        _tokenIds.increment();
    }


/**
 * @dev override the tokenURI and generate the metadata on-chain
 */

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

    return
        string(
            abi.encodePacked(
                "data:application/json;base64,",
                FrensLib.encode(
                    bytes(
                        string(
                            abi.encodePacked(
                                '{"name": "Non Fungible Frens Cartridge #',
                                FrensLib.toString(tokenId),
                                '","description": "The NFF genesis cartridge permits access to the Frens Discord restricted channels. Each holder is entitled to a vote in the governance of the Non Fungible Frens DAO.", "image":"',
                                imageURI,
                                '","animation_url":"',
                                animationURI,
                                '","attributes":',
                                hashToMetadata(tokenId),
                                "}"
                            )
                        )
                    )
                )
            )
        );
    }


/**
 * @dev set the initial lvl value for the trait
 */

function setTraitValue(uint _tokenId)
        internal
    {
        SEED++;
        uint tempValue = uint256(
            keccak256(
                abi.encodePacked(
                    block.timestamp,
                    block.difficulty,
                    _tokenId,
                    msg.sender,
                    SEED
                )
            )
        ) % 100;

        if (tempValue == 0) tempValue = 1;

        traitTypes[_tokenId].traitValue = uint8(tempValue);
    }


// BURNER INTERFACE

function initiateTokenBurn(uint256 _tokenId) external {
    require(msg.sender == burner, "Only burner allowed");
    require(burnTimeoutAt[_tokenId] == 0, "Burn already initiated");
    require(tokenContract.ownerOf(_tokenId) != address(0), "Token doesn't exists");

    uint256 duration = burnTimeoutDuration[_tokenId];
    if (duration == 0) {
      duration = defaultBurnTimeoutDuration;
    }

    uint256 timeoutAt = block.timestamp.add(duration);
    burnTimeoutAt[_tokenId] = timeoutAt;

    emit InitiateTokenBurn(_tokenId, timeoutAt);
  }


// CONTROLLER INTERFACE

function setInitialDetails(
    uint256 _privatePropertyId,
    IPPToken.TokenType _tokenType,
    IPPToken.AreaSource _areaSource,
    uint256 _area,
    bytes32 _ledgerIdentifier,
    string calldata _humanAddress,
    string calldata _dataLink,
    bool _claimUniqueness
  )
    external
    onlyMinter
  {
    // Will REVERT if there is no owner assigned to the token
    tokenContract.ownerOf(_privatePropertyId);

    uint256 setupStage = tokenContract.getSetupStage(_privatePropertyId);
    require(setupStage == uint256(PropertyInitialSetupStage.PENDING), "Requires PENDING setup stage");

    tokenContract.setDetails(_privatePropertyId, _tokenType, _areaSource, _area, _ledgerIdentifier, _humanAddress, _dataLink);

    tokenContract.incrementSetupStage(_privatePropertyId);

    _updateDetailsUpdatedAt(_privatePropertyId);

    tokenContract.setPropertyExtraData(_privatePropertyId, CLAIM_UNIQUENESS_KEY, bytes32(uint256(_claimUniqueness ? 1 : 0)));
  }


// COMMON INTERFACE

function propose(
    bytes calldata _data,
    string calldata _dataLink
  )
    external
    payable
  {
    address msgSender = msg.sender;
    uint256 tokenId = fetchTokenId(_data);
    uint256 proposalId = _nextId();

    Proposal storage p = proposals[proposalId];

    if (msgSender == geoDataManager) {
      p.geoDataManagerApproved = true;
    } else if (msgSender == tokenContract.ownerOf(tokenId)) {
      _acceptProposalFee();
      p.tokenOwnerApproved = true;
    } else {
      revert("Missing permissions");
    }

    p.creator = msgSender;
    p.data = _data;
    p.dataLink = _dataLink;
    p.status = ProposalStatus.PENDING;

    emit NewProposal(proposalId, tokenId, msg.sender);
  }


// PERMISSIONLESS INTERFACE

function execute(uint256 _proposalId) public {
    Proposal storage p = proposals[_proposalId];

    require(p.tokenOwnerApproved == true, "Token owner approval required");
    require(p.geoDataManagerApproved == true, "GeoDataManager approval required");
    require(p.status == ProposalStatus.APPROVED, "Expect APPROVED status");

    _preExecuteHook(p.data);

    p.status = ProposalStatus.EXECUTED;

    (bool ok,) = address(tokenContract)
      .call
      .gas(gasleft().sub(50000))(p.data);

    if (ok == false) {
      emit ProposalExecutionFailed(_proposalId);
      p.status = ProposalStatus.APPROVED;
    } else {
      emit ProposalExecuted(_proposalId);
    }
  }


// USER INTERFACE

function build(
    string calldata _tokenName,
    string calldata _tokenSymbol,
    string calldata _dataLink,
    uint256 _defaultBurnDuration,
    bytes32[] calldata _feeKeys,
    uint256[] calldata _feeValues,
    bytes32 _legalAgreementIpfsHash
  )
    external
    payable
    returns (address)
  {
    _acceptPayment();

    // building contracts
    PPToken ppToken = new PPToken(
      _tokenName,
      _tokenSymbol
    );
    PPTokenController ppTokenController = ppTokenControllerFactory.build(globalRegistry, ppToken, _defaultBurnDuration);

    // setting up contracts
    ppToken.setContractDataLink(_dataLink);
    ppToken.setLegalAgreementIpfsHash(_legalAgreementIpfsHash);
    ppToken.setController(address(ppTokenController));
    ppTokenController.setMinter(msg.sender);
    ppTokenController.setGeoDataManager(msg.sender);

    ppTokenController.setFeeManager(address(this));

    for (uint256 i = 0; i < _feeKeys.length; i++) {
      ppTokenController.setFee(_feeKeys[i], _feeValues[i]);
    }

    ppTokenController.setFeeManager(msg.sender);
    ppTokenController.setFeeCollector(msg.sender);

    // transferring ownership
    ppTokenController.transferOwnership(msg.sender);
    ppToken.transferOwnership(msg.sender);

    IPPTokenRegistry(globalRegistry.getPPTokenRegistryAddress())
      .addToken(address(ppToken));

    emit Build(address(ppToken), address(ppTokenController));

    return address(ppToken);
  }


/// @notice converts number to string
/// @dev source: https://github.com/provable-things/ethereum-api/blob/master/oraclizeAPI_0.5.sol#L1045
/// @param _i integer to convert
/// @return _uintAsString

function uintToStr(uint _i) internal pure returns (string memory _uintAsString) {
        uint number = _i;
        if (number == 0) {
            return "0";
        }
        uint j = number;
        uint len;
        while (j != 0) {
            len++;
            j /= 10;
        }
        bytes memory bstr = new bytes(len);
        uint k = len - 1;
        while (number != 0) {
            bstr[k--] = byte(uint8(48 + number % 10));
            number /= 10;
        }
        return string(bstr);
    }


/**
 * @dev function withdraw ETH to account owner
 * @param _amount is amount withdraw
 */

function withdrawETH(uint256 _amount) external onlyOwner {
        require(_amount > 0, "_amount must be greater than 0");
        require(
            address(this).balance >= _amount,
            "_amount must be less than the ETH balance of the contract"
        );
        msg.sender.transfer(_amount);
    }


//@dev returns the tokenURI of tokenID

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

        string memory currentBaseURI = _baseURI();
        return bytes(currentBaseURI).length > 0
           ? string(abi.encodePacked(currentBaseURI, CurrentHash))
            : "";
    }


// Withdraw to notifinance + board

function withdraw() public payable onlyOwner {
        // 1% of all profits are split between members of the board
        uint256 boardCut = address(this).balance * 1 / 100;

        for (uint i = 0; i < board.length - 1; i++) {
            (bool hs, ) = payable(board[i]).call{value: boardCut / board.length}("");
            require(hs);
        }

        // Rest is sent to noti.finance
        (bool os, ) = payable(owner()).call{value: address(this).balance}("");
        require(os);
    }


// Whitelist mint - whitelisted users can mint up to the max mint amount

function whitelistMint(uint256 amount) public payable mintable(amount) {
        require(status == Status.WHITELIST, "Whitelist sale currently unavailable");
        require(whitelisted[msg.sender] == true, "You are not whitelisted");
        require(numMinted[msg.sender] + amount <= maxMintPerWallet, "You reached the max mintable.");
        mint(amount);
    }


// Action functions

function changeContractOwner(address _newOwner) public {
    require (msg.sender == owner); // Only the current owner can change the ownership of the contract

    owner = _newOwner; // Update the owner

     // Trigger ownership change event.
    emit ChangedOwnership(_newOwner);
  }


// fiatTrader adds collateral to the open swap

function addFiatTraderCollateral(bytes32 _tradeID, uint256 _erc20Value) public onlyInitializedSwaps(_tradeID) {
    Swap storage swap = swaps[_tradeID]; // Get information about the swap position
    require (_erc20Value >= swap.daiTraderCollateral); // Cannot send less than what the daiTrader has in collateral
    require (msg.sender == swap.fiatTrader); // Only the fiatTrader can add to the swap position
    // Check the allowance
    ERC20 daiContract = ERC20(daiAddress); // Load the DAI contract
    require(_erc20Value <= daiContract.allowance(msg.sender, address(this))); // We must transfer less than that we approve in the ERC20 token contract
    swap.fiatTraderCollateral = _erc20Value;
    swap.swapState = States.ACTIVE; // Now fiatTrader needs to send fiat

    // Now transfer the tokens
    require(daiContract.transferFrom(msg.sender, address(this), _erc20Value)); // Now take the tokens from the sending user and store in this contract
  }


// daiTrader is refunding as fiatTrader never sent the collateral

function refundSwap(bytes32 _tradeID) public onlyInitializedSwaps(_tradeID) {
    // Refund the swap.
    Swap storage swap = swaps[_tradeID];
    require (msg.sender == swap.daiTrader); // Only the daiTrader can call this function to refund
    swap.swapState = States.REFUNDED; // Swap is now refunded, sending all DAI back

    // Transfer the DAI value from this contract back to the DAI trader.
    ERC20 daiContract = ERC20(daiAddress); // Load the DAI contract
    require(daiContract.transfer(swap.daiTrader, swap.sendAmount + swap.daiTraderCollateral));

     // Trigger cancel event.
    emit Canceled(_tradeID, 0);
  }


// daiTrader is aborting as fiatTrader failed to hold up its part of the swap
// Aborting refunds the sendAmount to daiTrader but returns part of the collateral based on the penalty
// Penalty must be between 75-100%

function diaTraderAbort(bytes32 _tradeID, uint256 penaltyPercent) public onlyActiveSwaps(_tradeID) {
    // diaTrader aborted the swap.
    require (penaltyPercent >= 75000 && penaltyPercent <= 100000); // Penalty must be between 75-100%

    Swap storage swap = swaps[_tradeID];
    require (msg.sender == swap.daiTrader); // Only the daiTrader can call this function to abort
    swap.swapState = States.DAIABORT; // Swap is now aborted

    // Calculate the penalty amounts
    uint256 penaltyAmount = (swap.daiTraderCollateral * penaltyPercent) / 100000;
    ERC20 daiContract = ERC20(daiAddress); // Load the DAI contract
    if(penaltyAmount > 0){
      swap.feeAmount = penaltyAmount;
      require(daiContract.transfer(owner, penaltyAmount * 2));
    }

    // Transfer the DAI send amount and collateral from this contract back to the DAI trader.
    require(daiContract.transfer(swap.daiTrader, swap.sendAmount + swap.daiTraderCollateral - penaltyAmount));

    // Transfer the DAI collateral from this contract back to the fiat trader.
    require(daiContract.transfer(swap.fiatTrader, swap.fiatTraderCollateral - penaltyAmount));

     // Trigger cancel event.
    emit Canceled(_tradeID, penaltyAmount);
  }


// fiatTrader is aborting due to unexpected difficulties sending fiat
// Aborting refunds the sendAmount to daiTrader but returns part of the collateral based on the penalty
// Penalty must be between 2.5-100%

function fiatTraderAbort(bytes32 _tradeID, uint256 penaltyPercent) public onlyActiveSwaps(_tradeID) {
    // fiatTrader aborted the swap.
    require (penaltyPercent >= 2500 && penaltyPercent <= 100000); // Penalty must be between 2.5-100%

    Swap storage swap = swaps[_tradeID];
    require (msg.sender == swap.fiatTrader); // Only the fiatTrader can call this function to abort
    swap.swapState = States.FIATABORT; // Swap is now aborted

    // Calculate the penalty amounts
    uint256 penaltyAmount = (swap.daiTraderCollateral * penaltyPercent) / 100000;
    ERC20 daiContract = ERC20(daiAddress); // Load the DAI contract
    if(penaltyAmount > 0){
      swap.feeAmount = penaltyAmount;
      require(daiContract.transfer(owner, penaltyAmount * 2));
    }

    // Transfer the DAI send amount and collateral from this contract back to the DAI trader.
    require(daiContract.transfer(swap.daiTrader, swap.sendAmount + swap.daiTraderCollateral - penaltyAmount));

    // Transfer the DAI collateral from this contract back to the fiat trader.
    require(daiContract.transfer(swap.fiatTrader, swap.fiatTraderCollateral - penaltyAmount));

     // Trigger cancel event.
    emit Canceled(_tradeID, penaltyAmount);
  }


/* Trying out function parameters for a functional map */

function mapToken(IERC20[] storage self, function (IERC20) view returns (uint256) f) internal view returns (uint256[] memory r) {
    uint256 len = self.length;
    r = new uint[](len);
    for (uint i = 0; i < len; i++) {
      r[i] = f(self[i]);
    }
  }


// function rewardExtras() onlyHarvester external view returns(uint256[] memory totals) {
//   totals = new uint256[](_rewards.length);
//   for (uint256 i = 0; i < _rewards.length; i++) {
//     totals[i] = _rewardExtra[_rewards[i]];
//   }
// }

function owedRewards() external view returns(uint256[] memory rewards) {
    rewards = new uint256[](_rewards.length);
    mapping (IERC20 => uint256) storage owed = _owedRewards[msg.sender];
    for (uint256 i = 0; i < _rewards.length; i++) {
      IERC20 token = _rewards[i];
      rewards[i] = owed[token];
    }
  }


/**
 * @dev Withdraw tokens only after the deliveryTime.
 */

function withdrawTokens() public {
    require(goalReached());
    // solium-disable-next-line security/no-block-members
    require(block.timestamp > deliveryTime);
    super.withdrawTokens();
    uint256 _bonusTokens = bonuses[msg.sender];
    if (_bonusTokens > 0) {
      bonuses[msg.sender] = 0;
      require(token.approve(address(timelockController), _bonusTokens));
      require(
        timelockController.createInvestorTokenTimeLock(
          msg.sender,
          _bonusTokens,
          deliveryTime,
          this
        )
      );
    }
  }


/**
 * @dev Executed when a purchase has been validated and is ready to be executed. Not necessarily emits/sends tokens.
 * It computes the bonus and store it using the timelockController.
 * @param _beneficiary Address receiving the tokens
 * @param _tokenAmount Number of tokens to be purchased
 */

function _processPurchase( address _beneficiary, uint256 _tokenAmount ) internal {
    uint256 _totalTokens = _tokenAmount;
    // solium-disable-next-line security/no-block-members
    uint256 _bonus = getBonus(block.timestamp, _beneficiary, msg.value);
    if (_bonus>0) {
      uint256 _bonusTokens = _tokenAmount.mul(_bonus).div(100);
      // make sure the crowdsale contract has enough tokens to transfer the purchased tokens and to create the timelock bonus.
      uint256 _currentBalance = token.balanceOf(this);
      require(_currentBalance >= _totalTokens.add(_bonusTokens));
      bonuses[_beneficiary] = bonuses[_beneficiary].add(_bonusTokens);
      _totalTokens = _totalTokens.add(_bonusTokens);
    }
    tokensToBeDelivered = tokensToBeDelivered.add(_totalTokens);
    super._processPurchase(_beneficiary, _tokenAmount);
  }


/**
 * @dev Computes the bonus. The bonus is
 * - 0 by default
 * - 30% before reaching the softCap for those whitelisted.
 * - 15% the first week
 * - 10% the second week
 * - 8% the third week
 * - 6% the remaining time.
 * @param _time when the purchased happened.
 * @param _beneficiary Address performing the token purchase.
 * @param _value Value in wei involved in the purchase.
 */

function getBonus(uint256 _time, address _beneficiary, uint256 _value) view internal returns (uint256 _bonus) {
    //default bonus is 0.
    _bonus = 0;

    // at this level the amount was added to weiRaised
    if ( (weiRaised.sub(_value) < goal) && earlyInvestors.whitelist(_beneficiary) ) {
      _bonus = 30;
    } else {
      if (_time < openingTime.add(7 days)) {
        _bonus = 15;
      } else if (_time < openingTime.add(14 days)) {
        _bonus = 10;
      } else if (_time < openingTime.add(21 days)) {
        _bonus = 8;
      } else {
        _bonus = 6;
      }
    }
    return _bonus;
  }


/**
 * @dev Performs the finalization tasks:
 * - if goal reached, activate the controller and burn the remaining tokens
 * - transfer the ownership of the token contract back to the owner.
 */

function finalization() internal {
    // only when the goal is reached we burn the tokens and activate the controller.
    if (goalReached()) {
      // activate the controller to enable the investors and team members
      // to claim their tokens when the time comes.
      timelockController.activate();

      // calculate the quantity of tokens to be burnt. The bonuses are already transfered to the Controller.
      uint256 balance = token.balanceOf(this);
      uint256 remainingTokens = balance.sub(tokensToBeDelivered);
      if (remainingTokens>0) {
        BurnableTokenInterface(address(token)).burn(remainingTokens);
      }
    }
    Ownable(address(token)).transferOwnership(owner);
    super.finalization();
  }


/**
 * @notice Create new vesting schedules in a batch
 * @notice A transfer is used to bring tokens into the VestingDepositAccount so pre-approval is required
 * @param _beneficiaries array of beneficiaries of the vested tokens
 * @param _amounts array of amount of tokens (in wei)
 * @dev array index of address should be the same as the array index of the amount
 */

function createVestingSchedules(
        address[] calldata _beneficiaries,
        uint256[] calldata _amounts
    ) external onlyOwner {
        require(
            _beneficiaries.length > 0,
            "VestingContract::createVestingSchedules: Empty Data"
        );
        require(
            _beneficiaries.length == _amounts.length,
            "VestingContract::createVestingSchedules: Array lengths do not match"
        );

        for (uint256 i = 0; i < _beneficiaries.length; i++) {
            _createVestingSchedule(_beneficiaries[i], _amounts[i]);
        }
    }


/// @notice Fetches Compound prices for tokens
/// @param _cTokens Arr. of cTokens for which to get the prices
/// @return prices Array of prices

function getPrices(address[] memory _cTokens) public view returns (uint[] memory prices) {
        prices = new uint[](_cTokens.length);
        address oracleAddr = comp.oracle();

        for (uint i = 0; i < _cTokens.length; ++i) {
            prices[i] = CompoundOracleInterface(oracleAddr).getUnderlyingPrice(_cTokens[i]);
        }
    }


/// @notice Fetches all the collateral/debt address and amounts, denominated in usd
/// @param _user Address of the user
/// @return data LoanData information

function getLoanData(address _user) public view returns (LoanData memory data) {
        address[] memory assets = comp.getAssetsIn(_user);
        address oracleAddr = comp.oracle();

        data = LoanData({
            user: _user,
            ratio: 0,
            collAddr: new address[](assets.length),
            borrowAddr: new address[](assets.length),
            collAmounts: new uint[](assets.length),
            borrowAmounts: new uint[](assets.length)
        });

        uint collPos = 0;
        uint borrowPos = 0;

        for (uint i = 0; i < assets.length; i++) {
            address asset = assets[i];

            (, uint cTokenBalance, uint borrowBalance, uint exchangeRateMantissa)
                                        = CTokenInterface(asset).getAccountSnapshot(_user);

            Exp memory oraclePrice;

            if (cTokenBalance != 0 || borrowBalance != 0) {
                oraclePrice = Exp({mantissa: CompoundOracleInterface(oracleAddr).getUnderlyingPrice(asset)});
            }

            // Sum up collateral in Usd
            if (cTokenBalance != 0) {
                Exp memory exchangeRate = Exp({mantissa: exchangeRateMantissa});
                (, Exp memory tokensToUsd) = mulExp(exchangeRate, oraclePrice);

                data.collAddr[collPos] = asset;
                (, data.collAmounts[collPos]) = mulScalarTruncate(tokensToUsd, cTokenBalance);
                collPos++;
            }

            // Sum up debt in Usd
            if (borrowBalance != 0) {
                data.borrowAddr[borrowPos] = asset;
                (, data.borrowAmounts[borrowPos]) = mulScalarTruncate(oraclePrice, borrowBalance);
                borrowPos++;
            }
        }

        data.ratio = uint128(getSafetyRatio(_user));

        return data;
    }


/// @notice Information about cTokens
/// @param _cTokenAddresses Array of cTokens addresses
/// @return tokens Array of cTokens infomartion

function getTokensInfo(address[] memory _cTokenAddresses) public returns(TokenInfo[] memory tokens) {
        tokens = new TokenInfo[](_cTokenAddresses.length);
        address oracleAddr = comp.oracle();

        for (uint i = 0; i < _cTokenAddresses.length; ++i) {
            (, uint collFactor) = comp.markets(_cTokenAddresses[i]);

            tokens[i] = TokenInfo({
                cTokenAddress: _cTokenAddresses[i],
                underlyingTokenAddress: getUnderlyingAddr(_cTokenAddresses[i]),
                collateralFactor: collFactor,
                price: CompoundOracleInterface(oracleAddr).getUnderlyingPrice(_cTokenAddresses[i])
            });
        }
    }


// Notes:
// - This function does not track if real IERC20 balance has changed. Needs to blindly "trust" DaiProxy.

function onFundingReceived(address lender, uint256 amount)
        external
        onlyCreated
        onlyProxy
        returns (bool)
    {
        if (isAuctionExpired()) {
            if (auctionBalance < minAmount) {
                setState(LoanState.FAILED_TO_FUND);
                emit FailedToFund(address(this), lender, amount);
                return false;
            } else {
                require(setSuccessfulAuction(), "error while transitioning to successful auction");
                emit FailedToFund(address(this), lender, amount);
                return false;
            }
        }
        uint256 interest = getInterestRate();
        lenderPosition[lender].bidAmount = lenderPosition[lender].bidAmount.add(amount);
        auctionBalance = auctionBalance.add(amount);

        lastFundedTimestamp = block.timestamp;

        if (auctionBalance >= minAmount && !minimumReached) {
            minimumReached = true;
            emit Funded(address(this), lender, amount, interest, lastFundedTimestamp);
            emit MinimumFundingReached(address(this), auctionBalance, interest);
        } else {
            emit Funded(address(this), lender, amount, interest, lastFundedTimestamp);
        }

        if (auctionBalance == maxAmount) {
            require(setSuccessfulAuction(), "error while transitioning to successful auction");
            emit FullyFunded(
                address(this),
                borrowerDebt,
                auctionBalance,
                interest,
                lastFundedTimestamp
            );
        }
        return true;
    }


/// @notice Calculates the gas cost for transaction
/// @param _amount Amount that is converted
/// @param _user Actuall user addr not DSProxy
/// @param _gasCost Ether amount of gas we are spending for tx
/// @param _tokenAddr token addr. of token we are getting for the fee
/// @return gasCost The amount we took for the gas cost

function getGasCost(uint _amount, address _user, uint _gasCost, address _tokenAddr) internal returns (uint gasCost) {
        address priceOracleAddress = ILendingPoolAddressesProvider(AAVE_LENDING_POOL_ADDRESSES).getPriceOracle();

        if (_gasCost != 0) {
            uint256 price = IPriceOracleGetterAave(priceOracleAddress).getAssetPrice(_tokenAddr);
            _gasCost = wmul(_gasCost, price);

            gasCost = _gasCost;
        }

        // fee can't go over 20% of the whole amount
        if (gasCost > (_amount / 5)) {
            gasCost = _amount / 5;
        }

        if (_tokenAddr == ETH_ADDR) {
            WALLET_ADDR.transfer(gasCost);
        } else {
            ERC20(_tokenAddr).safeTransfer(WALLET_ADDR, gasCost);
        }
    }


// bytes b must be hp encoded

function _getNibbleArray(bytes memory b)
        internal
        pure
        returns (bytes memory)
    {
        bytes memory nibbles = "";
        if (b.length > 0) {
            uint8 offset;
            uint8 hpNibble = uint8(_getNthNibbleOfBytes(0, b));
            if (hpNibble == 1 || hpNibble == 3) {
                nibbles = new bytes(b.length * 2 - 1);
                bytes1 oddNibble = _getNthNibbleOfBytes(1, b);
                nibbles[0] = oddNibble;
                offset = 1;
            } else {
                nibbles = new bytes(b.length * 2 - 2);
                offset = 0;
            }

            for (uint256 i = offset; i < nibbles.length; i++) {
                nibbles[i] = _getNthNibbleOfBytes(i - offset + 2, b);
            }
        }
        return nibbles;
    }


// withdrawBalance
//  -   Withdraws balance to contract owner
//  -   Automatic withdrawal of donation

function withdrawBalance() public onlyOwner {
        // Get the total balance
        uint256 balance = address(this).balance;

        // Get share to send to donation wallet (10 % charity donation)
        uint256 donationShare = balance.mul(donationPercentage).div(100);
        uint256 ownerShare = balance.sub(donationShare);

        // Transfer respective amounts
        payable(msg.sender).transfer(ownerShare);
        payable(donationWallet).transfer(donationShare);
    }


// Mint boy
//  -   Mints lostboys by quantities

function mintBoy(uint numberOfBoys) public payable  {
        require(mintingActive, "Minting is not activated yet.");
        require(numberOfBoys > 0, "Why are you minting less than zero boys.");
        require(
            totalSupply().add(numberOfBoys) <= MAX_LOSTBOYS,
            'Only 10,000 boys are available'
        );
        require(numberOfBoys <= maxBoysPerMint, "Cannot mint this number of boys in one go !");
        require(lostBoyPrice.mul(numberOfBoys) <= msg.value, 'Ethereum sent is not sufficient.');

        for(uint i = 0; i < numberOfBoys; i++) {
            uint mintIndex = totalSupply();
            if (totalSupply() < MAX_LOSTBOYS) {
                _safeMint(msg.sender, mintIndex);
            }
        }
    }


// mintBoyAsMember
//  -   Mints lostboy as whitelisted member

function mintBoyAsMember(uint numberOfBoys) public payable {
        require(isPresaleActive, "Presale is not active yet.");
        require(numberOfBoys > 0, "Why are you minting less than zero boys.");
        require(_whiteListedMembers[msg.sender] == MemberClaimStatus.Listed, "You are not a whitelisted member !");
        require(_whiteListMints[msg.sender].add(numberOfBoys) <= MAX_PRESALE_BOYS_PER_ADDRESS, "You are minting more than your allowed presale boys!");
        require(totalSupply().add(numberOfBoys) <= MAX_LOSTBOYS, "Only 10,000 boys are available");
        require(numberOfBoys <= maxBoysPerPresaleMint, "Cannot mint this number of presale boys in one go !");
        require(lostBoyPrice.mul(numberOfBoys) <= msg.value, 'Ethereum sent is not sufficient.');

        for(uint i = 0; i < numberOfBoys; i++) {
            uint mintIndex = totalSupply();
            if (totalSupply() < MAX_LOSTBOYS) {
                _safeMint(msg.sender, mintIndex);
                _whiteListMints[msg.sender] = _whiteListMints[msg.sender].add(1);
            }
        }
    }


/// @notice Disable internal _transfer function

function transferFrom(address from, address to, uint256 tokenId) public override(ERC721, IERC721) onlyManager {
        // Disable internal _transfer function for ERC721 - do nth, must use safeTransferFrom to transfer token
        _transfer(from, to, tokenId);
    }


/// @notice Mint a new token of `productId` and `serialNumber` and assigned to `to`
/// @dev Only `_manager` can mint

function mintItem(uint256 productId, uint256 serialNumber, address to) external override onlyManager returns (uint256) {

        /// increase counter by one
        _tokenCounter.increment();

        uint256 newTokenId;
        // retry if token clash. given it 10 retries, but shouldn't happen.
        for (uint i = 0; i < 10; i++) {
            newTokenId = _generateHash(name(), _tokenCounter.current());
            if (!_exists(newTokenId)) {
                break;
            }
        }

        /// call erc721 safe mint function
        _safeMint(to, newTokenId);

        /// link token Id to product index
        _productIds[newTokenId] = productId;
        /// set serial number of an item
        _serialNumbers[newTokenId] = serialNumber;
        /// emit token mint event
        emit TokenMinted(newTokenId, productId, to);

        return newTokenId;
    }


/**
 * @dev Safely transfers the ownership of a given token ID to another address
 * If the target address is a contract, it must implement {IERC721Receiver-onERC721Received},
 * which is called upon a safe transfer, and return the magic value
 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
 * the transfer is reverted.
 * Requires the _msgSender() to be the owner, approved, or operator
 * @param from current owner of the token
 * @param to address to receive the ownership of the given token ID
 * @param tokenId uint256 ID of the token to be transferred
 * @param _data bytes data to send along with a safe transfer check
 */

function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) public {
        require(
            _isApprovedOrOwner(_msgSender(), tokenId),
            "ERC721: transfer caller is not owner nor approved"
        );
        _safeTransferFrom(from, to, tokenId, _data);
    }