ETH Price: $2,981.43 (-6.61%)

Contract

0xe92ea17a074643326c8B5F11f579997eABfcD428

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Perform Upkeep220566432025-11-18 6:55:256 secs ago1763448925IN
0xe92ea17a...eABfcD428
0 ETH0.000000780.00015025
Perform Upkeep220566422025-11-18 6:55:238 secs ago1763448923IN
0xe92ea17a...eABfcD428
0 ETH0.000000180.00025025
Perform Upkeep220566412025-11-18 6:55:2110 secs ago1763448921IN
0xe92ea17a...eABfcD428
0 ETH0.000000170.00020025
Perform Upkeep220566392025-11-18 6:55:1714 secs ago1763448917IN
0xe92ea17a...eABfcD428
0 ETH0.000000180.00025025
Perform Upkeep220566382025-11-18 6:55:1516 secs ago1763448915IN
0xe92ea17a...eABfcD428
0 ETH0.000000550.00020025
Perform Upkeep220566352025-11-18 6:55:0922 secs ago1763448909IN
0xe92ea17a...eABfcD428
0 ETH0.000000170.00020025
Perform Upkeep220566312025-11-18 6:55:0130 secs ago1763448901IN
0xe92ea17a...eABfcD428
0 ETH0.00000110.00015025
Perform Upkeep220566302025-11-18 6:54:5932 secs ago1763448899IN
0xe92ea17a...eABfcD428
0 ETH0.000000630.00020025
Perform Upkeep220566272025-11-18 6:54:5338 secs ago1763448893IN
0xe92ea17a...eABfcD428
0 ETH0.000000410.00020025
Perform Upkeep220566252025-11-18 6:54:4942 secs ago1763448889IN
0xe92ea17a...eABfcD428
0 ETH0.000000160.00020025
Perform Upkeep220566202025-11-18 6:54:3952 secs ago1763448879IN
0xe92ea17a...eABfcD428
0 ETH0.000000590.00015025
Perform Upkeep220566192025-11-18 6:54:3754 secs ago1763448877IN
0xe92ea17a...eABfcD428
0 ETH0.000000360.00015025
Perform Upkeep220566192025-11-18 6:54:3754 secs ago1763448877IN
0xe92ea17a...eABfcD428
0 ETH0.000000150.00020025
Perform Upkeep220566172025-11-18 6:54:3358 secs ago1763448873IN
0xe92ea17a...eABfcD428
0 ETH0.000000150.00020025
Perform Upkeep220566132025-11-18 6:54:251 min ago1763448865IN
0xe92ea17a...eABfcD428
0 ETH0.000000280.00025025
Perform Upkeep220566122025-11-18 6:54:231 min ago1763448863IN
0xe92ea17a...eABfcD428
0 ETH0.000000470.00020025
Perform Upkeep220566072025-11-18 6:54:131 min ago1763448853IN
0xe92ea17a...eABfcD428
0 ETH0.000000550.00015025
Perform Upkeep220566062025-11-18 6:54:111 min ago1763448851IN
0xe92ea17a...eABfcD428
0 ETH0.000000150.00020025
Perform Upkeep220566012025-11-18 6:54:011 min ago1763448841IN
0xe92ea17a...eABfcD428
0 ETH0.00000060.00015025
Perform Upkeep220566012025-11-18 6:54:011 min ago1763448841IN
0xe92ea17a...eABfcD428
0 ETH0.000000440.00020025
Perform Upkeep220566012025-11-18 6:54:011 min ago1763448841IN
0xe92ea17a...eABfcD428
0 ETH0.000000150.00025025
Perform Upkeep220565992025-11-18 6:53:571 min ago1763448837IN
0xe92ea17a...eABfcD428
0 ETH0.000000240.00020025
Perform Upkeep220565952025-11-18 6:53:491 min ago1763448829IN
0xe92ea17a...eABfcD428
0 ETH0.000000510.00015025
Perform Upkeep220565952025-11-18 6:53:491 min ago1763448829IN
0xe92ea17a...eABfcD428
0 ETH0.000000140.00025025
Perform Upkeep220565942025-11-18 6:53:471 min ago1763448827IN
0xe92ea17a...eABfcD428
0 ETH0.000000130.00020025
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
AdrastiaWorldChainDataStreamsUpdater

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 2000 runs

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";

import {AdrastiaDataStreamsUpdater} from "./AdrastiaDataStreamsUpdater.sol";

interface IWorldChainFeed {
    function FEED_ID() external view returns (bytes32);

    function updatePriceData(
        bytes memory verifyReportRequest,
        bytes memory parameterPayload
    ) external returns (bytes memory);
}

/**
 * @title AdrastiaWorldChainDataStreamsUpdater
 * @author Tyler Loewen, TRILEZ SOFTWARE INC. dba. Adrastia
 * @notice This contract is an implementation of the AdrastiaDataStreamsUpdater for World Chain feeds.
 *
 * These feeds handle verification themselves, so this contract changes performUpkeep to call the feed contract
 * directly with the unverified report. The feed contract will then verify the report and update the price data.
 *
 * This contract assumes that there are no LINK token fees associated with the verification of the report. The
 * perforkUpkeep function does not implement access control because it's unable to spend LINK tokens. The
 * approveVerifierFeeSpend function is a no-op.
 */
contract AdrastiaWorldChainDataStreamsUpdater is AdrastiaDataStreamsUpdater {
    using EnumerableMap for EnumerableMap.Bytes32ToAddressMap;

    constructor(address initialAdmin) AdrastiaDataStreamsUpdater(address(0), initialAdmin, initialAdmin) {}

    function performUpkeep(bytes calldata performData) external payable virtual override {
        bytes[] memory unverifiedReports = abi.decode(performData, (bytes[]));

        bytes memory parameterPayload = abi.encode(address(0));

        uint256 successCount = 0;

        for (uint256 i = 0; i < unverifiedReports.length; ++i) {
            // Decode unverified report to extract report data
            (, bytes memory reportData) = abi.decode(unverifiedReports[i], (bytes32[3], bytes));

            // Extract extract the feedId from the reportData, which is always stored in the first 32 bytes of the
            // report data.
            // Extract the observationsTimestamp for the report. Used for short-circuiting the update if old.
            (bytes32 feedId, , uint32 feedObservationsTimestamp) = abi.decode(reportData, (bytes32, uint32, uint32));

            // Get the data stream address
            (bool feedExists_, address targetFeed) = _feedTargets.tryGet(feedId);
            if (!feedExists_) {
                // The updater should never try to update a feed that is not registered
                revert FeedNotRegistered(feedId);
            }

            // Get the contract latest report timestamp
            (, uint256 storedTimestamp) = readUnderlyingFeed(feedId);
            if (storedTimestamp >= feedObservationsTimestamp) {
                // The provided report is old, skip it
                emit FeedUpdateSkipped(feedId, targetFeed, storedTimestamp, feedObservationsTimestamp, block.timestamp);

                continue;
            }

            // Pass the unverified report to the feed contract for verification and update
            (bool success, bytes memory data) = targetFeed.call(
                abi.encodeWithSelector(IWorldChainFeed.updatePriceData.selector, unverifiedReports[i], parameterPayload)
            );
            if (success) {
                // Emit an event for the successful update
                emit FeedUpdatePerformed(feedId, targetFeed, block.timestamp);

                ++successCount;
            } else {
                // Log the error
                emit FeedUpdateFailed(feedId, targetFeed, data, block.timestamp);
            }
        }

        if (successCount == 0) {
            revert NoFeedsUpdated();
        }
    }

    function approveVerifierFeeSpend() public virtual override {
        // NO-OP
    }

    function _getIdFromFeed(address feed) internal view virtual override returns (bytes32) {
        return IWorldChainFeed(feed).FEED_ID();
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    mapping(bytes32 role => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        return _roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        if (!hasRole(role, account)) {
            _roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        if (hasRole(role, account)) {
            _roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/extensions/AccessControlEnumerable.sol)

pragma solidity ^0.8.20;

import {IAccessControlEnumerable} from "./IAccessControlEnumerable.sol";
import {AccessControl} from "../AccessControl.sol";
import {EnumerableSet} from "../../utils/structs/EnumerableSet.sol";

/**
 * @dev Extension of {AccessControl} that allows enumerating the members of each role.
 */
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
    using EnumerableSet for EnumerableSet.AddressSet;

    mapping(bytes32 role => EnumerableSet.AddressSet) private _roleMembers;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) public view virtual returns (address) {
        return _roleMembers[role].at(index);
    }

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) public view virtual returns (uint256) {
        return _roleMembers[role].length();
    }

    /**
     * @dev Return all accounts that have `role`
     *
     * 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 getRoleMembers(bytes32 role) public view virtual returns (address[] memory) {
        return _roleMembers[role].values();
    }

    /**
     * @dev Overload {AccessControl-_grantRole} to track enumerable memberships
     */
    function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {
        bool granted = super._grantRole(role, account);
        if (granted) {
            _roleMembers[role].add(account);
        }
        return granted;
    }

    /**
     * @dev Overload {AccessControl-_revokeRole} to track enumerable memberships
     */
    function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {
        bool revoked = super._revokeRole(role, account);
        if (revoked) {
            _roleMembers[role].remove(account);
        }
        return revoked;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/extensions/IAccessControlEnumerable.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "../IAccessControl.sol";

/**
 * @dev External interface of AccessControlEnumerable declared to support ERC-165 detection.
 */
interface IAccessControlEnumerable is IAccessControl {
    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) external view returns (address);

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC-165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
     * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}

File 7 of 23 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";

File 8 of 23 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";

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

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
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 value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value) external returns (bool);
}

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

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 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 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @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).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @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).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @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;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 14 of 23 : EnumerableMap.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/EnumerableMap.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableMap.js.

pragma solidity ^0.8.20;

import {EnumerableSet} from "./EnumerableSet.sol";

/**
 * @dev Library for managing an enumerable variant of Solidity's
 * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
 * type.
 *
 * Maps have the following properties:
 *
 * - Entries are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Entries are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableMap for EnumerableMap.UintToAddressMap;
 *
 *     // Declare a set state variable
 *     EnumerableMap.UintToAddressMap private myMap;
 * }
 * ```
 *
 * The following map types are supported:
 *
 * - `uint256 -> address` (`UintToAddressMap`) since v3.0.0
 * - `address -> uint256` (`AddressToUintMap`) since v4.6.0
 * - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0
 * - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0
 * - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0
 * - `uint256 -> bytes32` (`UintToBytes32Map`) since v5.1.0
 * - `address -> address` (`AddressToAddressMap`) since v5.1.0
 * - `address -> bytes32` (`AddressToBytes32Map`) since v5.1.0
 * - `bytes32 -> address` (`Bytes32ToAddressMap`) since v5.1.0
 *
 * [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 EnumerableMap, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableMap.
 * ====
 */
