ETH Price: $1,873.40 (-0.25%)

Transaction Decoder

Block:
22019338 at Mar-10-2025 10:05:23 PM +UTC
Transaction Fee:
0.00015819938615264 ETH $0.30
Gas Used:
69,664 Gas / 2.27089151 Gwei

Emitted Events:

156 ReceiveUln302.PayloadVerified( dvn=[Receiver] DVN, header=0x0100000000000004A4000075E50000000000000000000000004141EB977A8646EC60C4C2891AEA645118183221000075950000000000000000000000006B8AD17795D89B283E6D0362A87A403F3544BB9D, confirmations=2, proofHash=45B858A91C915172DD9D1460D24A1FD4932B459F7092048741E0D1CA87A10F72 )

Account State Difference:

  Address   Before After State Difference Code
(beaverbuild)
15.986097617650267736 Eth15.986129873707319864 Eth0.000032256057052128
0xB8FF877e...a57cA47Cb
5.802300819584693998 Eth
Nonce: 870149
5.802142620198541358 Eth
Nonce: 870150
0.00015819938615264
0xc02Ab410...5156024C2

Execution Trace

DVN.execute( _params= )
  • Null: 0x000...001.dfafc82c( )
  • ReceiveUln302.verify( _packetHeader=0x0100000000000004A4000075E50000000000000000000000004141EB977A8646EC60C4C2891AEA645118183221000075950000000000000000000000006B8AD17795D89B283E6D0362A87A403F3544BB9D, _payloadHash=45B858A91C915172DD9D1460D24A1FD4932B459F7092048741E0D1CA87A10F72, _confirmations=2 )
    File 1 of 2: DVN
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    import { IMessageLibManager } from "./IMessageLibManager.sol";
    import { IMessagingComposer } from "./IMessagingComposer.sol";
    import { IMessagingChannel } from "./IMessagingChannel.sol";
    import { IMessagingContext } from "./IMessagingContext.sol";
    struct MessagingParams {
        uint32 dstEid;
        bytes32 receiver;
        bytes message;
        bytes options;
        bool payInLzToken;
    }
    struct MessagingReceipt {
        bytes32 guid;
        uint64 nonce;
        MessagingFee fee;
    }
    struct MessagingFee {
        uint256 nativeFee;
        uint256 lzTokenFee;
    }
    struct Origin {
        uint32 srcEid;
        bytes32 sender;
        uint64 nonce;
    }
    interface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {
        event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);
        event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);
        event PacketDelivered(Origin origin, address receiver);
        event LzReceiveAlert(
            address indexed receiver,
            address indexed executor,
            Origin origin,
            bytes32 guid,
            uint256 gas,
            uint256 value,
            bytes message,
            bytes extraData,
            bytes reason
        );
        event LzTokenSet(address token);
        event DelegateSet(address sender, address delegate);
        function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);
        function send(
            MessagingParams calldata _params,
            address _refundAddress
        ) external payable returns (MessagingReceipt memory);
        function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;
        function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);
        function initializable(Origin calldata _origin, address _receiver) external view returns (bool);
        function lzReceive(
            Origin calldata _origin,
            address _receiver,
            bytes32 _guid,
            bytes calldata _message,
            bytes calldata _extraData
        ) external payable;
        // oapp can burn messages partially by calling this function with its own business logic if messages are verified in order
        function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;
        function setLzToken(address _lzToken) external;
        function lzToken() external view returns (address);
        function nativeToken() external view returns (address);
        function setDelegate(address _delegate) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
    import { SetConfigParam } from "./IMessageLibManager.sol";
    enum MessageLibType {
        Send,
        Receive,
        SendAndReceive
    }
    interface IMessageLib is IERC165 {
        function setConfig(address _oapp, SetConfigParam[] calldata _config) external;
        function getConfig(uint32 _eid, address _oapp, uint32 _configType) external view returns (bytes memory config);
        function isSupportedEid(uint32 _eid) external view returns (bool);
        // message libs of same major version are compatible
        function version() external view returns (uint64 major, uint8 minor, uint8 endpointVersion);
        function messageLibType() external view returns (MessageLibType);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    struct SetConfigParam {
        uint32 eid;
        uint32 configType;
        bytes config;
    }
    interface IMessageLibManager {
        struct Timeout {
            address lib;
            uint256 expiry;
        }
        event LibraryRegistered(address newLib);
        event DefaultSendLibrarySet(uint32 eid, address newLib);
        event DefaultReceiveLibrarySet(uint32 eid, address newLib);
        event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);
        event SendLibrarySet(address sender, uint32 eid, address newLib);
        event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);
        event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);
        function registerLibrary(address _lib) external;
        function isRegisteredLibrary(address _lib) external view returns (bool);
        function getRegisteredLibraries() external view returns (address[] memory);
        function setDefaultSendLibrary(uint32 _eid, address _newLib) external;
        function defaultSendLibrary(uint32 _eid) external view returns (address);
        function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _timeout) external;
        function defaultReceiveLibrary(uint32 _eid) external view returns (address);
        function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;
        function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);
        function isSupportedEid(uint32 _eid) external view returns (bool);
        function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);
        /// ------------------- OApp interfaces -------------------
        function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;
        function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);
        function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);
        function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;
        function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);
        function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _gracePeriod) external;
        function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);
        function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;
        function getConfig(
            address _oapp,
            address _lib,
            uint32 _eid,
            uint32 _configType
        ) external view returns (bytes memory config);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    interface IMessagingChannel {
        event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);
        event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);
        event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);
        function eid() external view returns (uint32);
        // this is an emergency function if a message cannot be verified for some reasons
        // required to provide _nextNonce to avoid race condition
        function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;
        function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;
        function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;
        function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);
        function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);
        function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);
        function inboundPayloadHash(
            address _receiver,
            uint32 _srcEid,
            bytes32 _sender,
            uint64 _nonce
        ) external view returns (bytes32);
        function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    interface IMessagingComposer {
        event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);
        event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);
        event LzComposeAlert(
            address indexed from,
            address indexed to,
            address indexed executor,
            bytes32 guid,
            uint16 index,
            uint256 gas,
            uint256 value,
            bytes message,
            bytes extraData,
            bytes reason
        );
        function composeQueue(
            address _from,
            address _to,
            bytes32 _guid,
            uint16 _index
        ) external view returns (bytes32 messageHash);
        function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;
        function lzCompose(
            address _from,
            address _to,
            bytes32 _guid,
            uint16 _index,
            bytes calldata _message,
            bytes calldata _extraData
        ) external payable;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    interface IMessagingContext {
        function isSendingMessage() external view returns (bool);
        function getSendContext() external view returns (uint32 dstEid, address sender);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    import { MessagingFee } from "./ILayerZeroEndpointV2.sol";
    import { IMessageLib } from "./IMessageLib.sol";
    struct Packet {
        uint64 nonce;
        uint32 srcEid;
        address sender;
        uint32 dstEid;
        bytes32 receiver;
        bytes32 guid;
        bytes message;
    }
    interface ISendLib is IMessageLib {
        function send(
            Packet calldata _packet,
            bytes calldata _options,
            bool _payInLzToken
        ) external returns (MessagingFee memory, bytes memory encodedPacket);
        function quote(
            Packet calldata _packet,
            bytes calldata _options,
            bool _payInLzToken
        ) external view returns (MessagingFee memory);
        function setTreasury(address _treasury) external;
        function withdrawFee(address _to, uint256 _amount) external;
        function withdrawLzTokenFee(address _lzToken, address _to, uint256 _amount) external;
    }
    // SPDX-License-Identifier: LZBL-1.2
    pragma solidity ^0.8.20;
    import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
    import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    library Transfer {
        using SafeERC20 for IERC20;
        address internal constant ADDRESS_ZERO = address(0);
        error Transfer_NativeFailed(address _to, uint256 _value);
        error Transfer_ToAddressIsZero();
        function native(address _to, uint256 _value) internal {
            if (_to == ADDRESS_ZERO) revert Transfer_ToAddressIsZero();
            (bool success, ) = _to.call{ value: _value }("");
            if (!success) revert Transfer_NativeFailed(_to, _value);
        }
        function token(address _token, address _to, uint256 _value) internal {
            if (_to == ADDRESS_ZERO) revert Transfer_ToAddressIsZero();
            IERC20(_token).safeTransfer(_to, _value);
        }
        function nativeOrToken(address _token, address _to, uint256 _value) internal {
            if (_token == ADDRESS_ZERO) {
                native(_to, _value);
            } else {
                token(_token, _to, _value);
            }
        }
    }
    // SPDX-License-Identifier: BUSL-1.1
    pragma solidity >=0.7.0;
    pragma abicoder v2;
    interface ILayerZeroUltraLightNodeV2 {
        // Relayer functions
        function validateTransactionProof(
            uint16 _srcChainId,
            address _dstAddress,
            uint _gasLimit,
            bytes32 _lookupHash,
            bytes32 _blockData,
            bytes calldata _transactionProof
        ) external;
        // an Oracle delivers the block data using updateHash()
        function updateHash(uint16 _srcChainId, bytes32 _lookupHash, uint _confirmations, bytes32 _blockData) external;
        // can only withdraw the receivable of the msg.sender
        function withdrawNative(address payable _to, uint _amount) external;
        function withdrawZRO(address _to, uint _amount) external;
        // view functions
        function getAppConfig(
            uint16 _remoteChainId,
            address _userApplicationAddress
        ) external view returns (ApplicationConfiguration memory);
        function accruedNativeFee(address _address) external view returns (uint);
        struct ApplicationConfiguration {
            uint16 inboundProofLibraryVersion;
            uint64 inboundBlockConfirmations;
            address relayer;
            uint16 outboundProofType;
            uint64 outboundBlockConfirmations;
            address oracle;
        }
        event HashReceived(
            uint16 indexed srcChainId,
            address indexed oracle,
            bytes32 lookupHash,
            bytes32 blockData,
            uint confirmations
        );
        event RelayerParams(bytes adapterParams, uint16 outboundProofType);
        event Packet(bytes payload);
        event InvalidDst(
            uint16 indexed srcChainId,
            bytes srcAddress,
            address indexed dstAddress,
            uint64 nonce,
            bytes32 payloadHash
        );
        event PacketReceived(
            uint16 indexed srcChainId,
            bytes srcAddress,
            address indexed dstAddress,
            uint64 nonce,
            bytes32 payloadHash
        );
        event AppConfigUpdated(address indexed userApplication, uint indexed configType, bytes newConfig);
        event AddInboundProofLibraryForChain(uint16 indexed chainId, address lib);
        event EnableSupportedOutboundProof(uint16 indexed chainId, uint16 proofType);
        event SetChainAddressSize(uint16 indexed chainId, uint size);
        event SetDefaultConfigForChainId(
            uint16 indexed chainId,
            uint16 inboundProofLib,
            uint64 inboundBlockConfirm,
            address relayer,
            uint16 outboundProofType,
            uint64 outboundBlockConfirm,
            address oracle
        );
        event SetDefaultAdapterParamsForChainId(uint16 indexed chainId, uint16 indexed proofType, bytes adapterParams);
        event SetLayerZeroToken(address indexed tokenAddress);
        event SetRemoteUln(uint16 indexed chainId, bytes32 uln);
        event SetTreasury(address indexed treasuryAddress);
        event WithdrawZRO(address indexed msgSender, address indexed to, uint amount);
        event WithdrawNative(address indexed msgSender, address indexed to, uint amount);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
    pragma solidity ^0.8.0;
    import "./IAccessControl.sol";
    import "../utils/Context.sol";
    import "../utils/Strings.sol";
    import "../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 => bool) members;
            bytes32 adminRole;
        }
        mapping(bytes32 => RoleData) private _roles;
        bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
        /**
         * @dev Modifier that checks that an account has a specific role. Reverts
         * with a standardized message including the required role.
         *
         * The format of the revert reason is given by the following regular expression:
         *
         *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
         *
         * _Available since v4.1._
         */
        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 override returns (bool) {
            return _roles[role].members[account];
        }
        /**
         * @dev Revert with a standard message if `_msgSender()` is missing `role`.
         * Overriding this function changes the behavior of the {onlyRole} modifier.
         *
         * Format of the revert message is described in {_checkRole}.
         *
         * _Available since v4.6._
         */
        function _checkRole(bytes32 role) internal view virtual {
            _checkRole(role, _msgSender());
        }
        /**
         * @dev Revert with a standard message if `account` is missing `role`.
         *
         * The format of the revert reason is given by the following regular expression:
         *
         *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
         */
        function _checkRole(bytes32 role, address account) internal view virtual {
            if (!hasRole(role, account)) {
                revert(
                    string(
                        abi.encodePacked(
                            "AccessControl: account ",
                            Strings.toHexString(account),
                            " is missing role ",
                            Strings.toHexString(uint256(role), 32)
                        )
                    )
                );
            }
        }
        /**
         * @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 override 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 override 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 override 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 `account`.
         *
         * May emit a {RoleRevoked} event.
         */
        function renounceRole(bytes32 role, address account) public virtual override {
            require(account == _msgSender(), "AccessControl: can only renounce roles for self");
            _revokeRole(role, account);
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event. Note that unlike {grantRole}, this function doesn't perform any
         * checks on the calling account.
         *
         * May emit a {RoleGranted} event.
         *
         * [WARNING]
         * ====
         * This function should only be called from the constructor when setting
         * up the initial roles for the system.
         *
         * Using this function in any other way is effectively circumventing the admin
         * system imposed by {AccessControl}.
         * ====
         *
         * NOTE: This function is deprecated in favor of {_grantRole}.
         */
        function _setupRole(bytes32 role, address account) internal virtual {
            _grantRole(role, account);
        }
        /**
         * @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 Grants `role` to `account`.
         *
         * Internal function without access restriction.
         *
         * May emit a {RoleGranted} event.
         */
        function _grantRole(bytes32 role, address account) internal virtual {
            if (!hasRole(role, account)) {
                _roles[role].members[account] = true;
                emit RoleGranted(role, account, _msgSender());
            }
        }
        /**
         * @dev Revokes `role` from `account`.
         *
         * Internal function without access restriction.
         *
         * May emit a {RoleRevoked} event.
         */
        function _revokeRole(bytes32 role, address account) internal virtual {
            if (hasRole(role, account)) {
                _roles[role].members[account] = false;
                emit RoleRevoked(role, account, _msgSender());
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev External interface of AccessControl declared to support ERC165 detection.
     */
    interface IAccessControl {
        /**
         * @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.
         *
         * _Available since v3.1._
         */
        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, an admin role
         * bearer except when using {AccessControl-_setupRole}.
         */
        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 `account`.
         */
        function renounceRole(bytes32 role, address account) external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which allows children to implement an emergency stop
     * mechanism that can be triggered by an authorized account.
     *
     * This module is used through inheritance. It will make available the
     * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
     * the functions of your contract. Note that they will not be pausable by
     * simply including this module, only once the modifiers are put in place.
     */
    abstract contract Pausable is Context {
        /**
         * @dev Emitted when the pause is triggered by `account`.
         */
        event Paused(address account);
        /**
         * @dev Emitted when the pause is lifted by `account`.
         */
        event Unpaused(address account);
        bool private _paused;
        /**
         * @dev Initializes the contract in unpaused state.
         */
        constructor() {
            _paused = false;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is not paused.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        modifier whenNotPaused() {
            _requireNotPaused();
            _;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is paused.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        modifier whenPaused() {
            _requirePaused();
            _;
        }
        /**
         * @dev Returns true if the contract is paused, and false otherwise.
         */
        function paused() public view virtual returns (bool) {
            return _paused;
        }
        /**
         * @dev Throws if the contract is paused.
         */
        function _requireNotPaused() internal view virtual {
            require(!paused(), "Pausable: paused");
        }
        /**
         * @dev Throws if the contract is not paused.
         */
        function _requirePaused() internal view virtual {
            require(paused(), "Pausable: not paused");
        }
        /**
         * @dev Triggers stopped state.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        function _pause() internal virtual whenNotPaused {
            _paused = true;
            emit Paused(_msgSender());
        }
        /**
         * @dev Returns to normal state.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        function _unpause() internal virtual whenPaused {
            _paused = false;
            emit Unpaused(_msgSender());
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `to`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address to, uint256 amount) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `from` to `to` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(address from, address to, uint256 amount) external returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
     * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
     *
     * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
     * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
     * need to send a transaction, and thus is not required to hold Ether at all.
     *
     * ==== Security Considerations
     *
     * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
     * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
     * considered as an intention to spend the allowance in any specific way. The second is that because permits have
     * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
     * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
     * generally recommended is:
     *
     * ```solidity
     * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
     *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
     *     doThing(..., value);
     * }
     *
     * function doThing(..., uint256 value) public {
     *     token.safeTransferFrom(msg.sender, address(this), value);
     *     ...
     * }
     * ```
     *
     * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
     * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
     * {SafeERC20-safeTransferFrom}).
     *
     * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
     * contracts should have entry points that don't rely on permit.
     */
    interface IERC20Permit {
        /**
         * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
         * given ``owner``'s signed approval.
         *
         * IMPORTANT: The same issues {IERC20-approve} has related to transaction
         * ordering also apply here.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `deadline` must be a timestamp in the future.
         * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
         * over the EIP712-formatted function arguments.
         * - the signature must use ``owner``'s current nonce (see {nonces}).
         *
         * For more information on the signature format, see the
         * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
         * section].
         *
         * CAUTION: See Security Considerations above.
         */
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
        /**
         * @dev Returns the current nonce for `owner`. This value must be
         * included whenever a signature is generated for {permit}.
         *
         * Every successful call to {permit} increases ``owner``'s nonce by one. This
         * prevents a signature from being used multiple times.
         */
        function nonces(address owner) external view returns (uint256);
        /**
         * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view returns (bytes32);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
    pragma solidity ^0.8.0;
    import "../IERC20.sol";
    import "../extensions/IERC20Permit.sol";
    import "../../../utils/Address.sol";
    /**
     * @title SafeERC20
     * @dev Wrappers around ERC20 operations that throw on failure (when the token
     * contract returns false). Tokens that return no value (and instead revert or
     * throw on failure) are also supported, non-reverting calls are assumed to be
     * successful.
     * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
     * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
     */
    library SafeERC20 {
        using Address for address;
        /**
         * @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.encodeWithSelector(token.transfer.selector, 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.encodeWithSelector(token.transferFrom.selector, from, to, value));
        }
        /**
         * @dev Deprecated. This function has issues similar to the ones found in
         * {IERC20-approve}, and its usage is discouraged.
         *
         * Whenever possible, use {safeIncreaseAllowance} and
         * {safeDecreaseAllowance} instead.
         */
        function safeApprove(IERC20 token, address spender, uint256 value) internal {
            // safeApprove should only be called when setting an initial allowance,
            // or when resetting it to zero. To increase and decrease it, use
            // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
            require(
                (value == 0) || (token.allowance(address(this), spender) == 0),
                "SafeERC20: approve from non-zero to non-zero allowance"
            );
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
        }
        /**
         * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         */
        function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 oldAllowance = token.allowance(address(this), spender);
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
        }
        /**
         * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         */
        function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            unchecked {
                uint256 oldAllowance = token.allowance(address(this), spender);
                require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
            }
        }
        /**
         * @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.
         */
        function forceApprove(IERC20 token, address spender, uint256 value) internal {
            bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
            if (!_callOptionalReturnBool(token, approvalCall)) {
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
                _callOptionalReturn(token, approvalCall);
            }
        }
        /**
         * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
         * Revert on invalid signature.
         */
        function safePermit(
            IERC20Permit token,
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal {
            uint256 nonceBefore = token.nonces(owner);
            token.permit(owner, spender, value, deadline, v, r, s);
            uint256 nonceAfter = token.nonces(owner);
            require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         */
        function _callOptionalReturn(IERC20 token, bytes memory data) private {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
            // the target address contains contract code and also asserts for success in the low-level call.
            bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
            require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         *
         * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
         */
        function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
            // and not revert is the subcall reverts.
            (bool success, bytes memory returndata) = address(token).call(data);
            return
                success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
    pragma solidity ^0.8.1;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         *
         * Furthermore, `isContract` will also return true if the target contract within
         * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
         * which only has an effect at the end of a transaction.
         * ====
         *
         * [IMPORTANT]
         * ====
         * You shouldn't rely on `isContract` to protect against flash loan attacks!
         *
         * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
         * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
         * constructor.
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize/address.code.length, which returns 0
            // for contracts in construction, since the code is only stored at the end
            // of the constructor execution.
            return account.code.length > 0;
        }
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, "Address: low-level call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
        /**
         * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
         * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
         *
         * _Available since v4.8._
         */
        function verifyCallResultFromTarget(
            address target,
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            if (success) {
                if (returndata.length == 0) {
                    // only check isContract if the call was successful and the return data is empty
                    // otherwise we already know that it was a contract
                    require(isContract(target), "Address: call to non-contract");
                }
                return returndata;
            } else {
                _revert(returndata, errorMessage);
            }
        }
        /**
         * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
         * revert reason or using the provided one.
         *
         * _Available since v4.3._
         */
        function verifyCallResult(
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal pure returns (bytes memory) {
            if (success) {
                return returndata;
            } else {
                _revert(returndata, errorMessage);
            }
        }
        function _revert(bytes memory returndata, string memory errorMessage) private pure {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
        function _contextSuffixLength() internal view virtual returns (uint256) {
            return 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
    pragma solidity ^0.8.0;
    import "./math/Math.sol";
    import "./math/SignedMath.sol";
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant _SYMBOLS = "0123456789abcdef";
        uint8 private constant _ADDRESS_LENGTH = 20;
        /**
         * @dev Converts a `uint256` to its ASCII `string` decimal representation.
         */
        function toString(uint256 value) internal pure returns (string memory) {
            unchecked {
                uint256 length = Math.log10(value) + 1;
                string memory buffer = new string(length);
                uint256 ptr;
                /// @solidity memory-safe-assembly
                assembly {
                    ptr := add(buffer, add(32, length))
                }
                while (true) {
                    ptr--;
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                    }
                    value /= 10;
                    if (value == 0) break;
                }
                return buffer;
            }
        }
        /**
         * @dev Converts a `int256` to its ASCII `string` decimal representation.
         */
        function toString(int256 value) internal pure returns (string memory) {
            return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
         */
        function toHexString(uint256 value) internal pure returns (string memory) {
            unchecked {
                return toHexString(value, Math.log256(value) + 1);
            }
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
         */
        function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
            bytes memory buffer = new bytes(2 * length + 2);
            buffer[0] = "0";
            buffer[1] = "x";
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = _SYMBOLS[value & 0xf];
                value >>= 4;
            }
            require(value == 0, "Strings: hex length insufficient");
            return string(buffer);
        }
        /**
         * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
         */
        function toHexString(address addr) internal pure returns (string memory) {
            return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
        }
        /**
         * @dev Returns true if the two strings are equal.
         */
        function equal(string memory a, string memory b) internal pure returns (bool) {
            return keccak256(bytes(a)) == keccak256(bytes(b));
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
    pragma solidity ^0.8.0;
    import "../Strings.sol";
    /**
     * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
     *
     * These functions can be used to verify that a message was signed by the holder
     * of the private keys of a given address.
     */
    library ECDSA {
        enum RecoverError {
            NoError,
            InvalidSignature,
            InvalidSignatureLength,
            InvalidSignatureS,
            InvalidSignatureV // Deprecated in v4.8
        }
        function _throwError(RecoverError error) private pure {
            if (error == RecoverError.NoError) {
                return; // no error: do nothing
            } else if (error == RecoverError.InvalidSignature) {
                revert("ECDSA: invalid signature");
            } else if (error == RecoverError.InvalidSignatureLength) {
                revert("ECDSA: invalid signature length");
            } else if (error == RecoverError.InvalidSignatureS) {
                revert("ECDSA: invalid signature 's' value");
            }
        }
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature` or error string. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
         * this function rejects them by requiring the `s` value to be in the lower
         * half order, and the `v` value to be either 27 or 28.
         *
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
         * verification to be secure: it is possible to craft signatures that
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
         * this is by receiving a hash of the original message (which may otherwise
         * be too long), and then calling {toEthSignedMessageHash} on it.
         *
         * Documentation for signature generation:
         * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
         * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
         *
         * _Available since v4.3._
         */
        function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
            if (signature.length == 65) {
                bytes32 r;
                bytes32 s;
                uint8 v;
                // ecrecover takes the signature parameters, and the only way to get them
                // currently is to use assembly.
                /// @solidity memory-safe-assembly
                assembly {
                    r := mload(add(signature, 0x20))
                    s := mload(add(signature, 0x40))
                    v := byte(0, mload(add(signature, 0x60)))
                }
                return tryRecover(hash, v, r, s);
            } else {
                return (address(0), RecoverError.InvalidSignatureLength);
            }
        }
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature`. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
         * this function rejects them by requiring the `s` value to be in the lower
         * half order, and the `v` value to be either 27 or 28.
         *
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
         * verification to be secure: it is possible to craft signatures that
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
         * this is by receiving a hash of the original message (which may otherwise
         * be too long), and then calling {toEthSignedMessageHash} on it.
         */
        function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, signature);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
         *
         * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
         *
         * _Available since v4.3._
         */
        function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
         *
         * _Available since v4.2._
         */
        function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, r, vs);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
         * `r` and `s` signature fields separately.
         *
         * _Available since v4.3._
         */
        function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
            // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
            // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
            // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
            // signatures from current libraries generate a unique signature with an s-value in the lower half order.
            //
            // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
            // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
            // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
            // these malleable signatures as well.
            if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                return (address(0), RecoverError.InvalidSignatureS);
            }
            // If the signature is valid (and not malleable), return the signer address
            address signer = ecrecover(hash, v, r, s);
            if (signer == address(0)) {
                return (address(0), RecoverError.InvalidSignature);
            }
            return (signer, RecoverError.NoError);
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `v`,
         * `r` and `s` signature fields separately.
         */
        function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Returns an Ethereum Signed Message, created from a `hash`. This
         * produces hash corresponding to the one signed with the
         * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
         * JSON-RPC method as part of EIP-191.
         *
         * See {recover}.
         */
        function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
            // 32 is the length in bytes of hash,
            // enforced by the type signature above
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x00, "\\x19Ethereum Signed Message:\
    32")
                mstore(0x1c, hash)
                message := keccak256(0x00, 0x3c)
            }
        }
        /**
         * @dev Returns an Ethereum Signed Message, created from `s`. This
         * produces hash corresponding to the one signed with the
         * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
         * JSON-RPC method as part of EIP-191.
         *
         * See {recover}.
         */
        function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
    ", Strings.toString(s.length), s));
        }
        /**
         * @dev Returns an Ethereum Signed Typed Data, created from a
         * `domainSeparator` and a `structHash`. This produces hash corresponding
         * to the one signed with the
         * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
         * JSON-RPC method as part of EIP-712.
         *
         * See {recover}.
         */
        function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
            /// @solidity memory-safe-assembly
            assembly {
                let ptr := mload(0x40)
                mstore(ptr, "\\x19\\x01")
                mstore(add(ptr, 0x02), domainSeparator)
                mstore(add(ptr, 0x22), structHash)
                data := keccak256(ptr, 0x42)
            }
        }
        /**
         * @dev Returns an Ethereum Signed Data with intended validator, created from a
         * `validator` and `data` according to the version 0 of EIP-191.
         *
         * See {recover}.
         */
        function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19\\x00", validator, data));
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
    pragma solidity ^0.8.0;
    import "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 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);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * 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[EIP 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);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Standard math utilities missing in the Solidity language.
     */
    library Math {
        enum Rounding {
            Down, // Toward negative infinity
            Up, // Toward infinity
            Zero // Toward zero
        }
        /**
         * @dev Returns the largest of two numbers.
         */
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
            return a > b ? a : b;
        }
        /**
         * @dev Returns the smallest of two numbers.
         */
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
            return a < b ? a : b;
        }
        /**
         * @dev Returns the average of two numbers. The result is rounded towards
         * zero.
         */
        function average(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b) / 2 can overflow.
            return (a & b) + (a ^ b) / 2;
        }
        /**
         * @dev Returns the ceiling of the division of two numbers.
         *
         * This differs from standard division with `/` in that it rounds up instead
         * of rounding down.
         */
        function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b - 1) / b can overflow on addition, so we distribute.
            return a == 0 ? 0 : (a - 1) / b + 1;
        }
        /**
         * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
         * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
         * with further edits by Uniswap Labs also under MIT license.
         */
        function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
            unchecked {
                // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                // variables such that product = prod1 * 2^256 + prod0.
                uint256 prod0; // Least significant 256 bits of the product
                uint256 prod1; // Most significant 256 bits of the product
                assembly {
                    let mm := mulmod(x, y, not(0))
                    prod0 := mul(x, y)
                    prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                }
                // Handle non-overflow cases, 256 by 256 division.
                if (prod1 == 0) {
                    // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                    // The surrounding unchecked block does not change this fact.
                    // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                    return prod0 / denominator;
                }
                // Make sure the result is less than 2^256. Also prevents denominator == 0.
                require(denominator > prod1, "Math: mulDiv overflow");
                ///////////////////////////////////////////////
                // 512 by 256 division.
                ///////////////////////////////////////////////
                // Make division exact by subtracting the remainder from [prod1 prod0].
                uint256 remainder;
                assembly {
                    // Compute remainder using mulmod.
                    remainder := mulmod(x, y, denominator)
                    // Subtract 256 bit number from 512 bit number.
                    prod1 := sub(prod1, gt(remainder, prod0))
                    prod0 := sub(prod0, remainder)
                }
                // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                // See https://cs.stackexchange.com/q/138556/92363.
                // Does not overflow because the denominator cannot be zero at this stage in the function.
                uint256 twos = denominator & (~denominator + 1);
                assembly {
                    // Divide denominator by twos.
                    denominator := div(denominator, twos)
                    // Divide [prod1 prod0] by twos.
                    prod0 := div(prod0, twos)
                    // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                    twos := add(div(sub(0, twos), twos), 1)
                }
                // Shift in bits from prod1 into prod0.
                prod0 |= prod1 * twos;
                // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                // four bits. That is, denominator * inv = 1 mod 2^4.
                uint256 inverse = (3 * denominator) ^ 2;
                // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                // in modular arithmetic, doubling the correct bits in each step.
                inverse *= 2 - denominator * inverse; // inverse mod 2^8
                inverse *= 2 - denominator * inverse; // inverse mod 2^16
                inverse *= 2 - denominator * inverse; // inverse mod 2^32
                inverse *= 2 - denominator * inverse; // inverse mod 2^64
                inverse *= 2 - denominator * inverse; // inverse mod 2^128
                inverse *= 2 - denominator * inverse; // inverse mod 2^256
                // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                // is no longer required.
                result = prod0 * inverse;
                return result;
            }
        }
        /**
         * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
         */
        function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
            uint256 result = mulDiv(x, y, denominator);
            if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                result += 1;
            }
            return result;
        }
        /**
         * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
         *
         * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
         */
        function sqrt(uint256 a) internal pure returns (uint256) {
            if (a == 0) {
                return 0;
            }
            // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
            //
            // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
            // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
            //
            // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
            // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
            // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
            //
            // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
            uint256 result = 1 << (log2(a) >> 1);
            // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
            // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
            // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
            // into the expected uint128 result.
            unchecked {
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                return min(result, a / result);
            }
        }
        /**
         * @notice Calculates sqrt(a), following the selected rounding direction.
         */
        function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = sqrt(a);
                return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 2, rounded down, of a positive value.
         * Returns 0 if given 0.
         */
        function log2(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 128;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 64;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 32;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 16;
                }
                if (value >> 8 > 0) {
                    value >>= 8;
                    result += 8;
                }
                if (value >> 4 > 0) {
                    value >>= 4;
                    result += 4;
                }
                if (value >> 2 > 0) {
                    value >>= 2;
                    result += 2;
                }
                if (value >> 1 > 0) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log2(value);
                return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 10, rounded down, of a positive value.
         * Returns 0 if given 0.
         */
        function log10(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >= 10 ** 64) {
                    value /= 10 ** 64;
                    result += 64;
                }
                if (value >= 10 ** 32) {
                    value /= 10 ** 32;
                    result += 32;
                }
                if (value >= 10 ** 16) {
                    value /= 10 ** 16;
                    result += 16;
                }
                if (value >= 10 ** 8) {
                    value /= 10 ** 8;
                    result += 8;
                }
                if (value >= 10 ** 4) {
                    value /= 10 ** 4;
                    result += 4;
                }
                if (value >= 10 ** 2) {
                    value /= 10 ** 2;
                    result += 2;
                }
                if (value >= 10 ** 1) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log10(value);
                return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 256, rounded down, of a positive value.
         * Returns 0 if given 0.
         *
         * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
         */
        function log256(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 16;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 8;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 4;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 2;
                }
                if (value >> 8 > 0) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log256(value);
                return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Standard signed math utilities missing in the Solidity language.
     */
    library SignedMath {
        /**
         * @dev Returns the largest of two signed numbers.
         */
        function max(int256 a, int256 b) internal pure returns (int256) {
            return a > b ? a : b;
        }
        /**
         * @dev Returns the smallest of two signed numbers.
         */
        function min(int256 a, int256 b) internal pure returns (int256) {
            return a < b ? a : b;
        }
        /**
         * @dev Returns the average of two signed numbers without overflow.
         * The result is rounded towards zero.
         */
        function average(int256 a, int256 b) internal pure returns (int256) {
            // Formula from the book "Hacker's Delight"
            int256 x = (a & b) + ((a ^ b) >> 1);
            return x + (int256(uint256(x) >> 255) & (a ^ b));
        }
        /**
         * @dev Returns the absolute unsigned value of a signed value.
         */
        function abs(int256 n) internal pure returns (uint256) {
            unchecked {
                // must be unchecked in order to support `n = type(int256).min`
                return uint256(n >= 0 ? n : -n);
            }
        }
    }
    // SPDX-License-Identifier: LZBL-1.2
    pragma solidity ^0.8.20;
    import { Pausable } from "@openzeppelin/contracts/security/Pausable.sol";
    import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol";
    import { ISendLib } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ISendLib.sol";
    import { Transfer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/Transfer.sol";
    import { IWorker } from "./interfaces/IWorker.sol";
    abstract contract Worker is AccessControl, Pausable, IWorker {
        bytes32 internal constant MESSAGE_LIB_ROLE = keccak256("MESSAGE_LIB_ROLE");
        bytes32 internal constant ALLOWLIST = keccak256("ALLOWLIST");
        bytes32 internal constant DENYLIST = keccak256("DENYLIST");
        bytes32 internal constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
        address public workerFeeLib;
        uint64 public allowlistSize;
        uint16 public defaultMultiplierBps;
        address public priceFeed;
        mapping(uint32 eid => uint8[] optionTypes) internal supportedOptionTypes;
        // ========================= Constructor =========================
        /// @param _messageLibs array of message lib addresses that are granted the MESSAGE_LIB_ROLE
        /// @param _priceFeed price feed address
        /// @param _defaultMultiplierBps default multiplier for worker fee
        /// @param _roleAdmin address that is granted the DEFAULT_ADMIN_ROLE (can grant and revoke all roles)
        /// @param _admins array of admin addresses that are granted the ADMIN_ROLE
        constructor(
            address[] memory _messageLibs,
            address _priceFeed,
            uint16 _defaultMultiplierBps,
            address _roleAdmin,
            address[] memory _admins
        ) {
            defaultMultiplierBps = _defaultMultiplierBps;
            priceFeed = _priceFeed;
            if (_roleAdmin != address(0x0)) {
                _grantRole(DEFAULT_ADMIN_ROLE, _roleAdmin); // _roleAdmin can grant and revoke all roles
            }
            for (uint256 i = 0; i < _messageLibs.length; ++i) {
                _grantRole(MESSAGE_LIB_ROLE, _messageLibs[i]);
            }
            for (uint256 i = 0; i < _admins.length; ++i) {
                _grantRole(ADMIN_ROLE, _admins[i]);
            }
        }
        // ========================= Modifier =========================
        modifier onlyAcl(address _sender) {
            if (!hasAcl(_sender)) {
                revert Worker_NotAllowed();
            }
            _;
        }
        /// @dev Access control list using allowlist and denylist
        /// @dev 1) if one address is in the denylist -> deny
        /// @dev 2) else if address in the allowlist OR allowlist is empty (allows everyone)-> allow
        /// @dev 3) else deny
        /// @param _sender address to check
        function hasAcl(address _sender) public view returns (bool) {
            if (hasRole(DENYLIST, _sender)) {
                return false;
            } else if (allowlistSize == 0 || hasRole(ALLOWLIST, _sender)) {
                return true;
            } else {
                return false;
            }
        }
        // ========================= OnyDefaultAdmin =========================
        /// @dev flag to pause execution of workers (if used with whenNotPaused modifier)
        /// @param _paused true to pause, false to unpause
        function setPaused(bool _paused) external onlyRole(DEFAULT_ADMIN_ROLE) {
            if (_paused) {
                _pause();
            } else {
                _unpause();
            }
        }
        // ========================= OnlyAdmin =========================
        /// @param _priceFeed price feed address
        function setPriceFeed(address _priceFeed) external onlyRole(ADMIN_ROLE) {
            priceFeed = _priceFeed;
            emit SetPriceFeed(_priceFeed);
        }
        /// @param _workerFeeLib worker fee lib address
        function setWorkerFeeLib(address _workerFeeLib) external onlyRole(ADMIN_ROLE) {
            workerFeeLib = _workerFeeLib;
            emit SetWorkerLib(_workerFeeLib);
        }
        /// @param _multiplierBps default multiplier for worker fee
        function setDefaultMultiplierBps(uint16 _multiplierBps) external onlyRole(ADMIN_ROLE) {
            defaultMultiplierBps = _multiplierBps;
            emit SetDefaultMultiplierBps(_multiplierBps);
        }
        /// @dev supports withdrawing fee from ULN301, ULN302 and more
        /// @param _lib message lib address
        /// @param _to address to withdraw fee to
        /// @param _amount amount to withdraw
        function withdrawFee(address _lib, address _to, uint256 _amount) external onlyRole(ADMIN_ROLE) {
            if (!hasRole(MESSAGE_LIB_ROLE, _lib)) revert Worker_OnlyMessageLib();
            ISendLib(_lib).withdrawFee(_to, _amount);
            emit Withdraw(_lib, _to, _amount);
        }
        /// @dev supports withdrawing token from the contract
        /// @param _token token address
        /// @param _to address to withdraw token to
        /// @param _amount amount to withdraw
        function withdrawToken(address _token, address _to, uint256 _amount) external onlyRole(ADMIN_ROLE) {
            // transfers native if _token is address(0x0)
            Transfer.nativeOrToken(_token, _to, _amount);
        }
        function setSupportedOptionTypes(uint32 _eid, uint8[] calldata _optionTypes) external onlyRole(ADMIN_ROLE) {
            supportedOptionTypes[_eid] = _optionTypes;
        }
        // ========================= View Functions =========================
        function getSupportedOptionTypes(uint32 _eid) external view returns (uint8[] memory) {
            return supportedOptionTypes[_eid];
        }
        // ========================= Internal Functions =========================
        /// @dev overrides AccessControl to allow for counting of allowlistSize
        /// @param _role role to grant
        /// @param _account address to grant role to
        function _grantRole(bytes32 _role, address _account) internal override {
            if (_role == ALLOWLIST && !hasRole(_role, _account)) {
                ++allowlistSize;
            }
            super._grantRole(_role, _account);
        }
        /// @dev overrides AccessControl to allow for counting of allowlistSize
        /// @param _role role to revoke
        /// @param _account address to revoke role from
        function _revokeRole(bytes32 _role, address _account) internal override {
            if (_role == ALLOWLIST && hasRole(_role, _account)) {
                --allowlistSize;
            }
            super._revokeRole(_role, _account);
        }
        /// @dev overrides AccessControl to disable renouncing of roles
        function renounceRole(bytes32 /*role*/, address /*account*/) public pure override {
            revert Worker_RoleRenouncingDisabled();
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    interface IWorker {
        event SetWorkerLib(address workerLib);
        event SetPriceFeed(address priceFeed);
        event SetDefaultMultiplierBps(uint16 multiplierBps);
        event SetSupportedOptionTypes(uint32 dstEid, uint8[] optionTypes);
        event Withdraw(address lib, address to, uint256 amount);
        error Worker_NotAllowed();
        error Worker_OnlyMessageLib();
        error Worker_RoleRenouncingDisabled();
        function setPriceFeed(address _priceFeed) external;
        function priceFeed() external view returns (address);
        function setDefaultMultiplierBps(uint16 _multiplierBps) external;
        function defaultMultiplierBps() external view returns (uint16);
        function withdrawFee(address _lib, address _to, uint256 _amount) external;
        function setSupportedOptionTypes(uint32 _eid, uint8[] calldata _optionTypes) external;
        function getSupportedOptionTypes(uint32 _eid) external view returns (uint8[] memory);
    }
    // SPDX-License-Identifier: LZBL-1.2
    pragma solidity ^0.8.20;
    import { ILayerZeroUltraLightNodeV2 } from "@layerzerolabs/lz-evm-v1-0.7/contracts/interfaces/ILayerZeroUltraLightNodeV2.sol";
    import { Worker } from "../../Worker.sol";
    import { MultiSig } from "./MultiSig.sol";
    import { IDVN } from "../interfaces/IDVN.sol";
    import { IDVNFeeLib } from "../interfaces/IDVNFeeLib.sol";
    import { IReceiveUlnE2 } from "../interfaces/IReceiveUlnE2.sol";
    struct ExecuteParam {
        uint32 vid;
        address target;
        bytes callData;
        uint256 expiration;
        bytes signatures;
    }
    contract DVN is Worker, MultiSig, IDVN {
        // to uniquely identify this DVN instance
        // set to endpoint v1 eid if available OR endpoint v2 eid % 30_000
        uint32 public immutable vid;
        mapping(uint32 dstEid => DstConfig) public dstConfig;
        mapping(bytes32 executableHash => bool used) public usedHashes;
        error DVN_OnlySelf();
        error DVN_InvalidRole(bytes32 role);
        error DVN_InstructionExpired();
        error DVN_InvalidTarget(address target);
        error DVN_InvalidVid(uint32 vid);
        error DVN_InvalidSignatures();
        error DVN_DuplicatedHash(bytes32 executableHash);
        event VerifySignaturesFailed(uint256 idx);
        event ExecuteFailed(uint256 _index, bytes _data);
        event HashAlreadyUsed(ExecuteParam param, bytes32 _hash);
        // same as DVNFeePaid, but for ULNv2
        event VerifierFeePaid(uint256 fee);
        // ========================= Constructor =========================
        /// @dev DVN doesn't have a roleAdmin (address(0x0))
        /// @dev Supports all of ULNv2, ULN301, ULN302 and more
        /// @param _vid unique identifier for this DVN instance
        /// @param _messageLibs array of message lib addresses that are granted the MESSAGE_LIB_ROLE
        /// @param _priceFeed price feed address
        /// @param _signers array of signer addresses for multisig
        /// @param _quorum quorum for multisig
        /// @param _admins array of admin addresses that are granted the ADMIN_ROLE
        constructor(
            uint32 _vid,
            address[] memory _messageLibs,
            address _priceFeed,
            address[] memory _signers,
            uint64 _quorum,
            address[] memory _admins
        ) Worker(_messageLibs, _priceFeed, 12000, address(0x0), _admins) MultiSig(_signers, _quorum) {
            vid = _vid;
        }
        // ========================= Modifier =========================
        /// @dev depending on role, restrict access to only self or admin
        /// @dev ALLOWLIST, DENYLIST, MESSAGE_LIB_ROLE can only be granted/revoked by self
        /// @dev ADMIN_ROLE can only be granted/revoked by admin
        /// @dev reverts if not one of the above roles
        /// @param _role role to check
        modifier onlySelfOrAdmin(bytes32 _role) {
            if (_role == ALLOWLIST || _role == DENYLIST || _role == MESSAGE_LIB_ROLE) {
                // self required
                if (address(this) != msg.sender) {
                    revert DVN_OnlySelf();
                }
            } else if (_role == ADMIN_ROLE) {
                // admin required
                _checkRole(ADMIN_ROLE);
            } else {
                revert DVN_InvalidRole(_role);
            }
            _;
        }
        modifier onlySelf() {
            if (address(this) != msg.sender) {
                revert DVN_OnlySelf();
            }
            _;
        }
        // ========================= OnlySelf =========================
        /// @dev set signers for multisig
        /// @dev function sig 0x31cb6105
        /// @param _signer signer address
        /// @param _active true to add, false to remove
        function setSigner(address _signer, bool _active) external onlySelf {
            _setSigner(_signer, _active);
        }
        /// @dev set quorum for multisig
        /// @dev function sig 0x8585c945
        /// @param _quorum to set
        function setQuorum(uint64 _quorum) external onlySelf {
            _setQuorum(_quorum);
        }
        // ========================= OnlySelf / OnlyAdmin =========================
        /// @dev overrides AccessControl to allow self/admin to grant role'
        /// @dev function sig 0x2f2ff15d
        /// @param _role role to grant
        /// @param _account account to grant role to
        function grantRole(bytes32 _role, address _account) public override onlySelfOrAdmin(_role) {
            _grantRole(_role, _account);
        }
        /// @dev overrides AccessControl to allow self/admin to revoke role
        /// @dev function sig 0xd547741f
        /// @param _role role to revoke
        /// @param _account account to revoke role from
        function revokeRole(bytes32 _role, address _account) public override onlySelfOrAdmin(_role) {
            _revokeRole(_role, _account);
        }
        // ========================= OnlyQuorum =========================
        /// @notice function for quorum to change admin without going through execute function
        /// @dev calldata in the case is abi.encode new admin address
        function quorumChangeAdmin(ExecuteParam calldata _param) external {
            if (_param.expiration <= block.timestamp) {
                revert DVN_InstructionExpired();
            }
            if (_param.target != address(this)) {
                revert DVN_InvalidTarget(_param.target);
            }
            if (_param.vid != vid) {
                revert DVN_InvalidVid(_param.vid);
            }
            // generate and validate hash
            bytes32 hash = hashCallData(_param.vid, _param.target, _param.callData, _param.expiration);
            (bool sigsValid, ) = verifySignatures(hash, _param.signatures);
            if (!sigsValid) {
                revert DVN_InvalidSignatures();
            }
            if (usedHashes[hash]) {
                revert DVN_DuplicatedHash(hash);
            }
            usedHashes[hash] = true;
            _grantRole(ADMIN_ROLE, abi.decode(_param.callData, (address)));
        }
        // ========================= OnlyAdmin =========================
        /// @param _params array of DstConfigParam
        function setDstConfig(DstConfigParam[] calldata _params) external onlyRole(ADMIN_ROLE) {
            for (uint256 i = 0; i < _params.length; ++i) {
                DstConfigParam calldata param = _params[i];
                dstConfig[param.dstEid] = DstConfig(param.gas, param.multiplierBps, param.floorMarginUSD);
            }
            emit SetDstConfig(_params);
        }
        /// @dev takes a list of instructions and executes them in order
        /// @dev if any of the instructions fail, it will emit an error event and continue to execute the rest of the instructions
        /// @param _params array of ExecuteParam, includes target, callData, expiration, signatures
        function execute(ExecuteParam[] calldata _params) external onlyRole(ADMIN_ROLE) {
            for (uint256 i = 0; i < _params.length; ++i) {
                ExecuteParam calldata param = _params[i];
                // 1. skip if invalid vid
                if (param.vid != vid) {
                    continue;
                }
                // 2. skip if expired
                if (param.expiration <= block.timestamp) {
                    continue;
                }
                // generate and validate hash
                bytes32 hash = hashCallData(param.vid, param.target, param.callData, param.expiration);
                // 3. check signatures
                (bool sigsValid, ) = verifySignatures(hash, param.signatures);
                if (!sigsValid) {
                    emit VerifySignaturesFailed(i);
                    continue;
                }
                // 4. should check hash
                bool shouldCheckHash = _shouldCheckHash(bytes4(param.callData));
                if (shouldCheckHash) {
                    if (usedHashes[hash]) {
                        emit HashAlreadyUsed(param, hash);
                        continue;
                    } else {
                        usedHashes[hash] = true; // prevent reentry and replay attack
                    }
                }
                (bool success, bytes memory rtnData) = param.target.call(param.callData);
                if (!success) {
                    if (shouldCheckHash) {
                        // need to unset the usedHash otherwise it cant be used
                        usedHashes[hash] = false;
                    }
                    // emit an event in any case
                    emit ExecuteFailed(i, rtnData);
                }
            }
        }
        /// @dev to support ULNv2
        /// @dev the withdrawFee function for ULN30X is built in the Worker contract
        /// @param _lib message lib address
        /// @param _to address to withdraw to
        /// @param _amount amount to withdraw
        function withdrawFeeFromUlnV2(address _lib, address payable _to, uint256 _amount) external onlyRole(ADMIN_ROLE) {
            if (!hasRole(MESSAGE_LIB_ROLE, _lib)) {
                revert Worker_OnlyMessageLib();
            }
            ILayerZeroUltraLightNodeV2(_lib).withdrawNative(_to, _amount);
        }
        // ========================= OnlyMessageLib =========================
        /// @dev for ULN301, ULN302 and more to assign job
        /// @dev dvn network can reject job from _sender by adding/removing them from allowlist/denylist
        /// @param _param assign job param
        /// @param _options dvn options
        function assignJob(
            AssignJobParam calldata _param,
            bytes calldata _options
        ) external payable onlyRole(MESSAGE_LIB_ROLE) onlyAcl(_param.sender) returns (uint256 totalFee) {
            IDVNFeeLib.FeeParams memory feeParams = IDVNFeeLib.FeeParams(
                priceFeed,
                _param.dstEid,
                _param.confirmations,
                _param.sender,
                quorum,
                defaultMultiplierBps
            );
            totalFee = IDVNFeeLib(workerFeeLib).getFeeOnSend(feeParams, dstConfig[_param.dstEid], _options);
        }
        /// @dev to support ULNv2
        /// @dev dvn network can reject job from _sender by adding/removing them from allowlist/denylist
        /// @param _dstEid destination EndpointId
        /// @param //_outboundProofType outbound proof type
        /// @param _confirmations block confirmations
        /// @param _sender message sender address
        function assignJob(
            uint16 _dstEid,
            uint16 /*_outboundProofType*/,
            uint64 _confirmations,
            address _sender
        ) external onlyRole(MESSAGE_LIB_ROLE) onlyAcl(_sender) returns (uint256 totalFee) {
            IDVNFeeLib.FeeParams memory params = IDVNFeeLib.FeeParams(
                priceFeed,
                _dstEid,
                _confirmations,
                _sender,
                quorum,
                defaultMultiplierBps
            );
            // ULNV2 does not have dvn options
            totalFee = IDVNFeeLib(workerFeeLib).getFeeOnSend(params, dstConfig[_dstEid], bytes(""));
            emit VerifierFeePaid(totalFee);
        }
        // ========================= View =========================
        /// @dev getFee can revert if _sender doesn't pass ACL
        /// @param _dstEid destination EndpointId
        /// @param _confirmations block confirmations
        /// @param _sender message sender address
        /// @param _options dvn options
        /// @return fee fee in native amount
        function getFee(
            uint32 _dstEid,
            uint64 _confirmations,
            address _sender,
            bytes calldata _options
        ) external view onlyAcl(_sender) returns (uint256 fee) {
            IDVNFeeLib.FeeParams memory params = IDVNFeeLib.FeeParams(
                priceFeed,
                _dstEid,
                _confirmations,
                _sender,
                quorum,
                defaultMultiplierBps
            );
            return IDVNFeeLib(workerFeeLib).getFee(params, dstConfig[_dstEid], _options);
        }
        /// @dev to support ULNv2
        /// @dev getFee can revert if _sender doesn't pass ACL
        /// @param _dstEid destination EndpointId
        /// @param //_outboundProofType outbound proof type
        /// @param _confirmations block confirmations
        /// @param _sender message sender address
        function getFee(
            uint16 _dstEid,
            uint16 /*_outboundProofType*/,
            uint64 _confirmations,
            address _sender
        ) public view onlyAcl(_sender) returns (uint256 fee) {
            IDVNFeeLib.FeeParams memory params = IDVNFeeLib.FeeParams(
                priceFeed,
                _dstEid,
                _confirmations,
                _sender,
                quorum,
                defaultMultiplierBps
            );
            return IDVNFeeLib(workerFeeLib).getFee(params, dstConfig[_dstEid], bytes(""));
        }
        /// @param _target target address
        /// @param _callData call data
        /// @param _expiration expiration timestamp
        /// @return hash of above
        function hashCallData(
            uint32 _vid,
            address _target,
            bytes calldata _callData,
            uint256 _expiration
        ) public pure returns (bytes32) {
            return keccak256(abi.encodePacked(_vid, _target, _expiration, _callData));
        }
        // ========================= Internal =========================
        /// @dev to save gas, we don't check hash for some functions (where replaying won't change the state)
        /// @dev for example, some administrative functions like changing signers, the contract should check hash to double spending
        /// @dev should ensure that all onlySelf functions have unique functionSig
        /// @param _functionSig function signature
        /// @return true if should check hash
        function _shouldCheckHash(bytes4 _functionSig) internal pure returns (bool) {
            // never check for these selectors to save gas
            return
                _functionSig != IReceiveUlnE2.verify.selector && // 0x0223536e, replaying won't change the state
                _functionSig != ILayerZeroUltraLightNodeV2.updateHash.selector; // 0x704316e5, replaying will be revert at uln
        }
    }
    // SPDX-License-Identifier: LZBL-1.2
    pragma solidity ^0.8.20;
    import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
    abstract contract MultiSig {
        enum Errors {
            NoError,
            SignatureError,
            DuplicatedSigner,
            SignerNotInCommittee
        }
        mapping(address signer => bool active) public signers;
        uint64 public signerSize;
        uint64 public quorum;
        error MultiSig_OnlySigner();
        error MultiSig_QuorumIsZero();
        error MultiSig_SignersSizeIsLessThanQuorum(uint64 signersSize, uint64 quorum);
        error MultiSig_UnorderedSigners();
        error MultiSig_StateAlreadySet(address signer, bool active);
        event UpdateSigner(address _signer, bool _active);
        event UpdateQuorum(uint64 _quorum);
        modifier onlySigner() {
            if (!signers[msg.sender]) {
                revert MultiSig_OnlySigner();
            }
            _;
        }
        constructor(address[] memory _signers, uint64 _quorum) {
            if (_quorum == 0) {
                revert MultiSig_QuorumIsZero();
            }
            if (_signers.length < _quorum) {
                revert MultiSig_SignersSizeIsLessThanQuorum(uint64(_signers.length), _quorum);
            }
            address lastSigner = address(0);
            for (uint256 i = 0; i < _signers.length; i++) {
                address signer = _signers[i];
                if (signer <= lastSigner) {
                    revert MultiSig_UnorderedSigners();
                }
                signers[signer] = true;
                lastSigner = signer;
            }
            signerSize = uint64(_signers.length);
            quorum = _quorum;
        }
        function _setSigner(address _signer, bool _active) internal {
            if (signers[_signer] == _active) {
                revert MultiSig_StateAlreadySet(_signer, _active);
            }
            signers[_signer] = _active;
            uint64 _signerSize = _active ? signerSize + 1 : signerSize - 1;
            uint64 _quorum = quorum;
            if (_signerSize < _quorum) {
                revert MultiSig_SignersSizeIsLessThanQuorum(_signerSize, _quorum);
            }
            signerSize = _signerSize;
            emit UpdateSigner(_signer, _active);
        }
        function _setQuorum(uint64 _quorum) internal {
            if (_quorum == 0) {
                revert MultiSig_QuorumIsZero();
            }
            uint64 _signerSize = signerSize;
            if (_signerSize < _quorum) {
                revert MultiSig_SignersSizeIsLessThanQuorum(_signerSize, _quorum);
            }
            quorum = _quorum;
            emit UpdateQuorum(_quorum);
        }
        function verifySignatures(bytes32 _hash, bytes calldata _signatures) public view returns (bool, Errors) {
            if (_signatures.length != uint256(quorum) * 65) {
                return (false, Errors.SignatureError);
            }
            bytes32 messageDigest = _getEthSignedMessageHash(_hash);
            address lastSigner = address(0); // There cannot be a signer with address 0.
            for (uint256 i = 0; i < quorum; i++) {
                bytes calldata signature = _signatures[i * 65:(i + 1) * 65];
                (address currentSigner, ECDSA.RecoverError error) = ECDSA.tryRecover(messageDigest, signature);
                if (error != ECDSA.RecoverError.NoError) return (false, Errors.SignatureError);
                if (currentSigner <= lastSigner) return (false, Errors.DuplicatedSigner); // prevent duplicate signatures
                if (!signers[currentSigner]) return (false, Errors.SignerNotInCommittee); // signature is not in committee
                lastSigner = currentSigner;
            }
            return (true, Errors.NoError);
        }
        function _getEthSignedMessageHash(bytes32 _messageHash) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
    32", _messageHash));
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    import { IWorker } from "../../interfaces/IWorker.sol";
    import { ILayerZeroDVN } from "./ILayerZeroDVN.sol";
    interface IDVN is IWorker, ILayerZeroDVN {
        struct DstConfigParam {
            uint32 dstEid;
            uint64 gas;
            uint16 multiplierBps;
            uint128 floorMarginUSD;
        }
        struct DstConfig {
            uint64 gas;
            uint16 multiplierBps;
            uint128 floorMarginUSD; // uses priceFeed PRICE_RATIO_DENOMINATOR
        }
        event SetDstConfig(DstConfigParam[] params);
        function dstConfig(uint32 _dstEid) external view returns (uint64, uint16, uint128);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    import { IDVN } from "./IDVN.sol";
    interface IDVNFeeLib {
        struct FeeParams {
            address priceFeed;
            uint32 dstEid;
            uint64 confirmations;
            address sender;
            uint64 quorum;
            uint16 defaultMultiplierBps;
        }
        error DVN_UnsupportedOptionType(uint8 optionType);
        error DVN_EidNotSupported(uint32 eid);
        function getFeeOnSend(
            FeeParams calldata _params,
            IDVN.DstConfig calldata _dstConfig,
            bytes calldata _options
        ) external payable returns (uint256 fee);
        function getFee(
            FeeParams calldata _params,
            IDVN.DstConfig calldata _dstConfig,
            bytes calldata _options
        ) external view returns (uint256 fee);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    interface ILayerZeroDVN {
        struct AssignJobParam {
            uint32 dstEid;
            bytes packetHeader;
            bytes32 payloadHash;
            uint64 confirmations;
            address sender;
        }
        // @notice query price and assign jobs at the same time
        // @param _dstEid - the destination endpoint identifier
        // @param _packetHeader - version + nonce + path
        // @param _payloadHash - hash of guid + message
        // @param _confirmations - block confirmation delay before relaying blocks
        // @param _sender - the source sending contract address
        // @param _options - options
        function assignJob(AssignJobParam calldata _param, bytes calldata _options) external payable returns (uint256 fee);
        // @notice query the dvn fee for relaying block information to the destination chain
        // @param _dstEid the destination endpoint identifier
        // @param _confirmations - block confirmation delay before relaying blocks
        // @param _sender - the source sending contract address
        // @param _options - options
        function getFee(
            uint32 _dstEid,
            uint64 _confirmations,
            address _sender,
            bytes calldata _options
        ) external view returns (uint256 fee);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    /// @dev should be implemented by the ReceiveUln302 contract and future ReceiveUln contracts on EndpointV2
    interface IReceiveUlnE2 {
        /// @notice for each dvn to verify the payload
        /// @dev this function signature 0x0223536e
        function verify(bytes calldata _packetHeader, bytes32 _payloadHash, uint64 _confirmations) external;
        /// @notice verify the payload at endpoint, will check if all DVNs verified
        function commitVerification(bytes calldata _packetHeader, bytes32 _payloadHash) external;
    }
    

    File 2 of 2: ReceiveUln302
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    import { IMessageLibManager } from "./IMessageLibManager.sol";
    import { IMessagingComposer } from "./IMessagingComposer.sol";
    import { IMessagingChannel } from "./IMessagingChannel.sol";
    import { IMessagingContext } from "./IMessagingContext.sol";
    struct MessagingParams {
        uint32 dstEid;
        bytes32 receiver;
        bytes message;
        bytes options;
        bool payInLzToken;
    }
    struct MessagingReceipt {
        bytes32 guid;
        uint64 nonce;
        MessagingFee fee;
    }
    struct MessagingFee {
        uint256 nativeFee;
        uint256 lzTokenFee;
    }
    struct Origin {
        uint32 srcEid;
        bytes32 sender;
        uint64 nonce;
    }
    interface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {
        event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);
        event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);
        event PacketDelivered(Origin origin, address receiver);
        event LzReceiveAlert(
            address indexed receiver,
            address indexed executor,
            Origin origin,
            bytes32 guid,
            uint256 gas,
            uint256 value,
            bytes message,
            bytes extraData,
            bytes reason
        );
        event LzTokenSet(address token);
        event DelegateSet(address sender, address delegate);
        function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);
        function send(
            MessagingParams calldata _params,
            address _refundAddress
        ) external payable returns (MessagingReceipt memory);
        function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;
        function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);
        function initializable(Origin calldata _origin, address _receiver) external view returns (bool);
        function lzReceive(
            Origin calldata _origin,
            address _receiver,
            bytes32 _guid,
            bytes calldata _message,
            bytes calldata _extraData
        ) external payable;
        // oapp can burn messages partially by calling this function with its own business logic if messages are verified in order
        function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;
        function setLzToken(address _lzToken) external;
        function lzToken() external view returns (address);
        function nativeToken() external view returns (address);
        function setDelegate(address _delegate) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
    import { SetConfigParam } from "./IMessageLibManager.sol";
    enum MessageLibType {
        Send,
        Receive,
        SendAndReceive
    }
    interface IMessageLib is IERC165 {
        function setConfig(address _oapp, SetConfigParam[] calldata _config) external;
        function getConfig(uint32 _eid, address _oapp, uint32 _configType) external view returns (bytes memory config);
        function isSupportedEid(uint32 _eid) external view returns (bool);
        // message libs of same major version are compatible
        function version() external view returns (uint64 major, uint8 minor, uint8 endpointVersion);
        function messageLibType() external view returns (MessageLibType);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    struct SetConfigParam {
        uint32 eid;
        uint32 configType;
        bytes config;
    }
    interface IMessageLibManager {
        struct Timeout {
            address lib;
            uint256 expiry;
        }
        event LibraryRegistered(address newLib);
        event DefaultSendLibrarySet(uint32 eid, address newLib);
        event DefaultReceiveLibrarySet(uint32 eid, address newLib);
        event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);
        event SendLibrarySet(address sender, uint32 eid, address newLib);
        event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);
        event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);
        function registerLibrary(address _lib) external;
        function isRegisteredLibrary(address _lib) external view returns (bool);
        function getRegisteredLibraries() external view returns (address[] memory);
        function setDefaultSendLibrary(uint32 _eid, address _newLib) external;
        function defaultSendLibrary(uint32 _eid) external view returns (address);
        function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _timeout) external;
        function defaultReceiveLibrary(uint32 _eid) external view returns (address);
        function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;
        function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);
        function isSupportedEid(uint32 _eid) external view returns (bool);
        function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);
        /// ------------------- OApp interfaces -------------------
        function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;
        function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);
        function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);
        function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;
        function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);
        function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _gracePeriod) external;
        function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);
        function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;
        function getConfig(
            address _oapp,
            address _lib,
            uint32 _eid,
            uint32 _configType
        ) external view returns (bytes memory config);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    interface IMessagingChannel {
        event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);
        event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);
        event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);
        function eid() external view returns (uint32);
        // this is an emergency function if a message cannot be verified for some reasons
        // required to provide _nextNonce to avoid race condition
        function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;
        function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;
        function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;
        function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);
        function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);
        function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);
        function inboundPayloadHash(
            address _receiver,
            uint32 _srcEid,
            bytes32 _sender,
            uint64 _nonce
        ) external view returns (bytes32);
        function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    interface IMessagingComposer {
        event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);
        event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);
        event LzComposeAlert(
            address indexed from,
            address indexed to,
            address indexed executor,
            bytes32 guid,
            uint16 index,
            uint256 gas,
            uint256 value,
            bytes message,
            bytes extraData,
            bytes reason
        );
        function composeQueue(
            address _from,
            address _to,
            bytes32 _guid,
            uint16 _index
        ) external view returns (bytes32 messageHash);
        function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;
        function lzCompose(
            address _from,
            address _to,
            bytes32 _guid,
            uint16 _index,
            bytes calldata _message,
            bytes calldata _extraData
        ) external payable;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    interface IMessagingContext {
        function isSendingMessage() external view returns (bool);
        function getSendContext() external view returns (uint32 dstEid, address sender);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    import { MessagingFee } from "./ILayerZeroEndpointV2.sol";
    import { IMessageLib } from "./IMessageLib.sol";
    struct Packet {
        uint64 nonce;
        uint32 srcEid;
        address sender;
        uint32 dstEid;
        bytes32 receiver;
        bytes32 guid;
        bytes message;
    }
    interface ISendLib is IMessageLib {
        function send(
            Packet calldata _packet,
            bytes calldata _options,
            bool _payInLzToken
        ) external returns (MessagingFee memory, bytes memory encodedPacket);
        function quote(
            Packet calldata _packet,
            bytes calldata _options,
            bool _payInLzToken
        ) external view returns (MessagingFee memory);
        function setTreasury(address _treasury) external;
        function withdrawFee(address _to, uint256 _amount) external;
        function withdrawLzTokenFee(address _lzToken, address _to, uint256 _amount) external;
    }
    // SPDX-License-Identifier: LZBL-1.2
    pragma solidity ^0.8.20;
    library AddressCast {
        error AddressCast_InvalidSizeForAddress();
        error AddressCast_InvalidAddress();
        function toBytes32(bytes calldata _addressBytes) internal pure returns (bytes32 result) {
            if (_addressBytes.length > 32) revert AddressCast_InvalidAddress();
            result = bytes32(_addressBytes);
            unchecked {
                uint256 offset = 32 - _addressBytes.length;
                result = result >> (offset * 8);
            }
        }
        function toBytes32(address _address) internal pure returns (bytes32 result) {
            result = bytes32(uint256(uint160(_address)));
        }
        function toBytes(bytes32 _addressBytes32, uint256 _size) internal pure returns (bytes memory result) {
            if (_size == 0 || _size > 32) revert AddressCast_InvalidSizeForAddress();
            result = new bytes(_size);
            unchecked {
                uint256 offset = 256 - _size * 8;
                assembly {
                    mstore(add(result, 32), shl(offset, _addressBytes32))
                }
            }
        }
        function toAddress(bytes32 _addressBytes32) internal pure returns (address result) {
            result = address(uint160(uint256(_addressBytes32)));
        }
        function toAddress(bytes calldata _addressBytes) internal pure returns (address result) {
            if (_addressBytes.length != 20) revert AddressCast_InvalidAddress();
            result = address(bytes20(_addressBytes));
        }
    }
    // SPDX-License-Identifier: LZBL-1.2
    pragma solidity ^0.8.20;
    import { Packet } from "../../interfaces/ISendLib.sol";
    import { AddressCast } from "../../libs/AddressCast.sol";
    library PacketV1Codec {
        using AddressCast for address;
        using AddressCast for bytes32;
        uint8 internal constant PACKET_VERSION = 1;
        // header (version + nonce + path)
        // version
        uint256 private constant PACKET_VERSION_OFFSET = 0;
        //    nonce
        uint256 private constant NONCE_OFFSET = 1;
        //    path
        uint256 private constant SRC_EID_OFFSET = 9;
        uint256 private constant SENDER_OFFSET = 13;
        uint256 private constant DST_EID_OFFSET = 45;
        uint256 private constant RECEIVER_OFFSET = 49;
        // payload (guid + message)
        uint256 private constant GUID_OFFSET = 81; // keccak256(nonce + path)
        uint256 private constant MESSAGE_OFFSET = 113;
        function encode(Packet memory _packet) internal pure returns (bytes memory encodedPacket) {
            encodedPacket = abi.encodePacked(
                PACKET_VERSION,
                _packet.nonce,
                _packet.srcEid,
                _packet.sender.toBytes32(),
                _packet.dstEid,
                _packet.receiver,
                _packet.guid,
                _packet.message
            );
        }
        function encodePacketHeader(Packet memory _packet) internal pure returns (bytes memory) {
            return
                abi.encodePacked(
                    PACKET_VERSION,
                    _packet.nonce,
                    _packet.srcEid,
                    _packet.sender.toBytes32(),
                    _packet.dstEid,
                    _packet.receiver
                );
        }
        function encodePayload(Packet memory _packet) internal pure returns (bytes memory) {
            return abi.encodePacked(_packet.guid, _packet.message);
        }
        function header(bytes calldata _packet) internal pure returns (bytes calldata) {
            return _packet[0:GUID_OFFSET];
        }
        function version(bytes calldata _packet) internal pure returns (uint8) {
            return uint8(bytes1(_packet[PACKET_VERSION_OFFSET:NONCE_OFFSET]));
        }
        function nonce(bytes calldata _packet) internal pure returns (uint64) {
            return uint64(bytes8(_packet[NONCE_OFFSET:SRC_EID_OFFSET]));
        }
        function srcEid(bytes calldata _packet) internal pure returns (uint32) {
            return uint32(bytes4(_packet[SRC_EID_OFFSET:SENDER_OFFSET]));
        }
        function sender(bytes calldata _packet) internal pure returns (bytes32) {
            return bytes32(_packet[SENDER_OFFSET:DST_EID_OFFSET]);
        }
        function senderAddressB20(bytes calldata _packet) internal pure returns (address) {
            return sender(_packet).toAddress();
        }
        function dstEid(bytes calldata _packet) internal pure returns (uint32) {
            return uint32(bytes4(_packet[DST_EID_OFFSET:RECEIVER_OFFSET]));
        }
        function receiver(bytes calldata _packet) internal pure returns (bytes32) {
            return bytes32(_packet[RECEIVER_OFFSET:GUID_OFFSET]);
        }
        function receiverB20(bytes calldata _packet) internal pure returns (address) {
            return receiver(_packet).toAddress();
        }
        function guid(bytes calldata _packet) internal pure returns (bytes32) {
            return bytes32(_packet[GUID_OFFSET:MESSAGE_OFFSET]);
        }
        function message(bytes calldata _packet) internal pure returns (bytes calldata) {
            return bytes(_packet[MESSAGE_OFFSET:]);
        }
        function payload(bytes calldata _packet) internal pure returns (bytes calldata) {
            return bytes(_packet[GUID_OFFSET:]);
        }
        function payloadHash(bytes calldata _packet) internal pure returns (bytes32) {
            return keccak256(payload(_packet));
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby disabling any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
        function _contextSuffixLength() internal view virtual returns (uint256) {
            return 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
    pragma solidity ^0.8.0;
    import "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 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);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * 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[EIP 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);
    }
    // SPDX-License-Identifier: LZBL-1.2
    pragma solidity ^0.8.20;
    /// @dev simply a container of endpoint address and local eid
    abstract contract MessageLibBase {
        address internal immutable endpoint;
        uint32 internal immutable localEid;
        error LZ_MessageLib_OnlyEndpoint();
        modifier onlyEndpoint() {
            if (endpoint != msg.sender) revert LZ_MessageLib_OnlyEndpoint();
            _;
        }
        constructor(address _endpoint, uint32 _localEid) {
            endpoint = _endpoint;
            localEid = _localEid;
        }
    }
    // SPDX-License-Identifier: LZBL-1.2
    pragma solidity ^0.8.20;
    import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
    import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
    import { ILayerZeroEndpointV2, Origin } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
    import { IMessageLib, MessageLibType } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLib.sol";
    import { PacketV1Codec } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol";
    import { MessageLibBase } from "./MessageLibBase.sol";
    /// @dev receive-side message library base contract on endpoint v2.
    /// it does not have the complication as the one of endpoint v1, such as nonce, executor whitelist, etc.
    abstract contract ReceiveLibBaseE2 is MessageLibBase, ERC165, IMessageLib {
        using PacketV1Codec for bytes;
        constructor(address _endpoint) MessageLibBase(_endpoint, ILayerZeroEndpointV2(_endpoint).eid()) {}
        function supportsInterface(bytes4 _interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
            return _interfaceId == type(IMessageLib).interfaceId || super.supportsInterface(_interfaceId);
        }
        function messageLibType() external pure virtual override returns (MessageLibType) {
            return MessageLibType.Receive;
        }
    }
    // SPDX-License-Identifier: LZBL-1.2
    pragma solidity ^0.8.20;
    import { PacketV1Codec } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol";
    import { UlnBase, UlnConfig } from "./UlnBase.sol";
    struct Verification {
        bool submitted;
        uint64 confirmations;
    }
    /// @dev includes the utility functions for checking ULN states and logics
    abstract contract ReceiveUlnBase is UlnBase {
        using PacketV1Codec for bytes;
        mapping(bytes32 headerHash => mapping(bytes32 payloadHash => mapping(address dvn => Verification)))
            public hashLookup;
        event PayloadVerified(address dvn, bytes header, uint256 confirmations, bytes32 proofHash);
        error LZ_ULN_InvalidPacketHeader();
        error LZ_ULN_InvalidPacketVersion();
        error LZ_ULN_InvalidEid();
        error LZ_ULN_Verifying();
        // ============================ External ===================================
        function verifiable(
            UlnConfig memory _config,
            bytes32 _headerHash,
            bytes32 _payloadHash
        ) external view returns (bool) {
            return _checkVerifiable(_config, _headerHash, _payloadHash);
        }
        function assertHeader(bytes calldata _packetHeader, uint32 _localEid) external pure {
            _assertHeader(_packetHeader, _localEid);
        }
        // ============================ Internal ===================================
        /// @dev per DVN signing function
        function _verify(bytes calldata _packetHeader, bytes32 _payloadHash, uint64 _confirmations) internal {
            hashLookup[keccak256(_packetHeader)][_payloadHash][msg.sender] = Verification(true, _confirmations);
            emit PayloadVerified(msg.sender, _packetHeader, _confirmations, _payloadHash);
        }
        function _verified(
            address _dvn,
            bytes32 _headerHash,
            bytes32 _payloadHash,
            uint64 _requiredConfirmation
        ) internal view returns (bool verified) {
            Verification memory verification = hashLookup[_headerHash][_payloadHash][_dvn];
            // return true if the dvn has signed enough confirmations
            verified = verification.submitted && verification.confirmations >= _requiredConfirmation;
        }
        function _verifyAndReclaimStorage(UlnConfig memory _config, bytes32 _headerHash, bytes32 _payloadHash) internal {
            if (!_checkVerifiable(_config, _headerHash, _payloadHash)) {
                revert LZ_ULN_Verifying();
            }
            // iterate the required DVNs
            if (_config.requiredDVNCount > 0) {
                for (uint8 i = 0; i < _config.requiredDVNCount; ++i) {
                    delete hashLookup[_headerHash][_payloadHash][_config.requiredDVNs[i]];
                }
            }
            // iterate the optional DVNs
            if (_config.optionalDVNCount > 0) {
                for (uint8 i = 0; i < _config.optionalDVNCount; ++i) {
                    delete hashLookup[_headerHash][_payloadHash][_config.optionalDVNs[i]];
                }
            }
        }
        function _assertHeader(bytes calldata _packetHeader, uint32 _localEid) internal pure {
            // assert packet header is of right size 81
            if (_packetHeader.length != 81) revert LZ_ULN_InvalidPacketHeader();
            // assert packet header version is the same as ULN
            if (_packetHeader.version() != PacketV1Codec.PACKET_VERSION) revert LZ_ULN_InvalidPacketVersion();
            // assert the packet is for this endpoint
            if (_packetHeader.dstEid() != _localEid) revert LZ_ULN_InvalidEid();
        }
        /// @dev for verifiable view function
        /// @dev checks if this verification is ready to be committed to the endpoint
        function _checkVerifiable(
            UlnConfig memory _config,
            bytes32 _headerHash,
            bytes32 _payloadHash
        ) internal view returns (bool) {
            // iterate the required DVNs
            if (_config.requiredDVNCount > 0) {
                for (uint8 i = 0; i < _config.requiredDVNCount; ++i) {
                    if (!_verified(_config.requiredDVNs[i], _headerHash, _payloadHash, _config.confirmations)) {
                        // return if any of the required DVNs haven't signed
                        return false;
                    }
                }
                if (_config.optionalDVNCount == 0) {
                    // returns early if all required DVNs have signed and there are no optional DVNs
                    return true;
                }
            }
            // then it must require optional validations
            uint8 threshold = _config.optionalDVNThreshold;
            for (uint8 i = 0; i < _config.optionalDVNCount; ++i) {
                if (_verified(_config.optionalDVNs[i], _headerHash, _payloadHash, _config.confirmations)) {
                    // increment the optional count if the optional DVN has signed
                    threshold--;
                    if (threshold == 0) {
                        // early return if the optional threshold has hit
                        return true;
                    }
                }
            }
            // return false as a catch-all
            return false;
        }
    }
    // SPDX-License-Identifier: LZBL-1.2
    pragma solidity ^0.8.20;
    import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
    // the formal properties are documented in the setter functions
    struct UlnConfig {
        uint64 confirmations;
        // we store the length of required DVNs and optional DVNs instead of using DVN.length directly to save gas
        uint8 requiredDVNCount; // 0 indicate DEFAULT, NIL_DVN_COUNT indicate NONE (to override the value of default)
        uint8 optionalDVNCount; // 0 indicate DEFAULT, NIL_DVN_COUNT indicate NONE (to override the value of default)
        uint8 optionalDVNThreshold; // (0, optionalDVNCount]
        address[] requiredDVNs; // no duplicates. sorted an an ascending order. allowed overlap with optionalDVNs
        address[] optionalDVNs; // no duplicates. sorted an an ascending order. allowed overlap with requiredDVNs
    }
    struct SetDefaultUlnConfigParam {
        uint32 eid;
        UlnConfig config;
    }
    /// @dev includes the utility functions for checking ULN states and logics
    abstract contract UlnBase is Ownable {
        address private constant DEFAULT_CONFIG = address(0);
        // reserved values for
        uint8 internal constant DEFAULT = 0;
        uint8 internal constant NIL_DVN_COUNT = type(uint8).max;
        uint64 internal constant NIL_CONFIRMATIONS = type(uint64).max;
        // 127 to prevent total number of DVNs (127 * 2) exceeding uint8.max (255)
        // by limiting the total size, it would help constraint the design of DVNOptions
        uint8 private constant MAX_COUNT = (type(uint8).max - 1) / 2;
        mapping(address oapp => mapping(uint32 eid => UlnConfig)) internal ulnConfigs;
        error LZ_ULN_Unsorted();
        error LZ_ULN_InvalidRequiredDVNCount();
        error LZ_ULN_InvalidOptionalDVNCount();
        error LZ_ULN_AtLeastOneDVN();
        error LZ_ULN_InvalidOptionalDVNThreshold();
        error LZ_ULN_InvalidConfirmations();
        error LZ_ULN_UnsupportedEid(uint32 eid);
        event DefaultUlnConfigsSet(SetDefaultUlnConfigParam[] params);
        event UlnConfigSet(address oapp, uint32 eid, UlnConfig config);
        // ============================ OnlyOwner ===================================
        /// @dev about the DEFAULT ULN config
        /// 1) its values are all LITERAL (e.g. 0 is 0). whereas in the oapp ULN config, 0 (default value) points to the default ULN config
        ///     this design enables the oapp to point to DEFAULT config without explicitly setting the config
        /// 2) its configuration is more restrictive than the oapp ULN config that
        ///     a) it must not use NIL value, where NIL is used only by oapps to indicate the LITERAL 0
        ///     b) it must have at least one DVN
        function setDefaultUlnConfigs(SetDefaultUlnConfigParam[] calldata _params) external onlyOwner {
            for (uint256 i = 0; i < _params.length; ++i) {
                SetDefaultUlnConfigParam calldata param = _params[i];
                // 2.a must not use NIL
                if (param.config.requiredDVNCount == NIL_DVN_COUNT) revert LZ_ULN_InvalidRequiredDVNCount();
                if (param.config.optionalDVNCount == NIL_DVN_COUNT) revert LZ_ULN_InvalidOptionalDVNCount();
                if (param.config.confirmations == NIL_CONFIRMATIONS) revert LZ_ULN_InvalidConfirmations();
                // 2.b must have at least one dvn
                _assertAtLeastOneDVN(param.config);
                _setConfig(DEFAULT_CONFIG, param.eid, param.config);
            }
            emit DefaultUlnConfigsSet(_params);
        }
        // ============================ View ===================================
        // @dev assuming most oapps use default, we get default as memory and custom as storage to save gas
        function getUlnConfig(address _oapp, uint32 _remoteEid) public view returns (UlnConfig memory rtnConfig) {
            UlnConfig storage defaultConfig = ulnConfigs[DEFAULT_CONFIG][_remoteEid];
            UlnConfig storage customConfig = ulnConfigs[_oapp][_remoteEid];
            // if confirmations is 0, use default
            uint64 confirmations = customConfig.confirmations;
            if (confirmations == DEFAULT) {
                rtnConfig.confirmations = defaultConfig.confirmations;
            } else if (confirmations != NIL_CONFIRMATIONS) {
                // if confirmations is uint64.max, no block confirmations required
                rtnConfig.confirmations = confirmations;
            } // else do nothing, rtnConfig.confirmation is 0
            if (customConfig.requiredDVNCount == DEFAULT) {
                if (defaultConfig.requiredDVNCount > 0) {
                    // copy only if count > 0. save gas
                    rtnConfig.requiredDVNs = defaultConfig.requiredDVNs;
                    rtnConfig.requiredDVNCount = defaultConfig.requiredDVNCount;
                } // else, do nothing
            } else {
                if (customConfig.requiredDVNCount != NIL_DVN_COUNT) {
                    rtnConfig.requiredDVNs = customConfig.requiredDVNs;
                    rtnConfig.requiredDVNCount = customConfig.requiredDVNCount;
                } // else, do nothing
            }
            if (customConfig.optionalDVNCount == DEFAULT) {
                if (defaultConfig.optionalDVNCount > 0) {
                    // copy only if count > 0. save gas
                    rtnConfig.optionalDVNs = defaultConfig.optionalDVNs;
                    rtnConfig.optionalDVNCount = defaultConfig.optionalDVNCount;
                    rtnConfig.optionalDVNThreshold = defaultConfig.optionalDVNThreshold;
                }
            } else {
                if (customConfig.optionalDVNCount != NIL_DVN_COUNT) {
                    rtnConfig.optionalDVNs = customConfig.optionalDVNs;
                    rtnConfig.optionalDVNCount = customConfig.optionalDVNCount;
                    rtnConfig.optionalDVNThreshold = customConfig.optionalDVNThreshold;
                }
            }
            // the final value must have at least one dvn
            // it is possible that some default config result into 0 dvns
            _assertAtLeastOneDVN(rtnConfig);
        }
        /// @dev Get the uln config without the default config for the given remoteEid.
        function getAppUlnConfig(address _oapp, uint32 _remoteEid) external view returns (UlnConfig memory) {
            return ulnConfigs[_oapp][_remoteEid];
        }
        // ============================ Internal ===================================
        function _setUlnConfig(uint32 _remoteEid, address _oapp, UlnConfig memory _param) internal {
            _setConfig(_oapp, _remoteEid, _param);
            // get ULN config again as a catch all to ensure the config is valid
            getUlnConfig(_oapp, _remoteEid);
            emit UlnConfigSet(_oapp, _remoteEid, _param);
        }
        /// @dev a supported Eid must have a valid default uln config, which has at least one dvn
        function _isSupportedEid(uint32 _remoteEid) internal view returns (bool) {
            UlnConfig storage defaultConfig = ulnConfigs[DEFAULT_CONFIG][_remoteEid];
            return defaultConfig.requiredDVNCount > 0 || defaultConfig.optionalDVNThreshold > 0;
        }
        function _assertSupportedEid(uint32 _remoteEid) internal view {
            if (!_isSupportedEid(_remoteEid)) revert LZ_ULN_UnsupportedEid(_remoteEid);
        }
        // ============================ Private ===================================
        function _assertAtLeastOneDVN(UlnConfig memory _config) private pure {
            if (_config.requiredDVNCount == 0 && _config.optionalDVNThreshold == 0) revert LZ_ULN_AtLeastOneDVN();
        }
        /// @dev this private function is used in both setDefaultUlnConfigs and setUlnConfig
        function _setConfig(address _oapp, uint32 _eid, UlnConfig memory _param) private {
            // @dev required dvns
            // if dvnCount == NONE, dvns list must be empty
            // if dvnCount == DEFAULT, dvn list must be empty
            // otherwise, dvnList.length == dvnCount and assert the list is valid
            if (_param.requiredDVNCount == NIL_DVN_COUNT || _param.requiredDVNCount == DEFAULT) {
                if (_param.requiredDVNs.length != 0) revert LZ_ULN_InvalidRequiredDVNCount();
            } else {
                if (_param.requiredDVNs.length != _param.requiredDVNCount || _param.requiredDVNCount > MAX_COUNT)
                    revert LZ_ULN_InvalidRequiredDVNCount();
                _assertNoDuplicates(_param.requiredDVNs);
            }
            // @dev optional dvns
            // if optionalDVNCount == NONE, optionalDVNs list must be empty and threshold must be 0
            // if optionalDVNCount == DEFAULT, optionalDVNs list must be empty and threshold must be 0
            // otherwise, optionalDVNs.length == optionalDVNCount, threshold > 0 && threshold <= optionalDVNCount and assert the list is valid
            // example use case: an oapp uses the DEFAULT 'required' but
            //     a) use a custom 1/1 dvn (practically a required dvn), or
            //     b) use a custom 2/3 dvn
            if (_param.optionalDVNCount == NIL_DVN_COUNT || _param.optionalDVNCount == DEFAULT) {
                if (_param.optionalDVNs.length != 0) revert LZ_ULN_InvalidOptionalDVNCount();
                if (_param.optionalDVNThreshold != 0) revert LZ_ULN_InvalidOptionalDVNThreshold();
            } else {
                if (_param.optionalDVNs.length != _param.optionalDVNCount || _param.optionalDVNCount > MAX_COUNT)
                    revert LZ_ULN_InvalidOptionalDVNCount();
                if (_param.optionalDVNThreshold == 0 || _param.optionalDVNThreshold > _param.optionalDVNCount)
                    revert LZ_ULN_InvalidOptionalDVNThreshold();
                _assertNoDuplicates(_param.optionalDVNs);
            }
            // don't assert valid count here, as it needs to be validated along side default config
            ulnConfigs[_oapp][_eid] = _param;
        }
        function _assertNoDuplicates(address[] memory _dvns) private pure {
            address lastDVN = address(0);
            for (uint256 i = 0; i < _dvns.length; i++) {
                address dvn = _dvns[i];
                if (dvn <= lastDVN) revert LZ_ULN_Unsorted(); // to ensure no duplicates
                lastDVN = dvn;
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    /// @dev should be implemented by the ReceiveUln302 contract and future ReceiveUln contracts on EndpointV2
    interface IReceiveUlnE2 {
        /// @notice for each dvn to verify the payload
        /// @dev this function signature 0x0223536e
        function verify(bytes calldata _packetHeader, bytes32 _payloadHash, uint64 _confirmations) external;
        /// @notice verify the payload at endpoint, will check if all DVNs verified
        function commitVerification(bytes calldata _packetHeader, bytes32 _payloadHash) external;
    }
    // SPDX-License-Identifier: LZBL-1.2
    pragma solidity ^0.8.20;
    import { PacketV1Codec } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol";
    import { SetConfigParam } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLibManager.sol";
    import { ILayerZeroEndpointV2, Origin } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
    import { IReceiveUlnE2 } from "../interfaces/IReceiveUlnE2.sol";
    import { ReceiveUlnBase } from "../ReceiveUlnBase.sol";
    import { ReceiveLibBaseE2 } from "../../ReceiveLibBaseE2.sol";
    import { UlnConfig } from "../UlnBase.sol";
    /// @dev This is a gluing contract. It simply parses the requests and forward to the super.impl() accordingly.
    /// @dev In this case, it combines the logic of ReceiveUlnBase and ReceiveLibBaseE2
    contract ReceiveUln302 is IReceiveUlnE2, ReceiveUlnBase, ReceiveLibBaseE2 {
        using PacketV1Codec for bytes;
        /// @dev CONFIG_TYPE_ULN=2 here to align with SendUln302/ReceiveUln302/ReceiveUln301
        uint32 internal constant CONFIG_TYPE_ULN = 2;
        error LZ_ULN_InvalidConfigType(uint32 configType);
        constructor(address _endpoint) ReceiveLibBaseE2(_endpoint) {}
        function supportsInterface(bytes4 _interfaceId) public view override returns (bool) {
            return _interfaceId == type(IReceiveUlnE2).interfaceId || super.supportsInterface(_interfaceId);
        }
        // ============================ OnlyEndpoint ===================================
        // only the ULN config on the receive side
        function setConfig(address _oapp, SetConfigParam[] calldata _params) external override onlyEndpoint {
            for (uint256 i = 0; i < _params.length; i++) {
                SetConfigParam calldata param = _params[i];
                _assertSupportedEid(param.eid);
                if (param.configType == CONFIG_TYPE_ULN) {
                    _setUlnConfig(param.eid, _oapp, abi.decode(param.config, (UlnConfig)));
                } else {
                    revert LZ_ULN_InvalidConfigType(param.configType);
                }
            }
        }
        // ============================ External ===================================
        /// @dev dont need to check endpoint verifiable here to save gas, as it will reverts if not verifiable.
        function commitVerification(bytes calldata _packetHeader, bytes32 _payloadHash) external {
            _assertHeader(_packetHeader, localEid);
            // cache these values to save gas
            address receiver = _packetHeader.receiverB20();
            uint32 srcEid = _packetHeader.srcEid();
            UlnConfig memory config = getUlnConfig(receiver, srcEid);
            _verifyAndReclaimStorage(config, keccak256(_packetHeader), _payloadHash);
            Origin memory origin = Origin(srcEid, _packetHeader.sender(), _packetHeader.nonce());
            // endpoint will revert if nonce <= lazyInboundNonce
            ILayerZeroEndpointV2(endpoint).verify(origin, receiver, _payloadHash);
        }
        /// @dev for dvn to verify the payload
        function verify(bytes calldata _packetHeader, bytes32 _payloadHash, uint64 _confirmations) external {
            _verify(_packetHeader, _payloadHash, _confirmations);
        }
        // ============================ View ===================================
        function getConfig(uint32 _eid, address _oapp, uint32 _configType) external view override returns (bytes memory) {
            if (_configType == CONFIG_TYPE_ULN) {
                return abi.encode(getUlnConfig(_oapp, _eid));
            } else {
                revert LZ_ULN_InvalidConfigType(_configType);
            }
        }
        function isSupportedEid(uint32 _eid) external view override returns (bool) {
            return _isSupportedEid(_eid);
        }
        function version() external pure override returns (uint64 major, uint8 minor, uint8 endpointVersion) {
            return (3, 0, 2);
        }
    }