// File: convex-platform/contracts/contracts/PoolManagerProxy.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import "./Interfaces.sol";

/**
 * @title   PoolManagerProxy
 * @author  ConvexFinance
 * @notice  Immutable pool manager proxy to enforce that there are no multiple pools of the same gauge
 *          as well as new lp tokens are not gauge tokens
 * @dev     Called by PoolManagerShutdownProxy 
 */
contract PoolManagerProxy{

    address public immutable pools;
    address public owner;
    address public operator;
    bool public isShutdown;

    /**
     * @param _pools      Contract can call addPool currently Booster
     * @param _owner      Contract owner currently multisig
     */
    constructor(
      address _pools, 
      address _owner
    ) public {
        pools = _pools;
        owner = _owner;
        operator = msg.sender;
    }

    modifier onlyOwner() {
        require(owner == msg.sender, "!owner");
        _;
    }

    modifier onlyOperator() {
        require(operator == msg.sender, "!op");
        _;
    }

    //set owner - only OWNER
    function setOwner(address _owner) external onlyOwner{
        owner = _owner;
    }

    //set operator - only OWNER
    function setOperator(address _operator) external onlyOwner{
        operator = _operator;
    }

    // sealed to be immutable
    // function revertControl() external{
    // }

    //shutdown pool management and disallow new pools. change is immutable
    function shutdownSystem() external onlyOwner{
        isShutdown = true;
    }

    //shutdown a pool - only OPERATOR
    function shutdownPool(uint256 _pid) external onlyOperator returns(bool){
        require(!isShutdown, "shutdown");
        return IPools(pools).shutdownPool(_pid);
    }

    /**
     * @notice  Add pool to system
     * @dev     Only callable by the operator looks up the gauge from the gaugeMap in Booster to ensure
     *          it hasn't already been added
     */
    function addPool(address _lptoken, address _gauge, uint256 _stashVersion) external onlyOperator returns(bool){
        require(!isShutdown, "shutdown");
        require(_gauge != address(0),"gauge is 0");
        require(_lptoken != address(0),"lp token is 0");

        //check if a pool with this gauge already exists
        bool gaugeExists = IPools(pools).gaugeMap(_gauge);
        require(!gaugeExists, "already registered gauge");

        //must also check that the lp token is not a registered gauge
        //because curve gauges are tokenized
        gaugeExists = IPools(pools).gaugeMap(_lptoken);
        require(!gaugeExists, "lptoken is gauge");

        return IPools(pools).addPool(_lptoken,_gauge,_stashVersion);
    }
}

// File: convex-platform/contracts/contracts/Interfaces.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;



interface ICurveGauge {
    function deposit(uint256) external;
    function balanceOf(address) external view returns (uint256);
    function withdraw(uint256) external;
    function claim_rewards() external;
    function reward_tokens(uint256) external view returns(address);//v2
    function rewarded_token() external view returns(address);//v1
    function lp_token() external view returns(address);
}

interface ICurveVoteEscrow {
    function create_lock(uint256, uint256) external;
    function increase_amount(uint256) external;
    function increase_unlock_time(uint256) external;
    function withdraw() external;
    function smart_wallet_checker() external view returns (address);
    function commit_smart_wallet_checker(address) external;
    function apply_smart_wallet_checker() external;
}

interface IWalletChecker {
    function check(address) external view returns (bool);
    function approveWallet(address) external;
    function dao() external view returns (address);
}

interface IVoting{
    function vote(uint256, bool, bool) external; //voteId, support, executeIfDecided
    function getVote(uint256) external view returns(bool,bool,uint64,uint64,uint64,uint64,uint256,uint256,uint256,bytes memory); 
    function vote_for_gauge_weights(address,uint256) external;
}

interface IMinter{
    function mint(address) external;
}

interface IStaker{
    function deposit(address, address) external returns (bool);
    function withdraw(address) external returns (uint256);
    function withdraw(address, address, uint256) external returns (bool);
    function withdrawAll(address, address) external returns (bool);
    function createLock(uint256, uint256) external returns(bool);
    function increaseAmount(uint256) external returns(bool);
    function increaseTime(uint256) external returns(bool);
    function release() external returns(bool);
    function claimCrv(address) external returns (uint256);
    function claimRewards(address) external returns(bool);
    function claimFees(address,address) external returns (uint256);
    function setStashAccess(address, bool) external returns (bool);
    function vote(uint256,address,bool) external returns(bool);
    function voteGaugeWeight(address,uint256) external returns(bool);
    function balanceOfPool(address) external view returns (uint256);
    function operator() external view returns (address);
    function execute(address _to, uint256 _value, bytes calldata _data) external returns (bool, bytes memory);
    function setVote(bytes32 hash, bool valid) external;
    function migrate(address to) external;
}

