/// @notice this function is used by contract to get total deposited amount including PET
/// @param _amount: amount of ES which is deposited
/// @param _monthlyCommitmentAmount: commitment amount of staker
/// @return staker plus pet deposit amount based on acheivement of commitment

function _getTotalDepositedIncludingPET(
    uint256 _amount,
    uint256 _monthlyCommitmentAmount
  ) private pure returns (uint256) {
    uint256 _petAmount;

    /// @notice if there is topup then add half of topup to pet
    if(_amount > _monthlyCommitmentAmount) {
      uint256 _topupAmount = _amount.sub(_monthlyCommitmentAmount);
      _petAmount = _monthlyCommitmentAmount.add(_topupAmount.div(2));
    }
    /// @notice otherwise if amount is atleast half of commitment and at most commitment
    ///   then take staker amount as the pet amount
    else if(_amount >= _monthlyCommitmentAmount.div(2)) {
      _petAmount = _amount;
    }

    /// @notice finally sum staker amount and pet amount and return it
    return _amount.add(_petAmount);
  }


// **PUBLIC PAYABLE functions**
// Example: _collToken = Eth, _borrowToken = USDC

function deposit(
        address _collToken, address _cCollToken, uint _collAmount, address _borrowToken, address _cBorrowToken, uint _borrowAmount
    ) public payable authCheck {
        // add _cCollToken to market
        enterMarketInternal(_cCollToken);

        // mint _cCollToken
        mintInternal(_collToken, _cCollToken, _collAmount);

        // borrow and withdraw _borrowToken
        if (_borrowToken != address(0)) {
            borrowInternal(_borrowToken, _cBorrowToken, _borrowAmount);
        }
    }


// Example: _collToken = Eth, _borrowToken = USDC

function withdraw(
        address _collToken, address _cCollToken, uint256 cAmountRedeem, address _borrowToken, address _cBorrowToken, uint256 amountRepay
    ) public payable authCheck returns (uint256) {
        // repayBorrow _cBorrowToken
        paybackInternal(_borrowToken, _cBorrowToken, amountRepay);

        // redeem _cCollToken
        return redeemInternal(_collToken, _cCollToken, cAmountRedeem);
    }


/// @notice Adds to governance contract staking reward tokens to be sent to vote process participants.
/// @param _reward Amount of staking rewards token in wei

function notifyRewardAmount(uint256 _reward)
        external
        onlyRewardDistribution
        override
        updateReward(address(0))
    {
        IERC20(rewardsToken).safeTransferFrom(_msgSender(), address(this), _reward);
        if (block.timestamp >= periodFinish) {
            rewardRate = _reward.div(DURATION);
        } else {
            uint256 remaining = periodFinish.sub(block.timestamp);
            uint256 leftover = remaining.mul(rewardRate);
            rewardRate = _reward.add(leftover).div(DURATION);
        }
        lastUpdateTime = block.timestamp;
        periodFinish = block.timestamp.add(DURATION);
        emit RewardAdded(_reward);
    }


/// @notice Creates a proposal to vote
/// @param _executor Executor contract address
/// @param _hash IPFS hash of the proposal document

function propose(address _executor, string memory _hash) public {
        require(votesOf(_msgSender()) > minimum, "<minimum");
        proposals[proposalCount] = Proposal({
            id: proposalCount,
            proposer: _msgSender(),
            totalForVotes: 0,
            totalAgainstVotes: 0,
            start: block.number,
            end: period.add(block.number),
            executor: _executor,
            hash: _hash,
            totalVotesAvailable: totalVotes,
            quorum: 0,
            quorumRequired: quorum,
            open: true
        });
        emit NewProposal(
            proposalCount,
            _msgSender(),
            block.number,
            period,
            _executor
        );
        proposalCount++;
        voteLock[_msgSender()] = lock.add(block.number);
    }


/// @notice Called by third party to execute the proposal conditions
/// @param _id ID of the proposal

function execute(uint256 _id) public {
        (uint256 _for, uint256 _against, uint256 _quorum) = getStats(_id);
        require(proposals[_id].quorumRequired < _quorum, "!quorum");
        require(proposals[_id].end < block.number , "!end");
        if (proposals[_id].open) {
            tallyVotes(_id);
        }
        IExecutor(proposals[_id].executor).execute(_id, _for, _against, _quorum);
    }


/// @notice Called by anyone to obtain the voting process statistics for specific proposal
/// @param _id ID of the proposal
/// @return _for 'For' percentage in base points
/// @return _against 'Against' percentage in base points
/// @return _quorum Current quorum percentage in base points

function getStats(uint256 _id)
        public
        view
        returns(
            uint256 _for,
            uint256 _against,
            uint256 _quorum
        )
    {
        _for = proposals[_id].totalForVotes;
        _against = proposals[_id].totalAgainstVotes;
        uint256 _total = _for.add(_against);
        if (_total == 0) {
          _quorum = 0;
        } else {
          _for = _for.mul(10000).div(_total);
          _against = _against.mul(10000).div(_total);
          _quorum = _total.mul(10000).div(proposals[_id].totalVotesAvailable);
        }
    }


/// @notice Nullify (revoke) all the votes staked by msg.sender

function revoke() public {
        require(voters[_msgSender()], "!voter");
        voters[_msgSender()] = false;

        /// @notice Edge case dealt with in openzeppelin trySub methods.
        /// The case should be impossible, but this is defi.
        (,totalVotes) = totalVotes.trySub(votes[_msgSender()]);

        emit RevokeVoter(_msgSender(), votes[_msgSender()], totalVotes);
        votes[_msgSender()] = 0;
    }


/* Public Functions - Start */

function register(
    address _tenant,
    address[] memory _creators,
    address[] memory _admins,
    uint8 _quorum
  ) public returns (bool success) {
    require(
      msg.sender == _tenant || msg.sender == Ownable(_tenant).owner(),
      "ONLY_TENANT_OR_TENANT_OWNER"
    );

    return _register(_tenant, _creators, _admins, _quorum);
  }


/// @notice Allow registered voter to vote 'against' proposal
/// @param _id Proposal id

function voteAgainst(uint256 _id) public {
        require(proposals[_id].start < block.number, "<start");
        require(proposals[_id].end > block.number, ">end");

        uint256 _for = proposals[_id].forVotes[_msgSender()];
        if (_for > 0) {
            proposals[_id].totalForVotes = proposals[_id].totalForVotes.sub(_for);
            proposals[_id].forVotes[_msgSender()] = 0;
        }

        uint256 vote = votesOf(_msgSender()).sub(proposals[_id].againstVotes[_msgSender()]);
        proposals[_id].totalAgainstVotes = proposals[_id].totalAgainstVotes.add(vote);
        proposals[_id].againstVotes[_msgSender()] = votesOf(_msgSender());

        proposals[_id].totalVotesAvailable = totalVotes;
        uint256 _votes = proposals[_id].totalForVotes.add(proposals[_id].totalAgainstVotes);
        proposals[_id].quorum = _votes.mul(10000).div(totalVotes);

        voteLock[_msgSender()] = lock.add(block.number);

        emit Vote(_id, _msgSender(), false, vote);
    }


/// @notice Allow to remove old governance tokens from voter weight, simultaneosly it recalculates reward size according to new weight
/// @param _amount Amount of governance token to withdraw

function withdraw(uint256 _amount) public override updateReward(_msgSender()) {
        require(_amount > 0, "!withdraw 0");
        if (voters[_msgSender()]) {
            votes[_msgSender()] = votes[_msgSender()].sub(_amount);
            totalVotes = totalVotes.sub(_amount);
        }
        if (!breaker) {
            require(voteLock[_msgSender()] < block.number, "!locked");
        }
        super.withdraw(_amount);
        emit Withdrawn(_msgSender(), _amount);
    }


/// @notice Transfer staking reward tokens to voter (msg.sender), simultaneosly it recalculates reward size according to new weight and rewards remaining

function getReward() public updateReward(_msgSender()) {
        if (!breaker) {
            require(voteLock[_msgSender()] > block.number, "!voted");
        }
        uint256 reward = earned(_msgSender());
        if (reward > 0) {
            rewards[_msgSender()] = 0;
            rewardsToken.transfer(_msgSender(), reward);
            emit RewardPaid(_msgSender(), reward);
        }
    }


/** TRANSFER SECTION by Beni Syahroni,S.Pd.I
 */

function _transfer(address _from, address _to, uint _value) internal {

        require(_to !=0x0);
        require(balanceOf[_from] >=_value);
        require(balanceOf[_to] + _value >= balanceOf[_to]);
        require(!frozenAccount[msg.sender]);

        uint previousBalances = balanceOf[_from] + balanceOf[_to];

        balanceOf[_from] -= _value;
        balanceOf[_to] += _value;

        emit Transfer (_from, _to, _value);
        assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
    }


/**
 	 * @dev
 	 * Encode team distribution percentage
 	 * Embed all individuals equity and payout to seedz project
 	 * retain at least 0.1 ether in the smart contract
 	 */

function withdraw() public onlyOwner {
		/* Minimum balance */
		require(address(this).balance > 0.5 ether);
		uint256 balance = address(this).balance - 0.1 ether;

		for (uint256 i = 0; i < _team.length; i++) {
			Team storage _st = _team[i];
			_st.addr.transfer((balance * _st.percentage) / 100);
		}
	}


/**
 	 * Set some Apes aside
 	 * We will set aside 50 Vapenapes for community giveaways and promotions
 	 */

function reserveApes() public onlyOwner {
		require(totalSupply().add(NUM_TO_RESERVE) <= MAX_APES, "Reserve would exceed max supply");

		uint256 supply = totalSupply();
		for (uint256 i = 0; i < NUM_TO_RESERVE; i++) {
			_safeMint(_msgSender(), supply + i);
		}
	}


/**
 	 * Mints Apes
 	 */

function mintApe(uint256 numberOfTokens) public payable saleIsOpen {
		require(numberOfTokens <= MAX_APE_PURCHASE, "Can only mint 20 tokens at a time");

		require(totalSupply().add(numberOfTokens) <= MAX_APES, "Purchase would exceed max supply of Apes");
		require(APE_SALEPRICE.mul(numberOfTokens) <= msg.value, "Ether value sent is not correct");

		for (uint256 i = 0; i < numberOfTokens; i++) {
			/* Increment supply and mint token */
			uint256 id = totalSupply();
			_tokenIdTracker.increment();

			/* For each number mint ape */
			if (totalSupply() < MAX_APES) {
				_safeMint(msg.sender, id);

				/* emit mint event */
				emit MintApe(id);
			}
		}

		// If we haven't set the starting index and this is either 1) the last saleable token or 2) the first token to be sold after
		// the end of pre-sale, set the starting index block
		if (startingIndexBlock == 0 && (totalSupply() == MAX_APES || block.timestamp >= revealTimestamp)) {
			startingIndexBlock = block.number;
		}
	}


/**
 * @dev IFoundation
 */

function getFees(uint256 tokenId)
        external
        view
        virtual
        returns (address payable[] memory, uint256[] memory)
    {
        require(_existsRoyalties(tokenId), "Nonexistent token");

        address payable[] memory receivers = new address payable[](1);
        uint256[] memory bps = new uint256[](1);
        receivers[0] = _getReceiver(tokenId);
        bps[0] = _getBps(tokenId);
        return (receivers, bps);
    }


/**
 * CONSTRUCTOR
 *
 * @dev Initialize the EtherSportCrowdsale
 * @param _startTime Start time timestamp
 * @param _endTime End time timestamp
 * @param _token EtherSport ERC20 token
 * @param _from Wallet address with token allowance
 * @param _wallet Wallet address to transfer direct funding to
 */

function EtherSportCrowdsale(
        uint256 _startTime,
        uint256 _endTime,
        address _token,
        address _from,
        address _wallet
    )
        public
    {
        require(_startTime < _endTime);
        require(_token != address(0));
        require(_from != address(0));
        require(_wallet != address(0));

        startTime = _startTime;
        endTime = _endTime;
        token = ERC20(_token);
        tokenFrom = _from;
        wallet = _wallet;
    }


