// File: contracts/common/CommonEventsAndErrors.sol



pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (common/CommonEventsAndErrors.sol)

/// @notice A collection of common errors thrown within the Temple contracts
library CommonEventsAndErrors {
    error InsufficientBalance(address token, uint256 required, uint256 balance);
    error InvalidParam();
    error InvalidAddress();
    error InvalidAccess();
    error InvalidAmount(address token, uint256 amount);
    error ExpectedNonZero();
    error Unimplemented();
    event TokenRecovered(address indexed to, address indexed token, uint256 amount);
}

// File: contracts/interfaces/v2/access/ITempleElevatedAccess.sol



pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (interfaces/v2/access/ITempleElevatedAccess.sol)

/**
 * @notice Inherit to add Executor and Rescuer roles for DAO elevated access.
 */ 
interface ITempleElevatedAccess {
    event ExplicitAccessSet(address indexed account, bytes4 indexed fnSelector, bool indexed value);
    event RescueModeSet(bool indexed value);

    event NewRescuerProposed(address indexed oldRescuer, address indexed oldProposedRescuer, address indexed newProposedRescuer);
    event NewRescuerAccepted(address indexed oldRescuer, address indexed newRescuer);

    event NewExecutorProposed(address indexed oldExecutor, address indexed oldProposedExecutor, address indexed newProposedExecutor);
    event NewExecutorAccepted(address indexed oldExecutor, address indexed newExecutor);

    struct ExplicitAccess {
        bytes4 fnSelector;
        bool allowed;
    }

    /**
     * @notice A set of addresses which are approved to execute emergency operations.
     */ 
    function rescuer() external returns (address);

    /**
     * @notice A set of addresses which are approved to execute normal operations on behalf of the DAO.
     */ 
    function executor() external returns (address);

    /**
     * @notice Explicit approval for an address to execute a function.
     * allowedCaller => function selector => true/false
     */
    function explicitFunctionAccess(address contractAddr, bytes4 functionSelector) external returns (bool);

    /**
     * @notice Under normal circumstances, rescuers don't have access to admin/operational functions.
     * However when rescue mode is enabled (by rescuers or executors), they claim the access rights.
     */
    function inRescueMode() external returns (bool);
    
    /**
     * @notice Set the contract into or out of rescue mode.
     * Only the rescuers or executors are allowed to set.
     */
    function setRescueMode(bool value) external;

    /**
     * @notice Proposes a new Rescuer.
     * Can only be called by the current rescuer.
     */
    function proposeNewRescuer(address account) external;

    /**
     * @notice Caller accepts the role as new Rescuer.
     * Can only be called by the proposed rescuer
     */
    function acceptRescuer() external;

    /**
     * @notice Proposes a new Executor.
     * Can only be called by the current executor or resucer (if in resuce mode)
     */
    function proposeNewExecutor(address account) external;

    /**
     * @notice Caller accepts the role as new Executor.
     * Can only be called by the proposed executor
     */
    function acceptExecutor() external;

    /**
     * @notice Grant `allowedCaller` the rights to call the function selectors in the access list.
     * @dev fnSelector == bytes4(keccak256("fn(argType1,argType2,...)"))
     */
    function setExplicitAccess(address allowedCaller, ExplicitAccess[] calldata access) external;
}

// File: contracts/interfaces/v2/interestRate/IInterestRateModel.sol



pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (interfaces/v2/interestRate/IInterestRateModel.sol)

interface IInterestRateModel {
    /**
     * @notice Calculates the current interest rate based on a utilization ratio
     * @param utilizationRatio The utilization ratio scaled to `PRECISION`
     * @return interestRate The interest rate (scaled by PRECISION). 0.05e18 == 5%
     */
    function calculateInterestRate(
        uint256 utilizationRatio
    ) external view returns (uint96 interestRate);
}
// File: contracts/v2/access/TempleElevatedAccess.sol



pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (v2/access/TempleElevatedAccess.sol)

import { ITempleElevatedAccess } from "contracts/interfaces/v2/access/ITempleElevatedAccess.sol";
import { CommonEventsAndErrors } from "contracts/common/CommonEventsAndErrors.sol";

