pragma solidity 0.6.12;

abstract contract Context {

    function _msgSender() internal view virtual returns (address payable) {

        return msg.sender;

    }



    function _msgData() internal view virtual returns (bytes memory) {

        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691

        return msg.data;

    }

}

interface IContractRegistry {



	event ContractAddressUpdated(string contractName, address addr, bool managedContract);

	event ManagerChanged(string role, address newManager);

	event ContractRegistryUpdated(address newContractRegistry);



	/*

	* External functions

	*/



	/// @dev updates the contracts address and emits a corresponding event

	/// managedContract indicates whether the contract is managed by the registry and notified on changes

	function setContract(string calldata contractName, address addr, bool managedContract) external /* onlyAdmin */;



	/// @dev returns the current address of the given contracts

	function getContract(string calldata contractName) external view returns (address);



	/// @dev returns the list of contract addresses managed by the registry

	function getManagedContracts() external view returns (address[] memory);



	function setManager(string calldata role, address manager) external /* onlyAdmin */;



	function getManager(string calldata role) external view returns (address);



	function lockContracts() external /* onlyAdmin */;



	function unlockContracts() external /* onlyAdmin */;



	function setNewContractRegistry(IContractRegistry newRegistry) external /* onlyAdmin */;



	function getPreviousContractRegistry() external view returns (address);



}

interface ILockable {



    event Locked();

    event Unlocked();



    function lock() external /* onlyLockOwner */;

    function unlock() external /* onlyLockOwner */;

    function isLocked() view external returns (bool);



}

interface IProtocol {

    event ProtocolVersionChanged(string deploymentSubset, uint256 currentVersion, uint256 nextVersion, uint256 fromTimestamp);



    /*

     *   External functions

     */



    /// @dev returns true if the given deployment subset exists (i.e - is registered with a protocol version)

    function deploymentSubsetExists(string calldata deploymentSubset) external view returns (bool);



    /// @dev returns the current protocol version for the given deployment subset.

    function getProtocolVersion(string calldata deploymentSubset) external view returns (uint256);



    /*

     *   Governance functions

     */



    /// @dev create a new deployment subset.

    function createDeploymentSubset(string calldata deploymentSubset, uint256 initialProtocolVersion) external /* onlyFunctionalManager */;



    /// @dev schedules a protocol version upgrade for the given deployment subset.

    function setProtocolVersion(string calldata deploymentSubset, uint256 nextVersion, uint256 fromTimestamp) external /* onlyFunctionalManager */;

}

contract Initializable {



    address private _initializationAdmin;



    event InitializationComplete();



    constructor() public{

        _initializationAdmin = msg.sender;

    }



    modifier onlyInitializationAdmin() {

        require(msg.sender == initializationAdmin(), "sender is not the initialization admin");



        _;

    }



    /*

    * External functions

    */



    function initializationAdmin() public view returns (address) {

        return _initializationAdmin;

    }



    function initializationComplete() external onlyInitializationAdmin {

        _initializationAdmin = address(0);

        emit InitializationComplete();

    }



    function isInitializationComplete() public view returns (bool) {

        return _initializationAdmin == address(0);

    }



}

