// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: @openzeppelin/contracts/utils/Context.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// File: contracts/infrastructure/IKresusRegistry.sol



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

/**
 * @title IModuleRegistry
 * @notice Interface for the registry of authorised modules.
 */
interface IKresusRegistry {
    /**
     * @notice Registers a module.
     * @param _module The module.
     * @param _name The unique name of the module.
     */
    function registerModule(address _module, string calldata _name) external;

    /**
     * @notice Deregisters a module.
     * @param _module The module.
     */
    function deregisterModule(address _module) external;

    /**
     * @notice Registers contract addresses with their selectors.
     * @param _contracts Contract addresses to be whitelisted.
     * @param _selectors List of corresponding method ids to be whitelisted.
     */
    function registerContract(address[] memory _contracts, bytes4[] memory _selectors) external;
    
    /**
     * @notice Deregisters contract addresses with their selectors.
     * @param _contracts Contract addresses to be whitelisted.
     * @param _selectors List of corresponding method ids to be whitelisted.
     */
    function deregisterContract(address[] memory _contracts, bytes4[] memory _selectors) external;

    /**
     * @notice Function to set the time delay for remove guardian operation.
     * @param _td New time delay for removing guardian.
     */
    function setRemoveGuardianTd(uint256 _td) external;

    /**
     * @notice Function to set the time delay for unlock operation.
     * @param _td New time delay for unlocking a vault.
     */
    function setUnlockTd(uint256 _td) external;

    /**
     * @notice Gets the name of a module from its address.
     * @param _module The module address.
     * @return the name.
     */
    function moduleInfo(address _module) external view returns (string memory);

    /**
     * @notice Checks if a module is registered.
     * @param _module The module address.
     * @return true if the module is registered.
     */
    function isRegisteredModule(address _module) external view returns (bool);

    /**
     * @notice Checks if given modules are registered.
     * @param _modules The module addresses.
     * @return true if modules are registered.
     */
    function isRegisteredModule(address[] calldata _modules) external view returns (bool);

    /**
     * @notice Checks if given list of contracts addresses and corresponsing method ids are whitelisted.
     * @param _contracts List of contract addresses.
     * @param _sigs List of corresponding method ids.
     * @return true if all the contract addresses and method ids are whitelisted else false.
     */
    function isRegisteredCalls(address[] memory _contracts, bytes4[] memory _sigs) external view returns (bool);

    /**
     * @notice Function to get the time delay for unlock.
     * @return Time delay for unlocking a vault.
     */
    function getUnlockTd() external view returns(uint256); 


    /**
     * @notice Function to get the time delay for remove guardian.
     * @return Time delay for remove guardian.
     */
    function getRemoveGuardianTd() external view returns(uint256); 
}
// File: contracts/infrastructure/KresusRegistry.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IKresusRegistry} from "./IKresusRegistry.sol";

/**
 * @title KresusRegistry
 * @notice Registry of authorised modules.
 * Modules must be registered before they can be authorised on a vault.
 */