/**
 * @notice Inherit to add Executor and Rescuer roles for DAO elevated access.
 */ 
abstract contract TempleElevatedAccess is ITempleElevatedAccess {
    /**
     * @notice The address which is approved to execute emergency operations.
     */ 
    address public override rescuer;

    /**
     * @notice The address which is approved to execute normal operations on behalf of the DAO.
     */ 
    address public override executor;

    /**
     * @notice Explicit approval for an address to execute a function.
     * allowedCaller => function selector => true/false
     */
    mapping(address => mapping(bytes4 => bool)) public override explicitFunctionAccess;

    /**
     * @notice Under normal circumstances, rescuers don't have access to admin/operational functions.
     * However when rescue mode is enabled (by rescuers or executors), they claim the access rights.
     */
    bool public override inRescueMode;

    /// @dev Track proposed rescuer/executor
    address private _proposedNewRescuer;
    address private _proposedNewExecutor;

    constructor(address initialRescuer, address initialExecutor) {
        if (initialRescuer == address(0)) revert CommonEventsAndErrors.InvalidAddress();
        if (initialExecutor == address(0)) revert CommonEventsAndErrors.InvalidAddress();
        if (initialExecutor == initialRescuer) revert CommonEventsAndErrors.InvalidAddress();

        rescuer = initialRescuer;
        executor = initialExecutor;
    }

    /**
     * @notice Set the contract into or out of rescue mode.
     * Only the rescuers are allowed to set.
     */
    function setRescueMode(bool value) external override {
        if (msg.sender != rescuer) revert CommonEventsAndErrors.InvalidAccess();
        emit RescueModeSet(value);
        inRescueMode = value;
    }

    /**
     * @notice Proposes a new Rescuer.
     * Can only be called by the current rescuer.
     */
    function proposeNewRescuer(address account) external override {
        if (msg.sender != rescuer) revert CommonEventsAndErrors.InvalidAccess();
        if (account == address(0)) revert CommonEventsAndErrors.InvalidAddress();
        emit NewRescuerProposed(msg.sender, _proposedNewRescuer, account);
        _proposedNewRescuer = account;
    }

    /**
     * @notice Caller accepts the role as new Rescuer.
     * Can only be called by the proposed rescuer
     */
    function acceptRescuer() external override {
        if (msg.sender != _proposedNewRescuer) revert CommonEventsAndErrors.InvalidAccess();
        if (msg.sender == executor) revert CommonEventsAndErrors.InvalidAddress();

        emit NewRescuerAccepted(rescuer, msg.sender);
        rescuer = msg.sender;
        delete _proposedNewRescuer;
    }

    /**
     * @notice Proposes a new Executor.
     * Can only be called by the current executor or rescuer (if in resuce mode)
     */
    function proposeNewExecutor(address account) external override onlyElevatedAccess {
        if (account == address(0)) revert CommonEventsAndErrors.InvalidAddress();
        emit NewExecutorProposed(executor, _proposedNewExecutor, account);
        _proposedNewExecutor = account;
    }

    /**
     * @notice Caller accepts the role as new Executor.
     * Can only be called by the proposed executor
     */
    function acceptExecutor() external override {
        if (msg.sender != _proposedNewExecutor) revert CommonEventsAndErrors.InvalidAccess();
        if (msg.sender == rescuer) revert CommonEventsAndErrors.InvalidAddress();

        emit NewExecutorAccepted(executor, msg.sender);
        executor = msg.sender;
        delete _proposedNewExecutor;
    }

    /**
     * @notice Grant `allowedCaller` the rights to call the function selectors in the access list.
     * @dev fnSelector == bytes4(keccak256("fn(argType1,argType2,...)"))
     */
    function setExplicitAccess(address allowedCaller, ExplicitAccess[] calldata access) external override onlyElevatedAccess {
        if (allowedCaller == address(0)) revert CommonEventsAndErrors.InvalidAddress();
        uint256 _length = access.length;
        ExplicitAccess memory _access;
        for (uint256 i; i < _length; ++i) {
            _access = access[i];
            emit ExplicitAccessSet(allowedCaller, _access.fnSelector, _access.allowed);
            explicitFunctionAccess[allowedCaller][_access.fnSelector] = _access.allowed;
        }
    }

    function isElevatedAccess(address caller, bytes4 fnSelector) internal view returns (bool) {
        if (inRescueMode) {
            // If we're in rescue mode, then only the rescuers can call
            return caller == rescuer;
        } else if (caller == executor || explicitFunctionAccess[caller][fnSelector]) {
            // If we're not in rescue mode, the executor can call all functions
            // or the caller has been given explicit access on this function
            return true;
        }
        return false;
    }

    /**
     * @notice Under normal operations, only the executors are allowed to call.
     * If 'rescue mode' has been enabled, then only the rescuers are allowed to call.
     * @dev Important: Only for use when called from an *external* contract. 
     * If a function with this modifier is called internally then the `msg.sig` 
     * will still refer to the top level externally called function.
     */
    modifier onlyElevatedAccess() {
        if (!isElevatedAccess(msg.sender, msg.sig)) revert CommonEventsAndErrors.InvalidAccess();
        _;
    }

    /**
     * @notice Only the executors or rescuers can call.
     */
    modifier onlyInRescueMode() {
        if (!(inRescueMode && msg.sender == rescuer)) revert CommonEventsAndErrors.InvalidAccess();
        _;
    }

    modifier notInRescueMode() {
        if (inRescueMode) revert CommonEventsAndErrors.InvalidAccess();
        _;
    }
}