contract WithClaimableRegistryManagement is Context {

    address private _registryAdmin;

    address private _pendingRegistryAdmin;



    event RegistryManagementTransferred(address indexed previousRegistryAdmin, address indexed newRegistryAdmin);



    /**

     * @dev Initializes the contract setting the deployer as the initial registryRegistryAdmin.

     */

    constructor () internal {

        address msgSender = _msgSender();

        _registryAdmin = msgSender;

        emit RegistryManagementTransferred(address(0), msgSender);

    }



    /**

     * @dev Returns the address of the current registryAdmin.

     */

    function registryAdmin() public view returns (address) {

        return _registryAdmin;

    }



    /**

     * @dev Throws if called by any account other than the registryAdmin.

     */

    modifier onlyRegistryAdmin() {

        require(isRegistryAdmin(), "WithClaimableRegistryManagement: caller is not the registryAdmin");

        _;

    }



    /**

     * @dev Returns true if the caller is the current registryAdmin.

     */

    function isRegistryAdmin() public view returns (bool) {

        return _msgSender() == _registryAdmin;

    }



    /**

     * @dev Leaves the contract without registryAdmin. It will not be possible to call

     * `onlyManager` functions anymore. Can only be called by the current registryAdmin.

     *

     * NOTE: Renouncing registryManagement will leave the contract without an registryAdmin,

     * thereby removing any functionality that is only available to the registryAdmin.

     */

    function renounceRegistryManagement() public onlyRegistryAdmin {

        emit RegistryManagementTransferred(_registryAdmin, address(0));

        _registryAdmin = address(0);

    }



    /**

     * @dev Transfers registryManagement of the contract to a new account (`newManager`).

     */

    function _transferRegistryManagement(address newRegistryAdmin) internal {

        require(newRegistryAdmin != address(0), "RegistryAdmin: new registryAdmin is the zero address");

        emit RegistryManagementTransferred(_registryAdmin, newRegistryAdmin);

        _registryAdmin = newRegistryAdmin;

    }



    /**

     * @dev Modifier throws if called by any account other than the pendingManager.

     */

    modifier onlyPendingRegistryAdmin() {

        require(msg.sender == _pendingRegistryAdmin, "Caller is not the pending registryAdmin");

        _;

    }

    /**

     * @dev Allows the current registryAdmin to set the pendingManager address.

     * @param newRegistryAdmin The address to transfer registryManagement to.

     */

    function transferRegistryManagement(address newRegistryAdmin) public onlyRegistryAdmin {

        _pendingRegistryAdmin = newRegistryAdmin;

    }



    /**

     * @dev Allows the _pendingRegistryAdmin address to finalize the transfer.

     */

    function claimRegistryManagement() external onlyPendingRegistryAdmin {

        _transferRegistryManagement(_pendingRegistryAdmin);

        _pendingRegistryAdmin = address(0);

    }



    /**

     * @dev Returns the current pendingRegistryAdmin

    */

    function pendingRegistryAdmin() public view returns (address) {

       return _pendingRegistryAdmin;  

    }

}

contract ContractRegistryAccessor is WithClaimableRegistryManagement, Initializable {



    IContractRegistry private contractRegistry;



    constructor(IContractRegistry _contractRegistry, address _registryAdmin) public {

        require(address(_contractRegistry) != address(0), "_contractRegistry cannot be 0");

        setContractRegistry(_contractRegistry);

        _transferRegistryManagement(_registryAdmin);

    }



    modifier onlyAdmin {

        require(isAdmin(), "sender is not an admin (registryManger or initializationAdmin)");



        _;

    }



    function isManager(string memory role) internal view returns (bool) {

        IContractRegistry _contractRegistry = contractRegistry;

        return isAdmin() || _contractRegistry != IContractRegistry(0) && contractRegistry.getManager(role) == msg.sender;

    }



    function isAdmin() internal view returns (bool) {

        return msg.sender == registryAdmin() || msg.sender == initializationAdmin() || msg.sender == address(contractRegistry);

    }



    function getProtocolContract() internal view returns (address) {

        return contractRegistry.getContract("protocol");

    }



    function getStakingRewardsContract() internal view returns (address) {

        return contractRegistry.getContract("stakingRewards");

    }



    function getFeesAndBootstrapRewardsContract() internal view returns (address) {

        return contractRegistry.getContract("feesAndBootstrapRewards");

    }



    function getCommitteeContract() internal view returns (address) {

        return contractRegistry.getContract("committee");

    }



    function getElectionsContract() internal view returns (address) {

        return contractRegistry.getContract("elections");

    }



    function getDelegationsContract() internal view returns (address) {

        return contractRegistry.getContract("delegations");

    }



    function getGuardiansRegistrationContract() internal view returns (address) {

        return contractRegistry.getContract("guardiansRegistration");

    }



    function getCertificationContract() internal view returns (address) {

        return contractRegistry.getContract("certification");

    }



    function getStakingContract() internal view returns (address) {

        return contractRegistry.getContract("staking");

    }



    function getSubscriptionsContract() internal view returns (address) {

        return contractRegistry.getContract("subscriptions");

    }



    function getStakingRewardsWallet() internal view returns (address) {

        return contractRegistry.getContract("stakingRewardsWallet");

    }



    function getBootstrapRewardsWallet() internal view returns (address) {

        return contractRegistry.getContract("bootstrapRewardsWallet");

    }



    function getGeneralFeesWallet() internal view returns (address) {

        return contractRegistry.getContract("generalFeesWallet");

    }



    function getCertifiedFeesWallet() internal view returns (address) {

        return contractRegistry.getContract("certifiedFeesWallet");

    }



    function getStakingContractHandler() internal view returns (address) {

        return contractRegistry.getContract("stakingContractHandler");

    }



    /*

    * Governance functions

    */



    event ContractRegistryAddressUpdated(address addr);



    function setContractRegistry(IContractRegistry newContractRegistry) public onlyAdmin {

        require(newContractRegistry.getPreviousContractRegistry() == address(contractRegistry), "new contract registry must provide the previous contract registry");

        contractRegistry = newContractRegistry;

        emit ContractRegistryAddressUpdated(address(newContractRegistry));

    }



    function getContractRegistry() public view returns (IContractRegistry) {

        return contractRegistry;

    }



}

