// File: lib/openzeppelin-contracts/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: lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

// File: lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

// File: lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

// File: lib/openzeppelin-contracts/contracts/utils/Address.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

// File: lib/openzeppelin-contracts/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: lib/openzeppelin-contracts/contracts/utils/Counters.sol



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

pragma solidity ^0.8.0;

/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 */
library Counters {
    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        unchecked {
            counter._value += 1;
        }
    }

    function decrement(Counter storage counter) internal {
        uint256 value = counter._value;
        require(value > 0, "Counter: decrement overflow");
        unchecked {
            counter._value = value - 1;
        }
    }

    function reset(Counter storage counter) internal {
        counter._value = 0;
    }
}

// File: lib/protocol/packages/core/contracts/optimistic-oracle-v3/interfaces/OptimisticOracleV3Interface.sol



// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.16;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
 * @title Optimistic Oracle V3 Interface that callers must use to assert truths about the world.
 */
interface OptimisticOracleV3Interface {
    // Struct grouping together the settings related to the escalation manager stored in the assertion.
    struct EscalationManagerSettings {
        bool arbitrateViaEscalationManager; // False if the DVM is used as an oracle (EscalationManager on True).
        bool discardOracle; // False if Oracle result is used for resolving assertion after dispute.
        bool validateDisputers; // True if the EM isDisputeAllowed should be checked on disputes.
        address assertingCaller; // Stores msg.sender when assertion was made.
        address escalationManager; // Address of the escalation manager (zero address if not configured).
    }

    // Struct for storing properties and lifecycle of an assertion.
    struct Assertion {
        EscalationManagerSettings escalationManagerSettings; // Settings related to the escalation manager.
        address asserter; // Address of the asserter.
        uint64 assertionTime; // Time of the assertion.
        bool settled; // True if the request is settled.
        IERC20 currency; // ERC20 token used to pay rewards and fees.
        uint64 expirationTime; // Unix timestamp marking threshold when the assertion can no longer be disputed.
        bool settlementResolution; // Resolution of the assertion (false till resolved).
        bytes32 domainId; // Optional domain that can be used to relate the assertion to others in the escalationManager.
        bytes32 identifier; // UMA DVM identifier to use for price requests in the event of a dispute.
        uint256 bond; // Amount of currency that the asserter has bonded.
        address callbackRecipient; // Address that receives the callback.
        address disputer; // Address of the disputer.
    }

    // Struct for storing cached currency whitelist.
    struct WhitelistedCurrency {
        bool isWhitelisted; // True if the currency is whitelisted.
        uint256 finalFee; // Final fee of the currency.
    }

    /**
     * @notice Returns the default identifier used by the Optimistic Oracle V3.
     * @return The default identifier.
     */
    function defaultIdentifier() external view returns (bytes32);

    /**
     * @notice Fetches information about a specific assertion and returns it.
     * @param assertionId unique identifier for the assertion to fetch information for.
     * @return assertion information about the assertion.
     */
    function getAssertion(bytes32 assertionId) external view returns (Assertion memory);

    /**
     * @notice Asserts a truth about the world, using the default currency and liveness. No callback recipient or
     * escalation manager is enabled. The caller is expected to provide a bond of finalFee/burnedBondPercentage
     * (with burnedBondPercentage set to 50%, the bond is 2x final fee) of the default currency.
     * @dev The caller must approve this contract to spend at least the result of getMinimumBond(defaultCurrency).
     * @param claim the truth claim being asserted. This is an assertion about the world, and is verified by disputers.
     * @param asserter receives bonds back at settlement. This could be msg.sender or
     * any other account that the caller wants to receive the bond at settlement time.
     * @return assertionId unique identifier for this assertion.
     */
    function assertTruthWithDefaults(bytes memory claim, address asserter) external returns (bytes32);

