// File: contracts/HarvestV2KeeperJob.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.8.14;

import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import "./interfaces/IV2KeeperJob.sol";
import "./interfaces/IBaseStrategy.sol";

import "./dependencies/Governable.sol";
import "./dependencies/Keep3rBondedJob.sol";

contract HarvestV2KeeperJob is IV2KeeperJob, Keep3rBondedJob, Pausable {
    using EnumerableSet for EnumerableSet.AddressSet;

    EnumerableSet.AddressSet internal _availableStrategies;

    mapping(address => uint256) public requiredAmount;
    mapping(address => uint256) public lastWorkAt;

    uint256 public workCooldown;

    constructor(address _governor) Governable(_governor) {
        workCooldown = 5 days;

        onlyEOA = true;

        requiredBond = 0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44; // KP3R Token
        requiredMinBond = 50 ether;
        requiredEarnings = 0;
        requiredAge = 0;
    }

    // --- Public View Functions --- //

    /// @inheritdoc IV2KeeperJob
    function workable(address _strategy) external view returns (bool _isWorkable) {
        if (!_workable(_strategy)) return false;
        return IBaseStrategy(_strategy).harvestTrigger(_getCallCosts(_strategy));
    }

    /// @inheritdoc IV2KeeperJob
    function strategies() public view returns (address[] memory _strategies) {
        _strategies = new address[](_availableStrategies.length());
        for (uint256 _i; _i < _availableStrategies.length(); _i++) {
            _strategies[_i] = _availableStrategies.at(_i);
        }
    }

    // --- Methods --- //

    /// @inheritdoc IV2KeeperJob
    function work(address _strategy) external upkeep whenNotPaused {
        _workInternal(_strategy);
    }

    function forceWork(address _strategy) external onlyGovernor {
        _forceWork(_strategy);
    }

    // --- Setters --- //

    /// @inheritdoc IV2KeeperJob
    function setWorkCooldown(uint256 _workCooldown) external onlyGovernor {
        _setWorkCooldown(_workCooldown);
    }

    /// @inheritdoc IV2KeeperJob
    function addStrategy(address _strategy, uint256 _requiredAmount) external onlyGovernor {
        _addStrategy(_strategy, _requiredAmount);
    }

    /// @inheritdoc IV2KeeperJob
    function addStrategies(address[] calldata _strategies, uint256[] calldata _requiredAmounts)
        external
        onlyGovernor
    {
        if (_strategies.length != _requiredAmounts.length) revert WrongLengths();
        for (uint256 _i; _i < _strategies.length; _i++) {
            _addStrategy(_strategies[_i], _requiredAmounts[_i]);
        }
    }

    /// @inheritdoc IV2KeeperJob
    function updateRequiredAmount(address _strategy, uint256 _requiredAmount) external onlyGovernor {
        _updateRequiredAmount(_strategy, _requiredAmount);
    }

    /// @inheritdoc IV2KeeperJob
    function updateRequiredAmounts(address[] calldata _strategies, uint256[] calldata _requiredAmounts)
        external
        onlyGovernor
    {
        if (_strategies.length != _requiredAmounts.length) revert WrongLengths();
        for (uint256 _i; _i < _strategies.length; _i++) {
            _updateRequiredAmount(_strategies[_i], _requiredAmounts[_i]);
        }
    }

    /// @inheritdoc IV2KeeperJob
    function removeStrategy(address _strategy) external onlyGovernor {
        _removeStrategy(_strategy);
    }

    function pause() external onlyGovernor {
        _pause();
    }

    function unpause() external onlyGovernor {
        _unpause();
    }

    // --- Internal Functions --- //

    function _setWorkCooldown(uint256 _workCooldown) internal {
        if (_workCooldown == 0) revert ZeroCooldown();
        workCooldown = _workCooldown;
    }

    function _addStrategy(address _strategy, uint256 _requiredAmount) internal {
        if (_availableStrategies.contains(_strategy)) revert StrategyAlreadyAdded();
        _setRequiredAmount(_strategy, _requiredAmount);
        emit StrategyAdded(_strategy, _requiredAmount);
        _availableStrategies.add(_strategy);
    }

    function _updateRequiredAmount(address _strategy, uint256 _requiredAmount) internal {
        if (!_availableStrategies.contains(_strategy)) revert StrategyNotAdded();
        _setRequiredAmount(_strategy, _requiredAmount);
        emit StrategyModified(_strategy, _requiredAmount);
    }

    function _removeStrategy(address _strategy) internal {
        if (!_availableStrategies.contains(_strategy)) revert StrategyNotAdded();
        delete requiredAmount[_strategy];
        _availableStrategies.remove(_strategy);
        emit StrategyRemoved(_strategy);
    }

    function _setRequiredAmount(address _strategy, uint256 _requiredAmount) internal {
        requiredAmount[_strategy] = _requiredAmount;
    }

    function _workable(address _strategy) internal view returns (bool) {
        if (!_availableStrategies.contains(_strategy)) revert StrategyNotAdded();
        if (workCooldown == 0 || block.timestamp > lastWorkAt[_strategy] + workCooldown) return true;
        return false;
    }

    function _getCallCosts(address _strategy) internal view returns (uint256 _callCost) {
        uint256 _gasAmount = requiredAmount[_strategy];
        if (_gasAmount == 0) return 0;
        return _gasAmount * _gasPrice();
    }

    function _gasPrice() internal view returns (uint256) {
        return block.basefee;
    }

    function _workInternal(address _strategy) internal {
        if (!_workable(_strategy)) revert StrategyNotWorkable();
        lastWorkAt[_strategy] = block.timestamp;
        _work(_strategy);
        emit KeeperWorked(_strategy);
    }

    function _forceWork(address _strategy) internal {
        _work(_strategy);
        emit ForceWorked(_strategy);
    }

    function _work(address _strategy) internal {
        IBaseStrategy(_strategy).harvest();
    }
}

// File: @openzeppelin/contracts/security/Pausable.sol



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

pragma solidity ^0.8.0;

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

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        require(paused(), "Pausable: not paused");
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

// File: @openzeppelin/contracts/utils/structs/EnumerableSet.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 *  Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable.
 *  See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 *  In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

// File: contracts/interfaces/IV2KeeperJob.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.8.14;

interface IV2KeeperJob {
    // Errors