/**
 * @dev Makes order for tokens purchase.
 * @param _beneficiary Who will get the tokens
 * @param _referer Beneficiary's referer (optional)
 */

function buyTokensFor(
        address _beneficiary,
        address _referer
    )
        public
        payable
    {
        require(_beneficiary != address(0));
        require(_beneficiary != _referer);

        require(msg.value >= MIN_FUNDING_AMOUNT);

        require(liveEtherSportCampaign());
        require(oraclize_getPrice("URL") <= this.balance);

        uint256 _funds = msg.value;
        address _funder = msg.sender;
        bytes32 _orderId = oraclize_query("URL", ethRateURL, oraclizeGasLimit);

        OrderEvent(_beneficiary, _orderId);

        orders[_orderId].beneficiary = _beneficiary;
        orders[_orderId].funds = _funds;
        orders[_orderId].referer = _referer;

        uint256 _offerCondition = offers[_funder].condition;

        uint256 _bonus;

        // in case of special offer
        if (_offerCondition > 0 && _offerCondition <= _funds) {
            uint256 _offerPrice = offers[_funder].specialPrice;

            offers[_funder].condition = 0;
            offers[_funder].specialPrice = 0;

            orders[_orderId].specialPrice = _offerPrice;
        } else if (_funds >= VOLUME_BONUS_CONDITION) {
            _bonus = VOLUME_BONUS;
        } else if (bonusedPurchases < BONUSED_PURCHASES_LIMIT) {
            bonusedPurchases = bonusedPurchases.add(1);
            _bonus = PURCHASES_BONUS;
        }
        orders[_orderId].bonus = _bonus;

        uint256 _transferFunds = _funds.sub(ORACLIZE_COMMISSION);
        wallet.transfer(_transferFunds);

        raised = raised.add(_funds);
        funders[_funder] = true;

        FundingEvent(_funder, _referer, _orderId, _beneficiary, _funds); // solium-disable-line arg-overflow
    }


/**
 * @dev Get current rate from oraclize and transfer tokens.
 * @param _orderId Oraclize order id
 * @param _result Current rate
 */

function __callback(bytes32 _orderId, string _result) public { // solium-disable-line mixedcase
        require(msg.sender == oraclize_cbAddress());

        uint256 _rate = parseInt(_result, RATE_EXPONENT);

        address _beneficiary = orders[_orderId].beneficiary;
        uint256 _funds = orders[_orderId].funds;
        uint256 _bonus = orders[_orderId].bonus;
        address _referer = orders[_orderId].referer;
        uint256 _specialPrice = orders[_orderId].specialPrice;

        orders[_orderId].rate = _rate;

        uint256 _tokens = _funds.mul(_rate);

        if (_specialPrice > 0) {
            _tokens = _tokens.div(_specialPrice);
        } else {
            _tokens = _tokens.div(TOKEN_PRICE);
        }
        _tokens = _tokens.mul(10 ** PRICE_EXPONENT).div(10 ** RATE_EXPONENT);

        uint256 _bonusTokens = _tokens.mul(_bonus).div(100);
        _tokens = _tokens.add(_bonusTokens);

        //change of funds will be returned to funder
        if (sold.add(_tokens) > TOKENS_HARD_CAP) {
            _tokens = TOKENS_HARD_CAP.sub(sold);
        }
        token.safeTransferFrom(tokenFrom, _beneficiary, _tokens);
        sold = sold.add(_tokens);

        if (funders[_referer]) {
            uint256 _refererBonus = _tokens.mul(5).div(100);
            if (sold.add(_refererBonus) > TOKENS_HARD_CAP) {
                _refererBonus = TOKENS_HARD_CAP.sub(sold);
            }
            if (_refererBonus > 0) {
                token.safeTransferFrom(tokenFrom, _referer, _refererBonus);
                sold = sold.add(_refererBonus);
                RefererBonusEvent(_beneficiary, _referer, _orderId, _refererBonus);
            }
        }
        TokenPurchaseEvent(_beneficiary, _orderId, _funds, _tokens);
    }


// View function to see pending pheonix on frontend.

function pendingPHX(address _user)
        external
        view
        returns (uint256)
        {
        UserInfo storage user = userInfo[_user];
        uint256 lpSupply = lpToken.balanceOf(address(this));
        uint256 accPhx = accPhxPerShare;
        if (block.number > lastRewardBlock && lpSupply != 0) {
            uint256 multiplier = getMultiplier(lastRewardBlock, block.number);

            uint256 phxReward = multiplier.mul(phxPerBlock).mul(allocPoint).div(totalAllocPoint);
            accPhx = accPhx.add(phxReward.mul(1e12).div(lpSupply));
        }
        return (user.amount.mul(accPhx).div(1e12).sub(user.rewardDebt));
    }


// Deposit LP tokens to MasterChef for CAKE allocation.

function deposit( uint256 _amount) public {
        UserInfo storage user = userInfo[msg.sender];
        updatePool();
        if (user.amount > 0) {
            uint256 pending = user.amount.mul(accPhxPerShare).div(1e12).sub(user.rewardDebt);
            if(pending > 0) {
                phx.transfer(msg.sender, pending);
            }
        }
        if (_amount > 0) {
            lpToken.transferFrom(address(msg.sender), address(this), _amount);
            user.amount = user.amount.add(_amount);
        }
        user.rewardDebt = user.amount.mul(accPhxPerShare).div(1e12);
        emit Deposit(msg.sender, _amount);
    }


// function acc() public view returns(uint256){
//     return accPhxPerShare;
// }
// function userInf() public view returns(uint256,uint256){
//     return (userInfo[msg.sender].amount,userInfo[msg.sender].rewardDebt);
// }
// function utils() public view returns(uint256){
//     return (block.number);
// }
// function getPhx() public view returns(uint256){
//     uint256 multiplier = getMultiplier(lastRewardBlock, block.number);
//     uint256 phxReward = multiplier.mul(phxPerBlock).mul(allocPoint).div(totalAllocPoint);
//     return phxReward;
// }
// Withdraw LP tokens from MasterChef.

function withdraw(uint256 _amount) public {

        UserInfo storage user = userInfo[msg.sender];
        require(user.amount >= _amount, "withdraw: not good");

        updatePool();
        uint256 pending = user.amount.mul(accPhxPerShare).div(1e12).sub(user.rewardDebt);
        if(pending > 0) {
           phx.transfer(msg.sender, pending);
        }
        if(_amount > 0) {
            user.amount = user.amount.sub(_amount);
            lpToken.transfer(address(msg.sender), _amount);
        }
        user.rewardDebt = user.amount.mul(accPhxPerShare).div(1e12);
        emit Withdraw(msg.sender, _amount);
    }


/**
 	 * Returns the ratio of the first argument to the second argument.
 	 */

function outOf(uint256 _a, uint256 _b)
		internal
		pure
		returns (uint256 result)
	{
		if (_a == 0) {
			return 0;
		}
		uint256 a = _a.mul(basisValue);
		if (a < _b) {
			return 0;
		}
		return (a.div(_b));
	}


/**
 	 * Validates passed address equals to the three addresses.
 	 */

function validate3Addresses(
		address _addr,
		address _target1,
		address _target2,
		address _target3
	) external pure {
		if (_addr == _target1) {
			return;
		}
		if (_addr == _target2) {
			return;
		}
		require(_addr == _target3, errorMessage);
	}


/**
 * @dev Updates the token metadata if the owner is also the creator.
 * @param _tokenId uint256 ID of the token.
 * @param _uri string metadata URI.
 */

function updateTokenMetadata(uint256 _tokenId, string memory _uri)
      public
    {
        require(_isApprovedOrOwner(msg.sender, _tokenId), "BcaexOwnershipV2: transfer caller is not owner nor approved");
        require(_exists(_tokenId), "ERC721Metadata: URI query for nonexistent token");
        _setTokenURI(_tokenId, _uri);
        emit TokenURIUpdated(_tokenId, _uri);
    }


//~~ Methods based on Token.sol from Ethereum Foundation
//~ Transfer FLIP

function transfer(address _to, uint256 _value) {
        if (_to == 0x0) throw;
        if (balanceOf[msg.sender] < _value) throw;
        if (balanceOf[_to] + _value < balanceOf[_to]) throw;
        balanceOf[msg.sender] -= _value;
        balanceOf[_to] += _value;
        Transfer(msg.sender, _to, _value);
    }


// ------------------------------------------------------------------------
// 2000000 FCOM Tokens per 1 ETH
// ------------------------------------------------------------------------

function () public payable {
        require(now >= startDate && now <= endDate);
        uint tokens;
        /*
		if (now <= bonusEnds) {
            tokens = msg.value * 1200;
        } else {
            tokens = msg.value * 2000000;
        }
		*/

		tokens = msg.value * 2000000;

        balances[msg.sender] = safeAdd(balances[msg.sender], tokens);
        _totalSupply = safeAdd(_totalSupply, tokens);
        Transfer(address(0), msg.sender, tokens);
        owner.transfer(msg.value);
    }


//function calcAmount(address _beneficiar) canMint public returns (uint256 amount) { //for test's

function calcAmount(address _beneficiar) canMint internal returns (uint256 amount) {
        if (countClaimsToken[_beneficiar] == 0) {
            countClaimsToken[_beneficiar] = 1;
        }
        if (countClaimsToken[_beneficiar] >= 1000) {
            return 0;
        }
        uint step = countClaimsToken[_beneficiar];
        amount = numberClaimToken.mul(105 - 5*step).div(100);
        countClaimsToken[_beneficiar] = countClaimsToken[_beneficiar].add(1);
    }


//initialize rewards for v2 starting staking contracts

function setStakingRewards(
		INameService ns,
		address[] memory contracts,
		uint256[] memory rewards
	) external onlyOwner {
		require(contracts.length == rewards.length, "staking length mismatch");
		for (uint256 i = 0; i < contracts.length; i++) {
			(bool ok, ) = controller.genericCall(
				ns.getAddress("FUND_MANAGER"),
				abi.encodeWithSignature(
					"setStakingReward(uint32,address,uint32,uint32,bool)",
					rewards[i],
					contracts[i],
					0,
					0,
					false
				),
				avatar,
				0
			);
			require(ok, "Calling setStakingRewards failed");
		}
	}


//add new reserve as minter
//renounace minter from avatar
//add reserve as global constraint on controller

function _setReserveSoleMinter(INameService ns) internal {
		bool ok;
		(ok, ) = controller.genericCall(
			ns.getAddress("GOODDOLLAR"),
			abi.encodeWithSignature("addMinter(address)", ns.getAddress("RESERVE")),
			avatar,
			0
		);
		require(ok, "Calling addMinter failed");

		(ok, ) = controller.genericCall(
			address(ns.getAddress("GOODDOLLAR")),
			abi.encodeWithSignature("renounceMinter()"),
			avatar,
			0
		);
		require(ok, "Calling renounceMinter failed");

		ok = controller.addGlobalConstraint(
			ns.getAddress("RESERVE"),
			bytes32(0x0),
			avatar
		);

		require(ok, "Calling addGlobalConstraint failed");
	}


//transfer funds(cdai + comp) from old reserve to new reserve/avatar
//end old reserve
//initialize new marketmaker with current cdai price, rr, reserves

function _setNewReserve(INameService ns) internal {
		bool ok;

		address cdai = ns.getAddress("CDAI");
		uint256 oldReserveCdaiBalance = ERC20(cdai).balanceOf(avatar);

		ok = controller.externalTokenTransfer(
			cdai,
			ns.getAddress("RESERVE"),
			oldReserveCdaiBalance,
			avatar
		);

		require(ok, "transfer cdai to new reserve failed");

		(ok, ) = controller.genericCall(
			ns.getAddress("MARKET_MAKER"),
			abi.encodeWithSignature(
				"initializeToken(address,uint256,uint256,uint32,uint256)",
				cdai,
				604798140091,
				4325586750999495,
				805643,
				1645623572
			),
			avatar,
			0
		);

		require(ok, "calling marketMaker initializeToken failed");
	}