// File: contracts/v2/interestRate/BaseInterestRateModel.sol



pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (v2/interestRate/BaseInterestRateModel.sol)

import { IInterestRateModel } from "contracts/interfaces/v2/interestRate/IInterestRateModel.sol";

/**
 * @notice An abstract base contract to calculate the interest rate derived from the current utilization ratio (UR) of debt.
 */
abstract contract BaseInterestRateModel is IInterestRateModel {
    
    uint256 internal constant PRECISION = 1e18;

    uint96 internal constant MAX_ALLOWED_INTEREST_RATE = 5e18; // 500% APR

    /**
     * @notice Derived interest rate model contracts need to implement.
     */
    function computeInterestRateImpl(uint256) internal virtual view returns (uint96);

    /**
     * @notice Calculates the current interest rate based on a utilization ratio
     * @dev The rates are bound to sensible min/max amounts, but it does not fail.
     * @return interestRate The interest rate (scaled by PRECISION). 0.05e18 == 5%
     */
    function calculateInterestRate(uint256 utilizationRatio) external view returns (uint96 interestRate) {
        // Cap the UR at 100%
        if (utilizationRatio > PRECISION) {
            utilizationRatio = PRECISION;
        }

        interestRate = computeInterestRateImpl(utilizationRatio);

        if (interestRate > MAX_ALLOWED_INTEREST_RATE) {
            interestRate = MAX_ALLOWED_INTEREST_RATE;
        }
    }
}

// File: contracts/v2/interestRate/LinearWithKinkInterestRateModel.sol



pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Temple (v2/interestRate/LinearWithKinkInterestRateModel.sol)

import { BaseInterestRateModel } from "contracts/v2/interestRate/BaseInterestRateModel.sol";
import { TempleElevatedAccess } from "contracts/v2/access/TempleElevatedAccess.sol";
import { CommonEventsAndErrors } from "contracts/common/CommonEventsAndErrors.sol";

/**
 * @title 'Linear With Kink' Interest Rate Model
 * @notice An interest rate curve derived from the current utilization ratio (UR) of debt.
 * This is represented as two seperate linear slopes, joined at a 'kink' - a particular UR.
 */