    /// @notice Throws if the strategy being added has already been added
    error StrategyAlreadyAdded();
    /// @notice Throws if the strategy being summoned is not added
    error StrategyNotAdded();
    /// @notice Throws if a keeper tries to work a non-workable strategy
    error StrategyNotWorkable();
    /// @notice Throws if the cooldown is being set to 0
    error ZeroCooldown();
    /// @notice Throws if a set of correlated input param arrays differ in lengths
    error WrongLengths();

    // Events

    /// @notice Emitted when a new strategy is added
    /// @param _strategy Address of the strategy being added
    /// @param _requiredAmount Estimated amount of gas required to trigger the strategy
    event StrategyAdded(address _strategy, uint256 _requiredAmount);

    /// @notice Emitted when a strategy is modified
    /// @param _strategy Address of the strategy being modified
    /// @param _requiredAmount New estimated amount of gas required to trigger the strategy
    event StrategyModified(address _strategy, uint256 _requiredAmount);

    /// @notice Emitted when a strategy is removed
    /// @param _strategy Address of the strategy being removed
    event StrategyRemoved(address _strategy);

    /// @notice Emitted when a strategy is worked
    /// @param _strategy Address of the strategy being worked
    event KeeperWorked(address _strategy);

    /// @notice Emitted when a strategy is force-worked by governor or mechanic
    /// @param _strategy Address of the strategy being force-worked
    event ForceWorked(address _strategy);

    // views

    /// @return _strategies List of added strategies
    function strategies() external view returns (address[] memory _strategies);

    /// @return _workCooldown Amount of seconds to wait until a strategy can be worked again
    function workCooldown() external view returns (uint256 _workCooldown);

    /// @param _strategy Address of the strategy to query
    /// @return _isWorkable Whether the queried strategy is workable or not
    function workable(address _strategy) external view returns (bool _isWorkable);

    /// @param _strategy Address of the strategy to query
    /// @return _lastWorkAt Timestamp of the last time the strategy was worked
    function lastWorkAt(address _strategy) external view returns (uint256 _lastWorkAt);

    /// @param _strategy Address of the strategy to query
    /// @return _requiredAmount Estimated amount of gas that the strategy requires to be executed
    function requiredAmount(address _strategy) external view returns (uint256 _requiredAmount);

    // Methods

    /// @param _workCooldown Amount of seconds to wait until a strategy can be worked again
    function setWorkCooldown(uint256 _workCooldown) external;

    /// @param _strategy Address of the strategy to add
    /// @param _requiredAmount Amount of gas that the strategy requires to execute
    function addStrategy(address _strategy, uint256 _requiredAmount) external;

    /// @param _strategies Array of addresses of strategies to add
    /// @param _requiredAmount Array of amount of gas that each strategy requires to execute
    function addStrategies(address[] calldata _strategies, uint256[] calldata _requiredAmount) external;

    /// @param _strategy Address of the strategy to modify
    /// @param _requiredAmount New amount of gas that te strategy requires to execute
    function updateRequiredAmount(address _strategy, uint256 _requiredAmount) external;

    /// @param _strategies Array of addresses of strategies to modify
    /// @param _requiredAmounts Array of new amounts of gas that each strategy requires to execute
    function updateRequiredAmounts(address[] calldata _strategies, uint256[] calldata _requiredAmounts)
        external;

    /// @param _strategy Address of the strategy to remove
    function removeStrategy(address _strategy) external;

    /// @notice Function to be called by the keeper that triggers the execution of the given strategy
    /// @param _strategy Address of the strategy to be worked
    function work(address _strategy) external;

    /// @notice Function to be called by governor or mechanics that triggers the execution of the given strategy
    /// @notice This function bypasses the workable checks
    /// @param _strategy Address of the strategy to be worked
    function forceWork(address _strategy) external;
}

// File: contracts/interfaces/IBaseStrategy.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.8.14;

interface IBaseStrategy {
    // Events

    event Harvested(uint256 _profit, uint256 _loss, uint256 _debtPayment, uint256 _debtOutstanding);

    // Views

    function vault() external view returns (address _vault);

    function strategist() external view returns (address _strategist);

    function rewards() external view returns (address _rewards);

    function keeper() external view returns (address _keeper);

    function want() external view returns (address _want);

    function name() external view returns (string memory _name);

    function harvestTrigger(uint256 _callCost) external view returns (bool);

    // Methods

    function harvest() external;
}

// File: contracts/dependencies/Governable.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.8.14;

import "../interfaces/IGovernable.sol";

abstract contract Governable is IGovernable {
    address public override governor;
    address public override pendingGovernor;

    constructor(address _governor) {
        if (_governor == address(0)) revert ZeroAddress();
        governor = _governor;
    }

    function setPendingGovernor(address _pendingGovernor) external override onlyGovernor {
        if (_pendingGovernor == address(0)) revert ZeroAddress();
        pendingGovernor = _pendingGovernor;
        emit PendingGovernorSet(governor, pendingGovernor);
    }

    function acceptPendingGovernor() external override onlyPendingGovernor {
        governor = pendingGovernor;
        pendingGovernor = address(0);
        emit PendingGovernorAccepted(governor);
    }

    modifier onlyGovernor() {
        if (msg.sender != governor) revert OnlyGovernor();
        _;
    }

    modifier onlyPendingGovernor() {
        if (msg.sender != pendingGovernor) revert OnlyPendingGovernor();
        _;
    }
}

// File: contracts/dependencies/Keep3rBondedJob.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.8.14;

import "./Keep3rJob.sol";
import "./OnlyEOA.sol";
import "../interfaces/IKeep3rBondedJob.sol";

abstract contract Keep3rBondedJob is IKeep3rBondedJob, Keep3rJob, OnlyEOA {
    /// @inheritdoc IKeep3rBondedJob
    address public requiredBond;
    /// @inheritdoc IKeep3rBondedJob
    uint256 public requiredMinBond;
    /// @inheritdoc IKeep3rBondedJob
    uint256 public requiredEarnings;
    /// @inheritdoc IKeep3rBondedJob
    uint256 public requiredAge;

    // Methods

    /// @inheritdoc IKeep3rBondedJob
    function setKeep3rRequirements(
        address _bond,
        uint256 _minBond,
        uint256 _earned,
        uint256 _age
    ) public onlyGovernor {
        _setKeep3rRequirements(_bond, _minBond, _earned, _age);
    }

    // Internals

    function _setKeep3rRequirements(
        address _bond,
        uint256 _minBond,
        uint256 _earned,
        uint256 _age
    ) internal {
        requiredBond = _bond;
        requiredMinBond = _minBond;
        requiredEarnings = _earned;
        requiredAge = _age;
        emit Keep3rRequirementsSet(_bond, _minBond, _earned, _age);
    }

    function _isValidKeeper(address _keeper) internal virtual override {
        if (onlyEOA) _validateEOA(_keeper);
        if (
            !IKeep3rV2(keep3r).isBondedKeeper(
                _keeper,
                requiredBond,
                requiredMinBond,
                requiredEarnings,
                requiredAge
            )
        ) revert KeeperNotValid();
    }
}