library EnumerableMap {
    using EnumerableSet for EnumerableSet.Bytes32Set;

    // To implement this library for multiple types with as little code repetition as possible, we write it in
    // terms of a generic Map type with bytes32 keys and values. The Map implementation uses private functions,
    // and user-facing implementations such as `UintToAddressMap` are just wrappers around the underlying Map.
    // This means that we can only create new EnumerableMaps for types that fit in bytes32.

    /**
     * @dev Query for a nonexistent map key.
     */
    error EnumerableMapNonexistentKey(bytes32 key);

    struct Bytes32ToBytes32Map {
        // Storage of keys
        EnumerableSet.Bytes32Set _keys;
        mapping(bytes32 key => bytes32) _values;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value) internal returns (bool) {
        map._values[key] = value;
        return map._keys.add(key);
    }

    /**
     * @dev Removes a key-value pair from a map. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) {
        delete map._values[key];
        return map._keys.remove(key);
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) {
        return map._keys.contains(key);
    }

    /**
     * @dev Returns the number of key-value pairs in the map. O(1).
     */
    function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) {
        return map._keys.length();
    }

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

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool exists, bytes32 value) {
        bytes32 val = map._values[key];
        if (val == bytes32(0)) {
            return (contains(map, key), bytes32(0));
        } else {
            return (true, val);
        }
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
        bytes32 value = map._values[key];
        if (value == 0 && !contains(map, key)) {
            revert EnumerableMapNonexistentKey(key);
        }
        return value;
    }

    /**
     * @dev Return the an array containing all the keys
     *
     * 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function keys(Bytes32ToBytes32Map storage map) internal view returns (bytes32[] memory) {
        return map._keys.values();
    }

    // UintToUintMap

    struct UintToUintMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(UintToUintMap storage map, uint256 key, uint256 value) internal returns (bool) {
        return set(map._inner, bytes32(key), bytes32(value));
    }

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

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

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(UintToUintMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the map. 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(UintToUintMap storage map, uint256 index) internal view returns (uint256 key, uint256 value) {
        (bytes32 atKey, bytes32 val) = at(map._inner, index);
        return (uint256(atKey), uint256(val));
    }

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool exists, uint256 value) {
        (bool success, bytes32 val) = tryGet(map._inner, bytes32(key));
        return (success, uint256(val));
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) {
        return uint256(get(map._inner, bytes32(key)));
    }

    /**
     * @dev Return the an array containing all the keys
     *
     * 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function keys(UintToUintMap storage map) internal view returns (uint256[] memory) {
        bytes32[] memory store = keys(map._inner);
        uint256[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // UintToAddressMap

    struct UintToAddressMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
        return set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
    }

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

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

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(UintToAddressMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the map. 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(UintToAddressMap storage map, uint256 index) internal view returns (uint256 key, address value) {
        (bytes32 atKey, bytes32 val) = at(map._inner, index);
        return (uint256(atKey), address(uint160(uint256(val))));
    }

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool exists, address value) {
        (bool success, bytes32 val) = tryGet(map._inner, bytes32(key));
        return (success, address(uint160(uint256(val))));
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
        return address(uint160(uint256(get(map._inner, bytes32(key)))));
    }

    /**
     * @dev Return the an array containing all the keys
     *
     * 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function keys(UintToAddressMap storage map) internal view returns (uint256[] memory) {
        bytes32[] memory store = keys(map._inner);
        uint256[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // UintToBytes32Map

    struct UintToBytes32Map {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(UintToBytes32Map storage map, uint256 key, bytes32 value) internal returns (bool) {
        return set(map._inner, bytes32(key), value);
    }

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

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

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(UintToBytes32Map storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the map. 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(UintToBytes32Map storage map, uint256 index) internal view returns (uint256 key, bytes32 value) {
        (bytes32 atKey, bytes32 val) = at(map._inner, index);
        return (uint256(atKey), val);
    }

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(UintToBytes32Map storage map, uint256 key) internal view returns (bool exists, bytes32 value) {
        (bool success, bytes32 val) = tryGet(map._inner, bytes32(key));
        return (success, val);
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(UintToBytes32Map storage map, uint256 key) internal view returns (bytes32) {
        return get(map._inner, bytes32(key));
    }

    /**
     * @dev Return the an array containing all the keys
     *
     * 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function keys(UintToBytes32Map storage map) internal view returns (uint256[] memory) {
        bytes32[] memory store = keys(map._inner);
        uint256[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // AddressToUintMap

    struct AddressToUintMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(AddressToUintMap storage map, address key, uint256 value) internal returns (bool) {
        return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
    }

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

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

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(AddressToUintMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the map. 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(AddressToUintMap storage map, uint256 index) internal view returns (address key, uint256 value) {
        (bytes32 atKey, bytes32 val) = at(map._inner, index);
        return (address(uint160(uint256(atKey))), uint256(val));
    }

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(AddressToUintMap storage map, address key) internal view returns (bool exists, uint256 value) {
        (bool success, bytes32 val) = tryGet(map._inner, bytes32(uint256(uint160(key))));
        return (success, uint256(val));
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(AddressToUintMap storage map, address key) internal view returns (uint256) {
        return uint256(get(map._inner, bytes32(uint256(uint160(key)))));
    }

    /**
     * @dev Return the an array containing all the keys
     *
     * 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function keys(AddressToUintMap storage map) internal view returns (address[] memory) {
        bytes32[] memory store = keys(map._inner);
        address[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // AddressToAddressMap

    struct AddressToAddressMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(AddressToAddressMap storage map, address key, address value) internal returns (bool) {
        return set(map._inner, bytes32(uint256(uint160(key))), bytes32(uint256(uint160(value))));
    }

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

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

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(AddressToAddressMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the map. 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(AddressToAddressMap storage map, uint256 index) internal view returns (address key, address value) {
        (bytes32 atKey, bytes32 val) = at(map._inner, index);
        return (address(uint160(uint256(atKey))), address(uint160(uint256(val))));
    }

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(AddressToAddressMap storage map, address key) internal view returns (bool exists, address value) {
        (bool success, bytes32 val) = tryGet(map._inner, bytes32(uint256(uint160(key))));
        return (success, address(uint160(uint256(val))));
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(AddressToAddressMap storage map, address key) internal view returns (address) {
        return address(uint160(uint256(get(map._inner, bytes32(uint256(uint160(key)))))));
    }

    /**
     * @dev Return the an array containing all the keys
     *
     * 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function keys(AddressToAddressMap storage map) internal view returns (address[] memory) {
        bytes32[] memory store = keys(map._inner);
        address[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // AddressToBytes32Map

    struct AddressToBytes32Map {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(AddressToBytes32Map storage map, address key, bytes32 value) internal returns (bool) {
        return set(map._inner, bytes32(uint256(uint160(key))), value);
    }

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

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

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(AddressToBytes32Map storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the map. 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(AddressToBytes32Map storage map, uint256 index) internal view returns (address key, bytes32 value) {
        (bytes32 atKey, bytes32 val) = at(map._inner, index);
        return (address(uint160(uint256(atKey))), val);
    }

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(AddressToBytes32Map storage map, address key) internal view returns (bool exists, bytes32 value) {
        (bool success, bytes32 val) = tryGet(map._inner, bytes32(uint256(uint160(key))));
        return (success, val);
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(AddressToBytes32Map storage map, address key) internal view returns (bytes32) {
        return get(map._inner, bytes32(uint256(uint160(key))));
    }

    /**
     * @dev Return the an array containing all the keys
     *
     * 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function keys(AddressToBytes32Map storage map) internal view returns (address[] memory) {
        bytes32[] memory store = keys(map._inner);
        address[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // Bytes32ToUintMap

    struct Bytes32ToUintMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(Bytes32ToUintMap storage map, bytes32 key, uint256 value) internal returns (bool) {
        return set(map._inner, key, bytes32(value));
    }

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

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

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(Bytes32ToUintMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the map. 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(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32 key, uint256 value) {
        (bytes32 atKey, bytes32 val) = at(map._inner, index);
        return (atKey, uint256(val));
    }

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool exists, uint256 value) {
        (bool success, bytes32 val) = tryGet(map._inner, key);
        return (success, uint256(val));
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) {
        return uint256(get(map._inner, key));
    }

    /**
     * @dev Return the an array containing all the keys
     *
     * 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function keys(Bytes32ToUintMap storage map) internal view returns (bytes32[] memory) {
        bytes32[] memory store = keys(map._inner);
        bytes32[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // Bytes32ToAddressMap

    struct Bytes32ToAddressMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(Bytes32ToAddressMap storage map, bytes32 key, address value) internal returns (bool) {
        return set(map._inner, key, bytes32(uint256(uint160(value))));
    }

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

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

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(Bytes32ToAddressMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the map. 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(Bytes32ToAddressMap storage map, uint256 index) internal view returns (bytes32 key, address value) {
        (bytes32 atKey, bytes32 val) = at(map._inner, index);
        return (atKey, address(uint160(uint256(val))));
    }

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (bool exists, address value) {
        (bool success, bytes32 val) = tryGet(map._inner, key);
        return (success, address(uint160(uint256(val))));
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (address) {
        return address(uint160(uint256(get(map._inner, key))));
    }

    /**
     * @dev Return the an array containing all the keys
     *
     * 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function keys(Bytes32ToAddressMap storage map) internal view returns (bytes32[] memory) {
        bytes32[] memory store = keys(map._inner);
        bytes32[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

/**
 * @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.
 *
 * ```solidity
 * 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 is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @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._positions[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 cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 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 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

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

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

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

            // Delete the tracked position for the deleted slot
            delete set._positions[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._positions[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) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // 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;

        assembly ("memory-safe") {
            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 in 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;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AutomationCompatibleInterface {
    /**
     * @notice method that is simulated by the keepers to see if any work actually
     * needs to be performed. This method does does not actually need to be
     * executable, and since it is only ever simulated it can consume lots of gas.
     * @dev To ensure that it is never called, you may want to add the
     * cannotExecute modifier from KeeperBase to your implementation of this
     * method.
     * @param checkData specified in the upkeep registration so it is always the
     * same for a registered upkeep. This can easily be broken down into specific
     * arguments using `abi.decode`, so multiple upkeeps can be registered on the
     * same contract and easily differentiated by the contract.
     * @return upkeepNeeded boolean to indicate whether the keeper should call
     * performUpkeep or not.
     * @return performData bytes that the keeper should call performUpkeep with, if
     * upkeep is needed. If you would like to encode data to decode later, try
     * `abi.encode`.
     */
    function checkUpkeep(bytes calldata checkData) external returns (bool upkeepNeeded, bytes memory performData);

    /**
     * @notice method that is actually executed by the keepers, via the registry.
     * The data returned by the checkUpkeep simulation will be passed into
     * this method to actually be executed.
     * @dev The input to this method should not be trusted, and the caller of the
     * method should not even be restricted to any single registry. Anyone should
     * be able call it, and the input should be validated, there is no guarantee
     * that the data passed in is the performData returned from checkUpkeep. This
     * could happen due to malicious keepers, racing keepers, or simply a state
     * change while the performUpkeep transaction is waiting for confirmation.
     * Always validate the data passed in.
     * @param performData is the data which was passed back from the checkData
     * simulation. If it is encoded, it can easily be decoded into other types by
     * calling `abi.decode`. This data should not be trusted, and should be
     * validated against the contract's current state.
     */
    function performUpkeep(bytes calldata performData) external payable;
}