contract LinearWithKinkInterestRateModel is BaseInterestRateModel, TempleElevatedAccess {
    struct RateParams {
        /// @notice The base interest rate which is the y-intercept when utilization rate is 0
        uint80 baseInterestRate;

        /// @notice Interest rate at 100 percent utilization
        uint80 maxInterestRate;

        /// @notice Interest rate at kink
        uint80 kinkInterestRate;

        /// @notice The utilization ratio point at which slope changes
        uint256 kinkUtilizationRatio;
    }

    /**
     * @notice The interest rate parameters to derive the two curves with a kink.
     */
    RateParams public rateParams;

    event InterestRateParamsSet(
        uint80 _baseInterestRate, 
        uint80 _maxInterestRate, 
        uint256 _kinkUtilizationRatio, 
        uint80 _kinkInterestRate
    );

    /**
     * @notice Construct an interest rate model
     * @param _baseInterestRate base interest rate which is the y-intercept when utilization rate is 0
     * @param _maxInterestRate Interest rate at 100 percent utilization
     * @param _kinkUtilizationRatio The utilization point at which slope changes
     * @param _kinkInterestRate Interest rate at the `kinkUtiliszation`;
     */
    constructor(
        address _initialRescuer, 
        address _initialExecutor,
        uint80 _baseInterestRate, 
        uint80 _maxInterestRate, 
        uint256 _kinkUtilizationRatio, 
        uint80 _kinkInterestRate
    ) TempleElevatedAccess(_initialRescuer, _initialExecutor)
    {
        _setRateParams(
            _baseInterestRate, 
            _maxInterestRate, 
            _kinkUtilizationRatio, 
            _kinkInterestRate
        );
    }
    
    /**
     * @notice Update the interest rate parameters.
     */
    function setRateParams(
        uint80 _baseInterestRate, 
        uint80 _maxInterestRate, 
        uint256 _kinkUtilizationRatio, 
        uint80 _kinkInterestRate
    ) external onlyElevatedAccess {
        _setRateParams(
            _baseInterestRate, 
            _maxInterestRate, 
            _kinkUtilizationRatio, 
            _kinkInterestRate
        );
    }

    function _setRateParams(
        uint80 _baseInterestRate, 
        uint80 _maxInterestRate, 
        uint256 _kinkUtilizationRatio, 
        uint80 _kinkInterestRate
    ) internal {
        if (_kinkUtilizationRatio == 0) revert CommonEventsAndErrors.InvalidParam();
        if (_kinkUtilizationRatio >= PRECISION) revert CommonEventsAndErrors.InvalidParam();
        if (_baseInterestRate > _kinkInterestRate) revert CommonEventsAndErrors.InvalidParam();
        if (_kinkInterestRate > _maxInterestRate) revert CommonEventsAndErrors.InvalidParam();

        rateParams = RateParams({
            baseInterestRate: _baseInterestRate,
            maxInterestRate: _maxInterestRate,
            kinkInterestRate: _kinkInterestRate,
            kinkUtilizationRatio: _kinkUtilizationRatio
        });
        emit InterestRateParamsSet(
            _baseInterestRate, 
            _maxInterestRate, 
            _kinkUtilizationRatio, 
            _kinkInterestRate
        );
    }

    /**
     * @notice Calculates the current interest rate based on a utilization ratio
     * @param utilizationRatio The utilization ratio scaled to `PRECISION`
     */
    function computeInterestRateImpl(uint256 utilizationRatio) internal override view returns (uint96) {
        RateParams memory _rateParams = rateParams;

        uint256 interestRate;
        // slither-disable-start divide-before-multiply
        if (utilizationRatio > _rateParams.kinkUtilizationRatio) {
            // Slope between kink% -> max%
            uint256 slope = (
                (PRECISION * (_rateParams.maxInterestRate - _rateParams.kinkInterestRate))
                / (PRECISION - _rateParams.kinkUtilizationRatio)
            );
            interestRate = (
                (
                    (slope * (utilizationRatio - _rateParams.kinkUtilizationRatio)) 
                    / PRECISION
                ) + _rateParams.kinkInterestRate
            );
        } else {
            // Slope between base% -> kink%
            uint256 slope = (
                (PRECISION * (_rateParams.kinkInterestRate - _rateParams.baseInterestRate))
                 / _rateParams.kinkUtilizationRatio
            );
            interestRate = (
                (utilizationRatio * slope / PRECISION) 
                + _rateParams.baseInterestRate
            );
        }
        // slither-disable-end divide-before-multiply
        return uint96(interestRate);
    }
}