interface IRewards{
    function stake(address, uint256) external;
    function stakeFor(address, uint256) external;
    function withdraw(address, uint256) external;
    function exit(address) external;
    function getReward(address) external;
    function queueNewRewards(uint256) external;
    function notifyRewardAmount(uint256) external;
    function addExtraReward(address) external;
    function extraRewardsLength() external view returns (uint256);
    function stakingToken() external view returns (address);
    function rewardToken() external view returns(address);
    function earned(address account) external view returns (uint256);
}

interface IStash{
    function stashRewards() external returns (bool);
    function processStash() external returns (bool);
    function claimRewards() external returns (bool);
    function initialize(uint256 _pid, address _operator, address _staker, address _gauge, address _rewardFactory) external;
    function setExtraReward(address) external;
}

interface IFeeDistributor {
    function claimToken(address user, address token) external returns (uint256);
    function claimTokens(address user, address[] calldata tokens) external returns (uint256[] memory);
    function getTokenTimeCursor(address token) external view returns (uint256);
}

interface ITokenMinter{
    function mint(address,uint256) external;
    function burn(address,uint256) external;
}

interface IDeposit{
    function isShutdown() external view returns(bool);
    function balanceOf(address _account) external view returns(uint256);
    function totalSupply() external view returns(uint256);
    function poolInfo(uint256) external view returns(address,address,address,address,address, bool);
    function rewardClaimed(uint256,address,uint256) external;
    function withdrawTo(uint256,uint256,address) external;
    function claimRewards(uint256,address) external returns(bool);
    function rewardArbitrator() external returns(address);
    function setGaugeRedirect(uint256 _pid) external returns(bool);
    function owner() external returns(address);
    function deposit(uint256 _pid, uint256 _amount, bool _stake) external returns(bool);
}

interface ICrvDeposit{
    function deposit(uint256, bool) external;
    function lockIncentive() external view returns(uint256);
}

interface IRewardFactory{
    function setAccess(address,bool) external;
    function CreateCrvRewards(uint256,address,address) external returns(address);
    function CreateTokenRewards(address,address,address) external returns(address);
    function activeRewardCount(address) external view returns(uint256);
    function addActiveReward(address,uint256) external returns(bool);
    function removeActiveReward(address,uint256) external returns(bool);
}

interface IStashFactory{
    function CreateStash(uint256,address,address,uint256) external returns(address);
    function setImplementation(address, address, address) external;
}

interface ITokenFactory{
    function CreateDepositToken(address) external returns(address);
}

interface IPools{
    function addPool(address _lptoken, address _gauge, uint256 _stashVersion) external returns(bool);
    function forceAddPool(address _lptoken, address _gauge, uint256 _stashVersion) external returns(bool);
    function shutdownPool(uint256 _pid) external returns(bool);
    function poolInfo(uint256) external view returns(address,address,address,address,address,bool);
    function poolLength() external view returns (uint256);
    function gaugeMap(address) external view returns(bool);
    function setPoolManager(address _poolM) external;
    function shutdownSystem() external;
    function setUsedAddress(address[] memory) external;
}

interface IVestedEscrow{
    function fund(address[] calldata _recipient, uint256[] calldata _amount) external returns(bool);
}

interface IRewardDeposit {
    function addReward(address, uint256) external;
}

interface ILocker {
    function lock(address _account, uint256 _amount) external;
    function checkpointEpoch() external;
    function epochCount() external view returns (uint256);
    function balanceAtEpochOf(uint256 _epoch, address _user) external view returns (uint256 amount);
    function totalSupplyAtEpoch(uint256 _epoch) external view returns (uint256 supply);
    function queueNewRewards(address _rewardsToken, uint256 reward) external;
    function getReward(address _account, bool _stake) external;
    function getReward(address _account) external;
}