// 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/interfaces/IGovernable.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.8.14;

interface IGovernable {
    // Events

    event PendingGovernorSet(address _governor, address _pendingGovernor);
    event PendingGovernorAccepted(address _newGovernor);

    // Errors

    error OnlyGovernor();
    error OnlyPendingGovernor();
    error ZeroAddress();

    // Variables

    function governor() external view returns (address _governor);

    function pendingGovernor() external view returns (address _pendingGovernor);

    // Methods

    function setPendingGovernor(address _pendingGovernor) external;

    function acceptPendingGovernor() external;
}

// File: contracts/dependencies/Keep3rJob.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.8.14;

import "./Governable.sol";
import "../interfaces/IKeep3rJob.sol";
import "../interfaces/IKeep3rV2.sol";

abstract contract Keep3rJob is IKeep3rJob, Governable {
    address public override keep3r = 0xeb02addCfD8B773A5FFA6B9d1FE99c566f8c44CC;

    function setKeep3r(address _keep3r) public override onlyGovernor {
        keep3r = _keep3r;
        emit Keep3rSet(_keep3r);
    }

    function _isValidKeeper(address _keeper) internal virtual {
        if (!IKeep3rV2(keep3r).isKeeper(_keeper)) revert KeeperNotValid();
    }

    modifier upkeep() {
        _isValidKeeper(msg.sender);
        _;
        IKeep3rV2(keep3r).worked(msg.sender);
    }
}

// File: contracts/dependencies/OnlyEOA.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.8.14;

import "./Governable.sol";
import "../interfaces/IOnlyEOA.sol";

abstract contract OnlyEOA is IOnlyEOA, Governable {
    /// @inheritdoc IOnlyEOA
    bool public onlyEOA;

    // Methods

    /// @inheritdoc IOnlyEOA
    function setOnlyEOA(bool _onlyEOA) external onlyGovernor {
        _setOnlyEOA(_onlyEOA);
    }

    // Internals

    function _setOnlyEOA(bool _onlyEOA) internal {
        onlyEOA = _onlyEOA;
        emit OnlyEOASet(_onlyEOA);
    }

    function _validateEOA(address _caller) internal view {
        // solhint-disable-next-line avoid-tx-origin
        if (_caller != tx.origin) revert OnlyEOA();
    }
}

// File: contracts/interfaces/IKeep3rBondedJob.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.8.14;

import "./IKeep3rJob.sol";

interface IKeep3rBondedJob is IKeep3rJob {
    // Events

    /// @notice Emitted when a new set of requirements is set
    /// @param _bond Address of the token required to bond to work the job
    /// @param _minBond Amount of tokens required to bond to work the job
    /// @param _earned Amount of KP3R earnings required to work the job
    /// @param _age Amount of seconds since keeper registration required to work the job
    event Keep3rRequirementsSet(address _bond, uint256 _minBond, uint256 _earned, uint256 _age);

    // Views

    /// @return _requiredBond Address of the token required to bond to work the job
    function requiredBond() external view returns (address _requiredBond);

    /// @return _requiredMinBond Amount of tokens required to bond to work the job
    function requiredMinBond() external view returns (uint256 _requiredMinBond);

    /// @return _requiredEarnings Amount of KP3R earnings required to work the job
    function requiredEarnings() external view returns (uint256 _requiredEarnings);

    /// @return _requiredAge Amount of seconds since keeper registration required to work the job
    function requiredAge() external view returns (uint256 _requiredAge);

    // Methods

    /// @notice Allows the governor to set new requirements to work the job
    /// @param _bond Address of the token required to bond to work the job
    /// @param _minBond Amount of tokens required to bond to work the job
    /// @param _earned Amount of KP3R earnings required to work the job
    /// @param _age Amount of seconds since keeper registration required to work the job
    function setKeep3rRequirements(
        address _bond,
        uint256 _minBond,
        uint256 _earned,
        uint256 _age
    ) external;
}

// File: contracts/interfaces/IKeep3rJob.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.8.14;

import "./IGovernable.sol";

interface IKeep3rJob is IGovernable {
    // Events

    event Keep3rSet(address _keep3r);

    // Errors
    error KeeperNotValid();

    // Variables

    function keep3r() external view returns (address _keep3r);

    // Methods
    function setKeep3r(address _keep3r) external;
}

// File: contracts/interfaces/IKeep3rV2.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.8.14;

interface IKeep3rV2 {
    /// @notice Stores the tick information of the different liquidity pairs
    struct TickCache {
        int56 current; // Tracks the current tick
        int56 difference; // Stores the difference between the current tick and the last tick
        uint256 period; // Stores the period at which the last observation was made
    }

    // Events

    /// @notice Emitted when the Keep3rHelper address is changed
    /// @param _keep3rHelper The address of Keep3rHelper's contract
    event Keep3rHelperChange(address _keep3rHelper);

    /// @notice Emitted when the Keep3rV1 address is changed
    /// @param _keep3rV1 The address of Keep3rV1's contract
    event Keep3rV1Change(address _keep3rV1);

    /// @notice Emitted when the Keep3rV1Proxy address is changed
    /// @param _keep3rV1Proxy The address of Keep3rV1Proxy's contract
    event Keep3rV1ProxyChange(address _keep3rV1Proxy);

    /// @notice Emitted when the KP3R-WETH pool address is changed
    /// @param _kp3rWethPool The address of the KP3R-WETH pool
    event Kp3rWethPoolChange(address _kp3rWethPool);

    /// @notice Emitted when bondTime is changed
    /// @param _bondTime The new bondTime
    event BondTimeChange(uint256 _bondTime);

    /// @notice Emitted when _liquidityMinimum is changed
    /// @param _liquidityMinimum The new _liquidityMinimum
    event LiquidityMinimumChange(uint256 _liquidityMinimum);