contract KresusRegistry is IKresusRegistry, Ownable {

    uint256 private removeGuardianTd = 5 days;
    uint256 private unlockTd = 10 days;

    struct ModuleInfo {
        bool exists;
        string name;
    }

    struct ContractInfo {
        bytes4[] selectors;
        mapping(bytes4 => uint256) index;
    }

    // deployed module address to Info struct mapping.
    mapping (address => ModuleInfo) internal modules;

    // contract addresses which are whitelisted.
    mapping (address => ContractInfo) internal contracts;

    // emitted when a new module is registered.
    event ModuleRegistered(address indexed module, string name);

    // emitted when an existing module is removed from the registry.
    event ModuleDeRegistered(address module);

    // emitted when a new contract is registered with selectors.
    event ContractRegistered(address[] contracts, bytes4[] selectors);

    // emitted when an existing contract is removed from the registry
    event ContractDeRegistered(address[] contracts, bytes4[] selectors);

    // emitted when time delay is changed for unlock
    event UnlockTdChanged(uint256 _td);

    // emitted when time delay is changed for remove guardian
    event RemoveGuardianTdChanged(uint256 _td);

    /**
     * @inheritdoc IKresusRegistry
     */
    function registerModule(
        address _module,
        string calldata _name
    )
        external
        onlyOwner
    {
        require(_module != address(0), "KRe: Invalid module");
        require(!modules[_module].exists, "KRe: module already exists");
        modules[_module] = ModuleInfo({exists: true, name: _name});
        emit ModuleRegistered(_module, _name);
    }

    /**
     * @inheritdoc IKresusRegistry
     */
    function deregisterModule(address _module) external onlyOwner {
        require(modules[_module].exists, "KRe: module does not exist");
        delete modules[_module];
        emit ModuleDeRegistered(_module);
    }

    /**
     * @inheritdoc IKresusRegistry
     */
    function registerContract(
        address[] calldata _contracts,
        bytes4[] calldata _selectors
    )
        external
        onlyOwner
    {
        uint256 len = _contracts.length;
        require(len == _selectors.length, "KRe: Inconsistent lengths");
        for(uint256 i=0; i<len; i++) {
            ContractInfo storage ci = contracts[_contracts[i]];
            require(ci.index[_selectors[i]] == 0, "KRe: Already registered");
            ci.selectors.push(_selectors[i]);
            ci.index[_selectors[i]] = ci.selectors.length;
        }
        emit ContractRegistered(_contracts, _selectors);
    }

    /**
     * @inheritdoc IKresusRegistry
     */
    function deregisterContract(
        address[] calldata _contracts,
        bytes4[] calldata _selectors
    )
        external
        onlyOwner
    {
        uint256 len = _contracts.length;
        require(len == _selectors.length, "KRe: Inconsistent lengths");
        for(uint256 i=0; i<len; i++) {
            ContractInfo storage ci = contracts[_contracts[i]];
            uint256 j = ci.index[_selectors[i]];
            require(j != 0, "KRe: Already deregistered");
            ci.selectors[j - 1] = ci.selectors[ci.selectors.length - 1];
            ci.index[ci.selectors[j-1]] = j;
            ci.index[_selectors[i]] = 0;
            ci.selectors.pop();
        }
        emit ContractDeRegistered(_contracts, _selectors);
    }

    /**
     * @inheritdoc IKresusRegistry
     */
    function setRemoveGuardianTd(
        uint256 _td
    ) 
        external
        onlyOwner
    {
        removeGuardianTd = _td;
        emit RemoveGuardianTdChanged(_td);
    }

    /**
     * @inheritdoc IKresusRegistry
     */
    function setUnlockTd(
        uint256 _td
    )
        external
        onlyOwner
    {
        unlockTd = _td;
        emit UnlockTdChanged(_td);
    }

    /**
     * @inheritdoc IKresusRegistry
     */
    function moduleInfo(address _module) external view returns (string memory) {
        return modules[_module].name;
    }

    /**
     * @inheritdoc IKresusRegistry
     */
    function isRegisteredModule(
        address _module
    )
        external
        view
        returns(bool)
    {
        return modules[_module].exists;
    }

    /**
     * @inheritdoc IKresusRegistry
     */
    function isRegisteredModule(address[] calldata _modules) external view returns (bool) {
        for (uint256 i = 0; i < _modules.length; i++) {
            if (!modules[_modules[i]].exists) {
                return false;
            }
        }
        return true;
    }

    /**
     * @inheritdoc IKresusRegistry
     */
    function isRegisteredCalls(
        address[] memory _contracts,
        bytes4[] memory _sigs
    )
        external
        view
        returns(bool registered)
    {
        registered = true;
        uint256 len = _contracts.length;
        require(len == _sigs.length, "KRe: Inconsistent lengths");
        for(uint256 i=0;i<len;i++) {
            uint256 index = contracts[_contracts[i]].index[_sigs[i]];
            if(index == 0)
                return false;
        }
    }

    /**
     * @inheritdoc IKresusRegistry
     */
    function getUnlockTd() external view returns(uint256) {
        return unlockTd;
    }

    /**
     * @inheritdoc IKresusRegistry
     */
    function getRemoveGuardianTd() external view returns(uint256) {
        return removeGuardianTd;
    }
}