    /**
     * @notice Asserts a truth about the world, using a fully custom configuration.
     * @dev The caller must approve this contract to spend at least bond amount of currency.
     * @param claim the truth claim being asserted. This is an assertion about the world, and is verified by disputers.
     * @param asserter receives bonds back at settlement. This could be msg.sender or
     * any other account that the caller wants to receive the bond at settlement time.
     * @param callbackRecipient if configured, this address will receive a function call assertionResolvedCallback and
     * assertionDisputedCallback at resolution or dispute respectively. Enables dynamic responses to these events. The
     * recipient _must_ implement these callbacks and not revert or the assertion resolution will be blocked.
     * @param escalationManager if configured, this address will control escalation properties of the assertion. This
     * means a) choosing to arbitrate via the UMA DVM, b) choosing to discard assertions on dispute, or choosing to
     * validate disputes. Combining these, the asserter can define their own security properties for the assertion.
     * escalationManager also _must_ implement the same callbacks as callbackRecipient.
     * @param liveness time to wait before the assertion can be resolved. Assertion can be disputed in this time.
     * @param currency bond currency pulled from the caller and held in escrow until the assertion is resolved.
     * @param bond amount of currency to pull from the caller and hold in escrow until the assertion is resolved. This
     * must be >= getMinimumBond(address(currency)).
     * @param identifier UMA DVM identifier to use for price requests in the event of a dispute. Must be pre-approved.
     * @param domainId optional domain that can be used to relate this assertion to others in the escalationManager and
     * can be used by the configured escalationManager to define custom behavior for groups of assertions. This is
     * typically used for "escalation games" by changing bonds or other assertion properties based on the other
     * assertions that have come before. If not needed this value should be 0 to save gas.
     * @return assertionId unique identifier for this assertion.
     */
    function assertTruth(
        bytes memory claim,
        address asserter,
        address callbackRecipient,
        address escalationManager,
        uint64 liveness,
        IERC20 currency,
        uint256 bond,
        bytes32 identifier,
        bytes32 domainId
    ) external returns (bytes32);

    /**
     * @notice Fetches information about a specific identifier & currency from the UMA contracts and stores a local copy
     * of the information within this contract. This is used to save gas when making assertions as we can avoid an
     * external call to the UMA contracts to fetch this.
     * @param identifier identifier to fetch information for and store locally.
     * @param currency currency to fetch information for and store locally.
     */
    function syncUmaParams(bytes32 identifier, address currency) external;

    /**
     * @notice Resolves an assertion. If the assertion has not been disputed, the assertion is resolved as true and the
     * asserter receives the bond. If the assertion has been disputed, the assertion is resolved depending on the oracle
     * result. Based on the result, the asserter or disputer receives the bond. If the assertion was disputed then an
     * amount of the bond is sent to the UMA Store as an oracle fee based on the burnedBondPercentage. The remainder of
     * the bond is returned to the asserter or disputer.
     * @param assertionId unique identifier for the assertion to resolve.
     */
    function settleAssertion(bytes32 assertionId) external;

    /**
     * @notice Settles an assertion and returns the resolution.
     * @param assertionId unique identifier for the assertion to resolve and return the resolution for.
     * @return resolution of the assertion.
     */
    function settleAndGetAssertionResult(bytes32 assertionId) external returns (bool);

    /**
     * @notice Fetches the resolution of a specific assertion and returns it. If the assertion has not been settled then
     * this will revert. If the assertion was disputed and configured to discard the oracle resolution return false.
     * @param assertionId unique identifier for the assertion to fetch the resolution for.
     * @return resolution of the assertion.
     */
    function getAssertionResult(bytes32 assertionId) external view returns (bool);

    /**
     * @notice Returns the minimum bond amount required to make an assertion. This is calculated as the final fee of the
     * currency divided by the burnedBondPercentage. If burn percentage is 50% then the min bond is 2x the final fee.
     * @param currency currency to calculate the minimum bond for.
     * @return minimum bond amount.
     */
    function getMinimumBond(address currency) external view returns (uint256);