contract Lockable is ILockable, ContractRegistryAccessor {



    bool public locked;



    constructor(IContractRegistry _contractRegistry, address _registryAdmin) ContractRegistryAccessor(_contractRegistry, _registryAdmin) public {}



    modifier onlyLockOwner() {

        require(msg.sender == registryAdmin() || msg.sender == address(getContractRegistry()), "caller is not a lock owner");



        _;

    }



    function lock() external override onlyLockOwner {

        locked = true;

        emit Locked();

    }



    function unlock() external override onlyLockOwner {

        locked = false;

        emit Unlocked();

    }



    function isLocked() external override view returns (bool) {

        return locked;

    }



    modifier onlyWhenActive() {

        require(!locked, "contract is locked for this operation");



        _;

    }

}

contract ManagedContract is Lockable {



    constructor(IContractRegistry _contractRegistry, address _registryAdmin) Lockable(_contractRegistry, _registryAdmin) public {}



    modifier onlyMigrationManager {

        require(isManager("migrationManager"), "sender is not the migration manager");



        _;

    }



    modifier onlyFunctionalManager {

        require(isManager("functionalManager"), "sender is not the functional manager");



        _;

    }



    function refreshContracts() virtual external {}



}

contract Protocol is IProtocol, ManagedContract {



    struct DeploymentSubset {

        bool exists;

        uint256 nextVersion;

        uint fromTimestamp;

        uint256 currentVersion;

    }



    mapping(string => DeploymentSubset) public deploymentSubsets;



    constructor(IContractRegistry _contractRegistry, address _registryAdmin) ManagedContract(_contractRegistry, _registryAdmin) public {}



    /*

     * External functions

     */



    function deploymentSubsetExists(string calldata deploymentSubset) external override view returns (bool) {

        return deploymentSubsets[deploymentSubset].exists;

    }



    function getProtocolVersion(string calldata deploymentSubset) external override view returns (uint256 currentVersion) {

        (, currentVersion) = checkPrevUpgrades(deploymentSubset);

    }



    function createDeploymentSubset(string calldata deploymentSubset, uint256 initialProtocolVersion) external override onlyFunctionalManager {

        require(!deploymentSubsets[deploymentSubset].exists, "deployment subset already exists");



        deploymentSubsets[deploymentSubset].currentVersion = initialProtocolVersion;

        deploymentSubsets[deploymentSubset].nextVersion = initialProtocolVersion;

        deploymentSubsets[deploymentSubset].fromTimestamp = now;

        deploymentSubsets[deploymentSubset].exists = true;



        emit ProtocolVersionChanged(deploymentSubset, initialProtocolVersion, initialProtocolVersion, now);

    }



    function setProtocolVersion(string calldata deploymentSubset, uint256 nextVersion, uint256 fromTimestamp) external override onlyFunctionalManager {

        require(deploymentSubsets[deploymentSubset].exists, "deployment subset does not exist");

        require(fromTimestamp > now, "a protocol update can only be scheduled for the future");



        (bool prevUpgradeExecuted, uint256 currentVersion) = checkPrevUpgrades(deploymentSubset);



        require(nextVersion >= currentVersion, "protocol version must be greater or equal to current version");



        deploymentSubsets[deploymentSubset].nextVersion = nextVersion;

        deploymentSubsets[deploymentSubset].fromTimestamp = fromTimestamp;

        if (prevUpgradeExecuted) {

            deploymentSubsets[deploymentSubset].currentVersion = currentVersion;

        }



        emit ProtocolVersionChanged(deploymentSubset, currentVersion, nextVersion, fromTimestamp);

    }



    /*

     * Private functions

     */



    function checkPrevUpgrades(string memory deploymentSubset) private view returns (bool prevUpgradeExecuted, uint256 currentVersion) {

        prevUpgradeExecuted = deploymentSubsets[deploymentSubset].fromTimestamp <= now;

        currentVersion = prevUpgradeExecuted ? deploymentSubsets[deploymentSubset].nextVersion :

                                               deploymentSubsets[deploymentSubset].currentVersion;

    }



    /*

     * Contracts topology / registry interface

     */



    function refreshContracts() external override {}

}

{"success": true, "error": null, "results": {}}