File 17 of 23 : AdrastiaDataStreamsCommon.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AdrastiaDataStreamsCommon {
    error InvalidReportVersion(uint16 reportVersion);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {AccessControlEnumerable} from "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";

import {AutomationCompatibleInterface} from "../AutomationCompatibleInterface.sol";
import {AdrastiaDataStreamsCommon} from "./AdrastiaDataStreamsCommon.sol";
import {IDataStreamsFeed} from "./IDataStreamsFeed.sol";

import {IVerifierProxy} from "./vendor/IVerifierProxy.sol";
import {IFeeManager} from "./vendor/IFeeManager.sol";
import {Common} from "./vendor/Common.sol";
import {AggregatorV3Interface} from "./vendor/AggregatorV3Interface.sol";

/**
 * @title AdrastiaDataStreamsUpdater
 * @author Tyler Loewen, TRILEZ SOFTWARE INC. dba. Adrastia
 * @notice The Adrastia Data Streams Updater contract is responsible for updating registered Data Streams feeds with new
 * reports. It verifies the reports using the Chainlink verifier proxy and its verifyBulk function, then passes
 * the verified reports to the respective Data Streams feed for processing.
 *
 * Access is controlled using OpenZeppelin's AccessControlEnumerable, allowing for fine-grained permissions.
 * The roles are setup as follows:
 * - ADMIN: Can withdraw ERC20 and native tokens, and manage the role and sub-roles.
 *   - CONFIG_ADMIN: Can register, unregister, and change feeds.
 * - UPDATER_ADMIN: Can manage the ORACLE_UPDATER role.
 *   - ORACLE_UPDATER: Can call performUpkeep to update feeds.
 *
 * Granting the ORACLE_UPDATER role to `address(0)` allows anyone to call performUpkeep to update feeds.
 */
contract AdrastiaDataStreamsUpdater is
    AutomationCompatibleInterface,
    AdrastiaDataStreamsCommon,
    AccessControlEnumerable
{
    using EnumerableMap for EnumerableMap.Bytes32ToAddressMap;

    /**
     * @notice The action to take when registering or unregistering a feed.
     */
    enum FeedRegistrationAction {
        REGISTER,
        UNREGISTER
    }

    struct FeedIdAndAddress {
        bytes32 feedId;
        address feed;
    }

    bytes32 public constant ADMIN = keccak256("ADMIN_ROLE");

    bytes32 public constant CONFIG_ADMIN = keccak256("CONFIG_ADMIN_ROLE");

    bytes32 public constant UPDATER_ADMIN = keccak256("UPDATER_ADMIN_ROLE");

    bytes32 public constant ORACLE_UPDATER = keccak256("ORACLE_UPDATER_ROLE");

    /**
     * @notice The precision factor to use when calculating the change between two numbers.
     * @dev Fixed to 8 decimal places.
     */
    uint256 public constant CHANGE_PRECISION = 10 ** 8;

    /**
     * @notice The Chainlink verifier proxy contract.
     */
    IVerifierProxy public immutable verifierProxy;

    /**
     * @notice The mapping of feed IDs to their respective feed addresses.
     */
    EnumerableMap.Bytes32ToAddressMap internal _feedTargets;

    /**
     * @notice Emitted when a feed update is skipped due to the provided report timestamp being older than the stored
     * timestamp.
     *
     * @param feedId The feed ID.
     * @param feed The feed address.
     * @param storedTimestamp The stored timestamp of the feed.
     * @param providedTimestamp The provided timestamp of the report.
     * @param timestamp The timestamp of the event.
     */
    event FeedUpdateSkipped(
        bytes32 indexed feedId,
        address indexed feed,
        uint256 storedTimestamp,
        uint256 providedTimestamp,
        uint256 timestamp
    );

    /**
     * @notice Emitted when a feed update is performed successfully.
     *
     * @param feedId The feed ID.
     * @param feed The feed address.
     * @param timestamp The timestamp of the event.
     */
    event FeedUpdatePerformed(bytes32 indexed feedId, address indexed feed, uint256 timestamp);

    /**
     * @notice Emitted when a feed update fails.
     *
     * @param feedId The feed ID.
     * @param feed The feed address.
     * @param data The error data.
     * @param timestamp The timestamp of the event.
     */
    event FeedUpdateFailed(bytes32 indexed feedId, address indexed feed, bytes data, uint256 timestamp);

    /**
     * @notice Emitted when a feed is registered or unregistered.
     * @param feedId The feed ID.
     * @param action The action taken.
     * @param timestamp The timestamp of the event.
     */
    event FeedRegistrationChanged(bytes32 indexed feedId, FeedRegistrationAction indexed action, uint256 timestamp);

    /**
     * @notice Emitted when a feed target is changed.
     * @param feedId The feed ID.
     * @param oldFeed The old feed address.
     * @param newFeed The new feed address.
     * @param timestamp The timestamp of the event.
     */
    event FeedTargetChanged(
        bytes32 indexed feedId,
        address indexed oldFeed,
        address indexed newFeed,
        uint256 timestamp
    );

    /**
     * @notice Thrown when performing an operation using a feed ID that is not registered.
     * @param feedId The feed ID.
     */
    error FeedNotRegistered(bytes32 feedId);

    /**
     * @notice Thrown when a feed is already registered.
     * @param feedId The feed ID.
     */
    error FeedAlreadyRegistered(bytes32 feedId);

    /**
     * @notice Thrown when the provided feed ID does not match the expected feed ID.
     */
    error FeedMismatch(bytes32 expectedFeedId, bytes32 providedFeedId);

    /**
     * @notice Thrown when no feeds were updated during a performUpkeep call.
     */
    error NoFeedsUpdated();

    /**
     * @notice Thrown when the verified reports length does not match the unverified reports length.
     * @param unverifiedReportsLength The length of the unverified reports.
     * @param verifiedReportsLength The length of the verified reports.
     */
    error ReportLengthMismatch(uint256 unverifiedReportsLength, uint256 verifiedReportsLength);

    /**
     * @notice Modifier to make a function callable only by a certain role. In addition to checking the sender's role,
     * `address(0)` 's role is also considered. Granting a role to `address(0)` is equivalent to enabling this role for
     * everyone.
     * @param role The role to check.
     */
    modifier onlyRoleOrOpenRole(bytes32 role) {
        if (!hasRole(role, address(0))) {
            if (!hasRole(role, msg.sender)) revert AccessControlUnauthorizedAccount(msg.sender, role);
        }
        _;
    }

    /**
     * @notice Constructs the Adrastia Data Streams Updater contract.
     * @param _verifierProxy The Chainlink verifier proxy contract.
     * @param initialAdmin The initial admin address.
     * @param initialUpdaterAdmin The initial updater admin address.
     */
    constructor(address _verifierProxy, address initialAdmin, address initialUpdaterAdmin) {
        if (initialAdmin == address(0) || initialUpdaterAdmin == address(0)) {
            revert("Invalid initial admins");
        }

        verifierProxy = IVerifierProxy(_verifierProxy);

        _initializeRoles(initialAdmin, initialUpdaterAdmin);

        approveVerifierFeeSpend();
    }

    /**
     * @notice Registers a new feed with the contract.
     * @param feedId The feed ID.
     * @param feed The feed address.
     */
    function registerFeed(bytes32 feedId, address feed) external virtual onlyRole(CONFIG_ADMIN) {
        bytes32 contractFeedId = _getIdFromFeed(feed);
        if (feedId != contractFeedId) {
            revert FeedMismatch(contractFeedId, feedId);
        }

        (bool feedExists_, address oldFeed) = _feedTargets.tryGet(feedId);
        if (feedExists_) {
            revert FeedAlreadyRegistered(feedId);
        }

        _feedTargets.set(feedId, feed);

        emit FeedRegistrationChanged(feedId, FeedRegistrationAction.REGISTER, block.timestamp);

        emit FeedTargetChanged(feedId, oldFeed, feed, block.timestamp);
    }

    /**
     * @notice Changes the feed address for a registered feed.
     * @param feedId The feed ID.
     * @param feed The new feed address.
     */
    function changeFeed(bytes32 feedId, address feed) external virtual onlyRole(CONFIG_ADMIN) {
        bytes32 contractFeedId = _getIdFromFeed(feed);
        if (feedId != contractFeedId) {
            revert FeedMismatch(contractFeedId, feedId);
        }

        (bool feedExists_, address oldFeed) = _feedTargets.tryGet(feedId);
        if (!feedExists_) {
            revert FeedNotRegistered(feedId);
        }

        _feedTargets.set(feedId, feed);

        emit FeedTargetChanged(feedId, oldFeed, feed, block.timestamp);
    }

    /**
     * @notice Unregisters a feed from the contract.
     * @param feedId The feed ID.
     */
    function unregisterFeed(bytes32 feedId) external virtual onlyRole(CONFIG_ADMIN) {
        (bool feedExists_, address oldFeed) = _feedTargets.tryGet(feedId);
        if (!feedExists_) {
            revert FeedNotRegistered(feedId);
        }

        _feedTargets.remove(feedId);

        emit FeedTargetChanged(feedId, oldFeed, address(0), block.timestamp);

        emit FeedRegistrationChanged(feedId, FeedRegistrationAction.UNREGISTER, block.timestamp);
    }

    /**
     * @notice Gets the feed address for a given feed ID, if it exists.
     *
     * @param feedId The feed ID.
     *
     * @custom:throws FeedNotRegistered if the feed ID is not registered.
     *
     * @return The feed address.
     */
    function getFeed(bytes32 feedId) external view virtual returns (address) {
        (bool feedExists_, address targetFeed) = _feedTargets.tryGet(feedId);
        if (!feedExists_) {
            revert FeedNotRegistered(feedId);
        }

        return targetFeed;
    }

    /**
     * @notice Checks if a feed exists for a given feed ID.
     *
     * @param feedId The feed ID.
     *
     * @return True if the feed exists, false otherwise.
     */
    function feedExists(bytes32 feedId) external view virtual returns (bool) {
        return _feedTargets.contains(feedId);
    }

    /**
     * @notice Gets the number of registered feeds.
     *
     * @return The number of registered feeds.
     */
    function getFeedCount() external view virtual returns (uint256) {
        return _feedTargets.length();
    }

    /**
     * @notice Gets the feed IDs of all registered feeds.
     *
     * @return An array of feed IDs.
     */
    function getFeedIds() external view virtual returns (bytes32[] memory) {
        return _feedTargets.keys();
    }

    /**
     * @notice Gets all of the pairs of feed IDs and their respective feed addresses.
     *
     * @return An array of FeedIdAndAddress structs containing the feed ID and feed address.
     */
    function getFeedMapping() external view virtual returns (FeedIdAndAddress[] memory) {
        uint256 length = _feedTargets.length();
        FeedIdAndAddress[] memory feedMapping = new FeedIdAndAddress[](length);

        for (uint256 i = 0; i < length; ++i) {
            (bytes32 feedId, address feed) = _feedTargets.at(i);
            feedMapping[i] = FeedIdAndAddress({feedId: feedId, feed: feed});
        }

        return feedMapping;
    }

    /**
     * @notice Withdraws ERC20 tokens from the contract.
     * @param token The token address.
     * @param to The recipient address.
     * @param amount The amount to withdraw.
     */
    function withdrawErc20(address token, address to, uint256 amount) external virtual onlyRole(ADMIN) {
        SafeERC20.safeTransfer(IERC20(token), to, amount);
    }

    /**
     * @notice Withdraws native tokens from the contract.
     * @param to The recipient address.
     * @param amount The amount to withdraw.
     */
    function withdrawNative(address to, uint256 amount) external virtual onlyRole(ADMIN) {
        (bool success, ) = to.call{value: amount}("");
        require(success, "Transfer failed");
    }

    /**
     * @notice Approves the current verifier reward manager to spend an unlimited amount of LINK.
     * @dev Can only be called once, unless the fee manager returns a different LINK address. Impotent otherwise.
     */
    function approveVerifierFeeSpend() public virtual {
        // Retrieve fee manager and reward manager
        IFeeManager feeManager = IFeeManager(address(verifierProxy.s_feeManager()));
        if (address(feeManager) == address(0)) {
            // Fees are disabled. Nothing to approve.
            return;
        }

        address rewardManager = feeManager.i_rewardManager();
        if (rewardManager == address(0)) {
            // No reward manager. Nothing to approve.
            return;
        }

        IERC20 feeToken = IERC20(feeManager.i_linkAddress());
        if (address(feeToken) == address(0)) {
            // No fee token. Nothing to approve.
            return;
        }

        uint256 allowance = feeToken.allowance(address(this), rewardManager);
        if (allowance == 0) {
            feeToken.approve(rewardManager, type(uint256).max);
        }
    }

    /**
     * @notice Checks if an update is needed for a specific price feed.
     * @dev The feed being checked must be registered with the contract.
     * @param checkData An array of bytes containing:
     * - feedId: First 32 bytes - The feed ID.
     * - updateThreshold: Second 32 bytes - The percentage change threshold that must be met for an update to be needed.
     * - heartbeat: Third 32 bytes - The maximum time in seconds since the last update before an update is needed.
     * - offchainPrice: Fourth 32 bytes - The offchain price to compare the current onchain value against.
     * - offchainPublishTime: Fifth 32 bytes - The timestamp of the offchain price.
     * @return upkeepNeeded True if an update is needed, false otherwise.
     * @return performData Encoded onchain price, timestamp, whether the heartbeat was triggered, and whether the price
     * deviation was triggered. The onchain price and timestamp will be zero if the onchain report is expired or missing.
     */
    function checkUpkeep(
        bytes memory checkData
    ) public view virtual returns (bool upkeepNeeded, bytes memory performData) {
        if (checkData.length != 160) {
            require(checkData.length >= 96, "Missing feed data.");

            if (checkData.length < 128) {
                revert("Missing offchain price.");
            }

            if (checkData.length < 160) {
                revert("Missing offchain timestamp.");
            }

            revert("Too much data.");
        }

        (
            bytes32 feedId,
            uint256 updateThreshold,
            uint256 heartbeat,
            int256 offchainPrice,
            uint256 offchainPublishTime
        ) = abi.decode(checkData, (bytes32, uint256, uint256, int256, uint256));

        if (!_feedTargets.contains(feedId)) {
            revert FeedNotRegistered(feedId);
        }

        (int256 price, uint256 timestamp) = readUnderlyingFeed(feedId);

        bool heartbeatTriggered = false;
        bool priceDeviationTriggered = false;

        // Only check trigger conditions if the offchain price is not older than the onchain price
        if (offchainPublishTime >= timestamp) {
            uint256 timeSinceUpdate = offchainPublishTime - timestamp;
            if (timeSinceUpdate >= heartbeat) {
                heartbeatTriggered = true;
            }

            (uint256 change, bool isMaximalChange) = calculateChange(price, offchainPrice);

            if (isMaximalChange || change >= updateThreshold) {
                priceDeviationTriggered = true;
            }
        }

        upkeepNeeded = heartbeatTriggered || priceDeviationTriggered;
        performData = abi.encode(price, timestamp, heartbeatTriggered, priceDeviationTriggered);
    }

    /**
     * @notice Updates the latest reports for registered feeds.
     * @param performData An encoded array of bytes representing the unverified reports.
     */
    function performUpkeep(bytes calldata performData) external payable virtual onlyRoleOrOpenRole(ORACLE_UPDATER) {
        bytes[] memory unverifiedReports = abi.decode(performData, (bytes[]));

        // Retrieve fee manager and reward manager
        IFeeManager feeManager = IFeeManager(address(verifierProxy.s_feeManager()));

        // Set the fee token address (LINK in this case)
        address feeTokenAddress;

        if (address(feeManager) == address(0)) {
            // No fee manager... are fees disabled?
            feeTokenAddress = address(0);
        } else {
            feeTokenAddress = feeManager.i_linkAddress();
        }

        bytes[] memory verifiedReports = verifierProxy.verifyBulk(unverifiedReports, abi.encode(feeTokenAddress));
        if (verifiedReports.length != unverifiedReports.length) {
            revert ReportLengthMismatch(unverifiedReports.length, verifiedReports.length);
        }

        uint256 successCount = 0;

        for (uint256 i = 0; i < unverifiedReports.length; ++i) {
            // Decode unverified report to extract report data
            (, bytes memory reportData) = abi.decode(unverifiedReports[i], (bytes32[3], bytes));

            // Extract report version from reportData
            uint16 reportVersion = (uint16(uint8(reportData[0])) << 8) | uint16(uint8(reportData[1]));

            // Extract extract the feedId from the reportData, which is always stored in the first 32 bytes of the
            // report data.
            // Extract the observationsTimestamp for the report. Used for short-circuiting the update if old.
            (bytes32 feedId, , uint32 feedObservationsTimestamp) = abi.decode(reportData, (bytes32, uint32, uint32));

            // Get the data stream address
            (bool feedExists_, address targetFeed) = _feedTargets.tryGet(feedId);
            if (!feedExists_) {
                // The updater should never try to update a feed that is not registered
                revert FeedNotRegistered(feedId);
            }

            // Get the contract latest report timestamp
            (, uint256 storedTimestamp) = readUnderlyingFeed(feedId);
            if (storedTimestamp >= feedObservationsTimestamp) {
                // The provided report is old, skip it
                emit FeedUpdateSkipped(feedId, targetFeed, storedTimestamp, feedObservationsTimestamp, block.timestamp);

                continue;
            }

            // Attempt to write the report to the data stream
            (bool success, bytes memory data) = targetFeed.call(
                abi.encodeWithSelector(IDataStreamsFeed.updateReport.selector, reportVersion, verifiedReports[i])
            );
            if (success) {
                // Emit an event for the successful update
                emit FeedUpdatePerformed(feedId, targetFeed, block.timestamp);

                ++successCount;
            } else {
                // Log the error
                emit FeedUpdateFailed(feedId, targetFeed, data, block.timestamp);
            }
        }

        if (successCount == 0) {
            revert NoFeedsUpdated();
        }
    }

    /**
     * @notice Reads the latest onchain price and timestamp for a Data Streams feed.
     * @param feedId The feed ID.
     * @return price The latest onchain price.
     * @return timestamp The timestamp of the latest onchain price.
     */
    function readUnderlyingFeed(bytes32 feedId) internal view virtual returns (int256 price, uint256 timestamp) {
        // Get the data stream address
        (bool feedExists_, address targetFeed) = _feedTargets.tryGet(feedId);
        if (!feedExists_) {
            // The updater should never try to update a feed that is not registered
            revert FeedNotRegistered(feedId);
        }

        (bool success, bytes memory data) = targetFeed.staticcall(
            abi.encodeWithSelector(AggregatorV3Interface.latestRoundData.selector)
        );
        if (!success) {
            // The call fails if the report is expired or missing. Return zeros to signal this.
            return (0, 0);
        } else {
            (, int256 answer, , uint256 updatedAt, ) = abi.decode(data, (uint80, int256, uint256, uint256, uint80));

            return (answer, updatedAt);
        }
    }

    /**
     * @notice Calculates the change between two numbers, scaled by the CHANGE_PRECISION.
     * @param a A number.
     * @param b Another number.
     * @return change The normalized percentage change between a and b. See Adrastia Documentation (docs.adrastia.io)
     * for more information regarding normalization.
     * @return maximalChange If one value is zero and other is non-zero, returns true. If the change is so large that
     * overflow occurs, returns true. Otherwise, returns false. If one value is negative, and the other is non-negative,
     * returns true.
     */
    function calculateChange(int256 a, int256 b) internal pure virtual returns (uint256 change, bool maximalChange) {
        // If one is zero and the other is not, treat as maximal change
        if (a == 0 && b == 0) {
            return (0, false);
        } else if (a == 0 || b == 0) {
            return (0, true);
        }

        unchecked {
            // Check for sign flips
            if ((a > 0 && b < 0) || (a < 0 && b > 0)) {
                return (0, true); // sign flip = maximum deviation
            }

            int256 delta = a - b;

            // We use absolute change scaled by CHANGE_PRECISION, divided by absolute base
            uint256 uDelta = uint256(delta >= 0 ? delta : -delta);
            uint256 uBase = uint256(b >= 0 ? b : -b);
            uint256 preciseDelta = uDelta * CHANGE_PRECISION;

            if (preciseDelta / CHANGE_PRECISION != uDelta) {
                // multiplication overflow
                return (0, true);
            }

            change = preciseDelta / uBase;
            maximalChange = false;
        }
    }

    /**
     * @notice Gets the feed ID for a given feed address from the feed contract itself. Used to verify that the feed ID
     * matches the expected feed ID.
     *
     * @param feed The feed address.
     *
     * @return The feed ID.
     */
    function _getIdFromFeed(address feed) internal view virtual returns (bytes32) {
        return IDataStreamsFeed(feed).feedId();
    }

    function _initializeRoles(address initialAdmin, address initialUpdaterAdmin) internal virtual {
        // Admin self manages its own role
        _setRoleAdmin(ADMIN, ADMIN);

        // Admin manages the config admin role
        _setRoleAdmin(CONFIG_ADMIN, ADMIN);

        // Oracle updater admin self manages its own role
        _setRoleAdmin(UPDATER_ADMIN, UPDATER_ADMIN);

        // Oracle updater admin manages the oracle updater role
        _setRoleAdmin(ORACLE_UPDATER, UPDATER_ADMIN);

        // Grant the admin role to the initial admin
        _grantRole(ADMIN, initialAdmin);

        // Grant the updater admin role to the initial updater admin
        _grantRole(UPDATER_ADMIN, initialUpdaterAdmin);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IDataStreamsFeed {
    function feedId() external view returns (bytes32);

    function updateReport(uint16 reportVersion, bytes calldata verifiedReportData) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// solhint-disable-next-line interface-starts-with-i
interface AggregatorV3Interface {
    function decimals() external view returns (uint8);

    function description() external view returns (string memory);

    function version() external view returns (uint256);

    function getRoundData(
        uint80 _roundId
    )
        external
        view
        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);

    function latestRoundData()
        external
        view
        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}

File 21 of 23 : Common.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/*
 * @title Common
 * @author Michael Fletcher
 * @notice Common functions and structs
 */
library Common {
    // @notice The asset struct to hold the address of an asset and amount
    struct Asset {
        address assetAddress;
        uint256 amount;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Common} from "./Common.sol";

interface IFeeManager {
    /**
     * @notice Calculates the fee and reward associated with verifying a report, including discounts for subscribers.
     * This function assesses the fee and reward for report verification, applying a discount for recognized subscriber addresses.
     * @param subscriber The address attempting to verify the report. A discount is applied if this address
     * is recognized as a subscriber.
     * @param unverifiedReport The report data awaiting verification. The content of this report is used to
     * determine the base fee and reward, before considering subscriber discounts.
     * @param quoteAddress The payment token address used for quoting fees and rewards.
     * @return fee The fee assessed for verifying the report, with subscriber discounts applied where applicable.
     * @return reward The reward allocated to the caller for successfully verifying the report.
     * @return totalDiscount The total discount amount deducted from the fee for subscribers
     */
    function getFeeAndReward(
        address subscriber,
        bytes memory unverifiedReport,
        address quoteAddress
    ) external returns (Common.Asset memory, Common.Asset memory, uint256);

    function i_linkAddress() external view returns (address);

    function i_nativeAddress() external view returns (address);

    function i_rewardManager() external view returns (address);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IVerifierProxy {
    /**
     * @notice Verifies that the data encoded has been signed.
     * correctly by routing to the correct verifier, and bills the user if applicable.
     * @param payload The encoded data to be verified, including the signed
     * report.
     * @param parameterPayload Fee metadata for billing. For the current implementation this is just the abi-encoded fee token ERC-20 address.
     * @return verifierResponse The encoded report from the verifier.
     */
    function verify(
        bytes calldata payload,
        bytes calldata parameterPayload
    ) external payable returns (bytes memory verifierResponse);

    /**
     * @notice Verifies multiple reports in bulk, ensuring that each is signed correctly, routes them to the appropriate verifier, and handles billing for the verification process.
     * @param payloads An array of encoded data to be verified, where each entry includes the signed report.
     * @param parameterPayload Fee metadata for billing. In the current implementation, this consists of the abi-encoded address of the ERC-20 token used for fees.
     * @return verifiedReports An array of encoded reports returned from the verifier.
     */
    function verifyBulk(
        bytes[] calldata payloads,
        bytes calldata parameterPayload
    ) external payable returns (bytes[] memory verifiedReports);

    function s_feeManager() external view returns (address);
}

Settings
{
  "viaIR": true,
  "optimizer": {
    "enabled": true,
    "runs": 2000
  },
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"initialAdmin","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"bytes32","name":"feedId","type":"bytes32"}],"name":"FeedAlreadyRegistered","type":"error"},{"inputs":[{"internalType":"bytes32","name":"expectedFeedId","type":"bytes32"},{"internalType":"bytes32","name":"providedFeedId","type":"bytes32"}],"name":"FeedMismatch","type":"error"},{"inputs":[{"internalType":"bytes32","name":"feedId","type":"bytes32"}],"name":"FeedNotRegistered","type":"error"},{"inputs":[{"internalType":"uint16","name":"reportVersion","type":"uint16"}],"name":"InvalidReportVersion","type":"error"},{"inputs":[],"name":"NoFeedsUpdated","type":"error"},{"inputs":[{"internalType":"uint256","name":"unverifiedReportsLength","type":"uint256"},{"internalType":"uint256","name":"verifiedReportsLength","type":"uint256"}],"name":"ReportLengthMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"feedId","type":"bytes32"},{"indexed":true,"internalType":"enum AdrastiaDataStreamsUpdater.FeedRegistrationAction","name":"action","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"FeedRegistrationChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"feedId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"oldFeed","type":"address"},{"indexed":true,"internalType":"address","name":"newFeed","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"FeedTargetChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"feedId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"feed","type":"address"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"FeedUpdateFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"feedId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"feed","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"FeedUpdatePerformed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"feedId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"feed","type":"address"},{"indexed":false,"internalType":"uint256","name":"storedTimestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"providedTimestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"FeedUpdateSkipped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"inputs":[],"name":"ADMIN","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CHANGE_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CONFIG_ADMIN","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ORACLE_UPDATER","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPDATER_ADMIN","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"approveVerifierFeeSpend","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"feedId","type":"bytes32"},{"internalType":"address","name":"feed","type":"address"}],"name":"changeFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"checkData","type":"bytes"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"feedId","type":"bytes32"}],"name":"feedExists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"feedId","type":"bytes32"}],"name":"getFeed","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFeedCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFeedIds","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFeedMapping","outputs":[{"components":[{"internalType":"bytes32","name":"feedId","type":"bytes32"},{"internalType":"address","name":"feed","type":"address"}],"internalType":"struct AdrastiaDataStreamsUpdater.FeedIdAndAddress[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMembers","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"performData","type":"bytes"}],"name":"performUpkeep","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"feedId","type":"bytes32"},{"internalType":"address","name":"feed","type":"address"}],"name":"registerFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"feedId","type":"bytes32"}],"name":"unregisterFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"verifierProxy","outputs":[{"internalType":"contract IVerifierProxy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawErc20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawNative","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a03461030757601f6121ad38819003918201601f19168301916001600160401b0383118484101761030c5780849260209460405283398101031261030757516001600160a01b038116908181036103075781158015610302575b6102bd576000608081905260008051602061218d83398151915280825260208290527f7d7ffb7a348e1c6a02869081a26547b49160dd3df72d1d75a570eb9b698292ed8054908290556102119392829060008051602061210d8339815191529080a47fb92d52e77ebaa0cae5c23e882d85609efbcb44029214147dd132daf9ef1018af600081815260208190527f88b4af5b933fdc1fcd68c2f9c47ec4f55dd961e785ecbbca61f1df8bed3ceffe805460008051602061218d833981519152918290559092909160008051602061210d8339815191529080a460008051602061212d833981519152600081815260208190527f93f956adba6c072fd936983f727a6152a8261208a80865e45d1d2b53f6de5fcb80549083905590829060008051602061210d8339815191529080a47f9792fdc19ab98adfa72ab2fa98d342618c661e01c406979c105b31eda87f5e6f600081815260208190527fedb88efb1d399b0da6f79caf435895256e371666ffd10d48d8fcc014b50fc926805460008051602061212d833981519152918290559092909160008051602061210d8339815191529080a461020881610322565b610276576103ae565b61022e575b604051611c3e90816104af8239608051816108990152f35b60008051602061212d833981519152600052600160205261026f907f2f5548e6c1fbe94de15cb3f3d7b569a8a69d06f58016b7928f56cf14dd930f3d610434565b5038610216565b60008051602061218d83398151915260005260016020526102b7837f50efbde2d46c37e9785f1791697f77e94bb7b701e19f1930a668820722d37694610434565b506103ae565b60405162461bcd60e51b815260206004820152601660248201527f496e76616c696420696e697469616c2061646d696e73000000000000000000006044820152606490fd5b61005a565b600080fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116600090815260008051602061214d833981519152602052604090205460ff166103a8576001600160a01b0316600081815260008051602061214d83398151915260205260408120805460ff1916600117905533919060008051602061218d833981519152906000805160206120ed8339815191529080a4600190565b50600090565b6001600160a01b038116600090815260008051602061216d833981519152602052604090205460ff166103a8576001600160a01b0316600081815260008051602061216d83398151915260205260408120805460ff1916600117905533919060008051602061212d833981519152906000805160206120ed8339815191529080a4600190565b60018101908260005281602052604060002054156000146104a65780546801000000000000000081101561030c576001810180835581101561049057839082600052602060002001555491600052602052604060002055600190565b634e487b7160e01b600052603260045260246000fd5b50505060009056fe608080604052600436101561001357600080fd5b60003560e01c90816301ffc9a7146112a75750806307b18bde146112275780630c518dce14611189578063105794681461103f5780631593dee114610f5857806321209cfa14610f1d578063248a9ca314610ee8578063280aebcf14610eb15780632a0acc6a14610e765780632a58990814610dcb5780632cc9dfb314610dad5780632f2ff15d14610d4557806336568abe14610ce65780634585e33b146108bd57806347f77bc0146108795780634a84d0211461078e578063581b3ee01461076f5780635affdd4c146107355780636e04ff0d1461049657806376ce5858146104605780639010d07c1461041a57806391d14854146103cc578063a217fddf146103b0578063a3246ad3146102fc578063abf05a1a146102c1578063b12a2d8b146102ae578063bd33b7c8146101d6578063ca15c873146101aa5763d547741f1461015e57600080fd5b346101a55760406003193601126101a5576101a360043561017d61138e565b9061019e61019982600052600060205260016040600020015490565b6115f5565b61163c565b005b600080fd5b346101a55760206003193601126101a55760043560005260016020526020604060002054604051908152f35b346101a55760406003193601126101a5576004356101f261138e565b6101fa611583565b61020381611735565b80830361027c575061021482611882565b919015610267576001600160a01b03169161022f83826119e2565b507f04edbd68b1a49774d0341412ee6173ec25051302eed0d72a0e45571496ccdf7660206001600160a01b03604051944286521693a4005b82631e59ac5160e21b60005260045260246000fd5b90507f352b24610000000000000000000000000000000000000000000000000000000060005260045260245260446000fd5b346101a55760006003193601126101a557005b346101a55760006003193601126101a55760206040517f9792fdc19ab98adfa72ab2fa98d342618c661e01c406979c105b31eda87f5e6f8152f35b346101a55760206003193601126101a5576004356000526001602052604060002060405181548082526020820190819360005260206000209060005b81811061039a575050508161034e9103826113c0565b6040519182916020830190602084525180915260408301919060005b818110610378575050500390f35b82516001600160a01b031684528594506020938401939092019160010161036a565b8254845260209093019260019283019201610338565b346101a55760006003193601126101a557602060405160008152f35b346101a55760406003193601126101a5576103e561138e565b60043560005260006020526001600160a01b0360406000209116600052602052602060ff604060002054166040519015158152f35b346101a55760406003193601126101a557600435600052600160205260206001600160a01b0361045060243560406000206119ff565b90549060031b1c16604051908152f35b346101a55760206003193601126101a557602061048c6004356000526003602052604060002054151590565b6040519015158152f35b346101a55760206003193601126101a55760043567ffffffffffffffff81116101a5576104c79036906004016113ff565b805160a0810361060c575060a0818051810103126101a557602081015160408201519060608301519160a0608085015194015191610512816000526003602052604060002054151590565b156105f8576105209061168d565b919094600094859484811015610593575b505050508215908161058a575b6040519460208601526040850152156060840152151560808301526080825261056860a0836113c0565b61058660405192839215158352604060208401526040830190611469565b0390f35b9250819261053e565b8481039081116105e45710156105db575b6105ae90866117b0565b919082156105d0575b50506105c7575b84808080610531565b600191506105be565b1015905085806105b7565b600194506105a4565b602487634e487b7160e01b81526011600452fd5b631e59ac5160e21b60005260045260246000fd5b6060116106f15760808151106106ad5760a090511061066957606460405162461bcd60e51b815260206004820152600e60248201527f546f6f206d75636820646174612e0000000000000000000000000000000000006044820152fd5b606460405162461bcd60e51b815260206004820152601b60248201527f4d697373696e67206f6666636861696e2074696d657374616d702e00000000006044820152fd5b606460405162461bcd60e51b815260206004820152601760248201527f4d697373696e67206f6666636861696e2070726963652e0000000000000000006044820152fd5b606460405162461bcd60e51b815260206004820152601260248201527f4d697373696e67206665656420646174612e00000000000000000000000000006044820152fd5b346101a55760006003193601126101a55760206040517ef7280a0db925c0d1e88a56cb8ae89369595b41df40ca283519b9b197f5fed08152f35b346101a55760006003193601126101a55760206040516305f5e1008152f35b346101a55760406003193601126101a5576004356107aa61138e565b6107b2611583565b6107bb81611735565b80830361027c57506107cc82611882565b919061084b576001600160a01b0316916107e683826119e2565b506000817f1461e90144abccd369b0962a63908835be724cf6c0e6275e1756bdedf47b51576020604051428152a37f04edbd68b1a49774d0341412ee6173ec25051302eed0d72a0e45571496ccdf7660206001600160a01b03604051944286521693a4005b827fd09bc9e10000000000000000000000000000000000000000000000000000000060005260045260246000fd5b346101a55760006003193601126101a55760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b60206003193601126101a55760043567ffffffffffffffff81116101a557366023820112156101a557806004013567ffffffffffffffff81116101a55781019060248201913683116101a557602090829003126101a55760248101359067ffffffffffffffff82116101a5570190806043830112156101a5576024820135610944816114be565b9261095260405194856113c0565b818452602060248186019360051b83010101918383116101a55760448201905b838210610cb557856040519060006020830152602082526109946040836113c0565b600091825b8251841015610c84576109ac84846114d6565b518051810190608081602084019303126101a55781603f820112156101a5576040516060810181811067ffffffffffffffff821117610c6e5760405260808201908382116101a55760208301905b828210610c5e575050519067ffffffffffffffff82116101a5570181603f820112156101a557602081015190610a2f826113e3565b91610a3d60405193846113c0565b8083526020830193604083830101116101a557836040610a5d9301611446565b6060818051810103126101a557610a8d60606001600160a01b03935192610a8660408201611500565b5001611500565b610a9682611882565b939093169215610c495763ffffffff610aae8361168d565b9290501680821015610c07575050600080610ac988886114d6565b51604051610b4e81610b40610b1060208301957ff3208a50000000000000000000000000000000000000000000000000000000008752604060248501526064840190611469565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc8382030160448401528b611469565b03601f1981018352826113c0565b519082865af1610b5c61148e565b9015610bba57507f9e0b7698b9f11443d38cb8a817dfdc478d23f7beff128684d788ca246a06d99c6020604051428152a36000198114610ba4576001809101935b0192610999565b634e487b7160e01b600052601160045260246000fd5b907f128604bb7ac214246287115a95d0b2e90d8447034bb6634b47ee34fc969dd4ba610bf9600195989493604051918291604083526040830190611469565b4260208301520390a3610b9d565b6060600195989493927fecc3a38fd26d51bc47185041b9eb9e8f456ee5315edd528b927e1681c3675ef5926040519182526020820152426040820152a3610b9d565b50631e59ac5160e21b60005260045260246000fd5b81518152602091820191016109fa565b634e487b7160e01b600052604160045260246000fd5b15610c8b57005b7f4186b1950000000000000000000000000000000000000000000000000000000060005260046000fd5b813567ffffffffffffffff81116101a557602091610cdb878460248195890101016113ff565b815201910190610972565b346101a55760406003193601126101a557610cff61138e565b336001600160a01b03821603610d1b576101a39060043561163c565b7f6697b2320000000000000000000000000000000000000000000000000000000060005260046000fd5b346101a55760406003193601126101a557600435610d6161138e565b610d7c61019983600052600060205260016040600020015490565b610d8681836118b9565b610d8c57005b6101a39160005260016020526001600160a01b036040600020911690611a17565b346101a55760006003193601126101a5576020600254604051908152f35b346101a55760206003193601126101a557600435610de7611583565b610df081611882565b9015610c49579060006001928282526004602052816040812055610e1383611a83565b50827f04edbd68b1a49774d0341412ee6173ec25051302eed0d72a0e45571496ccdf7660206001600160a01b03604051944286521693a47f1461e90144abccd369b0962a63908835be724cf6c0e6275e1756bdedf47b51576020604051428152a3005b346101a55760006003193601126101a55760206040517fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217758152f35b346101a55760206003193601126101a557600435610ece81611882565b9015610c49576020906001600160a01b0360405191168152f35b346101a55760206003193601126101a5576020610f15600435600052600060205260016040600020015490565b604051908152f35b346101a55760006003193601126101a55760206040517fb92d52e77ebaa0cae5c23e882d85609efbcb44029214147dd132daf9ef1018af8152f35b346101a55760606003193601126101a557610f71611378565b602060006001600160a01b03610f8561138e565b93610f8e611511565b16926040516001600160a01b03848201927fa9059cbb000000000000000000000000000000000000000000000000000000008452166024820152604435604482015260448152610fdf6064826113c0565b519082855af115611033576000513d61102a5750803b155b610ffd57005b7f5274afe70000000000000000000000000000000000000000000000000000000060005260045260246000fd5b60011415610ff7565b6040513d6000823e3d90fd5b346101a55760006003193601126101a55760025461105c816114be565b9061106a60405192836113c0565b808252601f19611079826114be565b0160005b8181106111645750506002549060005b8181106110ef578360405180916020820160208352815180915260206040840192019060005b8181106110c1575050500390f35b8251805185526020908101516001600160a01b031681860152869550604090940193909201916001016110b3565b60008382101561115057908060208360026001955220016001600160a01b036040600092549283815260046020522054166040519161112d836113a4565b8252602082015261113e82876114d6565b5261114981866114d6565b500161108d565b80634e487b7160e01b602492526032600452fd5b602090604051611173816113a4565b600081526000838201528282870101520161107d565b346101a55760006003193601126101a55760405160025490818152602081018092600260005260206000209060005b81811061121157505050816111ce9103826113c0565b6040519182916020830190602084525180915260408301919060005b8181106111f8575050500390f35b82518452859450602093840193909201916001016111ea565b82548452602090930192600192830192016111b8565b346101a55760406003193601126101a5576000808080611245611378565b61124d611511565b602435905af161125b61148e565b501561126357005b606460405162461bcd60e51b815260206004820152600f60248201527f5472616e73666572206661696c656400000000000000000000000000000000006044820152fd5b346101a55760206003193601126101a557600435907fffffffff0000000000000000000000000000000000000000000000000000000082168092036101a557817f5a05180f000000000000000000000000000000000000000000000000000000006020931490811561131b575b5015158152f35b7f7965db0b0000000000000000000000000000000000000000000000000000000081149150811561134e575b5083611314565b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501483611347565b600435906001600160a01b03821682036101a557565b602435906001600160a01b03821682036101a557565b6040810190811067ffffffffffffffff821117610c6e57604052565b90601f601f19910116810190811067ffffffffffffffff821117610c6e57604052565b67ffffffffffffffff8111610c6e57601f01601f191660200190565b81601f820112156101a557803590611416826113e3565b9261142460405194856113c0565b828452602083830101116101a557816000926020809301838601378301015290565b60005b8381106114595750506000910152565b8181015183820152602001611449565b90601f19601f60209361148781518092818752878088019101611446565b0116010190565b3d156114b9573d9061149f826113e3565b916114ad60405193846113c0565b82523d6000602084013e565b606090565b67ffffffffffffffff8111610c6e5760051b60200190565b80518210156114ea5760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b519063ffffffff821682036101a557565b3360009081527f7d7ffb7a348e1c6a02869081a26547b49160dd3df72d1d75a570eb9b698292ec602052604090205460ff161561154a57565b63e2517d3f60e01b600052336004527fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177560245260446000fd5b3360009081527f88b4af5b933fdc1fcd68c2f9c47ec4f55dd961e785ecbbca61f1df8bed3ceffd602052604090205460ff16156115bc57565b63e2517d3f60e01b600052336004527fb92d52e77ebaa0cae5c23e882d85609efbcb44029214147dd132daf9ef1018af60245260446000fd5b80600052600060205260406000206001600160a01b03331660005260205260ff60406000205416156116245750565b63e2517d3f60e01b6000523360045260245260446000fd5b6116468282611953565b918261165157505090565b6116729160005260016020526001600160a01b036040600020911690611b4c565b5090565b519069ffffffffffffffffffff821682036101a557565b61169681611882565b9190156105f85750600080916040516001600160a01b0360208201917ffeaf968c000000000000000000000000000000000000000000000000000000008352600481526116e46024826113c0565b5192165afa6116f161148e565b906116ff5750600090600090565b9060a0828051810103126101a55761171960208301611676565b50604082015161173060a060808501519401611676565b509190565b60206001600160a01b03916004604051809481937f01d61c49000000000000000000000000000000000000000000000000000000008352165afa90811561103357600091611781575090565b90506020813d6020116117a8575b8161179c602093836113c0565b810103126101a5575190565b3d915061178f565b8015808061187a575b156117c957505050600090600090565b8015611872575b61183b576000811380611868575b8015611853575b61183b57819003600080821261184c5750905b60008082126118455750905b6305f5e1008102906305f5e10082040361183b578115611825570490600090565b634e487b7160e01b600052601260045260246000fd5b5050600090600190565b0390611804565b03906117f8565b506000811280156117e55750600082136117e5565b50600082126117de565b5081156117d0565b5082156117b9565b80600052600460205260406000205480156000146118b157506000526003602052604060002054151590600090565b600192909150565b80600052600060205260406000206001600160a01b03831660005260205260ff604060002054161560001461194c5780600052600060205260406000206001600160a01b0383166000526020526040600020600160ff198254161790556001600160a01b03339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d600080a4600190565b5050600090565b80600052600060205260406000206001600160a01b03831660005260205260ff6040600020541660001461194c5780600052600060205260406000206001600160a01b038316600052602052604060002060ff1981541690556001600160a01b03339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b600080a4600190565b6119fc918160005260046020526040600020556002611a17565b90565b80548210156114ea5760005260206000200190600090565b600082815260018201602052604090205461194c5780549068010000000000000000821015610c6e5782611a6c611a558460018096018555846119ff565b81939154906000199060031b92831b921b19161790565b905580549260005201602052604060002055600190565b600081815260036020526040902054801561194c576000198101818111610ba457600254906000198201918211610ba457818103611b12575b5050506002548015611afc5760001901611ad78160026119ff565b60001982549160031b1b19169055600255600052600360205260006040812055600190565b634e487b7160e01b600052603160045260246000fd5b611b34611b23611a559360026119ff565b90549060031b1c92839260026119ff565b90556000526003602052604060002055388080611abc565b9060018201918160005282602052604060002054801515600014611bff576000198101818111610ba4578254906000198201918211610ba457818103611bc8575b50505080548015611afc576000190190611ba782826119ff565b60001982549160031b1b191690555560005260205260006040812055600190565b611be8611bd8611a5593866119ff565b90549060031b1c928392866119ff565b905560005283602052604060002055388080611b8d565b5050505060009056fea2646970667358221220f4a0cd9b8521f3ffd424bd273c7c0eeff8191fdc51519abbc4678ff38833d3d764736f6c634300081a00332f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0dbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff00f7280a0db925c0d1e88a56cb8ae89369595b41df40ca283519b9b197f5fed07d7ffb7a348e1c6a02869081a26547b49160dd3df72d1d75a570eb9b698292ec93f956adba6c072fd936983f727a6152a8261208a80865e45d1d2b53f6de5fcaa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775000000000000000000000000ec89a5dd6c179c345ea7996aa879e59cb18c8484

Deployed Bytecode

0x608080604052600436101561001357600080fd5b60003560e01c90816301ffc9a7146112a75750806307b18bde146112275780630c518dce14611189578063105794681461103f5780631593dee114610f5857806321209cfa14610f1d578063248a9ca314610ee8578063280aebcf14610eb15780632a0acc6a14610e765780632a58990814610dcb5780632cc9dfb314610dad5780632f2ff15d14610d4557806336568abe14610ce65780634585e33b146108bd57806347f77bc0146108795780634a84d0211461078e578063581b3ee01461076f5780635affdd4c146107355780636e04ff0d1461049657806376ce5858146104605780639010d07c1461041a57806391d14854146103cc578063a217fddf146103b0578063a3246ad3146102fc578063abf05a1a146102c1578063b12a2d8b146102ae578063bd33b7c8146101d6578063ca15c873146101aa5763d547741f1461015e57600080fd5b346101a55760406003193601126101a5576101a360043561017d61138e565b9061019e61019982600052600060205260016040600020015490565b6115f5565b61163c565b005b600080fd5b346101a55760206003193601126101a55760043560005260016020526020604060002054604051908152f35b346101a55760406003193601126101a5576004356101f261138e565b6101fa611583565b61020381611735565b80830361027c575061021482611882565b919015610267576001600160a01b03169161022f83826119e2565b507f04edbd68b1a49774d0341412ee6173ec25051302eed0d72a0e45571496ccdf7660206001600160a01b03604051944286521693a4005b82631e59ac5160e21b60005260045260246000fd5b90507f352b24610000000000000000000000000000000000000000000000000000000060005260045260245260446000fd5b346101a55760006003193601126101a557005b346101a55760006003193601126101a55760206040517f9792fdc19ab98adfa72ab2fa98d342618c661e01c406979c105b31eda87f5e6f8152f35b346101a55760206003193601126101a5576004356000526001602052604060002060405181548082526020820190819360005260206000209060005b81811061039a575050508161034e9103826113c0565b6040519182916020830190602084525180915260408301919060005b818110610378575050500390f35b82516001600160a01b031684528594506020938401939092019160010161036a565b8254845260209093019260019283019201610338565b346101a55760006003193601126101a557602060405160008152f35b346101a55760406003193601126101a5576103e561138e565b60043560005260006020526001600160a01b0360406000209116600052602052602060ff604060002054166040519015158152f35b346101a55760406003193601126101a557600435600052600160205260206001600160a01b0361045060243560406000206119ff565b90549060031b1c16604051908152f35b346101a55760206003193601126101a557602061048c6004356000526003602052604060002054151590565b6040519015158152f35b346101a55760206003193601126101a55760043567ffffffffffffffff81116101a5576104c79036906004016113ff565b805160a0810361060c575060a0818051810103126101a557602081015160408201519060608301519160a0608085015194015191610512816000526003602052604060002054151590565b156105f8576105209061168d565b919094600094859484811015610593575b505050508215908161058a575b6040519460208601526040850152156060840152151560808301526080825261056860a0836113c0565b61058660405192839215158352604060208401526040830190611469565b0390f35b9250819261053e565b8481039081116105e45710156105db575b6105ae90866117b0565b919082156105d0575b50506105c7575b84808080610531565b600191506105be565b1015905085806105b7565b600194506105a4565b602487634e487b7160e01b81526011600452fd5b631e59ac5160e21b60005260045260246000fd5b6060116106f15760808151106106ad5760a090511061066957606460405162461bcd60e51b815260206004820152600e60248201527f546f6f206d75636820646174612e0000000000000000000000000000000000006044820152fd5b606460405162461bcd60e51b815260206004820152601b60248201527f4d697373696e67206f6666636861696e2074696d657374616d702e00000000006044820152fd5b606460405162461bcd60e51b815260206004820152601760248201527f4d697373696e67206f6666636861696e2070726963652e0000000000000000006044820152fd5b606460405162461bcd60e51b815260206004820152601260248201527f4d697373696e67206665656420646174612e00000000000000000000000000006044820152fd5b346101a55760006003193601126101a55760206040517ef7280a0db925c0d1e88a56cb8ae89369595b41df40ca283519b9b197f5fed08152f35b346101a55760006003193601126101a55760206040516305f5e1008152f35b346101a55760406003193601126101a5576004356107aa61138e565b6107b2611583565b6107bb81611735565b80830361027c57506107cc82611882565b919061084b576001600160a01b0316916107e683826119e2565b506000817f1461e90144abccd369b0962a63908835be724cf6c0e6275e1756bdedf47b51576020604051428152a37f04edbd68b1a49774d0341412ee6173ec25051302eed0d72a0e45571496ccdf7660206001600160a01b03604051944286521693a4005b827fd09bc9e10000000000000000000000000000000000000000000000000000000060005260045260246000fd5b346101a55760006003193601126101a55760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b60206003193601126101a55760043567ffffffffffffffff81116101a557366023820112156101a557806004013567ffffffffffffffff81116101a55781019060248201913683116101a557602090829003126101a55760248101359067ffffffffffffffff82116101a5570190806043830112156101a5576024820135610944816114be565b9261095260405194856113c0565b818452602060248186019360051b83010101918383116101a55760448201905b838210610cb557856040519060006020830152602082526109946040836113c0565b600091825b8251841015610c84576109ac84846114d6565b518051810190608081602084019303126101a55781603f820112156101a5576040516060810181811067ffffffffffffffff821117610c6e5760405260808201908382116101a55760208301905b828210610c5e575050519067ffffffffffffffff82116101a5570181603f820112156101a557602081015190610a2f826113e3565b91610a3d60405193846113c0565b8083526020830193604083830101116101a557836040610a5d9301611446565b6060818051810103126101a557610a8d60606001600160a01b03935192610a8660408201611500565b5001611500565b610a9682611882565b939093169215610c495763ffffffff610aae8361168d565b9290501680821015610c07575050600080610ac988886114d6565b51604051610b4e81610b40610b1060208301957ff3208a50000000000000000000000000000000000000000000000000000000008752604060248501526064840190611469565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc8382030160448401528b611469565b03601f1981018352826113c0565b519082865af1610b5c61148e565b9015610bba57507f9e0b7698b9f11443d38cb8a817dfdc478d23f7beff128684d788ca246a06d99c6020604051428152a36000198114610ba4576001809101935b0192610999565b634e487b7160e01b600052601160045260246000fd5b907f128604bb7ac214246287115a95d0b2e90d8447034bb6634b47ee34fc969dd4ba610bf9600195989493604051918291604083526040830190611469565b4260208301520390a3610b9d565b6060600195989493927fecc3a38fd26d51bc47185041b9eb9e8f456ee5315edd528b927e1681c3675ef5926040519182526020820152426040820152a3610b9d565b50631e59ac5160e21b60005260045260246000fd5b81518152602091820191016109fa565b634e487b7160e01b600052604160045260246000fd5b15610c8b57005b7f4186b1950000000000000000000000000000000000000000000000000000000060005260046000fd5b813567ffffffffffffffff81116101a557602091610cdb878460248195890101016113ff565b815201910190610972565b346101a55760406003193601126101a557610cff61138e565b336001600160a01b03821603610d1b576101a39060043561163c565b7f6697b2320000000000000000000000000000000000000000000000000000000060005260046000fd5b346101a55760406003193601126101a557600435610d6161138e565b610d7c61019983600052600060205260016040600020015490565b610d8681836118b9565b610d8c57005b6101a39160005260016020526001600160a01b036040600020911690611a17565b346101a55760006003193601126101a5576020600254604051908152f35b346101a55760206003193601126101a557600435610de7611583565b610df081611882565b9015610c49579060006001928282526004602052816040812055610e1383611a83565b50827f04edbd68b1a49774d0341412ee6173ec25051302eed0d72a0e45571496ccdf7660206001600160a01b03604051944286521693a47f1461e90144abccd369b0962a63908835be724cf6c0e6275e1756bdedf47b51576020604051428152a3005b346101a55760006003193601126101a55760206040517fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217758152f35b346101a55760206003193601126101a557600435610ece81611882565b9015610c49576020906001600160a01b0360405191168152f35b346101a55760206003193601126101a5576020610f15600435600052600060205260016040600020015490565b604051908152f35b346101a55760006003193601126101a55760206040517fb92d52e77ebaa0cae5c23e882d85609efbcb44029214147dd132daf9ef1018af8152f35b346101a55760606003193601126101a557610f71611378565b602060006001600160a01b03610f8561138e565b93610f8e611511565b16926040516001600160a01b03848201927fa9059cbb000000000000000000000000000000000000000000000000000000008452166024820152604435604482015260448152610fdf6064826113c0565b519082855af115611033576000513d61102a5750803b155b610ffd57005b7f5274afe70000000000000000000000000000000000000000000000000000000060005260045260246000fd5b60011415610ff7565b6040513d6000823e3d90fd5b346101a55760006003193601126101a55760025461105c816114be565b9061106a60405192836113c0565b808252601f19611079826114be565b0160005b8181106111645750506002549060005b8181106110ef578360405180916020820160208352815180915260206040840192019060005b8181106110c1575050500390f35b8251805185526020908101516001600160a01b031681860152869550604090940193909201916001016110b3565b60008382101561115057908060208360026001955220016001600160a01b036040600092549283815260046020522054166040519161112d836113a4565b8252602082015261113e82876114d6565b5261114981866114d6565b500161108d565b80634e487b7160e01b602492526032600452fd5b602090604051611173816113a4565b600081526000838201528282870101520161107d565b346101a55760006003193601126101a55760405160025490818152602081018092600260005260206000209060005b81811061121157505050816111ce9103826113c0565b6040519182916020830190602084525180915260408301919060005b8181106111f8575050500390f35b82518452859450602093840193909201916001016111ea565b82548452602090930192600192830192016111b8565b346101a55760406003193601126101a5576000808080611245611378565b61124d611511565b602435905af161125b61148e565b501561126357005b606460405162461bcd60e51b815260206004820152600f60248201527f5472616e73666572206661696c656400000000000000000000000000000000006044820152fd5b346101a55760206003193601126101a557600435907fffffffff0000000000000000000000000000000000000000000000000000000082168092036101a557817f5a05180f000000000000000000000000000000000000000000000000000000006020931490811561131b575b5015158152f35b7f7965db0b0000000000000000000000000000000000000000000000000000000081149150811561134e575b5083611314565b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501483611347565b600435906001600160a01b03821682036101a557565b602435906001600160a01b03821682036101a557565b6040810190811067ffffffffffffffff821117610c6e57604052565b90601f601f19910116810190811067ffffffffffffffff821117610c6e57604052565b67ffffffffffffffff8111610c6e57601f01601f191660200190565b81601f820112156101a557803590611416826113e3565b9261142460405194856113c0565b828452602083830101116101a557816000926020809301838601378301015290565b60005b8381106114595750506000910152565b8181015183820152602001611449565b90601f19601f60209361148781518092818752878088019101611446565b0116010190565b3d156114b9573d9061149f826113e3565b916114ad60405193846113c0565b82523d6000602084013e565b606090565b67ffffffffffffffff8111610c6e5760051b60200190565b80518210156114ea5760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b519063ffffffff821682036101a557565b3360009081527f7d7ffb7a348e1c6a02869081a26547b49160dd3df72d1d75a570eb9b698292ec602052604090205460ff161561154a57565b63e2517d3f60e01b600052336004527fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177560245260446000fd5b3360009081527f88b4af5b933fdc1fcd68c2f9c47ec4f55dd961e785ecbbca61f1df8bed3ceffd602052604090205460ff16156115bc57565b63e2517d3f60e01b600052336004527fb92d52e77ebaa0cae5c23e882d85609efbcb44029214147dd132daf9ef1018af60245260446000fd5b80600052600060205260406000206001600160a01b03331660005260205260ff60406000205416156116245750565b63e2517d3f60e01b6000523360045260245260446000fd5b6116468282611953565b918261165157505090565b6116729160005260016020526001600160a01b036040600020911690611b4c565b5090565b519069ffffffffffffffffffff821682036101a557565b61169681611882565b9190156105f85750600080916040516001600160a01b0360208201917ffeaf968c000000000000000000000000000000000000000000000000000000008352600481526116e46024826113c0565b5192165afa6116f161148e565b906116ff5750600090600090565b9060a0828051810103126101a55761171960208301611676565b50604082015161173060a060808501519401611676565b509190565b60206001600160a01b03916004604051809481937f01d61c49000000000000000000000000000000000000000000000000000000008352165afa90811561103357600091611781575090565b90506020813d6020116117a8575b8161179c602093836113c0565b810103126101a5575190565b3d915061178f565b8015808061187a575b156117c957505050600090600090565b8015611872575b61183b576000811380611868575b8015611853575b61183b57819003600080821261184c5750905b60008082126118455750905b6305f5e1008102906305f5e10082040361183b578115611825570490600090565b634e487b7160e01b600052601260045260246000fd5b5050600090600190565b0390611804565b03906117f8565b506000811280156117e55750600082136117e5565b50600082126117de565b5081156117d0565b5082156117b9565b80600052600460205260406000205480156000146118b157506000526003602052604060002054151590600090565b600192909150565b80600052600060205260406000206001600160a01b03831660005260205260ff604060002054161560001461194c5780600052600060205260406000206001600160a01b0383166000526020526040600020600160ff198254161790556001600160a01b03339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d600080a4600190565b5050600090565b80600052600060205260406000206001600160a01b03831660005260205260ff6040600020541660001461194c5780600052600060205260406000206001600160a01b038316600052602052604060002060ff1981541690556001600160a01b03339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b600080a4600190565b6119fc918160005260046020526040600020556002611a17565b90565b80548210156114ea5760005260206000200190600090565b600082815260018201602052604090205461194c5780549068010000000000000000821015610c6e5782611a6c611a558460018096018555846119ff565b81939154906000199060031b92831b921b19161790565b905580549260005201602052604060002055600190565b600081815260036020526040902054801561194c576000198101818111610ba457600254906000198201918211610ba457818103611b12575b5050506002548015611afc5760001901611ad78160026119ff565b60001982549160031b1b19169055600255600052600360205260006040812055600190565b634e487b7160e01b600052603160045260246000fd5b611b34611b23611a559360026119ff565b90549060031b1c92839260026119ff565b90556000526003602052604060002055388080611abc565b9060018201918160005282602052604060002054801515600014611bff576000198101818111610ba4578254906000198201918211610ba457818103611bc8575b50505080548015611afc576000190190611ba782826119ff565b60001982549160031b1b191690555560005260205260006040812055600190565b611be8611bd8611a5593866119ff565b90549060031b1c928392866119ff565b905560005283602052604060002055388080611b8d565b5050505060009056fea2646970667358221220f4a0cd9b8521f3ffd424bd273c7c0eeff8191fdc51519abbc4678ff38833d3d764736f6c634300081a0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000ec89a5dd6c179c345ea7996aa879e59cb18c8484

-----Decoded View---------------
Arg [0] : initialAdmin (address): 0xec89a5dd6c179c345EA7996AA879E59cB18c8484

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000ec89a5dd6c179c345ea7996aa879e59cb18c8484


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.