    event AssertionMade(
        bytes32 indexed assertionId,
        bytes32 domainId,
        bytes claim,
        address indexed asserter,
        address callbackRecipient,
        address escalationManager,
        address caller,
        uint64 expirationTime,
        IERC20 currency,
        uint256 bond,
        bytes32 indexed identifier
    );

    event AssertionDisputed(bytes32 indexed assertionId, address indexed caller, address indexed disputer);

    event AssertionSettled(
        bytes32 indexed assertionId,
        address indexed bondRecipient,
        bool disputed,
        bool settlementResolution,
        address settleCaller
    );

    event AdminPropertiesSet(IERC20 defaultCurrency, uint64 defaultLiveness, uint256 burnedBondPercentage);
}

// File: src/stader/RatedOracleForStader.sol



// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.16;

import "@UMA/packages/core/contracts/optimistic-oracle-v3/interfaces/OptimisticOracleV3Interface.sol";

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

import "./utils/Utils.sol";

contract RatedOracle is Ownable {
    using SafeERC20 for IERC20;
    using Counters for Counters.Counter;

    /****************************************
     *              STRUCTS                 *
     ****************************************/

    struct Violation {
        bytes32 validatorIdentifier;
        uint32 epochNumber;
        uint32 penaltyType;
        address newFeeRecipientAddress;
    }

    struct Report {
        uint32 fromEpoch;
        uint32 toEpoch;
        uint256 timestamp;
        bytes32 assertionID;
        Violation[] listViolations;
    }

    /****************************************
     *              STORAGE                 *
     ****************************************/

    Counters.Counter public reportID; // Atomic counter for identifying reports.
    mapping(bytes32 => Violation[]) public violationsForValidator; // List of settled violations for a validator.
    mapping(uint256 => mapping(bytes32 => bool)) public validatorInReport; // Whether or not a report affects a validator.
    mapping(uint256 => Report) public reports; // Mapping of the reports against their ID.
    mapping(address => bool) public approvedProposer; // Mapping giving whether or not this address is approved for proposing reports.

    uint256[] public disputedReportsID; // List of the ID reports under dispute.
    uint256[] public pendingReportsID; // List of not yet finalized reports.

    OptimisticOracleV3Interface OO; // UMA Optimistic Oracle v3.
    bytes32 public priceIdentifier = "ROPU_ETHx"; // To be replaced with custom Identifier.
    uint256 public bondAmount; // Bond amount to commit on OO with assertion.
    uint64 public challengeWindow; // Number of seconds to wait before assertion is finalized.
    uint64 public timeToSettle = 345600; // Maximum amount of time required for a disputed report to settle.

    IERC20 public bondCurrency; // Currency used as bond.

    /****************************************
     *               EVENTS                 *
     ****************************************/

    /// @notice Emitted when a new bond is set.
    /// @param _newBondAmount The new amount to set.
    /// @param _newCurrency The new currency to set.
    event NewBondSet(uint256 _newBondAmount, IERC20 _newCurrency);

    /// @notice Emitted when a new proposer is approved.
    /// @param _proposerApproved The newly approved proposer.
    event ProposerApproved(address _proposerApproved);

    /// @notice Emitted when a proposer is revoked.
    /// @param _proposerRevoked The revoked proposer.
    event ProposerRevoked(address _proposerRevoked);

    /// @notice Emitted when a new challenge window is set.
    /// @param _newChallengeWindow The new challenge window.
    event NewChallengeWindowSet(uint64 _newChallengeWindow);

    /// @notice Emitted when a new time to settle is set.
    /// @param _timeToSettle The new time to settle.
    event NewTimeToSettleSet(uint64 _timeToSettle);

    /// @notice Emitted when a report is disputed.
    /// @param _reportID The disputed report.
    event reportDisputed(uint256 _reportID);

    /// @notice Emitted when a report is settled.
    /// @param _reportID The settled report.
    event reportSettled(uint256 _reportID);

    /// @notice Emitted when a new report is brought on chain.
    /// @param _reportID The new report.
    event reportMade(uint256 _reportID);

    /// @notice Emitted when a new report is discarded.
    /// @param _reportID The discarded report.
    event reportDiscarded(uint256 _reportID);

    /****************************************
     *               ERRORS                 *
     ****************************************/

    /// @notice Thrown if msg.sender isn't an approved proposer.
    error proposerNotApproved();

    /// @notice Thrown when zero is passed as a parameter.
    error amountCanNotBeZero();

    /// @notice Thrown when address zero is passed as a parameter.
    error canNotBeAddressZero();

    /// @notice Thrown when the challenge window passed is invalid.
    error invalidChallengeWindow();

    /****************************************
     *              MODIFIER                *
     ****************************************/

    // @dev Throws if called by a unapproved proposer.
    modifier onlyApprovedProposer() {
        if (approvedProposer[msg.sender] != true) revert proposerNotApproved();
        _;
    }

    /****************************************
     *             CONSTRUCTOR              *
     ****************************************/

    /// @notice Constructs the contract.
    constructor(
        uint256 _bondAmount,
        uint64 _challengeWindow,
        address _bondCurrency,
        address _proposer,
        address _oracle
    ) {
        changeBondAmountAndCurrency(_bondAmount, IERC20(_bondCurrency));
        setChallengeWindow(_challengeWindow);
        approveProposer(_proposer);
        OO = OptimisticOracleV3Interface(_oracle);
        transferOwnership(0x7e764ED499BcBd64Bc0Ab76222C239c666d50E4D);
    }

    /****************************************
     *         GOVERNANCE FUNCTIONS         *
     ****************************************/

    /// @notice Sets the currency and amount for the bond of the assertions.
    /// @param _newBondAmount The new amount to set.
    /// @param _newCurrency The new currency to set.
    function changeBondAmountAndCurrency(
        uint256 _newBondAmount,
        IERC20 _newCurrency
    ) public onlyOwner {
        if (_newBondAmount == 0) revert amountCanNotBeZero();
        if (address(_newCurrency) == address(0)) revert canNotBeAddressZero();

        bondAmount = _newBondAmount;
        bondCurrency = _newCurrency;

        emit NewBondSet(_newBondAmount, _newCurrency);
    }

    /// @notice Allow an address to propose.
    /// @param _address the address to authorize.
    function approveProposer(address _address) public onlyOwner {
        if (_address == address(0)) revert canNotBeAddressZero();
        approvedProposer[_address] = true;
        emit ProposerApproved(_address);
    }

    /// @notice Revoke an address priviledge to propose.
    /// @param _address the address to revoke.
    function revokeProposer(address _address) external onlyOwner {
        if (_address == address(0)) revert canNotBeAddressZero();
        approvedProposer[_address] = false;
        emit ProposerRevoked(_address);
    }

    /// @notice Allow owner to withdraw funds on the contract.
    /// @dev Make sure the contract always has enough funds to commit bonds to the OptimisticOracle.
    /// @param _token The address of the ERC20 to withdraw.
    /// @param _amount The amount to withdraw.
    /// @param _to Receiver of the funds.
    function withdrawFunds(
        address _token,
        uint256 _amount,
        address _to
    ) external onlyOwner {
        if (_to == address(0)) revert canNotBeAddressZero();
        IERC20(_token).safeTransfer(_to, _amount);
    }

    /// @notice Sets the size of the challenge window.
    /// @param _newChallengeWindow The new challenge window to set (in seconds).
    function setChallengeWindow(uint64 _newChallengeWindow) public onlyOwner {
        if (_newChallengeWindow > 604800 || _newChallengeWindow == 0)
            revert invalidChallengeWindow();

        challengeWindow = _newChallengeWindow;
        emit NewChallengeWindowSet(_newChallengeWindow);
    }

    /// @notice Sets the time to settle for a disputed report.
    /// @param _timeToSettle The new time to settle (in seconds).
    function setTimeToSettle(uint64 _timeToSettle) public onlyOwner {
        timeToSettle = _timeToSettle;
        emit NewTimeToSettleSet(_timeToSettle);
    }

    /// @notice Forces the removal of a report from disputedReportsID.
    /// @dev If Quorum is not reached in the dispute process, the report will not settle in the desired time frame.
    /// @param _index The index of the report to remove in the array.
    function removeDisputedReport(uint64 _index) external onlyOwner {
        emit reportDiscarded(disputedReportsID[_index]);
        Utils.removeFromArray(_index, disputedReportsID);
    }

    /****************************************
     *         INTERNAL FUNCTIONS           *
     ****************************************/

    /// @notice Settles the reports that can be settled and applies the changes to storage.
    /// @dev If a report is under dispute, will mark it as such and
    /// @dev settles it when vote is complete - 2 to 4 days later.
    function settleReports() internal {
        uint256 deletedElem = 0;
        uint256 numberOfDisputedReports = disputedReportsID.length;
        for (uint256 i = 0; i < numberOfDisputedReports; i++) {
            uint256 index = i - deletedElem;
            uint256 IDreportToSettle = disputedReportsID[index];
            if (
                block.timestamp >
                reports[IDreportToSettle].timestamp + timeToSettle
            ) {
                if (
                    OO.settleAndGetAssertionResult(
                        reports[IDreportToSettle].assertionID
                    )
                ) {
                    pushVerifiedReport(IDreportToSettle);
                    Utils.removeFromArray(index, disputedReportsID);
                    deletedElem++;
                    emit reportSettled(IDreportToSettle);
                } else {
                    Utils.removeFromArray(index, disputedReportsID);
                    deletedElem++;
                    emit reportDiscarded(IDreportToSettle);
                }
            }
        }

        deletedElem = 0;
        uint256 numberOfPendingReports = pendingReportsID.length;
        for (uint256 i = 0; i < numberOfPendingReports; i++) {
            uint256 index = i - deletedElem;
            uint256 IDreportToSettle = pendingReportsID[index];

            OptimisticOracleV3Interface.Assertion memory assertionDetails = OO
                .getAssertion(reports[IDreportToSettle].assertionID);

            if (
                assertionDetails.disputer == address(0) &&
                block.timestamp > assertionDetails.expirationTime
            ) {
                if (assertionDetails.settled == false) {
                    OO.settleAssertion(reports[IDreportToSettle].assertionID);
                }
                pushVerifiedReport(IDreportToSettle);
                Utils.removeFromArray(index, pendingReportsID);
                deletedElem++;
                emit reportSettled(IDreportToSettle);
            }

            if (assertionDetails.disputer != address(0)) {
                reports[IDreportToSettle].timestamp = block.timestamp;
                disputedReportsID.push(IDreportToSettle);
                Utils.removeFromArray(index, pendingReportsID);
                deletedElem++;
                emit reportDisputed(IDreportToSettle);
            }
        }
    }

    /// @notice Attributes the report's violation to the concerned validators.
    /// @param _reportID The report to push.
    function pushVerifiedReport(uint256 _reportID) internal {
        Violation[] memory listViolations = reports[_reportID].listViolations;

        uint256 numberOfViolations = listViolations.length;
        for (uint256 i = 0; i < numberOfViolations; i++) {
            violationsForValidator[listViolations[i].validatorIdentifier].push(
                listViolations[i]
            );
        }
    }

    /****************************************
     *         PROPOSER FUNCTIONS           *
     ****************************************/

    /// @notice Proposer creates a new report.
    /// @param _fromEpoch Starting epoch of the report on the Beacon Chain.
    /// @param _toEpoch Ending epoch of the report on the Beacon Chain.
    /// @param _listViolations List of the violations reported.
    function postReport(
        uint32 _fromEpoch,
        uint32 _toEpoch,
        Violation[] memory _listViolations
    ) public onlyApprovedProposer {
        reportID.increment();
        uint256 newReportID = reportID.current();

        settleReports();

        uint256 numberOfViolations = _listViolations.length;
        for (uint256 i = 0; i < numberOfViolations; i++) {
            validatorInReport[newReportID][
                _listViolations[i].validatorIdentifier
            ] = true;
            reports[newReportID].listViolations.push(_listViolations[i]);
        }

        reports[newReportID].fromEpoch = _fromEpoch;
        reports[newReportID].toEpoch = _toEpoch;

        bondCurrency.safeIncreaseAllowance(address(OO), bondAmount);

        reports[newReportID].assertionID = OO.assertTruth(
            Utils.toBytes(newReportID),
            address(this),
            address(0),
            address(0),
            challengeWindow,
            bondCurrency,
            bondAmount,
            priceIdentifier,
            ""
        );

        pendingReportsID.push(newReportID);
        emit reportMade(newReportID);
    }

    /****************************************
     *            STADER GETTERS            *
     ****************************************/

    /// @notice Get the all the finalized violations reported for a validator.
    /// @param _validatorIdentifier The Validator to get violations for.
    function getViolationsForValidator(
        bytes32 _validatorIdentifier
    ) external returns (Violation[] memory) {
        settleReports();
        return violationsForValidator[_validatorIdentifier];
    }

    /// @notice Get if a validator is concerned by a un-finalized report.
    /// @param _validatorIdentifier The Validator to check.
    function isValidatorInDispute(
        bytes32 _validatorIdentifier
    ) external returns (bool) {
        settleReports();

        uint256 numberOfPendingReports = pendingReportsID.length;
        for (uint256 i = 0; i < numberOfPendingReports; i++) {
            if (validatorInReport[pendingReportsID[i]][_validatorIdentifier]) {
                return true;
            }
        }

        uint256 numberOfDisputedReports = disputedReportsID.length;
        for (uint256 i = 0; i < numberOfDisputedReports; i++) {
            if (validatorInReport[disputedReportsID[i]][_validatorIdentifier]) {
                return true;
            }
        }

        return false;
    }

    /****************************************
     *            VIEW FUNCTIONS            *
     ****************************************/

    /// @notice Get the number of verified violations for a validator.
    /// @dev This is a view function that will not settle pending reports. It might not reflect latest reports.
    /// @dev It's usage on-chain is not recommended.
    /// @param _validatorIdentifier The Validator to check for.
    function numberOfViolationsForValidator(
        bytes32 _validatorIdentifier
    ) public view returns (uint256 len) {
        len = violationsForValidator[_validatorIdentifier].length;
    }

    /// @notice Get the violations contained in a report.
    /// @param _reportID The report to check for.
    function getViolationsInReport(
        uint256 _reportID
    ) public view returns (Violation[] memory containedViolation) {
        containedViolation = reports[_reportID].listViolations;
    }

    /**
     * @notice Computes the public key root.
     * @param _pubkey The validator public key for which to compute the root.
     * @return The root of the public key.
     */
    function getPubkeyRoot(
        bytes calldata _pubkey
    ) public pure returns (bytes32) {
        // Append 16 bytes of zero padding to the pubkey and compute its hash to get the pubkey root.
        return sha256(abi.encodePacked(_pubkey, bytes16(0)));
    }
}

// File: src/stader/utils/Utils.sol



// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.16;

library Utils {
    /// @notice Convert a unsigned integer to bytes.
    /// @param x the uint256 to convert.
    function toBytes(uint256 x) internal pure returns (bytes memory b) {
        b = new bytes(32);
        assembly {
            mstore(add(b, 32), x)
        }
    }

    /// @notice Remove an element from an array.
    /// @param index Index of the element to remove.
    /// @param array Array to remove from.
    function removeFromArray(uint256 index, uint256[] storage array) internal {
        array[index] = array[array.length - 1];
        array.pop();
    }
}