    /// @notice Emitted when _unbondTime is changed
    /// @param _unbondTime The new _unbondTime
    event UnbondTimeChange(uint256 _unbondTime);

    /// @notice Emitted when _rewardPeriodTime is changed
    /// @param _rewardPeriodTime The new _rewardPeriodTime
    event RewardPeriodTimeChange(uint256 _rewardPeriodTime);

    /// @notice Emitted when the inflationPeriod is changed
    /// @param _inflationPeriod The new inflationPeriod
    event InflationPeriodChange(uint256 _inflationPeriod);

    /// @notice Emitted when the fee is changed
    /// @param _fee The new token credits fee
    event FeeChange(uint256 _fee);

    /// @notice Emitted when a slasher is added
    /// @param _slasher Address of the added slasher
    event SlasherAdded(address _slasher);

    /// @notice Emitted when a slasher is removed
    /// @param _slasher Address of the removed slasher
    event SlasherRemoved(address _slasher);

    /// @notice Emitted when a disputer is added
    /// @param _disputer Address of the added disputer
    event DisputerAdded(address _disputer);

    /// @notice Emitted when a disputer is removed
    /// @param _disputer Address of the removed disputer
    event DisputerRemoved(address _disputer);

    /// @notice Emitted when the bonding process of a new keeper begins
    /// @param _keeper The caller of Keep3rKeeperFundable#bond function
    /// @param _bonding The asset the keeper has bonded
    /// @param _amount The amount the keeper has bonded
    event Bonding(address indexed _keeper, address indexed _bonding, uint256 _amount);

    /// @notice Emitted when a keeper or job begins the unbonding process to withdraw the funds
    /// @param _keeperOrJob The keeper or job that began the unbonding process
    /// @param _unbonding The liquidity pair or asset being unbonded
    /// @param _amount The amount being unbonded
    event Unbonding(address indexed _keeperOrJob, address indexed _unbonding, uint256 _amount);

    /// @notice Emitted when Keep3rKeeperFundable#activate is called
    /// @param _keeper The keeper that has been activated
    /// @param _bond The asset the keeper has bonded
    /// @param _amount The amount of the asset the keeper has bonded
    event Activation(address indexed _keeper, address indexed _bond, uint256 _amount);

    /// @notice Emitted when Keep3rKeeperFundable#withdraw is called
    /// @param _keeper The caller of Keep3rKeeperFundable#withdraw function
    /// @param _bond The asset to withdraw from the bonding pool
    /// @param _amount The amount of funds withdrawn
    event Withdrawal(address indexed _keeper, address indexed _bond, uint256 _amount);

    /// @notice Emitted when Keep3rKeeperDisputable#slash is called
    /// @param _keeper The slashed keeper
    /// @param _slasher The user that called Keep3rKeeperDisputable#slash
    /// @param _amount The amount of credits slashed from the keeper
    event KeeperSlash(address indexed _keeper, address indexed _slasher, uint256 _amount);

    /// @notice Emitted when Keep3rKeeperDisputable#revoke is called
    /// @param _keeper The revoked keeper
    /// @param _slasher The user that called Keep3rKeeperDisputable#revoke
    event KeeperRevoke(address indexed _keeper, address indexed _slasher);

    /// @notice Emitted when Keep3rJobFundableCredits#addTokenCreditsToJob is called
    /// @param _job The address of the job being credited
    /// @param _token The address of the token being provided
    /// @param _provider The user that calls the function
    /// @param _amount The amount of credit being added to the job
    event TokenCreditAddition(
        address indexed _job,
        address indexed _token,
        address indexed _provider,
        uint256 _amount
    );

    /// @notice Emitted when Keep3rJobFundableCredits#withdrawTokenCreditsFromJob is called
    /// @param _job The address of the job from which the credits are withdrawn
    /// @param _token The credit being withdrawn from the job
    /// @param _receiver The user that receives the tokens
    /// @param _amount The amount of credit withdrawn
    event TokenCreditWithdrawal(
        address indexed _job,
        address indexed _token,
        address indexed _receiver,
        uint256 _amount
    );

    /// @notice Emitted when Keep3rJobFundableLiquidity#approveLiquidity function is called
    /// @param _liquidity The address of the liquidity pair being approved
    event LiquidityApproval(address _liquidity);

    /// @notice Emitted when Keep3rJobFundableLiquidity#revokeLiquidity function is called
    /// @param _liquidity The address of the liquidity pair being revoked
    event LiquidityRevocation(address _liquidity);

    /// @notice Emitted when IKeep3rJobFundableLiquidity#addLiquidityToJob function is called
    /// @param _job The address of the job to which liquidity will be added
    /// @param _liquidity The address of the liquidity being added
    /// @param _provider The user that calls the function
    /// @param _amount The amount of liquidity being added
    event LiquidityAddition(
        address indexed _job,
        address indexed _liquidity,
        address indexed _provider,
        uint256 _amount
    );

    /// @notice Emitted when IKeep3rJobFundableLiquidity#withdrawLiquidityFromJob function is called
    /// @param _job The address of the job of which liquidity will be withdrawn from
    /// @param _liquidity The address of the liquidity being withdrawn
    /// @param _receiver The receiver of the liquidity tokens
    /// @param _amount The amount of liquidity being withdrawn from the job
    event LiquidityWithdrawal(
        address indexed _job,
        address indexed _liquidity,
        address indexed _receiver,
        uint256 _amount
    );

    /// @notice Emitted when Keep3rJobFundableLiquidity#addLiquidityToJob function is called
    /// @param _job The address of the job whose credits will be updated
    /// @param _rewardedAt The time at which the job was last rewarded
    /// @param _currentCredits The current credits of the job
    /// @param _periodCredits The credits of the job for the current period
    event LiquidityCreditsReward(
        address indexed _job,
        uint256 _rewardedAt,
        uint256 _currentCredits,
        uint256 _periodCredits
    );

    /// @notice Emitted when Keep3rJobFundableLiquidity#forceLiquidityCreditsToJob function is called
    /// @param _job The address of the job whose credits will be updated
    /// @param _rewardedAt The time at which the job was last rewarded
    /// @param _currentCredits The current credits of the job
    event LiquidityCreditsForced(address indexed _job, uint256 _rewardedAt, uint256 _currentCredits);