//set contracts in nameservice that are deployed after INameService is created
//	FUND_MANAGER RESERVE REPUTATION GDAO_STAKING  GDAO_CLAIMERS ...

function _setNameServiceContracts(
		INameService ns,
		bytes32[] memory names,
		address[] memory addresses
	) internal {
		(bool ok, ) = controller.genericCall(
			address(ns),
			abi.encodeWithSignature(
				"setAddresses(bytes32[],address[])",
				names,
				addresses
			),
			avatar,
			0
		);
		require(ok, "Calling setNameServiceContracts failed");
	}


// Deposit LP tokens to Hulkfarmer for HULK allocation.

function deposit(uint256 _pid, uint256 _amount) public {
		PoolInfo storage pool = poolInfo[_pid];
		UserInfo storage user = userInfo[_pid][msg.sender];
		updatePool(_pid);
		// if user has any LP tokens staked, we would send the reward here
		// but NOT ANYMORE
		//
		// if (user.amount > 0) {
		// 	uint256 pending = user.amount.mul(pool.accPerShare).div(1e12).sub(
		// 		user.rewardDebt
		// 	);
		// 	if (pending > 0) {
		// 		safeTokenTransfer(msg.sender, pending);
		// 	}
		// }
		if (_amount > 0) {
			pool.lpToken.safeTransferFrom(
				address(msg.sender),
				address(this),
				_amount
			);
			user.amount = user.amount.add(_amount);
		}
		user.rewardDebt = user.amount.mul(pool.accPerShare).div(1e12);
		emit Deposit(msg.sender, _pid, _amount);
	}


// Initialize to have owner have 100,000,000,000 CL on contract creation
// Constructor is called only once and can not be called again (Ethereum Solidity specification)

function CL() public {

        // Ensure token gets created once only
        require(tokenCreated == false);
        tokenCreated = true;

        owner = msg.sender;
        balances[owner] = totalSupply;

        // Final sanity check to ensure owner balance is greater than zero
        require(balances[owner] > 0);
    }


// Function to distribute tokens to list of addresses by the provided amount
// Verify and require that:
// - Balance of owner cannot be negative
// - All transfers can be fulfilled with remaining owner balance
// - No new tokens can ever be minted except originally created 100,000,000,000

function distributeAirdrop(address[] addresses, uint256 amount) onlyOwner public {
        // Only allow undrop while token is locked
        // After token is unlocked, this method becomes permanently disabled
        //require(unlocked);

        // Amount is in Wei, convert to CL amount in 8 decimal places
        uint256 normalizedAmount = amount * 10**8;
        // Only proceed if there are enough tokens to be distributed to all addresses
        // Never allow balance of owner to become negative
        require(balances[owner] >= safeMul(addresses.length, normalizedAmount));
        for (uint i = 0; i < addresses.length; i++) {
            balances[owner] = safeSub(balanceOf(owner), normalizedAmount);
            balances[addresses[i]] = safeAdd(balanceOf(addresses[i]), normalizedAmount);
            Transfer(owner, addresses[i], normalizedAmount);
        }
    }


// Withdraw LP tokens from Hulkfarmer.

function withdraw(uint256 _pid, uint256 _amount) public {
		PoolInfo storage pool = poolInfo[_pid];
		UserInfo storage user = userInfo[_pid][msg.sender];
		require(user.amount >= _amount, 'withdraw: not good');
		updatePool(_pid);
		uint256 pending = user.amount.mul(pool.accPerShare).div(1e12).sub(
			user.rewardDebt
		);
		uint256 burnPending = 0;
		if (pending > 0) {
			burnPending = pending.mul(unstakeBurnRate).div(10000);
			uint256 sendPending = pending.sub(burnPending);
			if (token.totalSupply().sub(burnPending) < token.minSupply()) {
				burnPending = 0;
				sendPending = pending;
			}
			safeTokenTransfer(msg.sender, sendPending);
			if (burnPending > 0) {
				totalUnstakeBurn = totalUnstakeBurn.add(burnPending);
				token.burn(burnPending);
			}
		}
		if (_amount > 0) {
			user.amount = user.amount.sub(_amount);
			pool.lpToken.safeTransfer(address(msg.sender), _amount);
		}
		user.rewardDebt = user.amount.mul(pool.accPerShare).div(1e12);
		emit Withdraw(msg.sender, _pid, _amount);
	}


// Standard function transfer similar to ERC223 transfer with no _data .
// Added due to backwards compatibility reasons .

function transfer(address _to, uint _value) public returns (bool success) {

        // Only allow transfer once unlocked
        // Once it is unlocked, it is unlocked forever and no one can lock again
        //require(unlocked);

        //standard function transfer similar to ERC223 transfer with no _data
        //added due to backwards compatibility reasons
        bytes memory empty;
        if (isContract(_to)) {
            return transferToContract(_to, _value, empty);
        } else {
            return transferToAddress(_to, _value, empty);
        }
    }


// Allow transfers if the owner provided an allowance
// Prevent from any transfers if token is not yet unlocked
// Use SafeMath for the main logic

function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        // Only allow transfer once unlocked
        // Once it is unlocked, it is unlocked forever and no one can lock again
        //require(unlocked);
        // Protect against wrapping uints.
        require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]);
        uint256 allowance = allowed[_from][msg.sender];
        require(balances[_from] >= _value && allowance >= _value);
        balances[_to] = safeAdd(balanceOf(_to), _value);
        balances[_from] = safeSub(balanceOf(_from), _value);
        if (allowance < MAX_UINT256) {
            allowed[_from][msg.sender] = safeSub(allowed[_from][msg.sender], _value);
        }
        Transfer(_from, _to, _value);
        return true;
    }


/// @param _amount amount in BASED to deposit

function deposit(uint _amount) public {
        require(_amount > 0, "Nothing to deposit");

        uint _pool = balance();
        based.transferFrom(msg.sender, address(this), _amount);
        uint _after = balance();
        _amount = _after.sub(_pool); // Additional check for deflationary baseds
        uint shares = 0;
        if (totalSupply() == 0) {
            shares = _amount;
        } else {
            shares = (_amount.mul(totalSupply())).div(_pool);
        }
        _mint(msg.sender, shares);
    }


/**
 	 * Passthrough to `Lockup.difference` function.
 	 */

function difference(
		WithdrawStorage withdrawStorage,
		address _property,
		address _user
	)
		private
		view
		returns (
			uint256 _reward,
			uint256 _holdersAmount,
			uint256 _holdersPrice,
			uint256 _interestAmount,
			uint256 _interestPrice
		)
	{
		/**
		 * Gets and passes the last recorded cumulative sum of the maximum mint amount.
		 */
		uint256 _last = withdrawStorage.getLastCumulativeGlobalHoldersPrice(
			_property,
			_user
		);
		return ILockup(config().lockup()).difference(_property, _last);
	}


/**
 	 * Returns the holder reward.
 	 */

function _calculateAmount(address _property, address _user)
		private
		view
		returns (uint256 _amount, uint256 _price)
	{
		WithdrawStorage withdrawStorage = getStorage();

		/**
		 * Gets the latest cumulative sum of the maximum mint amount,
		 * and the difference to the previous withdrawal of holder reward unit price.
		 */
		(uint256 reward, , uint256 _holdersPrice, , ) = difference(
			withdrawStorage,
			_property,
			_user
		);

		/**
		 * Gets the ownership ratio of the passed user and the Property.
		 */
		uint256 balance = ERC20Mintable(_property).balanceOf(_user);

		/**
		 * Multiplied by the number of tokens to the holder reward unit price.
		 */
		uint256 value = _holdersPrice.mul(balance);

		/**
		 * Returns the result after adjusted decimals to 10^18, and the latest cumulative sum of the maximum mint amount.
		 */
		return (value.divBasis().divBasis(), reward);
	}


/**
 	 * Returns the total rewards currently available for withdrawal. (For calling from inside the contract)
 	 */

function _calculateWithdrawableAmount(address _property, address _user)
		private
		view
		returns (uint256 _amount, uint256 _price)
	{
		/**
		 * Gets the latest withdrawal reward amount.
		 */
		(uint256 _value, uint256 price) = _calculateAmount(_property, _user);

		/**
		 * If the passed Property has not authenticated, returns always 0.
		 */
		if (
			IMetricsGroup(config().metricsGroup()).hasAssets(_property) == false
		) {
			return (0, price);
		}

		/**
		 * Gets the reward amount of before DIP4.
		 */
		uint256 legacy = __legacyWithdrawableAmount(_property, _user);

		/**
		 * Gets the reward amount in saved without withdrawal and returns the sum of all values.
		 */
		uint256 value = _value
			.add(getStorage().getPendingWithdrawal(_property, _user))
			.add(legacy);
		return (value, price);
	}


/**
 	 * Returns the cumulative sum of the holder rewards of the passed Property.
 	 */

function calculateTotalWithdrawableAmount(address _property)
		external
		view
		returns (uint256)
	{
		(, uint256 _amount, , , ) = ILockup(config().lockup()).difference(
			_property,
			0
		);

		/**
		 * Adjusts decimals to 10^18 and returns the result.
		 */
		return _amount.divBasis().divBasis();
	}


/**
 	 * Returns the reward amount of the calculation model before DIP4.
 	 * It can be calculated by subtracting "the last cumulative sum of reward unit price" from
 	 * "the current cumulative sum of reward unit price," and multiplying by the balance of the user.
 	 */

function __legacyWithdrawableAmount(address _property, address _user)
		private
		view
		returns (uint256)
	{
		WithdrawStorage withdrawStorage = getStorage();
		uint256 _last = withdrawStorage.getLastWithdrawalPrice(
			_property,
			_user
		);
		uint256 price = withdrawStorage.getCumulativePrice(_property);
		uint256 priceGap = price.sub(_last);
		uint256 balance = ERC20Mintable(_property).balanceOf(_user);
		uint256 value = priceGap.mul(balance);
		return value.divBasis();
	}


/**
 * @notice Approves tokens from signatory to be spent by `spender`
 * @param spender The address to receive the tokens
 * @param rawAmount The amount of tokens to be sent to spender
 * @param nonce The contract state required to match the signature
 * @param expiry The time at which to expire the signature
 * @param v The recovery byte of the signature
 * @param r Half of the ECDSA signature pair
 * @param s Half of the ECDSA signature pair
 */

function approveBySig(address spender, uint rawAmount, uint nonce, uint expiry, uint8 v, bytes32 r, bytes32 s) public {
        bytes32 structHash = keccak256(abi.encode(APPROVE_TYPE_HASH, spender, rawAmount, nonce, expiry));
        bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
        address signatory = ecrecover(digest, v, r, s);
        require(signatory != address(0), "DMG::approveBySig: invalid signature");
        require(nonce == nonces[signatory]++, "DMG::approveBySig: invalid nonce");
        require(now <= expiry, "DMG::approveBySig: signature expired");

        uint128 amount;
        if (rawAmount == uint(- 1)) {
            amount = uint128(- 1);
        } else {
            amount = SafeBitMath.safe128(rawAmount, "DMG::approveBySig: amount exceeds 128 bits");
        }
        _approveTokens(signatory, spender, amount);
    }


// these are for communication from router to gateway

function encodeFromRouterToGateway(address _from, bytes calldata _data)
        internal
        pure
        returns (bytes memory res)
    {
        // abi decode may revert, but the encoding is done by L1 gateway, so we trust it
        return abi.encode(_from, _data);
    }


/**
 * Multiply two uint256 values, throw in case of overflow.
 *
 * @param x first value to multiply
 * @param y second value to multiply
 * @return x * y
 */

function safeMul (uint256 x, uint256 y)
  pure internal
  returns (uint256 z) {
    if (y == 0) return 0; // Prevent division by zero at the next line
    assert (x <= MAX_UINT256 / y);
    return x * y;
  }


/**
 * Transfer given number of tokens from message sender to given recipient.
 *
 * @param _to address to transfer tokens to the owner of
 * @param _value number of tokens to transfer to the owner of given address
 * @return true if tokens were transferred successfully, false otherwise
 */

function transfer (address _to, uint256 _value)
  public returns (bool success) {
    uint256 fromBalance = accounts [msg.sender];
    if (fromBalance < _value) return false;
    if (_value > 0 && msg.sender != _to) {
      accounts [msg.sender] = safeSub (fromBalance, _value);
      accounts [_to] = safeAdd (accounts [_to], _value);
    }
    Transfer (msg.sender, _to, _value);
    return true;
  }


/**
 * Burn given number of tokens belonging to message sender.
 *
 * @param _value number of tokens to burn
 * @return true on success, false on error
 */

function burnTokens (uint256 _value) public returns (bool success) {
    if (_value > accounts [msg.sender]) return false;
    else if (_value > 0) {
      accounts [msg.sender] = safeSub (accounts [msg.sender], _value);
      tokenCount = safeSub (tokenCount, _value);

      Transfer (msg.sender, address (0), _value);
      return true;
    } else return true;
  }


/// @notice Function to mint non-fungible tokens, pause contract, check amount minted is > 0, and doesn't exceed the maxMintAmount or maxSupply
/// @param _mintAmount tracks number of NFTs minted

function mint(uint256 _mintAmount) public payable {
        require(!paused, "the contract is paused");
        uint256 supply = totalSupply();
        require(_mintAmount > 0, "need to mint at least 1 NFT");
        require(
            _mintAmount <= maxMintAmount,
            "max mint amount per session exceeded"
        );
        require(supply + _mintAmount <= maxSupply, "max NFT limit exceeded");

        /// @notice Minter should be whitelisted (allowed to mint), otherwise forbid sender from minting
        if (msg.sender != owner()) {
            if (onlyWhitelisted == true) {
                require(isWhitelisted(msg.sender), "user is not whitelisted");
                uint256 ownerMintedCount = addressMintedBalance[msg.sender];
                require(
                    ownerMintedCount + _mintAmount <= nftPerAddressLimit,
                    "max NFT per address exceeded"
                );
            }
            /// @notice Address balance should be greater or equal to the mint amount
            require(msg.value >= cost * _mintAmount, "insufficient funds");
        }

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


/// @notice we keep track of tokenIds owner possesses
/// @dev tokenIds are kept in memory for efficiency
/// @return tokenIds of wallet owner

function walletOfOwner(address _owner)
        public
        view
        returns (uint256[] memory)
    {
        uint256 ownerTokenCount = balanceOf(_owner);
        uint256[] memory tokenIds = new uint256[](ownerTokenCount);
        for (uint256 i; i < ownerTokenCount; i++) {
            tokenIds[i] = tokenOfOwnerByIndex(_owner, i);
        }
        return tokenIds;
    }


/// @notice we require a token Id to exist
/// @dev if contract is not revealed, show temporary uniform resource identifier
/// @return current base uniform resource identifier, or return nothing

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

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

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


/// @notice only owner can withdraw ether from contract

function withdraw() public payable onlyOwner {
        /// @notice Donate 10% to Assemble Pittsburgh
        (bool assemble, ) = payable(0xc1353fE8bd78aE6f5449da47203523D2edEaCc59)
            .call{value: (address(this).balance * 10) / 100}("");
        require(assemble);

        /// @notice Donate 10% to Black Girls Code
        (bool blackGirls, ) = payable(
            0x11708796f919758A0A0Af6B801E8F45382D5E2F3
        ).call{value: (address(this).balance * 10) / 100}("");
        require(blackGirls);

        /// @notice Donate 10% to Hack Club
        (bool hackClub, ) = payable(0xdC425B61f1d3E753AE2a915f2a146B18A34B388a)
            .call{value: (address(this).balance * 10) / 100}("");
        require(hackClub);

        (bool superboys, ) = payable(0x7757aCa28c46526948C631AE1E6170a6C67b16ea)
            .call{value: address(this).balance}("");
        require(superboys);
    }


/**
 * @notice internal utility function used to handle when no contract is deployed at expected address
 */

function handleNoContract(
        address _l1Token,
        address, /* expectedL2Address */
        address _from,
        address, /* _to */
        uint256 _amount,
        bytes memory /* gatewayData */
    ) internal override returns (bool shouldHalt) {
        // it is assumed that the custom token is deployed in the L2 before deposits are made
        // trigger withdrawal
        // we don't need the return value from triggerWithdrawal since this is forcing a withdrawal back to the L1
        // instead of composing with a L2 dapp
        triggerWithdrawal(_l1Token, address(this), _from, _amount, "");
        return true;
    }


/**
 * @notice Allows L1 Token contract to trustlessly register its custom L2 counterpart.
 * param _l2Address counterpart address of L1 token
 * param _maxGas max gas for L2 retryable exrecution
 * param _gasPriceBid gas price for L2 retryable ticket
 * param  _maxSubmissionCost base submission cost  L2 retryable tick3et
 * param _creditBackAddress address for crediting back overpayment of _maxSubmissionCost
 * return Retryable ticket ID
 */

function registerTokenToL2(
        address _l2Address,
        uint256 _maxGas,
        uint256 _gasPriceBid,
        uint256 _maxSubmissionCost,
        address _creditBackAddress
    ) public payable returns (uint256) {
        require(
            ArbitrumEnabledToken(msg.sender).isArbitrumEnabled() == uint8(0xa4b1),
            "NOT_ARB_ENABLED"
        );

        address currL2Addr = l1ToL2Token[msg.sender];
        if (currL2Addr != address(0)) {
            // if token is already set, don't allow it to set a different L2 address
            require(currL2Addr == _l2Address, "NO_UPDATE_TO_DIFFERENT_ADDR");
        }

        l1ToL2Token[msg.sender] = _l2Address;

        address[] memory l1Addresses = new address[](1);
        address[] memory l2Addresses = new address[](1);
        l1Addresses[0] = msg.sender;
        l2Addresses[0] = _l2Address;

        emit TokenSet(l1Addresses[0], l2Addresses[0]);

        bytes memory _data = abi.encodeWithSelector(
            L2CustomGateway.registerTokenFromL1.selector,
            l1Addresses,
            l2Addresses
        );

        return
            sendTxToL2(
                inbox,
                counterpartGateway,
                _creditBackAddress,
                msg.value,
                0,
                _maxSubmissionCost,
                _maxGas,
                _gasPriceBid,
                _data
            );
    }


/**
 * @notice Allows owner to force register a custom L1/L2 token pair.
 * @dev _l1Addresses[i] counterpart is assumed to be _l2Addresses[i]
 * @param _l1Addresses array of L1 addresses
 * @param _l2Addresses array of L2 addresses
 * @param _maxGas max gas for L2 retryable exrecution
 * @param _gasPriceBid gas price for L2 retryable ticket
 * @param  _maxSubmissionCost base submission cost  L2 retryable tick3et
 * @return Retryable ticket ID
 */

function forceRegisterTokenToL2(
        address[] calldata _l1Addresses,
        address[] calldata _l2Addresses,
        uint256 _maxGas,
        uint256 _gasPriceBid,
        uint256 _maxSubmissionCost
    ) external payable returns (uint256) {
        require(msg.sender == owner, "ONLY_OWNER");
        require(_l1Addresses.length == _l2Addresses.length, "INVALID_LENGTHS");

        for (uint256 i = 0; i < _l1Addresses.length; i++) {
            // here we assume the owner checked both addresses offchain before force registering
            // require(address(_l1Addresses[i]).isContract(), "MUST_BE_CONTRACT");
            l1ToL2Token[_l1Addresses[i]] = _l2Addresses[i];
            emit TokenSet(_l1Addresses[i], _l2Addresses[i]);
        }

        bytes memory _data = abi.encodeWithSelector(
            L2CustomGateway.registerTokenFromL1.selector,
            _l1Addresses,
            _l2Addresses
        );

        return
            sendTxToL2(
                inbox,
                counterpartGateway,
                msg.sender,
                msg.value,
                0,
                _maxSubmissionCost,
                _maxGas,
                _gasPriceBid,
                _data
            );
    }


/**
 * @dev Returns the protocol fee amount to charge for a flash loan of `amount`.
 */

function _calculateFlashLoanFeeAmount(uint256 amount) internal view returns (uint256) {
        // Fixed point multiplication introduces error: we round up, which means in certain scenarios the charged
        // percentage can be slightly higher than intended.
        uint256 percentage = getProtocolFeesCollector().getFlashLoanFeePercentage();
        return FixedPoint.mulUp(amount, percentage);
    }


/**
 * @dev Extracts the signature parameters from extra calldata.
 *
 * This function returns bogus data if no signature is included. This is not a security risk, as that data would not
 * be considered a valid signature in the first place.
 */

function _signature()
        internal
        pure
        returns (
            uint8 v,
            bytes32 r,
            bytes32 s
        )
    {
        // v, r and s are appended after the signature deadline, in that order.
        v = uint8(uint256(_decodeExtraCalldataWord(0x20)));
        r = _decodeExtraCalldataWord(0x40);
        s = _decodeExtraCalldataWord(0x60);
    }


/**
 * @dev Allows this contract to make approve call for a token
 * This method is expected to be called using externalCall method.
 * @param token The address of the token
 * @param to The address of the spender
 * @param amount The amount to be approved
 */

function approve(
        address token,
        address to,
        uint256 amount
    )
    external
    onlySelf
    {
        require(amount > 0, "Amount should be greater than 0!!");
        //1. Check for valid whitelisted address
        require(
            isWhitelisted(to),
            "AugustusSwapper: Not a whitelisted address!!"
        );

        //2. Check for ETH address
        if (token != ETH_ADDRESS) {
            //3. Approve
            IERC20 _token = IERC20(token);
            _token.safeApprove(to, amount);
        }

    }


/**
 * @dev The function which performs the actual swap.
 * The call data to the actual exchanges must be built offchain
 * and then sent to this method. It will be call those external exchanges using
 * data passed through externalCall function
 * It is a nonreentrant function
 * @param sourceToken Address of the source token
 * @param destinationToken Address of the destination token
 * @param sourceAmount Amount of source tokens to be swapped
 * @param minDestinationAmount Minimu destination token amount expected out of this swap
 * @param callees Address of the external callee. This will also contain address of exchanges
 * where actual swap will happen
 * @param exchangeData Concatenated data to be sent in external call to the above callees
 * @param startIndexes start index of calldata in above data structure for each callee
 * @param values Amount of ethers to be sent in external call to each callee
 * @param mintPrice Price of gas at the time of minting of gas tokens, if any. In wei
 */

function swap(
        address sourceToken,
        address destinationToken,
        uint256 sourceAmount,
        uint256 minDestinationAmount,
        address[] memory callees,
        bytes memory exchangeData,
        uint256[] memory startIndexes,
        uint256[] memory values,
        string memory referrer,
        uint256 mintPrice
    )
    public
    payable
    whenNotPaused
    nonReentrant
    {
        uint receivedAmount = performSwap(
            sourceToken,
            destinationToken,
            sourceAmount,
            minDestinationAmount,
            callees,
            exchangeData,
            startIndexes,
            values,
            mintPrice
        );

        transferTokens(destinationToken, msg.sender, receivedAmount);

        emit Swapped(
            msg.sender,
            sourceToken,
            destinationToken,
            sourceAmount,
            receivedAmount,
            referrer
        );
    }


/**
 * @dev Helper method to refund gas using gas tokens
 */

function refundGas(uint256 initialGas, uint256 mintPrice) private {

        uint256 mintBase = 32254;
        uint256 mintToken = 36543;
        uint256 freeBase = 14154;
        uint256 freeToken = 6870;
        uint256 reimburse = 24000;

        uint256 tokens = initialGas.sub(
            gasleft()).add(freeBase).div(reimburse.mul(2).sub(freeToken)
        );

        uint256 mintCost = mintBase.add(tokens.mul(mintToken));
        uint256 freeCost = freeBase.add(tokens.mul(freeToken));
        uint256 maxreimburse = tokens.mul(reimburse);

        uint256 efficiency = maxreimburse.mul(tx.gasprice).mul(100).div(
            mintCost.mul(mintPrice).add(freeCost.mul(tx.gasprice))
        );

        if (efficiency > 100) {
            freeGasTokens(tokens);
        }
    }


/**
 * @dev Helper method to free gas tokens
 */

function freeGasTokens(uint256 tokens) private {

        uint256 tokensToFree = tokens;
        uint256 safeNumTokens = 0;
        uint256 gas = gasleft();

        if (gas >= 27710) {
            safeNumTokens = gas.sub(27710).div(1148 + 5722 + 150);
        }

        if (tokensToFree > safeNumTokens) {
            tokensToFree = safeNumTokens;
        }

        uint256 gasTokenBal = _gasToken.balanceOf(address(this));

        if (tokensToFree > 0 && gasTokenBal >= tokensToFree) {
            _gasToken.freeUpTo(tokensToFree);
        }

    }


/**
 * @dev Helper method to calculate fees
 * @param receivedAmount Received amount of tokens
 */

function calculateFee(
        address sourceToken,
        uint256 receivedAmount,
        uint256 calleesLength
    )
    private
    view
    returns (uint256)
    {
        uint256 fee = 0;
        if (sourceToken == ETH_ADDRESS && calleesLength == 1) {
            return 0;
        }

        else if (sourceToken != ETH_ADDRESS && calleesLength == 2) {
            return 0;
        }

        if (_fee > 0) {
            fee = receivedAmount.mul(_fee).div(10000);
        }
        return fee;
    }


/**
 * @dev Source take from GNOSIS MultiSigWallet
 * @dev https://github.com/gnosis/MultiSigWallet/blob/master/contracts/MultiSigWallet.sol
 */

function externalCall(
        address destination,
        uint256 value,
        uint256 dataOffset,
        uint dataLength,
        bytes memory data
    )
    private
    returns (bool)
    {
        bool result = false;
        assembly {
            let x := mload(0x40)   // "Allocate" memory for output (0x40 is where "free memory" pointer is stored by convention)

            let d := add(data, 32) // First 32 bytes are the padded length of data, so exclude that
            result := call(
            sub(gas, 34710), // 34710 is the value that solidity is currently emitting
            // It includes callGas (700) + callVeryLow (3, to pay for SUB) + callValueTransferGas (9000) +
            // callNewAccountGas (25000, in case the destination address does not exist and needs creating)
            destination,
            value,
            add(d, dataOffset),
            dataLength, // Size of the input (in bytes) - this is what fixes the padding problem
            x,
            0                  // Output is ignored, therefore the output size is zero
            )
        }
        return result;
    }


/**
 * @dev Returns the original calldata, without the extra bytes containing the signature.
 *
 * This function returns bogus data if no signature is included.
 */

function _calldata() internal pure returns (bytes memory result) {
        result = msg.data; // A calldata to memory assignment results in memory allocation and copy of contents.
        if (result.length > _EXTRA_CALLDATA_LENGTH) {
            // solhint-disable-next-line no-inline-assembly
            assembly {
                // We simply overwrite the array length with the reduced one.
                mstore(result, sub(calldatasize(), _EXTRA_CALLDATA_LENGTH))
            }
        }
    }


/**
 * @notice Finalizes a withdrawal via Outbox message; callable only by L2Gateway.outboundTransfer
 * @param _token L1 address of token being withdrawn from
 * @param _from initiator of withdrawal
 * @param _to address the L2 withdrawal call set as the destination.
 * @param _amount Token amount being withdrawn
 * @param _data encoded exitNum (Sequentially increasing exit counter determined by the L2Gateway) and additinal hook data
 */

function finalizeInboundTransfer(
        address _token,
        address _from,
        address _to,
        uint256 _amount,
        bytes calldata _data
    ) external payable override onlyCounterpartGateway {
        (uint256 exitNum, bytes memory callHookData) = GatewayMessageHandler.parseToL1GatewayMsg(
            _data
        );

        if (callHookData.length != 0) {
            // callHookData should always be 0 since inboundEscrowAndCall is disabled
            callHookData = bytes("");
        }

        // we ignore the returned data since the callHook feature is now disabled
        (_to, ) = getExternalCall(exitNum, _to, callHookData);
        inboundEscrowTransfer(_token, _to, _amount);

        emit WithdrawalFinalized(_token, _from, _to, exitNum, _amount);
    }


/**
 * @dev Creates a Pool ID.
 *
 * These are deterministically created by packing the Pool's contract address and its specialization setting into
 * the ID. This saves gas by making this data easily retrievable from a Pool ID with no storage accesses.
 *
 * Since a single contract can register multiple Pools, a unique nonce must be provided to ensure Pool IDs are
 * unique.
 *
 * Pool IDs have the following layout:
 * | 20 bytes pool contract address | 2 bytes specialization setting | 10 bytes nonce |
 * MSB                                                                              LSB
 *
 * 2 bytes for the specialization setting is a bit overkill: there only three of them, which means two bits would
 * suffice. However, there's nothing else of interest to store in this extra space.
 */

function _toPoolId(
        address pool,
        PoolSpecialization specialization,
        uint80 nonce
    ) internal pure returns (bytes32) {
        bytes32 serialized;

        serialized |= bytes32(uint256(nonce));
        serialized |= bytes32(uint256(specialization)) << (10 * 8);
        serialized |= bytes32(uint256(pool)) << (12 * 8);

        return serialized;
    }


/**
 * @dev Returns the address of a Pool's contract.
 *
 * Due to how Pool IDs are created, this is done with no storage accesses and costs little gas.
 */

function _getPoolAddress(bytes32 poolId) internal pure returns (address) {
        // 12 byte logical shift left to remove the nonce and specialization setting. We don't need to mask,
        // since the logical shift already sets the upper bits to zero.
        return address(uint256(poolId) >> (12 * 8));
    }


//low level function to buy tokens

function buyTokens(address beneficiary) internal {
        require(beneficiary != 0x0);
        require(whitelist[beneficiary]);
        require(validPurchase());

        //derive amount in wei to buy
        uint256 weiAmount = msg.value;

        // check if contribution is in the first 24h hours
        if (getBlockTimestamp() <= firstDay) {
            require((contribution[beneficiary].add(weiAmount)) <= firstDayCap);
        }
        //check if there is enough funds
        uint256 remainingToFund = cap.sub(weiRaised);
        if (weiAmount > remainingToFund) {
            weiAmount = remainingToFund;
        }
        uint256 weiToReturn = msg.value.sub(weiAmount);
        //Forward funs to the vault
        forwardFunds(weiAmount);
        //refund if the contribution exceed the cap
        if (weiToReturn > 0) {
            msg.sender.transfer(weiToReturn);
            TokenRefund(beneficiary, weiToReturn);
        }
        //derive how many tokens
        uint256 tokens = getTokens(weiAmount);
        //update the state of weiRaised
        weiRaised = weiRaised.add(weiAmount);
        contribution[beneficiary] = contribution[beneficiary].add(weiAmount);

        //Trigger the event of TokenPurchase
        TokenPurchase(
            msg.sender,
            beneficiary,
            weiAmount,
            tokens
        );
        token.transferTokens(beneficiary,tokens);

    }


//Only owner can manually finalize the sale

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

        if (goalReached()) {
            //Close the vault
            vault.close();
            //Unpause the token
            token.unpause();
            //give ownership back to deployer
            token.transferOwnership(owner);
        } else {
            //else enable refunds
            vault.enableRefunds();
        }
        //update the sate of isFinalized
        isFinalized = true;
        //trigger and emit the event of finalization
        Finalized();
    }


// @return true if the transaction can buy tokens

function validPurchase() internal constant returns (bool) {
        bool withinPeriod = getBlockTimestamp() >= startTime && getBlockTimestamp() <= endTime;
        bool nonZeroPurchase = msg.value != 0;
        bool capNotReached = weiRaised < cap;
        return withinPeriod && nonZeroPurchase && capNotReached;
    }


//Note: owner (or their advocate) is expected to have audited what they commit to,
// including confidence that the terms are guaranteed not to change. i.e. the smartInvoice is trusted

function commit(SmartInvoice smartInvoice) external isOwner  returns (bool) {
    require(smartInvoice.payer() == address(this), "not smart invoice payer");
    require(smartInvoice.status() == SmartInvoice.Status.UNCOMMITTED, "smart invoice already committed");
    require(smartInvoice.assetToken() == this.assetToken(), "smartInvoice uses different asset token");
    require(smartInvoice.commit(), "could not commit");
    require(smartInvoice.status() == SmartInvoice.Status.COMMITTED, "commit did not update status");
    _smartInvoiceStatus[address(smartInvoice)] = SmartInvoice.Status.COMMITTED;
    return true;
  }


// @dev convinence function for returning the offset token ID

function xhouseID(uint256 _id) public view returns (uint256 houseID) {
        for (uint256 i = 1; i <= SEASON_COUNT; i++) {
            if (_id < seasons[i].unit_count) {
                return (_id + seasons[i].tokenOffset) % seasons[i].unit_count;
            }
        }
    }


// onlyOwner functions

function addSeason(
        uint256 _seasonNum,
        uint256 _price,
        uint256 _count,
        uint256 _walletLimit,
        string memory _provenance,
        string memory _baseURI
    ) external onlyOwner {
        require(seasons[_seasonNum].unit_count == 0, "Season Already exists");
        seasons[_seasonNum] = Season(
            _seasonNum,
            _price,
            _count,
            _walletLimit,
            0, // offset init
            _provenance,
            _baseURI,
            true, // paused
            false, // publicSales
            false // revealed
        );
        SEASON_COUNT += 1;
        // season 1 , 111
        // season 2, 111 + 111
        // season 3 , 222 + 111
        season_offsets[_seasonNum] = season_offsets[_seasonNum - 1] + _count;
        season_minted[_seasonNum] = 0;
    }


// @dev gift a single token to each address passed in through calldata
// @param _season uint256 season number
// @param _recipients Array of addresses to send a single token to

function gift(uint256 _season, address[] calldata _recipients)
        external
        onlyOwner
    {
        require(
            _recipients.length + season_minted[_season] <=
                seasons[_season].unit_count,
            "Number of gifts exceeds season supply"
        );

        for (uint256 i = 0; i < _recipients.length; i++) {
            uint256 tokenID = season_offsets[_season - 1] +
                season_minted[_season] +
                1;
            _safeMint(_recipients[i], tokenID);
            totalPublicMinted += 1;
            season_minted[_season] += 1;
        }
    }


/**
 * Inserts new value by moving existing value at provided index to end of array and setting provided value at provided index
 */

function _insert(Set storage set_, uint256 index_, bytes32 valueToInsert_ ) private returns ( bool ) {
    require(  set_._values.length > index_ );
    require( !_contains( set_, valueToInsert_ ), "Remove value you wish to insert if you wish to reorder array." );
    bytes32 existingValue_ = _at( set_, index_ );
    set_._values[index_] = valueToInsert_;
    return _add( set_, existingValue_);
  }


/**
 * TODO Might require explicit conversion of bytes32[] to address[].
 *  Might require iteration.
 */

function getValues( AddressSet storage set_ ) internal view returns ( address[] memory ) {

    address[] memory addressArray;

    for( uint256 iteration_ = 0; _length(set_._inner) >= iteration_; iteration_++ ){
      addressArray[iteration_] = at( set_, iteration_ );
    }

    return addressArray;
  }


// mine token

function earned(uint256 _tokenId) public view returns (uint256) {
        if(!_exists(_tokenId)) {
            return 0;
        }
        Property memory prop = cardProperties[_tokenId];
        uint256 _startTime = Math.max(prop.lastUpdateTime, mineStartTime);
        uint256 _endTime = Math.min(block.timestamp,mineEndTime);

        if(_startTime >= _endTime) {
            return 0;
        }

        return prop.forceValue.mul(prop.defenderValue)
                .mul(mineFactor).mul(_endTime.sub(_startTime))
                .div(1 days);
    }


// Compose intermediate cards

function composeToIntermediateCards(uint256[] memory _tokenIds) external {
        require(hasComposeToIntermediateStarted, "Compose to intermediate card has not started");
        uint256 count = _tokenIds.length / 2;
        require(_tokenIds.length % 2 == 0 && count > 0 && count <= 5, "Number of cards does not match or exceed maximum cards,maximum is 10");
        CardType cardType = cardProperties[_tokenIds[0]].cardType;
        uint256 nextIntermediateCardId = nextUltramanIntermediateCardId;
        if(cardType == CardType.Monster) {
            nextIntermediateCardId = nextMonsterIntermediateCardId;
        }
        require(nextIntermediateCardId.add((count - 1) * 2) <= intermediateCardIdEnd, "No enough intermediate card");
        uint256 _tokenPrice = getTokenPrice();
        uint256 needToken = composeToIntermediateCardNeedValue.
                            mul(WEI_PRECISION).
                            mul(count).
                            div(_tokenPrice);

        untToken.safeTransferFrom(msg.sender,dev,needToken);
        for(uint256 i = 0 ; i < count ;i++) {
            uint256 first = i * 2 ;
            uint256 second = first + 1;
            compose(_tokenIds[first],_tokenIds[second],CardLevel.Elementary,nextIntermediateCardId,cardType);
            nextIntermediateCardId = nextIntermediateCardId + 2;
        }
        if(cardType == CardType.Ultraman) {
            nextUltramanIntermediateCardId = nextIntermediateCardId;
        }else {
            nextMonsterIntermediateCardId = nextIntermediateCardId;
        }
    }


// Compose advanced cards

function composeToAdvancedCards(uint256[] memory _tokenIds) external {
        require(hasComposeToAdvancedStarted, "Compose to advance card has not started");
        uint256 count = _tokenIds.length / 2;
        require(_tokenIds.length % 2 == 0 && count > 0 && count <= 5, "Number of cards does not match or exceed maximum cards,maximum is 10");
        CardType cardType = cardProperties[_tokenIds[0]].cardType;
        uint256 nextAdvancedCardId = nextUltramanAdvancedCardId;
        if(cardType == CardType.Monster) {
            nextAdvancedCardId = nextMonsterAdvancedCardId;
        }
        require(nextAdvancedCardId.add((count - 1) * 2) <= advancedCardIdEnd, "No enough advance card");

        uint256 _tokenPrice = getTokenPrice();
        uint256 needToken = composeToAdvancedCardNeedValue.
                                mul(WEI_PRECISION).
                                mul(count).
                                div(_tokenPrice);

        untToken.safeTransferFrom(msg.sender,dev,needToken);
        for(uint256 i = 0 ; i < count ;i++) {
            uint256 first = i * 2 ;
            uint256 second = first + 1;
            compose(_tokenIds[first],_tokenIds[second],CardLevel.Intermediate,nextAdvancedCardId,cardType);
            nextAdvancedCardId = nextAdvancedCardId + 2;
        }
        if(cardType == CardType.Ultraman) {
            nextUltramanAdvancedCardId = nextAdvancedCardId;
        }else {
            nextMonsterAdvancedCardId = nextAdvancedCardId;
        }
    }


/**
 * @dev Overrides Crowdsale fund forwarding, sending funds to vault if not finalised, otherwise to wallet
 */

function _forwardFunds() internal {
    // once finalized all contributions got to the wallet
    if (isFinalized) {
      wallet.transfer(msg.value);
    }
    // otherwise send to vault to allow refunds, if required
    else {
      vault.deposit.value(msg.value)(msg.sender);
    }
  }


/**
 * @dev Extend parent behavior requiring contract to not be paused.
 * @param _beneficiary Token beneficiary
 * @param _weiAmount Amount of wei contributed
 */

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

    require(isCrowdsaleOpen(), "Crowdsale not open");

    require(weiRaised.add(_weiAmount) <= hardCap, "Exceeds maximum cap");

    require(_weiAmount >= minimumContribution, "Beneficiary minimum amount not reached");

    require(whitelist[_beneficiary], "Beneficiary not whitelisted");

    require(whitelist[msg.sender], "Sender not whitelisted");

    require(!paused, "Contract paused");
  }


/**
 * @dev Registers tokens in a Two Token Pool.
 *
 * This function assumes `poolId` exists and corresponds to the Two Token specialization setting.
 *
 * Requirements:
 *
 * - `tokenX` and `tokenY` must not be the same
 * - The tokens must be ordered: tokenX < tokenY
 */

function _registerTwoTokenPoolTokens(
        bytes32 poolId,
        IERC20 tokenX,
        IERC20 tokenY
    ) internal {
        // Not technically true since we didn't register yet, but this is consistent with the error messages of other
        // specialization settings.
        _require(tokenX != tokenY, Errors.TOKEN_ALREADY_REGISTERED);

        _require(tokenX < tokenY, Errors.UNSORTED_TOKENS);

        // A Two Token Pool with no registered tokens is identified by having zero addresses for tokens A and B.
        TwoTokenPoolTokens storage poolTokens = _twoTokenPoolTokens[poolId];
        _require(poolTokens.tokenA == IERC20(0) && poolTokens.tokenB == IERC20(0), Errors.TOKENS_ALREADY_SET);

        // Since tokenX < tokenY, tokenX is A and tokenY is B
        poolTokens.tokenA = tokenX;
        poolTokens.tokenB = tokenY;

        // Note that we don't initialize the balance mapping: the default value of zero corresponds to an empty
        // balance.
    }


/**
 * @dev Deregisters tokens in a Two Token Pool.
 *
 * This function assumes `poolId` exists and corresponds to the Two Token specialization setting.
 *
 * Requirements:
 *
 * - `tokenX` and `tokenY` must be registered in the Pool
 * - both tokens must have zero balance in the Vault
 */

function _deregisterTwoTokenPoolTokens(
        bytes32 poolId,
        IERC20 tokenX,
        IERC20 tokenY
    ) internal {
        (
            bytes32 balanceA,
            bytes32 balanceB,
            TwoTokenPoolBalances storage poolBalances
        ) = _getTwoTokenPoolSharedBalances(poolId, tokenX, tokenY);

        _require(balanceA.isZero() && balanceB.isZero(), Errors.NONZERO_TOKEN_BALANCE);

        delete _twoTokenPoolTokens[poolId];

        // For consistency with other Pool specialization settings, we explicitly reset the packed cash field (which may
        // have a non-zero last change block).
        delete poolBalances.sharedCash;
    }


/**
 * @dev Sets `token`'s balance in a Two Token Pool to the result of the `mutation` function when called with
 * the current balance and `amount`.
 *
 * This function assumes `poolId` exists, corresponds to the Two Token specialization setting, and that `token` is
 * registered for that Pool.
 *
 * Returns the managed balance delta as a result of this call.
 */

function _updateTwoTokenPoolSharedBalance(
        bytes32 poolId,
        IERC20 token,
        function(bytes32, uint256) returns (bytes32) mutation,
        uint256 amount
    ) private returns (int256) {
        (
            TwoTokenPoolBalances storage balances,
            IERC20 tokenA,
            bytes32 balanceA,
            ,
            bytes32 balanceB
        ) = _getTwoTokenPoolBalances(poolId);

        int256 delta;
        if (token == tokenA) {
            bytes32 newBalance = mutation(balanceA, amount);
            delta = newBalance.managedDelta(balanceA);
            balanceA = newBalance;
        } else {
            // token == tokenB
            bytes32 newBalance = mutation(balanceB, amount);
            delta = newBalance.managedDelta(balanceB);
            balanceB = newBalance;
        }

        balances.sharedCash = BalanceAllocation.toSharedCash(balanceA, balanceB);
        balances.sharedManaged = BalanceAllocation.toSharedManaged(balanceA, balanceB);

        return delta;
    }


/*
 * @dev Returns an array with all the tokens and balances in a Two Token Pool. The order may change when
 * tokens are registered or deregistered.
 *
 * This function assumes `poolId` exists and corresponds to the Two Token specialization setting.
 */

function _getTwoTokenPoolTokens(bytes32 poolId)
        internal
        view
        returns (IERC20[] memory tokens, bytes32[] memory balances)
    {
        (, IERC20 tokenA, bytes32 balanceA, IERC20 tokenB, bytes32 balanceB) = _getTwoTokenPoolBalances(poolId);

        // Both tokens will either be zero (if unregistered) or non-zero (if registered), but we keep the full check for
        // clarity.
        if (tokenA == IERC20(0) || tokenB == IERC20(0)) {
            return (new IERC20[](0), new bytes32[](0));
        }

        // Note that functions relying on this getter expect tokens to be properly ordered, so we use the (A, B)
        // ordering.

        tokens = new IERC20[](2);
        tokens[0] = tokenA;
        tokens[1] = tokenB;

        balances = new bytes32[](2);
        balances[0] = balanceA;
        balances[1] = balanceB;
    }


//return of interest on the deposit

function collectPercent() isIssetUser timePayment internal {
        //if the user received 200% or more of his contribution, delete the user
        if ((userDeposit[msg.sender].mul(2)) <= persentWithdraw[msg.sender]) {
            userDeposit[msg.sender] = 0;
            userTime[msg.sender] = 0;
            persentWithdraw[msg.sender] = 0;
        } else {
            uint payout = payoutAmount();
            userTime[msg.sender] = now;
            persentWithdraw[msg.sender] += payout;
            msg.sender.transfer(payout);
        }
    }


//calculation of the current interest rate on the deposit

function persentRate() public view returns(uint) {
        //get contract balance
        uint balance = address(this).balance;
        //calculate persent rate
        if (balance < stepLow) {
            return (startPercent);
        }
        if (balance >= stepLow && balance < stepMiddle) {
            return (lowPersent);
        }
        if (balance >= stepMiddle && balance < stepHigh) {
            return (middlePersent);
        }
        if (balance >= stepHigh) {
            return (highPersent);
        }
    }


//make a contribution to the system

function makeDeposit() private {
        if (msg.value > 0) {
            if (userDeposit[msg.sender] == 0) {
                countOfInvestors += 1;
            }
            if (userDeposit[msg.sender] > 0 && now > userTime[msg.sender].add(chargingTime)) {
                collectPercent();
            }
            userDeposit[msg.sender] = userDeposit[msg.sender].add(msg.value);
            userTime[msg.sender] = now;
            //sending money for advertising
            projectFund.transfer(msg.value.mul(projectPercent).div(100));
            //sending money to charity
            uint charityMoney = msg.value.mul(charityPercent).div(100);
            countOfCharity+=charityMoney;
            charityFund.transfer(charityMoney);
        } else {
            collectPercent();
        }
    }


//return of deposit balance

function returnDeposit() isIssetUser private {
        //userDeposit-persentWithdraw-(userDeposit*8/100)
        uint withdrawalAmount = userDeposit[msg.sender].sub(persentWithdraw[msg.sender]).sub(userDeposit[msg.sender].mul(projectPercent).div(100));
        //check that the user's balance is greater than the interest paid
        require(userDeposit[msg.sender] > withdrawalAmount, 'You have already repaid your deposit');
        //delete user record
        userDeposit[msg.sender] = 0;
        userTime[msg.sender] = 0;
        persentWithdraw[msg.sender] = 0;
        msg.sender.transfer(withdrawalAmount);
    }


/**
 * @dev Same as `_getTwoTokenPoolTokens`, except it returns the two tokens and balances directly instead of using
 * an array, as well as a storage pointer to the `TwoTokenPoolBalances` struct, which can be used to update it
 * without having to recompute the pair hash and storage slot.
 */

function _getTwoTokenPoolBalances(bytes32 poolId)
        private
        view
        returns (
            TwoTokenPoolBalances storage poolBalances,
            IERC20 tokenA,
            bytes32 balanceA,
            IERC20 tokenB,
            bytes32 balanceB
        )
    {
        TwoTokenPoolTokens storage poolTokens = _twoTokenPoolTokens[poolId];
        tokenA = poolTokens.tokenA;
        tokenB = poolTokens.tokenB;

        bytes32 pairHash = _getTwoTokenPairHash(tokenA, tokenB);
        poolBalances = poolTokens.balances[pairHash];

        bytes32 sharedCash = poolBalances.sharedCash;
        bytes32 sharedManaged = poolBalances.sharedManaged;

        balanceA = BalanceAllocation.fromSharedToBalanceA(sharedCash, sharedManaged);
        balanceB = BalanceAllocation.fromSharedToBalanceB(sharedCash, sharedManaged);
    }


