ETH Price: $2,258.17 (-7.66%)

Transaction Decoder

Block:
21183027 at Nov-14-2024 02:44:23 AM +UTC
Transaction Fee:
0.002233018151450955 ETH $5.04
Gas Used:
51,435 Gas / 43.414370593 Gwei

Emitted Events:

266 TransparentUpgradeableProxy.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x000000000000000000000000b072acf77eb3b09279b433679002045d33668817, 0x000000000000000000000000c92e8bdf79f0507f65a392b0ab4667716bfe0110, 000000000000000000000000000000000000000000000000d02ab486cedc0000 )

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
12.769059028529436245 Eth12.769158812429436245 Eth0.0000997839
0x8c9532a6...E1c3E9811
0xB072ACf7...d33668817
0.394727437992569316 Eth
Nonce: 39
0.392494419841118361 Eth
Nonce: 40
0.002233018151450955

Execution Trace

TransparentUpgradeableProxy.095ea7b3( )
  • Vault.approve( spender=0xC92E8bdf79f0507f65a392b0ab4667716BFE0110, value=15000000000000000000 ) => ( True )
    File 1 of 2: TransparentUpgradeableProxy
    // 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) (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/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) (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/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/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) (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.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
            }
        }
    }
    // 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) (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.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;
        }
    }
    

    File 2 of 2: Vault
    // SPDX-License-Identifier: BSL-1.1
    pragma solidity 0.8.25;
    import "./interfaces/IVault.sol";
    import "./utils/DefaultAccessControl.sol";
    import "./libraries/external/FullMath.sol";
    import "./VaultConfigurator.sol";
    contract Vault is IVault, ERC20, DefaultAccessControl, ReentrancyGuard {
        using EnumerableSet for EnumerableSet.AddressSet;
        using SafeERC20 for IERC20;
        /// @inheritdoc IVault
        uint256 public constant Q96 = 2 ** 96;
        /// @inheritdoc IVault
        uint256 public constant D9 = 1e9;
        /// @inheritdoc IVault
        IVaultConfigurator public configurator;
        mapping(address => WithdrawalRequest) private _withdrawalRequest;
        EnumerableSet.AddressSet private _pendingWithdrawers;
        address[] private _underlyingTokens;
        mapping(address => bool) private _isUnderlyingToken;
        EnumerableSet.AddressSet private _tvlModules;
        modifier checkDeadline(uint256 deadline) {
            if (deadline < block.timestamp) revert Deadline();
            _;
        }
        /// @inheritdoc IVault
        function withdrawalRequest(
            address user
        ) external view returns (WithdrawalRequest memory) {
            return _withdrawalRequest[user];
        }
        /// @inheritdoc IVault
        function pendingWithdrawersCount() external view returns (uint256) {
            return _pendingWithdrawers.length();
        }
        /// @inheritdoc IVault
        function pendingWithdrawers(
            uint256 limit,
            uint256 offset
        ) external view returns (address[] memory result) {
            EnumerableSet.AddressSet storage withdrawers_ = _pendingWithdrawers;
            uint256 count = withdrawers_.length();
            if (offset >= count || limit == 0) return result;
            count -= offset;
            if (count > limit) count = limit;
            result = new address[](count);
            for (uint256 i = 0; i < count; i++) {
                result[i] = withdrawers_.at(offset + i);
            }
            return result;
        }
        /// @inheritdoc IVault
        function pendingWithdrawers() external view returns (address[] memory) {
            return _pendingWithdrawers.values();
        }
        /// @inheritdoc IVault
        function underlyingTokens() external view returns (address[] memory) {
            return _underlyingTokens;
        }
        /// @inheritdoc IVault
        function isUnderlyingToken(
            address token
        ) external view returns (bool isUnderlying) {
            return _isUnderlyingToken[token];
        }
        /// @inheritdoc IVault
        function tvlModules() external view returns (address[] memory) {
            return _tvlModules.values();
        }
        function _calculateTvl(
            address[] memory tokens,
            bool isUnderlying
        ) private view returns (uint256[] memory amounts) {
            amounts = new uint256[](tokens.length);
            uint256[] memory negativeAmounts = new uint256[](tokens.length);
            ITvlModule.Data[] memory tvl_ = _tvls();
            ITvlModule.Data memory data;
            for (uint256 i = 0; i < tvl_.length; i++) {
                data = tvl_[i];
                (uint256 amount, address token) = isUnderlying
                    ? (data.underlyingAmount, data.underlyingToken)
                    : (data.amount, data.token);
                for (uint256 j = 0; j < tokens.length; j++) {
                    if (token != tokens[j]) continue;
                    (data.isDebt ? negativeAmounts : amounts)[j] += amount;
                    break;
                }
            }
            for (uint256 i = 0; i < tokens.length; i++) {
                if (amounts[i] < negativeAmounts[i]) revert InvalidState();
                amounts[i] -= negativeAmounts[i];
            }
        }
        /// @inheritdoc IVault
        function underlyingTvl()
            public
            view
            returns (address[] memory tokens, uint256[] memory amounts)
        {
            tokens = _underlyingTokens;
            amounts = _calculateTvl(tokens, true);
        }
        /// @inheritdoc IVault
        function baseTvl()
            public
            view
            returns (address[] memory tokens, uint256[] memory amounts)
        {
            ITvlModule.Data[] memory data = _tvls();
            tokens = new address[](data.length);
            uint256 length = 0;
            for (uint256 i = 0; i < data.length; i++) {
                if (data[i].token == address(0)) continue;
                uint256 tokenIndex = length;
                for (uint256 j = 0; j < length; j++) {
                    if (tokens[j] != data[i].token) continue;
                    tokenIndex = j;
                    break;
                }
                if (tokenIndex != length) continue;
                tokens[tokenIndex] = data[i].token;
                length++;
            }
            for (uint256 i = 0; i < length; i++) {
                for (uint256 j = i + 1; j < length; j++) {
                    if (tokens[i] < tokens[j]) continue;
                    (tokens[i], tokens[j]) = (tokens[j], tokens[i]);
                }
            }
            assembly {
                mstore(tokens, length)
            }
            amounts = _calculateTvl(tokens, false);
        }
        function _tvls() private view returns (ITvlModule.Data[] memory data) {
            ITvlModule.Data[][] memory responses = new ITvlModule.Data[][](
                _tvlModules.length()
            );
            uint256 length = 0;
            for (uint256 i = 0; i < responses.length; i++) {
                address module = _tvlModules.at(i);
                responses[i] = ITvlModule(module).tvl(address(this));
                length += responses[i].length;
            }
            data = new ITvlModule.Data[](length);
            uint256 index = 0;
            for (uint256 i = 0; i < responses.length; i++) {
                for (uint256 j = 0; j < responses[i].length; j++) {
                    data[index++] = responses[i][j];
                }
            }
        }
        constructor(
            string memory name_,
            string memory symbol_,
            address admin
        ) ERC20(name_, symbol_) DefaultAccessControl(admin) {
            configurator = new VaultConfigurator();
        }
        /// @inheritdoc IVault
        function addToken(address token) external nonReentrant {
            _requireAdmin();
            if (token == address(0)) revert InvalidToken();
            if (_isUnderlyingToken[token]) revert InvalidToken();
            if (token == address(this)) revert InvalidToken();
            _isUnderlyingToken[token] = true;
            address[] storage tokens = _underlyingTokens;
            tokens.push(token);
            uint256 n = tokens.length;
            uint256 index = 0;
            for (uint256 i = 1; i < n; i++) {
                address token_ = tokens[n - 1 - i];
                if (token_ < token) {
                    index = n - i;
                    break;
                }
                tokens[n - i] = token_;
            }
            tokens[index] = token;
            emit TokenAdded(token);
        }
        /// @inheritdoc IVault
        function removeToken(address token) external nonReentrant {
            _requireAdmin();
            if (!_isUnderlyingToken[token]) revert InvalidToken();
            (address[] memory tokens, uint256[] memory amounts) = underlyingTvl();
            uint256 index = tokens.length;
            for (uint256 i = 0; i < tokens.length; i++) {
                if (tokens[i] == token) {
                    if (amounts[i] != 0) revert NonZeroValue();
                    index = i;
                    break;
                }
            }
            _isUnderlyingToken[token] = false;
            while (index + 1 < tokens.length) {
                _underlyingTokens[index] = tokens[index + 1];
                index++;
            }
            _underlyingTokens.pop();
            emit TokenRemoved(token);
        }
        /// @inheritdoc IVault
        function addTvlModule(address module) external nonReentrant {
            _requireAdmin();
            ITvlModule.Data[] memory data = ITvlModule(module).tvl(address(this));
            for (uint256 i = 0; i < data.length; i++) {
                if (!_isUnderlyingToken[data[i].underlyingToken])
                    revert InvalidToken();
            }
            if (!_tvlModules.add(module)) {
                revert AlreadyAdded();
            }
            emit TvlModuleAdded(module);
        }
        /// @inheritdoc IVault
        function removeTvlModule(address module) external nonReentrant {
            _requireAdmin();
            if (!_tvlModules.contains(module)) revert InvalidState();
            _tvlModules.remove(module);
            emit TvlModuleRemoved(module);
        }
        /// @inheritdoc IVault
        function externalCall(
            address to,
            bytes calldata data
        ) external nonReentrant returns (bool success, bytes memory response) {
            _requireAtLeastOperator();
            if (configurator.isDelegateModuleApproved(to)) revert Forbidden();
            IValidator validator = IValidator(configurator.validator());
            validator.validate(
                msg.sender,
                address(this),
                abi.encodeWithSelector(msg.sig, to, data)
            );
            validator.validate(address(this), to, data);
            (success, response) = to.call(data);
            emit ExternalCall(to, data, success, response);
        }
        /// @inheritdoc IVault
        function delegateCall(
            address to,
            bytes calldata data
        ) external returns (bool success, bytes memory response) {
            _requireAtLeastOperator();
            if (!configurator.isDelegateModuleApproved(to)) revert Forbidden();
            IValidator validator = IValidator(configurator.validator());
            validator.validate(
                msg.sender,
                address(this),
                abi.encodeWithSelector(msg.sig, to, data)
            );
            validator.validate(address(this), to, data);
            (success, response) = to.delegatecall(data);
            emit DelegateCall(to, data, success, response);
        }
        /// @inheritdoc IVault
        function deposit(
            address to,
            uint256[] memory amounts,
            uint256 minLpAmount,
            uint256 deadline
        )
            external
            nonReentrant
            checkDeadline(deadline)
            returns (uint256[] memory actualAmounts, uint256 lpAmount)
        {
            if (configurator.isDepositLocked()) revert Forbidden();
            IValidator(configurator.validator()).validate(
                msg.sender,
                address(this),
                abi.encodeWithSelector(msg.sig)
            );
            (
                address[] memory tokens,
                uint256[] memory totalAmounts
            ) = underlyingTvl();
            if (tokens.length != amounts.length) revert InvalidLength();
            uint128[] memory ratiosX96 = IRatiosOracle(configurator.ratiosOracle())
                .getTargetRatiosX96(address(this), true);
            uint256 ratioX96 = type(uint256).max;
            for (uint256 i = 0; i < tokens.length; i++) {
                if (ratiosX96[i] == 0) continue;
                uint256 ratioX96_ = FullMath.mulDiv(amounts[i], Q96, ratiosX96[i]);
                if (ratioX96_ < ratioX96) ratioX96 = ratioX96_;
            }
            if (ratioX96 == 0) revert ValueZero();
            uint256 depositValue = 0;
            uint256 totalValue = 0;
            actualAmounts = new uint256[](tokens.length);
            IPriceOracle priceOracle = IPriceOracle(configurator.priceOracle());
            for (uint256 i = 0; i < tokens.length; i++) {
                uint256 priceX96 = priceOracle.priceX96(address(this), tokens[i]);
                totalValue += totalAmounts[i] == 0
                    ? 0
                    : FullMath.mulDivRoundingUp(totalAmounts[i], priceX96, Q96);
                if (ratiosX96[i] == 0) continue;
                uint256 amount = FullMath.mulDiv(ratioX96, ratiosX96[i], Q96);
                IERC20(tokens[i]).safeTransferFrom(
                    msg.sender,
                    address(this),
                    amount
                );
                actualAmounts[i] = amount;
                depositValue += FullMath.mulDiv(amount, priceX96, Q96);
            }
            lpAmount = _processLpAmount(to, depositValue, totalValue, minLpAmount);
            emit Deposit(to, actualAmounts, lpAmount);
            address callback = configurator.depositCallback();
            if (callback == address(0)) return (actualAmounts, lpAmount);
            IDepositCallback(callback).depositCallback(actualAmounts, lpAmount);
            emit DepositCallback(callback, actualAmounts, lpAmount);
        }
        function _processLpAmount(
            address to,
            uint256 depositValue,
            uint256 totalValue,
            uint256 minLpAmount
        ) private returns (uint256 lpAmount) {
            uint256 totalSupply = totalSupply();
            if (totalSupply == 0) {
                // scenario for initial deposit
                _requireAtLeastOperator();
                lpAmount = minLpAmount;
                if (lpAmount == 0) revert ValueZero();
                if (to != address(this)) revert Forbidden();
            } else {
                lpAmount = FullMath.mulDiv(depositValue, totalSupply, totalValue);
                if (lpAmount < minLpAmount) revert InsufficientLpAmount();
                if (to == address(0)) revert AddressZero();
            }
            if (lpAmount + totalSupply > configurator.maximalTotalSupply())
                revert LimitOverflow();
            _mint(to, lpAmount);
        }
        /// @inheritdoc IVault
        function emergencyWithdraw(
            uint256[] memory minAmounts,
            uint256 deadline
        )
            external
            nonReentrant
            checkDeadline(deadline)
            returns (uint256[] memory actualAmounts)
        {
            uint256 timestamp = block.timestamp;
            address sender = msg.sender;
            if (!_pendingWithdrawers.contains(sender)) revert InvalidState();
            WithdrawalRequest memory request = _withdrawalRequest[sender];
            if (timestamp > request.deadline) {
                _cancelWithdrawalRequest(sender);
                return actualAmounts;
            }
            if (
                request.timestamp + configurator.emergencyWithdrawalDelay() >
                timestamp
            ) revert InvalidState();
            uint256 totalSupply = totalSupply();
            (address[] memory tokens, uint256[] memory amounts) = baseTvl();
            if (minAmounts.length != tokens.length) revert InvalidLength();
            actualAmounts = new uint256[](tokens.length);
            for (uint256 i = 0; i < tokens.length; i++) {
                if (amounts[i] == 0) {
                    if (minAmounts[i] != 0) revert InsufficientAmount();
                    continue;
                }
                uint256 amount = FullMath.mulDiv(
                    IERC20(tokens[i]).balanceOf(address(this)),
                    request.lpAmount,
                    totalSupply
                );
                if (amount < minAmounts[i]) revert InsufficientAmount();
                IERC20(tokens[i]).safeTransfer(request.to, amount);
                actualAmounts[i] = amount;
            }
            delete _withdrawalRequest[sender];
            _pendingWithdrawers.remove(sender);
            _burn(address(this), request.lpAmount);
            emit EmergencyWithdrawal(sender, request, actualAmounts);
        }
        /// @inheritdoc IVault
        function cancelWithdrawalRequest() external nonReentrant {
            address sender = msg.sender;
            if (!_pendingWithdrawers.contains(sender)) return;
            _cancelWithdrawalRequest(sender);
        }
        function _cancelWithdrawalRequest(address sender) private {
            WithdrawalRequest memory request = _withdrawalRequest[sender];
            delete _withdrawalRequest[sender];
            _pendingWithdrawers.remove(sender);
            _transfer(address(this), sender, request.lpAmount);
            emit WithdrawalRequestCanceled(sender, tx.origin);
        }
        /// @inheritdoc IVault
        function registerWithdrawal(
            address to,
            uint256 lpAmount,
            uint256[] memory minAmounts,
            uint256 deadline,
            uint256 requestDeadline,
            bool closePrevious
        )
            external
            nonReentrant
            checkDeadline(deadline)
            checkDeadline(requestDeadline)
        {
            uint256 timestamp = block.timestamp;
            address sender = msg.sender;
            if (_pendingWithdrawers.contains(sender)) {
                if (!closePrevious) revert InvalidState();
                _cancelWithdrawalRequest(sender);
            }
            uint256 balance = balanceOf(sender);
            if (lpAmount > balance) lpAmount = balance;
            if (lpAmount == 0) revert ValueZero();
            if (to == address(0)) revert AddressZero();
            address[] memory tokens = _underlyingTokens;
            if (tokens.length != minAmounts.length) revert InvalidLength();
            WithdrawalRequest memory request = WithdrawalRequest({
                to: to,
                lpAmount: lpAmount,
                tokensHash: keccak256(abi.encode(tokens)),
                minAmounts: minAmounts,
                deadline: requestDeadline,
                timestamp: timestamp
            });
            _withdrawalRequest[sender] = request;
            _pendingWithdrawers.add(sender);
            _transfer(sender, address(this), lpAmount);
            emit WithdrawalRequested(sender, request);
        }
        /// @inheritdoc IVault
        function analyzeRequest(
            ProcessWithdrawalsStack memory s,
            WithdrawalRequest memory request
        ) public pure returns (bool, bool, uint256[] memory expectedAmounts) {
            uint256 lpAmount = request.lpAmount;
            if (
                request.tokensHash != s.tokensHash || request.deadline < s.timestamp
            ) return (false, false, expectedAmounts);
            uint256 value = FullMath.mulDiv(lpAmount, s.totalValue, s.totalSupply);
            value = FullMath.mulDiv(value, D9 - s.feeD9, D9);
            uint256 coefficientX96 = FullMath.mulDiv(value, Q96, s.ratiosX96Value);
            uint256 length = s.erc20Balances.length;
            expectedAmounts = new uint256[](length);
            for (uint256 i = 0; i < length; i++) {
                uint256 ratiosX96 = s.ratiosX96[i];
                expectedAmounts[i] = ratiosX96 == 0
                    ? 0
                    : FullMath.mulDiv(coefficientX96, ratiosX96, Q96);
                if (expectedAmounts[i] >= request.minAmounts[i]) continue;
                return (false, false, expectedAmounts);
            }
            for (uint256 i = 0; i < length; i++) {
                if (s.erc20Balances[i] >= expectedAmounts[i]) continue;
                return (true, false, expectedAmounts);
            }
            return (true, true, expectedAmounts);
        }
        /// @inheritdoc IVault
        function calculateStack()
            public
            view
            returns (ProcessWithdrawalsStack memory s)
        {
            (address[] memory tokens, uint256[] memory amounts) = underlyingTvl();
            s = ProcessWithdrawalsStack({
                tokens: tokens,
                ratiosX96: IRatiosOracle(configurator.ratiosOracle())
                    .getTargetRatiosX96(address(this), false),
                erc20Balances: new uint256[](tokens.length),
                totalSupply: totalSupply(),
                totalValue: 0,
                ratiosX96Value: 0,
                timestamp: block.timestamp,
                feeD9: configurator.withdrawalFeeD9(),
                tokensHash: keccak256(abi.encode(tokens))
            });
            IPriceOracle priceOracle = IPriceOracle(configurator.priceOracle());
            for (uint256 i = 0; i < tokens.length; i++) {
                uint256 priceX96 = priceOracle.priceX96(address(this), tokens[i]);
                s.totalValue += FullMath.mulDiv(amounts[i], priceX96, Q96);
                s.ratiosX96Value += FullMath.mulDiv(s.ratiosX96[i], priceX96, Q96);
                s.erc20Balances[i] = IERC20(tokens[i]).balanceOf(address(this));
            }
        }
        /// @inheritdoc IVault
        function processWithdrawals(
            address[] memory users
        ) external nonReentrant returns (bool[] memory statuses) {
            _requireAtLeastOperator();
            statuses = new bool[](users.length);
            ProcessWithdrawalsStack memory s = calculateStack();
            uint256 burningSupply = 0;
            for (uint256 i = 0; i < users.length; i++) {
                address user = users[i];
                if (!_pendingWithdrawers.contains(user)) continue;
                WithdrawalRequest memory request = _withdrawalRequest[user];
                (
                    bool isProcessingPossible,
                    bool isWithdrawalPossible,
                    uint256[] memory expectedAmounts
                ) = analyzeRequest(s, request);
                if (!isProcessingPossible) {
                    _cancelWithdrawalRequest(user);
                    continue;
                }
                if (!isWithdrawalPossible) continue;
                for (uint256 j = 0; j < s.tokens.length; j++) {
                    s.erc20Balances[j] -= expectedAmounts[j];
                    IERC20(s.tokens[j]).safeTransfer(
                        request.to,
                        expectedAmounts[j]
                    );
                }
                burningSupply += request.lpAmount;
                delete _withdrawalRequest[user];
                _pendingWithdrawers.remove(user);
                statuses[i] = true;
            }
            if (burningSupply == 0) return statuses;
            _burn(address(this), burningSupply);
            emit WithdrawalsProcessed(users, statuses);
            address callback = configurator.withdrawalCallback();
            if (callback == address(0)) return statuses;
            IWithdrawalCallback(callback).withdrawalCallback();
            emit WithdrawCallback(callback);
        }
        receive() external payable {}
        function _update(
            address from,
            address to,
            uint256 value
        ) internal override {
            if (configurator.areTransfersLocked()) {
                address this_ = address(this);
                address zero_ = address(0);
                if (from != this_ && to != this_ && from != zero_ && to != zero_)
                    revert Forbidden();
            }
            super._update(from, to, value);
        }
    }
    // SPDX-License-Identifier: BSL-1.1
    pragma solidity 0.8.25;
    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
    import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
    import "@openzeppelin/contracts/utils/Arrays.sol";
    import "./modules/ITvlModule.sol";
    import "./validators/IValidator.sol";
    import "./oracles/IPriceOracle.sol";
    import "./oracles/IRatiosOracle.sol";
    import "./utils/IDepositCallback.sol";
    import "./utils/IWithdrawalCallback.sol";
    import "./IVaultConfigurator.sol";
    /**
     * @title IVault
     * @notice Interface defining core methods, constants, and errors for vault contracts.
     * Includes events, data structures, functions, and permissions required for managing the vault.
     * @dev Main contract of the system managing interactions between users, administrators, and operators.
     *      System parameters are set within the corresponding contract - VaultConfigurator.
     *      Upon deposit, LP tokens are issued to users based on asset valuation by oracles.
     *      Deposits are made through the deposit function, where a deposit can only be made in underlyingTokens and
     *      only at the specified ratiosOracle ratio. Deposits can be paused by setting the isDepositLocked flag.
     *
     *      Withdrawals can occur through two scenarios:
     *          - Regular withdrawal via the registerWithdrawal function and emergency withdrawal via the emergencyWithdraw function.
     *          In a regular withdrawal, the user registers a withdrawal request, after which the operator must perform a series of operations
     *          to ensure there are enough underlyingTokens on the vault's balance to fulfill the user's request. Subsequently, the operator must call
     *          the processWithdrawals function. If a user's request is not processed within the emergencyWithdrawalDelay period, the user can perform an emergency withdrawal.
     *          Note! In this case, the user may receive less funds than entitled by the system, as this function only handles ERC20 tokens in the system.
     *          Therefore, if the system has a base asset that is not represented as an ERC20 token, the corresponding portion of the funds will be lost by the user.
     *
     *      It is assumed that the main system management will occur through calls to delegateModules via delegateCalls on behalf of the operator.
     *      For this to be possible, certain conditions must be met:
     *          - From the validator's perspective, two conditions must be met:
     *              1. The caller must have the right to call the delegateCall function with the corresponding data parameter.
     *              2. The contract itself must be able to call the function on the delegateModule with the specified data.
     *          - From the configurator's perspective, the called module must have the appropriate approval - isDelegateModuleApproved.
     *
     *      If external calls need to be made, the externalCall function is used, for the execution of which a similar set of properties exists:
     *          - From the validator's perspective, two conditions must be met:
     *              1. The caller must have the right to call the externalCall function with the corresponding data parameter.
     *              2. The contract itself must be able to call the function on the external contract with the specified data.
     *          - From the configurator's perspective, the called contract must NOT have isDelegateModuleApproved permission.
     *
     *      Vault also has the functionality of adding and removing underlyingTokens, as well as tvlModules.
     *      For this purpose, the following functions are available, which can only be called by the vault's admin:
     *          - addToken
     *          - removeToken
     *          - addTvlModule
     *          - removeTvlModule
     *      Upon calling removeToken, it is checked that the underlyingTvl function for the specified token returns a zero value. Otherwise, the function reverts with a NonZeroValue error.
     *      It is important to note that there is no such check when calling removeTvlModule, so when updating parameters, sequential execution of a transaction to remove the old and add the new tvlModule is implied.
     */
    interface IVault is IERC20 {
        /// @dev Errors
        error Deadline();
        error InvalidState();
        error InvalidLength();
        error InvalidToken();
        error NonZeroValue();
        error ValueZero();
        error InsufficientLpAmount();
        error InsufficientAmount();
        error LimitOverflow();
        error AlreadyAdded();
        /// @notice Struct representing a user's withdrawal request.
        struct WithdrawalRequest {
            address to;
            uint256 lpAmount;
            bytes32 tokensHash; // keccak256 hash of the tokens array at the moment of request
            uint256[] minAmounts;
            uint256 deadline;
            uint256 timestamp;
        }
        /// @notice Struct representing the current state used for processing withdrawals.
        struct ProcessWithdrawalsStack {
            address[] tokens;
            uint128[] ratiosX96;
            uint256[] erc20Balances;
            uint256 totalSupply;
            uint256 totalValue;
            uint256 ratiosX96Value;
            uint256 timestamp;
            uint256 feeD9;
            bytes32 tokensHash; // keccak256 hash of the tokens array at the moment of the call
        }
        /// @notice 2^96, used for fixed-point arithmetic
        function Q96() external view returns (uint256);
        /// @notice Multiplier of 1e9
        function D9() external view returns (uint256);
        /// @notice Returns the vault's configurator, which handles permissions and configuration settings.
        /// @return IVaultConfigurator The address of the configurator contract.
        function configurator() external view returns (IVaultConfigurator);
        /// @notice Returns the withdrawal request of a given user.
        /// @param user The address of the user.
        /// @return request The withdrawal request associated with the user.
        function withdrawalRequest(
            address user
        ) external view returns (WithdrawalRequest memory request);
        /// @return count The number of users with pending withdrawal requests.
        function pendingWithdrawersCount() external view returns (uint256 count);
        /// @notice Returns an array of addresses with pending withdrawal requests.
        /// @return users An array of addresses with pending withdrawal requests.
        function pendingWithdrawers()
            external
            view
            returns (address[] memory users);
        /// @notice Returns an array of addresses with pending withdrawal requests.
        /// @param limit The maximum number of users to return.
        /// @param offset The number of users to skip before returning.
        /// @return users An array of addresses with pending withdrawal requests.
        function pendingWithdrawers(
            uint256 limit,
            uint256 offset
        ) external view returns (address[] memory users);
        /// @notice Returns an array of underlying tokens of the vault.
        /// @return underlyinigTokens_ An array of underlying token addresses.
        function underlyingTokens()
            external
            view
            returns (address[] memory underlyinigTokens_);
        /// @notice Checks if a token is an underlying token of the vault.
        /// @return isUnderlyingToken_ true if the token is an underlying token of the vault.
        function isUnderlyingToken(
            address token
        ) external view returns (bool isUnderlyingToken_);
        /// @notice Returns an array of addresses of all TVL modules.
        /// @return tvlModules_ An array of TVL module addresses.
        function tvlModules() external view returns (address[] memory tvlModules_);
        /// @notice Calculates and returns the total value locked (TVL) of the underlying tokens.
        /// @return tokens An array of underlying token addresses.
        /// @return amounts An array of the amounts of each underlying token in the TVL.
        function underlyingTvl()
            external
            view
            returns (address[] memory tokens, uint256[] memory amounts);
        /// @notice Calculates and returns the base TVL (Total Value Locked) across all tokens in the vault.
        /// @return tokens An array of token addresses.
        /// @return amounts An array of the amounts of each token in the base TVL.
        function baseTvl()
            external
            view
            returns (address[] memory tokens, uint256[] memory amounts);
        /// @notice Adds a new token to the list of underlying tokens in the vault.
        /// @dev Only accessible by an admin.
        /// @param token The address of the token to add.
        function addToken(address token) external;
        /// @notice Removes a token from the list of underlying tokens in the vault.
        /// @dev Only accessible by an admin.
        /// @param token The address of the token to remove.
        function removeToken(address token) external;
        /// @notice Adds a new TVL module to the vault.
        /// @dev Only accessible by an admin.
        /// @param module The address of the TVL module to add.
        function addTvlModule(address module) external;
        /// @notice Removes an existing TVL module from the vault.
        /// @dev Only accessible by an admin.
        /// @param module The address of the TVL module to remove.
        function removeTvlModule(address module) external;
        /// @notice Performs an external call to a given address with specified data.
        /// @dev Only operators or admins should call this function. Checks access permissions.
        /// @param to The address to which the call will be made.
        /// @param data The calldata to use for the external call.
        /// @return success Indicates if the call was successful.
        /// @return response The response data from the external call.
        /// @dev Checks permissions using the validator from the configurator.
        function externalCall(
            address to,
            bytes calldata data
        ) external returns (bool success, bytes memory response);
        /// @notice Executes a delegate call to a specified address with given data.
        /// @dev Only operators or admins should call this function. Checks access permissions.
        /// @param to The address to which the delegate call will be made.
        /// @param data The calldata to use for the delegate call.
        /// @return success Indicates if the delegate call was successful.
        /// @return response The response data from the delegate call.
        /// @dev Checks permissions using the validator from the configurator.
        function delegateCall(
            address to,
            bytes calldata data
        ) external returns (bool success, bytes memory response);
        /// @notice Deposits specified amounts of tokens into the vault in exchange for LP tokens.
        /// @dev Only accessible when deposits are unlocked.
        /// @param to The address to receive LP tokens.
        /// @param amounts An array specifying the amounts for each underlying token.
        /// @param minLpAmount The minimum amount of LP tokens to mint.
        /// @param deadline The time before which the operation must be completed.
        /// @return actualAmounts The actual amounts deposited for each underlying token.
        /// @return lpAmount The amount of LP tokens minted.
        function deposit(
            address to,
            uint256[] memory amounts,
            uint256 minLpAmount,
            uint256 deadline
        ) external returns (uint256[] memory actualAmounts, uint256 lpAmount);
        /// @notice Handles emergency withdrawals, proportionally withdrawing all tokens in the system (not just the underlying).
        /// @dev Transfers tokens based on the user's share of lpAmount / totalSupply.
        /// @param minAmounts An array of minimum amounts expected for each underlying token.
        /// @param deadline The time before which the operation must be completed.
        /// @return actualAmounts The actual amounts withdrawn for each token.
        function emergencyWithdraw(
            uint256[] memory minAmounts,
            uint256 deadline
        ) external returns (uint256[] memory actualAmounts);
        /// @notice Cancels a pending withdrawal request.
        function cancelWithdrawalRequest() external;
        /// @notice Registers a new withdrawal request, optionally closing previous requests.
        /// @param to The address to receive the withdrawn tokens.
        /// @param lpAmount The amount of LP tokens to withdraw.
        /// @param minAmounts An array specifying minimum amounts for each token.
        /// @param deadline The time before which the operation must be completed.
        /// @param requestDeadline The deadline before which the request should be fulfilled.
        /// @param closePrevious Whether to close a previous request if it exists.
        function registerWithdrawal(
            address to,
            uint256 lpAmount,
            uint256[] memory minAmounts,
            uint256 deadline,
            uint256 requestDeadline,
            bool closePrevious
        ) external;
        /// @notice Analyzes a withdrawal request based on the current vault state.
        /// @param s The current state stack to use for analysis.
        /// @param request The withdrawal request to analyze.
        /// @return processingPossible Whether processing is possible based on current vault state.
        /// @return withdrawalPossible Whether the withdrawal can be fulfilled.
        /// @return expectedAmounts The expected amounts to be withdrawn for each token.
        function analyzeRequest(
            ProcessWithdrawalsStack memory s,
            WithdrawalRequest memory request
        )
            external
            pure
            returns (
                bool processingPossible,
                bool withdrawalPossible,
                uint256[] memory expectedAmounts
            );
        /// @notice Calculates and returns the state stack required for processing withdrawal requests.
        /// @return s The state stack with current vault balances and data.
        function calculateStack()
            external
            view
            returns (ProcessWithdrawalsStack memory s);
        /// @notice Processes multiple withdrawal requests by fulfilling eligible withdrawals.
        /// @param users An array of user addresses whose withdrawal requests should be processed.
        /// @return statuses An array indicating the status of each user's withdrawal request.
        function processWithdrawals(
            address[] memory users
        ) external returns (bool[] memory statuses);
        /**
         * @notice Emitted when a token is added to the vault.
         * @param token The address of the token added.
         */
        event TokenAdded(address token);
        /**
         * @notice Emitted when a token is removed from the vault.
         * @param token The address of the token removed.
         */
        event TokenRemoved(address token);
        /**
         * @notice Emitted when a TVL module is added to the vault.
         * @param module The address of the TVL module added.
         */
        event TvlModuleAdded(address module);
        /**
         * @notice Emitted when a TVL module is removed from the vault.
         * @param module The address of the TVL module removed.
         */
        event TvlModuleRemoved(address module);
        /**
         * @notice Emitted when an external call is made.
         * @param to The address of the contract called.
         * @param data The calldata of the call.
         * @param success The success status of the call.
         * @param response The response data of the call.
         */
        event ExternalCall(
            address indexed to,
            bytes data,
            bool success,
            bytes response
        );
        /**
         * @notice Emitted when a delegate call is made.
         * @param to The address of the contract called.
         * @param data The calldata of the call.
         * @param success The success status of the call.
         * @param response The response data of the call.
         */
        event DelegateCall(
            address indexed to,
            bytes data,
            bool success,
            bytes response
        );
        /**
         * @notice Emitted when a deposit occurs.
         * @param to The address where LP tokens are deposited.
         * @param amounts The amounts of tokens deposited.
         * @param lpAmount The amount of LP tokens minted.
         */
        event Deposit(address indexed to, uint256[] amounts, uint256 lpAmount);
        /**
         * @notice Emitted when a deposit callback occurs.
         * @param callback The address of the deposit callback contract.
         * @param amounts The amounts of tokens deposited.
         * @param lpAmount The amount of LP tokens minted.
         */
        event DepositCallback(
            address indexed callback,
            uint256[] amounts,
            uint256 lpAmount
        );
        /**
         * @notice Emitted when a withdrawal request is made.
         * @param from The address of the user making the request.
         * @param request The details of the withdrawal request.
         */
        event WithdrawalRequested(address indexed from, WithdrawalRequest request);
        /**
         * @notice Emitted when a withdrawal request is canceled.
         * @param user The address of the user canceling the request.
         * @param origin The origin of the cancellation.
         */
        event WithdrawalRequestCanceled(address indexed user, address origin);
        /**
         * @notice Emitted when an emergency withdrawal occurs.
         * @param from The address of the user initiating the emergency withdrawal.
         * @param request The details of the withdrawal request.
         * @param amounts The actual amounts withdrawn.
         */
        event EmergencyWithdrawal(
            address indexed from,
            WithdrawalRequest request,
            uint256[] amounts
        );
        /**
         * @notice Emitted when withdrawals are processed.
         * @param users The addresses of the users whose withdrawals are processed.
         * @param statuses The statuses of the withdrawal processing.
         */
        event WithdrawalsProcessed(address[] users, bool[] statuses);
        /**
         * @notice Emitted when a withdrawal callback occurs.
         * @param callback The address of the withdrawal callback contract.
         */
        event WithdrawCallback(address indexed callback);
    }
    // SPDX-License-Identifier: BUSL-1.1
    pragma solidity 0.8.25;
    import "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol";
    import "../interfaces/utils/IDefaultAccessControl.sol";
    contract DefaultAccessControl is
        IDefaultAccessControl,
        AccessControlEnumerable
    {
        bytes32 public constant OPERATOR = keccak256("operator");
        bytes32 public constant ADMIN_ROLE = keccak256("admin");
        bytes32 public constant ADMIN_DELEGATE_ROLE = keccak256("admin_delegate");
        /// @notice Creates a new contract.
        /// @param admin Admin of the contract
        constructor(address admin) {
            if (admin == address(0)) revert AddressZero();
            _grantRole(OPERATOR, admin);
            _grantRole(ADMIN_ROLE, admin);
            _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE);
            _setRoleAdmin(ADMIN_DELEGATE_ROLE, ADMIN_ROLE);
            _setRoleAdmin(OPERATOR, ADMIN_DELEGATE_ROLE);
        }
        /// @inheritdoc IDefaultAccessControl
        function isAdmin(address sender) public view returns (bool) {
            return
                hasRole(ADMIN_ROLE, sender) || hasRole(ADMIN_DELEGATE_ROLE, sender);
        }
        /// @inheritdoc IDefaultAccessControl
        function isOperator(address sender) public view returns (bool) {
            return hasRole(OPERATOR, sender);
        }
        /// @inheritdoc IDefaultAccessControl
        function requireAdmin(address sender) external view override {
            _requireAdmin(sender);
        }
        /// @inheritdoc IDefaultAccessControl
        function requireAtLeastOperator(address sender) external view override {
            _requireAtLeastOperator(sender);
        }
        function _requireAdmin(address sender) internal view {
            if (!isAdmin(sender)) revert Forbidden();
        }
        function _requireAtLeastOperator(address sender) internal view {
            if (!isAdmin(sender) && !isOperator(sender)) revert Forbidden();
        }
        function _requireAdmin() internal view {
            _requireAdmin(msg.sender);
        }
        function _requireAtLeastOperator() internal view {
            _requireAtLeastOperator(msg.sender);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.25;
    /// @title Contains 512-bit math functions
    /// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
    /// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
    library FullMath {
        /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
        /// @param a The multiplicand
        /// @param b The multiplier
        /// @param denominator The divisor
        /// @return result The 256-bit result
        /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
        function mulDiv(
            uint256 a,
            uint256 b,
            uint256 denominator
        ) internal pure returns (uint256 result) {
            // diff: original lib works under 0.7.6 with overflows enabled
            unchecked {
                // 512-bit multiply [prod1 prod0] = a * b
                // Compute the product mod 2**256 and mod 2**256 - 1
                // then use the Chinese Remainder Theorem to reconstruct
                // the 512 bit result. The result is stored in two 256
                // variables such that product = prod1 * 2**256 + prod0
                uint256 prod0; // Least significant 256 bits of the product
                uint256 prod1; // Most significant 256 bits of the product
                assembly {
                    let mm := mulmod(a, b, not(0))
                    prod0 := mul(a, b)
                    prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                }
                // Handle non-overflow cases, 256 by 256 division
                if (prod1 == 0) {
                    require(denominator > 0);
                    assembly {
                        result := div(prod0, denominator)
                    }
                    return result;
                }
                // Make sure the result is less than 2**256.
                // Also prevents denominator == 0
                require(denominator > prod1);
                ///////////////////////////////////////////////
                // 512 by 256 division.
                ///////////////////////////////////////////////
                // Make division exact by subtracting the remainder from [prod1 prod0]
                // Compute remainder using mulmod
                uint256 remainder;
                assembly {
                    remainder := mulmod(a, b, denominator)
                }
                // Subtract 256 bit number from 512 bit number
                assembly {
                    prod1 := sub(prod1, gt(remainder, prod0))
                    prod0 := sub(prod0, remainder)
                }
                // Factor powers of two out of denominator
                // Compute largest power of two divisor of denominator.
                // Always >= 1.
                // diff: original uint256 twos = -denominator & denominator;
                uint256 twos = uint256(-int256(denominator)) & denominator;
                // Divide denominator by power of two
                assembly {
                    denominator := div(denominator, twos)
                }
                // Divide [prod1 prod0] by the factors of two
                assembly {
                    prod0 := div(prod0, twos)
                }
                // Shift in bits from prod1 into prod0. For this we need
                // to flip `twos` such that it is 2**256 / twos.
                // If twos is zero, then it becomes one
                assembly {
                    twos := add(div(sub(0, twos), twos), 1)
                }
                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
                // correct for four bits. That is, denominator * inv = 1 mod 2**4
                uint256 inv = (3 * denominator) ^ 2;
                // Now use 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.
                inv *= 2 - denominator * inv; // inverse mod 2**8
                inv *= 2 - denominator * inv; // inverse mod 2**16
                inv *= 2 - denominator * inv; // inverse mod 2**32
                inv *= 2 - denominator * inv; // inverse mod 2**64
                inv *= 2 - denominator * inv; // inverse mod 2**128
                inv *= 2 - denominator * inv; // 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 precoditions 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 * inv;
                return result;
            }
        }
        /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
        /// @param a The multiplicand
        /// @param b The multiplier
        /// @param denominator The divisor
        /// @return result The 256-bit result
        function mulDivRoundingUp(
            uint256 a,
            uint256 b,
            uint256 denominator
        ) internal pure returns (uint256 result) {
            // diff: original lib works under 0.7.6 with overflows enabled
            unchecked {
                result = mulDiv(a, b, denominator);
                if (mulmod(a, b, denominator) > 0) {
                    require(result < type(uint256).max);
                    result++;
                }
            }
        }
    }
    // SPDX-License-Identifier: BSL-1.1
    pragma solidity 0.8.25;
    import "./interfaces/IVaultConfigurator.sol";
    import "./utils/DefaultAccessControl.sol";
    contract VaultConfigurator is IVaultConfigurator, ReentrancyGuard {
        /// @inheritdoc IVaultConfigurator
        uint256 public constant MAX_DELAY = 365 days;
        /// @inheritdoc IVaultConfigurator
        uint256 public constant MAX_WITHDRAWAL_FEE = 5e7; // 5%
        /// @inheritdoc IVaultConfigurator
        address public immutable vault;
        Data private _baseDelay;
        Data private _depositCallbackDelay;
        Data private _withdrawalCallbackDelay;
        Data private _withdrawalFeeD9Delay;
        Data private _maximalTotalSupplyDelay;
        Data private _isDepositLockedDelay;
        Data private _areTransfersLockedDelay;
        Data private _isDelegateModuleApprovedDelay;
        Data private _ratiosOracleDelay;
        Data private _priceOracleDelay;
        Data private _validatorDelay;
        Data private _emergencyWithdrawalDelay;
        Data private _depositCallback;
        Data private _withdrawalCallback;
        Data private _withdrawalFeeD9;
        Data private _maximalTotalSupply;
        Data private _isDepositLocked; // nothing
        Data private _areTransfersLocked;
        Data private _ratiosOracle;
        Data private _priceOracle;
        Data private _validator;
        mapping(address => Data) private _isDelegateModuleApproved; // bool
        constructor() {
            vault = msg.sender;
        }
        modifier onlyAdmin() {
            IDefaultAccessControl(vault).requireAdmin(msg.sender);
            _;
        }
        modifier atLeastOperator() {
            IDefaultAccessControl(vault).requireAtLeastOperator(msg.sender);
            _;
        }
        function _stage(Data storage s, uint256 value) private {
            s.stageTimestamp = block.timestamp;
            s.stagedValue = value;
            bytes32 slot;
            assembly {
                slot := s.slot
            }
            emit Stage(slot, s, value, block.timestamp);
        }
        function _commit(Data storage s, Data storage delay) private {
            uint256 timestamp = s.stageTimestamp;
            if (timestamp == 0) revert InvalidTimestamp();
            if (block.timestamp - timestamp < delay.value)
                revert InvalidTimestamp();
            bytes32 slot;
            assembly {
                slot := s.slot
            }
            emit Commit(slot, s, block.timestamp);
            s.value = s.stagedValue;
            delete s.stageTimestamp;
            delete s.stagedValue;
        }
        function _rollback(Data storage s) private {
            bytes32 slot;
            assembly {
                slot := s.slot
            }
            emit Rollback(slot, s, block.timestamp);
            delete s.stageTimestamp;
            delete s.stagedValue;
        }
        /// @inheritdoc IVaultConfigurator
        function isDelegateModuleApproved(
            address module
        ) external view returns (bool) {
            return _isDelegateModuleApproved[module].value != 0;
        }
        /// @inheritdoc IVaultConfigurator
        function isDepositLocked() external view returns (bool) {
            return _isDepositLocked.value != 0;
        }
        /// @inheritdoc IVaultConfigurator
        function areTransfersLocked() external view returns (bool) {
            return _areTransfersLocked.value != 0;
        }
        /// @inheritdoc IVaultConfigurator
        function maximalTotalSupply() external view returns (uint256) {
            return _maximalTotalSupply.value;
        }
        /// @inheritdoc IVaultConfigurator
        function depositCallback() external view returns (address) {
            return address(uint160(_depositCallback.value));
        }
        /// @inheritdoc IVaultConfigurator
        function withdrawalCallback() external view returns (address) {
            return address(uint160(_withdrawalCallback.value));
        }
        /// @inheritdoc IVaultConfigurator
        function withdrawalFeeD9() external view returns (uint256) {
            return _withdrawalFeeD9.value;
        }
        /// @inheritdoc IVaultConfigurator
        function stageDelegateModuleApproval(
            address module
        ) external onlyAdmin nonReentrant {
            if (module == address(0)) revert AddressZero();
            _stage(_isDelegateModuleApproved[module], 1);
        }
        /// @inheritdoc IVaultConfigurator
        function commitDelegateModuleApproval(
            address module
        ) external onlyAdmin nonReentrant {
            _commit(
                _isDelegateModuleApproved[module],
                _isDelegateModuleApprovedDelay
            );
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedDelegateModuleApproval(
            address module
        ) external onlyAdmin nonReentrant {
            _rollback(_isDelegateModuleApproved[module]);
        }
        /// @inheritdoc IVaultConfigurator
        function revokeDelegateModuleApproval(
            address module
        ) external onlyAdmin nonReentrant {
            _isDelegateModuleApproved[module].value = 0;
        }
        /// @inheritdoc IVaultConfigurator
        function stageDepositsLock() external atLeastOperator nonReentrant {
            _stage(_isDepositLocked, 1);
        }
        /// @inheritdoc IVaultConfigurator
        function commitDepositsLock() external atLeastOperator nonReentrant {
            _commit(_isDepositLocked, _isDepositLockedDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedDepositsLock()
            external
            atLeastOperator
            nonReentrant
        {
            _rollback(_isDepositLocked);
        }
        /// @inheritdoc IVaultConfigurator
        function revokeDepositsLock() external atLeastOperator nonReentrant {
            _isDepositLocked.value = 0;
        }
        /// @inheritdoc IVaultConfigurator
        function stageTransfersLock(bool flag) external onlyAdmin nonReentrant {
            _stage(_areTransfersLocked, flag ? 1 : 0);
        }
        /// @inheritdoc IVaultConfigurator
        function commitTransfersLock() external onlyAdmin nonReentrant {
            _commit(_areTransfersLocked, _areTransfersLockedDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedTransfersLock() external onlyAdmin nonReentrant {
            _rollback(_areTransfersLocked);
        }
        /// @inheritdoc IVaultConfigurator
        function stageMaximalTotalSupply(
            uint256 maximalTotalSupply_
        ) external onlyAdmin nonReentrant {
            if (maximalTotalSupply_ < IVault(vault).totalSupply())
                revert InvalidTotalSupply();
            _stage(_maximalTotalSupply, maximalTotalSupply_);
        }
        /// @inheritdoc IVaultConfigurator
        function commitMaximalTotalSupply() external onlyAdmin nonReentrant {
            _commit(_maximalTotalSupply, _maximalTotalSupplyDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedMaximalTotalSupply()
            external
            onlyAdmin
            nonReentrant
        {
            _rollback(_maximalTotalSupply);
        }
        /// @inheritdoc IVaultConfigurator
        function stageDepositCallback(
            address callback
        ) external onlyAdmin nonReentrant {
            _stage(_depositCallback, uint160(callback));
        }
        /// @inheritdoc IVaultConfigurator
        function commitDepositCallback() external onlyAdmin nonReentrant {
            _commit(_depositCallback, _depositCallbackDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedDepositCallback() external onlyAdmin nonReentrant {
            _rollback(_depositCallback);
        }
        /// @inheritdoc IVaultConfigurator
        function stageWithdrawalCallback(
            address callback
        ) external onlyAdmin nonReentrant {
            _stage(_withdrawalCallback, uint160(callback));
        }
        /// @inheritdoc IVaultConfigurator
        function commitWithdrawalCallback() external onlyAdmin nonReentrant {
            _commit(_withdrawalCallback, _withdrawalCallbackDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedWithdrawalCallback()
            external
            onlyAdmin
            nonReentrant
        {
            _rollback(_withdrawalCallback);
        }
        /// @inheritdoc IVaultConfigurator
        function stageWithdrawalFeeD9(
            uint256 feeD9
        ) external onlyAdmin nonReentrant {
            if (feeD9 > MAX_WITHDRAWAL_FEE) revert InvalidWithdrawalFee();
            _stage(_withdrawalFeeD9, feeD9);
        }
        /// @inheritdoc IVaultConfigurator
        function commitWithdrawalFeeD9() external onlyAdmin nonReentrant {
            _commit(_withdrawalFeeD9, _withdrawalFeeD9Delay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedWithdrawalFeeD9() external onlyAdmin nonReentrant {
            _rollback(_withdrawalFeeD9);
        }
        /// @inheritdoc IVaultConfigurator
        function baseDelay() external view returns (uint256) {
            return _baseDelay.value;
        }
        /// @inheritdoc IVaultConfigurator
        function stageBaseDelay(uint256 delay_) external onlyAdmin nonReentrant {
            if (delay_ > MAX_DELAY) revert InvalidDelay();
            _stage(_baseDelay, delay_);
        }
        /// @inheritdoc IVaultConfigurator
        function commitBaseDelay() external onlyAdmin nonReentrant {
            _commit(_baseDelay, _baseDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedBaseDelay() external onlyAdmin nonReentrant {
            _rollback(_baseDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function depositCallbackDelay() external view returns (uint256) {
            return _depositCallbackDelay.value;
        }
        /// @inheritdoc IVaultConfigurator
        function stageDepositCallbackDelay(
            uint256 delay_
        ) external onlyAdmin nonReentrant {
            if (delay_ > MAX_DELAY) revert InvalidDelay();
            _stage(_depositCallbackDelay, delay_);
        }
        /// @inheritdoc IVaultConfigurator
        function commitDepositCallbackDelay() external onlyAdmin nonReentrant {
            _commit(_depositCallbackDelay, _baseDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedDepositCallbackDelay()
            external
            onlyAdmin
            nonReentrant
        {
            _rollback(_depositCallbackDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function withdrawalCallbackDelay() external view returns (uint256) {
            return _withdrawalCallbackDelay.value;
        }
        /// @inheritdoc IVaultConfigurator
        function stageWithdrawalCallbackDelay(
            uint256 delay_
        ) external onlyAdmin nonReentrant {
            if (delay_ > MAX_DELAY) revert InvalidDelay();
            _stage(_withdrawalCallbackDelay, delay_);
        }
        /// @inheritdoc IVaultConfigurator
        function commitWithdrawalCallbackDelay() external onlyAdmin nonReentrant {
            _commit(_withdrawalCallbackDelay, _baseDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedWithdrawalCallbackDelay()
            external
            onlyAdmin
            nonReentrant
        {
            _rollback(_withdrawalCallbackDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function withdrawalFeeD9Delay() external view returns (uint256) {
            return _withdrawalFeeD9Delay.value;
        }
        /// @inheritdoc IVaultConfigurator
        function stageWithdrawalFeeD9Delay(
            uint256 delay_
        ) external onlyAdmin nonReentrant {
            if (delay_ > MAX_DELAY) revert InvalidDelay();
            _stage(_withdrawalFeeD9Delay, delay_);
        }
        /// @inheritdoc IVaultConfigurator
        function commitWithdrawalFeeD9Delay() external onlyAdmin nonReentrant {
            _commit(_withdrawalFeeD9Delay, _baseDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedWithdrawalFeeD9Delay()
            external
            onlyAdmin
            nonReentrant
        {
            _rollback(_withdrawalFeeD9Delay);
        }
        /// @inheritdoc IVaultConfigurator
        function isDepositLockedDelay() external view returns (uint256) {
            return _isDepositLockedDelay.value;
        }
        /// @inheritdoc IVaultConfigurator
        function stageDepositsLockedDelay(
            uint256 delay_
        ) external onlyAdmin nonReentrant {
            if (delay_ > MAX_DELAY) revert InvalidDelay();
            _stage(_isDepositLockedDelay, delay_);
        }
        /// @inheritdoc IVaultConfigurator
        function commitDepositsLockedDelay() external onlyAdmin nonReentrant {
            _commit(_isDepositLockedDelay, _baseDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedDepositsLockedDelay()
            external
            onlyAdmin
            nonReentrant
        {
            _rollback(_isDepositLockedDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function areTransfersLockedDelay() external view returns (uint256) {
            return _areTransfersLockedDelay.value;
        }
        /// @inheritdoc IVaultConfigurator
        function stageTransfersLockedDelay(
            uint256 delay_
        ) external onlyAdmin nonReentrant {
            if (delay_ > MAX_DELAY) revert InvalidDelay();
            _stage(_areTransfersLockedDelay, delay_);
        }
        /// @inheritdoc IVaultConfigurator
        function commitTransfersLockedDelay() external onlyAdmin nonReentrant {
            _commit(_areTransfersLockedDelay, _baseDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedTransfersLockedDelay()
            external
            onlyAdmin
            nonReentrant
        {
            _rollback(_areTransfersLockedDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function delegateModuleApprovalDelay() external view returns (uint256) {
            return _isDelegateModuleApprovedDelay.value;
        }
        /// @inheritdoc IVaultConfigurator
        function stageDelegateModuleApprovalDelay(
            uint256 delay_
        ) external onlyAdmin nonReentrant {
            if (delay_ > MAX_DELAY) revert InvalidDelay();
            _stage(_isDelegateModuleApprovedDelay, delay_);
        }
        /// @inheritdoc IVaultConfigurator
        function commitDelegateModuleApprovalDelay()
            external
            onlyAdmin
            nonReentrant
        {
            _commit(_isDelegateModuleApprovedDelay, _baseDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedDelegateModuleApprovalDelay()
            external
            onlyAdmin
            nonReentrant
        {
            _rollback(_isDelegateModuleApprovedDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function maximalTotalSupplyDelay() external view returns (uint256) {
            return _maximalTotalSupplyDelay.value;
        }
        /// @inheritdoc IVaultConfigurator
        function stageMaximalTotalSupplyDelay(
            uint256 delay_
        ) external onlyAdmin nonReentrant {
            if (delay_ > MAX_DELAY) revert InvalidDelay();
            _stage(_maximalTotalSupplyDelay, delay_);
        }
        /// @inheritdoc IVaultConfigurator
        function commitMaximalTotalSupplyDelay() external onlyAdmin nonReentrant {
            _commit(_maximalTotalSupplyDelay, _baseDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedMaximalTotalSupplyDelay()
            external
            onlyAdmin
            nonReentrant
        {
            _rollback(_maximalTotalSupplyDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function ratiosOracle() external view returns (address) {
            return address(uint160(_ratiosOracle.value));
        }
        /// @inheritdoc IVaultConfigurator
        function priceOracle() external view returns (address) {
            return address(uint160(_priceOracle.value));
        }
        /// @inheritdoc IVaultConfigurator
        function validator() external view returns (address) {
            return address(uint160(_validator.value));
        }
        /// @inheritdoc IVaultConfigurator
        function stageRatiosOracle(address oracle) external onlyAdmin nonReentrant {
            if (oracle == address(0)) revert AddressZero();
            _stage(_ratiosOracle, uint160(oracle));
        }
        /// @inheritdoc IVaultConfigurator
        function commitRatiosOracle() external onlyAdmin nonReentrant {
            _commit(_ratiosOracle, _ratiosOracleDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedRatiosOracle() external onlyAdmin nonReentrant {
            _rollback(_ratiosOracle);
        }
        /// @inheritdoc IVaultConfigurator
        function stagePriceOracle(address oracle) external onlyAdmin nonReentrant {
            if (oracle == address(0)) revert AddressZero();
            _stage(_priceOracle, uint160(oracle));
        }
        /// @inheritdoc IVaultConfigurator
        function commitPriceOracle() external onlyAdmin nonReentrant {
            _commit(_priceOracle, _priceOracleDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedPriceOracle() external onlyAdmin nonReentrant {
            _rollback(_priceOracle);
        }
        /// @inheritdoc IVaultConfigurator
        function stageValidator(
            address validator_
        ) external onlyAdmin nonReentrant {
            if (validator_ == address(0)) revert AddressZero();
            _stage(_validator, uint160(validator_));
        }
        /// @inheritdoc IVaultConfigurator
        function commitValidator() external onlyAdmin nonReentrant {
            _commit(_validator, _validatorDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedValidator() external onlyAdmin nonReentrant {
            _rollback(_validator);
        }
        /// @inheritdoc IVaultConfigurator
        function priceOracleDelay() external view returns (uint256) {
            return _priceOracleDelay.value;
        }
        /// @inheritdoc IVaultConfigurator
        function ratiosOracleDelay() external view returns (uint256) {
            return _ratiosOracleDelay.value;
        }
        /// @inheritdoc IVaultConfigurator
        function validatorDelay() external view returns (uint256) {
            return _validatorDelay.value;
        }
        /// @inheritdoc IVaultConfigurator
        function stageValidatorDelay(
            uint256 delay_
        ) external onlyAdmin nonReentrant {
            if (delay_ > MAX_DELAY) revert InvalidDelay();
            _stage(_validatorDelay, delay_);
        }
        /// @inheritdoc IVaultConfigurator
        function commitValidatorDelay() external onlyAdmin nonReentrant {
            _commit(_validatorDelay, _baseDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedValidatorDelay() external onlyAdmin nonReentrant {
            _rollback(_validatorDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function stagePriceOracleDelay(
            uint256 delay_
        ) external onlyAdmin nonReentrant {
            if (delay_ > MAX_DELAY) revert InvalidDelay();
            _stage(_priceOracleDelay, delay_);
        }
        /// @inheritdoc IVaultConfigurator
        function commitPriceOracleDelay() external onlyAdmin nonReentrant {
            _commit(_priceOracleDelay, _baseDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedPriceOracleDelay() external onlyAdmin nonReentrant {
            _rollback(_priceOracleDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function stageRatiosOracleDelay(
            uint256 delay_
        ) external onlyAdmin nonReentrant {
            if (delay_ > MAX_DELAY) revert InvalidDelay();
            _stage(_ratiosOracleDelay, delay_);
        }
        /// @inheritdoc IVaultConfigurator
        function commitRatiosOracleDelay() external onlyAdmin nonReentrant {
            _commit(_ratiosOracleDelay, _baseDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedRatiosOracleDelay() external onlyAdmin nonReentrant {
            _rollback(_ratiosOracleDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function emergencyWithdrawalDelay() external view returns (uint256) {
            return _emergencyWithdrawalDelay.value;
        }
        /// @inheritdoc IVaultConfigurator
        function stageEmergencyWithdrawalDelay(
            uint256 delay_
        ) external onlyAdmin nonReentrant {
            if (delay_ > MAX_DELAY) revert InvalidDelay();
            _stage(_emergencyWithdrawalDelay, delay_);
        }
        /// @inheritdoc IVaultConfigurator
        function commitEmergencyWithdrawalDelay() external onlyAdmin nonReentrant {
            _commit(_emergencyWithdrawalDelay, _baseDelay);
        }
        /// @inheritdoc IVaultConfigurator
        function rollbackStagedEmergencyWithdrawalDelay()
            external
            onlyAdmin
            nonReentrant
        {
            _rollback(_emergencyWithdrawalDelay);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
    pragma solidity ^0.8.20;
    import {IERC20} from "./IERC20.sol";
    import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
    import {Context} from "../../utils/Context.sol";
    import {IERC20Errors} from "../../interfaces/draft-IERC6093.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 ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
        mapping(address account => uint256) private _balances;
        mapping(address account => mapping(address spender => uint256)) private _allowances;
        uint256 private _totalSupply;
        string private _name;
        string private _symbol;
        /**
         * @dev Sets the values for {name} and {symbol}.
         *
         * All two of these values are immutable: they can only be set once during
         * construction.
         */
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual returns (string memory) {
            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) {
            return _totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual returns (uint256) {
            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) {
            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 {
            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 {
            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/utils/SafeERC20.sol)
    pragma solidity ^0.8.20;
    import {IERC20} from "../IERC20.sol";
    import {IERC20Permit} from "../extensions/IERC20Permit.sol";
    import {Address} from "../../../utils/Address.sol";
    /**
     * @title SafeERC20
     * @dev Wrappers around ERC20 operations that throw on failure (when the token
     * contract returns false). Tokens that return no value (and instead revert or
     * throw on failure) are also supported, non-reverting calls are assumed to be
     * successful.
     * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
     * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
     */
    library SafeERC20 {
        using Address for address;
        /**
         * @dev An operation with an ERC20 token failed.
         */
        error SafeERC20FailedOperation(address token);
        /**
         * @dev Indicates a failed `decreaseAllowance` request.
         */
        error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
        /**
         * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         */
        function safeTransfer(IERC20 token, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
        }
        /**
         * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
         * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
         */
        function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
        }
        /**
         * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         */
        function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 oldAllowance = token.allowance(address(this), spender);
            forceApprove(token, spender, oldAllowance + value);
        }
        /**
         * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
         * value, non-reverting calls are assumed to be successful.
         */
        function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
            unchecked {
                uint256 currentAllowance = token.allowance(address(this), spender);
                if (currentAllowance < requestedDecrease) {
                    revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
                }
                forceApprove(token, spender, currentAllowance - requestedDecrease);
            }
        }
        /**
         * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
         * to be set to zero before setting it to a non-zero value, such as USDT.
         */
        function forceApprove(IERC20 token, address spender, uint256 value) internal {
            bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
            if (!_callOptionalReturnBool(token, approvalCall)) {
                _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
                _callOptionalReturn(token, approvalCall);
            }
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         */
        function _callOptionalReturn(IERC20 token, bytes memory data) private {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
            // the target address contains contract code and also asserts for success in the low-level call.
            bytes memory returndata = address(token).functionCall(data);
            if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
                revert SafeERC20FailedOperation(address(token));
            }
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         *
         * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
         */
        function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
            // and not revert is the subcall reverts.
            (bool success, bytes memory returndata) = address(token).call(data);
            return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
    pragma solidity ^0.8.20;
    /**
     * @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 ReentrancyGuard {
        // 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;
        uint256 private _status;
        /**
         * @dev Unauthorized reentrant call.
         */
        error ReentrancyGuardReentrantCall();
        constructor() {
            _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 {
            // 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 {
            // 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) {
            return _status == ENTERED;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Arrays.sol)
    pragma solidity ^0.8.20;
    import {StorageSlot} from "./StorageSlot.sol";
    import {Math} from "./math/Math.sol";
    /**
     * @dev Collection of functions related to array types.
     */
    library Arrays {
        using StorageSlot for bytes32;
        /**
         * @dev Searches a sorted `array` and returns the first index that contains
         * a value greater or equal to `element`. If no such index exists (i.e. all
         * values in the array are strictly less than `element`), the array length is
         * returned. Time complexity O(log n).
         *
         * `array` is expected to be sorted in ascending order, and to contain no
         * repeated elements.
         */
        function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
            uint256 low = 0;
            uint256 high = array.length;
            if (high == 0) {
                return 0;
            }
            while (low < high) {
                uint256 mid = Math.average(low, high);
                // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
                // because Math.average rounds towards zero (it does integer division with truncation).
                if (unsafeAccess(array, mid).value > element) {
                    high = mid;
                } else {
                    low = mid + 1;
                }
            }
            // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
            if (low > 0 && unsafeAccess(array, low - 1).value == element) {
                return low - 1;
            } else {
                return low;
            }
        }
        /**
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
         *
         * WARNING: Only use if you are certain `pos` is lower than the array length.
         */
        function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
            bytes32 slot;
            // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
            // following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0, arr.slot)
                slot := add(keccak256(0, 0x20), pos)
            }
            return slot.getAddressSlot();
        }
        /**
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
         *
         * WARNING: Only use if you are certain `pos` is lower than the array length.
         */
        function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
            bytes32 slot;
            // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
            // following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0, arr.slot)
                slot := add(keccak256(0, 0x20), pos)
            }
            return slot.getBytes32Slot();
        }
        /**
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
         *
         * WARNING: Only use if you are certain `pos` is lower than the array length.
         */
        function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
            bytes32 slot;
            // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
            // following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0, arr.slot)
                slot := add(keccak256(0, 0x20), pos)
            }
            return slot.getUint256Slot();
        }
        /**
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
         *
         * WARNING: Only use if you are certain `pos` is lower than the array length.
         */
        function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
            assembly {
                res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
            }
        }
        /**
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
         *
         * WARNING: Only use if you are certain `pos` is lower than the array length.
         */
        function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
            assembly {
                res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
            }
        }
    }
    // SPDX-License-Identifier: BSL-1.1
    pragma solidity 0.8.25;
    /**
     * @title ITvlModule
     * @notice Interface for a Total Value Locked (TVL) module, providing information about token balances.
     */
    interface ITvlModule {
        // Structure representing TVL data for a token
        struct Data {
            address token; // Address of the token
            address underlyingToken; // Address of the underlying token
            uint256 amount; // Current amount of the token
            uint256 underlyingAmount; // Current amount of the underlying token
            bool isDebt; // Flag indicating if the token represents debt
        }
        /**
         * @notice Returns Total Value Locked (TVL) data for a specific user.
         * @param user The address of the user.
         * @return data An array of TVL data for each token held by the user.
         */
        function tvl(address user) external view returns (Data[] memory data);
    }
    // SPDX-License-Identifier: BSL-1.1
    pragma solidity 0.8.25;
    /**
     * @title IValidator
     * @notice Interface defining a generic validator for transaction data.
     */
    interface IValidator {
        /**
         * @notice Validates a transaction involving two addresses based on the provided calldata.
         * @param from The address initiating the transaction.
         * @param to The target address of the transaction.
         * @param data The transaction data containing the function selector and any necessary parameters.
         * @dev Implementers should validate that the transaction is authorized, properly formatted, and adheres to the required business logic.
         *      Reverts if the transaction is invalid.
         */
        function validate(
            address from,
            address to,
            bytes calldata data
        ) external view;
    }
    // SPDX-License-Identifier: BUSL-1.1
    pragma solidity 0.8.25;
    /**
     * @title IPriceOracle
     * @notice Interface defining a standard price oracle that provides token prices in 96-bit precision.
     */
    interface IPriceOracle {
        /**
         * @notice Returns the price of a specific token relative to the base token of the given vault, expressed in 96-bit precision.
         * @param vault The address of the vault requesting the price.
         * @param token The address of the token to calculate the price for.
         * @return priceX96_ The price of the token relative to the base token, using 96-bit precision.
         * @dev Implementations should ensure prices are accurate and may involve external oracle data.
         *      Reverts with an appropriate error if the price cannot be provided.
         */
        function priceX96(
            address vault,
            address token
        ) external view returns (uint256 priceX96_);
    }
    // SPDX-License-Identifier: BUSL-1.1
    pragma solidity 0.8.25;
    /**
     * @title IRatiosOracle
     * @notice Interface for a ratios oracle, providing the target allocation ratios for a vault.
     */
    interface IRatiosOracle {
        /**
         * @notice Retrieves the target allocation ratios (using 96-bit precision) for a specific vault's tokens.
         * @param vault The address of the vault requesting the ratios.
         * @param isDeposit A boolean indicating whether the ratios are for a deposit or a withdrawal.
         * @return ratiosX96 An array representing the target ratios for each token, expressed in 96-bit precision.
         * @dev The array of ratios should align with the underlying tokens associated with the vault.
         *      Reverts if the ratios cannot be provided due to missing or mismatched data.
         */
        function getTargetRatiosX96(
            address vault,
            bool isDeposit
        ) external view returns (uint128[] memory ratiosX96);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.25;
    /**
     * @title IDepositCallback
     * @notice Interface defining a callback function to handle deposit results.
     */
    interface IDepositCallback {
        /**
         * @notice Handles the callback after a deposit operation has been executed.
         * @param actualAmounts An array representing the actual amounts of each token that were deposited.
         * @param lpAmount The total amount of LP tokens that were issued as a result of the deposit.
         * @dev This function is intended to be implemented by contracts that need to take further action following a deposit.
         */
        function depositCallback(
            uint256[] memory actualAmounts,
            uint256 lpAmount
        ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.25;
    /**
     * @title IWithdrawalCallback
     * @notice Interface defining a callback function to handle post-withdrawal actions in processWithdrawals function.
     */
    interface IWithdrawalCallback {
        /**
         * @notice Handles the callback after a withdrawal operation has been executed.
         * @dev This function should be implemented to carry out any additional actions required after the withdrawal.
         *      It does not take any parameters and will be invoked once the withdrawal process is complete.
         */
        function withdrawalCallback() external;
    }
    // SPDX-License-Identifier: BSL-1.1
    pragma solidity 0.8.25;
    import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
    import "./IVault.sol";
    /**
     * @title IVaultConfigurator
     * @notice Contract defining the configuration and access control for a vault system.
     *         This interface specifies the parameters for the primary Vault contract,
     *         facilitating secure configuration updates through a two-stage process: staging and committing, with each parameter update subject to a specified delay.
     *         The stage function sets the new value and timestamp for the parameter, while the commit function finalizes the update
     *
     *         The delay parameter is expressed in seconds and is defined for each parameter of this contract as follows:
     *            - baseDelay: the base delay for stage/commit operations
     *            - depositCallbackDelay: delay for changing the deposit callback contract address
     *            - withdrawalCallbackDelay: delay for changing the withdrawal callback contract address
     *            - withdrawalFeeD9Delay: delay for changing the withdrawal fee
     *            - isDepositLockedDelay: delay for locking deposits
     *            - areTransfersLockedDelay: delay for locking LP token transfers
     *            - delegateModuleApprovalDelay: delay for approving delegated modules
     *            - maximalTotalSupplyDelay: delay for changing the maximum total supply
     *            - ratiosOracleDelay: delay for changing the ratios oracle address
     *            - priceOracleDelay: delay for changing the price oracle address
     *            - validatorDelay: delay for changing the validator address
     *            - emergencyWithdrawalDelay: delay for withdrawing funds after calling registerWithdrawal
     *
     *         Each of the above parameters has a pair of functions, stage/commit, through which their updates occur. The delay for all these parameters is set to baseDelay.
     *
     *         With the exception of functions for isDepositLocked parameter, all mutable functions of the contract can only be called by the vault's admin.
     *         Function for isDepositLocked parameter can be called by either the operator or the vault's admin
     *         to enable faster deposit locking if deemed necessary from the operator/strategy standpoint.
     */
    interface IVaultConfigurator {
        /// @dev Errors
        error AddressZero();
        error InvalidDelay();
        error InvalidTimestamp();
        error InvalidWithdrawalFee();
        error InvalidTotalSupply();
        /// @notice Struct to represent a staged data change with a delay period.
        struct Data {
            uint256 value; // Current value
            uint256 stagedValue; // Staged value waiting to be committed
            uint256 stageTimestamp; // Timestamp of staging
        }
        /// @notice Returns the maximum allowed delay for any staged data.
        /// @return uint256 The constant `MAX_DELAY` indicating the maximum delay period (365 days).
        function MAX_DELAY() external pure returns (uint256);
        /// @notice Returns the maximum withdrawal fee allowed.
        /// @return uint256 The constant `MAX_WITHDRAWAL_FEE` indicating the maximum withdrawal fee (5%).
        function MAX_WITHDRAWAL_FEE() external pure returns (uint256);
        /// @notice Returns the address of the vault associated with this configurator.
        /// @return address of the vault contract.
        function vault() external view returns (address);
        ///@notice Stages an approval for the specified delegate module.
        /// @param module The address of the module to approve.
        function stageDelegateModuleApproval(address module) external;
        /// @notice Commits the previously staged delegate module approval after the delay period.
        /// @param module The address of the module to approve.
        function commitDelegateModuleApproval(address module) external;
        /// @notice Rolls back any staged delegate module approval.
        /// @param module The address of the module to roll back.
        function rollbackStagedDelegateModuleApproval(address module) external;
        /// @notice @notice Revokes the approval of the specified delegate module.
        /// @param module The address of the module to revoke approval from.
        function revokeDelegateModuleApproval(address module) external;
        /// @notice Returns the base delay value for all staging operations.
        /// @return uint256 The base delay value in seconds.
        function baseDelay() external view returns (uint256);
        /// @notice Checks if the specified delegate module is approved for use.
        /// @param module The address of the module to check.
        /// @return bool `true` if the module is approved, otherwise `false`.
        function isDelegateModuleApproved(
            address module
        ) external view returns (bool);
        /// @notice Returns whether deposits are currently locked.
        /// @notice operator owned parameter.
        /// @return bool `true` if deposits are locked, otherwise `false`.
        function isDepositLocked() external view returns (bool);
        /// @notice Returns whether LP token transfers are currently locked.
        /// @notice admin owned parameter.
        /// @return bool `true` if transfers are locked, otherwise `false`.
        function areTransfersLocked() external view returns (bool);
        /// @notice Returns the maximum total supply of LP tokens allowed.
        /// @return uint256 The maximum total supply of LP tokens.
        function maximalTotalSupply() external view returns (uint256);
        /// @notice Returns the address of the deposit callback contract.
        /// @return address The address of the deposit callback contract.
        function depositCallback() external view returns (address);
        /// @notice Returns the address of the withdrawal callback contract.
        /// @return address The address of the withdrawal callback contract.
        function withdrawalCallback() external view returns (address);
        /// @notice Returns the current withdrawal fee in D9 format.
        /// @return uint256 The withdrawal fee, represented as an integer with 9 decimal places.
        function withdrawalFeeD9() external view returns (uint256);
        /// @notice Returns the delay for committing deposit callback changes.
        /// @return uint256 The delay in seconds.
        function depositCallbackDelay() external view returns (uint256);
        /// @notice Returns the delay for committing withdrawal callback changes.
        /// @return uint256 The delay in seconds.
        function withdrawalCallbackDelay() external view returns (uint256);
        /// @notice Returns the delay for committing withdrawal fee changes.
        /// @return uint256 The delay in seconds.
        function withdrawalFeeD9Delay() external view returns (uint256);
        /// @notice Returns the delay for committing deposit locks.
        /// @return uint256 The delay in seconds.
        function isDepositLockedDelay() external view returns (uint256);
        /// @notice Returns the delay for committing transfers locks.
        /// @return uint256 The delay in seconds.
        function areTransfersLockedDelay() external view returns (uint256);
        /// @notice Returns the delay for committing delegate module approvals.
        /// @return uint256 The delay in seconds.
        function delegateModuleApprovalDelay() external view returns (uint256);
        /// @notice Returns the delay for committing maximum total supply changes.
        /// @return uint256 The delay in seconds.
        function maximalTotalSupplyDelay() external view returns (uint256);
        /// @notice Returns the address of the ratios oracle.
        /// @return address The address of the ratios oracle.
        function ratiosOracle() external view returns (address);
        /// @notice Returns the address of the price oracle.
        /// @return address The address of the price oracle.
        function priceOracle() external view returns (address);
        /// @notice Returns the address of the validator.
        /// @return address The address of the validator.
        function validator() external view returns (address);
        /// @notice Returns the delay for committing validator changes.
        /// @return uint256 The delay in seconds.
        function validatorDelay() external view returns (uint256);
        /// @notice Returns the delay for committing price oracle changes.
        /// @return uint256 The delay in seconds.
        function priceOracleDelay() external view returns (uint256);
        /// @notice Returns the delay for committing ratios oracle changes.
        /// @return uint256 The delay in seconds.
        function ratiosOracleDelay() external view returns (uint256);
        /// @notice Returns the delay required between calling `registerWithdrawal` and being able to perform an emergency withdrawal for that request.
        /// @return uint256 The minimum delay time, in seconds, that a user must wait after calling `registerWithdrawal` before executing an emergency withdrawal.
        function emergencyWithdrawalDelay() external view returns (uint256);
        /// @notice Stages the deposits lock by setting a staged value and timestamp.
        function stageDepositsLock() external;
        /// @notice Commits the previously staged deposits lock after the delay period.
        function commitDepositsLock() external;
        /// @notice Rolls back any staged deposits lock.
        function rollbackStagedDepositsLock() external;
        /// @notice Revokes the current deposits lock, unlocking deposits.
        function revokeDepositsLock() external;
        /// @notice Stages the transfers lock by setting a staged value and timestamp.
        /// @param flag The new value to stage.
        function stageTransfersLock(bool flag) external;
        /// @notice Commits the previously staged transfers lock after the delay period.
        function commitTransfersLock() external;
        /// @notice Rolls back any staged transfers lock.
        function rollbackStagedTransfersLock() external;
        /// @notice Stages the maximum total supply with a staged value and timestamp.
        /// @param maximalTotalSupply_ The maximum total supply to stage.
        function stageMaximalTotalSupply(uint256 maximalTotalSupply_) external;
        /// @notice Commits the previously staged maximum total supply after the delay period.
        function commitMaximalTotalSupply() external;
        /// @notice Rolls back any staged maximum total supply changes.
        function rollbackStagedMaximalTotalSupply() external;
        /// @notice Stages a new deposit callback address.
        /// @param callback The address of the new deposit callback contract.
        function stageDepositCallback(address callback) external;
        /// @notice Commits the previously staged deposit callback address after the delay period.
        function commitDepositCallback() external;
        /// @notice Rolls back any staged deposit callback changes.
        function rollbackStagedDepositCallback() external;
        /// @notice Stages a new withdrawal callback address.
        /// @param callback The address of the new withdrawal callback contract.
        function stageWithdrawalCallback(address callback) external;
        /// @notice Commits the previously staged withdrawal callback address after the delay period.
        function commitWithdrawalCallback() external;
        /// @notice Rolls back any staged withdrawal callback changes.
        function rollbackStagedWithdrawalCallback() external;
        /// @notice Stages a new withdrawal fee in D9 format.
        /// @param feeD9 The new withdrawal fee in D9 format.
        function stageWithdrawalFeeD9(uint256 feeD9) external;
        /// @notice Commits the previously staged withdrawal fee after the delay period.
        function commitWithdrawalFeeD9() external;
        /// @notice Rolls back any staged withdrawal fee changes.
        function rollbackStagedWithdrawalFeeD9() external;
        /// @notice Stages a base delay value.
        /// @param delay_ The base delay value to stage.
        function stageBaseDelay(uint256 delay_) external;
        /// @notice Commits the previously staged base delay after the delay period.
        function commitBaseDelay() external;
        /// @notice Rolls back any staged base delay changes.
        function rollbackStagedBaseDelay() external;
        /// @notice Stages a delay value for the deposit callback.
        /// @param delay_ The delay value to stage.
        function stageDepositCallbackDelay(uint256 delay_) external;
        /// @notice Commits the previously staged deposit callback delay after the delay period.
        function commitDepositCallbackDelay() external;
        /// @notice Rolls back any staged deposit callback delay changes.
        function rollbackStagedDepositCallbackDelay() external;
        /// @notice Stages a delay value for the withdrawal callback.
        /// @param delay_ The delay value to stage.
        function stageWithdrawalCallbackDelay(uint256 delay_) external;
        /// @notice Commits the previously staged withdrawal callback delay after the delay period.
        function commitWithdrawalCallbackDelay() external;
        /// @notice Rolls back any staged withdrawal callback delay changes.
        function rollbackStagedWithdrawalCallbackDelay() external;
        /// @notice Stages a delay value for the withdrawal fee in D9 format.
        /// @param delay_ The delay value to stage.
        function stageWithdrawalFeeD9Delay(uint256 delay_) external;
        /// @notice Commits the previously staged withdrawal fee delay after the delay period.
        function commitWithdrawalFeeD9Delay() external;
        /// @notice Rolls back any staged withdrawal fee delay changes.
        function rollbackStagedWithdrawalFeeD9Delay() external;
        /// @notice Stages a delay value for locking deposits.
        /// @param delay_ The delay value to stage.
        function stageDepositsLockedDelay(uint256 delay_) external;
        /// @notice Commits the previously staged deposits lock delay after the delay period.
        function commitDepositsLockedDelay() external;
        /// @notice Rolls back any staged deposits lock delay changes.
        function rollbackStagedDepositsLockedDelay() external;
        /// @notice Stages a delay value for locking transfers.
        /// @param delay_ The delay value to stage.
        function stageTransfersLockedDelay(uint256 delay_) external;
        /// @notice Commits the previously staged transfers lock delay after the delay period.
        function commitTransfersLockedDelay() external;
        /// @notice Rolls back any staged transfers lock delay changes.
        function rollbackStagedTransfersLockedDelay() external;
        /// @notice Stages a delay value for the delegate module approval.
        /// @param delay_ The delay value to stage.
        function stageDelegateModuleApprovalDelay(uint256 delay_) external;
        /// @notice Commits the previously staged delegate module approval delay after the delay period.
        function commitDelegateModuleApprovalDelay() external;
        /// @notice Rolls back any staged delegate module approval delay changes.
        function rollbackStagedDelegateModuleApprovalDelay() external;
        /// @notice Stages a delay value for the maximum total supply.
        /// @param delay_ The delay value to stage.
        function stageMaximalTotalSupplyDelay(uint256 delay_) external;
        /// @notice Commits the previously staged maximum total supply delay after the delay period.
        function commitMaximalTotalSupplyDelay() external;
        /// @notice Rolls back any staged maximum total supply delay changes.
        function rollbackStagedMaximalTotalSupplyDelay() external;
        /// @notice Stages a ratios oracle address.
        /// @param oracle The address of the new ratios oracle.
        function stageRatiosOracle(address oracle) external;
        /// @notice Commits the previously staged ratios oracle after the delay period.
        function commitRatiosOracle() external;
        /// @notice Rolls back any staged ratios oracle changes.
        function rollbackStagedRatiosOracle() external;
        /// @notice Stages a price oracle address.
        /// @param oracle The address of the new price oracle.
        function stagePriceOracle(address oracle) external;
        /// @notice Commits the previously staged price oracle after the delay period.
        function commitPriceOracle() external;
        /// @notice Rolls back any staged price oracle changes.
        function rollbackStagedPriceOracle() external;
        /// @notice Stages a validator address.
        /// @param validator_ The address of the new validator.
        function stageValidator(address validator_) external;
        /// @notice Commits the previously staged validator after the delay period.
        function commitValidator() external;
        /// @notice Rolls back any staged validator changes.
        function rollbackStagedValidator() external;
        /// @notice Stages a delay value for the validator.
        /// @param delay_ The delay value to stage.
        function stageValidatorDelay(uint256 delay_) external;
        /// @notice Commits the previously staged validator delay after the delay period.
        function commitValidatorDelay() external;
        /// @notice Rolls back any staged validator delay changes.
        function rollbackStagedValidatorDelay() external;
        /// @notice Stages a delay value for the price oracle.
        /// @param delay_ The delay value to stage.
        function stagePriceOracleDelay(uint256 delay_) external;
        /// @notice Commits the previously staged price oracle delay after the delay period.
        function commitPriceOracleDelay() external;
        /// @notice Rolls back any staged price oracle delay changes.
        function rollbackStagedPriceOracleDelay() external;
        /// @notice Stages a delay value for the ratios oracle.
        /// @param delay_ The delay value to stage.
        function stageRatiosOracleDelay(uint256 delay_) external;
        /// @notice Commits the previously staged ratios oracle delay after the delay period.
        function commitRatiosOracleDelay() external;
        /// @notice Rolls back any staged ratios oracle delay changes.
        function rollbackStagedRatiosOracleDelay() external;
        /// @notice Stages a delay value for emergency withdrawals.
        /// @param delay_ The delay value to stage.
        function stageEmergencyWithdrawalDelay(uint256 delay_) external;
        /// @notice Commits the previously staged emergency withdrawal delay.
        function commitEmergencyWithdrawalDelay() external;
        /// @notice Rolls back any staged emergency withdrawal delay changes.
        function rollbackStagedEmergencyWithdrawalDelay() external;
        /// @dev Emitted when a value is staged for future commitment for given slot.
        event Stage(
            bytes32 indexed slot,
            Data indexed data,
            uint256 value,
            uint256 timestamp
        );
        /// @dev Emitted when a staged value is committed and updated for given slot.
        event Commit(bytes32 indexed slot, Data indexed data, uint256 timestamp);
        /// @dev Emitted when a staged value is rolled back without commitment for given slot.
        event Rollback(bytes32 indexed slot, Data indexed data, uint256 timestamp);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/extensions/AccessControlEnumerable.sol)
    pragma solidity ^0.8.20;
    import {IAccessControlEnumerable} from "./IAccessControlEnumerable.sol";
    import {AccessControl} from "../AccessControl.sol";
    import {EnumerableSet} from "../../utils/structs/EnumerableSet.sol";
    /**
     * @dev Extension of {AccessControl} that allows enumerating the members of each role.
     */
    abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
        using EnumerableSet for EnumerableSet.AddressSet;
        mapping(bytes32 role => EnumerableSet.AddressSet) private _roleMembers;
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
        }
        /**
         * @dev Returns one of the accounts that have `role`. `index` must be a
         * value between 0 and {getRoleMemberCount}, non-inclusive.
         *
         * Role bearers are not sorted in any particular way, and their ordering may
         * change at any point.
         *
         * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
         * you perform all queries on the same block. See the following
         * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
         * for more information.
         */
        function getRoleMember(bytes32 role, uint256 index) public view virtual returns (address) {
            return _roleMembers[role].at(index);
        }
        /**
         * @dev Returns the number of accounts that have `role`. Can be used
         * together with {getRoleMember} to enumerate all bearers of a role.
         */
        function getRoleMemberCount(bytes32 role) public view virtual returns (uint256) {
            return _roleMembers[role].length();
        }
        /**
         * @dev Overload {AccessControl-_grantRole} to track enumerable memberships
         */
        function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {
            bool granted = super._grantRole(role, account);
            if (granted) {
                _roleMembers[role].add(account);
            }
            return granted;
        }
        /**
         * @dev Overload {AccessControl-_revokeRole} to track enumerable memberships
         */
        function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {
            bool revoked = super._revokeRole(role, account);
            if (revoked) {
                _roleMembers[role].remove(account);
            }
            return revoked;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.25;
    import "@openzeppelin/contracts/access/extensions/IAccessControlEnumerable.sol";
    /// @notice This is a default access control with 3 roles:
    ///
    /// - ADMIN: allowed to do anything
    /// - ADMIN_DELEGATE: allowed to do anything except assigning ADMIN and ADMIN_DELEGATE roles
    /// - OPERATOR: low-privileged role, generally keeper or some other bot
    interface IDefaultAccessControl is IAccessControlEnumerable {
        error Forbidden();
        error AddressZero();
        function OPERATOR() external view returns (bytes32);
        function ADMIN_ROLE() external view returns (bytes32);
        function ADMIN_DELEGATE_ROLE() external view returns (bytes32);
        /// @notice Checks that the address is contract admin.
        /// @param who Address to check
        /// @return `true` if who is admin, `false` otherwise
        function isAdmin(address who) external view returns (bool);
        /// @notice Checks that the address is contract admin.
        /// @param who Address to check
        /// @return `true` if who is operator, `false` otherwise
        function isOperator(address who) external view returns (bool);
        /// @notice Checks that the address is contract admin.
        /// @param who Address to check
        /// @dev throws Forbbiden() if the sender does not have the admin or admin_delegate role
        function requireAdmin(address who) external view;
        /// @notice Checks that the address is contract admin.
        /// @param who Address to check
        /// @dev throws Forbbiden() if the sender has no roles
        function requireAtLeastOperator(address who) external view;
    }
    // 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) (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.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) (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) (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) (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.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
            }
        }
    }
    // 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) (access/extensions/IAccessControlEnumerable.sol)
    pragma solidity ^0.8.20;
    import {IAccessControl} from "../IAccessControl.sol";
    /**
     * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
     */
    interface IAccessControlEnumerable is IAccessControl {
        /**
         * @dev Returns one of the accounts that have `role`. `index` must be a
         * value between 0 and {getRoleMemberCount}, non-inclusive.
         *
         * Role bearers are not sorted in any particular way, and their ordering may
         * change at any point.
         *
         * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
         * you perform all queries on the same block. See the following
         * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
         * for more information.
         */
        function getRoleMember(bytes32 role, uint256 index) external view returns (address);
        /**
         * @dev Returns the number of accounts that have `role`. Can be used
         * together with {getRoleMember} to enumerate all bearers of a role.
         */
        function getRoleMemberCount(bytes32 role) external view returns (uint256);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
    pragma solidity ^0.8.20;
    import {IAccessControl} from "./IAccessControl.sol";
    import {Context} from "../utils/Context.sol";
    import {ERC165} from "../utils/introspection/ERC165.sol";
    /**
     * @dev Contract module that allows children to implement role-based access
     * control mechanisms. This is a lightweight version that doesn't allow enumerating role
     * members except through off-chain means by accessing the contract event logs. Some
     * applications may benefit from on-chain enumerability, for those cases see
     * {AccessControlEnumerable}.
     *
     * Roles are referred to by their `bytes32` identifier. These should be exposed
     * in the external API and be unique. The best way to achieve this is by
     * using `public constant` hash digests:
     *
     * ```solidity
     * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
     * ```
     *
     * Roles can be used to represent a set of permissions. To restrict access to a
     * function call, use {hasRole}:
     *
     * ```solidity
     * function foo() public {
     *     require(hasRole(MY_ROLE, msg.sender));
     *     ...
     * }
     * ```
     *
     * Roles can be granted and revoked dynamically via the {grantRole} and
     * {revokeRole} functions. Each role has an associated admin role, and only
     * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
     *
     * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
     * that only accounts with this role will be able to grant or revoke other
     * roles. More complex role relationships can be created by using
     * {_setRoleAdmin}.
     *
     * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
     * grant and revoke this role. Extra precautions should be taken to secure
     * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
     * to enforce additional security measures for this role.
     */
    abstract contract AccessControl is Context, IAccessControl, ERC165 {
        struct RoleData {
            mapping(address account => bool) hasRole;
            bytes32 adminRole;
        }
        mapping(bytes32 role => RoleData) private _roles;
        bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
        /**
         * @dev Modifier that checks that an account has a specific role. Reverts
         * with an {AccessControlUnauthorizedAccount} error including the required role.
         */
        modifier onlyRole(bytes32 role) {
            _checkRole(role);
            _;
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
        }
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) public view virtual returns (bool) {
            return _roles[role].hasRole[account];
        }
        /**
         * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
         * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
         */
        function _checkRole(bytes32 role) internal view virtual {
            _checkRole(role, _msgSender());
        }
        /**
         * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
         * is missing `role`.
         */
        function _checkRole(bytes32 role, address account) internal view virtual {
            if (!hasRole(role, account)) {
                revert AccessControlUnauthorizedAccount(account, role);
            }
        }
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
            return _roles[role].adminRole;
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         *
         * May emit a {RoleGranted} event.
         */
        function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
            _grantRole(role, account);
        }
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         *
         * May emit a {RoleRevoked} event.
         */
        function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
            _revokeRole(role, account);
        }
        /**
         * @dev Revokes `role` from the calling account.
         *
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
         * purpose is to provide a mechanism for accounts to lose their privileges
         * if they are compromised (such as when a trusted device is misplaced).
         *
         * If the calling account had been revoked `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `callerConfirmation`.
         *
         * May emit a {RoleRevoked} event.
         */
        function renounceRole(bytes32 role, address callerConfirmation) public virtual {
            if (callerConfirmation != _msgSender()) {
                revert AccessControlBadConfirmation();
            }
            _revokeRole(role, callerConfirmation);
        }
        /**
         * @dev Sets `adminRole` as ``role``'s admin role.
         *
         * Emits a {RoleAdminChanged} event.
         */
        function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
            bytes32 previousAdminRole = getRoleAdmin(role);
            _roles[role].adminRole = adminRole;
            emit RoleAdminChanged(role, previousAdminRole, adminRole);
        }
        /**
         * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
         *
         * Internal function without access restriction.
         *
         * May emit a {RoleGranted} event.
         */
        function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
            if (!hasRole(role, account)) {
                _roles[role].hasRole[account] = true;
                emit RoleGranted(role, account, _msgSender());
                return true;
            } else {
                return false;
            }
        }
        /**
         * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
         *
         * Internal function without access restriction.
         *
         * May emit a {RoleRevoked} event.
         */
        function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
            if (hasRole(role, account)) {
                _roles[role].hasRole[account] = false;
                emit RoleRevoked(role, account, _msgSender());
                return true;
            } else {
                return false;
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
    // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
    pragma solidity ^0.8.20;
    /**
     * @dev Library for managing
     * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
     * types.
     *
     * Sets have the following properties:
     *
     * - Elements are added, removed, and checked for existence in constant time
     * (O(1)).
     * - Elements are enumerated in O(n). No guarantees are made on the ordering.
     *
     * ```solidity
     * contract Example {
     *     // Add the library methods
     *     using EnumerableSet for EnumerableSet.AddressSet;
     *
     *     // Declare a set state variable
     *     EnumerableSet.AddressSet private mySet;
     * }
     * ```
     *
     * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
     * and `uint256` (`UintSet`) are supported.
     *
     * [WARNING]
     * ====
     * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
     * unusable.
     * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
     *
     * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
     * array of EnumerableSet.
     * ====
     */
    library EnumerableSet {
        // To implement this library for multiple types with as little code
        // repetition as possible, we write it in terms of a generic Set type with
        // bytes32 values.
        // The Set implementation uses private functions, and user-facing
        // implementations (such as AddressSet) are just wrappers around the
        // underlying Set.
        // This means that we can only create new EnumerableSets for types that fit
        // in bytes32.
        struct Set {
            // Storage of set values
            bytes32[] _values;
            // Position is the index of the value in the `values` array plus 1.
            // Position 0 is used to mean a value is not in the set.
            mapping(bytes32 value => uint256) _positions;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function _add(Set storage set, bytes32 value) private returns (bool) {
            if (!_contains(set, value)) {
                set._values.push(value);
                // The value is stored at length-1, but we add 1 to all indexes
                // and use 0 as a sentinel value
                set._positions[value] = set._values.length;
                return true;
            } else {
                return false;
            }
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function _remove(Set storage set, bytes32 value) private returns (bool) {
            // We cache the value's position to prevent multiple reads from the same storage slot
            uint256 position = set._positions[value];
            if (position != 0) {
                // Equivalent to contains(set, value)
                // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                // the array, and then remove the last element (sometimes called as 'swap and pop').
                // This modifies the order of the array, as noted in {at}.
                uint256 valueIndex = position - 1;
                uint256 lastIndex = set._values.length - 1;
                if (valueIndex != lastIndex) {
                    bytes32 lastValue = set._values[lastIndex];
                    // Move the lastValue to the index where the value to delete is
                    set._values[valueIndex] = lastValue;
                    // Update the tracked position of the lastValue (that was just moved)
                    set._positions[lastValue] = position;
                }
                // Delete the slot where the moved value was stored
                set._values.pop();
                // Delete the tracked position for the deleted slot
                delete set._positions[value];
                return true;
            } else {
                return false;
            }
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function _contains(Set storage set, bytes32 value) private view returns (bool) {
            return set._positions[value] != 0;
        }
        /**
         * @dev Returns the number of values on the set. O(1).
         */
        function _length(Set storage set) private view returns (uint256) {
            return set._values.length;
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function _at(Set storage set, uint256 index) private view returns (bytes32) {
            return set._values[index];
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function _values(Set storage set) private view returns (bytes32[] memory) {
            return set._values;
        }
        // Bytes32Set
        struct Bytes32Set {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
            return _add(set._inner, value);
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
            return _remove(set._inner, value);
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
            return _contains(set._inner, value);
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(Bytes32Set storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
            return _at(set._inner, index);
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
            bytes32[] memory store = _values(set._inner);
            bytes32[] memory result;
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
            return result;
        }
        // AddressSet
        struct AddressSet {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(AddressSet storage set, address value) internal returns (bool) {
            return _add(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(AddressSet storage set, address value) internal returns (bool) {
            return _remove(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(AddressSet storage set, address value) internal view returns (bool) {
            return _contains(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(AddressSet storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(AddressSet storage set, uint256 index) internal view returns (address) {
            return address(uint160(uint256(_at(set._inner, index))));
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(AddressSet storage set) internal view returns (address[] memory) {
            bytes32[] memory store = _values(set._inner);
            address[] memory result;
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
            return result;
        }
        // UintSet
        struct UintSet {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(UintSet storage set, uint256 value) internal returns (bool) {
            return _add(set._inner, bytes32(value));
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(UintSet storage set, uint256 value) internal returns (bool) {
            return _remove(set._inner, bytes32(value));
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(UintSet storage set, uint256 value) internal view returns (bool) {
            return _contains(set._inner, bytes32(value));
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(UintSet storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(UintSet storage set, uint256 index) internal view returns (uint256) {
            return uint256(_at(set._inner, index));
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(UintSet storage set) internal view returns (uint256[] memory) {
            bytes32[] memory store = _values(set._inner);
            uint256[] memory result;
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
            return result;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev External interface of AccessControl declared to support ERC165 detection.
     */
    interface IAccessControl {
        /**
         * @dev The `account` is missing a role.
         */
        error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
        /**
         * @dev The caller of a function is not the expected one.
         *
         * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
         */
        error AccessControlBadConfirmation();
        /**
         * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
         *
         * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
         * {RoleAdminChanged} not being emitted signaling this.
         */
        event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
        /**
         * @dev Emitted when `account` is granted `role`.
         *
         * `sender` is the account that originated the contract call, an admin role
         * bearer except when using {AccessControl-_setupRole}.
         */
        event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
        /**
         * @dev Emitted when `account` is revoked `role`.
         *
         * `sender` is the account that originated the contract call:
         *   - if using `revokeRole`, it is the admin role bearer
         *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
         */
        event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) external view returns (bool);
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {AccessControl-_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) external view returns (bytes32);
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function grantRole(bytes32 role, address account) external;
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function revokeRole(bytes32 role, address account) external;
        /**
         * @dev Revokes `role` from the calling account.
         *
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
         * purpose is to provide a mechanism for accounts to lose their privileges
         * if they are compromised (such as when a trusted device is misplaced).
         *
         * If the calling account had been granted `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `callerConfirmation`.
         */
        function renounceRole(bytes32 role, address callerConfirmation) external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
    pragma solidity ^0.8.20;
    import {IERC165} from "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }