ETH Price: $1,871.46 (-2.06%)

Transaction Decoder

Block:
22029778 at Mar-12-2025 09:05:35 AM +UTC
Transaction Fee:
0.0000500080882336 ETH $0.09
Gas Used:
51,380 Gas / 0.97329872 Gwei

Emitted Events:

576 TransparentUpgradeableProxy.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x0000000000000000000000009d24d011bd34a2323c3a29786ca5e69d15192891, 0x000000000000000000000000111111125421ca6dc452d289314280a0f8842a65, 000000000000000000000000000000000000000000000000000000000000558c )

Account State Difference:

  Address   Before After State Difference Code
4.936728877825610242 Eth4.936730419225610242 Eth0.0000015414
0x8236a870...8A5634494
0x9d24D011...D15192891
0.001629939199125719 Eth
Nonce: 16
0.001579931110892119 Eth
Nonce: 17
0.0000500080882336

Execution Trace

TransparentUpgradeableProxy.095ea7b3( )
  • LBTC.approve( spender=0x111111125421cA6dc452d289314280a0f8842A65, value=21900 ) => ( True )
    File 1 of 2: TransparentUpgradeableProxy
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
    pragma solidity ^0.8.20;
    import {Context} from "../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.
     *
     * The initial owner is set to the address provided by the deployer. 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;
        /**
         * @dev The caller account is not authorized to perform an operation.
         */
        error OwnableUnauthorizedAccount(address account);
        /**
         * @dev The owner is not a valid owner account. (eg. `address(0)`)
         */
        error OwnableInvalidOwner(address owner);
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
         */
        constructor(address initialOwner) {
            if (initialOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _transferOwnership(initialOwner);
        }
        /**
         * @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 {
            if (owner() != _msgSender()) {
                revert OwnableUnauthorizedAccount(_msgSender());
            }
        }
        /**
         * @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 {
            if (newOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _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 v5.0.0) (interfaces/IERC1967.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
     */
    interface IERC1967 {
        /**
         * @dev Emitted when the implementation is upgraded.
         */
        event Upgraded(address indexed implementation);
        /**
         * @dev Emitted when the admin account has changed.
         */
        event AdminChanged(address previousAdmin, address newAdmin);
        /**
         * @dev Emitted when the beacon is changed.
         */
        event BeaconUpgraded(address indexed beacon);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/BeaconProxy.sol)
    pragma solidity ^0.8.20;
    import {IBeacon} from "./IBeacon.sol";
    import {Proxy} from "../Proxy.sol";
    import {ERC1967Utils} from "../ERC1967/ERC1967Utils.sol";
    /**
     * @dev This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}.
     *
     * The beacon address can only be set once during construction, and cannot be changed afterwards. It is stored in an
     * immutable variable to avoid unnecessary storage reads, and also in the beacon storage slot specified by
     * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] so that it can be accessed externally.
     *
     * CAUTION: Since the beacon address can never be changed, you must ensure that you either control the beacon, or trust
     * the beacon to not upgrade the implementation maliciously.
     *
     * IMPORTANT: Do not use the implementation logic to modify the beacon storage slot. Doing so would leave the proxy in
     * an inconsistent state where the beacon storage slot does not match the beacon address.
     */
    contract BeaconProxy is Proxy {
        // An immutable address for the beacon to avoid unnecessary SLOADs before each delegate call.
        address private immutable _beacon;
        /**
         * @dev Initializes the proxy with `beacon`.
         *
         * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This
         * will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity
         * constructor.
         *
         * Requirements:
         *
         * - `beacon` must be a contract with the interface {IBeacon}.
         * - If `data` is empty, `msg.value` must be zero.
         */
        constructor(address beacon, bytes memory data) payable {
            ERC1967Utils.upgradeBeaconToAndCall(beacon, data);
            _beacon = beacon;
        }
        /**
         * @dev Returns the current implementation address of the associated beacon.
         */
        function _implementation() internal view virtual override returns (address) {
            return IBeacon(_getBeacon()).implementation();
        }
        /**
         * @dev Returns the beacon.
         */
        function _getBeacon() internal view virtual returns (address) {
            return _beacon;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev This is the interface that {BeaconProxy} expects of its beacon.
     */
    interface IBeacon {
        /**
         * @dev Must return an address that can be used as a delegate call target.
         *
         * {UpgradeableBeacon} will check that this address is a contract.
         */
        function implementation() external view returns (address);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/UpgradeableBeacon.sol)
    pragma solidity ^0.8.20;
    import {IBeacon} from "./IBeacon.sol";
    import {Ownable} from "../../access/Ownable.sol";
    /**
     * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their
     * implementation contract, which is where they will delegate all function calls.
     *
     * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.
     */
    contract UpgradeableBeacon is IBeacon, Ownable {
        address private _implementation;
        /**
         * @dev The `implementation` of the beacon is invalid.
         */
        error BeaconInvalidImplementation(address implementation);
        /**
         * @dev Emitted when the implementation returned by the beacon is changed.
         */
        event Upgraded(address indexed implementation);
        /**
         * @dev Sets the address of the initial implementation, and the initial owner who can upgrade the beacon.
         */
        constructor(address implementation_, address initialOwner) Ownable(initialOwner) {
            _setImplementation(implementation_);
        }
        /**
         * @dev Returns the current implementation address.
         */
        function implementation() public view virtual returns (address) {
            return _implementation;
        }
        /**
         * @dev Upgrades the beacon to a new implementation.
         *
         * Emits an {Upgraded} event.
         *
         * Requirements:
         *
         * - msg.sender must be the owner of the contract.
         * - `newImplementation` must be a contract.
         */
        function upgradeTo(address newImplementation) public virtual onlyOwner {
            _setImplementation(newImplementation);
        }
        /**
         * @dev Sets the implementation contract address for this beacon
         *
         * Requirements:
         *
         * - `newImplementation` must be a contract.
         */
        function _setImplementation(address newImplementation) private {
            if (newImplementation.code.length == 0) {
                revert BeaconInvalidImplementation(newImplementation);
            }
            _implementation = newImplementation;
            emit Upgraded(newImplementation);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Proxy.sol)
    pragma solidity ^0.8.20;
    import {Proxy} from "../Proxy.sol";
    import {ERC1967Utils} from "./ERC1967Utils.sol";
    /**
     * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
     * implementation address that can be changed. This address is stored in storage in the location specified by
     * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
     * implementation behind the proxy.
     */
    contract ERC1967Proxy is Proxy {
        /**
         * @dev Initializes the upgradeable proxy with an initial implementation specified by `implementation`.
         *
         * If `_data` is nonempty, it's used as data in a delegate call to `implementation`. This will typically be an
         * encoded function call, and allows initializing the storage of the proxy like a Solidity constructor.
         *
         * Requirements:
         *
         * - If `data` is empty, `msg.value` must be zero.
         */
        constructor(address implementation, bytes memory _data) payable {
            ERC1967Utils.upgradeToAndCall(implementation, _data);
        }
        /**
         * @dev Returns the current implementation address.
         *
         * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
         * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
         * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
         */
        function _implementation() internal view virtual override returns (address) {
            return ERC1967Utils.getImplementation();
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
    pragma solidity ^0.8.20;
    import {IBeacon} from "../beacon/IBeacon.sol";
    import {Address} from "../../utils/Address.sol";
    import {StorageSlot} from "../../utils/StorageSlot.sol";
    /**
     * @dev This abstract contract provides getters and event emitting update functions for
     * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
     */
    library ERC1967Utils {
        // We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
        // This will be fixed in Solidity 0.8.21. At that point we should remove these events.
        /**
         * @dev Emitted when the implementation is upgraded.
         */
        event Upgraded(address indexed implementation);
        /**
         * @dev Emitted when the admin account has changed.
         */
        event AdminChanged(address previousAdmin, address newAdmin);
        /**
         * @dev Emitted when the beacon is changed.
         */
        event BeaconUpgraded(address indexed beacon);
        /**
         * @dev Storage slot with the address of the current implementation.
         * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
         */
        // solhint-disable-next-line private-vars-leading-underscore
        bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
        /**
         * @dev The `implementation` of the proxy is invalid.
         */
        error ERC1967InvalidImplementation(address implementation);
        /**
         * @dev The `admin` of the proxy is invalid.
         */
        error ERC1967InvalidAdmin(address admin);
        /**
         * @dev The `beacon` of the proxy is invalid.
         */
        error ERC1967InvalidBeacon(address beacon);
        /**
         * @dev An upgrade function sees `msg.value > 0` that may be lost.
         */
        error ERC1967NonPayable();
        /**
         * @dev Returns the current implementation address.
         */
        function getImplementation() internal view returns (address) {
            return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
        }
        /**
         * @dev Stores a new address in the EIP1967 implementation slot.
         */
        function _setImplementation(address newImplementation) private {
            if (newImplementation.code.length == 0) {
                revert ERC1967InvalidImplementation(newImplementation);
            }
            StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
        }
        /**
         * @dev Performs implementation upgrade with additional setup call if data is nonempty.
         * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
         * to avoid stuck value in the contract.
         *
         * Emits an {IERC1967-Upgraded} event.
         */
        function upgradeToAndCall(address newImplementation, bytes memory data) internal {
            _setImplementation(newImplementation);
            emit Upgraded(newImplementation);
            if (data.length > 0) {
                Address.functionDelegateCall(newImplementation, data);
            } else {
                _checkNonPayable();
            }
        }
        /**
         * @dev Storage slot with the admin of the contract.
         * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
         */
        // solhint-disable-next-line private-vars-leading-underscore
        bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
        /**
         * @dev Returns the current admin.
         *
         * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
         * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
         * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
         */
        function getAdmin() internal view returns (address) {
            return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
        }
        /**
         * @dev Stores a new address in the EIP1967 admin slot.
         */
        function _setAdmin(address newAdmin) private {
            if (newAdmin == address(0)) {
                revert ERC1967InvalidAdmin(address(0));
            }
            StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
        }
        /**
         * @dev Changes the admin of the proxy.
         *
         * Emits an {IERC1967-AdminChanged} event.
         */
        function changeAdmin(address newAdmin) internal {
            emit AdminChanged(getAdmin(), newAdmin);
            _setAdmin(newAdmin);
        }
        /**
         * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
         * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
         */
        // solhint-disable-next-line private-vars-leading-underscore
        bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
        /**
         * @dev Returns the current beacon.
         */
        function getBeacon() internal view returns (address) {
            return StorageSlot.getAddressSlot(BEACON_SLOT).value;
        }
        /**
         * @dev Stores a new beacon in the EIP1967 beacon slot.
         */
        function _setBeacon(address newBeacon) private {
            if (newBeacon.code.length == 0) {
                revert ERC1967InvalidBeacon(newBeacon);
            }
            StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
            address beaconImplementation = IBeacon(newBeacon).implementation();
            if (beaconImplementation.code.length == 0) {
                revert ERC1967InvalidImplementation(beaconImplementation);
            }
        }
        /**
         * @dev Change the beacon and trigger a setup call if data is nonempty.
         * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
         * to avoid stuck value in the contract.
         *
         * Emits an {IERC1967-BeaconUpgraded} event.
         *
         * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
         * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
         * efficiency.
         */
        function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
            _setBeacon(newBeacon);
            emit BeaconUpgraded(newBeacon);
            if (data.length > 0) {
                Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
            } else {
                _checkNonPayable();
            }
        }
        /**
         * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
         * if an upgrade doesn't perform an initialization call.
         */
        function _checkNonPayable() private {
            if (msg.value > 0) {
                revert ERC1967NonPayable();
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (proxy/Proxy.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
     * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
     * be specified by overriding the virtual {_implementation} function.
     *
     * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
     * different contract through the {_delegate} function.
     *
     * The success and return data of the delegated call will be returned back to the caller of the proxy.
     */
    abstract contract Proxy {
        /**
         * @dev Delegates the current call to `implementation`.
         *
         * This function does not return to its internal call site, it will return directly to the external caller.
         */
        function _delegate(address implementation) internal virtual {
            assembly {
                // Copy msg.data. We take full control of memory in this inline assembly
                // block because it will not return to Solidity code. We overwrite the
                // Solidity scratch pad at memory position 0.
                calldatacopy(0, 0, calldatasize())
                // Call the implementation.
                // out and outsize are 0 because we don't know the size yet.
                let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                // Copy the returned data.
                returndatacopy(0, 0, returndatasize())
                switch result
                // delegatecall returns 0 on error.
                case 0 {
                    revert(0, returndatasize())
                }
                default {
                    return(0, returndatasize())
                }
            }
        }
        /**
         * @dev This is a virtual function that should be overridden so it returns the address to which the fallback
         * function and {_fallback} should delegate.
         */
        function _implementation() internal view virtual returns (address);
        /**
         * @dev Delegates the current call to the address returned by `_implementation()`.
         *
         * This function does not return to its internal call site, it will return directly to the external caller.
         */
        function _fallback() internal virtual {
            _delegate(_implementation());
        }
        /**
         * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
         * function in the contract matches the call data.
         */
        fallback() external payable virtual {
            _fallback();
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (proxy/transparent/ProxyAdmin.sol)
    pragma solidity ^0.8.20;
    import {ITransparentUpgradeableProxy} from "./TransparentUpgradeableProxy.sol";
    import {Ownable} from "../../access/Ownable.sol";
    /**
     * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
     * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
     */
    contract ProxyAdmin is Ownable {
        /**
         * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgrade(address)`
         * and `upgradeAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
         * while `upgradeAndCall` will invoke the `receive` function if the second argument is the empty byte string.
         * If the getter returns `"5.0.0"`, only `upgradeAndCall(address,bytes)` is present, and the second argument must
         * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
         * during an upgrade.
         */
        string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
        /**
         * @dev Sets the initial owner who can perform upgrades.
         */
        constructor(address initialOwner) Ownable(initialOwner) {}
        /**
         * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation.
         * See {TransparentUpgradeableProxy-_dispatchUpgradeToAndCall}.
         *
         * Requirements:
         *
         * - This contract must be the admin of `proxy`.
         * - If `data` is empty, `msg.value` must be zero.
         */
        function upgradeAndCall(
            ITransparentUpgradeableProxy proxy,
            address implementation,
            bytes memory data
        ) public payable virtual onlyOwner {
            proxy.upgradeToAndCall{value: msg.value}(implementation, data);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (proxy/transparent/TransparentUpgradeableProxy.sol)
    pragma solidity ^0.8.20;
    import {ERC1967Utils} from "../ERC1967/ERC1967Utils.sol";
    import {ERC1967Proxy} from "../ERC1967/ERC1967Proxy.sol";
    import {IERC1967} from "../../interfaces/IERC1967.sol";
    import {ProxyAdmin} from "./ProxyAdmin.sol";
    /**
     * @dev Interface for {TransparentUpgradeableProxy}. In order to implement transparency, {TransparentUpgradeableProxy}
     * does not implement this interface directly, and its upgradeability mechanism is implemented by an internal dispatch
     * mechanism. The compiler is unaware that these functions are implemented by {TransparentUpgradeableProxy} and will not
     * include them in the ABI so this interface must be used to interact with it.
     */
    interface ITransparentUpgradeableProxy is IERC1967 {
        function upgradeToAndCall(address, bytes calldata) external payable;
    }
    /**
     * @dev This contract implements a proxy that is upgradeable through an associated {ProxyAdmin} instance.
     *
     * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
     * clashing], which can potentially be used in an attack, this contract uses the
     * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
     * things that go hand in hand:
     *
     * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
     * that call matches the {ITransparentUpgradeableProxy-upgradeToAndCall} function exposed by the proxy itself.
     * 2. If the admin calls the proxy, it can call the `upgradeToAndCall` function but any other call won't be forwarded to
     * the implementation. If the admin tries to call a function on the implementation it will fail with an error indicating
     * the proxy admin cannot fallback to the target implementation.
     *
     * These properties mean that the admin account can only be used for upgrading the proxy, so it's best if it's a
     * dedicated account that is not used for anything else. This will avoid headaches due to sudden errors when trying to
     * call a function from the proxy implementation. For this reason, the proxy deploys an instance of {ProxyAdmin} and
     * allows upgrades only if they come through it. You should think of the `ProxyAdmin` instance as the administrative
     * interface of the proxy, including the ability to change who can trigger upgrades by transferring ownership.
     *
     * NOTE: The real interface of this proxy is that defined in `ITransparentUpgradeableProxy`. This contract does not
     * inherit from that interface, and instead `upgradeToAndCall` is implicitly implemented using a custom dispatch
     * mechanism in `_fallback`. Consequently, the compiler will not produce an ABI for this contract. This is necessary to
     * fully implement transparency without decoding reverts caused by selector clashes between the proxy and the
     * implementation.
     *
     * NOTE: This proxy does not inherit from {Context} deliberately. The {ProxyAdmin} of this contract won't send a
     * meta-transaction in any way, and any other meta-transaction setup should be made in the implementation contract.
     *
     * IMPORTANT: This contract avoids unnecessary storage reads by setting the admin only during construction as an
     * immutable variable, preventing any changes thereafter. However, the admin slot defined in ERC-1967 can still be
     * overwritten by the implementation logic pointed to by this proxy. In such cases, the contract may end up in an
     * undesirable state where the admin slot is different from the actual admin.
     *
     * WARNING: It is not recommended to extend this contract to add additional external functions. If you do so, the
     * compiler will not check that there are no selector conflicts, due to the note above. A selector clash between any new
     * function and the functions declared in {ITransparentUpgradeableProxy} will be resolved in favor of the new one. This
     * could render the `upgradeToAndCall` function inaccessible, preventing upgradeability and compromising transparency.
     */
    contract TransparentUpgradeableProxy is ERC1967Proxy {
        // An immutable address for the admin to avoid unnecessary SLOADs before each call
        // at the expense of removing the ability to change the admin once it's set.
        // This is acceptable if the admin is always a ProxyAdmin instance or similar contract
        // with its own ability to transfer the permissions to another account.
        address private immutable _admin;
        /**
         * @dev The proxy caller is the current admin, and can't fallback to the proxy target.
         */
        error ProxyDeniedAdminAccess();
        /**
         * @dev Initializes an upgradeable proxy managed by an instance of a {ProxyAdmin} with an `initialOwner`,
         * backed by the implementation at `_logic`, and optionally initialized with `_data` as explained in
         * {ERC1967Proxy-constructor}.
         */
        constructor(address _logic, address initialOwner, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
            _admin = address(new ProxyAdmin(initialOwner));
            // Set the storage value and emit an event for ERC-1967 compatibility
            ERC1967Utils.changeAdmin(_proxyAdmin());
        }
        /**
         * @dev Returns the admin of this proxy.
         */
        function _proxyAdmin() internal virtual returns (address) {
            return _admin;
        }
        /**
         * @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior.
         */
        function _fallback() internal virtual override {
            if (msg.sender == _proxyAdmin()) {
                if (msg.sig != ITransparentUpgradeableProxy.upgradeToAndCall.selector) {
                    revert ProxyDeniedAdminAccess();
                } else {
                    _dispatchUpgradeToAndCall();
                }
            } else {
                super._fallback();
            }
        }
        /**
         * @dev Upgrade the implementation of the proxy. See {ERC1967Utils-upgradeToAndCall}.
         *
         * Requirements:
         *
         * - If `data` is empty, `msg.value` must be zero.
         */
        function _dispatchUpgradeToAndCall() private {
            (address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes));
            ERC1967Utils.upgradeToAndCall(newImplementation, data);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev The ETH balance of the account is not enough to perform the operation.
         */
        error AddressInsufficientBalance(address account);
        /**
         * @dev There's no code at `target` (it is not a contract).
         */
        error AddressEmptyCode(address target);
        /**
         * @dev A call to an address target failed. The target may have reverted.
         */
        error FailedInnerCall();
        /**
         * @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            if (address(this).balance < amount) {
                revert AddressInsufficientBalance(address(this));
            }
            (bool success, ) = recipient.call{value: amount}("");
            if (!success) {
                revert FailedInnerCall();
            }
        }
        /**
         * @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 or custom error, it is bubbled
         * up by this function (like regular Solidity function calls). However, if
         * the call reverted with no returned reason, this function reverts with a
         * {FailedInnerCall} error.
         *
         * 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.
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0);
        }
        /**
         * @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`.
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
            if (address(this).balance < value) {
                revert AddressInsufficientBalance(address(this));
            }
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
        /**
         * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
         * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
         * unsuccessful call.
         */
        function verifyCallResultFromTarget(
            address target,
            bool success,
            bytes memory returndata
        ) internal view returns (bytes memory) {
            if (!success) {
                _revert(returndata);
            } else {
                // only check if target is a contract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                if (returndata.length == 0 && target.code.length == 0) {
                    revert AddressEmptyCode(target);
                }
                return returndata;
            }
        }
        /**
         * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
         * revert reason or with a default {FailedInnerCall} error.
         */
        function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
            if (!success) {
                _revert(returndata);
            } else {
                return returndata;
            }
        }
        /**
         * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
         */
        function _revert(bytes memory returndata) 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 FailedInnerCall();
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
        function _contextSuffixLength() internal view virtual returns (uint256) {
            return 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
    // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
    pragma solidity ^0.8.20;
    /**
     * @dev Library for reading and writing primitive types to specific storage slots.
     *
     * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
     * This library helps with reading and writing to such slots without the need for inline assembly.
     *
     * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
     *
     * Example usage to set ERC1967 implementation slot:
     * ```solidity
     * contract ERC1967 {
     *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
     *
     *     function _getImplementation() internal view returns (address) {
     *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
     *     }
     *
     *     function _setImplementation(address newImplementation) internal {
     *         require(newImplementation.code.length > 0);
     *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
     *     }
     * }
     * ```
     */
    library StorageSlot {
        struct AddressSlot {
            address value;
        }
        struct BooleanSlot {
            bool value;
        }
        struct Bytes32Slot {
            bytes32 value;
        }
        struct Uint256Slot {
            uint256 value;
        }
        struct StringSlot {
            string value;
        }
        struct BytesSlot {
            bytes value;
        }
        /**
         * @dev Returns an `AddressSlot` with member `value` located at `slot`.
         */
        function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
         */
        function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
         */
        function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
         */
        function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `StringSlot` with member `value` located at `slot`.
         */
        function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
         */
        function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := store.slot
            }
        }
        /**
         * @dev Returns an `BytesSlot` with member `value` located at `slot`.
         */
        function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
         */
        function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := store.slot
            }
        }
    }
    

    File 2 of 2: LBTC
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)
    pragma solidity ^0.8.20;
    import {OwnableUpgradeable} from "./OwnableUpgradeable.sol";
    import {Initializable} from "../proxy/utils/Initializable.sol";
    /**
     * @dev Contract module which provides access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * The initial owner is specified at deployment time in the constructor for `Ownable`. This
     * can later be changed with {transferOwnership} and {acceptOwnership}.
     *
     * This module is used through inheritance. It will make available all functions
     * from parent (Ownable).
     */
    abstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {
        /// @custom:storage-location erc7201:openzeppelin.storage.Ownable2Step
        struct Ownable2StepStorage {
            address _pendingOwner;
        }
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable2Step")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 private constant Ownable2StepStorageLocation = 0x237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c00;
        function _getOwnable2StepStorage() private pure returns (Ownable2StepStorage storage $) {
            assembly {
                $.slot := Ownable2StepStorageLocation
            }
        }
        event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
        function __Ownable2Step_init() internal onlyInitializing {
        }
        function __Ownable2Step_init_unchained() internal onlyInitializing {
        }
        /**
         * @dev Returns the address of the pending owner.
         */
        function pendingOwner() public view virtual returns (address) {
            Ownable2StepStorage storage $ = _getOwnable2StepStorage();
            return $._pendingOwner;
        }
        /**
         * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual override onlyOwner {
            Ownable2StepStorage storage $ = _getOwnable2StepStorage();
            $._pendingOwner = newOwner;
            emit OwnershipTransferStarted(owner(), newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual override {
            Ownable2StepStorage storage $ = _getOwnable2StepStorage();
            delete $._pendingOwner;
            super._transferOwnership(newOwner);
        }
        /**
         * @dev The new owner accepts the ownership transfer.
         */
        function acceptOwnership() public virtual {
            address sender = _msgSender();
            if (pendingOwner() != sender) {
                revert OwnableUnauthorizedAccount(sender);
            }
            _transferOwnership(sender);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
    pragma solidity ^0.8.20;
    import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
    import {Initializable} from "../proxy/utils/Initializable.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.
     *
     * The initial owner is set to the address provided by the deployer. 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 OwnableUpgradeable is Initializable, ContextUpgradeable {
        /// @custom:storage-location erc7201:openzeppelin.storage.Ownable
        struct OwnableStorage {
            address _owner;
        }
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
        function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
            assembly {
                $.slot := OwnableStorageLocation
            }
        }
        /**
         * @dev The caller account is not authorized to perform an operation.
         */
        error OwnableUnauthorizedAccount(address account);
        /**
         * @dev The owner is not a valid owner account. (eg. `address(0)`)
         */
        error OwnableInvalidOwner(address owner);
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
         */
        function __Ownable_init(address initialOwner) internal onlyInitializing {
            __Ownable_init_unchained(initialOwner);
        }
        function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
            if (initialOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _transferOwnership(initialOwner);
        }
        /**
         * @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) {
            OwnableStorage storage $ = _getOwnableStorage();
            return $._owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            if (owner() != _msgSender()) {
                revert OwnableUnauthorizedAccount(_msgSender());
            }
        }
        /**
         * @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 {
            if (newOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            OwnableStorage storage $ = _getOwnableStorage();
            address oldOwner = $._owner;
            $._owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
     * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
     * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
     * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
     *
     * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
     * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
     * case an upgrade adds a module that needs to be initialized.
     *
     * For example:
     *
     * [.hljs-theme-light.nopadding]
     * ```solidity
     * contract MyToken is ERC20Upgradeable {
     *     function initialize() initializer public {
     *         __ERC20_init("MyToken", "MTK");
     *     }
     * }
     *
     * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
     *     function initializeV2() reinitializer(2) public {
     *         __ERC20Permit_init("MyToken");
     *     }
     * }
     * ```
     *
     * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
     * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
     *
     * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
     * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
     *
     * [CAUTION]
     * ====
     * Avoid leaving a contract uninitialized.
     *
     * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
     * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
     * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
     *
     * [.hljs-theme-light.nopadding]
     * ```
     * /// @custom:oz-upgrades-unsafe-allow constructor
     * constructor() {
     *     _disableInitializers();
     * }
     * ```
     * ====
     */
    abstract contract Initializable {
        /**
         * @dev Storage of the initializable contract.
         *
         * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
         * when using with upgradeable contracts.
         *
         * @custom:storage-location erc7201:openzeppelin.storage.Initializable
         */
        struct InitializableStorage {
            /**
             * @dev Indicates that the contract has been initialized.
             */
            uint64 _initialized;
            /**
             * @dev Indicates that the contract is in the process of being initialized.
             */
            bool _initializing;
        }
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
        /**
         * @dev The contract is already initialized.
         */
        error InvalidInitialization();
        /**
         * @dev The contract is not initializing.
         */
        error NotInitializing();
        /**
         * @dev Triggered when the contract has been initialized or reinitialized.
         */
        event Initialized(uint64 version);
        /**
         * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
         * `onlyInitializing` functions can be used to initialize parent contracts.
         *
         * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
         * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
         * production.
         *
         * Emits an {Initialized} event.
         */
        modifier initializer() {
            // solhint-disable-next-line var-name-mixedcase
            InitializableStorage storage $ = _getInitializableStorage();
            // Cache values to avoid duplicated sloads
            bool isTopLevelCall = !$._initializing;
            uint64 initialized = $._initialized;
            // Allowed calls:
            // - initialSetup: the contract is not in the initializing state and no previous version was
            //                 initialized
            // - construction: the contract is initialized at version 1 (no reininitialization) and the
            //                 current contract is just being deployed
            bool initialSetup = initialized == 0 && isTopLevelCall;
            bool construction = initialized == 1 && address(this).code.length == 0;
            if (!initialSetup && !construction) {
                revert InvalidInitialization();
            }
            $._initialized = 1;
            if (isTopLevelCall) {
                $._initializing = true;
            }
            _;
            if (isTopLevelCall) {
                $._initializing = false;
                emit Initialized(1);
            }
        }
        /**
         * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
         * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
         * used to initialize parent contracts.
         *
         * A reinitializer may be used after the original initialization step. This is essential to configure modules that
         * are added through upgrades and that require initialization.
         *
         * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
         * cannot be nested. If one is invoked in the context of another, execution will revert.
         *
         * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
         * a contract, executing them in the right order is up to the developer or operator.
         *
         * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
         *
         * Emits an {Initialized} event.
         */
        modifier reinitializer(uint64 version) {
            // solhint-disable-next-line var-name-mixedcase
            InitializableStorage storage $ = _getInitializableStorage();
            if ($._initializing || $._initialized >= version) {
                revert InvalidInitialization();
            }
            $._initialized = version;
            $._initializing = true;
            _;
            $._initializing = false;
            emit Initialized(version);
        }
        /**
         * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
         * {initializer} and {reinitializer} modifiers, directly or indirectly.
         */
        modifier onlyInitializing() {
            _checkInitializing();
            _;
        }
        /**
         * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
         */
        function _checkInitializing() internal view virtual {
            if (!_isInitializing()) {
                revert NotInitializing();
            }
        }
        /**
         * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
         * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
         * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
         * through proxies.
         *
         * Emits an {Initialized} event the first time it is successfully executed.
         */
        function _disableInitializers() internal virtual {
            // solhint-disable-next-line var-name-mixedcase
            InitializableStorage storage $ = _getInitializableStorage();
            if ($._initializing) {
                revert InvalidInitialization();
            }
            if ($._initialized != type(uint64).max) {
                $._initialized = type(uint64).max;
                emit Initialized(type(uint64).max);
            }
        }
        /**
         * @dev Returns the highest version that has been initialized. See {reinitializer}.
         */
        function _getInitializedVersion() internal view returns (uint64) {
            return _getInitializableStorage()._initialized;
        }
        /**
         * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
         */
        function _isInitializing() internal view returns (bool) {
            return _getInitializableStorage()._initializing;
        }
        /**
         * @dev Returns a pointer to the storage namespace.
         */
        // solhint-disable-next-line var-name-mixedcase
        function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
            assembly {
                $.slot := INITIALIZABLE_STORAGE
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
    pragma solidity ^0.8.20;
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
    import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
    import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
    import {Initializable} from "../../proxy/utils/Initializable.sol";
    /**
     * @dev Implementation of the {IERC20} interface.
     *
     * This implementation is agnostic to the way tokens are created. This means
     * that a supply mechanism has to be added in a derived contract using {_mint}.
     *
     * TIP: For a detailed writeup see our guide
     * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
     * to implement supply mechanisms].
     *
     * The default value of {decimals} is 18. To change this, you should override
     * this function so it returns a different value.
     *
     * We have followed general OpenZeppelin Contracts guidelines: functions revert
     * instead returning `false` on failure. This behavior is nonetheless
     * conventional and does not conflict with the expectations of ERC20
     * applications.
     *
     * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
     * This allows applications to reconstruct the allowance for all accounts just
     * by listening to said events. Other implementations of the EIP may not emit
     * these events, as it isn't required by the specification.
     */
    abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors {
        /// @custom:storage-location erc7201:openzeppelin.storage.ERC20
        struct ERC20Storage {
            mapping(address account => uint256) _balances;
            mapping(address account => mapping(address spender => uint256)) _allowances;
            uint256 _totalSupply;
            string _name;
            string _symbol;
        }
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00;
        function _getERC20Storage() private pure returns (ERC20Storage storage $) {
            assembly {
                $.slot := ERC20StorageLocation
            }
        }
        /**
         * @dev Sets the values for {name} and {symbol}.
         *
         * All two of these values are immutable: they can only be set once during
         * construction.
         */
        function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
            __ERC20_init_unchained(name_, symbol_);
        }
        function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
            ERC20Storage storage $ = _getERC20Storage();
            $._name = name_;
            $._symbol = symbol_;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual returns (string memory) {
            ERC20Storage storage $ = _getERC20Storage();
            return $._name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual returns (string memory) {
            ERC20Storage storage $ = _getERC20Storage();
            return $._symbol;
        }
        /**
         * @dev Returns the number of decimals used to get its user representation.
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
         * be displayed to a user as `5.05` (`505 / 10 ** 2`).
         *
         * Tokens usually opt for a value of 18, imitating the relationship between
         * Ether and Wei. This is the default value returned by this function, unless
         * it's overridden.
         *
         * NOTE: This information is only used for _display_ purposes: it in
         * no way affects any of the arithmetic of the contract, including
         * {IERC20-balanceOf} and {IERC20-transfer}.
         */
        function decimals() public view virtual returns (uint8) {
            return 18;
        }
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view virtual returns (uint256) {
            ERC20Storage storage $ = _getERC20Storage();
            return $._totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual returns (uint256) {
            ERC20Storage storage $ = _getERC20Storage();
            return $._balances[account];
        }
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - the caller must have a balance of at least `value`.
         */
        function transfer(address to, uint256 value) public virtual returns (bool) {
            address owner = _msgSender();
            _transfer(owner, to, value);
            return true;
        }
        /**
         * @dev See {IERC20-allowance}.
         */
        function allowance(address owner, address spender) public view virtual returns (uint256) {
            ERC20Storage storage $ = _getERC20Storage();
            return $._allowances[owner][spender];
        }
        /**
         * @dev See {IERC20-approve}.
         *
         * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
         * `transferFrom`. This is semantically equivalent to an infinite approval.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function approve(address spender, uint256 value) public virtual returns (bool) {
            address owner = _msgSender();
            _approve(owner, spender, value);
            return true;
        }
        /**
         * @dev See {IERC20-transferFrom}.
         *
         * Emits an {Approval} event indicating the updated allowance. This is not
         * required by the EIP. See the note at the beginning of {ERC20}.
         *
         * NOTE: Does not update the allowance if the current allowance
         * is the maximum `uint256`.
         *
         * Requirements:
         *
         * - `from` and `to` cannot be the zero address.
         * - `from` must have a balance of at least `value`.
         * - the caller must have allowance for ``from``'s tokens of at least
         * `value`.
         */
        function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
            address spender = _msgSender();
            _spendAllowance(from, spender, value);
            _transfer(from, to, value);
            return true;
        }
        /**
         * @dev Moves a `value` amount of tokens from `from` to `to`.
         *
         * This internal function is equivalent to {transfer}, and can be used to
         * e.g. implement automatic token fees, slashing mechanisms, etc.
         *
         * Emits a {Transfer} event.
         *
         * NOTE: This function is not virtual, {_update} should be overridden instead.
         */
        function _transfer(address from, address to, uint256 value) internal {
            if (from == address(0)) {
                revert ERC20InvalidSender(address(0));
            }
            if (to == address(0)) {
                revert ERC20InvalidReceiver(address(0));
            }
            _update(from, to, value);
        }
        /**
         * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
         * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
         * this function.
         *
         * Emits a {Transfer} event.
         */
        function _update(address from, address to, uint256 value) internal virtual {
            ERC20Storage storage $ = _getERC20Storage();
            if (from == address(0)) {
                // Overflow check required: The rest of the code assumes that totalSupply never overflows
                $._totalSupply += value;
            } else {
                uint256 fromBalance = $._balances[from];
                if (fromBalance < value) {
                    revert ERC20InsufficientBalance(from, fromBalance, value);
                }
                unchecked {
                    // Overflow not possible: value <= fromBalance <= totalSupply.
                    $._balances[from] = fromBalance - value;
                }
            }
            if (to == address(0)) {
                unchecked {
                    // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                    $._totalSupply -= value;
                }
            } else {
                unchecked {
                    // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                    $._balances[to] += value;
                }
            }
            emit Transfer(from, to, value);
        }
        /**
         * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
         * Relies on the `_update` mechanism
         *
         * Emits a {Transfer} event with `from` set to the zero address.
         *
         * NOTE: This function is not virtual, {_update} should be overridden instead.
         */
        function _mint(address account, uint256 value) internal {
            if (account == address(0)) {
                revert ERC20InvalidReceiver(address(0));
            }
            _update(address(0), account, value);
        }
        /**
         * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
         * Relies on the `_update` mechanism.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * NOTE: This function is not virtual, {_update} should be overridden instead
         */
        function _burn(address account, uint256 value) internal {
            if (account == address(0)) {
                revert ERC20InvalidSender(address(0));
            }
            _update(account, address(0), value);
        }
        /**
         * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         *
         * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
         */
        function _approve(address owner, address spender, uint256 value) internal {
            _approve(owner, spender, value, true);
        }
        /**
         * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
         *
         * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
         * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
         * `Approval` event during `transferFrom` operations.
         *
         * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
         * true using the following override:
         * ```
         * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
         *     super._approve(owner, spender, value, true);
         * }
         * ```
         *
         * Requirements are the same as {_approve}.
         */
        function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
            ERC20Storage storage $ = _getERC20Storage();
            if (owner == address(0)) {
                revert ERC20InvalidApprover(address(0));
            }
            if (spender == address(0)) {
                revert ERC20InvalidSpender(address(0));
            }
            $._allowances[owner][spender] = value;
            if (emitEvent) {
                emit Approval(owner, spender, value);
            }
        }
        /**
         * @dev Updates `owner` s allowance for `spender` based on spent `value`.
         *
         * Does not update the allowance value in case of infinite allowance.
         * Revert if not enough allowance is available.
         *
         * Does not emit an {Approval} event.
         */
        function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
            uint256 currentAllowance = allowance(owner, spender);
            if (currentAllowance != type(uint256).max) {
                if (currentAllowance < value) {
                    revert ERC20InsufficientAllowance(spender, currentAllowance, value);
                }
                unchecked {
                    _approve(owner, spender, currentAllowance - value, false);
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC20Pausable.sol)
    pragma solidity ^0.8.20;
    import {ERC20Upgradeable} from "../ERC20Upgradeable.sol";
    import {PausableUpgradeable} from "../../../utils/PausableUpgradeable.sol";
    import {Initializable} from "../../../proxy/utils/Initializable.sol";
    /**
     * @dev ERC20 token with pausable token transfers, minting and burning.
     *
     * Useful for scenarios such as preventing trades until the end of an evaluation
     * period, or having an emergency switch for freezing all token transfers in the
     * event of a large bug.
     *
     * IMPORTANT: This contract does not include public pause and unpause functions. In
     * addition to inheriting this contract, you must define both functions, invoking the
     * {Pausable-_pause} and {Pausable-_unpause} internal functions, with appropriate
     * access control, e.g. using {AccessControl} or {Ownable}. Not doing so will
     * make the contract pause mechanism of the contract unreachable, and thus unusable.
     */
    abstract contract ERC20PausableUpgradeable is Initializable, ERC20Upgradeable, PausableUpgradeable {
        function __ERC20Pausable_init() internal onlyInitializing {
            __Pausable_init_unchained();
        }
        function __ERC20Pausable_init_unchained() internal onlyInitializing {
        }
        /**
         * @dev See {ERC20-_update}.
         *
         * Requirements:
         *
         * - the contract must not be paused.
         */
        function _update(address from, address to, uint256 value) internal virtual override whenNotPaused {
            super._update(from, to, value);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC20Permit.sol)
    pragma solidity ^0.8.20;
    import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
    import {ERC20Upgradeable} from "../ERC20Upgradeable.sol";
    import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
    import {EIP712Upgradeable} from "../../../utils/cryptography/EIP712Upgradeable.sol";
    import {NoncesUpgradeable} from "../../../utils/NoncesUpgradeable.sol";
    import {Initializable} from "../../../proxy/utils/Initializable.sol";
    /**
     * @dev Implementation 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.
     */
    abstract contract ERC20PermitUpgradeable is Initializable, ERC20Upgradeable, IERC20Permit, EIP712Upgradeable, NoncesUpgradeable {
        bytes32 private constant PERMIT_TYPEHASH =
            keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
        /**
         * @dev Permit deadline has expired.
         */
        error ERC2612ExpiredSignature(uint256 deadline);
        /**
         * @dev Mismatched signature.
         */
        error ERC2612InvalidSigner(address signer, address owner);
        /**
         * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
         *
         * It's a good idea to use the same `name` that is defined as the ERC20 token name.
         */
        function __ERC20Permit_init(string memory name) internal onlyInitializing {
            __EIP712_init_unchained(name, "1");
        }
        function __ERC20Permit_init_unchained(string memory) internal onlyInitializing {}
        /**
         * @inheritdoc IERC20Permit
         */
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) public virtual {
            if (block.timestamp > deadline) {
                revert ERC2612ExpiredSignature(deadline);
            }
            bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
            bytes32 hash = _hashTypedDataV4(structHash);
            address signer = ECDSA.recover(hash, v, r, s);
            if (signer != owner) {
                revert ERC2612InvalidSigner(signer, owner);
            }
            _approve(owner, spender, value);
        }
        /**
         * @inheritdoc IERC20Permit
         */
        function nonces(address owner) public view virtual override(IERC20Permit, NoncesUpgradeable) returns (uint256) {
            return super.nonces(owner);
        }
        /**
         * @inheritdoc IERC20Permit
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view virtual returns (bytes32) {
            return _domainSeparatorV4();
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
    pragma solidity ^0.8.20;
    import {Initializable} from "../proxy/utils/Initializable.sol";
    /**
     * @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 ContextUpgradeable is Initializable {
        function __Context_init() internal onlyInitializing {
        }
        function __Context_init_unchained() internal onlyInitializing {
        }
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
        function _contextSuffixLength() internal view virtual returns (uint256) {
            return 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/EIP712.sol)
    pragma solidity ^0.8.20;
    import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
    import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol";
    import {Initializable} from "../../proxy/utils/Initializable.sol";
    /**
     * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
     *
     * The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose
     * encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract
     * does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to
     * produce the hash of their typed data using a combination of `abi.encode` and `keccak256`.
     *
     * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
     * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
     * ({_hashTypedDataV4}).
     *
     * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
     * the chain id to protect against replay attacks on an eventual fork of the chain.
     *
     * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
     * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
     *
     * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
     * separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the
     * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
     */
    abstract contract EIP712Upgradeable is Initializable, IERC5267 {
        bytes32 private constant TYPE_HASH =
            keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
        /// @custom:storage-location erc7201:openzeppelin.storage.EIP712
        struct EIP712Storage {
            /// @custom:oz-renamed-from _HASHED_NAME
            bytes32 _hashedName;
            /// @custom:oz-renamed-from _HASHED_VERSION
            bytes32 _hashedVersion;
            string _name;
            string _version;
        }
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.EIP712")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 private constant EIP712StorageLocation = 0xa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100;
        function _getEIP712Storage() private pure returns (EIP712Storage storage $) {
            assembly {
                $.slot := EIP712StorageLocation
            }
        }
        /**
         * @dev Initializes the domain separator and parameter caches.
         *
         * The meaning of `name` and `version` is specified in
         * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
         *
         * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
         * - `version`: the current major version of the signing domain.
         *
         * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
         * contract upgrade].
         */
        function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
            __EIP712_init_unchained(name, version);
        }
        function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
            EIP712Storage storage $ = _getEIP712Storage();
            $._name = name;
            $._version = version;
            // Reset prior values in storage if upgrading
            $._hashedName = 0;
            $._hashedVersion = 0;
        }
        /**
         * @dev Returns the domain separator for the current chain.
         */
        function _domainSeparatorV4() internal view returns (bytes32) {
            return _buildDomainSeparator();
        }
        function _buildDomainSeparator() private view returns (bytes32) {
            return keccak256(abi.encode(TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash(), block.chainid, address(this)));
        }
        /**
         * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
         * function returns the hash of the fully encoded EIP712 message for this domain.
         *
         * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
         *
         * ```solidity
         * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
         *     keccak256("Mail(address to,string contents)"),
         *     mailTo,
         *     keccak256(bytes(mailContents))
         * )));
         * address signer = ECDSA.recover(digest, signature);
         * ```
         */
        function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
            return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
        }
        /**
         * @dev See {IERC-5267}.
         */
        function eip712Domain()
            public
            view
            virtual
            returns (
                bytes1 fields,
                string memory name,
                string memory version,
                uint256 chainId,
                address verifyingContract,
                bytes32 salt,
                uint256[] memory extensions
            )
        {
            EIP712Storage storage $ = _getEIP712Storage();
            // If the hashed name and version in storage are non-zero, the contract hasn't been properly initialized
            // and the EIP712 domain is not reliable, as it will be missing name and version.
            require($._hashedName == 0 && $._hashedVersion == 0, "EIP712: Uninitialized");
            return (
                hex"0f", // 01111
                _EIP712Name(),
                _EIP712Version(),
                block.chainid,
                address(this),
                bytes32(0),
                new uint256[](0)
            );
        }
        /**
         * @dev The name parameter for the EIP712 domain.
         *
         * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
         * are a concern.
         */
        function _EIP712Name() internal view virtual returns (string memory) {
            EIP712Storage storage $ = _getEIP712Storage();
            return $._name;
        }
        /**
         * @dev The version parameter for the EIP712 domain.
         *
         * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
         * are a concern.
         */
        function _EIP712Version() internal view virtual returns (string memory) {
            EIP712Storage storage $ = _getEIP712Storage();
            return $._version;
        }
        /**
         * @dev The hash of the name parameter for the EIP712 domain.
         *
         * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Name` instead.
         */
        function _EIP712NameHash() internal view returns (bytes32) {
            EIP712Storage storage $ = _getEIP712Storage();
            string memory name = _EIP712Name();
            if (bytes(name).length > 0) {
                return keccak256(bytes(name));
            } else {
                // If the name is empty, the contract may have been upgraded without initializing the new storage.
                // We return the name hash in storage if non-zero, otherwise we assume the name is empty by design.
                bytes32 hashedName = $._hashedName;
                if (hashedName != 0) {
                    return hashedName;
                } else {
                    return keccak256("");
                }
            }
        }
        /**
         * @dev The hash of the version parameter for the EIP712 domain.
         *
         * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Version` instead.
         */
        function _EIP712VersionHash() internal view returns (bytes32) {
            EIP712Storage storage $ = _getEIP712Storage();
            string memory version = _EIP712Version();
            if (bytes(version).length > 0) {
                return keccak256(bytes(version));
            } else {
                // If the version is empty, the contract may have been upgraded without initializing the new storage.
                // We return the version hash in storage if non-zero, otherwise we assume the version is empty by design.
                bytes32 hashedVersion = $._hashedVersion;
                if (hashedVersion != 0) {
                    return hashedVersion;
                } else {
                    return keccak256("");
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Nonces.sol)
    pragma solidity ^0.8.20;
    import {Initializable} from "../proxy/utils/Initializable.sol";
    /**
     * @dev Provides tracking nonces for addresses. Nonces will only increment.
     */
    abstract contract NoncesUpgradeable is Initializable {
        /**
         * @dev The nonce used for an `account` is not the expected current nonce.
         */
        error InvalidAccountNonce(address account, uint256 currentNonce);
        /// @custom:storage-location erc7201:openzeppelin.storage.Nonces
        struct NoncesStorage {
            mapping(address account => uint256) _nonces;
        }
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Nonces")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 private constant NoncesStorageLocation = 0x5ab42ced628888259c08ac98db1eb0cf702fc1501344311d8b100cd1bfe4bb00;
        function _getNoncesStorage() private pure returns (NoncesStorage storage $) {
            assembly {
                $.slot := NoncesStorageLocation
            }
        }
        function __Nonces_init() internal onlyInitializing {
        }
        function __Nonces_init_unchained() internal onlyInitializing {
        }
        /**
         * @dev Returns the next unused nonce for an address.
         */
        function nonces(address owner) public view virtual returns (uint256) {
            NoncesStorage storage $ = _getNoncesStorage();
            return $._nonces[owner];
        }
        /**
         * @dev Consumes a nonce.
         *
         * Returns the current value and increments nonce.
         */
        function _useNonce(address owner) internal virtual returns (uint256) {
            NoncesStorage storage $ = _getNoncesStorage();
            // For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be
            // decremented or reset. This guarantees that the nonce never overflows.
            unchecked {
                // It is important to do x++ and not ++x here.
                return $._nonces[owner]++;
            }
        }
        /**
         * @dev Same as {_useNonce} but checking that `nonce` is the next valid for `owner`.
         */
        function _useCheckedNonce(address owner, uint256 nonce) internal virtual {
            uint256 current = _useNonce(owner);
            if (nonce != current) {
                revert InvalidAccountNonce(owner, current);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
    pragma solidity ^0.8.20;
    import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
    import {Initializable} from "../proxy/utils/Initializable.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 PausableUpgradeable is Initializable, ContextUpgradeable {
        /// @custom:storage-location erc7201:openzeppelin.storage.Pausable
        struct PausableStorage {
            bool _paused;
        }
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;
        function _getPausableStorage() private pure returns (PausableStorage storage $) {
            assembly {
                $.slot := PausableStorageLocation
            }
        }
        /**
         * @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);
        /**
         * @dev The operation failed because the contract is paused.
         */
        error EnforcedPause();
        /**
         * @dev The operation failed because the contract is not paused.
         */
        error ExpectedPause();
        /**
         * @dev Initializes the contract in unpaused state.
         */
        function __Pausable_init() internal onlyInitializing {
            __Pausable_init_unchained();
        }
        function __Pausable_init_unchained() internal onlyInitializing {
            PausableStorage storage $ = _getPausableStorage();
            $._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) {
            PausableStorage storage $ = _getPausableStorage();
            return $._paused;
        }
        /**
         * @dev Throws if the contract is paused.
         */
        function _requireNotPaused() internal view virtual {
            if (paused()) {
                revert EnforcedPause();
            }
        }
        /**
         * @dev Throws if the contract is not paused.
         */
        function _requirePaused() internal view virtual {
            if (!paused()) {
                revert ExpectedPause();
            }
        }
        /**
         * @dev Triggers stopped state.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        function _pause() internal virtual whenNotPaused {
            PausableStorage storage $ = _getPausableStorage();
            $._paused = true;
            emit Paused(_msgSender());
        }
        /**
         * @dev Returns to normal state.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        function _unpause() internal virtual whenPaused {
            PausableStorage storage $ = _getPausableStorage();
            $._paused = false;
            emit Unpaused(_msgSender());
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
    pragma solidity ^0.8.20;
    import {Initializable} from "../proxy/utils/Initializable.sol";
    /**
     * @dev Contract module that helps prevent reentrant calls to a function.
     *
     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
     * available, which can be applied to functions to make sure there are no nested
     * (reentrant) calls to them.
     *
     * Note that because there is a single `nonReentrant` guard, functions marked as
     * `nonReentrant` may not call one another. This can be worked around by making
     * those functions `private`, and then adding `external` `nonReentrant` entry
     * points to them.
     *
     * TIP: If you would like to learn more about reentrancy and alternative ways
     * to protect against it, check out our blog post
     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
     */
    abstract contract ReentrancyGuardUpgradeable is Initializable {
        // Booleans are more expensive than uint256 or any type that takes up a full
        // word because each write operation emits an extra SLOAD to first read the
        // slot's contents, replace the bits taken up by the boolean, and then write
        // back. This is the compiler's defense against contract upgrades and
        // pointer aliasing, and it cannot be disabled.
        // The values being non-zero value makes deployment a bit more expensive,
        // but in exchange the refund on every call to nonReentrant will be lower in
        // amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to
        // increase the likelihood of the full refund coming into effect.
        uint256 private constant NOT_ENTERED = 1;
        uint256 private constant ENTERED = 2;
        /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
        struct ReentrancyGuardStorage {
            uint256 _status;
        }
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
        function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
            assembly {
                $.slot := ReentrancyGuardStorageLocation
            }
        }
        /**
         * @dev Unauthorized reentrant call.
         */
        error ReentrancyGuardReentrantCall();
        function __ReentrancyGuard_init() internal onlyInitializing {
            __ReentrancyGuard_init_unchained();
        }
        function __ReentrancyGuard_init_unchained() internal onlyInitializing {
            ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
            $._status = NOT_ENTERED;
        }
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         * Calling a `nonReentrant` function from another `nonReentrant`
         * function is not supported. It is possible to prevent this from happening
         * by making the `nonReentrant` function external, and making it call a
         * `private` function that does the actual work.
         */
        modifier nonReentrant() {
            _nonReentrantBefore();
            _;
            _nonReentrantAfter();
        }
        function _nonReentrantBefore() private {
            ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
            // On the first call to nonReentrant, _status will be NOT_ENTERED
            if ($._status == ENTERED) {
                revert ReentrancyGuardReentrantCall();
            }
            // Any calls to nonReentrant after this point will fail
            $._status = ENTERED;
        }
        function _nonReentrantAfter() private {
            ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
            // By storing the original value once again, a refund is triggered (see
            // https://eips.ethereum.org/EIPS/eip-2200)
            $._status = NOT_ENTERED;
        }
        /**
         * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
         * `nonReentrant` function in the call stack.
         */
        function _reentrancyGuardEntered() internal view returns (bool) {
            ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
            return $._status == ENTERED;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Standard ERC20 Errors
     * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
     */
    interface IERC20Errors {
        /**
         * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         * @param balance Current balance for the interacting account.
         * @param needed Minimum amount required to perform a transfer.
         */
        error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
        /**
         * @dev Indicates a failure with the token `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         */
        error ERC20InvalidSender(address sender);
        /**
         * @dev Indicates a failure with the token `receiver`. Used in transfers.
         * @param receiver Address to which tokens are being transferred.
         */
        error ERC20InvalidReceiver(address receiver);
        /**
         * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
         * @param spender Address that may be allowed to operate on tokens without being their owner.
         * @param allowance Amount of tokens a `spender` is allowed to operate with.
         * @param needed Minimum amount required to perform a transfer.
         */
        error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
        /**
         * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
         * @param approver Address initiating an approval operation.
         */
        error ERC20InvalidApprover(address approver);
        /**
         * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
         * @param spender Address that may be allowed to operate on tokens without being their owner.
         */
        error ERC20InvalidSpender(address spender);
    }
    /**
     * @dev Standard ERC721 Errors
     * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
     */
    interface IERC721Errors {
        /**
         * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
         * Used in balance queries.
         * @param owner Address of the current owner of a token.
         */
        error ERC721InvalidOwner(address owner);
        /**
         * @dev Indicates a `tokenId` whose `owner` is the zero address.
         * @param tokenId Identifier number of a token.
         */
        error ERC721NonexistentToken(uint256 tokenId);
        /**
         * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         * @param tokenId Identifier number of a token.
         * @param owner Address of the current owner of a token.
         */
        error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
        /**
         * @dev Indicates a failure with the token `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         */
        error ERC721InvalidSender(address sender);
        /**
         * @dev Indicates a failure with the token `receiver`. Used in transfers.
         * @param receiver Address to which tokens are being transferred.
         */
        error ERC721InvalidReceiver(address receiver);
        /**
         * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         * @param tokenId Identifier number of a token.
         */
        error ERC721InsufficientApproval(address operator, uint256 tokenId);
        /**
         * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
         * @param approver Address initiating an approval operation.
         */
        error ERC721InvalidApprover(address approver);
        /**
         * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         */
        error ERC721InvalidOperator(address operator);
    }
    /**
     * @dev Standard ERC1155 Errors
     * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
     */
    interface IERC1155Errors {
        /**
         * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         * @param balance Current balance for the interacting account.
         * @param needed Minimum amount required to perform a transfer.
         * @param tokenId Identifier number of a token.
         */
        error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
        /**
         * @dev Indicates a failure with the token `sender`. Used in transfers.
         * @param sender Address whose tokens are being transferred.
         */
        error ERC1155InvalidSender(address sender);
        /**
         * @dev Indicates a failure with the token `receiver`. Used in transfers.
         * @param receiver Address to which tokens are being transferred.
         */
        error ERC1155InvalidReceiver(address receiver);
        /**
         * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         * @param owner Address of the current owner of a token.
         */
        error ERC1155MissingApprovalForAll(address operator, address owner);
        /**
         * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
         * @param approver Address initiating an approval operation.
         */
        error ERC1155InvalidApprover(address approver);
        /**
         * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
         * @param operator Address that may be allowed to operate on tokens without being their owner.
         */
        error ERC1155InvalidOperator(address operator);
        /**
         * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
         * Used in batch transfers.
         * @param idsLength Length of the array of token identifiers
         * @param valuesLength Length of the array of token amounts
         */
        error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1271.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Interface of the ERC1271 standard signature validation method for
     * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
     */
    interface IERC1271 {
        /**
         * @dev Should return whether the signature provided is valid for the provided data
         * @param hash      Hash of the data to be signed
         * @param signature Signature byte array associated with _data
         */
        function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol)
    pragma solidity ^0.8.20;
    interface IERC5267 {
        /**
         * @dev MAY be emitted to signal that the domain could have changed.
         */
        event EIP712DomainChanged();
        /**
         * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
         * signature.
         */
        function eip712Domain()
            external
            view
            returns (
                bytes1 fields,
                string memory name,
                string memory version,
                uint256 chainId,
                address verifyingContract,
                bytes32 salt,
                uint256[] memory extensions
            );
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
    pragma solidity ^0.8.20;
    import {IERC20} from "../IERC20.sol";
    /**
     * @dev Interface for the optional metadata functions from the ERC20 standard.
     */
    interface IERC20Metadata is IERC20 {
        /**
         * @dev Returns the name of the token.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the symbol of the token.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the decimals places of the token.
         */
        function decimals() external view returns (uint8);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
    pragma solidity ^0.8.20;
    /**
     * @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 v5.0.0) (token/ERC20/IERC20.sol)
    pragma solidity ^0.8.20;
    /**
     * @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 value of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the value of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves a `value` amount of tokens from the caller's account to `to`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address to, uint256 value) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
         * caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 value) external returns (bool);
        /**
         * @dev Moves a `value` amount of tokens from `from` to `to` using the
         * allowance mechanism. `value` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(address from, address to, uint256 value) external returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
    pragma solidity ^0.8.20;
    /**
     * @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
        }
        /**
         * @dev The signature derives the `address(0)`.
         */
        error ECDSAInvalidSignature();
        /**
         * @dev The signature has an invalid length.
         */
        error ECDSAInvalidSignatureLength(uint256 length);
        /**
         * @dev The signature has an S value that is in the upper half order.
         */
        error ECDSAInvalidSignatureS(bytes32 s);
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
         * return address(0) without also returning an error description. Errors are documented using an enum (error type)
         * and a bytes32 providing additional information about the error.
         *
         * If no error is returned, then the address can be used for verification purposes.
         *
         * The `ecrecover` EVM precompile 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 {MessageHashUtils-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]
         */
        function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
            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, bytes32(signature.length));
            }
        }
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature`. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM precompile 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 {MessageHashUtils-toEthSignedMessageHash} on it.
         */
        function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
            (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
            _throwError(error, errorArg);
            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]
         */
        function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
            unchecked {
                bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
                // We do not check for an overflow here since the shift operation results in 0 or 1.
                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.
         */
        function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
            (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
            _throwError(error, errorArg);
            return recovered;
        }
        /**
         * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
         * `r` and `s` signature fields separately.
         */
        function tryRecover(
            bytes32 hash,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal pure returns (address, RecoverError, bytes32) {
            // 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, s);
            }
            // 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, bytes32(0));
            }
            return (signer, RecoverError.NoError, bytes32(0));
        }
        /**
         * @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, bytes32 errorArg) = tryRecover(hash, v, r, s);
            _throwError(error, errorArg);
            return recovered;
        }
        /**
         * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
         */
        function _throwError(RecoverError error, bytes32 errorArg) private pure {
            if (error == RecoverError.NoError) {
                return; // no error: do nothing
            } else if (error == RecoverError.InvalidSignature) {
                revert ECDSAInvalidSignature();
            } else if (error == RecoverError.InvalidSignatureLength) {
                revert ECDSAInvalidSignatureLength(uint256(errorArg));
            } else if (error == RecoverError.InvalidSignatureS) {
                revert ECDSAInvalidSignatureS(errorArg);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
    pragma solidity ^0.8.20;
    import {Strings} from "../Strings.sol";
    /**
     * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
     *
     * The library provides methods for generating a hash of a message that conforms to the
     * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
     * specifications.
     */
    library MessageHashUtils {
        /**
         * @dev Returns the keccak256 digest of an EIP-191 signed data with version
         * `0x45` (`personal_sign` messages).
         *
         * The digest is calculated by prefixing a bytes32 `messageHash` with
         * `"\\x19Ethereum Signed Message:\
    32"` and hashing the result. It corresponds with the
         * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
         *
         * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
         * keccak256, although any bytes32 value can be safely used because the final digest will
         * be re-hashed.
         *
         * See {ECDSA-recover}.
         */
        function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x00, "\\x19Ethereum Signed Message:\
    32") // 32 is the bytes-length of messageHash
                mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
                digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
            }
        }
        /**
         * @dev Returns the keccak256 digest of an EIP-191 signed data with version
         * `0x45` (`personal_sign` messages).
         *
         * The digest is calculated by prefixing an arbitrary `message` with
         * `"\\x19Ethereum Signed Message:\
    " + len(message)` and hashing the result. It corresponds with the
         * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
         *
         * See {ECDSA-recover}.
         */
        function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
            return
                keccak256(bytes.concat("\\x19Ethereum Signed Message:\
    ", bytes(Strings.toString(message.length)), message));
        }
        /**
         * @dev Returns the keccak256 digest of an EIP-191 signed data with version
         * `0x00` (data with intended validator).
         *
         * The digest is calculated by prefixing an arbitrary `data` with `"\\x19\\x00"` and the intended
         * `validator` address. Then hashing the result.
         *
         * See {ECDSA-recover}.
         */
        function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked(hex"19_00", validator, data));
        }
        /**
         * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
         *
         * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
         * `\\x19\\x01` and hashing the result. It corresponds to the hash signed by the
         * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
         *
         * See {ECDSA-recover}.
         */
        function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
            /// @solidity memory-safe-assembly
            assembly {
                let ptr := mload(0x40)
                mstore(ptr, hex"19_01")
                mstore(add(ptr, 0x02), domainSeparator)
                mstore(add(ptr, 0x22), structHash)
                digest := keccak256(ptr, 0x42)
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Standard math utilities missing in the Solidity language.
     */
    library Math {
        /**
         * @dev Muldiv operation overflow.
         */
        error MathOverflowedMulDiv();
        enum Rounding {
            Floor, // Toward negative infinity
            Ceil, // Toward positive infinity
            Trunc, // Toward zero
            Expand // Away from zero
        }
        /**
         * @dev Returns the addition of two unsigned integers, with an overflow flag.
         */
        function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                uint256 c = a + b;
                if (c < a) return (false, 0);
                return (true, c);
            }
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
         */
        function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b > a) return (false, 0);
                return (true, a - b);
            }
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
         */
        function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                // benefit is lost if 'b' is also tested.
                // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                if (a == 0) return (true, 0);
                uint256 c = a * b;
                if (c / a != b) return (false, 0);
                return (true, c);
            }
        }
        /**
         * @dev Returns the division of two unsigned integers, with a division by zero flag.
         */
        function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a / b);
            }
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
         */
        function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a % b);
            }
        }
        /**
         * @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 towards infinity instead
         * of rounding towards zero.
         */
        function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
            if (b == 0) {
                // Guarantee the same behavior as in a regular Solidity division.
                return a / b;
            }
            // (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 = x * y; // Least significant 256 bits of the product
                uint256 prod1; // Most significant 256 bits of the product
                assembly {
                    let mm := mulmod(x, y, not(0))
                    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.
                if (denominator <= prod1) {
                    revert MathOverflowedMulDiv();
                }
                ///////////////////////////////////////////////
                // 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.
                uint256 twos = denominator & (0 - denominator);
                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 (unsignedRoundsUp(rounding) && 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
         * towards zero.
         *
         * 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 + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 2 of a positive value rounded towards zero.
         * 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 + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 10 of a positive value rounded towards zero.
         * 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 + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 256 of a positive value rounded towards zero.
         * 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 + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
            }
        }
        /**
         * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
         */
        function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
            return uint8(rounding) % 2 == 1;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
    pragma solidity ^0.8.20;
    /**
     * @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: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
    pragma solidity ^0.8.20;
    import {Math} from "./math/Math.sol";
    import {SignedMath} from "./math/SignedMath.sol";
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant HEX_DIGITS = "0123456789abcdef";
        uint8 private constant ADDRESS_LENGTH = 20;
        /**
         * @dev The `value` string doesn't fit in the specified `length`.
         */
        error StringsInsufficientHexLength(uint256 value, uint256 length);
        /**
         * @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), HEX_DIGITS))
                    }
                    value /= 10;
                    if (value == 0) break;
                }
                return buffer;
            }
        }
        /**
         * @dev Converts a `int256` to its ASCII `string` decimal representation.
         */
        function toStringSigned(int256 value) internal pure returns (string memory) {
            return string.concat(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) {
            uint256 localValue = value;
            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] = HEX_DIGITS[localValue & 0xf];
                localValue >>= 4;
            }
            if (localValue != 0) {
                revert StringsInsufficientHexLength(value, length);
            }
            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 bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
        }
    }
    // SPDX-License-Identifier: MIT
    // Compatible with OpenZeppelin Contracts ^5.0.0
    pragma solidity 0.8.24;
    /// Interface of the Bascule contract as used by on-chain contracts.
    /// @custom:security-contact [email protected]
    interface IBascule {
        /**
         * Event emitted when a withdrawal is validated.
         * @param withdrawalAmount Amount of the withdrawal.
         * @param depositID Unique identifier for a deposit that took place on another chain and was withdrawn on this chain.
         */
        event WithdrawalValidated(bytes32 depositID, uint256 withdrawalAmount);
        /**
         * Error on attempt to withdraw an already withdrawn deposit.
         * @param depositID Unique identifier for deposit that failed validation.
         * @param withdrawalAmount Amount of the withdrawal.
         */
        error AlreadyWithdrawn(bytes32 depositID, uint256 withdrawalAmount);
        /**
         * Error when a withdrawal fails validation.
         * This means the corresponding deposit is not in the map.
         * @param depositID Unique identifier for deposit that failed validation.
         * @param withdrawalAmount Amount of the withdrawal.
         */
        error WithdrawalFailedValidation(
            bytes32 depositID,
            uint256 withdrawalAmount
        );
        /**
         * Validate a withdrawal (before executing it) if the amount is above
         * threshold.
         *
         * This function checks if our accounting has recorded a deposit that
         * corresponds to this withdrawal request. A deposit can only be withdrawn
         * once.
         *
         * @param depositID Unique identifier of the deposit on another chain.
         * @param withdrawalAmount Amount of the withdrawal.
         *
         * Emits {WithdrawalValidated}.
         */
        function validateWithdrawal(
            bytes32 depositID,
            uint256 withdrawalAmount
        ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
    import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
    import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
    import {Actions} from "../libs/Actions.sol";
    import {INotaryConsortium} from "./INotaryConsortium.sol";
    import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
    /// @title The contract utilizes consortium governance functions using multisignature verification
    /// @author Lombard.Finance
    /// @notice The contracts are a part of the Lombard.Finance protocol
    contract Consortium is Ownable2StepUpgradeable, INotaryConsortium {
        struct ValidatorSet {
            /// @notice addresses of the signers
            address[] validators;
            /// @notice weight of each signer
            uint256[] weights;
            /// @notice current threshold for signatures weight to be accepted
            uint256 weightThreshold;
        }
        /// @custom:storage-location erc7201:lombardfinance.storage.Consortium
        struct ConsortiumStorage {
            /// @notice Current epoch
            uint256 epoch;
            /// @notice Store the Validator set for each epoch
            mapping(uint256 => ValidatorSet) validatorSet;
        }
        // keccak256(abi.encode(uint256(keccak256("lombardfinance.storage.Consortium")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 private constant CONSORTIUM_STORAGE_LOCATION =
            0xbac09a3ab0e06910f94a49c10c16eb53146536ec1a9e948951735cde3a58b500;
        /// @dev https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#initializing_the_implementation_contract
        /// @custom:oz-upgrades-unsafe-allow constructor
        constructor() {
            _disableInitializers();
        }
        /// @notice Initializes the consortium contract
        /// @param _owner - The address of the initial owner
        function initialize(address _owner) external initializer {
            __Ownable_init(_owner);
            __Ownable2Step_init();
            __Consortium_init();
        }
        /// ONLY OWNER FUNCTIONS ///
        /// @notice Sets the initial validator set from any epoch
        /// @param _initialValSet - The initial list of validators
        function setInitialValidatorSet(
            bytes calldata _initialValSet
        ) external onlyOwner {
            // Payload validation
            if (bytes4(_initialValSet) != Actions.NEW_VALSET)
                revert UnexpectedAction(bytes4(_initialValSet));
            ConsortiumStorage storage $ = _getConsortiumStorage();
            Actions.ValSetAction memory action = Actions.validateValSet(
                _initialValSet[4:]
            );
            if ($.epoch != 0) {
                revert ValSetAlreadySet();
            }
            _setValidatorSet(
                $,
                action.validators,
                action.weights,
                action.weightThreshold,
                action.epoch
            );
        }
        /// USER ACTIONS ///
        /// @notice Validates the provided signature against the given hash
        /// @param _payloadHash the hash of the data to be signed
        /// @param _proof nonce, expiry and signatures to validate
        function checkProof(
            bytes32 _payloadHash,
            bytes calldata _proof
        ) public view override {
            _checkProof(_payloadHash, _proof);
        }
        function setNextValidatorSet(
            bytes calldata payload,
            bytes calldata proof
        ) external {
            // payload validation
            if (bytes4(payload) != Actions.NEW_VALSET) {
                revert UnexpectedAction(bytes4(payload));
            }
            Actions.ValSetAction memory action = Actions.validateValSet(
                payload[4:]
            );
            ConsortiumStorage storage $ = _getConsortiumStorage();
            // check proof
            bytes32 payloadHash = sha256(payload);
            checkProof(payloadHash, proof);
            if (action.epoch != $.epoch + 1) revert InvalidEpoch();
            _setValidatorSet(
                $,
                action.validators,
                action.weights,
                action.weightThreshold,
                action.epoch
            );
        }
        /// GETTERS ///
        /// @notice Returns the validator for a given epoch
        /// @param epoch the epoch to get the threshold for
        function getValidatorSet(
            uint256 epoch
        ) external view returns (ValidatorSet memory) {
            return _getConsortiumStorage().validatorSet[epoch];
        }
        /// @notice Returns the current epoch
        function curEpoch() external view returns (uint256) {
            return _getConsortiumStorage().epoch;
        }
        /// PRIVATE FUNCTIONS ///
        /// @notice Internal initializer for the consortium
        function __Consortium_init() internal onlyInitializing {}
        function _setValidatorSet(
            ConsortiumStorage storage $,
            address[] memory _validators,
            uint256[] memory _weights,
            uint256 _threshold,
            uint256 _epoch
        ) internal {
            // do not allow to rewrite existing valset
            if ($.validatorSet[_epoch].weightThreshold != 0) {
                revert InvalidEpoch();
            }
            $.epoch = _epoch;
            $.validatorSet[_epoch] = ValidatorSet({
                validators: _validators,
                weights: _weights,
                weightThreshold: _threshold
            });
            emit ValidatorSetUpdated(_epoch, _validators, _weights, _threshold);
        }
        /// @dev Checks that `_proof` is correct
        /// @param _payloadHash data to be signed
        /// @param _proof encoding of signatures array
        /// @dev Negative weight means that the validator did not sign, any positive weight means that the validator signed
        function _checkProof(
            bytes32 _payloadHash,
            bytes calldata _proof
        ) internal view virtual {
            ConsortiumStorage storage $ = _getConsortiumStorage();
            if ($.epoch == 0) {
                revert NoValidatorSet();
            }
            // decode proof
            bytes[] memory signatures = abi.decode(_proof, (bytes[]));
            address[] storage validators = $.validatorSet[$.epoch].validators;
            uint256 length = validators.length;
            if (signatures.length != length) {
                revert LengthMismatch();
            }
            uint256 weight = 0;
            uint256[] storage weights = $.validatorSet[$.epoch].weights;
            for (uint256 i; i < length; ++i) {
                // each signature preset R || S values
                // V is missed, because validators use Cosmos SDK keyring which is not signing in eth style
                // We only check signatures which are the expected 64 bytes long - we are expecting
                // a signatures array with the same amount of items as there are validators, but not all
                // validators will need to sign for a proof to be valid, so validators who have not signed
                // will have their corresponding signature set to 0 bytes.
                // In case of a malformed signature (i.e. length isn't 0 bytes but also isn't 64 bytes)
                // this signature will be discarded.
                if (signatures[i].length == 64) {
                    // split signature by R and S values
                    bytes memory sig = signatures[i];
                    bytes32 r;
                    bytes32 s;
                    // load the first 32 bytes (r) and the second 32 bytes (s) from the sig
                    assembly {
                        r := mload(add(sig, 0x20)) // first 32 bytes (offset 0x20)
                        s := mload(add(sig, 0x40)) // next 32 bytes (offset 0x40)
                    }
                    if (r != bytes32(0) && s != bytes32(0)) {
                        // try recover with V = 27
                        (address signer, ECDSA.RecoverError err, ) = ECDSA
                            .tryRecover(_payloadHash, 27, r, s);
                        // ignore if bad signature
                        if (err != ECDSA.RecoverError.NoError) {
                            continue;
                        }
                        // if signer doesn't match try V = 28
                        if (signer != validators[i]) {
                            (signer, err, ) = ECDSA.tryRecover(
                                _payloadHash,
                                28,
                                r,
                                s
                            );
                            if (err != ECDSA.RecoverError.NoError) {
                                continue;
                            }
                            if (signer != validators[i]) {
                                continue;
                            }
                        }
                        // signature accepted
                        unchecked {
                            weight += weights[i];
                        }
                    }
                }
            }
            if (weight < $.validatorSet[$.epoch].weightThreshold) {
                revert NotEnoughSignatures();
            }
        }
        /// @notice Retrieve the ConsortiumStorage struct from the specific storage slot
        function _getConsortiumStorage()
            private
            pure
            returns (ConsortiumStorage storage $)
        {
            assembly {
                $.slot := CONSORTIUM_STORAGE_LOCATION
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
    interface INotaryConsortium {
        /// @dev Error thrown when signature payload is already used
        error PayloadAlreadyUsed();
        /// @dev Error thrown when signatures length is not equal to signers length
        error LengthMismatch();
        /// @dev Error thrown when there are not enough signatures
        error NotEnoughSignatures();
        /// @dev Error thrown when unexpected action is used
        error UnexpectedAction(bytes4 action);
        /// @dev Event emitted when the validator set is updated
        event ValidatorSetUpdated(
            uint256 indexed epoch,
            address[] validators,
            uint256[] weights,
            uint256 threshold
        );
        /// @dev Error thrown when validator set already set
        error ValSetAlreadySet();
        /// @dev Error thrown when no validator set is set
        error NoValidatorSet();
        /// @dev Error thrown when invalid epoch is provided
        error InvalidEpoch();
        function checkProof(
            bytes32 _payloadHash,
            bytes calldata _proof
        ) external view;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    interface ILBTC {
        error ZeroAddress();
        error ZeroContractHash();
        error ZeroChainId();
        error WithdrawalsDisabled();
        error KnownDestination();
        error UnknownDestination();
        error ScriptPubkeyUnsupported();
        error AmountLessThanCommission(uint256 fee);
        error AmountBelowDustLimit(uint256 dustLimit);
        error InvalidDustFeeRate();
        error UnauthorizedAccount(address account);
        error UnexpectedAction(bytes4 action);
        error InvalidUserSignature();
        error PayloadAlreadyUsed();
        error InvalidInputLength();
        error InvalidMintAmount();
        event PauserRoleTransferred(
            address indexed previousPauser,
            address indexed newPauser
        );
        event OperatorRoleTransferred(
            address indexed previousOperator,
            address indexed newOperator
        );
        event UnstakeRequest(
            address indexed fromAddress,
            bytes scriptPubKey,
            uint256 amount
        );
        event WithdrawalsEnabled(bool);
        event NameAndSymbolChanged(string name, string symbol);
        event ConsortiumChanged(address indexed prevVal, address indexed newVal);
        event TreasuryAddressChanged(
            address indexed prevValue,
            address indexed newValue
        );
        event BurnCommissionChanged(
            uint64 indexed prevValue,
            uint64 indexed newValue
        );
        event DustFeeRateChanged(uint256 indexed oldRate, uint256 indexed newRate);
        event BasculeChanged(address indexed prevVal, address indexed newVal);
        event MinterUpdated(address indexed minter, bool isMinter);
        event BridgeChanged(address indexed prevVal, address indexed newVal);
        event ClaimerUpdated(address indexed claimer, bool isClaimer);
        event FeeCharged(uint256 indexed fee, bytes userSignature);
        event FeeChanged(uint256 indexed oldFee, uint256 indexed newFee);
        error FeeGreaterThanAmount();
        event MintProofConsumed(
            address indexed recipient,
            bytes32 indexed payloadHash,
            bytes payload
        );
        event BatchMintSkipped(bytes32 indexed payloadHash, bytes payload);
        function burn(uint256 amount) external;
        function burn(address from, uint256 amount) external;
        function mint(address to, uint256 amount) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    import {ERC20Upgradeable, IERC20} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
    import {ERC20PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PausableUpgradeable.sol";
    import {ERC20PermitUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol";
    import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
    import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
    import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
    import {BitcoinUtils, OutputType} from "../libs/BitcoinUtils.sol";
    import {IBascule} from "../bascule/interfaces/IBascule.sol";
    import {ILBTC} from "./ILBTC.sol";
    import {FeeUtils} from "../libs/FeeUtils.sol";
    import {Consortium} from "../consortium/Consortium.sol";
    import {Actions} from "../libs/Actions.sol";
    import {EIP1271SignatureUtils} from "../libs/EIP1271SignatureUtils.sol";
    /**
     * @title ERC20 representation of Lombard Staked Bitcoin
     * @author Lombard.Finance
     * @notice The contracts is a part of Lombard.Finace protocol
     */
    contract LBTC is
        ILBTC,
        ERC20PausableUpgradeable,
        Ownable2StepUpgradeable,
        ReentrancyGuardUpgradeable,
        ERC20PermitUpgradeable
    {
        /// @custom:storage-location erc7201:lombardfinance.storage.LBTC
        struct LBTCStorage {
            /// @dev is keccak256(payload[4:]) used
            /// @custom:oz-renamed-from usedProofs
            mapping(bytes32 => bool) legacyUsedPayloads;
            string name;
            string symbol;
            bool isWithdrawalsEnabled;
            address consortium;
            bool isWBTCEnabled;
            IERC20 wbtc;
            address treasury;
            /// @custom:oz-renamed-from destinations
            mapping(uint256 => address) __removed_destinations;
            /// @custom:oz-renamed-from depositCommission
            mapping(uint256 => uint16) __removed_depositCommission;
            /// @custom:oz-renamed-from usedBridgeProofs
            mapping(bytes32 => bool) __removed_usedBridgeProofs;
            /// @custom:oz-renamed-from globalNonce
            uint256 __removed_globalNonce;
            mapping(bytes32 => bytes32) __removed__destinations;
            mapping(bytes32 => uint16) __removed__depositRelativeCommission;
            mapping(bytes32 => uint64) __removed__depositAbsoluteCommission;
            uint64 burnCommission; // absolute commission to charge on burn (unstake)
            uint256 dustFeeRate;
            /// Bascule drawbridge used to confirm deposits before allowing withdrawals
            IBascule bascule;
            address pauser;
            mapping(address => bool) minters;
            mapping(address => bool) claimers;
            /// Maximum fee to apply on mints
            uint256 maximumFee;
            // @dev is sha256(payload) used
            mapping(bytes32 => bool) usedPayloads;
            address operator;
        }
        // keccak256(abi.encode(uint256(keccak256("lombardfinance.storage.LBTC")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 private constant LBTC_STORAGE_LOCATION =
            0xa9a2395ec4edf6682d754acb293b04902817fdb5829dd13adb0367ab3a26c700;
        /// @dev https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#initializing_the_implementation_contract
        /// @custom:oz-upgrades-unsafe-allow constructor
        constructor() {
            _disableInitializers();
        }
        /// INTIALIZERS ///
        function initialize(
            address consortium_,
            uint64 burnCommission_,
            address treasury,
            address owner_
        ) external initializer {
            __ERC20_init("", "");
            __ERC20Pausable_init();
            __Ownable_init(owner_);
            __Ownable2Step_init();
            __ReentrancyGuard_init();
            __LBTC_init(
                "Lombard Staked Bitcoin",
                "LBTC",
                consortium_,
                treasury,
                burnCommission_
            );
            LBTCStorage storage $ = _getLBTCStorage();
            $.dustFeeRate = BitcoinUtils.DEFAULT_DUST_FEE_RATE;
            emit DustFeeRateChanged(0, $.dustFeeRate);
        }
        function reinitialize() external reinitializer(2) {
            __ERC20Permit_init("Lombard Staked Bitcoin");
        }
        /// MODIFIER ///
        /**
         * PAUSE
         */
        modifier onlyPauser() {
            _checkPauser();
            _;
        }
        modifier onlyMinter() {
            if (!_getLBTCStorage().minters[_msgSender()]) {
                revert UnauthorizedAccount(_msgSender());
            }
            _;
        }
        modifier onlyClaimer() {
            if (!_getLBTCStorage().claimers[_msgSender()]) {
                revert UnauthorizedAccount(_msgSender());
            }
            _;
        }
        modifier onlyOperator() {
            if (_getLBTCStorage().operator != _msgSender()) {
                revert UnauthorizedAccount(_msgSender());
            }
            _;
        }
        /// ONLY OWNER FUNCTIONS ///
        function toggleWithdrawals() external onlyOwner {
            LBTCStorage storage $ = _getLBTCStorage();
            $.isWithdrawalsEnabled = !$.isWithdrawalsEnabled;
            emit WithdrawalsEnabled($.isWithdrawalsEnabled);
        }
        function changeNameAndSymbol(
            string calldata name_,
            string calldata symbol_
        ) external onlyOwner {
            _changeNameAndSymbol(name_, symbol_);
        }
        function changeConsortium(address newVal) external onlyOwner {
            _changeConsortium(newVal);
        }
        /**
         * @notice Set the contract current fee for mint
         * @param fee New fee value
         * @dev zero allowed to disable fee
         */
        function setMintFee(uint256 fee) external onlyOperator {
            LBTCStorage storage $ = _getLBTCStorage();
            uint256 oldFee = $.maximumFee;
            $.maximumFee = fee;
            emit FeeChanged(oldFee, fee);
        }
        function changeTreasuryAddress(address newValue) external onlyOwner {
            _changeTreasuryAddress(newValue);
        }
        function changeBurnCommission(uint64 newValue) external onlyOwner {
            _changeBurnCommission(newValue);
        }
        function pause() external onlyPauser {
            _pause();
        }
        function unpause() external onlyPauser {
            _unpause();
        }
        function addMinter(address newMinter) external onlyOwner {
            _updateMinter(newMinter, true);
        }
        function removeMinter(address oldMinter) external onlyOwner {
            _updateMinter(oldMinter, false);
        }
        function addClaimer(address newClaimer) external onlyOwner {
            _updateClaimer(newClaimer, true);
        }
        function removeClaimer(address oldClaimer) external onlyOwner {
            _updateClaimer(oldClaimer, false);
        }
        /// @notice Change the dust fee rate used for dust limit calculations
        /// @dev Only the contract owner can call this function. The new rate must be positive.
        /// @param newRate The new dust fee rate (in satoshis per 1000 bytes)
        function changeDustFeeRate(uint256 newRate) external onlyOwner {
            if (newRate == 0) revert InvalidDustFeeRate();
            LBTCStorage storage $ = _getLBTCStorage();
            uint256 oldRate = $.dustFeeRate;
            $.dustFeeRate = newRate;
            emit DustFeeRateChanged(oldRate, newRate);
        }
        /**
         * Change the address of the Bascule drawbridge contract.
         * Setting the address to 0 disables the Bascule check.
         * @param newVal The new address.
         *
         * Emits a {BasculeChanged} event.
         */
        function changeBascule(address newVal) external onlyOwner {
            _changeBascule(newVal);
        }
        function transferPauserRole(address newPauser) external onlyOwner {
            if (newPauser == address(0)) {
                revert ZeroAddress();
            }
            _transferPauserRole(newPauser);
        }
        function transferOperatorRole(address newOperator) external onlyOwner {
            if (newOperator == address(0)) {
                revert ZeroAddress();
            }
            _transferOperatorRole(newOperator);
        }
        /// GETTERS ///
        /**
         * @notice Returns the current maximum mint fee
         */
        function getMintFee() external view returns (uint256) {
            return _getLBTCStorage().maximumFee;
        }
        /// @notice Calculate the amount that will be unstaked and check if it's above the dust limit
        /// @dev This function can be used by front-ends to verify burn amounts before submitting a transaction
        /// @param scriptPubkey The Bitcoin script public key as a byte array
        /// @param amount The amount of LBTC to be burned
        /// @return amountAfterFee The amount that will be unstaked (after deducting the burn commission)
        /// @return isAboveDust Whether the amountAfterFee is equal to or above the dust limit
        function calcUnstakeRequestAmount(
            bytes calldata scriptPubkey,
            uint256 amount
        ) external view returns (uint256 amountAfterFee, bool isAboveDust) {
            LBTCStorage storage $ = _getLBTCStorage();
            (amountAfterFee, , , isAboveDust) = _calcFeeAndDustLimit(
                scriptPubkey,
                amount,
                $.burnCommission
            );
            return (amountAfterFee, isAboveDust);
        }
        function consortium() external view virtual returns (address) {
            return _getLBTCStorage().consortium;
        }
        /**
         * @dev Returns the number of decimals used to get its user representation.
         *
         * Because LBTC repsents BTC we use the same decimals.
         *
         */
        function decimals() public view virtual override returns (uint8) {
            return 8;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual override returns (string memory) {
            return _getLBTCStorage().name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual override returns (string memory) {
            return _getLBTCStorage().symbol;
        }
        function getTreasury() public view returns (address) {
            return _getLBTCStorage().treasury;
        }
        function getBurnCommission() public view returns (uint64) {
            return _getLBTCStorage().burnCommission;
        }
        /// @notice Get the current dust fee rate
        /// @return The current dust fee rate (in satoshis per 1000 bytes)
        function getDustFeeRate() public view returns (uint256) {
            return _getLBTCStorage().dustFeeRate;
        }
        /**
         * Get Bascule contract.
         */
        function Bascule() external view returns (IBascule) {
            return _getLBTCStorage().bascule;
        }
        function pauser() public view returns (address) {
            return _getLBTCStorage().pauser;
        }
        function operator() external view returns (address) {
            return _getLBTCStorage().operator;
        }
        function isMinter(address minter) external view returns (bool) {
            return _getLBTCStorage().minters[minter];
        }
        function isClaimer(address claimer) external view returns (bool) {
            return _getLBTCStorage().claimers[claimer];
        }
        /// USER ACTIONS ///
        /**
         * @notice Mint LBTC to the specified address
         * @param to The address to mint to
         * @param amount The amount of LBTC to mint
         * @dev Only callable by whitelisted minters
         */
        function mint(address to, uint256 amount) external override onlyMinter {
            _mint(to, amount);
        }
        /**
         * @notice Mint LBTC in batches
         * @param to The addresses to mint to
         * @param amount The amounts of LBTC to mint
         * @dev Only callable by whitelisted minters
         */
        function batchMint(
            address[] calldata to,
            uint256[] calldata amount
        ) external onlyMinter {
            if (to.length != amount.length) {
                revert InvalidInputLength();
            }
            for (uint256 i; i < to.length; ++i) {
                _mint(to[i], amount[i]);
            }
        }
        /**
         * @notice Mint LBTC by proving a stake action happened
         * @param payload The message with the stake data
         * @param proof Signature of the consortium approving the mint
         */
        function mint(
            bytes calldata payload,
            bytes calldata proof
        ) public nonReentrant {
            // payload validation
            if (bytes4(payload) != Actions.DEPOSIT_BTC_ACTION) {
                revert UnexpectedAction(bytes4(payload));
            }
            Actions.DepositBtcAction memory action = Actions.depositBtc(
                payload[4:]
            );
            _validateAndMint(
                action.recipient,
                action.amount,
                action.amount,
                payload,
                proof
            );
        }
        /**
         * @notice Mint LBTC in batches by proving stake actions happened
         * @param payload The messages with the stake data
         * @param proof Signatures of the consortium approving the mints
         */
        function batchMint(
            bytes[] calldata payload,
            bytes[] calldata proof
        ) external {
            if (payload.length != proof.length) {
                revert InvalidInputLength();
            }
            LBTCStorage storage $ = _getLBTCStorage();
            for (uint256 i; i < payload.length; ++i) {
                // Pre-emptive check if payload was used. If so, we can skip the call.
                bytes32 payloadHash = sha256(payload[i]);
                if (
                    $.usedPayloads[payloadHash] ||
                    $.legacyUsedPayloads[keccak256(payload[i][4:])]
                ) {
                    emit BatchMintSkipped(payloadHash, payload[i]);
                    continue;
                }
                mint(payload[i], proof[i]);
            }
        }
        /**
         * @notice Mint LBTC applying a commission to the amount
         * @dev Payload should be same as mint to avoid reusing them with and without fee
         * @param mintPayload The message with the stake data
         * @param proof Signature of the consortium approving the mint
         * @param feePayload Contents of the fee approval signed by the user
         * @param userSignature Signature of the user to allow Fee
         */
        function mintWithFee(
            bytes calldata mintPayload,
            bytes calldata proof,
            bytes calldata feePayload,
            bytes calldata userSignature
        ) external onlyClaimer {
            _mintWithFee(mintPayload, proof, feePayload, userSignature);
        }
        /**
         * @notice Mint LBTC in batches proving stake actions happened
         * @param mintPayload The messages with the stake data
         * @param proof Signatures of the consortium approving the mints
         * @param feePayload Contents of the fee approvals signed by the user
         * @param userSignature Signatures of the user to allow Fees
         */
        function batchMintWithFee(
            bytes[] calldata mintPayload,
            bytes[] calldata proof,
            bytes[] calldata feePayload,
            bytes[] calldata userSignature
        ) external onlyClaimer {
            uint256 length = mintPayload.length;
            if (
                length != proof.length ||
                length != feePayload.length ||
                length != userSignature.length
            ) {
                revert InvalidInputLength();
            }
            LBTCStorage storage $ = _getLBTCStorage();
            for (uint256 i; i < mintPayload.length; ++i) {
                // Pre-emptive check if payload was used. If so, we can skip the call.
                bytes32 payloadHash = sha256(mintPayload[i]);
                if (
                    $.usedPayloads[payloadHash] ||
                    $.legacyUsedPayloads[keccak256(mintPayload[i][4:])]
                ) {
                    emit BatchMintSkipped(payloadHash, mintPayload[i]);
                    continue;
                }
                _mintWithFee(
                    mintPayload[i],
                    proof[i],
                    feePayload[i],
                    userSignature[i]
                );
            }
        }
        /**
         * @dev Burns LBTC to initiate withdrawal of BTC to provided `scriptPubkey` with `amount`
         *
         * @param scriptPubkey scriptPubkey for output
         * @param amount Amount of LBTC to burn
         */
        function redeem(bytes calldata scriptPubkey, uint256 amount) external {
            LBTCStorage storage $ = _getLBTCStorage();
            if (!$.isWithdrawalsEnabled) {
                revert WithdrawalsDisabled();
            }
            uint64 fee = $.burnCommission;
            (
                uint256 amountAfterFee,
                bool isAboveFee,
                uint256 dustLimit,
                bool isAboveDust
            ) = _calcFeeAndDustLimit(scriptPubkey, amount, fee);
            if (!isAboveFee) {
                revert AmountLessThanCommission(fee);
            }
            if (!isAboveDust) {
                revert AmountBelowDustLimit(dustLimit);
            }
            address fromAddress = address(_msgSender());
            _transfer(fromAddress, getTreasury(), fee);
            _burn(fromAddress, amountAfterFee);
            emit UnstakeRequest(fromAddress, scriptPubkey, amountAfterFee);
        }
        /**
         * @dev Burns LBTC
         *
         * @param amount Amount of LBTC to burn
         */
        function burn(uint256 amount) external {
            _burn(_msgSender(), amount);
        }
        /**
         * @dev Allows minters to burn LBTC
         *
         * @param amount Amount of LBTC to burn
         */
        function burn(address from, uint256 amount) external override onlyMinter {
            _burn(from, amount);
        }
        /// PRIVATE FUNCTIONS ///
        function __LBTC_init(
            string memory name_,
            string memory symbol_,
            address consortium_,
            address treasury,
            uint64 burnCommission_
        ) internal onlyInitializing {
            _changeNameAndSymbol(name_, symbol_);
            _changeConsortium(consortium_);
            _changeTreasuryAddress(treasury);
            _changeBurnCommission(burnCommission_);
        }
        function _changeNameAndSymbol(
            string memory name_,
            string memory symbol_
        ) internal {
            LBTCStorage storage $ = _getLBTCStorage();
            $.name = name_;
            $.symbol = symbol_;
            emit NameAndSymbolChanged(name_, symbol_);
        }
        function _changeConsortium(address newVal) internal {
            if (newVal == address(0)) {
                revert ZeroAddress();
            }
            LBTCStorage storage $ = _getLBTCStorage();
            emit ConsortiumChanged($.consortium, newVal);
            $.consortium = newVal;
        }
        function _validateAndMint(
            address recipient,
            uint256 amountToMint,
            uint256 depositAmount,
            bytes calldata payload,
            bytes calldata proof
        ) internal {
            LBTCStorage storage $ = _getLBTCStorage();
            if (amountToMint > depositAmount) revert InvalidMintAmount();
            /// make sure that hash of payload not used before
            /// need to check new sha256 hash and legacy keccak256 from payload without selector
            /// 2 checks made to prevent migration of contract state
            bytes32 payloadHash = sha256(payload);
            bytes32 legacyHash = keccak256(payload[4:]);
            if ($.usedPayloads[payloadHash] || $.legacyUsedPayloads[legacyHash]) {
                revert PayloadAlreadyUsed();
            }
            Consortium($.consortium).checkProof(payloadHash, proof);
            $.usedPayloads[payloadHash] = true;
            // Confirm deposit against Bascule
            _confirmDeposit($, legacyHash, depositAmount);
            // Actually mint
            _mint(recipient, amountToMint);
            emit MintProofConsumed(recipient, payloadHash, payload);
        }
        function _changeBurnCommission(uint64 newValue) internal {
            LBTCStorage storage $ = _getLBTCStorage();
            uint64 prevValue = $.burnCommission;
            $.burnCommission = newValue;
            emit BurnCommissionChanged(prevValue, newValue);
        }
        /**
         * @dev Checks that the deposit was validated by the Bascule drawbridge.
         * @param self LBTC storage.
         * @param depositID The unique ID of the deposit.
         * @param amount The withdrawal amount.
         */
        function _confirmDeposit(
            LBTCStorage storage self,
            bytes32 depositID,
            uint256 amount
        ) internal {
            IBascule bascule = self.bascule;
            if (address(bascule) != address(0)) {
                bascule.validateWithdrawal(depositID, amount);
            }
        }
        /**
         * Change the address of the Bascule drawbridge contract.
         * @param newVal The new address.
         *
         * Emits a {BasculeChanged} event.
         */
        function _changeBascule(address newVal) internal {
            LBTCStorage storage $ = _getLBTCStorage();
            emit BasculeChanged(address($.bascule), newVal);
            $.bascule = IBascule(newVal);
        }
        function _transferPauserRole(address newPauser) internal {
            LBTCStorage storage $ = _getLBTCStorage();
            address oldPauser = $.pauser;
            $.pauser = newPauser;
            emit PauserRoleTransferred(oldPauser, newPauser);
        }
        function _transferOperatorRole(address newOperator) internal {
            LBTCStorage storage $ = _getLBTCStorage();
            address oldOperator = $.operator;
            $.operator = newOperator;
            emit OperatorRoleTransferred(oldOperator, newOperator);
        }
        function _mintWithFee(
            bytes calldata mintPayload,
            bytes calldata proof,
            bytes calldata feePayload,
            bytes calldata userSignature
        ) internal nonReentrant {
            // mint payload validation
            if (bytes4(mintPayload) != Actions.DEPOSIT_BTC_ACTION) {
                revert UnexpectedAction(bytes4(mintPayload));
            }
            Actions.DepositBtcAction memory mintAction = Actions.depositBtc(
                mintPayload[4:]
            );
            // fee payload validation
            if (bytes4(feePayload) != Actions.FEE_APPROVAL_ACTION) {
                revert UnexpectedAction(bytes4(feePayload));
            }
            Actions.FeeApprovalAction memory feeAction = Actions.feeApproval(
                feePayload[4:]
            );
            LBTCStorage storage $ = _getLBTCStorage();
            uint256 fee = $.maximumFee;
            if (fee > feeAction.fee) {
                fee = feeAction.fee;
            }
            if (fee >= mintAction.amount) {
                revert FeeGreaterThanAmount();
            }
            {
                // Fee validation
                bytes32 digest = _hashTypedDataV4(
                    keccak256(
                        abi.encode(
                            Actions.FEE_APPROVAL_EIP712_ACTION,
                            block.chainid,
                            feeAction.fee,
                            feeAction.expiry
                        )
                    )
                );
                if (
                    !EIP1271SignatureUtils.checkSignature(
                        mintAction.recipient,
                        digest,
                        userSignature
                    )
                ) {
                    revert InvalidUserSignature();
                }
            }
            // modified payload to be signed
            _validateAndMint(
                mintAction.recipient,
                mintAction.amount - fee,
                mintAction.amount,
                mintPayload,
                proof
            );
            // mint fee to treasury
            _mint($.treasury, fee);
            emit FeeCharged(fee, userSignature);
        }
        function _checkPauser() internal view {
            if (pauser() != _msgSender()) {
                revert UnauthorizedAccount(_msgSender());
            }
        }
        function _updateMinter(address minter, bool _isMinter) internal {
            if (minter == address(0)) {
                revert ZeroAddress();
            }
            _getLBTCStorage().minters[minter] = _isMinter;
            emit MinterUpdated(minter, _isMinter);
        }
        function _updateClaimer(address claimer, bool _isClaimer) internal {
            if (claimer == address(0)) {
                revert ZeroAddress();
            }
            _getLBTCStorage().claimers[claimer] = _isClaimer;
            emit ClaimerUpdated(claimer, _isClaimer);
        }
        function _changeTreasuryAddress(address newValue) internal {
            if (newValue == address(0)) {
                revert ZeroAddress();
            }
            LBTCStorage storage $ = _getLBTCStorage();
            address prevValue = $.treasury;
            $.treasury = newValue;
            emit TreasuryAddressChanged(prevValue, newValue);
        }
        function _calcFeeAndDustLimit(
            bytes calldata scriptPubkey,
            uint256 amount,
            uint64 fee
        ) internal view returns (uint256, bool, uint256, bool) {
            OutputType outType = BitcoinUtils.getOutputType(scriptPubkey);
            if (outType == OutputType.UNSUPPORTED) {
                revert ScriptPubkeyUnsupported();
            }
            if (amount <= fee) {
                return (0, false, 0, false);
            }
            LBTCStorage storage $ = _getLBTCStorage();
            uint256 amountAfterFee = amount - fee;
            uint256 dustLimit = BitcoinUtils.getDustLimitForOutput(
                outType,
                scriptPubkey,
                $.dustFeeRate
            );
            bool isAboveDust = amountAfterFee > dustLimit;
            return (amountAfterFee, true, dustLimit, isAboveDust);
        }
        function _getLBTCStorage() private pure returns (LBTCStorage storage $) {
            assembly {
                $.slot := LBTC_STORAGE_LOCATION
            }
        }
        /**
         * @dev Override of the _update function to satisfy both ERC20Upgradeable and ERC20PausableUpgradeable
         */
        function _update(
            address from,
            address to,
            uint256 value
        ) internal virtual override(ERC20Upgradeable, ERC20PausableUpgradeable) {
            super._update(from, to, value);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    library Actions {
        struct DepositBtcAction {
            uint256 toChain;
            address recipient;
            uint256 amount;
            bytes32 txid;
            uint32 vout;
        }
        struct DepositBridgeAction {
            uint256 fromChain;
            bytes32 fromContract;
            uint256 toChain;
            address toContract;
            address recipient;
            uint64 amount;
            uint256 nonce;
        }
        struct ValSetAction {
            uint256 epoch;
            address[] validators;
            uint256[] weights;
            uint256 weightThreshold;
            uint256 height;
        }
        struct FeeApprovalAction {
            uint256 fee;
            uint256 expiry;
        }
        /// @dev Error thrown when invalid public key is provided
        error InvalidPublicKey(bytes pubKey);
        /// @dev Error thrown when signatures length is not equal to signers length
        error Actions_LengthMismatch();
        /// @dev Error thrown when threshold is invalid
        error InvalidThreshold();
        /// @dev Error thrown when validator set size is invalid
        error InvalidValidatorSetSize();
        /// @dev Error thrown when zero validator is provided
        error ZeroValidator();
        /// @dev Error thrown when wrong chain id is provided
        error WrongChainId();
        /// @dev Error thrown when wrong contract is provided
        error WrongContract();
        /// @dev Error thrown when zero address is provided
        error Actions_ZeroAddress();
        /// @dev Error thrown when zero amount is provided
        error ZeroAmount();
        /// @dev Error thrown when zero weight is provided
        error ZeroWeight();
        /// @dev Error thrown when fee approval is expired
        error UserSignatureExpired(uint256 expiry);
        /// @dev Error thrown when amount is below fee
        error NotEnoughAmountToUseApproval();
        /// @dev Error thrown when zero fee is used
        error ZeroFee();
        /// @dev Error thrown when payload length is too big
        error PayloadTooLarge();
        // bytes4(keccak256("feeApproval(uint256,uint256)"))
        bytes4 internal constant FEE_APPROVAL_ACTION = 0x8175ca94;
        // keccak256("feeApproval(uint256 chainId,uint256 fee,uint256 expiry)")
        bytes32 internal constant FEE_APPROVAL_EIP712_ACTION =
            0x40ac9f6aa27075e64c1ed1ea2e831b20b8c25efdeb6b79fd0cf683c9a9c50725;
        // bytes4(keccak256("payload(bytes32,bytes32,uint64,bytes32,uint32)"))
        bytes4 internal constant DEPOSIT_BTC_ACTION = 0xf2e73f7c;
        // bytes4(keccak256("payload(bytes32,bytes32,bytes32,bytes32,bytes32,uint64,uint256)"))
        bytes4 internal constant DEPOSIT_BRIDGE_ACTION = 0x5c70a505;
        // bytes4(keccak256("payload(uint256,bytes[],uint256[],uint256,uint256)"))
        bytes4 internal constant NEW_VALSET = 0x4aab1d6f;
        /// @dev Maximum number of validators allowed in the consortium.
        /// @notice This value is determined by the minimum of CometBFT consensus limitations and gas considerations:
        /// - CometBFT has a hard limit of 10,000 validators (https://docs.cometbft.com/v0.38/spec/core/state)
        /// - Gas-based calculation:
        ///   - Assumes 4281 gas per ECDSA signature verification
        ///   - Uses a conservative 30 million gas block limit
        ///   - Maximum possible signatures: 30,000,000 / 4,281 ≈ 7007
        ///   - Reverse calculated for BFT consensus (2/3 + 1):
        ///     7,007 = (10,509 * 2/3 + 1) rounded down
        /// - The lower value of 10,000 (CometBFT limit) and 10,509 (gas calculation) is chosen
        /// @dev This limit ensures compatibility with CometBFT while also considering gas limitations
        ///      for signature verification within a single block.
        uint256 private constant MAX_VALIDATOR_SET_SIZE = 102;
        /// @dev Minimum number of validators allowed in the system.
        /// @notice While set to 1 to allow for non-distributed scenarios, this configuration
        /// does not provide Byzantine fault tolerance. For a truly distributed and
        /// fault-tolerant system, a minimum of 4 validators would be recommended to tolerate
        /// at least one Byzantine fault.
        uint256 private constant MIN_VALIDATOR_SET_SIZE = 1;
        /// @dev A constant representing the number of bytes for a slot of information in a payload.
        uint256 internal constant ABI_SLOT_SIZE = 32;
        /**
         * @notice Returns decoded deposit btc msg
         * @dev Message should not contain the selector
         * @param payload Body of the mint payload
         */
        function depositBtc(
            bytes memory payload
        ) internal view returns (DepositBtcAction memory) {
            if (payload.length != ABI_SLOT_SIZE * 5) revert PayloadTooLarge();
            (
                uint256 toChain,
                address recipient,
                uint256 amount,
                bytes32 txid,
                uint32 vout
            ) = abi.decode(payload, (uint256, address, uint256, bytes32, uint32));
            if (toChain != block.chainid) {
                revert WrongChainId();
            }
            if (recipient == address(0)) {
                revert Actions_ZeroAddress();
            }
            if (amount == 0) {
                revert ZeroAmount();
            }
            return DepositBtcAction(toChain, recipient, amount, txid, vout);
        }
        /**
         * @notice Returns decoded bridge payload
         * @dev Payload should not contain the selector
         * @param payload Body of the burn payload
         */
        function depositBridge(
            bytes memory payload
        ) internal view returns (DepositBridgeAction memory) {
            if (payload.length != ABI_SLOT_SIZE * 7) revert PayloadTooLarge();
            (
                uint256 fromChain,
                bytes32 fromContract,
                uint256 toChain,
                address toContract,
                address recipient,
                uint64 amount,
                uint256 nonce
            ) = abi.decode(
                    payload,
                    (uint256, bytes32, uint256, address, address, uint64, uint256)
                );
            if (toChain != block.chainid) {
                revert WrongChainId();
            }
            if (recipient == address(0)) {
                revert Actions_ZeroAddress();
            }
            if (amount == 0) {
                revert ZeroAmount();
            }
            return
                DepositBridgeAction(
                    fromChain,
                    fromContract,
                    toChain,
                    toContract,
                    recipient,
                    amount,
                    nonce
                );
        }
        /**
         * @notice Returns decoded validator set
         * @dev Payload should not contain the selector
         * @param payload Body of the set validators set payload
         */
        function validateValSet(
            bytes memory payload
        ) internal pure returns (ValSetAction memory) {
            (
                uint256 epoch,
                bytes[] memory pubKeys,
                uint256[] memory weights,
                uint256 weightThreshold,
                uint256 height
            ) = abi.decode(
                    payload,
                    (uint256, bytes[], uint256[], uint256, uint256)
                );
            // Since dynamic arrays can variably insert more slots of data for things such as data length,
            // offset etc., we will just encode the received variables again and check for a length match.
            bytes memory reEncodedPayload = abi.encode(
                epoch,
                pubKeys,
                weights,
                weightThreshold,
                height
            );
            if (reEncodedPayload.length != payload.length) revert PayloadTooLarge();
            if (
                pubKeys.length < MIN_VALIDATOR_SET_SIZE ||
                pubKeys.length > MAX_VALIDATOR_SET_SIZE
            ) revert InvalidValidatorSetSize();
            if (pubKeys.length != weights.length) revert Actions_LengthMismatch();
            if (weightThreshold == 0) revert InvalidThreshold();
            uint256 sum = 0;
            for (uint256 i; i < weights.length; ) {
                if (weights[i] == 0) {
                    revert ZeroWeight();
                }
                sum += weights[i];
                unchecked {
                    ++i;
                }
            }
            if (sum < weightThreshold) revert InvalidThreshold();
            address[] memory validators = pubKeysToAddress(pubKeys);
            return
                ValSetAction(epoch, validators, weights, weightThreshold, height);
        }
        function pubKeysToAddress(
            bytes[] memory _pubKeys
        ) internal pure returns (address[] memory) {
            address[] memory addresses = new address[](_pubKeys.length);
            for (uint256 i; i < _pubKeys.length; ) {
                // each pubkey represented as uncompressed
                if (_pubKeys[i].length == 65) {
                    bytes memory data = _pubKeys[i];
                    // Ensure that first byte of pubkey is 0x04
                    if (_pubKeys[i][0] != 0x04)
                        revert InvalidPublicKey(_pubKeys[i]);
                    // create a new array with length - 1 (excluding the first 0x04 byte)
                    bytes memory result = new bytes(data.length - 1);
                    // use inline assembly for memory manipulation
                    assembly {
                        // calculate the start of the `result` and `data` in memory
                        let resultData := add(result, 0x20) // points to the first byte of the result
                        let dataStart := add(data, 0x21) // points to the second byte of data (skip 0x04)
                        // copy 64 bytes from input (excluding the first byte) to result
                        mstore(resultData, mload(dataStart)) // copy the first 32 bytes
                        mstore(add(resultData, 0x20), mload(add(dataStart, 0x20))) // copy the next 32 bytes
                    }
                    addresses[i] = address(uint160(uint256(keccak256(result))));
                } else {
                    revert InvalidPublicKey(_pubKeys[i]);
                }
                unchecked {
                    ++i;
                }
            }
            return addresses;
        }
        /**
         * @notice Returns decoded fee approval
         * @dev Payload should not contain the selector
         * @param payload Body of the fee approval payload
         */
        function feeApproval(
            bytes memory payload
        ) internal view returns (FeeApprovalAction memory) {
            if (payload.length != ABI_SLOT_SIZE * 2) revert PayloadTooLarge();
            (uint256 fee, uint256 expiry) = abi.decode(payload, (uint256, uint256));
            if (block.timestamp > expiry) {
                revert UserSignatureExpired(expiry);
            }
            if (fee == 0) {
                revert ZeroFee();
            }
            return FeeApprovalAction(fee, expiry);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
    enum OutputType {
        UNSUPPORTED,
        P2TR,
        P2WPKH,
        P2WSH
    }
    bytes1 constant OP_0 = 0x00;
    bytes1 constant OP_1 = 0x51;
    bytes1 constant OP_DATA_32 = 0x20;
    bytes1 constant OP_DATA_20 = 0x14;
    uint256 constant BASE_SPEND_COST = 49; // 32 (txid) + 4 (vout) + 1 (scriptSig size) + 4 (nSequence) + 8 (amount)
    // Size of inputs spending different output types
    uint256 constant NON_WITNESS_INPUT_SIZE = 107; // Used for non-witness outputs (P2PKH, P2SH)
    uint256 constant WITNESS_INPUT_SIZE = 26; // floor(107 / 4), used for witness outputs (P2WPKH, P2WSH, P2TR)
    library BitcoinUtils {
        uint256 public constant DEFAULT_DUST_FEE_RATE = 3000; // Default value - 3 satoshis per byte
        function getOutputType(
            bytes calldata scriptPubkey
        ) internal pure returns (OutputType) {
            if (
                scriptPubkey.length == 22 &&
                scriptPubkey[0] == OP_0 &&
                scriptPubkey[1] == OP_DATA_20
            ) {
                return OutputType.P2WPKH;
            }
            if (
                scriptPubkey.length == 34 &&
                scriptPubkey[0] == OP_1 &&
                scriptPubkey[1] == OP_DATA_32
            ) {
                return OutputType.P2TR;
            }
            if (
                scriptPubkey.length == 34 &&
                scriptPubkey[0] == OP_0 &&
                scriptPubkey[1] == OP_DATA_32
            ) {
                return OutputType.P2WSH;
            }
            return OutputType.UNSUPPORTED;
        }
        /// @notice Compute the dust limit for a given Bitcoin script public key
        /// @dev The dust limit is the minimum payment to an address that is considered
        ///      spendable under consensus rules. This function is based on Bitcoin Core's
        ///      implementation.
        /// @param scriptPubkey The Bitcoin script public key as a byte array
        /// @param dustFeeRate The current dust fee rate (in satoshis per 1000 bytes)
        /// @return dustLimit The calculated dust limit in satoshis
        /// @custom:reference https://github.com/bitcoin/bitcoin/blob/43740f4971f45cd5499470b6a085b3ecd8b96d28/src/policy/policy.cpp#L54
        function getDustLimitForOutput(
            OutputType outType,
            bytes calldata scriptPubkey,
            uint256 dustFeeRate
        ) internal pure returns (uint256 dustLimit) {
            uint256 spendCost = BASE_SPEND_COST;
            if (
                outType == OutputType.P2TR ||
                outType == OutputType.P2WPKH ||
                outType == OutputType.P2WSH
            ) {
                // witness v0 and v1 has a cheaper payment formula
                spendCost += WITNESS_INPUT_SIZE;
                // The current addition creates a discrepancy of 1, and our final value should be 98 bytes.
                // Thus, we add 1 here.
                spendCost += 1;
            } else {
                spendCost += NON_WITNESS_INPUT_SIZE;
            }
            spendCost += scriptPubkey.length;
            // Calculate dust limit
            dustLimit = Math.ceilDiv(spendCost * dustFeeRate, 1000);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol";
    import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
    /**
     * @title Library of utilities for making EIP1271-compliant signature checks.
     * @author Lombard.Finance
     * @notice The contracts is a part of Lombard.Finace protocol
     */
    library EIP1271SignatureUtils {
        // bytes4(keccak256("isValidSignature(bytes32,bytes)")
        bytes4 internal constant EIP1271_MAGICVALUE = 0x1626ba7e;
        bytes4 internal constant EIP1271_WRONGVALUE = 0xffffffff;
        /**
         * @notice Checks @param signature is a valid signature of @param digest from @param signer.
         * If the `signer` contains no code -- i.e. it is not (yet, at least) a contract address, then checks using standard ECDSA logic
         * Otherwise, passes on the signature to the signer to verify the signature and checks that it returns the `EIP1271_MAGICVALUE`.
         */
        function checkSignature(
            address signer,
            bytes32 digest,
            bytes memory signature
        ) internal view returns (bool) {
            if (signer.code.length != 0) {
                if (
                    IERC1271(signer).isValidSignature(digest, signature) !=
                    EIP1271_MAGICVALUE
                ) {
                    return false;
                }
            } else {
                if (ECDSA.recover(digest, signature) != signer) {
                    return false;
                }
            }
            return true;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
    library FeeUtils {
        uint256 constant MAX_COMMISSION = 10000; // 100%
        error AmountTooSmallToPayRelativeFee();
        error BadCommission();
        function calcRelativeFee(
            uint256 amount,
            uint16 relativeComs
        ) internal pure returns (uint256) {
            return
                Math.mulDiv(
                    amount,
                    relativeComs,
                    MAX_COMMISSION,
                    Math.Rounding.Ceil
                );
        }
        function getRelativeFee(
            uint256 amount,
            uint16 relativeComs
        ) internal pure returns (uint256) {
            if (amount < relativeComs) revert AmountTooSmallToPayRelativeFee();
            return calcRelativeFee(amount, relativeComs);
        }
        function validateCommission(uint16 commission) internal pure {
            if (commission >= MAX_COMMISSION) revert BadCommission();
        }
    }