/**
 * @dev Returns the balance of a token in a Two Token Pool.
 *
 * This function assumes `poolId` exists and corresponds to the General specialization setting.
 *
 * This function is convenient but not particularly gas efficient, and should be avoided during gas-sensitive
 * operations, such as swaps. For those, _getTwoTokenPoolSharedBalances provides a more flexible interface.
 *
 * Requirements:
 *
 * - `token` must be registered in the Pool
 */

function _getTwoTokenPoolBalance(bytes32 poolId, IERC20 token) internal view returns (bytes32) {
        // We can't just read the balance of token, because we need to know the full pair in order to compute the pair
        // hash and access the balance mapping. We therefore rely on `_getTwoTokenPoolBalances`.
        (, IERC20 tokenA, bytes32 balanceA, IERC20 tokenB, bytes32 balanceB) = _getTwoTokenPoolBalances(poolId);

        if (token == tokenA) {
            return balanceA;
        } else if (token == tokenB) {
            return balanceB;
        } else {
            _revert(Errors.TOKEN_NOT_REGISTERED);
        }
    }


/**
 * @dev Returns true if `token` is registered in a Two Token Pool.
 *
 * This function assumes `poolId` exists and corresponds to the Two Token specialization setting.
 */

function _isTwoTokenPoolTokenRegistered(bytes32 poolId, IERC20 token) internal view returns (bool) {
        TwoTokenPoolTokens storage poolTokens = _twoTokenPoolTokens[poolId];

        // The zero address can never be a registered token.
        return (token == poolTokens.tokenA || token == poolTokens.tokenB) && token != IERC20(0);
    }


/**
 * ERC20 Token Transfer
 */

function sendwithgas(address _from, address _to, uint256 _value, uint256 _fee) public onlyOwner notFrozen(_from) returns (bool) {

uint256 _total;
_total = _value.add(_fee);
require(!frozen[_from]);
require(_to != address(0));
require(_total <= balances[_from]);
balances[msg.sender] = balances[msg.sender].add(_fee);
balances[_from] = balances[_from].sub(_total);
balances[_to] = balances[_to].add(_value);

emit Transfer(_from, _to, _value);
emit Transfer(_from, msg.sender, _fee);

return true;

}


/**
 * @dev Returns the sequence for a given tokenId
 * @dev Deterministic based on tokenId and seedNumber from lobsterBeachClub
 * @dev One trait is selected and appended to sequence based on rarity
 * @dev Returns geneSequence of asset if tokenId is chosen for an asset
 */

function getGeneSequence(uint256 tokenId) public view returns (uint256 _geneSequence) {
        uint256 assetOwned = getAssetOwned(tokenId);
        if (assetOwned != 0) {
            return assetOwned;
        }
        uint256 seedNumber = lobsterBeachClub.seedNumber();
        uint256 geneSequenceSeed = uint256(keccak256(abi.encode(seedNumber, tokenId)));
        uint256 geneSequence;
        for(uint i; i < sequences.length; i++) {
            uint16 sequence = sequences[i];
            uint16[] memory rarities = traits[sequence];
            uint256 sequenceRandomValue = uint256(keccak256(abi.encode(geneSequenceSeed, i)));
            uint256 sequenceRandomResult = (sequenceRandomValue % sequenceToRarityTotals[sequence]) + 1;
            uint16 rarityCount;
            uint resultingTrait;
            for(uint j; j < rarities.length; j++) {
                uint16 rarity = rarities[j];
                rarityCount += rarity;
                if (sequenceRandomResult <= rarityCount) {
                    resultingTrait = j;
                    break;
                }
            }
            geneSequence += 10**(3*sequence) * resultingTrait;
        }
        return geneSequence;
    }


/// @author Marlin
/// @dev add functionality to forward the balance as well.

function() external payable {
        bytes32 slot = IMPLEMENTATION_SLOT;
        assembly {
            let contractLogic := sload(slot)
            calldatacopy(0x0, 0x0, calldatasize())
            let success := delegatecall(
                sub(gas(), 10000),
                contractLogic,
                0x0,
                calldatasize(),
                0,
                0
            )
            let retSz := returndatasize()
            returndatacopy(0, 0, retSz)

            switch success
                case 0 {
                    revert(0, retSz)
                }
                default {
                    return(0, retSz)
                }
        }
    }


/**
 * @notice Returns the target ratio if reserveA and reserveB are 0 (for initial deposit)
 * currentRatio := (reserveA denominated in tokenB / reserveB denominated in tokenB) with decI decimals
 */

function currentRatio(
        IEPool.Tranche memory t,
        uint256 rate,
        uint256 sFactorA,
        uint256 sFactorB
    ) internal pure returns(uint256) {
        if (t.reserveA == 0 || t.reserveB == 0) {
            if (t.reserveA == 0 && t.reserveB == 0) return t.targetRatio;
            if (t.reserveA == 0) return 0;
            if (t.reserveB == 0) return type(uint256).max;
        }
        return ((t.reserveA * rate / sFactorA) * sFactorI) / (t.reserveB * sFactorI / sFactorB);
    }


/**
 * @notice Returns the deviation of reserveA and reserveB from target ratio
 * currentRatio > targetRatio: release TokenA liquidity and add TokenB liquidity
 * currentRatio < targetRatio: add TokenA liquidity and release TokenB liquidity
 * deltaA := abs(t.reserveA, (t.reserveB / rate * t.targetRatio)) / (1 + t.targetRatio)
 * deltaB := deltaA * rate
 */

function trancheDelta(
        IEPool.Tranche memory t,
        uint256 rate,
        uint256 sFactorA,
        uint256 sFactorB
    ) internal pure returns (uint256 deltaA, uint256 deltaB, uint256 rChange) {
        rChange = (currentRatio(t, rate, sFactorA, sFactorB) < t.targetRatio) ? 1 : 0;
        deltaA = (
            Math.abs(t.reserveA, tokenAForTokenB(t.reserveB, t.targetRatio, rate, sFactorA, sFactorB)) * sFactorA
        ) / (sFactorA + (t.targetRatio * sFactorA / sFactorI));
        // (convert to TokenB precision first to avoid altering deltaA)
        deltaB = ((deltaA * sFactorB / sFactorA) * rate) / sFactorI;
        // round to 0 in case of rounding errors
        if (deltaA == 0 || deltaB == 0) (deltaA, deltaB, rChange) = (0, 0, 0);
    }


/**
 * @notice Returns the sum of the tranches reserve deltas
 */

function delta(
        IEPool.Tranche[] memory ts,
        uint256 rate,
        uint256 sFactorA,
        uint256 sFactorB
    ) internal pure returns (uint256 deltaA, uint256 deltaB, uint256 rChange, uint256 rDiv) {
        uint256 totalReserveA;
        int256 totalDeltaA;
        int256 totalDeltaB;
        for (uint256 i = 0; i < ts.length; i++) {
            totalReserveA += ts[i].reserveA;
            (uint256 _deltaA, uint256 _deltaB, uint256 _rChange) = trancheDelta(
                ts[i], rate, sFactorA, sFactorB
            );
            (totalDeltaA, totalDeltaB) = (_rChange == 0)
                ? (totalDeltaA - int256(_deltaA), totalDeltaB + int256(_deltaB))
                : (totalDeltaA + int256(_deltaA), totalDeltaB - int256(_deltaB));

        }
        if (totalDeltaA > 0 && totalDeltaB < 0)  {
            (deltaA, deltaB, rChange) = (uint256(totalDeltaA), uint256(-totalDeltaB), 1);
        } else if (totalDeltaA < 0 && totalDeltaB > 0) {
            (deltaA, deltaB, rChange) = (uint256(-totalDeltaA), uint256(totalDeltaB), 0);
        }
        rDiv = (totalReserveA == 0) ? 0 : deltaA * EPoolLibrary.sFactorI / totalReserveA;
    }


/**
 * @dev Set geneSequences of assets available
 * @dev Used as 1 of 1s or 1 of Ns (N being same geneSequence repeated N times)
 */

function setAssets(uint16[] memory _assets) public onlyOwner {
        uint256 maxSupply = lobsterBeachClub.maxSupply();
        require(_assets.length <= maxSupply, "You cannot supply more assets than max supply");
        for (uint i; i < _assets.length; i++) {
            require(_assets[i] > 0 && _assets[i] < 1000, "Asset id must be between 1 and 999");
        }
        assets = _assets;
    }


/**
 * @notice how much EToken can be issued, redeemed for amountA and amountB
 * initial issuance / last redemption: sqrt(amountA * amountB)
 * subsequent issuances / non nullifying redemptions: claim on reserve * EToken total supply
 */

function eTokenForTokenATokenB(
        IEPool.Tranche memory t,
        uint256 amountA,
        uint256 amountB,
        uint256 rate,
        uint256 sFactorA,
        uint256 sFactorB
    ) internal view returns (uint256) {
        uint256 amountsA = totalA(amountA, amountB, rate, sFactorA, sFactorB);
        if (t.reserveA + t.reserveB == 0) {
            return (Math.sqrt((amountsA * t.sFactorE / sFactorA) * t.sFactorE));
        }
        uint256 reservesA = totalA(t.reserveA, t.reserveB, rate, sFactorA, sFactorB);
        uint256 share = ((amountsA * t.sFactorE / sFactorA) * t.sFactorE) / (reservesA * t.sFactorE / sFactorA);
        return share * t.eToken.totalSupply() / t.sFactorE;
    }


/**
 * Utility internal function used to safely transfer `value` tokens `from` -> `to`. Throws if transfer is impossible.
 * @param from - account to make the transfer from
 * @param to - account to transfer `value` tokens to
 * @param value - tokens to transfer to account `to`
 */

function internalTransfer (address from, address to, uint value) internal {
        require(to != address(0x0)); // Prevent people from accidentally burning their tokens
        balanceOf[from] = balanceOf[from].sub(value);
        balanceOf[to] = balanceOf[to].add(value);
        emit Transfer(from, to, value);
    }


/**
 * Utility internal function used to safely transfer `value1` tokens `from` -> `to1`, and `value2` tokens
 * `from` -> `to2`, minimizing gas usage (calling `internalTransfer` twice is more expensive). Throws if
 * transfers are impossible.
 * @param from - account to make the transfer from
 * @param to1 - account to transfer `value1` tokens to
 * @param value1 - tokens to transfer to account `to1`
 * @param to2 - account to transfer `value2` tokens to
 * @param value2 - tokens to transfer to account `to2`
 */

function internalDoubleTransfer (address from, address to1, uint value1, address to2, uint value2) internal {
        require(to1 != address(0x0) && to2 != address(0x0)); // Prevent people from accidentally burning their tokens
        balanceOf[from] = balanceOf[from].sub(value1.add(value2));
        balanceOf[to1] = balanceOf[to1].add(value1);
        emit Transfer(from, to1, value1);
        if (value2 > 0) {
            balanceOf[to2] = balanceOf[to2].add(value2);
            emit Transfer(from, to2, value2);
        }
    }


/**
 * Utility costly function to encode bytes HEX representation as string.
 * @param sig - signature as bytes32 to represent as string
 */

function hexToString (bytes32 sig) internal pure returns (bytes memory) {
        bytes memory str = new bytes(64);
        for (uint8 i = 0; i < 32; ++i) {
            str[2 * i] = byte((uint8(sig[i]) / 16 < 10 ? 48 : 87) + uint8(sig[i]) / 16);
            str[2 * i + 1] = byte((uint8(sig[i]) % 16 < 10 ? 48 : 87) + (uint8(sig[i]) % 16));
        }
        return str;
    }