    /// @notice Emitted when Keep3rJobManager#addJob is called
    /// @param _job The address of the job to add
    /// @param _jobOwner The job's owner
    event JobAddition(address indexed _job, address indexed _jobOwner);

    /// @notice Emitted when a keeper is validated before a job
    /// @param _gasLeft The amount of gas that the transaction has left at the moment of keeper validation
    event KeeperValidation(uint256 _gasLeft);

    /// @notice Emitted when a keeper works a job
    /// @param _credit The address of the asset in which the keeper is paid
    /// @param _job The address of the job the keeper has worked
    /// @param _keeper The address of the keeper that has worked the job
    /// @param _amount The amount that has been paid out to the keeper in exchange for working the job
    /// @param _gasLeft The amount of gas that the transaction has left at the moment of payment
    event KeeperWork(
        address indexed _credit,
        address indexed _job,
        address indexed _keeper,
        uint256 _amount,
        uint256 _gasLeft
    );

    /// @notice Emitted when Keep3rJobOwnership#changeJobOwnership is called
    /// @param _job The address of the job proposed to have a change of owner
    /// @param _owner The current owner of the job
    /// @param _pendingOwner The new address proposed to be the owner of the job
    event JobOwnershipChange(address indexed _job, address indexed _owner, address indexed _pendingOwner);

    /// @notice Emitted when Keep3rJobOwnership#JobOwnershipAssent is called
    /// @param _job The address of the job which the proposed owner will now own
    /// @param _previousOwner The previous owner of the job
    /// @param _newOwner The newOwner of the job
    event JobOwnershipAssent(address indexed _job, address indexed _previousOwner, address indexed _newOwner);

    /// @notice Emitted when Keep3rJobMigration#migrateJob function is called
    /// @param _fromJob The address of the job that requests to migrate
    /// @param _toJob The address at which the job requests to migrate
    event JobMigrationRequested(address indexed _fromJob, address _toJob);

    /// @notice Emitted when Keep3rJobMigration#acceptJobMigration function is called
    /// @param _fromJob The address of the job that requested to migrate
    /// @param _toJob The address at which the job had requested to migrate
    event JobMigrationSuccessful(address _fromJob, address indexed _toJob);

    /// @notice Emitted when Keep3rJobDisputable#slashTokenFromJob is called
    /// @param _job The address of the job from which the token will be slashed
    /// @param _token The address of the token being slashed
    /// @param _slasher The user that slashes the token
    /// @param _amount The amount of the token being slashed
    event JobSlashToken(address indexed _job, address _token, address indexed _slasher, uint256 _amount);

    /// @notice Emitted when Keep3rJobDisputable#slashLiquidityFromJob is called
    /// @param _job The address of the job from which the liquidity will be slashed
    /// @param _liquidity The address of the liquidity being slashed
    /// @param _slasher The user that slashes the liquidity
    /// @param _amount The amount of the liquidity being slashed
    event JobSlashLiquidity(
        address indexed _job,
        address _liquidity,
        address indexed _slasher,
        uint256 _amount
    );

    /// @notice Emitted when a keeper or a job is disputed
    /// @param _jobOrKeeper The address of the disputed keeper/job
    /// @param _disputer The user that called the function and disputed the keeper
    event Dispute(address indexed _jobOrKeeper, address indexed _disputer);

    /// @notice Emitted when a dispute is resolved
    /// @param _jobOrKeeper The address of the disputed keeper/job
    /// @param _resolver The user that called the function and resolved the dispute
    event Resolve(address indexed _jobOrKeeper, address indexed _resolver);

    // Errors

    /// @notice Throws if the reward period is less than the minimum reward period time
    error MinRewardPeriod();

    /// @notice Throws if either a job or a keeper is disputed
    error Disputed();

    /// @notice Throws if there are no bonded assets
    error BondsUnexistent();

    /// @notice Throws if the time required to bond an asset has not passed yet
    error BondsLocked();

    /// @notice Throws if there are no bonds to withdraw
    error UnbondsUnexistent();

    /// @notice Throws if the time required to withdraw the bonds has not passed yet
    error UnbondsLocked();

    /// @notice Throws if the address is already a registered slasher
    error SlasherExistent();

    /// @notice Throws if caller is not a registered slasher
    error SlasherUnexistent();

    /// @notice Throws if the address is already a registered disputer
    error DisputerExistent();

    /// @notice Throws if caller is not a registered disputer
    error DisputerUnexistent();

    /// @notice Throws if the msg.sender is not a slasher or is not a part of governance
    error OnlySlasher();

    /// @notice Throws if the msg.sender is not a disputer or is not a part of governance
    error OnlyDisputer();

    /// @notice Throws when an address is passed as a job, but that address is not a job
    error JobUnavailable();

    /// @notice Throws when an action that requires an undisputed job is applied on a disputed job
    error JobDisputed();

    /// @notice Throws when the address that is trying to register as a job is already a job
    error AlreadyAJob();

    /// @notice Throws when the token is KP3R, as it should not be used for direct token payments
    error TokenUnallowed();

    /// @notice Throws when the token withdraw cooldown has not yet passed
    error JobTokenCreditsLocked();

    /// @notice Throws when the user tries to withdraw more tokens than it has
    error InsufficientJobTokenCredits();

    /// @notice Throws when trying to add a job that has already been added
    error JobAlreadyAdded();

    /// @notice Throws when the address that is trying to register as a keeper is already a keeper
    error AlreadyAKeeper();

    /// @notice Throws when the liquidity being approved has already been approved
    error LiquidityPairApproved();

    /// @notice Throws when the liquidity being removed has not been approved
    error LiquidityPairUnexistent();

    /// @notice Throws when trying to add liquidity to an unapproved pool
    error LiquidityPairUnapproved();

    /// @notice Throws when the job doesn't have the requested liquidity
    error JobLiquidityUnexistent();

    /// @notice Throws when trying to remove more liquidity than the job has
    error JobLiquidityInsufficient();

    /// @notice Throws when trying to add less liquidity than the minimum liquidity required
    error JobLiquidityLessThanMin();

    /// @notice Throws if a variable is assigned to the zero address
    error ZeroAddress();

    /// @notice Throws if the address claiming to be a job is not in the list of approved jobs
    error JobUnapproved();

    /// @notice Throws if the amount of funds in the job is less than the payment that must be paid to the keeper that works that job
    error InsufficientFunds();

    /// @notice Throws when the caller of the function is not the job owner
    error OnlyJobOwner();

