contract TROPVault is OwnableUpgradeSafe, TimeLockLPToken {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    // Dev address.
    address public devaddr;

    // Total allocation poitns. Must be the sum of all allocation points in all pools.
    uint256 public totalAllocPoint;

    //// pending rewards awaiting anyone to massUpdate
    uint256 public pendingRewards;

    uint256 public epoch;

    uint256 public constant REWARD_LOCKED_PERIOD = 14 days;
    uint256 public constant REWARD_RELEASE_PERCENTAGE = 40;
    uint256 public contractStartBlock;

    uint256 private tropBalance;
    uint256 public START_BLOCK;
    bool public tripEnabled;

    // Sets the dev fee for this contract
    // defaults at 7.24%
    // Note contract owner is meant to be a governance contract allowing TROP governance consensus
    uint16 DEV_FEE;

    uint256 public pending_DEV_rewards;

    // Returns fees generated since start of this contract
    function averageFeesPerBlockSinceStart(uint256 _pid)
        external
        view
        returns (uint256 averagePerBlock)
    {
        averagePerBlock = poolInfo[_pid]
            .cumulativeRewardsSinceStart
            .add(poolInfo[_pid].rewardsInThisEpoch)
            .add(pendingTROPForPool(_pid))
            .div(block.number.sub(poolInfo[_pid].startBlock));
    }

    // Returns averge fees in this epoch
    function averageFeesPerBlockEpoch(uint256 _pid)
        external
        view
        returns (uint256 averagePerBlock)
    {
        averagePerBlock = poolInfo[_pid]
            .rewardsInThisEpoch
            .add(pendingTROPForPool(_pid))
            .div(block.number.sub(poolInfo[_pid].epochCalculationStartBlock));
    }

    function getEpochReward(uint256 _pid, uint256 _epoch)
        public
        view
        returns (uint256)
    {
        return poolInfo[_pid].epochRewards[_epoch];
    }

    //Starts a new calculation epoch
    // Because averge since start will not be accurate
    function startNewEpoch() public {
        for (uint256 _pid = 0; _pid < poolInfo.length; _pid++) {
            require(
                poolInfo[_pid].epochCalculationStartBlock + 50000 <
                    block.number,
                "New epoch not ready yet"
            ); // About a week
            poolInfo[_pid].epochRewards[epoch] = poolInfo[_pid]
                .rewardsInThisEpoch;
            poolInfo[_pid].cumulativeRewardsSinceStart = poolInfo[_pid]
                .cumulativeRewardsSinceStart
                .add(poolInfo[_pid].rewardsInThisEpoch);
            poolInfo[_pid].rewardsInThisEpoch = 0;
            poolInfo[_pid].epochCalculationStartBlock = block.number;
            ++epoch;
        }
    }

    event Deposit(address indexed user, uint256 indexed pid, uint256 amount);
    event Withdraw(address indexed user, uint256 indexed pid, uint256 amount);
    event EmergencyWithdraw(
        address indexed user,
        uint256 indexed pid,
        uint256 amount
    );
    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 _pid,
        uint256 value
    );

    function initialize(
        ITROP _trop,
        TRIP _trip,
        address _devFundAddress,
        uint256 _startTripBlock
    ) public initializer {
        OwnableUpgradeSafe.__Ownable_init();
        DEV_FEE = 1000; //10%
        trop = _trop;
        trip = _trip;
        devaddr = _devFundAddress;
        contractStartBlock = block.number;
        START_BLOCK = _startTripBlock > 0 ? _startTripBlock : block.number;
        tripEnabled = false;
    }

    function poolLength() external view returns (uint256) {
        return poolInfo.length;
    }

    function poolToken(uint256 _pid) external view returns (address) {
        return address(poolInfo[_pid].token);
    }

    modifier validLockTime(uint256 _time) {
        require(
            _time == 10 days ||
                _time == 20 days ||
                _time == 30 days ||
                _time == 40 days ||
                _time == 5 days ||
                _time == 2 days,
            "Lock time is not valid"
        );
        _;
    }

    //to avoid exploit in time lock
    function checkLockTiming(uint256 _pid, address _user, uint256 _time) internal {
        uint256 lpReleaseTime = userInfo[_pid][_user].lpReleaseTime;
        require(lpReleaseTime <= block.timestamp.add(_time), "timing invalid");
    }

    function setEnableTRIP(
        bool _enable,
        bool _withUpdate,
        bool _updateRewardBlock
    ) external onlyOwner {
        if (_withUpdate) {
            massUpdatePools();
        }
        tripEnabled = _enable;
        if (_updateRewardBlock) {
            updateTripRewardBlock(block.number);
        }
    }

    // Add a new token pool. Can only be called by the owner.
    // Note contract owner is meant to be a governance contract allowing TROP governance consensus
    function add(
        uint256 _allocPoint,
        IERC20 _token,
        bool _withUpdate
    ) public onlyOwner {
        require(address(_token) != address(trop), "!TROP token");
        if (_withUpdate) {
            massUpdatePools();
        }

        uint256 length = poolInfo.length;
        for (uint256 pid = 0; pid < length; ++pid) {
            require(poolInfo[pid].token != _token, "Error pool already added");
        }

        totalAllocPoint = totalAllocPoint.add(_allocPoint);

        poolInfo.push(
            PoolInfo({
                token: _token,
                allocPoint: _allocPoint,
                accTROPPerRAP: 0,
                accTRIPPerRAP: 0,
                totalRewardAllocPoint: 0,
                emergencyWithdrawable: false,
                rewardsInThisEpoch: 0,
                cumulativeRewardsSinceStart: 0,
                startBlock: block.number,
                startTime: block.timestamp,
                epochCalculationStartBlock: block.number,
                lastTRIPRewardBlock: block.number > START_BLOCK
                    ? block.number
                    : START_BLOCK
            })
        );
    }

    // Update the given pool's TROPs allocation point. Can only be called by the owner.
    // Note contract owner is meant to be a governance contract allowing TROP governance consensus

    function setTRIPRate(
        uint256 _tripRate,
        bool _withUpdate,
        bool _updateRewardBlock
    ) external onlyOwner {
        if (_withUpdate) {
            massUpdatePools();
        }
        tripRewardPerBlock = _tripRate;
        if (_updateRewardBlock) {
            updateTripRewardBlock(block.number);
        }
    }

    function set(
        uint256 _pid,
        uint256 _allocPoint,
        bool _withUpdate
    ) public onlyOwner {
        if (_withUpdate) {
            massUpdatePools();
        }

        totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(
            _allocPoint
        );
        poolInfo[_pid].allocPoint = _allocPoint;
    }

    function setEmergencyWithdrawable(uint256 _pid, bool _withdrawable)
        public
        onlyOwner
    {
        poolInfo[_pid].emergencyWithdrawable = _withdrawable;
    }

    function setDevFee(uint16 _DEV_FEE) public onlyOwner {
        require(_DEV_FEE <= 1000, "Dev fee clamped at 10%");
        DEV_FEE = _DEV_FEE;
    }

    function pendingTROPForPool(uint256 _pid) public view returns (uint256) {
        PoolInfo storage pool = poolInfo[_pid];

        uint256 tokenSupply = pool.token.balanceOf(address(this));

        if (tokenSupply == 0) return 0;

        uint256 rewardWhole = pendingRewards // Multiplies pending rewards by allocation point of this pool and then total allocation
            .mul(pool.allocPoint) // getting the percent of total pending rewards this pool should get
            .div(totalAllocPoint); // we can do this because pools are only mass updated
        uint256 rewardFee = rewardWhole.mul(DEV_FEE).div(10000);
        return rewardWhole.sub(rewardFee);
    }

    function pendingTRIP(uint256 _pid, address _user)
        external
        view
        returns (uint256)
    {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][_user];
        uint256 accTRIPPerRAP = pool.accTRIPPerRAP;
        uint256 totalRAP = pool.totalRewardAllocPoint;
        if (block.number > pool.lastTRIPRewardBlock && totalRAP > 0) {
            uint256 _farmTrip;
            (, _farmTrip) = getTRIPPoolReward(
                pool.lastTRIPRewardBlock,
                block.number,
                pool.allocPoint
            );
            uint256 inc = _farmTrip.mul(1e18).div(totalRAP);
            accTRIPPerRAP = accTRIPPerRAP.add(inc);
        }
        return
            user.rewardAllocPoint.mul(accTRIPPerRAP).div(1e18).sub(
                user.tripRewardDebt
            );
    }

    // View function to see pending TROPs on frontend.
    function pendingTROP(uint256 _pid, address _user)
        public
        view
        returns (uint256)
    {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][_user];
        uint256 accTROPPerRAP = pool.accTROPPerRAP;
        uint256 rewardAllocPoint = user.rewardAllocPoint;

        uint256 totalRAP = pool.totalRewardAllocPoint;

        if (totalRAP == 0) return 0;

        uint256 rewardWhole = pendingRewards // Multiplies pending rewards by allocation point of this pool and then total allocation
            .mul(pool.allocPoint) // getting the percent of total pending rewards this pool should get
            .div(totalAllocPoint); // we can do this because pools are only mass updated
        uint256 rewardFee = rewardWhole.mul(DEV_FEE).div(10000);
        uint256 rewardToDistribute = rewardWhole.sub(rewardFee);
        uint256 inc = rewardToDistribute.mul(1e18).div(totalRAP);
        accTROPPerRAP = accTROPPerRAP.add(inc);

        return
            rewardAllocPoint.mul(accTROPPerRAP).div(1e18).sub(user.rewardDebt);
    }

    function getLockedReward(uint256 _pid, address _user)
        public
        view
        returns (uint256)
    {
        return userInfo[_pid][_user].rewardLocked;
    }

    // Update reward vairables for all pools. Be careful of gas spending!
    function massUpdatePools() public {
        uint256 length = poolInfo.length;
        uint256 allRewards;
        for (uint256 pid = 0; pid < length; ++pid) {
            allRewards = allRewards.add(updatePool(pid));
        }

        pendingRewards = pendingRewards.sub(allRewards);
    }

    function updateTripRewardBlock(uint256 _rewardBlock) internal {
        for (uint256 pid = 0; pid < poolInfo.length; ++pid) {
            poolInfo[pid].lastTRIPRewardBlock = _rewardBlock;
        }
    }

    // ----
    // Function that adds pending rewards, called by the TROP token.
    // ----
    function updatePendingRewards() public {
        uint256 newRewards = trop.balanceOf(address(this)).sub(tropBalance);

        if (newRewards > 0) {
            tropBalance = trop.balanceOf(address(this)); // If there is no change the balance didn't change
            pendingRewards = pendingRewards.add(newRewards);
        }
    }

    // Update reward variables of the given pool to be up-to-date.
    function updatePool(uint256 _pid)
        internal
        returns (uint256 tropRewardWhole)
    {
        PoolInfo storage pool = poolInfo[_pid];

        uint256 totalRAP = pool.totalRewardAllocPoint;

        if (totalRAP == 0) {
            // avoids division by 0 errors
            return 0;
        }
        tropRewardWhole = pendingRewards // Multiplies pending rewards by allocation point of this pool and then total allocation
            .mul(pool.allocPoint) // getting the percent of total pending rewards this pool should get
            .div(totalAllocPoint); // we can do this because pools are only mass updated

        uint256 rewardFee = tropRewardWhole.mul(DEV_FEE).div(10000);
        uint256 rewardToDistribute = tropRewardWhole.sub(rewardFee);

        uint256 inc = rewardToDistribute.mul(1e18).div(totalRAP);
        pending_DEV_rewards = pending_DEV_rewards.add(rewardFee);

        pool.accTROPPerRAP = pool.accTROPPerRAP.add(inc);
        pool.rewardsInThisEpoch = pool.rewardsInThisEpoch.add(
            rewardToDistribute
        );
        if (tripEnabled) updateTripReward(_pid);
    }

    function getTRIPPoolReward(
        uint256 _from,
        uint256 _to,
        uint256 _allocPoint
    ) public view returns (uint256 _devReward, uint256 _farmReward) {
        if (_to <= _from) return (0, 0);
        uint256 numBlock = _to.sub(_from);
        uint256 amount = numBlock.mul(tripRewardPerBlock).mul(_allocPoint).div(
            totalAllocPoint
        );

        _devReward = amount.mul(DEV_FEE).div(10000);
        _farmReward = amount.sub(_devReward);
    }

    function updateTripReward(uint256 _pid) internal {
        PoolInfo storage pool = poolInfo[_pid];

        uint256 totalRAP = pool.totalRewardAllocPoint;

        if (totalRAP == 0) {
            // avoids division by 0 errors
            return;
        }

        (uint256 _devTrip, uint256 _farmTrip) = getTRIPPoolReward(
            pool.lastTRIPRewardBlock,
            block.number,
            pool.allocPoint
        );

        uint256 inc = _farmTrip.mul(1e18).div(totalRAP);
        pool.accTRIPPerRAP = pool.accTRIPPerRAP.add(inc);
        pool.lastTRIPRewardBlock = block.number;

        trip.mint(address(this), _farmTrip);
        trip.mint(devaddr, _devTrip);
    }

    function withdrawReward(uint256 _pid) public {
        withdraw(_pid, 0);
    }

    // Deposit  tokens to TROPVault for TROP allocation.
    function deposit(
        uint256 _pid,
        uint256 _amount,
        uint256 _lockTime
    ) public validLockTime(_lockTime) {
        checkLockTiming(_pid, msg.sender, _lockTime);
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];

        massUpdatePools();

        // Transfer pending tokens
        // to user
        updateAndPayOutPending(_pid, msg.sender);

        //Transfer in the amounts from user
        // save gas
        if (_amount > 0) {
            pool.token.safeTransferFrom(
                address(msg.sender),
                address(this),
                _amount
            );
            updateRewardAllocPoint(_pid, msg.sender, _amount, _lockTime);
            user.amount = user.amount.add(_amount);
        }

        user.rewardDebt = user.rewardAllocPoint.mul(pool.accTROPPerRAP).div(
            1e18
        );
        user.tripRewardDebt = user.rewardAllocPoint.mul(pool.accTROPPerRAP).div(
            1e18
        );
        emit Deposit(msg.sender, _pid, _amount);
    }

    function updateRewardAllocPoint(
        uint256 _pid,
        address _addr,
        uint256 _depositAmount,
        uint256 _lockTime
    ) internal {
        UserInfo storage user = userInfo[_pid][_addr];
        PoolInfo storage pool = poolInfo[_pid];
        if (user.amount == 0) {
            user.rewardAllocPoint = _depositAmount.mul(_lockTime);
            pool.totalRewardAllocPoint = pool.totalRewardAllocPoint.add(
                user.rewardAllocPoint
            );
            user.lockedPeriod = _lockTime;
            user.lpReleaseTime = block.timestamp.add(_lockTime);
        } else {
            user.lockedPeriod = _lockTime;
            user.lpReleaseTime = block.timestamp.add(_lockTime);

            uint256 pointMinus = user.rewardAllocPoint;
            uint256 amountAfterDeposit = user.amount.add(_depositAmount);
            user.rewardAllocPoint = amountAfterDeposit.mul(_lockTime);
            pool.totalRewardAllocPoint = pool
                .totalRewardAllocPoint
                .add(user.rewardAllocPoint)
                .sub(pointMinus);
        }
    }

    // Test coverage
    // [x] Does user get the deposited amounts?
    // [x] Does user that its deposited for update correcty?
    // [x] Does the depositor get their tokens decreased
    function depositFor(
        address _depositFor,
        uint256 _pid,
        uint256 _amount,
        uint256 _lockTime
    ) public validLockTime(_lockTime) {
        checkLockTiming(_pid, _depositFor, _lockTime);
        // requires no allowances
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][_depositFor];

        massUpdatePools();

        // Transfer pending tokens
        // to user
        updateAndPayOutPending(_pid, _depositFor); // Update the balances of person that amount is being deposited for

        if (_amount > 0) {
            pool.token.safeTransferFrom(
                address(msg.sender),
                address(this),
                _amount
            );
            updateRewardAllocPoint(_pid, _depositFor, _amount, _lockTime);
            user.amount = user.amount.add(_amount); // This is depositedFor address
        }

        user.rewardDebt = user.rewardAllocPoint.mul(pool.accTROPPerRAP).div(
            1e18
        ); /// This is deposited for address
        user.tripRewardDebt = user.rewardAllocPoint.mul(pool.accTRIPPerRAP).div(
            1e18
        );
        emit Deposit(_depositFor, _pid, _amount);
    }

    // Test coverage
    // [x] Does allowance update correctly?
    function setAllowanceForPoolToken(
        address spender,
        uint256 _pid,
        uint256 value
    ) public {
        PoolInfo storage pool = poolInfo[_pid];
        pool.allowance[msg.sender][spender] = value;
        emit Approval(msg.sender, spender, _pid, value);
    }

    function quitPool(uint256 _pid) public {
        require(
            block.timestamp > userInfo[_pid][msg.sender].lpReleaseTime,
            "cannot withdraw all lp tokens before"
        );

        uint256 withdrawnableAmount = computeReleasableLP(_pid, msg.sender);
        withdraw(_pid, withdrawnableAmount);
    }

    // Test coverage
    // [x] Does allowance decrease?
    // [x] Do oyu need allowance
    // [x] Withdraws to correct address
    function withdrawFrom(
        address owner,
        uint256 _pid,
        uint256 _amount
    ) public {
        PoolInfo storage pool = poolInfo[_pid];
        require(
            pool.allowance[owner][msg.sender] >= _amount,
            "withdraw: insufficient allowance"
        );
        pool.allowance[owner][msg.sender] = pool.allowance[owner][msg.sender]
            .sub(_amount);
        _withdraw(_pid, _amount, owner, msg.sender);
    }

    // Withdraw  tokens from TROPVault.
    function withdraw(uint256 _pid, uint256 _amount) public {
        _withdraw(_pid, _amount, msg.sender, msg.sender);
    }

    // Low level withdraw function
    function _withdraw(
        uint256 _pid,
        uint256 _amount,
        address from,
        address to
    ) internal {
        PoolInfo storage pool = poolInfo[_pid];
        //require(pool.withdrawable, "Withdrawing from this pool is disabled");
        UserInfo storage user = userInfo[_pid][from];

        uint256 withdrawnableAmount = computeReleasableLP(_pid, from);
        require(withdrawnableAmount >= _amount, "withdraw: not good");

        massUpdatePools();
        updateAndPayOutPending(_pid, from); // Update balances of from this is not withdrawal but claiming TROP farmed

        if (_amount > 0) {
            user.amount = user.amount.sub(_amount);
            user.rewardAllocPoint = user.amount.mul(user.lockedPeriod);
            pool.totalRewardAllocPoint = pool.totalRewardAllocPoint.sub(
                _amount.mul(user.lockedPeriod)
            );
            pool.token.safeTransfer(address(to), _amount);
            if (user.amount == 0) {
                user.lockedPeriod = 0;
                user.lpReleaseTime = 0;
            }
        }
        user.rewardDebt = user.rewardAllocPoint.mul(pool.accTROPPerRAP).div(
            1e18
        );
        user.tripRewardDebt = user.rewardAllocPoint.mul(pool.accTRIPPerRAP).div(
            1e18
        );
        emit Withdraw(to, _pid, _amount);
    }

    function updateAndPayOutPending(uint256 _pid, address from) internal {
        UserInfo storage user = userInfo[_pid][from];
        if (user.releaseTime == 0) {
            user.releaseTime = block.timestamp.add(REWARD_LOCKED_PERIOD);
        }
        uint256 formerReleaseTime = user.releaseTime;
        if (block.timestamp > user.releaseTime) {
            //compute withdrawnable amount
            uint256 lockedAmount = user.rewardLocked;
            user.rewardLocked = 0;
            safeTROPTransfer(from, lockedAmount);
            user.releaseTime = block.timestamp.add(REWARD_LOCKED_PERIOD);
        }

        uint256 pending = pendingTROP(_pid, from);
        uint256 paid = pending.mul(REWARD_RELEASE_PERCENTAGE).div(100);
        uint256 _lockedReward = pending.sub(paid);
        if (_lockedReward > 0) {
            user.rewardLocked = user.rewardLocked.add(_lockedReward);
        }

        if (paid > 0) {
            safeTROPTransfer(from, paid);
        }

        if (tripEnabled) {
            uint256 _tripReward = user
                .rewardAllocPoint
                .mul(poolInfo[_pid].accTRIPPerRAP)
                .div(1e18)
                .sub(user.tripRewardDebt);

            if (_tripReward > 0) {
                safeTRIPTransfer(from, _tripReward);
            }
        }
    }

    function emergencyWithdraw(uint256 _pid) public {
        PoolInfo storage pool = poolInfo[_pid];
        require(
            pool.emergencyWithdrawable,
            "Withdrawing from this pool is disabled"
        );
        UserInfo storage user = userInfo[_pid][msg.sender];
        pool.token.safeTransfer(address(msg.sender), user.amount);
        emit EmergencyWithdraw(msg.sender, _pid, user.amount);
        if (
            user.amount.mul(user.rewardAllocPoint) <= pool.totalRewardAllocPoint
        ) {
            pool.totalRewardAllocPoint = pool.totalRewardAllocPoint.sub(
                user.amount.mul(user.rewardAllocPoint)
            );
        }
        user.rewardAllocPoint = 0;
        user.amount = 0;
        user.rewardDebt = 0;
        user.tripRewardDebt = 0;
    }

    function safeTROPTransfer(address _to, uint256 _amount) internal {
        uint256 tropBal = trop.balanceOf(address(this));

        if (_amount > tropBal) {
            trop.transfer(_to, tropBal);
            tropBalance = trop.balanceOf(address(this));
        } else {
            trop.transfer(_to, _amount);
            tropBalance = trop.balanceOf(address(this));
        }
        transferDevFee();
    }

    function safeTRIPTransfer(address _to, uint256 _amount) internal {
        uint256 tripBal = trip.balanceOf(address(this));

        if (_amount > tripBal) {
            trip.transfer(_to, tripBal);
        } else {
            trip.transfer(_to, _amount);
        }
    }

    function transferDevFee() public {
        if (pending_DEV_rewards == 0) return;

        uint256 tropBal = trop.balanceOf(address(this));
        if (pending_DEV_rewards > tropBal) {
            trop.transfer(devaddr, tropBal);
            tropBalance = trop.balanceOf(address(this));
        } else {
            trop.transfer(devaddr, pending_DEV_rewards);
            tropBalance = trop.balanceOf(address(this));
        }

        pending_DEV_rewards = 0;
    }

    function setDevFeeReciever(address _devaddr) public {
        require(devaddr == msg.sender, "only dev can change");
        devaddr = _devaddr;
    }

    function getLiquidityInfo(uint256 _pid)
        public
        view
        returns (
            uint256 lpSupply,
            uint256 tropAmount,
            uint256 totalTROPAmount,
            uint256 tokenAmount,
            uint256 totalTokenAmount,
            uint256 lockedLP,
            uint256 totalLockedLP
        )
    {
        IERC20 lpToken = poolInfo[_pid].token;
        IERC20 tropToken = IERC20(address(trop));
        IUniswapV2Pair pair = IUniswapV2Pair(address(lpToken));
        address otherTokenAddress = (pair.token0() == address(trop))
            ? pair.token1()
            : pair.token0();
        IERC20 otherToken = IERC20(otherTokenAddress);

        lpSupply = lpToken.totalSupply();
        if (lpSupply > 0) {
            uint256 lpTROPBalance = tropToken.balanceOf(address(lpToken));
            uint256 lpOtherBalance = otherToken.balanceOf(address(lpToken));

            lockedLP = lpToken.balanceOf(address(this));

            totalLockedLP = lockedLP;

            tropAmount = lockedLP.mul(lpTROPBalance).div(lpSupply);
            totalTROPAmount = totalLockedLP.mul(lpTROPBalance).div(lpSupply);

            tokenAmount = lockedLP.mul(lpOtherBalance).div(lpSupply);
            totalTokenAmount = totalLockedLP.mul(lpOtherBalance).div(lpSupply);
        }
    }
}
function add(
    uint256 _allocPoint,
    IERC20 _token,
    bool _withUpdate
) public onlyOwner {
    require(address(_token) != address(trop), "!TROP token");
    if (_withUpdate) {
        massUpdatePools();
    }

    uint256 length = poolInfo.length;
    for (uint256 pid = 0; pid < length; ++pid) {
        require(poolInfo[pid].token != _token, "Error pool already added");
    }

    totalAllocPoint = totalAllocPoint.add(_allocPoint);

    poolInfo.push(
        PoolInfo({
            token: _token,
            allocPoint: _allocPoint,
            accTROPPerRAP: 0,
            accTRIPPerRAP: 0,
            totalRewardAllocPoint: 0,
            emergencyWithdrawable: false,
            rewardsInThisEpoch: 0,
            cumulativeRewardsSinceStart: 0,
            startBlock: block.number,
            startTime: block.timestamp,
            epochCalculationStartBlock: block.number,
            lastTRIPRewardBlock: block.number > START_BLOCK
                ? block.number
                : START_BLOCK
        })
    );
}

// Add a new token pool. Can only be called by the owner.
// Note contract owner is meant to be a governance contract allowing TROP governance consensus