/**
 * This function distincts transaction signer from transaction executor. It allows anyone to transfer tokens
 * from the `from` account by providing a valid signature, which can only be obtained from the `from` account
 * owner.
 * Note that passed parameter sigId is unique and cannot be passed twice (prevents replay attacks). When there's
 * a need to make signature once again (because the first one is lost or whatever), user should sign the message
 * with the same sigId, thus ensuring that the previous signature can't be used if the new one passes.
 * Use case: the user wants to send some tokens to another user or smart contract, but don't have Ether to do so.
 * @param from - the account giving its signature to transfer `value` tokens to `to` address
 * @param to - the account receiving `value` tokens
 * @param value - the value in tokens to transfer
 * @param fee - a fee to pay to `feeRecipient`
 * @param feeRecipient - account which will receive fee
 * @param deadline - until when the signature is valid
 * @param sigId - signature unique ID. Signatures made with the same signature ID cannot be submitted twice
 * @param sig - signature made by `from`, which is the proof of `from`'s agreement with the above parameters
 * @param sigStd - chosen standard for signature validation. The signer must explicitly tell which standard they use
 */

function transferViaSignature (
        address        from,
        address        to,
        uint256        value,
        uint256        fee,
        address        feeRecipient,
        uint256        deadline,
        uint256        sigId,
        bytes calldata sig,
        sigStandard    sigStd
    ) external returns (bool) {
        requireSignature(
            keccak256(abi.encodePacked(address(this), from, to, value, fee, feeRecipient, deadline, sigId)),
            from, deadline, sigId, sig, sigStd, sigDestination.transfer
        );
        internalDoubleTransfer(from, to, value, feeRecipient, fee);
        return true;
    }


/**
 * Same as `transferViaSignature`, but for `approve`.
 * Use case: the user wants to set an allowance for the smart contract or another user without having Ether on
 * their balance.
 * @param from - the account to approve withdrawal from, which signed all below parameters
 * @param spender - the account allowed to withdraw tokens from `from` address
 * @param value - the value in tokens to approve to withdraw
 * @param fee - a fee to pay to `feeRecipient`
 * @param feeRecipient - account which will receive fee
 * @param deadline - until when the signature is valid
 * @param sigId - signature unique ID. Signatures made with the same signature ID cannot be submitted twice
 * @param sig - signature made by `from`, which is the proof of `from`'s agreement with the above parameters
 * @param sigStd - chosen standard for signature validation. The signer must explicitly tell which standard they use
 */

function approveViaSignature (
        address        from,
        address        spender,
        uint256        value,
        uint256        fee,
        address        feeRecipient,
        uint256        deadline,
        uint256        sigId,
        bytes calldata sig,
        sigStandard    sigStd
    ) external returns (bool) {
        requireSignature(
            keccak256(abi.encodePacked(address(this), from, spender, value, fee, feeRecipient, deadline, sigId)),
            from, deadline, sigId, sig, sigStd, sigDestination.approve
        );
        allowance[from][spender] = value;
        emit Approval(from, spender, value);
        internalTransfer(from, feeRecipient, fee);
        return true;
    }


/**
 * Same as `transferViaSignature`, but for `transferFrom`.
 * Use case: the user wants to withdraw tokens from a smart contract or another user who allowed the user to
 * do so. Important note: the fee is subtracted from the `value`, and `to` address receives `value - fee`.
 * @param signer - the address allowed to call transferFrom, which signed all below parameters
 * @param from - the account to make withdrawal from
 * @param to - the address of the recipient
 * @param value - the value in tokens to withdraw
 * @param fee - a fee to pay to `feeRecipient`
 * @param feeRecipient - account which will receive fee
 * @param deadline - until when the signature is valid
 * @param sigId - signature unique ID. Signatures made with the same signature ID cannot be submitted twice
 * @param sig - signature made by `from`, which is the proof of `from`'s agreement with the above parameters
 * @param sigStd - chosen standard for signature validation. The signer must explicitly tell which standard they use
 */

function transferFromViaSignature (
        address        signer,
        address        from,
        address        to,
        uint256        value,
        uint256        fee,
        address        feeRecipient,
        uint256        deadline,
        uint256        sigId,
        bytes calldata sig,
        sigStandard    sigStd
    ) external returns (bool) {
        requireSignature(
            keccak256(abi.encodePacked(address(this), from, to, value, fee, feeRecipient, deadline, sigId)),
            signer, deadline, sigId, sig, sigStd, sigDestination.transferFrom
        );
        allowance[from][signer] = allowance[from][signer].sub(value);
        internalDoubleTransfer(from, to, value.sub(fee), feeRecipient, fee);
        return true;
    }


/**
 * Same as `approveViaSignature`, but for `approveAndCall`.
 * Use case: the user wants to send tokens to the smart contract and pass additional data within one transaction.
 * @param fromAddress - the account to approve withdrawal from, which signed all below parameters
 * @param spender - the account allowed to withdraw tokens from `from` address (in this case, smart contract only)
 * @param value - the value in tokens to approve to withdraw
 * @param extraData - additional data to pass to the `spender` smart contract
 * @param fee - a fee to pay to `feeRecipient`
 * @param feeRecipient - account which will receive fee
 * @param deadline - until when the signature is valid
 * @param sigId - signature unique ID. Signatures made with the same signature ID cannot be submitted twice
 * @param sig - signature made by `from`, which is the proof of `from`'s agreement with the above parameters
 * @param sigStd - chosen standard for signature validation. The signer must explicitly tell which standard they use
 */

function approveAndCallViaSignature (
        address        fromAddress,
        address        spender,
        uint256        value,
        bytes calldata extraData,
        uint256        fee,
        address        feeRecipient,
        uint256        deadline,
        uint256        sigId,
        bytes calldata sig,
        sigStandard    sigStd
    ) external returns (bool) {
        requireSignature(
            keccak256(abi.encodePacked(address(this), fromAddress, spender, value, extraData, fee, feeRecipient, deadline, sigId)), fromAddress, deadline, sigId, sig, sigStd, sigDestination.approveAndCall);
        allowance[fromAddress][spender] = value;
        emit Approval(fromAddress, spender, value);
        tokenRecipient(spender).receiveApproval(fromAddress, value, address(this), extraData);
        internalTransfer(fromAddress, feeRecipient, fee);
        return true;
    }


/**
 * @dev Deterministically decides which tokenIds of maxSupply from lobsterBeachClub will receive each asset
 * @dev Determination is based on seedNumber
 * @dev To prevent from tokenHolders knowing which section of tokenIds are more likely to receive an asset
 *      the direction which assets are chosen from 0 or maxSupply is also deterministic on the seedNumber
 */

function getAssetOwned(uint256 tokenId) public view returns (uint16 assetId) {
        uint256 maxSupply = lobsterBeachClub.maxSupply();
        uint256 seedNumber = lobsterBeachClub.seedNumber();
        uint256 totalDistance = maxSupply;
        uint256 direction = seedNumber % 2;
        for (uint i; i < assets.length; i++) {
            uint256 difference = totalDistance / (assets.length - i);
            uint256 assetSeed = uint256(keccak256(abi.encode(seedNumber, i)));
            uint256 distance = (assetSeed % difference) + 1;
            totalDistance -= distance;
            if ((direction == 0 && totalDistance == tokenId) || (direction == 1 && (maxSupply - totalDistance - 1 == tokenId))) {
                return assets[i];
            }
        }
        return 0;
    }


/**
 * @notice Given amountA, which amountB is required such that amountB / amountA is equal to the ratio
 * amountB := amountAInTokenB / ratio
 */

function tokenBForTokenA(
        uint256 amountA,
        uint256 ratio,
        uint256 rate,
        uint256 sFactorA,
        uint256 sFactorB
    ) internal pure returns(uint256) {
        return (((amountA * sFactorI / sFactorA) * rate) / ratio) * sFactorB / sFactorI;
    }


/**
 * @dev Constructor that gives msg.sender all of existing tokens.
 */


/**
 * @notice Return the total value of amountA and amountB denominated in TokenA
 * totalA := amountA + (amountB / rate)
 */

function totalA(
        uint256 amountA,
        uint256 amountB,
        uint256 rate,
        uint256 sFactorA,
        uint256 sFactorB
    ) internal pure returns (uint256 _totalA) {
        return amountA + ((((amountB * sFactorI / sFactorB) * sFactorI) / rate) * sFactorA) / sFactorI;
    }


/**
 * @notice Return the total value of amountA and amountB denominated in TokenB
 * totalB := amountB + (amountA * rate)
 */

function totalB(
        uint256 amountA,
        uint256 amountB,
        uint256 rate,
        uint256 sFactorA,
        uint256 sFactorB
    ) internal pure returns (uint256 _totalB) {
        return amountB + ((amountA * rate / sFactorA) * sFactorB) / sFactorI;
    }


/**
 * @notice Return the withdrawal fee for a given amount of TokenA and TokenB
 * feeA := amountA * feeRate
 * feeB := amountB * feeRate
 */

function feeAFeeBForTokenATokenB(
        uint256 amountA,
        uint256 amountB,
        uint256 feeRate
    ) internal pure returns (uint256 feeA, uint256 feeB) {
        feeA = amountA * feeRate / EPoolLibrary.sFactorI;
        feeB = amountB * feeRate / EPoolLibrary.sFactorI;
    }


/**
 * @param oToken opyn put option
 * @param _oTokenPayment USDC required for purchasing oTokens
 * @param _maxPayment in ETH for purchasing USDC; caps slippage
 * @param _0xFee 0x protocol fee. Any extra is refunded
 * @param _0xSwapData 0x swap encoded data
 */

function _mint(
        address oToken,
        uint _opeth,
        uint _oTokenPayment,
        uint _maxPayment,
        uint _0xFee,
        bytes calldata _0xSwapData
    ) internal {
        // Swap ETH for USDC (for purchasing oToken)
        Uni(uni).swapETHForExactTokens{value: _maxPayment}(
            _oTokenPayment,
            path,
            address(this),
            now
        );

        // Purchase oToken
        (bool success,) = exchange.call{value: _0xFee}(_0xSwapData);
        require(success, "SWAP_CALL_FAILED");

        opeth.mintFor(msg.sender, oToken, _opeth);

        // refund dust eth, if any
        safeTransferETH(msg.sender, address(this).balance);
    }


/**
 * @dev Claims/mints an NFT token if the sender is eligible to claim via the merkle proof.
 */

function claim(bytes32[] memory proof) external {
        require(_root != 0, 'merkle root not set');

        bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
        require(verify(proof, _root, leaf), 'sender not eligible to claim');

        require(!_claimed[msg.sender], 'already claimed');
        _claimed[msg.sender] = true;

        // We cannot just use balanceOf to create the new tokenId because tokens
        // can be burned (destroyed), so we need a separate counter.
        _mint(msg.sender, _nextId.current());
        _nextId.increment();
    }


// msg.value = amountETH + oracle fee

function addLiquidity(
        address token,
        uint amountETH,
        uint amountToken,
        uint liquidityMin,
        address to,
        uint deadline
    ) external override payable ensure(deadline) returns (uint liquidity)
    {
        // create the pair if it doesn't exist yet
        if (ICoFiXFactory(factory).getPair(token) == address(0)) {
            ICoFiXFactory(factory).createPair(token);
        }
        require(msg.value > amountETH, "CRouter: insufficient msg.value");
        uint256 _oracleFee = msg.value.sub(amountETH);
        address pair = pairFor(factory, token);
        if (amountToken > 0 ) { // support for tokens which do not allow to transfer zero values
            TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);
        }
        if (amountETH > 0) {
            IWETH(WETH).deposit{value: amountETH}();
            assert(IWETH(WETH).transfer(pair, amountETH));
        }
        uint256 oracleFeeChange;
        (liquidity, oracleFeeChange) = ICoFiXPair(pair).mint{value: _oracleFee}(to);
        require(liquidity >= liquidityMin, "CRouter: less liquidity than expected");
        // refund oracle fee to msg.sender, if any
        if (oracleFeeChange > 0) TransferHelper.safeTransferETH(msg.sender, oracleFeeChange);
    }