    /// @notice Throws when the caller of the function is not the pending job owner
    error OnlyPendingJobOwner();

    /// @notice Throws when the address of the job that requests to migrate wants to migrate to its same address
    error JobMigrationImpossible();

    /// @notice Throws when the _toJob address differs from the address being tracked in the pendingJobMigrations mapping
    error JobMigrationUnavailable();

    /// @notice Throws when cooldown between migrations has not yet passed
    error JobMigrationLocked();

    /// @notice Throws when the token trying to be slashed doesn't exist
    error JobTokenUnexistent();

    /// @notice Throws when someone tries to slash more tokens than the job has
    error JobTokenInsufficient();

    /// @notice Throws when a job or keeper is already disputed
    error AlreadyDisputed();

    /// @notice Throws when a job or keeper is not disputed and someone tries to resolve the dispute
    error NotDisputed();

    // Variables

    /// @notice Address of Keep3rHelper's contract
    /// @return _keep3rHelper The address of Keep3rHelper's contract
    function keep3rHelper() external view returns (address _keep3rHelper);

    /// @notice Address of Keep3rV1's contract
    /// @return _keep3rV1 The address of Keep3rV1's contract
    function keep3rV1() external view returns (address _keep3rV1);

    /// @notice Address of Keep3rV1Proxy's contract
    /// @return _keep3rV1Proxy The address of Keep3rV1Proxy's contract
    function keep3rV1Proxy() external view returns (address _keep3rV1Proxy);

    /// @notice Address of the KP3R-WETH pool
    /// @return _kp3rWethPool The address of KP3R-WETH pool
    function kp3rWethPool() external view returns (address _kp3rWethPool);

    /// @notice The amount of time required to pass after a keeper has bonded assets for it to be able to activate
    /// @return _days The required bondTime in days
    function bondTime() external view returns (uint256 _days);

    /// @notice The amount of time required to pass before a keeper can unbond what he has bonded
    /// @return _days The required unbondTime in days
    function unbondTime() external view returns (uint256 _days);

    /// @notice The minimum amount of liquidity required to fund a job per liquidity
    /// @return _amount The minimum amount of liquidity in KP3R
    function liquidityMinimum() external view returns (uint256 _amount);

    /// @notice The amount of time between each scheduled credits reward given to a job
    /// @return _days The reward period in days
    function rewardPeriodTime() external view returns (uint256 _days);

    /// @notice The inflation period is the denominator used to regulate the emission of KP3R
    /// @return _period The denominator used to regulate the emission of KP3R
    function inflationPeriod() external view returns (uint256 _period);

    /// @notice The fee to be sent to governance when a user adds liquidity to a job
    /// @return _amount The fee amount to be sent to governance when a user adds liquidity to a job
    function fee() external view returns (uint256 _amount);

    // solhint-disable func-name-mixedcase
    /// @notice The base that will be used to calculate the fee
    /// @return _base The base that will be used to calculate the fee
    function BASE() external view returns (uint256 _base);

    /// @notice The minimum rewardPeriodTime value to be set
    /// @return _minPeriod The minimum reward period in seconds
    function MIN_REWARD_PERIOD_TIME() external view returns (uint256 _minPeriod);

    /// @notice Maps an address to a boolean to determine whether the address is a slasher or not.
    /// @return _isSlasher Whether the address is a slasher or not
    function slashers(address _slasher) external view returns (bool _isSlasher);

    /// @notice Maps an address to a boolean to determine whether the address is a disputer or not.
    /// @return _isDisputer Whether the address is a disputer or not
    function disputers(address _disputer) external view returns (bool _isDisputer);

    /// @notice Tracks the total KP3R earnings of a keeper since it started working
    /// @return _workCompleted Total KP3R earnings of a keeper since it started working
    function workCompleted(address _keeper) external view returns (uint256 _workCompleted);

    /// @notice Tracks when a keeper was first registered
    /// @return timestamp The time at which the keeper was first registered
    function firstSeen(address _keeper) external view returns (uint256 timestamp);

    /// @notice Tracks if a keeper or job has a pending dispute
    /// @return _disputed Whether a keeper or job has a pending dispute
    function disputes(address _keeperOrJob) external view returns (bool _disputed);

    /// @notice Allows governance to create a dispute for a given keeper/job
    /// @param _jobOrKeeper The address in dispute
    function dispute(address _jobOrKeeper) external;

    /// @notice Allows governance to resolve a dispute on a keeper/job
    /// @param _jobOrKeeper The address cleared
    function resolve(address _jobOrKeeper) external;

    /// @notice Tracks how much a keeper has bonded of a certain token
    /// @return _bonds Amount of a certain token that a keeper has bonded
    function bonds(address _keeper, address _bond) external view returns (uint256 _bonds);

    /// @notice The current token credits available for a job
    /// @return _amount The amount of token credits available for a job
    function jobTokenCredits(address _job, address _token) external view returns (uint256 _amount);

    /// @notice Tracks the amount of assets deposited in pending bonds
    /// @return _pendingBonds Amount of a certain asset a keeper has unbonding
    function pendingBonds(address _keeper, address _bonding) external view returns (uint256 _pendingBonds);

    /// @notice Tracks when a bonding for a keeper can be activated
    /// @return _timestamp Time at which the bonding for a keeper can be activated
    function canActivateAfter(address _keeper, address _bonding) external view returns (uint256 _timestamp);

    /// @notice Tracks when keeper bonds are ready to be withdrawn
    /// @return _timestamp Time at which the keeper bonds are ready to be withdrawn
    function canWithdrawAfter(address _keeper, address _bonding) external view returns (uint256 _timestamp);

    /// @notice Tracks how much keeper bonds are to be withdrawn
    /// @return _pendingUnbonds The amount of keeper bonds that are to be withdrawn
    function pendingUnbonds(address _keeper, address _bonding)
        external
        view
        returns (uint256 _pendingUnbonds);

    /// @notice Checks whether the address has ever bonded an asset
    /// @return _hasBonded Whether the address has ever bonded an asset
    function hasBonded(address _keeper) external view returns (bool _hasBonded);

    /// @notice Last block where tokens were added to the job [job => token => timestamp]
    /// @return _timestamp The last block where tokens were added to the job
    function jobTokenCreditsAddedAt(address _job, address _token) external view returns (uint256 _timestamp);

    // Methods

    /// @notice Add credit to a job to be paid out for work
    /// @param _job The address of the job being credited
    /// @param _token The address of the token being credited
    /// @param _amount The amount of credit being added
    function addTokenCreditsToJob(
        address _job,
        address _token,
        uint256 _amount
    ) external;

    /// @notice Withdraw credit from a job
    /// @param _job The address of the job from which the credits are withdrawn
    /// @param _token The address of the token being withdrawn
    /// @param _amount The amount of token to be withdrawn
    /// @param _receiver The user that will receive tokens
    function withdrawTokenCreditsFromJob(
        address _job,
        address _token,
        uint256 _amount,
        address _receiver
    ) external;

    /// @notice Lists liquidity pairs
    /// @return _list An array of addresses with all the approved liquidity pairs
    function approvedLiquidities() external view returns (address[] memory _list);

    /// @notice Amount of liquidity in a specified job
    /// @param _job The address of the job being checked
    /// @param _liquidity The address of the liquidity we are checking
    /// @return _amount Amount of liquidity in the specified job
    function liquidityAmount(address _job, address _liquidity) external view returns (uint256 _amount);

    /// @notice Last time the job was rewarded liquidity credits
    /// @param _job The address of the job being checked
    /// @return _timestamp Timestamp of the last time the job was rewarded liquidity credits
    function rewardedAt(address _job) external view returns (uint256 _timestamp);

    /// @notice Last time the job was worked
    /// @param _job The address of the job being checked
    /// @return _timestamp Timestamp of the last time the job was worked
    function workedAt(address _job) external view returns (uint256 _timestamp);

    /// @notice Maps the job to the owner of the job (job => user)
    /// @return _owner The addres of the owner of the job
    function jobOwner(address _job) external view returns (address _owner);

    /// @notice Maps the owner of the job to its pending owner (job => user)
    /// @return _pendingOwner The address of the pending owner of the job
    function jobPendingOwner(address _job) external view returns (address _pendingOwner);

    /// @notice Maps the jobs that have requested a migration to the address they have requested to migrate to
    /// @return _toJob The address to which the job has requested to migrate to
    function pendingJobMigrations(address _fromJob) external view returns (address _toJob);

    // Methods

    /// @notice Sets the Keep3rHelper address
    /// @param _keep3rHelper The Keep3rHelper address
    function setKeep3rHelper(address _keep3rHelper) external;

    /// @notice Sets the Keep3rV1 address
    /// @param _keep3rV1 The Keep3rV1 address
    function setKeep3rV1(address _keep3rV1) external;

    /// @notice Sets the Keep3rV1Proxy address
    /// @param _keep3rV1Proxy The Keep3rV1Proxy address
    function setKeep3rV1Proxy(address _keep3rV1Proxy) external;

    /// @notice Sets the KP3R-WETH pool address
    /// @param _kp3rWethPool The KP3R-WETH pool address
    function setKp3rWethPool(address _kp3rWethPool) external;

    /// @notice Sets the bond time required to activate as a keeper
    /// @param _bond The new bond time
    function setBondTime(uint256 _bond) external;

    /// @notice Sets the unbond time required unbond what has been bonded
    /// @param _unbond The new unbond time
    function setUnbondTime(uint256 _unbond) external;

    /// @notice Sets the minimum amount of liquidity required to fund a job
    /// @param _liquidityMinimum The new minimum amount of liquidity
    function setLiquidityMinimum(uint256 _liquidityMinimum) external;

    /// @notice Sets the time required to pass between rewards for jobs
    /// @param _rewardPeriodTime The new amount of time required to pass between rewards
    function setRewardPeriodTime(uint256 _rewardPeriodTime) external;

    /// @notice Sets the new inflation period
    /// @param _inflationPeriod The new inflation period
    function setInflationPeriod(uint256 _inflationPeriod) external;

    /// @notice Sets the new fee
    /// @param _fee The new fee
    function setFee(uint256 _fee) external;

    /// @notice Registers a slasher by updating the slashers mapping
    function addSlasher(address _slasher) external;

    /// @notice Removes a slasher by updating the slashers mapping
    function removeSlasher(address _slasher) external;

    /// @notice Registers a disputer by updating the disputers mapping
    function addDisputer(address _disputer) external;

    /// @notice Removes a disputer by updating the disputers mapping
    function removeDisputer(address _disputer) external;

    /// @notice Lists all jobs
    /// @return _jobList Array with all the jobs in _jobs
    function jobs() external view returns (address[] memory _jobList);

    /// @notice Lists all keepers
    /// @return _keeperList Array with all the jobs in keepers
    function keepers() external view returns (address[] memory _keeperList);

    /// @notice Beginning of the bonding process
    /// @param _bonding The asset being bound
    /// @param _amount The amount of bonding asset being bound
    function bond(address _bonding, uint256 _amount) external;

    /// @notice Beginning of the unbonding process
    /// @param _bonding The asset being unbound
    /// @param _amount Allows for partial unbonding
    function unbond(address _bonding, uint256 _amount) external;

    /// @notice End of the bonding process after bonding time has passed
    /// @param _bonding The asset being activated as bond collateral
    function activate(address _bonding) external;

    /// @notice Withdraw funds after unbonding has finished
    /// @param _bonding The asset to withdraw from the bonding pool
    function withdraw(address _bonding) external;

    /// @notice Allows governance to slash a keeper based on a dispute
    /// @param _keeper The address being slashed
    /// @param _bonded The asset being slashed
    /// @param _amount The amount being slashed
    function slash(
        address _keeper,
        address _bonded,
        uint256 _amount
    ) external;

    /// @notice Blacklists a keeper from participating in the network
    /// @param _keeper The address being slashed
    function revoke(address _keeper) external;

    /// @notice Allows any caller to add a new job
    /// @param _job Address of the contract for which work should be performed
    function addJob(address _job) external;

    /// @notice Returns the liquidity credits of a given job
    /// @param _job The address of the job of which we want to know the liquidity credits
    /// @return _amount The liquidity credits of a given job
    function jobLiquidityCredits(address _job) external view returns (uint256 _amount);

    /// @notice Returns the credits of a given job for the current period
    /// @param _job The address of the job of which we want to know the period credits
    /// @return _amount The credits the given job has at the current period
    function jobPeriodCredits(address _job) external view returns (uint256 _amount);

    /// @notice Calculates the total credits of a given job
    /// @param _job The address of the job of which we want to know the total credits
    /// @return _amount The total credits of the given job
    function totalJobCredits(address _job) external view returns (uint256 _amount);

    /// @notice Calculates how many credits should be rewarded periodically for a given liquidity amount
    /// @dev _periodCredits = underlying KP3Rs for given liquidity amount * rewardPeriod / inflationPeriod
    /// @param _liquidity The liquidity to provide
    /// @param _amount The amount of liquidity to provide
    /// @return _periodCredits The amount of KP3R periodically minted for the given liquidity
    function quoteLiquidity(address _liquidity, uint256 _amount)
        external
        view
        returns (uint256 _periodCredits);

    /// @notice Observes the current state of the liquidity pair being observed and updates TickCache with the information
    /// @param _liquidity The liquidity pair being observed
    /// @return _tickCache The updated TickCache
    function observeLiquidity(address _liquidity) external view returns (TickCache memory _tickCache);

    /// @notice Gifts liquidity credits to the specified job
    /// @param _job The address of the job being credited
    /// @param _amount The amount of liquidity credits to gift
    function forceLiquidityCreditsToJob(address _job, uint256 _amount) external;

    /// @notice Approve a liquidity pair for being accepted in future
    /// @param _liquidity The address of the liquidity accepted
    function approveLiquidity(address _liquidity) external;

    /// @notice Revoke a liquidity pair from being accepted in future
    /// @param _liquidity The liquidity no longer accepted
    function revokeLiquidity(address _liquidity) external;

    /// @notice Allows anyone to fund a job with liquidity
    /// @param _job The address of the job to assign liquidity to
    /// @param _liquidity The liquidity being added
    /// @param _amount The amount of liquidity tokens to add
    function addLiquidityToJob(
        address _job,
        address _liquidity,
        uint256 _amount
    ) external;

    /// @notice Unbond liquidity for a job
    /// @dev Can only be called by the job's owner
    /// @param _job The address of the job being unbound from
    /// @param _liquidity The liquidity being unbound
    /// @param _amount The amount of liquidity being removed
    function unbondLiquidityFromJob(
        address _job,
        address _liquidity,
        uint256 _amount
    ) external;

    /// @notice Withdraw liquidity from a job
    /// @param _job The address of the job being withdrawn from
    /// @param _liquidity The liquidity being withdrawn
    /// @param _receiver The address that will receive the withdrawn liquidity
    function withdrawLiquidityFromJob(
        address _job,
        address _liquidity,
        address _receiver
    ) external;

    /// @notice Confirms if the current keeper is registered, can be used for general (non critical) functions
    /// @param _keeper The keeper being investigated
    /// @return _isKeeper Whether the address passed as a parameter is a keeper or not
    function isKeeper(address _keeper) external returns (bool _isKeeper);

    /// @notice Confirms if the current keeper is registered and has a minimum bond of any asset. Should be used for protected functions
    /// @param _keeper The keeper to check
    /// @param _bond The bond token being evaluated
    /// @param _minBond The minimum amount of bonded tokens
    /// @param _earned The minimum funds earned in the keepers lifetime
    /// @param _age The minimum keeper age required
    /// @return _isBondedKeeper Whether the `_keeper` meets the given requirements
    function isBondedKeeper(
        address _keeper,
        address _bond,
        uint256 _minBond,
        uint256 _earned,
        uint256 _age
    ) external returns (bool _isBondedKeeper);

    /// @notice Implemented by jobs to show that a keeper performed work
    /// @dev Automatically calculates the payment for the keeper
    /// @param _keeper Address of the keeper that performed the work
    function worked(address _keeper) external;

    /// @notice Implemented by jobs to show that a keeper performed work
    /// @dev Pays the keeper that performs the work with KP3R
    /// @param _keeper Address of the keeper that performed the work
    /// @param _payment The reward that should be allocated for the job
    function bondedPayment(address _keeper, uint256 _payment) external;

    /// @notice Implemented by jobs to show that a keeper performed work
    /// @dev Pays the keeper that performs the work with a specific token
    /// @param _token The asset being awarded to the keeper
    /// @param _keeper Address of the keeper that performed the work
    /// @param _amount The reward that should be allocated
    function directTokenPayment(
        address _token,
        address _keeper,
        uint256 _amount
    ) external;

    /// @notice Proposes a new address to be the owner of the job
    function changeJobOwnership(address _job, address _newOwner) external;

    /// @notice The proposed address accepts to be the owner of the job
    function acceptJobOwnership(address _job) external;

    /// @notice Initializes the migration process for a job by adding the request to the pendingJobMigrations mapping
    /// @param _fromJob The address of the job that is requesting to migrate
    /// @param _toJob The address at which the job is requesting to migrate
    function migrateJob(address _fromJob, address _toJob) external;

    /// @notice Completes the migration process for a job
    /// @dev Unbond/withdraw process doesn't get migrated
    /// @param _fromJob The address of the job that requested to migrate
    /// @param _toJob The address to which the job wants to migrate to
    function acceptJobMigration(address _fromJob, address _toJob) external;

    /// @notice Allows governance or slasher to slash a job specific token
    /// @param _job The address of the job from which the token will be slashed
    /// @param _token The address of the token that will be slashed
    /// @param _amount The amount of the token that will be slashed
    function slashTokenFromJob(
        address _job,
        address _token,
        uint256 _amount
    ) external;

    /// @notice Allows governance or a slasher to slash liquidity from a job
    /// @param _job The address being slashed
    /// @param _liquidity The address of the liquidity that will be slashed
    /// @param _amount The amount of liquidity that will be slashed
    function slashLiquidityFromJob(
        address _job,
        address _liquidity,
        uint256 _amount
    ) external;
}

// File: contracts/interfaces/IOnlyEOA.sol



// SPDX-License-Identifier: MIT

pragma solidity 0.8.14;

interface IOnlyEOA {
    // Events

    /// @notice Emitted when onlyEOA is set
    event OnlyEOASet(bool _onlyEOA);

    // Errors

    /// @notice Throws when keeper is not tx.origin
    error OnlyEOA();

    // Views

    /// @return _onlyEOA Whether the keeper is required to be an EOA or not
    function onlyEOA() external returns (bool _onlyEOA);

    // Methods

    /// @notice Allows governor to set the onlyEOA condition
    /// @param _onlyEOA Whether the keeper is required to be an EOA or not
    function setOnlyEOA(bool _onlyEOA) external;
}