{"file_path":"contracts/core/VaultRewardDistributor.sol","creation_status":"success","source_code":"// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.28;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {AccessControlUpgradeable} from \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport {SafeERC20} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport {IFirelightVault} from \"./interfaces/IFirelightVault.sol\";\n\n/**\n * @title VaultRewardDistributor\n * @notice Forwards reward and incentive vault assets into the Firelight vault.\n * @dev The contract does not perform swaps or mint vault shares. It only pulls\n * vault assets from authorized distributors, transfers them directly to the\n * vault, and emits metadata events for off-chain accounting.\n * @custom:security-contact securityreport@firelight.finance\n */\ncontract VaultRewardDistributor is AccessControlUpgradeable {\n    using SafeERC20 for IERC20;\n\n    /// @notice Role allowed to forward reward and incentive vault assets into the vault.\n    bytes32 public constant DISTRIBUTOR_ROLE = keccak256(\"DISTRIBUTOR_ROLE\");\n\n    /// @notice Role allowed to recover tokens accidentally sent to this contract.\n    bytes32 public constant SWEEPER_ROLE = keccak256(\"SWEEPER_ROLE\");\n\n    // --- ERC-7201 Namespaced Storage ---\n    /// @custom:storage-location erc7201:firelight.vaultrewarddistributor.storage\n    struct VaultRewardDistributorStorage {\n        /// @notice Firelight vault receiving forwarded vault assets.\n        IFirelightVault vault;\n        /// @notice ERC20 asset accepted by the vault and forwarded by this contract.\n        IERC20 vaultAsset;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"firelight.vaultrewarddistributor.storage\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant STORAGE_LOCATION = 0x651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b00;\n\n    function _getStorage() private pure returns (VaultRewardDistributorStorage storage $) {\n        assembly {\n            $.slot := STORAGE_LOCATION\n        }\n    }\n\n    /**\n     * @notice Emitted when premium-sale proceeds are forwarded to the vault as vault assets.\n     * @param vaultPeriod Vault period at the time the vault assets are transferred.\n     * @param premiumToken Premium token that was swapped off-chain into the vault asset.\n     * @param vaultAssetAmount Amount of vault asset transferred into the vault.\n     * @param premiumTokenAmount Amount of premium token swapped off-chain.\n     * @param premiumSwapTimestamp Timestamp when the off-chain premium-token swap occurred.\n     */\n    event RewardsDistributed(\n        uint256 indexed vaultPeriod,\n        address indexed premiumToken,\n        uint256 vaultAssetAmount,\n        uint256 premiumTokenAmount,\n        uint48 premiumSwapTimestamp\n    );\n\n    /**\n     * @notice Emitted when incentive funds are forwarded to the vault.\n     * @param vaultPeriod Vault period at the time the vault assets are transferred.\n     * @param incentiveRef Optional external reference for the incentive program or payment.\n     * @param vaultAssetAmount Amount of vault asset transferred into the vault.\n     */\n    event IncentiveDistributed(uint256 indexed vaultPeriod, bytes32 indexed incentiveRef, uint256 vaultAssetAmount);\n\n    /**\n     * @notice Emitted when tokens accidentally sent to this contract are recovered.\n     * @param token Token recovered from this contract.\n     * @param to Address receiving the recovered tokens.\n     * @param amount Amount of tokens recovered.\n     */\n    event TokenSwept(address indexed token, address indexed to, uint256 amount);\n\n    /// @notice Reverts when a required address argument is zero.\n    error InvalidZeroAddress();\n\n    /// @notice Reverts when an amount argument is zero.\n    error InvalidAmount();\n\n    /// @notice Reverts when premium swap metadata has a zero or future timestamp.\n    error InvalidSwapTimestamp();\n\n    /// @custom:oz-upgrades-unsafe-allow constructor\n    constructor() {\n        _disableInitializers();\n    }\n\n    /**\n     * @notice Initializes the vault reward distributor proxy.\n     * @dev Reads and stores the vault asset from `_vault.asset()`.\n     * @param _vault Firelight vault receiving forwarded vault assets.\n     * @param admin Address receiving the default admin role.\n     * @param distributor Address allowed to forward reward and incentive funds.\n     * @param sweeper Optional address allowed to recover tokens accidentally sent to this contract.\n     */\n    function initialize(\n        IFirelightVault _vault,\n        address admin,\n        address distributor,\n        address sweeper\n    ) public initializer {\n        if (admin == address(0) || distributor == address(0) || address(_vault) == address(0)) {\n            revert InvalidZeroAddress();\n        }\n\n        __AccessControl_init();\n\n        VaultRewardDistributorStorage storage $ = _getStorage();\n\n        $.vault = IFirelightVault(_vault);\n        $.vaultAsset = IERC20(_vault.asset());\n\n        _grantRole(DEFAULT_ADMIN_ROLE, admin);\n        _grantRole(DISTRIBUTOR_ROLE, distributor);\n        if (sweeper != address(0)) _grantRole(SWEEPER_ROLE, sweeper);\n    }\n\n    /**\n     * @notice Forwards premium-sale proceeds into the vault as vault assets.\n     * @dev The caller must approve this contract to transfer `vaultAssetAmount`\n     * of the vault asset. `premiumToken`, `premiumTokenAmount`, and\n     * `premiumSwapTimestamp` are accounting metadata only; no premium tokens are\n     * transferred by this function.\n     * @param vaultAssetAmount Amount of vault asset to transfer into the vault.\n     * @param premiumToken Premium token swapped off-chain into the vault asset.\n     * @param premiumTokenAmount Amount of premium token swapped off-chain.\n     * @param premiumSwapTimestamp Timestamp when the off-chain premium-token swap occurred.\n     */\n    function distributeRewards(\n        uint256 vaultAssetAmount,\n        address premiumToken,\n        uint256 premiumTokenAmount,\n        uint48 premiumSwapTimestamp\n    ) external onlyRole(DISTRIBUTOR_ROLE) {\n        if (premiumToken == address(0)) revert InvalidZeroAddress();\n        if (vaultAssetAmount == 0 || premiumTokenAmount == 0) revert InvalidAmount();\n        if (premiumSwapTimestamp == 0 || premiumSwapTimestamp > block.timestamp) revert InvalidSwapTimestamp();\n\n        VaultRewardDistributorStorage storage $ = _getStorage();\n\n        IFirelightVault _vault = $.vault;\n        uint256 vaultPeriod = _vault.currentPeriod();\n        $.vaultAsset.safeTransferFrom(msg.sender, address(_vault), vaultAssetAmount);\n\n        // Checkpoint atomically so historical totalAssetsAt lookups (period-start\n        // snapshots) include the forwarded assets. Requires CHECKPOINT_ROLE on the vault.\n        _vault.checkpointTotalAssets();\n\n        emit RewardsDistributed(vaultPeriod, premiumToken, vaultAssetAmount, premiumTokenAmount, premiumSwapTimestamp);\n    }\n\n    /**\n     * @notice Forwards incentive funds into the vault as vault assets.\n     * @dev The caller must approve this contract to transfer `vaultAssetAmount`\n     * of the vault asset. `incentiveRef` is emitted as opaque metadata and is\n     * not interpreted on-chain.\n     * @param vaultAssetAmount Amount of vault asset to transfer into the vault.\n     * @param incentiveRef Optional external reference for the incentive program or payment.\n     */\n    function distributeIncentive(uint256 vaultAssetAmount, bytes32 incentiveRef) external onlyRole(DISTRIBUTOR_ROLE) {\n        if (vaultAssetAmount == 0) revert InvalidAmount();\n\n        VaultRewardDistributorStorage storage $ = _getStorage();\n\n        IFirelightVault _vault = $.vault;\n        uint256 vaultPeriod = _vault.currentPeriod();\n        $.vaultAsset.safeTransferFrom(msg.sender, address(_vault), vaultAssetAmount);\n\n        // Checkpoint atomically so historical totalAssetsAt lookups (period-start\n        // snapshots) include the forwarded assets. Requires CHECKPOINT_ROLE on the vault.\n        _vault.checkpointTotalAssets();\n\n        emit IncentiveDistributed(vaultPeriod, incentiveRef, vaultAssetAmount);\n    }\n\n    /**\n     * @notice Recovers tokens accidentally sent to this contract.\n     * @dev Restricted to `SWEEPER_ROLE`. This contract should not retain token\n     * balances during normal reward distribution flows.\n     * @param token Token to recover.\n     * @param to Address receiving the recovered tokens.\n     * @param amount Amount of tokens to recover.\n     */\n    function sweep(IERC20 token, address to, uint256 amount) external onlyRole(SWEEPER_ROLE) {\n        if (address(token) == address(0) || to == address(0)) revert InvalidZeroAddress();\n        if (amount == 0) revert InvalidAmount();\n        token.safeTransfer(to, amount);\n\n        emit TokenSwept(address(token), to, amount);\n    }\n\n    /**\n     * @notice Returns the Firelight vault receiving forwarded vault assets.\n     * @return Firelight vault receiving forwarded vault assets.\n     */\n    function vault() external view returns (IFirelightVault) {\n        return _getStorage().vault;\n    }\n\n    /**\n     * @notice Returns the vault asset token distributed to the vault.\n     * @return The vault asset token.\n     */\n    function vaultAsset() external view returns (IERC20) {\n        return _getStorage().vaultAsset;\n    }\n}\n","deployed_bytecode":"0x608080604052600436101561001357600080fd5b600090813560e01c90816301ffc9a714610f06575080630b5420b014610d38578063248a9ca314610ce35780632f2ff15d14610c8357806336568abe14610c185780634ab9e16b14610bc55780635f82abb914610b8a57806362c06767146109be578063855235fd1461072057806391d14854146106a7578063a217fddf1461068b578063d547741f14610622578063f0bd87cc146105e7578063f8c8765e1461011b5763fbfa77cf146100c657600080fd5b34610118578060031936011261011857602073ffffffffffffffffffffffffffffffffffffffff7f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b005416604051908152f35b80fd5b50346101185760806003193601126101185760043573ffffffffffffffffffffffffffffffffffffffff81168091036105e357610156610fa6565b906044359073ffffffffffffffffffffffffffffffffffffffff8216928383036105df576064359273ffffffffffffffffffffffffffffffffffffffff8416928385036104cf577ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00549560ff8760401c16159667ffffffffffffffff8116801590816105d7575b60011490816105cd575b1590816105c4575b5061059c578760017fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000008316177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0055610547575b5073ffffffffffffffffffffffffffffffffffffffff84161590811561053e575b508015610536575b61050e5760ff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460401c16156104e6576020816004927fffffffffffffffffffffffff00000000000000000000000000000000000000007f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b005416177f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b0055604051928380927f38d52e0f0000000000000000000000000000000000000000000000000000000082525afa9081156104db578791610486575b506103d49273ffffffffffffffffffffffffffffffffffffffff6103ce92167fffffffffffffffffffffffff00000000000000000000000000000000000000007f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b015416177f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b01556111b9565b506112a3565b50610476575b506103e25780f35b7fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054167ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a180f35b61047f906113a9565b50386103da565b90506020813d6020116104d3575b816104a160209383610fc9565b810103126104cf575173ffffffffffffffffffffffffffffffffffffffff811681036104cf576103d4610343565b8680fd5b3d9150610494565b6040513d89823e3d90fd5b6004877fd7e6bcf8000000000000000000000000000000000000000000000000000000008152fd5b6004877ff6b2911f000000000000000000000000000000000000000000000000000000008152fd5b50801561026b565b90501538610263565b7fffffffffffffffffffffffffffffffffffffffffffffff0000000000000000001668010000000000000001177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005538610242565b6004897ff92ee8a9000000000000000000000000000000000000000000000000000000008152fd5b905015386101ef565b303b1591506101e7565b8991506101dd565b8480fd5b5080fd5b503461011857806003193601126101185760206040517ffbd454f36a7e1a388bd6fc3ab10d434aa4578f811acbbcf33afb1c697486313c8152f35b503461011857604060031936011261011857610687600435610642610fa6565b9061068261067d826000527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260016040600020015490565b6110c4565b6115cc565b5080f35b5034610118578060031936011261011857602090604051908152f35b50346101185760406003193601126101185773ffffffffffffffffffffffffffffffffffffffff60406106d8610fa6565b9260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052209116600052602052602060ff604060002054166040519015158152f35b50346101185760806003193601126101185760043561073d610fa6565b604435906064359065ffffffffffff82168092036105df5773ffffffffffffffffffffffffffffffffffffffff90610773611039565b16928315610996578015801561098e575b610966578115801561095d575b6109355773ffffffffffffffffffffffffffffffffffffffff7f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b0054169285604051947f06040618000000000000000000000000000000000000000000000000000000008652602086600481845afa9586156108ee5782966108f9575b50610852848273ffffffffffffffffffffffffffffffffffffffff7f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b0154163390611150565b803b156105e3578180916004604051809481937f37cbfb540000000000000000000000000000000000000000000000000000000083525af180156108ee576108d1575b5050907f62cd1760fb4555cb2e984912e5d1cde678038fe456dafe3b4fc9daa165db21a99260609260405192835260208301526040820152a380f35b816108de91949394610fc9565b6108ea57908538610895565b8580fd5b6040513d84823e3d90fd5b915094506020813d60201161092d575b8161091660209383610fc9565b8101031261092857869051943861080d565b600080fd5b3d9150610909565b6004857f5e0feb80000000000000000000000000000000000000000000000000000000008152fd5b50428211610791565b6004857f2c5211c6000000000000000000000000000000000000000000000000000000008152fd5b508215610784565b6004857ff6b2911f000000000000000000000000000000000000000000000000000000008152fd5b50346101185760606003193601126101185760043573ffffffffffffffffffffffffffffffffffffffff8116808203610b86576109f9610fa6565b604435907f8aef0597c0be1e090afba1f387ee99f604b5d975ccbed6215cdf146ffd5c49fc85527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526040852073ffffffffffffffffffffffffffffffffffffffff331660005260205260ff6040600020541615610b365782158015610b18575b610996578115610966577f115d7b5114b5954762cc233b141a7c777a8f79d93f50af7216d645c87fb4883e91610b0e60209273ffffffffffffffffffffffffffffffffffffffff604051917fa9059cbb0000000000000000000000000000000000000000000000000000000086840152169687602483015283604483015260448252610b09606483610fc9565b6116de565b604051908152a380f35b5073ffffffffffffffffffffffffffffffffffffffff811615610a7c565b6044857fe2517d3f000000000000000000000000000000000000000000000000000000008152336004527f8aef0597c0be1e090afba1f387ee99f604b5d975ccbed6215cdf146ffd5c49fc602452fd5b8280fd5b503461011857806003193601126101185760206040517f8aef0597c0be1e090afba1f387ee99f604b5d975ccbed6215cdf146ffd5c49fc8152f35b5034610118578060031936011261011857602073ffffffffffffffffffffffffffffffffffffffff7f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b015416604051908152f35b503461011857604060031936011261011857610c32610fa6565b3373ffffffffffffffffffffffffffffffffffffffff821603610c5b57610687906004356115cc565b6004827f6697b232000000000000000000000000000000000000000000000000000000008152fd5b503461011857604060031936011261011857610687600435610ca3610fa6565b90610cde61067d826000527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260016040600020015490565b6114af565b5034610118576020600319360112610118576020610d306004356000527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260016040600020015490565b604051908152f35b503461011857604060031936011261011857600435610d55611039565b8015610ede5773ffffffffffffffffffffffffffffffffffffffff7f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b00541682604051917f06040618000000000000000000000000000000000000000000000000000000008352602083600481845afa9283156108ee578293610eaa575b50610e17848273ffffffffffffffffffffffffffffffffffffffff7f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b0154163390611150565b803b156105e35781906004604051809481937f37cbfb540000000000000000000000000000000000000000000000000000000083525af18015610e9f57610e8b575b506040519182527f061815bb9389e99671bd235fdce8001cb96e0e728228c3f6198b4b5c34e793ab602060243593a380f35b83610e9891949294610fc9565b9138610e59565b6040513d86823e3d90fd5b9092506020813d602011610ed6575b81610ec660209383610fc9565b810103126105e357519138610dd2565b3d9150610eb9565b6004827f2c5211c6000000000000000000000000000000000000000000000000000000008152fd5b9050346105e35760206003193601126105e3576004357fffffffff000000000000000000000000000000000000000000000000000000008116809103610b8657602092507f7965db0b000000000000000000000000000000000000000000000000000000008114908115610f7c575b5015158152f35b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501438610f75565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361092857565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761100a57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b3360009081527fb2ce6f3a0968c56a08b54f4f0d1d0af4871f2c3340441c6ce862aaa53c9d37c7602052604090205460ff161561107257565b7fe2517d3f00000000000000000000000000000000000000000000000000000000600052336004527ffbd454f36a7e1a388bd6fc3ab10d434aa4578f811acbbcf33afb1c697486313c60245260446000fd5b806000527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052604060002073ffffffffffffffffffffffffffffffffffffffff331660005260205260ff604060002054161561111f5750565b7fe2517d3f000000000000000000000000000000000000000000000000000000006000523360045260245260446000fd5b90919273ffffffffffffffffffffffffffffffffffffffff6111b79481604051957f23b872dd000000000000000000000000000000000000000000000000000000006020880152166024860152166044840152606483015260648252610b09608483610fc9565b565b73ffffffffffffffffffffffffffffffffffffffff811660009081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff1661129d5773ffffffffffffffffffffffffffffffffffffffff1660008181527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d6020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790553391907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b50600090565b73ffffffffffffffffffffffffffffffffffffffff811660009081527fb2ce6f3a0968c56a08b54f4f0d1d0af4871f2c3340441c6ce862aaa53c9d37c7602052604090205460ff1661129d5773ffffffffffffffffffffffffffffffffffffffff1660008181527fb2ce6f3a0968c56a08b54f4f0d1d0af4871f2c3340441c6ce862aaa53c9d37c76020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790553391907ffbd454f36a7e1a388bd6fc3ab10d434aa4578f811acbbcf33afb1c697486313c907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9080a4600190565b73ffffffffffffffffffffffffffffffffffffffff811660009081527f1b996b57c973fc580e60d7b693bdee5a42ae6959b06e124dccb244a8f0bf98b2602052604090205460ff1661129d5773ffffffffffffffffffffffffffffffffffffffff1660008181527f1b996b57c973fc580e60d7b693bdee5a42ae6959b06e124dccb244a8f0bf98b26020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790553391907f8aef0597c0be1e090afba1f387ee99f604b5d975ccbed6215cdf146ffd5c49fc907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9080a4600190565b806000527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052604060002073ffffffffffffffffffffffffffffffffffffffff831660005260205260ff60406000205416156000146115c557806000527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052604060002073ffffffffffffffffffffffffffffffffffffffff8316600052602052604060002060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905573ffffffffffffffffffffffffffffffffffffffff339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d600080a4600190565b5050600090565b806000527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052604060002073ffffffffffffffffffffffffffffffffffffffff831660005260205260ff604060002054166000146115c557806000527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052604060002073ffffffffffffffffffffffffffffffffffffffff831660005260205260406000207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b600080a4600190565b906000602091828151910182855af115611769576000513d611760575073ffffffffffffffffffffffffffffffffffffffff81163b155b61171c5750565b73ffffffffffffffffffffffffffffffffffffffff907f5274afe7000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b60011415611715565b6040513d6000823e3d90fdfea26469706673582212207d5864b5152bb634d28efca8c1fbfcb657aede78761113dc19631a1bda55c7b364736f6c634300081c0033","optimization_enabled":true,"verified_twin_address_hash":null,"is_verified":true,"compiler_settings":{"evmVersion":"london","libraries":{"":{"__CACHE_BREAKER__":"0x00000000d41867734bbee4c6863d9255b2b06ac1"}},"optimizer":{"enabled":true,"runs":10000},"outputSelection":{"*":{"":["*"],"*":["*"]}},"viaIR":true},"optimization_runs":10000,"sourcify_repo_url":null,"decoded_constructor_args":null,"compiler_version":"v0.8.28+commit.7893614a","is_verified_via_verifier_alliance":false,"verified_at":"2026-09-30T03:00:15.668836Z","implementations":[],"proxy_type":null,"external_libraries":[{"name":":__CACHE_BREAKER__","address_hash":"0x00000000D41867734BBee4C6863D9255b2b06aC1"}],"creation_bytecode":"0x6080806040523460d2577ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460ff8160401c1660c1576002600160401b03196001600160401b03821601605c575b6040516117ab90816100d88239f35b6001600160401b0319166001600160401b039081177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005581527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a13880604d565b63f92ee8a960e01b60005260046000fd5b600080fdfe608080604052600436101561001357600080fd5b600090813560e01c90816301ffc9a714610f06575080630b5420b014610d38578063248a9ca314610ce35780632f2ff15d14610c8357806336568abe14610c185780634ab9e16b14610bc55780635f82abb914610b8a57806362c06767146109be578063855235fd1461072057806391d14854146106a7578063a217fddf1461068b578063d547741f14610622578063f0bd87cc146105e7578063f8c8765e1461011b5763fbfa77cf146100c657600080fd5b34610118578060031936011261011857602073ffffffffffffffffffffffffffffffffffffffff7f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b005416604051908152f35b80fd5b50346101185760806003193601126101185760043573ffffffffffffffffffffffffffffffffffffffff81168091036105e357610156610fa6565b906044359073ffffffffffffffffffffffffffffffffffffffff8216928383036105df576064359273ffffffffffffffffffffffffffffffffffffffff8416928385036104cf577ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00549560ff8760401c16159667ffffffffffffffff8116801590816105d7575b60011490816105cd575b1590816105c4575b5061059c578760017fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000008316177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0055610547575b5073ffffffffffffffffffffffffffffffffffffffff84161590811561053e575b508015610536575b61050e5760ff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460401c16156104e6576020816004927fffffffffffffffffffffffff00000000000000000000000000000000000000007f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b005416177f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b0055604051928380927f38d52e0f0000000000000000000000000000000000000000000000000000000082525afa9081156104db578791610486575b506103d49273ffffffffffffffffffffffffffffffffffffffff6103ce92167fffffffffffffffffffffffff00000000000000000000000000000000000000007f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b015416177f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b01556111b9565b506112a3565b50610476575b506103e25780f35b7fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054167ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a180f35b61047f906113a9565b50386103da565b90506020813d6020116104d3575b816104a160209383610fc9565b810103126104cf575173ffffffffffffffffffffffffffffffffffffffff811681036104cf576103d4610343565b8680fd5b3d9150610494565b6040513d89823e3d90fd5b6004877fd7e6bcf8000000000000000000000000000000000000000000000000000000008152fd5b6004877ff6b2911f000000000000000000000000000000000000000000000000000000008152fd5b50801561026b565b90501538610263565b7fffffffffffffffffffffffffffffffffffffffffffffff0000000000000000001668010000000000000001177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005538610242565b6004897ff92ee8a9000000000000000000000000000000000000000000000000000000008152fd5b905015386101ef565b303b1591506101e7565b8991506101dd565b8480fd5b5080fd5b503461011857806003193601126101185760206040517ffbd454f36a7e1a388bd6fc3ab10d434aa4578f811acbbcf33afb1c697486313c8152f35b503461011857604060031936011261011857610687600435610642610fa6565b9061068261067d826000527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260016040600020015490565b6110c4565b6115cc565b5080f35b5034610118578060031936011261011857602090604051908152f35b50346101185760406003193601126101185773ffffffffffffffffffffffffffffffffffffffff60406106d8610fa6565b9260043581527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052209116600052602052602060ff604060002054166040519015158152f35b50346101185760806003193601126101185760043561073d610fa6565b604435906064359065ffffffffffff82168092036105df5773ffffffffffffffffffffffffffffffffffffffff90610773611039565b16928315610996578015801561098e575b610966578115801561095d575b6109355773ffffffffffffffffffffffffffffffffffffffff7f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b0054169285604051947f06040618000000000000000000000000000000000000000000000000000000008652602086600481845afa9586156108ee5782966108f9575b50610852848273ffffffffffffffffffffffffffffffffffffffff7f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b0154163390611150565b803b156105e3578180916004604051809481937f37cbfb540000000000000000000000000000000000000000000000000000000083525af180156108ee576108d1575b5050907f62cd1760fb4555cb2e984912e5d1cde678038fe456dafe3b4fc9daa165db21a99260609260405192835260208301526040820152a380f35b816108de91949394610fc9565b6108ea57908538610895565b8580fd5b6040513d84823e3d90fd5b915094506020813d60201161092d575b8161091660209383610fc9565b8101031261092857869051943861080d565b600080fd5b3d9150610909565b6004857f5e0feb80000000000000000000000000000000000000000000000000000000008152fd5b50428211610791565b6004857f2c5211c6000000000000000000000000000000000000000000000000000000008152fd5b508215610784565b6004857ff6b2911f000000000000000000000000000000000000000000000000000000008152fd5b50346101185760606003193601126101185760043573ffffffffffffffffffffffffffffffffffffffff8116808203610b86576109f9610fa6565b604435907f8aef0597c0be1e090afba1f387ee99f604b5d975ccbed6215cdf146ffd5c49fc85527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b6268006020526040852073ffffffffffffffffffffffffffffffffffffffff331660005260205260ff6040600020541615610b365782158015610b18575b610996578115610966577f115d7b5114b5954762cc233b141a7c777a8f79d93f50af7216d645c87fb4883e91610b0e60209273ffffffffffffffffffffffffffffffffffffffff604051917fa9059cbb0000000000000000000000000000000000000000000000000000000086840152169687602483015283604483015260448252610b09606483610fc9565b6116de565b604051908152a380f35b5073ffffffffffffffffffffffffffffffffffffffff811615610a7c565b6044857fe2517d3f000000000000000000000000000000000000000000000000000000008152336004527f8aef0597c0be1e090afba1f387ee99f604b5d975ccbed6215cdf146ffd5c49fc602452fd5b8280fd5b503461011857806003193601126101185760206040517f8aef0597c0be1e090afba1f387ee99f604b5d975ccbed6215cdf146ffd5c49fc8152f35b5034610118578060031936011261011857602073ffffffffffffffffffffffffffffffffffffffff7f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b015416604051908152f35b503461011857604060031936011261011857610c32610fa6565b3373ffffffffffffffffffffffffffffffffffffffff821603610c5b57610687906004356115cc565b6004827f6697b232000000000000000000000000000000000000000000000000000000008152fd5b503461011857604060031936011261011857610687600435610ca3610fa6565b90610cde61067d826000527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260016040600020015490565b6114af565b5034610118576020600319360112610118576020610d306004356000527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680060205260016040600020015490565b604051908152f35b503461011857604060031936011261011857600435610d55611039565b8015610ede5773ffffffffffffffffffffffffffffffffffffffff7f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b00541682604051917f06040618000000000000000000000000000000000000000000000000000000008352602083600481845afa9283156108ee578293610eaa575b50610e17848273ffffffffffffffffffffffffffffffffffffffff7f651ce661f7078ecd2e4440f466af8c7b90ea5bb0ee816595039a0e5aea2c8b0154163390611150565b803b156105e35781906004604051809481937f37cbfb540000000000000000000000000000000000000000000000000000000083525af18015610e9f57610e8b575b506040519182527f061815bb9389e99671bd235fdce8001cb96e0e728228c3f6198b4b5c34e793ab602060243593a380f35b83610e9891949294610fc9565b9138610e59565b6040513d86823e3d90fd5b9092506020813d602011610ed6575b81610ec660209383610fc9565b810103126105e357519138610dd2565b3d9150610eb9565b6004827f2c5211c6000000000000000000000000000000000000000000000000000000008152fd5b9050346105e35760206003193601126105e3576004357fffffffff000000000000000000000000000000000000000000000000000000008116809103610b8657602092507f7965db0b000000000000000000000000000000000000000000000000000000008114908115610f7c575b5015158152f35b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501438610f75565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361092857565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761100a57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b3360009081527fb2ce6f3a0968c56a08b54f4f0d1d0af4871f2c3340441c6ce862aaa53c9d37c7602052604090205460ff161561107257565b7fe2517d3f00000000000000000000000000000000000000000000000000000000600052336004527ffbd454f36a7e1a388bd6fc3ab10d434aa4578f811acbbcf33afb1c697486313c60245260446000fd5b806000527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052604060002073ffffffffffffffffffffffffffffffffffffffff331660005260205260ff604060002054161561111f5750565b7fe2517d3f000000000000000000000000000000000000000000000000000000006000523360045260245260446000fd5b90919273ffffffffffffffffffffffffffffffffffffffff6111b79481604051957f23b872dd000000000000000000000000000000000000000000000000000000006020880152166024860152166044840152606483015260648252610b09608483610fc9565b565b73ffffffffffffffffffffffffffffffffffffffff811660009081527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d602052604090205460ff1661129d5773ffffffffffffffffffffffffffffffffffffffff1660008181527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d6020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790553391907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b50600090565b73ffffffffffffffffffffffffffffffffffffffff811660009081527fb2ce6f3a0968c56a08b54f4f0d1d0af4871f2c3340441c6ce862aaa53c9d37c7602052604090205460ff1661129d5773ffffffffffffffffffffffffffffffffffffffff1660008181527fb2ce6f3a0968c56a08b54f4f0d1d0af4871f2c3340441c6ce862aaa53c9d37c76020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790553391907ffbd454f36a7e1a388bd6fc3ab10d434aa4578f811acbbcf33afb1c697486313c907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9080a4600190565b73ffffffffffffffffffffffffffffffffffffffff811660009081527f1b996b57c973fc580e60d7b693bdee5a42ae6959b06e124dccb244a8f0bf98b2602052604090205460ff1661129d5773ffffffffffffffffffffffffffffffffffffffff1660008181527f1b996b57c973fc580e60d7b693bdee5a42ae6959b06e124dccb244a8f0bf98b26020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790553391907f8aef0597c0be1e090afba1f387ee99f604b5d975ccbed6215cdf146ffd5c49fc907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9080a4600190565b806000527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052604060002073ffffffffffffffffffffffffffffffffffffffff831660005260205260ff60406000205416156000146115c557806000527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052604060002073ffffffffffffffffffffffffffffffffffffffff8316600052602052604060002060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905573ffffffffffffffffffffffffffffffffffffffff339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d600080a4600190565b5050600090565b806000527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052604060002073ffffffffffffffffffffffffffffffffffffffff831660005260205260ff604060002054166000146115c557806000527f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800602052604060002073ffffffffffffffffffffffffffffffffffffffff831660005260205260406000207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b600080a4600190565b906000602091828151910182855af115611769576000513d611760575073ffffffffffffffffffffffffffffffffffffffff81163b155b61171c5750565b73ffffffffffffffffffffffffffffffffffffffff907f5274afe7000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b60011415611715565b6040513d6000823e3d90fdfea26469706673582212207d5864b5152bb634d28efca8c1fbfcb657aede78761113dc19631a1bda55c7b364736f6c634300081c0033","name":"VaultRewardDistributor","is_blueprint":false,"license_type":"none","is_fully_verified":true,"is_verified_via_eth_bytecode_db":false,"language":"solidity","evm_version":"london","can_be_visualized_via_sol2uml":false,"is_verified_via_sourcify":false,"additional_sources":[{"file_path":"@openzeppelin/contracts/interfaces/IERC20.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC20} from \"../token/ERC20/IERC20.sol\";\n"},{"file_path":"contracts/core/interfaces/IAggregatorV3.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title IAggregatorV3\n * @dev Chainlink Aggregator V3 interface for price feeds. Mirrors\n *      https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol\n */\ninterface IAggregatorV3 {\n    /**\n     * @notice Number of decimals for the price value.\n     * @return The decimals.\n     */\n    function decimals() external view returns (uint8);\n\n    /**\n     * @notice Human-readable asset pair description (e.g., \"ETH/USD\").\n     * @return The asset pair description.\n     */\n    function description() external view returns (string memory);\n\n    /**\n     * @notice Contract version.\n     * @return Version as a uint256 value.\n     */\n    function version() external view returns (uint256);\n\n    /**\n     * @notice Obtain round data. Used for historical information, each round corresponds to a data point.\n     * @dev Implementations that do not retain historical rounds (e.g. live FTSO-backed\n     *      aggregators) should revert with a `NotImplemented()` custom error.\n     */\n    function getRoundData(uint80 _roundId)\n        external\n        view\n        returns (\n            uint80 roundId,\n            int256 answer,\n            uint256 startedAt,\n            uint256 updatedAt,\n            uint80 answeredInRound\n        );\n\n    /**\n     * @notice Fetch the latest round information. Retrieves the last recorded data.\n     */\n    function latestRoundData()\n        external\n        view\n        returns (\n            uint80 roundId,\n            int256 answer,\n            uint256 startedAt,\n            uint256 updatedAt,\n            uint80 answeredInRound\n        );\n}\n\n"},{"file_path":"contracts/test/CoverNFTHarness.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.28;\n\nimport {CoverNFT} from \"../core/CoverNFT.sol\";\n\n/**\n * @dev Exposes CoverNFT internal hooks that are unreachable through the external ABI,\n *      so tests can exercise the required multiple-inheritance overrides.\n */\ncontract CoverNFTHarness is CoverNFT {\n    function exposedIncreaseBalance(address account, uint128 value) external {\n        _increaseBalance(account, value);\n    }\n}\n"},{"file_path":"contracts/core/lib/PriceFeed.sol","source_code":"// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.28;\n\nimport {IAggregatorV3} from \"../interfaces/IAggregatorV3.sol\";\n\n/**\n * @title PriceFeed\n * @notice Reads and validates a Chainlink-compatible price feed answer.\n * @custom:security-contact securityreport@firelight.finance\n */\nlibrary PriceFeed {\n    /// @notice Thrown when the feed returns a non-positive answer.\n    /// @param price The non-positive answer returned by the feed.\n    error InvalidAssetPrice(int256 price);\n    /// @notice Thrown when the feed reports a zero `updatedAt` timestamp.\n    error InvalidAssetPriceUpdatedAt();\n    /// @notice Thrown when the feed answer is older than the allowed maximum age.\n    /// @param updatedAt Timestamp of the feed answer.\n    /// @param maxPriceAge Maximum tolerated age, in seconds.\n    error PriceFeedTooOld(uint256 updatedAt, uint48 maxPriceAge);\n    /// @notice Thrown when the answered round is older than the requested round.\n    /// @param roundId Round id of the latest answer.\n    /// @param answeredInRound Round in which the answer was computed.\n    error StaleAssetPriceRound(uint80 roundId, uint80 answeredInRound);\n\n    /// @notice Reads `latestRoundData` and validates positivity, round, and freshness.\n    /// @param feedAdapter Chainlink-compatible feed to query.\n    /// @param maxAge Maximum tolerated age of the answer, in seconds.\n    /// @return price The validated price, in the feed's own decimals.\n    function getPrice(\n        IAggregatorV3 feedAdapter,\n        uint48 maxAge\n    ) internal view returns (uint256 price) {\n        (uint80 roundId, int256 answer,, uint256 updatedAt, uint80 answeredInRound) = feedAdapter.latestRoundData();\n\n        if (answer <= 0) revert InvalidAssetPrice(answer);\n        if (updatedAt == 0) revert InvalidAssetPriceUpdatedAt();\n        if (answeredInRound < roundId) revert StaleAssetPriceRound(roundId, answeredInRound);\n        if (block.timestamp - updatedAt > maxAge) revert PriceFeedTooOld(updatedAt, maxAge);\n\n        return (uint256(answer));\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/governance/TimelockController.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (governance/TimelockController.sol)\n\npragma solidity ^0.8.20;\n\nimport {AccessControl} from \"../access/AccessControl.sol\";\nimport {ERC721Holder} from \"../token/ERC721/utils/ERC721Holder.sol\";\nimport {ERC1155Holder} from \"../token/ERC1155/utils/ERC1155Holder.sol\";\nimport {Address} from \"../utils/Address.sol\";\n\n/**\n * @dev Contract module which acts as a timelocked controller. When set as the\n * owner of an `Ownable` smart contract, it enforces a timelock on all\n * `onlyOwner` maintenance operations. This gives time for users of the\n * controlled contract to exit before a potentially dangerous maintenance\n * operation is applied.\n *\n * By default, this contract is self administered, meaning administration tasks\n * have to go through the timelock process. The proposer (resp executor) role\n * is in charge of proposing (resp executing) operations. A common use case is\n * to position this {TimelockController} as the owner of a smart contract, with\n * a multisig or a DAO as the sole proposer.\n */\ncontract TimelockController is AccessControl, ERC721Holder, ERC1155Holder {\n    bytes32 public constant PROPOSER_ROLE = keccak256(\"PROPOSER_ROLE\");\n    bytes32 public constant EXECUTOR_ROLE = keccak256(\"EXECUTOR_ROLE\");\n    bytes32 public constant CANCELLER_ROLE = keccak256(\"CANCELLER_ROLE\");\n    uint256 internal constant _DONE_TIMESTAMP = uint256(1);\n\n    mapping(bytes32 id => uint256) private _timestamps;\n    uint256 private _minDelay;\n\n    enum OperationState {\n        Unset,\n        Waiting,\n        Ready,\n        Done\n    }\n\n    /**\n     * @dev Mismatch between the parameters length for an operation call.\n     */\n    error TimelockInvalidOperationLength(uint256 targets, uint256 payloads, uint256 values);\n\n    /**\n     * @dev The schedule operation doesn't meet the minimum delay.\n     */\n    error TimelockInsufficientDelay(uint256 delay, uint256 minDelay);\n\n    /**\n     * @dev The current state of an operation is not as required.\n     * The `expectedStates` is a bitmap with the bits enabled for each OperationState enum position\n     * counting from right to left.\n     *\n     * See {_encodeStateBitmap}.\n     */\n    error TimelockUnexpectedOperationState(bytes32 operationId, bytes32 expectedStates);\n\n    /**\n     * @dev The predecessor to an operation not yet done.\n     */\n    error TimelockUnexecutedPredecessor(bytes32 predecessorId);\n\n    /**\n     * @dev The caller account is not authorized.\n     */\n    error TimelockUnauthorizedCaller(address caller);\n\n    /**\n     * @dev Emitted when a call is scheduled as part of operation `id`.\n     */\n    event CallScheduled(\n        bytes32 indexed id,\n        uint256 indexed index,\n        address target,\n        uint256 value,\n        bytes data,\n        bytes32 predecessor,\n        uint256 delay\n    );\n\n    /**\n     * @dev Emitted when a call is performed as part of operation `id`.\n     */\n    event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data);\n\n    /**\n     * @dev Emitted when new proposal is scheduled with non-zero salt.\n     */\n    event CallSalt(bytes32 indexed id, bytes32 salt);\n\n    /**\n     * @dev Emitted when operation `id` is cancelled.\n     */\n    event Cancelled(bytes32 indexed id);\n\n    /**\n     * @dev Emitted when the minimum delay for future operations is modified.\n     */\n    event MinDelayChange(uint256 oldDuration, uint256 newDuration);\n\n    /**\n     * @dev Initializes the contract with the following parameters:\n     *\n     * - `minDelay`: initial minimum delay in seconds for operations\n     * - `proposers`: accounts to be granted proposer and canceller roles\n     * - `executors`: accounts to be granted executor role\n     * - `admin`: optional account to be granted admin role; disable with zero address\n     *\n     * IMPORTANT: The optional admin can aid with initial configuration of roles after deployment\n     * without being subject to delay, but this role should be subsequently renounced in favor of\n     * administration through timelocked proposals. Previous versions of this contract would assign\n     * this admin to the deployer automatically and should be renounced as well.\n     */\n    constructor(uint256 minDelay, address[] memory proposers, address[] memory executors, address admin) {\n        // self administration\n        _grantRole(DEFAULT_ADMIN_ROLE, address(this));\n\n        // optional admin\n        if (admin != address(0)) {\n            _grantRole(DEFAULT_ADMIN_ROLE, admin);\n        }\n\n        // register proposers and cancellers\n        for (uint256 i = 0; i < proposers.length; ++i) {\n            _grantRole(PROPOSER_ROLE, proposers[i]);\n            _grantRole(CANCELLER_ROLE, proposers[i]);\n        }\n\n        // register executors\n        for (uint256 i = 0; i < executors.length; ++i) {\n            _grantRole(EXECUTOR_ROLE, executors[i]);\n        }\n\n        _minDelay = minDelay;\n        emit MinDelayChange(0, minDelay);\n    }\n\n    /**\n     * @dev Modifier to make a function callable only by a certain role. In\n     * addition to checking the sender's role, `address(0)` 's role is also\n     * considered. Granting a role to `address(0)` is equivalent to enabling\n     * this role for everyone.\n     */\n    modifier onlyRoleOrOpenRole(bytes32 role) {\n        if (!hasRole(role, address(0))) {\n            _checkRole(role, _msgSender());\n        }\n        _;\n    }\n\n    /**\n     * @dev Contract might receive/hold ETH as part of the maintenance process.\n     */\n    receive() external payable {}\n\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(\n        bytes4 interfaceId\n    ) public view virtual override(AccessControl, ERC1155Holder) returns (bool) {\n        return super.supportsInterface(interfaceId);\n    }\n\n    /**\n     * @dev Returns whether an id corresponds to a registered operation. This\n     * includes both Waiting, Ready, and Done operations.\n     */\n    function isOperation(bytes32 id) public view returns (bool) {\n        return getOperationState(id) != OperationState.Unset;\n    }\n\n    /**\n     * @dev Returns whether an operation is pending or not. Note that a \"pending\" operation may also be \"ready\".\n     */\n    function isOperationPending(bytes32 id) public view returns (bool) {\n        OperationState state = getOperationState(id);\n        return state == OperationState.Waiting || state == OperationState.Ready;\n    }\n\n    /**\n     * @dev Returns whether an operation is ready for execution. Note that a \"ready\" operation is also \"pending\".\n     */\n    function isOperationReady(bytes32 id) public view returns (bool) {\n        return getOperationState(id) == OperationState.Ready;\n    }\n\n    /**\n     * @dev Returns whether an operation is done or not.\n     */\n    function isOperationDone(bytes32 id) public view returns (bool) {\n        return getOperationState(id) == OperationState.Done;\n    }\n\n    /**\n     * @dev Returns the timestamp at which an operation becomes ready (0 for\n     * unset operations, 1 for done operations).\n     */\n    function getTimestamp(bytes32 id) public view virtual returns (uint256) {\n        return _timestamps[id];\n    }\n\n    /**\n     * @dev Returns operation state.\n     */\n    function getOperationState(bytes32 id) public view virtual returns (OperationState) {\n        uint256 timestamp = getTimestamp(id);\n        if (timestamp == 0) {\n            return OperationState.Unset;\n        } else if (timestamp == _DONE_TIMESTAMP) {\n            return OperationState.Done;\n        } else if (timestamp > block.timestamp) {\n            return OperationState.Waiting;\n        } else {\n            return OperationState.Ready;\n        }\n    }\n\n    /**\n     * @dev Returns the minimum delay in seconds for an operation to become valid.\n     *\n     * This value can be changed by executing an operation that calls `updateDelay`.\n     */\n    function getMinDelay() public view virtual returns (uint256) {\n        return _minDelay;\n    }\n\n    /**\n     * @dev Returns the identifier of an operation containing a single\n     * transaction.\n     */\n    function hashOperation(\n        address target,\n        uint256 value,\n        bytes calldata data,\n        bytes32 predecessor,\n        bytes32 salt\n    ) public pure virtual returns (bytes32) {\n        return keccak256(abi.encode(target, value, data, predecessor, salt));\n    }\n\n    /**\n     * @dev Returns the identifier of an operation containing a batch of\n     * transactions.\n     */\n    function hashOperationBatch(\n        address[] calldata targets,\n        uint256[] calldata values,\n        bytes[] calldata payloads,\n        bytes32 predecessor,\n        bytes32 salt\n    ) public pure virtual returns (bytes32) {\n        return keccak256(abi.encode(targets, values, payloads, predecessor, salt));\n    }\n\n    /**\n     * @dev Schedule an operation containing a single transaction.\n     *\n     * Emits {CallSalt} if salt is nonzero, and {CallScheduled}.\n     *\n     * Requirements:\n     *\n     * - the caller must have the 'proposer' role.\n     */\n    function schedule(\n        address target,\n        uint256 value,\n        bytes calldata data,\n        bytes32 predecessor,\n        bytes32 salt,\n        uint256 delay\n    ) public virtual onlyRole(PROPOSER_ROLE) {\n        bytes32 id = hashOperation(target, value, data, predecessor, salt);\n        _schedule(id, delay);\n        emit CallScheduled(id, 0, target, value, data, predecessor, delay);\n        if (salt != bytes32(0)) {\n            emit CallSalt(id, salt);\n        }\n    }\n\n    /**\n     * @dev Schedule an operation containing a batch of transactions.\n     *\n     * Emits {CallSalt} if salt is nonzero, and one {CallScheduled} event per transaction in the batch.\n     *\n     * Requirements:\n     *\n     * - the caller must have the 'proposer' role.\n     */\n    function scheduleBatch(\n        address[] calldata targets,\n        uint256[] calldata values,\n        bytes[] calldata payloads,\n        bytes32 predecessor,\n        bytes32 salt,\n        uint256 delay\n    ) public virtual onlyRole(PROPOSER_ROLE) {\n        if (targets.length != values.length || targets.length != payloads.length) {\n            revert TimelockInvalidOperationLength(targets.length, payloads.length, values.length);\n        }\n\n        bytes32 id = hashOperationBatch(targets, values, payloads, predecessor, salt);\n        _schedule(id, delay);\n        for (uint256 i = 0; i < targets.length; ++i) {\n            emit CallScheduled(id, i, targets[i], values[i], payloads[i], predecessor, delay);\n        }\n        if (salt != bytes32(0)) {\n            emit CallSalt(id, salt);\n        }\n    }\n\n    /**\n     * @dev Schedule an operation that is to become valid after a given delay.\n     */\n    function _schedule(bytes32 id, uint256 delay) private {\n        if (isOperation(id)) {\n            revert TimelockUnexpectedOperationState(id, _encodeStateBitmap(OperationState.Unset));\n        }\n        uint256 minDelay = getMinDelay();\n        if (delay < minDelay) {\n            revert TimelockInsufficientDelay(delay, minDelay);\n        }\n        _timestamps[id] = block.timestamp + delay;\n    }\n\n    /**\n     * @dev Cancel an operation.\n     *\n     * Requirements:\n     *\n     * - the caller must have the 'canceller' role.\n     */\n    function cancel(bytes32 id) public virtual onlyRole(CANCELLER_ROLE) {\n        if (!isOperationPending(id)) {\n            revert TimelockUnexpectedOperationState(\n                id,\n                _encodeStateBitmap(OperationState.Waiting) | _encodeStateBitmap(OperationState.Ready)\n            );\n        }\n        delete _timestamps[id];\n\n        emit Cancelled(id);\n    }\n\n    /**\n     * @dev Execute an (ready) operation containing a single transaction.\n     *\n     * Emits a {CallExecuted} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have the 'executor' role.\n     */\n    // This function can reenter, but it doesn't pose a risk because _afterCall checks that the proposal is pending,\n    // thus any modifications to the operation during reentrancy should be caught.\n    // slither-disable-next-line reentrancy-eth\n    function execute(\n        address target,\n        uint256 value,\n        bytes calldata payload,\n        bytes32 predecessor,\n        bytes32 salt\n    ) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE) {\n        bytes32 id = hashOperation(target, value, payload, predecessor, salt);\n\n        _beforeCall(id, predecessor);\n        _execute(target, value, payload);\n        emit CallExecuted(id, 0, target, value, payload);\n        _afterCall(id);\n    }\n\n    /**\n     * @dev Execute an (ready) operation containing a batch of transactions.\n     *\n     * Emits one {CallExecuted} event per transaction in the batch.\n     *\n     * Requirements:\n     *\n     * - the caller must have the 'executor' role.\n     */\n    // This function can reenter, but it doesn't pose a risk because _afterCall checks that the proposal is pending,\n    // thus any modifications to the operation during reentrancy should be caught.\n    // slither-disable-next-line reentrancy-eth\n    function executeBatch(\n        address[] calldata targets,\n        uint256[] calldata values,\n        bytes[] calldata payloads,\n        bytes32 predecessor,\n        bytes32 salt\n    ) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE) {\n        if (targets.length != values.length || targets.length != payloads.length) {\n            revert TimelockInvalidOperationLength(targets.length, payloads.length, values.length);\n        }\n\n        bytes32 id = hashOperationBatch(targets, values, payloads, predecessor, salt);\n\n        _beforeCall(id, predecessor);\n        for (uint256 i = 0; i < targets.length; ++i) {\n            address target = targets[i];\n            uint256 value = values[i];\n            bytes calldata payload = payloads[i];\n            _execute(target, value, payload);\n            emit CallExecuted(id, i, target, value, payload);\n        }\n        _afterCall(id);\n    }\n\n    /**\n     * @dev Execute an operation's call.\n     */\n    function _execute(address target, uint256 value, bytes calldata data) internal virtual {\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        Address.verifyCallResult(success, returndata);\n    }\n\n    /**\n     * @dev Checks before execution of an operation's calls.\n     */\n    function _beforeCall(bytes32 id, bytes32 predecessor) private view {\n        if (!isOperationReady(id)) {\n            revert TimelockUnexpectedOperationState(id, _encodeStateBitmap(OperationState.Ready));\n        }\n        if (predecessor != bytes32(0) && !isOperationDone(predecessor)) {\n            revert TimelockUnexecutedPredecessor(predecessor);\n        }\n    }\n\n    /**\n     * @dev Checks after execution of an operation's calls.\n     */\n    function _afterCall(bytes32 id) private {\n        if (!isOperationReady(id)) {\n            revert TimelockUnexpectedOperationState(id, _encodeStateBitmap(OperationState.Ready));\n        }\n        _timestamps[id] = _DONE_TIMESTAMP;\n    }\n\n    /**\n     * @dev Changes the minimum timelock duration for future operations.\n     *\n     * Emits a {MinDelayChange} event.\n     *\n     * Requirements:\n     *\n     * - the caller must be the timelock itself. This can only be achieved by scheduling and later executing\n     * an operation where the timelock is the target and the data is the ABI-encoded call to this function.\n     */\n    function updateDelay(uint256 newDelay) external virtual {\n        address sender = _msgSender();\n        if (sender != address(this)) {\n            revert TimelockUnauthorizedCaller(sender);\n        }\n        emit MinDelayChange(_minDelay, newDelay);\n        _minDelay = newDelay;\n    }\n\n    /**\n     * @dev Encodes a `OperationState` into a `bytes32` representation where each bit enabled corresponds to\n     * the underlying position in the `OperationState` enum. For example:\n     *\n     * 0x000...1000\n     *   ^^^^^^----- ...\n     *         ^---- Done\n     *          ^--- Ready\n     *           ^-- Waiting\n     *            ^- Unset\n     */\n    function _encodeStateBitmap(OperationState operationState) internal pure returns (bytes32) {\n        return bytes32(1 << uint8(operationState));\n    }\n}\n"},{"file_path":"contracts/core/lib/Decimals.sol","source_code":"// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.28;\n\nimport {Math} from \"@openzeppelin/contracts/utils/math/Math.sol\";\n\n/**\n * @title Decimals\n * @notice Converts a fixed-point amount between two decimal scales.\n * @custom:security-contact securityreport@firelight.finance\n */\nlibrary Decimals {\n    /// @notice Rescales `amount` from `fromDecimals` to `toDecimals`.\n    /// @dev Scaling up is exact (rounding is irrelevant); scaling down applies `rounding`.\n    /// @param amount Amount expressed with `fromDecimals` decimals.\n    /// @param fromDecimals Decimals the amount is currently expressed in.\n    /// @param toDecimals Target decimals.\n    /// @param rounding Rounding direction used when scaling down.\n    /// @return The amount expressed with `toDecimals` decimals.\n    function convert(\n        uint256 amount,\n        uint8 fromDecimals,\n        uint8 toDecimals,\n        Math.Rounding rounding\n    ) internal pure returns (uint256) {\n        if (fromDecimals == toDecimals) return amount;\n        if (fromDecimals < toDecimals) {\n            return amount * 10 ** (toDecimals - fromDecimals);\n        }\n        return Math.mulDiv(amount, 1, 10 ** (fromDecimals - toDecimals), rounding);\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/utils/Address.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.2.0) (utils/Address.sol)\n\npragma solidity ^0.8.20;\n\nimport {Errors} from \"./Errors.sol\";\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev There's no code at `target` (it is not a contract).\n     */\n    error AddressEmptyCode(address target);\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        if (address(this).balance < amount) {\n            revert Errors.InsufficientBalance(address(this).balance, amount);\n        }\n\n        (bool success, bytes memory returndata) = recipient.call{value: amount}(\"\");\n        if (!success) {\n            _revert(returndata);\n        }\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain `call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason or custom error, it is bubbled\n     * up by this function (like regular Solidity function calls). However, if\n     * the call reverted with no returned reason, this function reverts with a\n     * {Errors.FailedCall} error.\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n        if (address(this).balance < value) {\n            revert Errors.InsufficientBalance(address(this).balance, value);\n        }\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, success, returndata);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, success, returndata);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target\n     * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case\n     * of an unsuccessful call.\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata\n    ) internal view returns (bytes memory) {\n        if (!success) {\n            _revert(returndata);\n        } else {\n            // only check if target is a contract if the call was successful and the return data is empty\n            // otherwise we already know that it was a contract\n            if (returndata.length == 0 && target.code.length == 0) {\n                revert AddressEmptyCode(target);\n            }\n            return returndata;\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the\n     * revert reason or with a default {Errors.FailedCall} error.\n     */\n    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {\n        if (!success) {\n            _revert(returndata);\n        } else {\n            return returndata;\n        }\n    }\n\n    /**\n     * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.\n     */\n    function _revert(bytes memory returndata) private pure {\n        // Look for revert reason and bubble it up if present\n        if (returndata.length > 0) {\n            // The easiest way to bubble the revert reason is using memory via assembly\n            assembly (\"memory-safe\") {\n                let returndata_size := mload(returndata)\n                revert(add(32, returndata), returndata_size)\n            }\n        } else {\n            revert Errors.FailedCall();\n        }\n    }\n}\n"},{"file_path":"@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)\n\npragma solidity ^0.8.20;\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract ContextUpgradeable is Initializable {\n    function __Context_init() internal onlyInitializing {\n    }\n\n    function __Context_init_unchained() internal onlyInitializing {\n    }\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n\n    function _contextSuffixLength() internal view virtual returns (uint256) {\n        return 0;\n    }\n}\n"},{"file_path":"contracts/core/FirelightVaultStorage.sol","source_code":"// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.28;\n\nimport {Checkpoints} from \"./lib/Checkpoints.sol\";\n\n/**\n * @title FirelightVaultStorage\n * @notice Storage layout for FirelightVault.\n * @custom:security-contact securityreport@firelight.finance\n */\nabstract contract FirelightVaultStorage {\n    /// @notice Role for updating deposit limits.\n    bytes32 public constant DEPOSIT_LIMIT_UPDATE_ROLE = keccak256(\"DEPOSIT_LIMIT_UPDATE_ROLE\");\n\n    /// @notice Role for rescuing funds from blocklisted addresses.\n    bytes32 public constant RESCUER_ROLE = keccak256(\"RESCUER_ROLE\");\n\n    /// @notice Role for managing the blocklist.\n    bytes32 public constant BLOCKLIST_ROLE = keccak256(\"BLOCKLIST_ROLE\");\n\n    /// @notice Role for pausing and unpausing contract operations.\n    bytes32 public constant PAUSE_ROLE = keccak256(\"PAUSE_ROLE\");\n\n    /// @notice Role for updating the period configurations.\n    bytes32 public constant PERIOD_CONFIGURATION_UPDATE_ROLE = keccak256(\"PERIOD_CONFIGURATION_UPDATE_ROLE\");\n\n    /// @notice Role for managing the payout allowlist.\n    bytes32 public constant PAYOUT_ALLOWLIST_ROLE = keccak256(\"PAYOUT_ALLOWLIST_ROLE\");\n\n    /// @notice Role for executing payout actions.\n    bytes32 public constant PAYOUT_ROLE = keccak256(\"PAYOUT_ROLE\");\n\n    /// @notice Role allowed to update active incident flags used by deposit checks.\n    bytes32 public constant INCIDENT_ROLE = keccak256(\"INCIDENT_ROLE\");\n\n    /// @notice Role allowed to record total-assets checkpoints outside deposit/withdraw flows.\n    bytes32 public constant CHECKPOINT_ROLE = keccak256(\"CHECKPOINT_ROLE\");\n\n    /// @notice Minimum period duration in seconds.\n    uint48 public constant SMALLEST_PERIOD_DURATION = 1 days;\n\n    /// @notice Maximum period duration in seconds.\n    uint48 public constant MAX_PERIOD_DURATION = 90 days;\n\n    /// @notice The maximum total amount of assets that can be deposited into the vault.\n    uint256 public depositLimit;\n\n    /// @notice The current version of the contract.\n    uint256 public contractVersion;\n\n    /// @notice The total amount of assets pending withdrawal across all periods.\n    uint256 public pendingWithdrawAssets;\n\n    struct PeriodConfiguration {\n        uint48 epoch;\n        uint48 duration;\n        uint256 startingPeriod;\n    }\n\n    // solhint-disable-next-line max-line-length\n    /// @notice Array of period configurations consisting of a starting timestamp (epoch), starting period number (startingPeriod) and period duration (duration).\n    PeriodConfiguration[] public periodConfigurations;\n\n    /// @notice Total shares allocated for withdrawals in a given period.\n    mapping(uint256 period => uint256 shares) public withdrawShares;\n\n    /// @notice Total assets allocated for withdrawals in a given period.\n    mapping(uint256 period => uint256 assets) public withdrawAssets;\n\n    /// @notice Total shares allocated for withdrawals in a given period and a given account.\n    mapping(uint256 period => mapping(address account => uint256 shares)) public withdrawSharesOf;\n\n    /// @notice Indicates whether an account has claimed their withdrawal for a given period.\n    mapping(uint256 period => mapping(address account => bool value)) public isWithdrawClaimed;\n\n    /// @notice Indicates whether an account is blocklisted.\n    mapping(address account => bool) public isBlocklisted;\n\n    /// @notice Checkpoints for assets and shares.\n    mapping(address account => Checkpoints.Trace256 shares) internal _traceBalanceOf;\n    Checkpoints.Trace256 internal _traceTotalSupply;\n    Checkpoints.Trace256 internal _traceTotalAssets;\n\n    /// @notice Indicates whether an account is payout allowlisted.\n    mapping(address account => bool) public isPayoutAllowlisted;\n\n    /// @notice Indicates whether deposits are blocked for a period due to an active incident.\n    mapping(uint256 period => bool) public hasActiveIncident;\n\n    uint256[48] private __gap;\n}\n"},{"file_path":"@openzeppelin/contracts/token/ERC721/utils/ERC721Utils.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/utils/ERC721Utils.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC721Receiver} from \"../IERC721Receiver.sol\";\nimport {IERC721Errors} from \"../../../interfaces/draft-IERC6093.sol\";\n\n/**\n * @dev Library that provide common ERC-721 utility functions.\n *\n * See https://eips.ethereum.org/EIPS/eip-721[ERC-721].\n *\n * _Available since v5.1._\n */\nlibrary ERC721Utils {\n    /**\n     * @dev Performs an acceptance check for the provided `operator` by calling {IERC721-onERC721Received}\n     * on the `to` address. The `operator` is generally the address that initiated the token transfer (i.e. `msg.sender`).\n     *\n     * The acceptance call is not executed and treated as a no-op if the target address doesn't contain code (i.e. an EOA).\n     * Otherwise, the recipient must implement {IERC721Receiver-onERC721Received} and return the acceptance magic value to accept\n     * the transfer.\n     */\n    function checkOnERC721Received(\n        address operator,\n        address from,\n        address to,\n        uint256 tokenId,\n        bytes memory data\n    ) internal {\n        if (to.code.length > 0) {\n            try IERC721Receiver(to).onERC721Received(operator, from, tokenId, data) returns (bytes4 retval) {\n                if (retval != IERC721Receiver.onERC721Received.selector) {\n                    // Token rejected\n                    revert IERC721Errors.ERC721InvalidReceiver(to);\n                }\n            } catch (bytes memory reason) {\n                if (reason.length == 0) {\n                    // non-IERC721Receiver implementer\n                    revert IERC721Errors.ERC721InvalidReceiver(to);\n                } else {\n                    assembly (\"memory-safe\") {\n                        revert(add(32, reason), mload(reason))\n                    }\n                }\n            }\n        }\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/access/IAccessControl.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev External interface of AccessControl declared to support ERC-165 detection.\n */\ninterface IAccessControl {\n    /**\n     * @dev The `account` is missing a role.\n     */\n    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);\n\n    /**\n     * @dev The caller of a function is not the expected one.\n     *\n     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.\n     */\n    error AccessControlBadConfirmation();\n\n    /**\n     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n     *\n     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n     * {RoleAdminChanged} not being emitted signaling this.\n     */\n    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n    /**\n     * @dev Emitted when `account` is granted `role`.\n     *\n     * `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).\n     * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.\n     */\n    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Emitted when `account` is revoked `role`.\n     *\n     * `sender` is the account that originated the contract call:\n     *   - if using `revokeRole`, it is the admin role bearer\n     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)\n     */\n    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) external view returns (bool);\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function grantRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function revokeRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function's\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been granted `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `callerConfirmation`.\n     */\n    function renounceRole(bytes32 role, address callerConfirmation) external;\n}\n"},{"file_path":"@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC165} from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\nimport {Initializable} from \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n */\nabstract contract ERC165Upgradeable is Initializable, IERC165 {\n    function __ERC165_init() internal onlyInitializing {\n    }\n\n    function __ERC165_init_unchained() internal onlyInitializing {\n    }\n    /// @inheritdoc IERC165\n    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {\n        return interfaceId == type(IERC165).interfaceId;\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721Receiver.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @title ERC-721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC-721 asset contracts.\n */\ninterface IERC721Receiver {\n    /**\n     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n     * by `operator` from `from`, this function is called.\n     *\n     * It must return its Solidity selector to confirm the token transfer.\n     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be\n     * reverted.\n     *\n     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.\n     */\n    function onERC721Received(\n        address operator,\n        address from,\n        uint256 tokenId,\n        bytes calldata data\n    ) external returns (bytes4);\n}\n"},{"file_path":"@openzeppelin/contracts/token/ERC721/IERC721.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC165} from \"../../utils/introspection/IERC165.sol\";\n\n/**\n * @dev Required interface of an ERC-721 compliant contract.\n */\ninterface IERC721 is IERC165 {\n    /**\n     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n    /**\n     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n     */\n    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n    /**\n     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n     */\n    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n    /**\n     * @dev Returns the number of tokens in ``owner``'s account.\n     */\n    function balanceOf(address owner) external view returns (uint256 balance);\n\n    /**\n     * @dev Returns the owner of the `tokenId` token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function ownerOf(uint256 tokenId) external view returns (address owner);\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must exist and be owned by `from`.\n     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon\n     *   a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n     * are aware of the ERC-721 protocol to prevent tokens from being forever locked.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must exist and be owned by `from`.\n     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or\n     *   {setApprovalForAll}.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon\n     *   a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId) external;\n\n    /**\n     * @dev Transfers `tokenId` token from `from` to `to`.\n     *\n     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721\n     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must\n     * understand this adds an external call which potentially creates a reentrancy vulnerability.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must be owned by `from`.\n     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 tokenId) external;\n\n    /**\n     * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n     * The approval is cleared when the token is transferred.\n     *\n     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n     *\n     * Requirements:\n     *\n     * - The caller must own the token or be an approved operator.\n     * - `tokenId` must exist.\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address to, uint256 tokenId) external;\n\n    /**\n     * @dev Approve or remove `operator` as an operator for the caller.\n     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n     *\n     * Requirements:\n     *\n     * - The `operator` cannot be the address zero.\n     *\n     * Emits an {ApprovalForAll} event.\n     */\n    function setApprovalForAll(address operator, bool approved) external;\n\n    /**\n     * @dev Returns the account approved for `tokenId` token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function getApproved(uint256 tokenId) external view returns (address operator);\n\n    /**\n     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n     *\n     * See {setApprovalForAll}\n     */\n    function isApprovedForAll(address owner, address operator) external view returns (bool);\n}\n"},{"file_path":"contracts/test/MockFlareRegistry.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.28;\n\n/**\n * @notice Test-only mock of `IFlareContractRegistry`. Resolves only the\n *         `\"FtsoV2\"` name; everything else returns `address(0)`. Designed\n *         to be deployed once, then have its runtime bytecode copied via\n *         `hardhat_setCode` to the canonical registry address.\n */\ncontract MockFlareRegistry {\n    address public ftsoV2;\n\n    function setFtsoV2(address newFtso) external {\n        ftsoV2 = newFtso;\n    }\n\n    function getContractAddressByName(string calldata _name) external view returns (address) {\n        if (keccak256(bytes(_name)) == keccak256(bytes(\"FtsoV2\"))) {\n            return ftsoV2;\n        }\n        return address(0);\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (access/extensions/IAccessControlDefaultAdminRules.sol)\n\npragma solidity ^0.8.20;\n\nimport {IAccessControl} from \"../IAccessControl.sol\";\n\n/**\n * @dev External interface of AccessControlDefaultAdminRules declared to support ERC-165 detection.\n */\ninterface IAccessControlDefaultAdminRules is IAccessControl {\n    /**\n     * @dev The new default admin is not a valid default admin.\n     */\n    error AccessControlInvalidDefaultAdmin(address defaultAdmin);\n\n    /**\n     * @dev At least one of the following rules was violated:\n     *\n     * - The `DEFAULT_ADMIN_ROLE` must only be managed by itself.\n     * - The `DEFAULT_ADMIN_ROLE` must only be held by one account at the time.\n     * - Any `DEFAULT_ADMIN_ROLE` transfer must be in two delayed steps.\n     */\n    error AccessControlEnforcedDefaultAdminRules();\n\n    /**\n     * @dev The delay for transferring the default admin delay is enforced and\n     * the operation must wait until `schedule`.\n     *\n     * NOTE: `schedule` can be 0 indicating there's no transfer scheduled.\n     */\n    error AccessControlEnforcedDefaultAdminDelay(uint48 schedule);\n\n    /**\n     * @dev Emitted when a {defaultAdmin} transfer is started, setting `newAdmin` as the next\n     * address to become the {defaultAdmin} by calling {acceptDefaultAdminTransfer} only after `acceptSchedule`\n     * passes.\n     */\n    event DefaultAdminTransferScheduled(address indexed newAdmin, uint48 acceptSchedule);\n\n    /**\n     * @dev Emitted when a {pendingDefaultAdmin} is reset if it was never accepted, regardless of its schedule.\n     */\n    event DefaultAdminTransferCanceled();\n\n    /**\n     * @dev Emitted when a {defaultAdminDelay} change is started, setting `newDelay` as the next\n     * delay to be applied between default admin transfer after `effectSchedule` has passed.\n     */\n    event DefaultAdminDelayChangeScheduled(uint48 newDelay, uint48 effectSchedule);\n\n    /**\n     * @dev Emitted when a {pendingDefaultAdminDelay} is reset if its schedule didn't pass.\n     */\n    event DefaultAdminDelayChangeCanceled();\n\n    /**\n     * @dev Returns the address of the current `DEFAULT_ADMIN_ROLE` holder.\n     */\n    function defaultAdmin() external view returns (address);\n\n    /**\n     * @dev Returns a tuple of a `newAdmin` and an accept schedule.\n     *\n     * After the `schedule` passes, the `newAdmin` will be able to accept the {defaultAdmin} role\n     * by calling {acceptDefaultAdminTransfer}, completing the role transfer.\n     *\n     * A zero value only in `acceptSchedule` indicates no pending admin transfer.\n     *\n     * NOTE: A zero address `newAdmin` means that {defaultAdmin} is being renounced.\n     */\n    function pendingDefaultAdmin() external view returns (address newAdmin, uint48 acceptSchedule);\n\n    /**\n     * @dev Returns the delay required to schedule the acceptance of a {defaultAdmin} transfer started.\n     *\n     * This delay will be added to the current timestamp when calling {beginDefaultAdminTransfer} to set\n     * the acceptance schedule.\n     *\n     * NOTE: If a delay change has been scheduled, it will take effect as soon as the schedule passes, making this\n     * function returns the new delay. See {changeDefaultAdminDelay}.\n     */\n    function defaultAdminDelay() external view returns (uint48);\n\n    /**\n     * @dev Returns a tuple of `newDelay` and an effect schedule.\n     *\n     * After the `schedule` passes, the `newDelay` will get into effect immediately for every\n     * new {defaultAdmin} transfer started with {beginDefaultAdminTransfer}.\n     *\n     * A zero value only in `effectSchedule` indicates no pending delay change.\n     *\n     * NOTE: A zero value only for `newDelay` means that the next {defaultAdminDelay}\n     * will be zero after the effect schedule.\n     */\n    function pendingDefaultAdminDelay() external view returns (uint48 newDelay, uint48 effectSchedule);\n\n    /**\n     * @dev Starts a {defaultAdmin} transfer by setting a {pendingDefaultAdmin} scheduled for acceptance\n     * after the current timestamp plus a {defaultAdminDelay}.\n     *\n     * Requirements:\n     *\n     * - Only can be called by the current {defaultAdmin}.\n     *\n     * Emits a DefaultAdminRoleChangeStarted event.\n     */\n    function beginDefaultAdminTransfer(address newAdmin) external;\n\n    /**\n     * @dev Cancels a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}.\n     *\n     * A {pendingDefaultAdmin} not yet accepted can also be cancelled with this function.\n     *\n     * Requirements:\n     *\n     * - Only can be called by the current {defaultAdmin}.\n     *\n     * May emit a DefaultAdminTransferCanceled event.\n     */\n    function cancelDefaultAdminTransfer() external;\n\n    /**\n     * @dev Completes a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}.\n     *\n     * After calling the function:\n     *\n     * - `DEFAULT_ADMIN_ROLE` should be granted to the caller.\n     * - `DEFAULT_ADMIN_ROLE` should be revoked from the previous holder.\n     * - {pendingDefaultAdmin} should be reset to zero values.\n     *\n     * Requirements:\n     *\n     * - Only can be called by the {pendingDefaultAdmin}'s `newAdmin`.\n     * - The {pendingDefaultAdmin}'s `acceptSchedule` should've passed.\n     */\n    function acceptDefaultAdminTransfer() external;\n\n    /**\n     * @dev Initiates a {defaultAdminDelay} update by setting a {pendingDefaultAdminDelay} scheduled for getting\n     * into effect after the current timestamp plus a {defaultAdminDelay}.\n     *\n     * This function guarantees that any call to {beginDefaultAdminTransfer} done between the timestamp this\n     * method is called and the {pendingDefaultAdminDelay} effect schedule will use the current {defaultAdminDelay}\n     * set before calling.\n     *\n     * The {pendingDefaultAdminDelay}'s effect schedule is defined in a way that waiting until the schedule and then\n     * calling {beginDefaultAdminTransfer} with the new delay will take at least the same as another {defaultAdmin}\n     * complete transfer (including acceptance).\n     *\n     * The schedule is designed for two scenarios:\n     *\n     * - When the delay is changed for a larger one the schedule is `block.timestamp + newDelay` capped by\n     * {defaultAdminDelayIncreaseWait}.\n     * - When the delay is changed for a shorter one, the schedule is `block.timestamp + (current delay - new delay)`.\n     *\n     * A {pendingDefaultAdminDelay} that never got into effect will be canceled in favor of a new scheduled change.\n     *\n     * Requirements:\n     *\n     * - Only can be called by the current {defaultAdmin}.\n     *\n     * Emits a DefaultAdminDelayChangeScheduled event and may emit a DefaultAdminDelayChangeCanceled event.\n     */\n    function changeDefaultAdminDelay(uint48 newDelay) external;\n\n    /**\n     * @dev Cancels a scheduled {defaultAdminDelay} change.\n     *\n     * Requirements:\n     *\n     * - Only can be called by the current {defaultAdmin}.\n     *\n     * May emit a DefaultAdminDelayChangeCanceled event.\n     */\n    function rollbackDefaultAdminDelay() external;\n\n    /**\n     * @dev Maximum time in seconds for an increase to {defaultAdminDelay} (that is scheduled using {changeDefaultAdminDelay})\n     * to take effect. Default to 5 days.\n     *\n     * When the {defaultAdminDelay} is scheduled to be increased, it goes into effect after the new delay has passed with\n     * the purpose of giving enough time for reverting any accidental change (i.e. using milliseconds instead of seconds)\n     * that may lock the contract. However, to avoid excessive schedules, the wait is capped by this function and it can\n     * be overrode for a custom {defaultAdminDelay} increase scheduling.\n     *\n     * IMPORTANT: Make sure to add a reasonable amount of time while overriding this value, otherwise,\n     * there's a risk of setting a high new delay that goes into effect almost immediately without the\n     * possibility of human intervention in the case of an input error (eg. set milliseconds instead of seconds).\n     */\n    function defaultAdminDelayIncreaseWait() external view returns (uint48);\n}\n"},{"file_path":"@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/IERC1155Receiver.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC165} from \"../../utils/introspection/IERC165.sol\";\n\n/**\n * @dev Interface that must be implemented by smart contracts in order to receive\n * ERC-1155 token transfers.\n */\ninterface IERC1155Receiver is IERC165 {\n    /**\n     * @dev Handles the receipt of a single ERC-1155 token type. This function is\n     * called at the end of a `safeTransferFrom` after the balance has been updated.\n     *\n     * NOTE: To accept the transfer, this must return\n     * `bytes4(keccak256(\"onERC1155Received(address,address,uint256,uint256,bytes)\"))`\n     * (i.e. 0xf23a6e61, or its own function selector).\n     *\n     * @param operator The address which initiated the transfer (i.e. msg.sender)\n     * @param from The address which previously owned the token\n     * @param id The ID of the token being transferred\n     * @param value The amount of tokens being transferred\n     * @param data Additional data with no specified format\n     * @return `bytes4(keccak256(\"onERC1155Received(address,address,uint256,uint256,bytes)\"))` if transfer is allowed\n     */\n    function onERC1155Received(\n        address operator,\n        address from,\n        uint256 id,\n        uint256 value,\n        bytes calldata data\n    ) external returns (bytes4);\n\n    /**\n     * @dev Handles the receipt of a multiple ERC-1155 token types. This function\n     * is called at the end of a `safeBatchTransferFrom` after the balances have\n     * been updated.\n     *\n     * NOTE: To accept the transfer(s), this must return\n     * `bytes4(keccak256(\"onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)\"))`\n     * (i.e. 0xbc197c81, or its own function selector).\n     *\n     * @param operator The address which initiated the batch transfer (i.e. msg.sender)\n     * @param from The address which previously owned the token\n     * @param ids An array containing ids of each token being transferred (order and length must match values array)\n     * @param values An array containing amounts of each token being transferred (order and length must match ids array)\n     * @param data Additional data with no specified format\n     * @return `bytes4(keccak256(\"onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)\"))` if transfer is allowed\n     */\n    function onERC1155BatchReceived(\n        address operator,\n        address from,\n        uint256[] calldata ids,\n        uint256[] calldata values,\n        bytes calldata data\n    ) external returns (bytes4);\n}\n"},{"file_path":"@openzeppelin/contracts/utils/math/SafeCast.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)\n// This file was procedurally generated from scripts/generate/templates/SafeCast.js.\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeCast {\n    /**\n     * @dev Value doesn't fit in an uint of `bits` size.\n     */\n    error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);\n\n    /**\n     * @dev An int value doesn't fit in an uint of `bits` size.\n     */\n    error SafeCastOverflowedIntToUint(int256 value);\n\n    /**\n     * @dev Value doesn't fit in an int of `bits` size.\n     */\n    error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);\n\n    /**\n     * @dev An uint value doesn't fit in an int of `bits` size.\n     */\n    error SafeCastOverflowedUintToInt(uint256 value);\n\n    /**\n     * @dev Returns the downcasted uint248 from uint256, reverting on\n     * overflow (when the input is greater than largest uint248).\n     *\n     * Counterpart to Solidity's `uint248` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 248 bits\n     */\n    function toUint248(uint256 value) internal pure returns (uint248) {\n        if (value > type(uint248).max) {\n            revert SafeCastOverflowedUintDowncast(248, value);\n        }\n        return uint248(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint240 from uint256, reverting on\n     * overflow (when the input is greater than largest uint240).\n     *\n     * Counterpart to Solidity's `uint240` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 240 bits\n     */\n    function toUint240(uint256 value) internal pure returns (uint240) {\n        if (value > type(uint240).max) {\n            revert SafeCastOverflowedUintDowncast(240, value);\n        }\n        return uint240(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint232 from uint256, reverting on\n     * overflow (when the input is greater than largest uint232).\n     *\n     * Counterpart to Solidity's `uint232` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 232 bits\n     */\n    function toUint232(uint256 value) internal pure returns (uint232) {\n        if (value > type(uint232).max) {\n            revert SafeCastOverflowedUintDowncast(232, value);\n        }\n        return uint232(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint224 from uint256, reverting on\n     * overflow (when the input is greater than largest uint224).\n     *\n     * Counterpart to Solidity's `uint224` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 224 bits\n     */\n    function toUint224(uint256 value) internal pure returns (uint224) {\n        if (value > type(uint224).max) {\n            revert SafeCastOverflowedUintDowncast(224, value);\n        }\n        return uint224(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint216 from uint256, reverting on\n     * overflow (when the input is greater than largest uint216).\n     *\n     * Counterpart to Solidity's `uint216` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 216 bits\n     */\n    function toUint216(uint256 value) internal pure returns (uint216) {\n        if (value > type(uint216).max) {\n            revert SafeCastOverflowedUintDowncast(216, value);\n        }\n        return uint216(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint208 from uint256, reverting on\n     * overflow (when the input is greater than largest uint208).\n     *\n     * Counterpart to Solidity's `uint208` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 208 bits\n     */\n    function toUint208(uint256 value) internal pure returns (uint208) {\n        if (value > type(uint208).max) {\n            revert SafeCastOverflowedUintDowncast(208, value);\n        }\n        return uint208(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint200 from uint256, reverting on\n     * overflow (when the input is greater than largest uint200).\n     *\n     * Counterpart to Solidity's `uint200` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 200 bits\n     */\n    function toUint200(uint256 value) internal pure returns (uint200) {\n        if (value > type(uint200).max) {\n            revert SafeCastOverflowedUintDowncast(200, value);\n        }\n        return uint200(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint192 from uint256, reverting on\n     * overflow (when the input is greater than largest uint192).\n     *\n     * Counterpart to Solidity's `uint192` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 192 bits\n     */\n    function toUint192(uint256 value) internal pure returns (uint192) {\n        if (value > type(uint192).max) {\n            revert SafeCastOverflowedUintDowncast(192, value);\n        }\n        return uint192(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint184 from uint256, reverting on\n     * overflow (when the input is greater than largest uint184).\n     *\n     * Counterpart to Solidity's `uint184` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 184 bits\n     */\n    function toUint184(uint256 value) internal pure returns (uint184) {\n        if (value > type(uint184).max) {\n            revert SafeCastOverflowedUintDowncast(184, value);\n        }\n        return uint184(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint176 from uint256, reverting on\n     * overflow (when the input is greater than largest uint176).\n     *\n     * Counterpart to Solidity's `uint176` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 176 bits\n     */\n    function toUint176(uint256 value) internal pure returns (uint176) {\n        if (value > type(uint176).max) {\n            revert SafeCastOverflowedUintDowncast(176, value);\n        }\n        return uint176(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint168 from uint256, reverting on\n     * overflow (when the input is greater than largest uint168).\n     *\n     * Counterpart to Solidity's `uint168` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 168 bits\n     */\n    function toUint168(uint256 value) internal pure returns (uint168) {\n        if (value > type(uint168).max) {\n            revert SafeCastOverflowedUintDowncast(168, value);\n        }\n        return uint168(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint160 from uint256, reverting on\n     * overflow (when the input is greater than largest uint160).\n     *\n     * Counterpart to Solidity's `uint160` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 160 bits\n     */\n    function toUint160(uint256 value) internal pure returns (uint160) {\n        if (value > type(uint160).max) {\n            revert SafeCastOverflowedUintDowncast(160, value);\n        }\n        return uint160(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint152 from uint256, reverting on\n     * overflow (when the input is greater than largest uint152).\n     *\n     * Counterpart to Solidity's `uint152` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 152 bits\n     */\n    function toUint152(uint256 value) internal pure returns (uint152) {\n        if (value > type(uint152).max) {\n            revert SafeCastOverflowedUintDowncast(152, value);\n        }\n        return uint152(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint144 from uint256, reverting on\n     * overflow (when the input is greater than largest uint144).\n     *\n     * Counterpart to Solidity's `uint144` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 144 bits\n     */\n    function toUint144(uint256 value) internal pure returns (uint144) {\n        if (value > type(uint144).max) {\n            revert SafeCastOverflowedUintDowncast(144, value);\n        }\n        return uint144(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint136 from uint256, reverting on\n     * overflow (when the input is greater than largest uint136).\n     *\n     * Counterpart to Solidity's `uint136` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 136 bits\n     */\n    function toUint136(uint256 value) internal pure returns (uint136) {\n        if (value > type(uint136).max) {\n            revert SafeCastOverflowedUintDowncast(136, value);\n        }\n        return uint136(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint128 from uint256, reverting on\n     * overflow (when the input is greater than largest uint128).\n     *\n     * Counterpart to Solidity's `uint128` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 128 bits\n     */\n    function toUint128(uint256 value) internal pure returns (uint128) {\n        if (value > type(uint128).max) {\n            revert SafeCastOverflowedUintDowncast(128, value);\n        }\n        return uint128(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint120 from uint256, reverting on\n     * overflow (when the input is greater than largest uint120).\n     *\n     * Counterpart to Solidity's `uint120` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 120 bits\n     */\n    function toUint120(uint256 value) internal pure returns (uint120) {\n        if (value > type(uint120).max) {\n            revert SafeCastOverflowedUintDowncast(120, value);\n        }\n        return uint120(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint112 from uint256, reverting on\n     * overflow (when the input is greater than largest uint112).\n     *\n     * Counterpart to Solidity's `uint112` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 112 bits\n     */\n    function toUint112(uint256 value) internal pure returns (uint112) {\n        if (value > type(uint112).max) {\n            revert SafeCastOverflowedUintDowncast(112, value);\n        }\n        return uint112(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint104 from uint256, reverting on\n     * overflow (when the input is greater than largest uint104).\n     *\n     * Counterpart to Solidity's `uint104` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 104 bits\n     */\n    function toUint104(uint256 value) internal pure returns (uint104) {\n        if (value > type(uint104).max) {\n            revert SafeCastOverflowedUintDowncast(104, value);\n        }\n        return uint104(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint96 from uint256, reverting on\n     * overflow (when the input is greater than largest uint96).\n     *\n     * Counterpart to Solidity's `uint96` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 96 bits\n     */\n    function toUint96(uint256 value) internal pure returns (uint96) {\n        if (value > type(uint96).max) {\n            revert SafeCastOverflowedUintDowncast(96, value);\n        }\n        return uint96(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint88 from uint256, reverting on\n     * overflow (when the input is greater than largest uint88).\n     *\n     * Counterpart to Solidity's `uint88` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 88 bits\n     */\n    function toUint88(uint256 value) internal pure returns (uint88) {\n        if (value > type(uint88).max) {\n            revert SafeCastOverflowedUintDowncast(88, value);\n        }\n        return uint88(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint80 from uint256, reverting on\n     * overflow (when the input is greater than largest uint80).\n     *\n     * Counterpart to Solidity's `uint80` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 80 bits\n     */\n    function toUint80(uint256 value) internal pure returns (uint80) {\n        if (value > type(uint80).max) {\n            revert SafeCastOverflowedUintDowncast(80, value);\n        }\n        return uint80(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint72 from uint256, reverting on\n     * overflow (when the input is greater than largest uint72).\n     *\n     * Counterpart to Solidity's `uint72` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 72 bits\n     */\n    function toUint72(uint256 value) internal pure returns (uint72) {\n        if (value > type(uint72).max) {\n            revert SafeCastOverflowedUintDowncast(72, value);\n        }\n        return uint72(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint64 from uint256, reverting on\n     * overflow (when the input is greater than largest uint64).\n     *\n     * Counterpart to Solidity's `uint64` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 64 bits\n     */\n    function toUint64(uint256 value) internal pure returns (uint64) {\n        if (value > type(uint64).max) {\n            revert SafeCastOverflowedUintDowncast(64, value);\n        }\n        return uint64(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint56 from uint256, reverting on\n     * overflow (when the input is greater than largest uint56).\n     *\n     * Counterpart to Solidity's `uint56` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 56 bits\n     */\n    function toUint56(uint256 value) internal pure returns (uint56) {\n        if (value > type(uint56).max) {\n            revert SafeCastOverflowedUintDowncast(56, value);\n        }\n        return uint56(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint48 from uint256, reverting on\n     * overflow (when the input is greater than largest uint48).\n     *\n     * Counterpart to Solidity's `uint48` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 48 bits\n     */\n    function toUint48(uint256 value) internal pure returns (uint48) {\n        if (value > type(uint48).max) {\n            revert SafeCastOverflowedUintDowncast(48, value);\n        }\n        return uint48(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint40 from uint256, reverting on\n     * overflow (when the input is greater than largest uint40).\n     *\n     * Counterpart to Solidity's `uint40` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 40 bits\n     */\n    function toUint40(uint256 value) internal pure returns (uint40) {\n        if (value > type(uint40).max) {\n            revert SafeCastOverflowedUintDowncast(40, value);\n        }\n        return uint40(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint32 from uint256, reverting on\n     * overflow (when the input is greater than largest uint32).\n     *\n     * Counterpart to Solidity's `uint32` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 32 bits\n     */\n    function toUint32(uint256 value) internal pure returns (uint32) {\n        if (value > type(uint32).max) {\n            revert SafeCastOverflowedUintDowncast(32, value);\n        }\n        return uint32(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint24 from uint256, reverting on\n     * overflow (when the input is greater than largest uint24).\n     *\n     * Counterpart to Solidity's `uint24` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 24 bits\n     */\n    function toUint24(uint256 value) internal pure returns (uint24) {\n        if (value > type(uint24).max) {\n            revert SafeCastOverflowedUintDowncast(24, value);\n        }\n        return uint24(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint16 from uint256, reverting on\n     * overflow (when the input is greater than largest uint16).\n     *\n     * Counterpart to Solidity's `uint16` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 16 bits\n     */\n    function toUint16(uint256 value) internal pure returns (uint16) {\n        if (value > type(uint16).max) {\n            revert SafeCastOverflowedUintDowncast(16, value);\n        }\n        return uint16(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint8 from uint256, reverting on\n     * overflow (when the input is greater than largest uint8).\n     *\n     * Counterpart to Solidity's `uint8` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 8 bits\n     */\n    function toUint8(uint256 value) internal pure returns (uint8) {\n        if (value > type(uint8).max) {\n            revert SafeCastOverflowedUintDowncast(8, value);\n        }\n        return uint8(value);\n    }\n\n    /**\n     * @dev Converts a signed int256 into an unsigned uint256.\n     *\n     * Requirements:\n     *\n     * - input must be greater than or equal to 0.\n     */\n    function toUint256(int256 value) internal pure returns (uint256) {\n        if (value < 0) {\n            revert SafeCastOverflowedIntToUint(value);\n        }\n        return uint256(value);\n    }\n\n    /**\n     * @dev Returns the downcasted int248 from int256, reverting on\n     * overflow (when the input is less than smallest int248 or\n     * greater than largest int248).\n     *\n     * Counterpart to Solidity's `int248` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 248 bits\n     */\n    function toInt248(int256 value) internal pure returns (int248 downcasted) {\n        downcasted = int248(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(248, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int240 from int256, reverting on\n     * overflow (when the input is less than smallest int240 or\n     * greater than largest int240).\n     *\n     * Counterpart to Solidity's `int240` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 240 bits\n     */\n    function toInt240(int256 value) internal pure returns (int240 downcasted) {\n        downcasted = int240(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(240, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int232 from int256, reverting on\n     * overflow (when the input is less than smallest int232 or\n     * greater than largest int232).\n     *\n     * Counterpart to Solidity's `int232` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 232 bits\n     */\n    function toInt232(int256 value) internal pure returns (int232 downcasted) {\n        downcasted = int232(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(232, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int224 from int256, reverting on\n     * overflow (when the input is less than smallest int224 or\n     * greater than largest int224).\n     *\n     * Counterpart to Solidity's `int224` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 224 bits\n     */\n    function toInt224(int256 value) internal pure returns (int224 downcasted) {\n        downcasted = int224(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(224, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int216 from int256, reverting on\n     * overflow (when the input is less than smallest int216 or\n     * greater than largest int216).\n     *\n     * Counterpart to Solidity's `int216` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 216 bits\n     */\n    function toInt216(int256 value) internal pure returns (int216 downcasted) {\n        downcasted = int216(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(216, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int208 from int256, reverting on\n     * overflow (when the input is less than smallest int208 or\n     * greater than largest int208).\n     *\n     * Counterpart to Solidity's `int208` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 208 bits\n     */\n    function toInt208(int256 value) internal pure returns (int208 downcasted) {\n        downcasted = int208(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(208, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int200 from int256, reverting on\n     * overflow (when the input is less than smallest int200 or\n     * greater than largest int200).\n     *\n     * Counterpart to Solidity's `int200` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 200 bits\n     */\n    function toInt200(int256 value) internal pure returns (int200 downcasted) {\n        downcasted = int200(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(200, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int192 from int256, reverting on\n     * overflow (when the input is less than smallest int192 or\n     * greater than largest int192).\n     *\n     * Counterpart to Solidity's `int192` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 192 bits\n     */\n    function toInt192(int256 value) internal pure returns (int192 downcasted) {\n        downcasted = int192(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(192, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int184 from int256, reverting on\n     * overflow (when the input is less than smallest int184 or\n     * greater than largest int184).\n     *\n     * Counterpart to Solidity's `int184` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 184 bits\n     */\n    function toInt184(int256 value) internal pure returns (int184 downcasted) {\n        downcasted = int184(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(184, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int176 from int256, reverting on\n     * overflow (when the input is less than smallest int176 or\n     * greater than largest int176).\n     *\n     * Counterpart to Solidity's `int176` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 176 bits\n     */\n    function toInt176(int256 value) internal pure returns (int176 downcasted) {\n        downcasted = int176(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(176, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int168 from int256, reverting on\n     * overflow (when the input is less than smallest int168 or\n     * greater than largest int168).\n     *\n     * Counterpart to Solidity's `int168` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 168 bits\n     */\n    function toInt168(int256 value) internal pure returns (int168 downcasted) {\n        downcasted = int168(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(168, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int160 from int256, reverting on\n     * overflow (when the input is less than smallest int160 or\n     * greater than largest int160).\n     *\n     * Counterpart to Solidity's `int160` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 160 bits\n     */\n    function toInt160(int256 value) internal pure returns (int160 downcasted) {\n        downcasted = int160(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(160, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int152 from int256, reverting on\n     * overflow (when the input is less than smallest int152 or\n     * greater than largest int152).\n     *\n     * Counterpart to Solidity's `int152` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 152 bits\n     */\n    function toInt152(int256 value) internal pure returns (int152 downcasted) {\n        downcasted = int152(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(152, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int144 from int256, reverting on\n     * overflow (when the input is less than smallest int144 or\n     * greater than largest int144).\n     *\n     * Counterpart to Solidity's `int144` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 144 bits\n     */\n    function toInt144(int256 value) internal pure returns (int144 downcasted) {\n        downcasted = int144(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(144, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int136 from int256, reverting on\n     * overflow (when the input is less than smallest int136 or\n     * greater than largest int136).\n     *\n     * Counterpart to Solidity's `int136` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 136 bits\n     */\n    function toInt136(int256 value) internal pure returns (int136 downcasted) {\n        downcasted = int136(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(136, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int128 from int256, reverting on\n     * overflow (when the input is less than smallest int128 or\n     * greater than largest int128).\n     *\n     * Counterpart to Solidity's `int128` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 128 bits\n     */\n    function toInt128(int256 value) internal pure returns (int128 downcasted) {\n        downcasted = int128(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(128, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int120 from int256, reverting on\n     * overflow (when the input is less than smallest int120 or\n     * greater than largest int120).\n     *\n     * Counterpart to Solidity's `int120` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 120 bits\n     */\n    function toInt120(int256 value) internal pure returns (int120 downcasted) {\n        downcasted = int120(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(120, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int112 from int256, reverting on\n     * overflow (when the input is less than smallest int112 or\n     * greater than largest int112).\n     *\n     * Counterpart to Solidity's `int112` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 112 bits\n     */\n    function toInt112(int256 value) internal pure returns (int112 downcasted) {\n        downcasted = int112(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(112, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int104 from int256, reverting on\n     * overflow (when the input is less than smallest int104 or\n     * greater than largest int104).\n     *\n     * Counterpart to Solidity's `int104` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 104 bits\n     */\n    function toInt104(int256 value) internal pure returns (int104 downcasted) {\n        downcasted = int104(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(104, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int96 from int256, reverting on\n     * overflow (when the input is less than smallest int96 or\n     * greater than largest int96).\n     *\n     * Counterpart to Solidity's `int96` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 96 bits\n     */\n    function toInt96(int256 value) internal pure returns (int96 downcasted) {\n        downcasted = int96(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(96, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int88 from int256, reverting on\n     * overflow (when the input is less than smallest int88 or\n     * greater than largest int88).\n     *\n     * Counterpart to Solidity's `int88` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 88 bits\n     */\n    function toInt88(int256 value) internal pure returns (int88 downcasted) {\n        downcasted = int88(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(88, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int80 from int256, reverting on\n     * overflow (when the input is less than smallest int80 or\n     * greater than largest int80).\n     *\n     * Counterpart to Solidity's `int80` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 80 bits\n     */\n    function toInt80(int256 value) internal pure returns (int80 downcasted) {\n        downcasted = int80(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(80, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int72 from int256, reverting on\n     * overflow (when the input is less than smallest int72 or\n     * greater than largest int72).\n     *\n     * Counterpart to Solidity's `int72` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 72 bits\n     */\n    function toInt72(int256 value) internal pure returns (int72 downcasted) {\n        downcasted = int72(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(72, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int64 from int256, reverting on\n     * overflow (when the input is less than smallest int64 or\n     * greater than largest int64).\n     *\n     * Counterpart to Solidity's `int64` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 64 bits\n     */\n    function toInt64(int256 value) internal pure returns (int64 downcasted) {\n        downcasted = int64(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(64, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int56 from int256, reverting on\n     * overflow (when the input is less than smallest int56 or\n     * greater than largest int56).\n     *\n     * Counterpart to Solidity's `int56` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 56 bits\n     */\n    function toInt56(int256 value) internal pure returns (int56 downcasted) {\n        downcasted = int56(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(56, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int48 from int256, reverting on\n     * overflow (when the input is less than smallest int48 or\n     * greater than largest int48).\n     *\n     * Counterpart to Solidity's `int48` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 48 bits\n     */\n    function toInt48(int256 value) internal pure returns (int48 downcasted) {\n        downcasted = int48(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(48, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int40 from int256, reverting on\n     * overflow (when the input is less than smallest int40 or\n     * greater than largest int40).\n     *\n     * Counterpart to Solidity's `int40` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 40 bits\n     */\n    function toInt40(int256 value) internal pure returns (int40 downcasted) {\n        downcasted = int40(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(40, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int32 from int256, reverting on\n     * overflow (when the input is less than smallest int32 or\n     * greater than largest int32).\n     *\n     * Counterpart to Solidity's `int32` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 32 bits\n     */\n    function toInt32(int256 value) internal pure returns (int32 downcasted) {\n        downcasted = int32(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(32, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int24 from int256, reverting on\n     * overflow (when the input is less than smallest int24 or\n     * greater than largest int24).\n     *\n     * Counterpart to Solidity's `int24` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 24 bits\n     */\n    function toInt24(int256 value) internal pure returns (int24 downcasted) {\n        downcasted = int24(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(24, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int16 from int256, reverting on\n     * overflow (when the input is less than smallest int16 or\n     * greater than largest int16).\n     *\n     * Counterpart to Solidity's `int16` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 16 bits\n     */\n    function toInt16(int256 value) internal pure returns (int16 downcasted) {\n        downcasted = int16(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(16, value);\n        }\n    }\n\n    /**\n     * @dev Returns the downcasted int8 from int256, reverting on\n     * overflow (when the input is less than smallest int8 or\n     * greater than largest int8).\n     *\n     * Counterpart to Solidity's `int8` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 8 bits\n     */\n    function toInt8(int256 value) internal pure returns (int8 downcasted) {\n        downcasted = int8(value);\n        if (downcasted != value) {\n            revert SafeCastOverflowedIntDowncast(8, value);\n        }\n    }\n\n    /**\n     * @dev Converts an unsigned uint256 into a signed int256.\n     *\n     * Requirements:\n     *\n     * - input must be less than or equal to maxInt256.\n     */\n    function toInt256(uint256 value) internal pure returns (int256) {\n        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n        if (value > uint256(type(int256).max)) {\n            revert SafeCastOverflowedUintToInt(value);\n        }\n        return int256(value);\n    }\n\n    /**\n     * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.\n     */\n    function toUint(bool b) internal pure returns (uint256 u) {\n        assembly (\"memory-safe\") {\n            u := iszero(iszero(b))\n        }\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/token/ERC20/ERC20.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC20} from \"./IERC20.sol\";\nimport {IERC20Metadata} from \"./extensions/IERC20Metadata.sol\";\nimport {Context} from \"../../utils/Context.sol\";\nimport {IERC20Errors} from \"../../interfaces/draft-IERC6093.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * The default value of {decimals} is 18. To change this, you should override\n * this function so it returns a different value.\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC-20\n * applications.\n */\nabstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {\n    mapping(address account => uint256) private _balances;\n\n    mapping(address account => mapping(address spender => uint256)) private _allowances;\n\n    uint256 private _totalSupply;\n\n    string private _name;\n    string private _symbol;\n\n    /**\n     * @dev Sets the values for {name} and {symbol}.\n     *\n     * All two of these values are immutable: they can only be set once during\n     * construction.\n     */\n    constructor(string memory name_, string memory symbol_) {\n        _name = name_;\n        _symbol = symbol_;\n    }\n\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() public view virtual returns (string memory) {\n        return _name;\n    }\n\n    /**\n     * @dev Returns the symbol of the token, usually a shorter version of the\n     * name.\n     */\n    function symbol() public view virtual returns (string memory) {\n        return _symbol;\n    }\n\n    /**\n     * @dev Returns the number of decimals used to get its user representation.\n     * For example, if `decimals` equals `2`, a balance of `505` tokens should\n     * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n     *\n     * Tokens usually opt for a value of 18, imitating the relationship between\n     * Ether and Wei. This is the default value returned by this function, unless\n     * it's overridden.\n     *\n     * NOTE: This information is only used for _display_ purposes: it in\n     * no way affects any of the arithmetic of the contract, including\n     * {IERC20-balanceOf} and {IERC20-transfer}.\n     */\n    function decimals() public view virtual returns (uint8) {\n        return 18;\n    }\n\n    /**\n     * @dev See {IERC20-totalSupply}.\n     */\n    function totalSupply() public view virtual returns (uint256) {\n        return _totalSupply;\n    }\n\n    /**\n     * @dev See {IERC20-balanceOf}.\n     */\n    function balanceOf(address account) public view virtual returns (uint256) {\n        return _balances[account];\n    }\n\n    /**\n     * @dev See {IERC20-transfer}.\n     *\n     * Requirements:\n     *\n     * - `to` cannot be the zero address.\n     * - the caller must have a balance of at least `value`.\n     */\n    function transfer(address to, uint256 value) public virtual returns (bool) {\n        address owner = _msgSender();\n        _transfer(owner, to, value);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-allowance}.\n     */\n    function allowance(address owner, address spender) public view virtual returns (uint256) {\n        return _allowances[owner][spender];\n    }\n\n    /**\n     * @dev See {IERC20-approve}.\n     *\n     * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on\n     * `transferFrom`. This is semantically equivalent to an infinite approval.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     */\n    function approve(address spender, uint256 value) public virtual returns (bool) {\n        address owner = _msgSender();\n        _approve(owner, spender, value);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-transferFrom}.\n     *\n     * Skips emitting an {Approval} event indicating an allowance update. This is not\n     * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].\n     *\n     * NOTE: Does not update the allowance if the current allowance\n     * is the maximum `uint256`.\n     *\n     * Requirements:\n     *\n     * - `from` and `to` cannot be the zero address.\n     * - `from` must have a balance of at least `value`.\n     * - the caller must have allowance for ``from``'s tokens of at least\n     * `value`.\n     */\n    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {\n        address spender = _msgSender();\n        _spendAllowance(from, spender, value);\n        _transfer(from, to, value);\n        return true;\n    }\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to`.\n     *\n     * This internal function is equivalent to {transfer}, and can be used to\n     * e.g. implement automatic token fees, slashing mechanisms, etc.\n     *\n     * Emits a {Transfer} event.\n     *\n     * NOTE: This function is not virtual, {_update} should be overridden instead.\n     */\n    function _transfer(address from, address to, uint256 value) internal {\n        if (from == address(0)) {\n            revert ERC20InvalidSender(address(0));\n        }\n        if (to == address(0)) {\n            revert ERC20InvalidReceiver(address(0));\n        }\n        _update(from, to, value);\n    }\n\n    /**\n     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`\n     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding\n     * this function.\n     *\n     * Emits a {Transfer} event.\n     */\n    function _update(address from, address to, uint256 value) internal virtual {\n        if (from == address(0)) {\n            // Overflow check required: The rest of the code assumes that totalSupply never overflows\n            _totalSupply += value;\n        } else {\n            uint256 fromBalance = _balances[from];\n            if (fromBalance < value) {\n                revert ERC20InsufficientBalance(from, fromBalance, value);\n            }\n            unchecked {\n                // Overflow not possible: value <= fromBalance <= totalSupply.\n                _balances[from] = fromBalance - value;\n            }\n        }\n\n        if (to == address(0)) {\n            unchecked {\n                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.\n                _totalSupply -= value;\n            }\n        } else {\n            unchecked {\n                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.\n                _balances[to] += value;\n            }\n        }\n\n        emit Transfer(from, to, value);\n    }\n\n    /**\n     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).\n     * Relies on the `_update` mechanism\n     *\n     * Emits a {Transfer} event with `from` set to the zero address.\n     *\n     * NOTE: This function is not virtual, {_update} should be overridden instead.\n     */\n    function _mint(address account, uint256 value) internal {\n        if (account == address(0)) {\n            revert ERC20InvalidReceiver(address(0));\n        }\n        _update(address(0), account, value);\n    }\n\n    /**\n     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.\n     * Relies on the `_update` mechanism.\n     *\n     * Emits a {Transfer} event with `to` set to the zero address.\n     *\n     * NOTE: This function is not virtual, {_update} should be overridden instead\n     */\n    function _burn(address account, uint256 value) internal {\n        if (account == address(0)) {\n            revert ERC20InvalidSender(address(0));\n        }\n        _update(account, address(0), value);\n    }\n\n    /**\n     * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.\n     *\n     * This internal function is equivalent to `approve`, and can be used to\n     * e.g. set automatic allowances for certain subsystems, etc.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `owner` cannot be the zero address.\n     * - `spender` cannot be the zero address.\n     *\n     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.\n     */\n    function _approve(address owner, address spender, uint256 value) internal {\n        _approve(owner, spender, value, true);\n    }\n\n    /**\n     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.\n     *\n     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by\n     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any\n     * `Approval` event during `transferFrom` operations.\n     *\n     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to\n     * true using the following override:\n     *\n     * ```solidity\n     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {\n     *     super._approve(owner, spender, value, true);\n     * }\n     * ```\n     *\n     * Requirements are the same as {_approve}.\n     */\n    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {\n        if (owner == address(0)) {\n            revert ERC20InvalidApprover(address(0));\n        }\n        if (spender == address(0)) {\n            revert ERC20InvalidSpender(address(0));\n        }\n        _allowances[owner][spender] = value;\n        if (emitEvent) {\n            emit Approval(owner, spender, value);\n        }\n    }\n\n    /**\n     * @dev Updates `owner` s allowance for `spender` based on spent `value`.\n     *\n     * Does not update the allowance value in case of infinite allowance.\n     * Revert if not enough allowance is available.\n     *\n     * Does not emit an {Approval} event.\n     */\n    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {\n        uint256 currentAllowance = allowance(owner, spender);\n        if (currentAllowance < type(uint256).max) {\n            if (currentAllowance < value) {\n                revert ERC20InsufficientAllowance(spender, currentAllowance, value);\n            }\n            unchecked {\n                _approve(owner, spender, currentAllowance - value, false);\n            }\n        }\n    }\n}\n"},{"file_path":"contracts/legacy/FirelightVaultPredeposits.sol","source_code":"// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.28;\n\nimport {ERC4626Upgradeable} from \"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol\";\nimport {ERC20Upgradeable} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {AccessControlUpgradeable} from \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport {PausableUpgradeable} from \"@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol\";\nimport {ReentrancyGuardUpgradeable} from \"@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol\";\nimport {SafeERC20} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {Math} from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport {Time} from \"@openzeppelin/contracts/utils/types/Time.sol\";\n\nimport {FirelightVaultStoragePredeposits} from \"./FirelightVaultStoragePredeposits.sol\";\nimport {Checkpoints} from \"../core/lib/Checkpoints.sol\";\n\n/**\n * @title FirelightVault\n * @notice Upgradeable ERC4626-compatible vault with delayed withdrawals.\n *\n * @dev FirelightVault is an ERC4626 vault that intentionally deviates from the standard.\n * It overrides `withdraw` and `redeem` to implement delayed withdrawals.\n * Instead of transferring assets immediately, these functions create a withdrawal request,\n * which must be completed later via `claimWithdraw` after a set delay.\n *\n * The standard `Withdraw` event is not emitted. Instead, `WithdrawRequest` and `CompleteWithdraw`\n * are used to track the withdrawal process.\n *\n * Off-chain and on-chain tools must account for this custom flow and event structure.\n *\n * @custom:security-contact securityreport@firelight.finance\n */\ncontract FirelightVaultPredeposits is\n    FirelightVaultStoragePredeposits,\n    ERC4626Upgradeable,\n    AccessControlUpgradeable,\n    PausableUpgradeable,\n    ReentrancyGuardUpgradeable\n{\n    using Checkpoints for Checkpoints.Trace256;\n    using SafeERC20 for IERC20;\n    using Math for uint256;\n\n    /**\n     * @notice Initial parameters needed for the vault's deployment.\n     * @param defaultAdmin Vault's admin that grants and revokes roles.\n     * @param limitUpdater Address assigned the DEPOSIT_LIMIT_UPDATE_ROLE at initialization.\n     * @param blocklister Address assigned the BLOCKLIST_ROLE at initialization.\n     * @param pauser Address assigned the PAUSE_ROLE at initialization.\n     * @param periodConfigurationUpdater Address assigned the PERIOD_CONFIGURATION_UPDATE_ROLE at initialization.\n     * @param rescuer Address assigned the RESCUER_ROLE at initialization.\n     * @param depositLimit Initial total deposit limit.\n     * @param periodConfigurationDuration Initial period duration of the vault.\n     */\n    struct InitParams {\n        address defaultAdmin;\n        address limitUpdater;\n        address blocklister;\n        address pauser;\n        address periodConfigurationUpdater;\n        address rescuer;\n        uint256 depositLimit;\n        uint48 periodConfigurationDuration;\n    }\n\n    /**\n     * @notice Emitted when the vault's deposit limit is updated.\n     * @param limit The new maximum amount of assets allowed in the vault.\n     */\n    event DepositLimitUpdated(uint256 limit);\n\n    /**\n     * @notice Emitted when a new periodConfiguration is added.\n     * @param periodConfiguration The details of the newly added periodConfiguration.\n     */\n    event PeriodConfigurationAdded(PeriodConfiguration periodConfiguration);\n\n    /**\n     * @notice Emitted when a withdrawal request is created by a user.\n     * @param sender The caller who initiated the withdrawal request.\n     * @param receiver The address that will receive the assets in the next period.\n     * @param owner The address whose shares are being redeemed, using allowance.\n     * @param period The period when the withdrawal will be available.\n     * @param assets The amount of assets to be withdrawn.\n     * @param shares The number of shares burned for the withdrawal.\n     */\n    event WithdrawRequest(\n        address indexed sender,\n        address indexed receiver,\n        address indexed owner,\n        uint256 period,\n        uint256 assets,\n        uint256 shares\n    );\n\n    /**\n     * @notice Emitted when a user successfully claims a withdrawal for a given period.\n     * @param receiver The address that received the withdrawn assets.\n     * @param assets The amount of assets withdrawn.\n     * @param period The period for which the withdrawal was claimed.\n     */\n    event CompleteWithdraw(address indexed receiver, uint256 assets, uint256 period);\n\n    /**\n     * @notice Emitted when a user with RESCUER_ROLE successfully rescues shares from a blocklisted address.\n     * @param from The blocklisted address.\n     * @param to The beneficiary of the rescued shares.\n     * @param rescuedShares The amount of shares rescued.\n     */\n    event SharesRescuedFromBlocklisted(address from, address to, uint256 rescuedShares);\n\n    /**\n     * @notice Emitted when a user with RESCUER_ROLE successfully rescues a pending withdrawal from blocklisted address.\n     * @param from The blocklisted address.\n     * @param to The beneficiary of the rescued withdrawals.\n     * @param periods The array of periods rescued.\n     * @param rescuedShares The array of pending shares from withdrawals rescued for each period.\n     */\n    event WithdrawRescuedFromBlocklisted(address from, address to, uint256[] periods, uint256[] rescuedShares);\n\n    error BlocklistedAddress();\n    error NotBlocklistedAddress();\n    error DepositLimitExceeded();\n    error InvalidDepositLimit();\n    error InvalidPeriodConfigurationEpoch();\n    error InvalidPeriodConfigurationDuration();\n    error InsufficientShares();\n    error InvalidAssetAddress();\n    error InvalidAdminAddress();\n    error InvalidAddress();\n    error InvalidAmount();\n    error InvalidPeriod();\n    error CurrentPeriodConfigurationNotLast();\n    error InvalidArrayLength();\n    error AlreadyClaimedPeriod(uint256 period);\n    error NoWithdrawalAmount(uint256 period);\n\n    modifier notBlocklisted(address account) {\n        if (isBlocklisted[account]) {\n            revert BlocklistedAddress();\n        }\n        _;\n    }\n\n    modifier onlyBlocklisted(address account) {\n        if (!isBlocklisted[account]) {\n            revert NotBlocklistedAddress();\n        }\n        _;\n    }\n\n    /**\n     * @notice Prevents unauthorized direct deployment via the constructor.\n     */\n    /// @custom:oz-upgrades-unsafe-allow constructor\n    constructor() {\n        _disableInitializers();\n    }\n\n    /**\n     * @notice Initializes the FirelightVault contract with given parameters\n     * @param _asset The underlying collateral ERC20 token.\n     * @param _name The name of the vault token.\n     * @param _symbol The symbol of the vault token.\n     * @param _initParams Initial parameters.\n     */\n    function initialize(\n        IERC20 _asset,\n        string memory _name,\n        string memory _symbol,\n        bytes memory _initParams\n    ) public initializer {\n        InitParams memory initParams = abi.decode(_initParams, (InitParams));\n        __ERC20_init(_name, _symbol);\n        __ERC4626_init(_asset);\n        __Pausable_init();\n        __ReentrancyGuard_init();\n        __AccessControl_init();\n\n        if (address(_asset) == address(0)) {\n            revert InvalidAssetAddress();\n        }\n\n        if (initParams.depositLimit == 0) {\n            revert InvalidDepositLimit();\n        }\n\n        if (initParams.periodConfigurationDuration == 0) {\n            revert InvalidPeriodConfigurationDuration();\n        }\n\n        if (initParams.defaultAdmin == address(0)) {\n            revert InvalidAdminAddress();\n        }\n\n        depositLimit = initParams.depositLimit;\n        _addPeriodConfiguration(Time.timestamp(), initParams.periodConfigurationDuration);\n        contractVersion = 1;\n\n        _grantRole(DEFAULT_ADMIN_ROLE, initParams.defaultAdmin);\n\n        if (initParams.limitUpdater != address(0)) {\n            _grantRole(DEPOSIT_LIMIT_UPDATE_ROLE, initParams.limitUpdater);\n        }\n\n        if (initParams.blocklister != address(0)) {\n            _grantRole(BLOCKLIST_ROLE, initParams.blocklister);\n        }\n\n        if (initParams.pauser != address(0)) {\n            _grantRole(PAUSE_ROLE, initParams.pauser);\n        }\n\n        if (initParams.periodConfigurationUpdater != address(0)) {\n            _grantRole(PERIOD_CONFIGURATION_UPDATE_ROLE, initParams.periodConfigurationUpdater);\n        }\n\n        if (initParams.rescuer != address(0)) {\n            _grantRole(RESCUER_ROLE, initParams.rescuer);\n        }\n    }\n\n    /**\n     * @notice Returns the period configuration corresponding to a given timestamp.\n     * @dev Return value may be unreliable if timestamp given is far away in the future\n     * @dev given that new period configurations can be added after nextPeriodEnd().\n     * @param timestamp The timestamp to find the period configuration for.\n     * @return The period configuration corresponding to the given timestamp.\n     */\n    function periodConfigurationAtTimestamp(uint48 timestamp) public view returns (PeriodConfiguration memory) {\n        uint256 length = periodConfigurations.length;\n        if (length == 0) revert InvalidPeriod();\n\n        PeriodConfiguration memory periodConfiguration;\n        for (uint256 i = 0; i < length; i++) {\n            if (timestamp < periodConfigurations[i].epoch) break;\n            periodConfiguration = periodConfigurations[i];\n        }\n        if (periodConfiguration.epoch == 0) revert InvalidPeriod();\n        return periodConfiguration;\n    }\n\n    /**\n     * @notice Returns the period configuration corresponding to a given period number.\n     * @dev Return value may be unreliable if period number given is far away in the future\n     * @dev given that new period configurations can be added after nextPeriodEnd().\n     * @param periodNumber The period number to find the period configuration for.\n     * @return The period configuration corresponding to the given period number.\n     */\n    function periodConfigurationAtNumber(uint256 periodNumber) external view returns (PeriodConfiguration memory) {\n        uint256 length = periodConfigurations.length;\n        if (length == 0) revert InvalidPeriod();\n\n        PeriodConfiguration memory periodConfiguration;\n        for (uint256 i = 0; i < length; i++) {\n            if (periodNumber < periodConfigurations[i].startingPeriod) break;\n            periodConfiguration = periodConfigurations[i];\n        }\n        if (periodConfiguration.epoch == 0) revert InvalidPeriod();\n        return periodConfiguration;\n    }\n\n    /**\n     * @notice Returns the period number for the timestamp given.\n     * @dev Return value may be unreliable if period number given is far away in the future\n     * @dev given that new period configurations can be added after nextPeriodEnd().\n     * @return The period number corresponding to the given timestamp.\n     */\n    function periodAtTimestamp(uint48 timestamp) public view returns (uint256) {\n        PeriodConfiguration memory periodConfiguration = periodConfigurationAtTimestamp(timestamp);\n        // solhint-disable-next-line max-line-length\n        return\n            periodConfiguration.startingPeriod +\n            _timestampSinceEpoch(timestamp, periodConfiguration.epoch) / periodConfiguration.duration;\n    }\n\n    /**\n     * @notice Returns the period configuration for the current period.\n     * @return The period configuration corresponding to the current period.\n     */\n    function currentPeriodConfiguration() public view returns (PeriodConfiguration memory) {\n        return periodConfigurationAtTimestamp(Time.timestamp());\n    }\n\n    /**\n     * @notice Returns the current active period.\n     * @return The current period number since contract deployment.\n     */\n    function currentPeriod() public view returns (uint256) {\n        return periodAtTimestamp(Time.timestamp());\n    }\n\n    /**\n     * @notice Returns the start timestamp of the current period.\n     * @return Timestamp of the current period start.\n     */\n    function currentPeriodStart() external view returns (uint48) {\n        PeriodConfiguration memory currentPC = currentPeriodConfiguration();\n        return currentPC.epoch + (_nowSinceEpoch(currentPC.epoch) / currentPC.duration) * currentPC.duration;\n    }\n\n    /**\n     * @notice Returns the end timestamp of the current period.\n     * @return Timestamp of the current period end.\n     */\n    function currentPeriodEnd() public view returns (uint48) {\n        PeriodConfiguration memory currentPC = currentPeriodConfiguration();\n        return currentPC.epoch + (_nowSinceEpoch(currentPC.epoch) / currentPC.duration + 1) * currentPC.duration;\n    }\n\n    /**\n     * @notice Returns the end timestamp of the period following the current period.\n     * @return Timestamp of the next period end.\n     */\n    function nextPeriodEnd() public view returns (uint48) {\n        uint48 currentEnd = currentPeriodEnd();\n        return currentEnd + periodConfigurationAtTimestamp(currentEnd).duration;\n    }\n\n    /**\n     * @notice Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,\n     * through a deposit call.\n     * @param receiver The address of the deposit receiver.\n     * @return amount Maximum amount of assets that can be deposited.\n     */\n    function maxDeposit(address receiver) public view override returns (uint256 amount) {\n        uint256 assets = totalAssets();\n        if (isBlocklisted[receiver] || paused() || assets > depositLimit) {\n            return 0;\n        } else {\n            return depositLimit - assets;\n        }\n    }\n\n    /**\n     * @notice Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.\n     * @param receiver The address of the mint receiver.\n     * @return amount Maximum amount of shares that can be minted.\n     */\n    function maxMint(address receiver) public view override returns (uint256 amount) {\n        uint256 shares = totalSupply();\n        uint256 sharesLimit = convertToShares(depositLimit);\n        if (isBlocklisted[receiver] || paused() || shares > sharesLimit) {\n            return 0;\n        } else {\n            return sharesLimit - shares;\n        }\n    }\n\n    /**\n     * @notice Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the\n     * Vault, through a withdraw call.\n     * @param owner The owner of the assets.\n     * @return amount Maximum amount of assets that can be withdrawn.\n     */\n    function maxWithdraw(address owner) public view override returns (uint256 amount) {\n        if (isBlocklisted[owner] || paused()) {\n            return 0;\n        } else {\n            return _convertToAssets(balanceOf(owner), Math.Rounding.Floor);\n        }\n    }\n\n    /**\n     * @notice Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,\n     * through a redeem call.\n     * @param owner The owner of the shares.\n     * @param amount Maximum amount of shares that can be redeemed.\n     */\n    function maxRedeem(address owner) public view override returns (uint256 amount) {\n        if (isBlocklisted[owner] || paused()) {\n            return 0;\n        } else {\n            return balanceOf(owner);\n        }\n    }\n\n    /**\n     * @notice Returns the total assets in the vault excluding those marked for withdrawal.\n     * @return The total assets held by the vault.\n     */\n    function totalAssets() public view override returns (uint256) {\n        return super.totalAssets() - pendingWithdrawAssets;\n    }\n\n    /**\n     * @notice Returns the effective total shares for `account` at a specific `timestamp`.\n     * @param account The address whose share balance is being queried.\n     * @param timestamp The point in time for which the balance is being checked.\n     * @return The shares owned by `account` at the specified time.\n     */\n    function balanceOfAt(address account, uint48 timestamp) external view returns (uint256) {\n        return _traceBalanceOf[account].upperLookupRecent(timestamp);\n    }\n\n    /**\n     * @notice Returns the total supply of shares at a specific `timestamp`.\n     * @param timestamp The point in time for which the total supply is being checked.\n     * @return The total shares in existence at the specified time.\n     */\n    function totalSupplyAt(uint48 timestamp) external view returns (uint256) {\n        return _traceTotalSupply.upperLookupRecent(timestamp);\n    }\n\n    /**\n     * @notice Returns the total underlying assets held by the vault at a specific `timestamp`, excluding any assets\n     * marked for withdrawal.\n     * @param timestamp The point in time for which the total assets are being checked.\n     * @return The total underlying assets held by the vault at the specified time.\n     */\n    function totalAssetsAt(uint48 timestamp) external view returns (uint256) {\n        return _traceTotalAssets.upperLookupRecent(timestamp);\n    }\n\n    /**\n     * @notice Returns the amount that was made withdrawable for the given period and account, whether claimed or not.\n     * @param period Period number to check.\n     * @param account Account address.\n     * @return Amount of assets claimable for that period.\n     */\n    function withdrawalsOf(uint256 period, address account) external view returns (uint256) {\n        return\n            _convertToAssetsTotals(\n                withdrawSharesOf[period][account],\n                withdrawShares[period],\n                withdrawAssets[period],\n                Math.Rounding.Floor\n            );\n    }\n\n    /**\n     * @notice Returns the length of the periodConfigurations array.\n     * @return Length of the periodConfigurations array.\n     */\n    function periodConfigurationsLength() public view returns (uint256) {\n        return periodConfigurations.length;\n    }\n\n    /**\n     * @notice Pauses the contract. Requires PAUSE_ROLE.\n     */\n    function pause() external onlyRole(PAUSE_ROLE) {\n        _pause();\n    }\n\n    /**\n     * @notice Unpauses the contract. Requires PAUSE_ROLE.\n     */\n    function unpause() external onlyRole(PAUSE_ROLE) {\n        _unpause();\n    }\n\n    /**\n     * @notice Updates the maximum deposit limit for the vault. Requires DEPOSIT_LIMIT_UPDATE_ROLE.\n     * @param newLimit The new deposit limit.\n     */\n    function updateDepositLimit(uint256 newLimit) external onlyRole(DEPOSIT_LIMIT_UPDATE_ROLE) {\n        if (newLimit == 0) {\n            revert InvalidDepositLimit();\n        }\n        depositLimit = newLimit;\n        emit DepositLimitUpdated(newLimit);\n    }\n\n    /**\n     * @notice Adds a period configuration. Requires PERIOD_CONFIGURATION_UPDATE_ROLE.\n     * @param epoch The epoch timestamp.\n     * @param duration The period duration.\n     */\n    function addPeriodConfiguration(uint48 epoch, uint48 duration) external onlyRole(PERIOD_CONFIGURATION_UPDATE_ROLE) {\n        _addPeriodConfiguration(epoch, duration);\n    }\n\n    /**\n     * @notice Adds an address to the blocklist. Requires BLOCKLIST_ROLE.\n     * @param account Address to blocklist. Cannot be zero address nor blocklisted.\n     */\n    function addToBlocklist(address account) external onlyRole(BLOCKLIST_ROLE) notBlocklisted(account) {\n        if (account == address(0)) revert InvalidAddress();\n        isBlocklisted[account] = true;\n    }\n\n    /**\n     * @notice Removes an address from the blocklist. Requires BLOCKLIST_ROLE.\n     * @param account Address to remove from blocklist. Must be blocklisted.\n     */\n    function removeFromBlocklist(address account) external onlyRole(BLOCKLIST_ROLE) onlyBlocklisted(account) {\n        isBlocklisted[account] = false;\n    }\n\n    /**\n     * @notice Transfers shares to an address, with blocklist and pause checks.\n     * @param to Recipient address.\n     * @param shares Number of shares to transfer.\n     * @return Boolean indicating transfer success.\n     */\n    function transfer(\n        address to,\n        uint256 shares\n    )\n        public\n        override(ERC20Upgradeable, IERC20)\n        whenNotPaused\n        notBlocklisted(_msgSender())\n        notBlocklisted(to)\n        returns (bool)\n    {\n        super.transfer(to, shares);\n\n        uint48 ts = Time.timestamp();\n        address sender = _msgSender();\n        _traceBalanceOf[sender].push(ts, balanceOf(sender));\n        _traceBalanceOf[to].push(ts, balanceOf(to));\n\n        return true;\n    }\n\n    /**\n     * @notice Transfers shares from one account to another using allowance, with blocklist and pause checks.\n     * @param from Address sending the shares.\n     * @param to Address receiving the shares.\n     * @param shares Number of shares to transfer.\n     * @return Boolean indicating transfer success.\n     */\n    function transferFrom(\n        address from,\n        address to,\n        uint256 shares\n    )\n        public\n        override(ERC20Upgradeable, IERC20)\n        whenNotPaused\n        notBlocklisted(_msgSender())\n        notBlocklisted(from)\n        notBlocklisted(to)\n        returns (bool)\n    {\n        super.transferFrom(from, to, shares);\n\n        uint48 ts = Time.timestamp();\n        _traceBalanceOf[from].push(ts, balanceOf(from));\n        _traceBalanceOf[to].push(ts, balanceOf(to));\n\n        return true;\n    }\n\n    /**\n     * @notice Deposits assets into the vault and receive shares, with blocklist and pause checks.\n     * @param assets Amount of assets to deposit.\n     * @param receiver Address receiving the shares.\n     * @return Amount of shares received.\n     */\n    function deposit(\n        uint256 assets,\n        address receiver\n    )\n        public\n        override\n        whenNotPaused\n        notBlocklisted(_msgSender())\n        notBlocklisted(receiver)\n        nonReentrant\n        returns (uint256)\n    {\n        if (assets == 0) revert InvalidAmount();\n\n        (uint256 shares, uint256 _totalSupply, uint256 _totalAssets) = _previewTotals(\n            assets,\n            true,\n            Math.Rounding.Floor\n        );\n\n        _depositFunds(_msgSender(), receiver, assets, shares, _totalSupply, _totalAssets);\n\n        return shares;\n    }\n\n    /**\n     * @notice Mints shares by depositing the required amount of assets into the vault, with blocklist and pause checks.\n     * @param shares Amount of shares to mint.\n     * @param receiver Address receiving the shares.\n     * @return Amount of assets deposited.\n     */\n    function mint(\n        uint256 shares,\n        address receiver\n    )\n        public\n        override\n        whenNotPaused\n        notBlocklisted(_msgSender())\n        notBlocklisted(receiver)\n        nonReentrant\n        returns (uint256)\n    {\n        if (shares == 0) revert InvalidAmount();\n\n        (uint256 assets, uint256 _totalSupply, uint256 _totalAssets) = _previewTotals(\n            shares,\n            false,\n            Math.Rounding.Ceil\n        );\n\n        _depositFunds(_msgSender(), receiver, assets, shares, _totalSupply, _totalAssets);\n\n        return assets;\n    }\n\n    /**\n     * @notice Redeems shares from the vault and receives underlying assets, with blocklist and pause checks.\n     * Creates a withdrawal request, which will be available in the next period. Shares are burned.\n     * @param shares Amount of shares to redeem.\n     * @param receiver Address to receive the assets in the next period.\n     * @param owner Address whose shares are being redeemed.\n     * @return Amount of assets that will be received in the next period.\n     */\n    function redeem(\n        uint256 shares,\n        address receiver,\n        address owner\n    )\n        public\n        override\n        whenNotPaused\n        notBlocklisted(_msgSender())\n        notBlocklisted(owner)\n        notBlocklisted(receiver)\n        nonReentrant\n        returns (uint256)\n    {\n        if (shares == 0) revert InvalidAmount();\n\n        (uint256 assets, uint256 _totalSupply, uint256 _totalAssets) = _previewTotals(\n            shares,\n            false,\n            Math.Rounding.Floor\n        );\n\n        uint256 ownerBalance = _requestWithdraw(assets, shares, receiver, owner);\n\n        _logTrace(owner, ownerBalance, _totalSupply - shares, _totalAssets - assets, true);\n\n        return assets;\n    }\n\n    /**\n     * @notice Initiates a withdrawal request from the vault, with blocklist and pause checks.\n     * The request becomes claimable starting from the period after the next full period.\n     * The calculated shares are burned.\n     * @param assets The amount of assets to withdraw.\n     * @param receiver The address to receive the assets in the next period.\n     * @param owner The address whose shares are being withdrawn.\n     * @return The amount of shares that were burned.\n     */\n    function withdraw(\n        uint256 assets,\n        address receiver,\n        address owner\n    )\n        public\n        override\n        whenNotPaused\n        notBlocklisted(_msgSender())\n        notBlocklisted(owner)\n        notBlocklisted(receiver)\n        nonReentrant\n        returns (uint256)\n    {\n        if (assets == 0) revert InvalidAmount();\n\n        (uint256 shares, uint256 _totalSupply, uint256 _totalAssets) = _previewTotals(assets, true, Math.Rounding.Ceil);\n\n        uint256 ownerBalance = _requestWithdraw(assets, shares, receiver, owner);\n\n        _logTrace(owner, ownerBalance, _totalSupply - shares, _totalAssets - assets, true);\n\n        return shares;\n    }\n\n    /**\n     * @notice Claims a pending withdrawal for a given period.\n     * Transfers the corresponding assets to the caller if not already claimed.\n     * Can only be called after the specified period has ended.\n     * Reverts if the withdrawal has already been claimed or if no withdrawal amount is available for the period.\n     * @param period The period number for which to claim the withdrawal.\n     * @return assets The amount of assets transferred to the caller.\n     */\n    function claimWithdraw(\n        uint256 period\n    ) external whenNotPaused notBlocklisted(_msgSender()) returns (uint256 assets) {\n        if (period >= currentPeriod()) revert InvalidPeriod();\n\n        address sender = _msgSender();\n        if (isWithdrawClaimed[period][sender]) revert AlreadyClaimedPeriod(period);\n\n        assets = _convertToAssetsTotals(\n            withdrawSharesOf[period][sender],\n            withdrawShares[period],\n            withdrawAssets[period],\n            Math.Rounding.Floor\n        );\n\n        if (assets == 0) revert NoWithdrawalAmount(period);\n\n        pendingWithdrawAssets -= assets;\n        isWithdrawClaimed[period][sender] = true;\n\n        IERC20(asset()).safeTransfer(sender, assets);\n\n        emit CompleteWithdraw(sender, assets, period);\n    }\n\n    /**\n     * @notice Rescues shares from a blocklisted address. Requires RESCUER_ROLE.\n     * @param from The blocklisted address.\n     * @param to The address to transfer the shares. Must not be blocklisted.\n     */\n    function rescueSharesFromBlocklisted(\n        address from,\n        address to\n    ) external onlyRole(RESCUER_ROLE) onlyBlocklisted(from) notBlocklisted(to) {\n        uint256 rescuedShares = balanceOf(from);\n        if (rescuedShares == 0) revert InsufficientShares();\n\n        _transfer(from, to, rescuedShares);\n\n        uint48 ts = Time.timestamp();\n        _traceBalanceOf[from].push(ts, 0);\n        _traceBalanceOf[to].push(ts, balanceOf(to));\n\n        emit SharesRescuedFromBlocklisted(from, to, rescuedShares);\n    }\n\n    /**\n     * @notice Rescues pending withdrawals from a blocklisted address. Requires RESCUER_ROLE.\n     * @param from The blocklisted address.\n     * @param to The address to transfer the shares to. Must not be blocklisted.\n     * @param periods An array of periods to rescue.\n     */\n    function rescueWithdrawFromBlocklisted(\n        address from,\n        address to,\n        uint256[] calldata periods\n    ) external onlyRole(RESCUER_ROLE) onlyBlocklisted(from) notBlocklisted(to) {\n        if (to == address(0)) revert InvalidAddress();\n\n        uint256 len = periods.length;\n        if (len == 0) revert InvalidArrayLength();\n\n        uint256[] memory rescuedShares = new uint256[](len);\n        for (uint256 i = 0; i < len; i++) {\n            uint256 _withdrawOf = withdrawSharesOf[periods[i]][from];\n\n            if (isWithdrawClaimed[periods[i]][from]) revert AlreadyClaimedPeriod(periods[i]);\n            if (isWithdrawClaimed[periods[i]][to]) revert AlreadyClaimedPeriod(periods[i]);\n            if (_withdrawOf == 0) revert NoWithdrawalAmount(periods[i]);\n\n            withdrawSharesOf[periods[i]][to] += _withdrawOf;\n            withdrawSharesOf[periods[i]][from] = 0;\n            isWithdrawClaimed[periods[i]][from] = true;\n\n            rescuedShares[i] = _withdrawOf;\n        }\n\n        emit WithdrawRescuedFromBlocklisted(from, to, periods, rescuedShares);\n    }\n\n    function _depositFunds(\n        address caller,\n        address receiver,\n        uint256 assets,\n        uint256 shares,\n        uint256 _totalSupply,\n        uint256 _totalAssets\n    ) private {\n        _totalSupply += shares;\n        _totalAssets += assets;\n\n        if (_totalAssets > depositLimit) revert DepositLimitExceeded();\n\n        _deposit(caller, receiver, assets, shares);\n\n        _logTrace(receiver, balanceOf(receiver), _totalSupply, _totalAssets, true);\n    }\n\n    function _requestWithdraw(\n        uint256 assets,\n        uint256 shares,\n        address receiver,\n        address owner\n    ) private returns (uint256 ownerBalance) {\n        if (receiver == address(0) || owner == address(0)) revert InvalidAddress();\n\n        ownerBalance = balanceOf(owner);\n        if (shares > ownerBalance) revert InsufficientShares();\n        ownerBalance -= shares;\n\n        address sender = _msgSender();\n\n        uint256 period = currentPeriod() + 1;\n        uint256 sharesWithdraw = _convertToSharesTotals(\n            assets,\n            withdrawShares[period],\n            withdrawAssets[period],\n            Math.Rounding.Floor\n        );\n        withdrawAssets[period] += assets;\n        withdrawShares[period] += sharesWithdraw;\n        withdrawSharesOf[period][receiver] += sharesWithdraw;\n\n        pendingWithdrawAssets += assets;\n\n        if (sender != owner) {\n            _spendAllowance(owner, sender, shares);\n        }\n\n        _update(owner, address(0), shares);\n\n        emit WithdrawRequest(sender, receiver, owner, period, assets, shares);\n    }\n\n    function _previewTotals(\n        uint256 assetsOrShares,\n        bool isAssets,\n        Math.Rounding rounding\n    ) private view returns (uint256 amount, uint256 _totalSupply, uint256 _totalAssets) {\n        _totalSupply = totalSupply();\n        _totalAssets = totalAssets();\n        if (isAssets) {\n            amount = _convertToSharesTotals(assetsOrShares, _totalSupply, _totalAssets, rounding);\n        } else {\n            amount = _convertToAssetsTotals(assetsOrShares, _totalSupply, _totalAssets, rounding);\n        }\n    }\n\n    function _logTrace(\n        address owner,\n        uint256 balance,\n        uint256 _totalSupply,\n        uint256 _totalAssets,\n        bool isLogAssets\n    ) private {\n        uint48 ts = Time.timestamp();\n        _traceBalanceOf[owner].push(ts, balance);\n        _traceTotalSupply.push(ts, _totalSupply);\n\n        if (isLogAssets) _traceTotalAssets.push(ts, _totalAssets);\n    }\n\n    function _convertToSharesTotals(\n        uint256 assets,\n        uint256 totSupply,\n        uint256 totAssets,\n        Math.Rounding rounding\n    ) private view returns (uint256) {\n        return assets.mulDiv(totSupply + 10 ** _decimalsOffset(), totAssets + 1, rounding);\n    }\n\n    function _convertToAssetsTotals(\n        uint256 shares,\n        uint256 totSupply,\n        uint256 totAssets,\n        Math.Rounding rounding\n    ) private view returns (uint256) {\n        return shares.mulDiv(totAssets + 1, totSupply + 10 ** _decimalsOffset(), rounding);\n    }\n\n    function _nowSinceEpoch(uint48 epoch) private view returns (uint48) {\n        return _timestampSinceEpoch(Time.timestamp(), epoch);\n    }\n\n    function _timestampSinceEpoch(uint48 timestamp, uint48 epoch) private pure returns (uint48) {\n        return timestamp - epoch;\n    }\n\n    function _addPeriodConfiguration(uint48 newEpoch, uint48 newDuration) private {\n        if (newDuration < SMALLEST_PERIOD_DURATION || newDuration % SMALLEST_PERIOD_DURATION != 0)\n            revert InvalidPeriodConfigurationDuration();\n\n        uint256 startingPeriod;\n        if (periodConfigurations.length > 0) {\n            PeriodConfiguration memory currentPC = currentPeriodConfiguration();\n            if (currentPC.epoch != periodConfigurations[periodConfigurations.length - 1].epoch)\n                revert CurrentPeriodConfigurationNotLast();\n            if (newEpoch < nextPeriodEnd() || (newEpoch - currentPC.epoch) % currentPC.duration != 0)\n                revert InvalidPeriodConfigurationEpoch();\n\n            startingPeriod = currentPC.startingPeriod + (newEpoch - currentPC.epoch) / currentPC.duration;\n        } else {\n            if (newEpoch < Time.timestamp()) revert InvalidPeriodConfigurationEpoch();\n\n            startingPeriod = 0;\n        }\n\n        PeriodConfiguration memory newPeriod = PeriodConfiguration({\n            epoch: newEpoch,\n            duration: newDuration,\n            startingPeriod: startingPeriod\n        });\n        periodConfigurations.push(newPeriod);\n        emit PeriodConfigurationAdded(newPeriod);\n    }\n}\n"},{"file_path":"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/ERC4626.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {IERC20Metadata} from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport {ERC20Upgradeable} from \"../ERC20Upgradeable.sol\";\nimport {SafeERC20} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {IERC4626} from \"@openzeppelin/contracts/interfaces/IERC4626.sol\";\nimport {Math} from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport {Initializable} from \"../../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of the ERC-4626 \"Tokenized Vault Standard\" as defined in\n * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].\n *\n * This extension allows the minting and burning of \"shares\" (represented using the ERC-20 inheritance) in exchange for\n * underlying \"assets\" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends\n * the ERC-20 standard. Any additional extensions included along it would affect the \"shares\" token represented by this\n * contract and not the \"assets\" token which is an independent contract.\n *\n * [CAUTION]\n * ====\n * In empty (or nearly empty) ERC-4626 vaults, deposits are at high risk of being stolen through frontrunning\n * with a \"donation\" to the vault that inflates the price of a share. This is variously known as a donation or inflation\n * attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial\n * deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may\n * similarly be affected by slippage. Users can protect against this attack as well as unexpected slippage in general by\n * verifying the amount received is as expected, using a wrapper that performs these checks such as\n * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router].\n *\n * Since v4.9, this implementation introduces configurable virtual assets and shares to help developers mitigate that risk.\n * The `_decimalsOffset()` corresponds to an offset in the decimal representation between the underlying asset's decimals\n * and the vault decimals. This offset also determines the rate of virtual shares to virtual assets in the vault, which\n * itself determines the initial exchange rate. While not fully preventing the attack, analysis shows that the default\n * offset (0) makes it non-profitable even if an attacker is able to capture value from multiple user deposits, as a result\n * of the value being captured by the virtual shares (out of the attacker's donation) matching the attacker's expected gains.\n * With a larger offset, the attack becomes orders of magnitude more expensive than it is profitable. More details about the\n * underlying math can be found xref:ROOT:erc4626.adoc#inflation-attack[here].\n *\n * The drawback of this approach is that the virtual shares do capture (a very small) part of the value being accrued\n * to the vault. Also, if the vault experiences losses, the users try to exit the vault, the virtual shares and assets\n * will cause the first user to exit to experience reduced losses in detriment to the last users that will experience\n * bigger losses. Developers willing to revert back to the pre-v4.9 behavior just need to override the\n * `_convertToShares` and `_convertToAssets` functions.\n *\n * To learn more, check out our xref:ROOT:erc4626.adoc[ERC-4626 guide].\n * ====\n */\nabstract contract ERC4626Upgradeable is Initializable, ERC20Upgradeable, IERC4626 {\n    using Math for uint256;\n\n    /// @custom:storage-location erc7201:openzeppelin.storage.ERC4626\n    struct ERC4626Storage {\n        IERC20 _asset;\n        uint8 _underlyingDecimals;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.ERC4626\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant ERC4626StorageLocation = 0x0773e532dfede91f04b12a73d3d2acd361424f41f76b4fb79f090161e36b4e00;\n\n    function _getERC4626Storage() private pure returns (ERC4626Storage storage $) {\n        assembly {\n            $.slot := ERC4626StorageLocation\n        }\n    }\n\n    /**\n     * @dev Attempted to deposit more assets than the max amount for `receiver`.\n     */\n    error ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max);\n\n    /**\n     * @dev Attempted to mint more shares than the max amount for `receiver`.\n     */\n    error ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max);\n\n    /**\n     * @dev Attempted to withdraw more assets than the max amount for `receiver`.\n     */\n    error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max);\n\n    /**\n     * @dev Attempted to redeem more shares than the max amount for `receiver`.\n     */\n    error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max);\n\n    /**\n     * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC-20 or ERC-777).\n     */\n    function __ERC4626_init(IERC20 asset_) internal onlyInitializing {\n        __ERC4626_init_unchained(asset_);\n    }\n\n    function __ERC4626_init_unchained(IERC20 asset_) internal onlyInitializing {\n        ERC4626Storage storage $ = _getERC4626Storage();\n        (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_);\n        $._underlyingDecimals = success ? assetDecimals : 18;\n        $._asset = asset_;\n    }\n\n    /**\n     * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way.\n     */\n    function _tryGetAssetDecimals(IERC20 asset_) private view returns (bool ok, uint8 assetDecimals) {\n        (bool success, bytes memory encodedDecimals) = address(asset_).staticcall(\n            abi.encodeCall(IERC20Metadata.decimals, ())\n        );\n        if (success && encodedDecimals.length >= 32) {\n            uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256));\n            if (returnedDecimals <= type(uint8).max) {\n                return (true, uint8(returnedDecimals));\n            }\n        }\n        return (false, 0);\n    }\n\n    /**\n     * @dev Decimals are computed by adding the decimal offset on top of the underlying asset's decimals. This\n     * \"original\" value is cached during construction of the vault contract. If this read operation fails (e.g., the\n     * asset has not been created yet), a default of 18 is used to represent the underlying asset's decimals.\n     *\n     * See {IERC20Metadata-decimals}.\n     */\n    function decimals() public view virtual override(IERC20Metadata, ERC20Upgradeable) returns (uint8) {\n        ERC4626Storage storage $ = _getERC4626Storage();\n        return $._underlyingDecimals + _decimalsOffset();\n    }\n\n    /// @inheritdoc IERC4626\n    function asset() public view virtual returns (address) {\n        ERC4626Storage storage $ = _getERC4626Storage();\n        return address($._asset);\n    }\n\n    /// @inheritdoc IERC4626\n    function totalAssets() public view virtual returns (uint256) {\n        return IERC20(asset()).balanceOf(address(this));\n    }\n\n    /// @inheritdoc IERC4626\n    function convertToShares(uint256 assets) public view virtual returns (uint256) {\n        return _convertToShares(assets, Math.Rounding.Floor);\n    }\n\n    /// @inheritdoc IERC4626\n    function convertToAssets(uint256 shares) public view virtual returns (uint256) {\n        return _convertToAssets(shares, Math.Rounding.Floor);\n    }\n\n    /// @inheritdoc IERC4626\n    function maxDeposit(address) public view virtual returns (uint256) {\n        return type(uint256).max;\n    }\n\n    /// @inheritdoc IERC4626\n    function maxMint(address) public view virtual returns (uint256) {\n        return type(uint256).max;\n    }\n\n    /// @inheritdoc IERC4626\n    function maxWithdraw(address owner) public view virtual returns (uint256) {\n        return _convertToAssets(balanceOf(owner), Math.Rounding.Floor);\n    }\n\n    /// @inheritdoc IERC4626\n    function maxRedeem(address owner) public view virtual returns (uint256) {\n        return balanceOf(owner);\n    }\n\n    /// @inheritdoc IERC4626\n    function previewDeposit(uint256 assets) public view virtual returns (uint256) {\n        return _convertToShares(assets, Math.Rounding.Floor);\n    }\n\n    /// @inheritdoc IERC4626\n    function previewMint(uint256 shares) public view virtual returns (uint256) {\n        return _convertToAssets(shares, Math.Rounding.Ceil);\n    }\n\n    /// @inheritdoc IERC4626\n    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {\n        return _convertToShares(assets, Math.Rounding.Ceil);\n    }\n\n    /// @inheritdoc IERC4626\n    function previewRedeem(uint256 shares) public view virtual returns (uint256) {\n        return _convertToAssets(shares, Math.Rounding.Floor);\n    }\n\n    /// @inheritdoc IERC4626\n    function deposit(uint256 assets, address receiver) public virtual returns (uint256) {\n        uint256 maxAssets = maxDeposit(receiver);\n        if (assets > maxAssets) {\n            revert ERC4626ExceededMaxDeposit(receiver, assets, maxAssets);\n        }\n\n        uint256 shares = previewDeposit(assets);\n        _deposit(_msgSender(), receiver, assets, shares);\n\n        return shares;\n    }\n\n    /// @inheritdoc IERC4626\n    function mint(uint256 shares, address receiver) public virtual returns (uint256) {\n        uint256 maxShares = maxMint(receiver);\n        if (shares > maxShares) {\n            revert ERC4626ExceededMaxMint(receiver, shares, maxShares);\n        }\n\n        uint256 assets = previewMint(shares);\n        _deposit(_msgSender(), receiver, assets, shares);\n\n        return assets;\n    }\n\n    /// @inheritdoc IERC4626\n    function withdraw(uint256 assets, address receiver, address owner) public virtual returns (uint256) {\n        uint256 maxAssets = maxWithdraw(owner);\n        if (assets > maxAssets) {\n            revert ERC4626ExceededMaxWithdraw(owner, assets, maxAssets);\n        }\n\n        uint256 shares = previewWithdraw(assets);\n        _withdraw(_msgSender(), receiver, owner, assets, shares);\n\n        return shares;\n    }\n\n    /// @inheritdoc IERC4626\n    function redeem(uint256 shares, address receiver, address owner) public virtual returns (uint256) {\n        uint256 maxShares = maxRedeem(owner);\n        if (shares > maxShares) {\n            revert ERC4626ExceededMaxRedeem(owner, shares, maxShares);\n        }\n\n        uint256 assets = previewRedeem(shares);\n        _withdraw(_msgSender(), receiver, owner, assets, shares);\n\n        return assets;\n    }\n\n    /**\n     * @dev Internal conversion function (from assets to shares) with support for rounding direction.\n     */\n    function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) {\n        return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding);\n    }\n\n    /**\n     * @dev Internal conversion function (from shares to assets) with support for rounding direction.\n     */\n    function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) {\n        return shares.mulDiv(totalAssets() + 1, totalSupply() + 10 ** _decimalsOffset(), rounding);\n    }\n\n    /**\n     * @dev Deposit/mint common workflow.\n     */\n    function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual {\n        // If asset() is ERC-777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the\n        // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer,\n        // calls the vault, which is assumed not malicious.\n        //\n        // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the\n        // assets are transferred and before the shares are minted, which is a valid state.\n        // slither-disable-next-line reentrancy-no-eth\n        SafeERC20.safeTransferFrom(IERC20(asset()), caller, address(this), assets);\n        _mint(receiver, shares);\n\n        emit Deposit(caller, receiver, assets, shares);\n    }\n\n    /**\n     * @dev Withdraw/redeem common workflow.\n     */\n    function _withdraw(\n        address caller,\n        address receiver,\n        address owner,\n        uint256 assets,\n        uint256 shares\n    ) internal virtual {\n        if (caller != owner) {\n            _spendAllowance(owner, caller, shares);\n        }\n\n        // If asset() is ERC-777, `transfer` can trigger a reentrancy AFTER the transfer happens through the\n        // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer,\n        // calls the vault, which is assumed not malicious.\n        //\n        // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the\n        // shares are burned and after the assets are transferred, which is a valid state.\n        _burn(owner, shares);\n        SafeERC20.safeTransfer(IERC20(asset()), receiver, assets);\n\n        emit Withdraw(caller, receiver, owner, assets, shares);\n    }\n\n    function _decimalsOffset() internal view virtual returns (uint8) {\n        return 0;\n    }\n}\n"},{"file_path":"contracts/test/MockIncidentManagerVault.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.28;\n\nimport {IFirelightVault} from \"../core/interfaces/IFirelightVault.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @dev Minimal IFirelightVault mock for IncidentManager tests.\n *\n *      IncidentManager consumes: `asset()` (once in initialize), `periodAtTimestamp`, `currentPeriod`\n *      and `payout`. All are settable so tests can drive incident periods, expiry checks and\n *      the amount the vault reports as paid. Everything else is stubbed to safe defaults.\n */\ncontract MockIncidentManagerVault is IFirelightVault {\n    uint256 private _currentPeriod;\n    address private _asset;\n    mapping(uint48 => uint256) private _periodAtTimestamp;\n\n    // When set, `payout` returns this fixed value; otherwise it echoes the requested amount.\n    bool private _payoutReturnIsSet;\n    uint256 private _payoutReturn;\n\n    // -------------------------------------------------------------------------\n    // Test setters\n    // -------------------------------------------------------------------------\n\n    function setCurrentPeriod(uint256 p) external { _currentPeriod = p; }\n    function setAsset(address a) external { _asset = a; }\n    function setPeriodAtTimestamp(uint48 timestamp, uint256 period) external {\n        _periodAtTimestamp[timestamp] = period;\n    }\n    function isPeriodInPayoutWindow(uint256 period) external view override returns (bool) {\n        return _currentPeriod == period || _currentPeriod == period + 1;\n    }\n    function setPayoutReturn(uint256 r) external { _payoutReturn = r; _payoutReturnIsSet = true; }\n\n    // -------------------------------------------------------------------------\n    // Functions exercised by IncidentManager\n    // -------------------------------------------------------------------------\n\n    function currentPeriod() external view override returns (uint256) { return _currentPeriod; }\n    function asset() external view override returns (address) { return _asset; }\n\n    function periodAtTimestamp(uint48 timestamp) external view override returns (uint256) {\n        return _periodAtTimestamp[timestamp];\n    }\n\n    function payout(address, uint256 amount, uint48) external view override returns (uint256 paidAmount) {\n        if (_payoutReturnIsSet) return _payoutReturn;\n        return amount;\n    }\n\n    // -------------------------------------------------------------------------\n    // IERC20 / IERC20Metadata stubs\n    // -------------------------------------------------------------------------\n\n    function name() external pure override returns (string memory) { return \"\"; }\n    function symbol() external pure override returns (string memory) { return \"\"; }\n    function decimals() external pure override returns (uint8) { return 18; }\n    function totalSupply() external pure override returns (uint256) { return 0; }\n    function balanceOf(address) external pure override returns (uint256) { return 0; }\n    function transfer(address, uint256) external pure override returns (bool) { return false; }\n    function allowance(address, address) external pure override returns (uint256) { return 0; }\n    function approve(address, uint256) external pure override returns (bool) { return false; }\n    function transferFrom(address, address, uint256) external pure override returns (bool) { return false; }\n\n    // -------------------------------------------------------------------------\n    // IERC4626 stubs\n    // -------------------------------------------------------------------------\n\n    function convertToShares(uint256) external pure override returns (uint256) { return 0; }\n    function convertToAssets(uint256) external pure override returns (uint256) { return 0; }\n    function maxDeposit(address) external pure override returns (uint256) { return 0; }\n    function previewDeposit(uint256) external pure override returns (uint256) { return 0; }\n    function deposit(uint256, address) external pure override returns (uint256) { return 0; }\n    function maxMint(address) external pure override returns (uint256) { return 0; }\n    function previewMint(uint256) external pure override returns (uint256) { return 0; }\n    function mint(uint256, address) external pure override returns (uint256) { return 0; }\n    function maxWithdraw(address) external pure override returns (uint256) { return 0; }\n    function previewWithdraw(uint256) external pure override returns (uint256) { return 0; }\n    function withdraw(uint256, address, address) external pure override returns (uint256) { return 0; }\n    function maxRedeem(address) external pure override returns (uint256) { return 0; }\n    function previewRedeem(uint256) external pure override returns (uint256) { return 0; }\n    function redeem(uint256, address, address) external pure override returns (uint256) { return 0; }\n\n    // -------------------------------------------------------------------------\n    // IAccessControl stubs\n    // -------------------------------------------------------------------------\n\n    function hasRole(bytes32, address) external pure override returns (bool) { return false; }\n    function getRoleAdmin(bytes32) external pure override returns (bytes32) { return bytes32(0); }\n    function grantRole(bytes32, address) external override {}\n    function revokeRole(bytes32, address) external override {}\n    function renounceRole(bytes32, address) external override {}\n\n    // -------------------------------------------------------------------------\n    // Role / constant getters\n    // -------------------------------------------------------------------------\n\n    function DEPOSIT_LIMIT_UPDATE_ROLE() external pure override returns (bytes32) { return bytes32(0); }\n    function RESCUER_ROLE() external pure override returns (bytes32) { return bytes32(0); }\n    function BLOCKLIST_ROLE() external pure override returns (bytes32) { return bytes32(0); }\n    function PAUSE_ROLE() external pure override returns (bytes32) { return bytes32(0); }\n    function PERIOD_CONFIGURATION_UPDATE_ROLE() external pure override returns (bytes32) { return bytes32(0); }\n    function SMALLEST_PERIOD_DURATION() external pure override returns (uint48) { return 0; }\n    function MAX_PERIOD_DURATION() external pure override returns (uint48) { return 0; }\n    function PAYOUT_ALLOWLIST_ROLE() external pure override returns (bytes32) { return bytes32(0); }\n    function PAYOUT_ROLE() external pure override returns (bytes32) { return bytes32(0); }\n    function INCIDENT_ROLE() external pure override returns (bytes32) { return bytes32(0); }\n    function CHECKPOINT_ROLE() external pure override returns (bytes32) { return bytes32(0); }\n\n    // -------------------------------------------------------------------------\n    // Storage-getter stubs\n    // -------------------------------------------------------------------------\n\n    function depositLimit() external pure override returns (uint256) { return 0; }\n    function contractVersion() external pure override returns (uint256) { return 0; }\n    function pendingWithdrawAssets() external pure override returns (uint256) { return 0; }\n\n    function periodConfigurations(uint256) external pure override returns (uint48, uint48, uint256) {\n        return (0, 0, 0);\n    }\n\n    function withdrawShares(uint256) external pure override returns (uint256) { return 0; }\n    function withdrawAssets(uint256) external pure override returns (uint256) { return 0; }\n    function withdrawSharesOf(uint256, address) external pure override returns (uint256) { return 0; }\n    function isWithdrawClaimed(uint256, address) external pure override returns (bool) { return false; }\n    function isBlocklisted(address) external pure override returns (bool) { return false; }\n    function isPayoutAllowlisted(address) external pure override returns (bool) { return false; }\n\n    // -------------------------------------------------------------------------\n    // Initialization stub\n    // -------------------------------------------------------------------------\n\n    function initialize(IERC20, string memory, string memory, bytes memory) external override {}\n\n    // -------------------------------------------------------------------------\n    // Period query stubs\n    // -------------------------------------------------------------------------\n\n    function periodConfigurationAtTimestamp(uint48) external pure override returns (PeriodConfiguration memory pc) {\n        return pc;\n    }\n\n    function periodConfigurationAtNumber(uint256) external pure override returns (PeriodConfiguration memory pc) {\n        return pc;\n    }\n\n    function currentPeriodConfiguration() external pure override returns (PeriodConfiguration memory pc) {\n        return pc;\n    }\n\n    function currentPeriodStart() external pure override returns (uint48) { return 0; }\n    function currentPeriodEnd() external pure override returns (uint48) { return 0; }\n    function nextPeriodEnd() external pure override returns (uint48) { return 0; }\n    function periodConfigurationsLength() external pure override returns (uint256) { return 0; }\n\n    function totalAssets() external pure override returns (uint256) { return 0; }\n\n    // -------------------------------------------------------------------------\n    // Historical query stubs\n    // -------------------------------------------------------------------------\n\n    function balanceOfAt(address, uint48) external pure override returns (uint256) { return 0; }\n    function totalSupplyAt(uint48) external pure override returns (uint256) { return 0; }\n    function totalAssetsAt(uint48) external pure override returns (uint256) { return 0; }\n    function withdrawalsOf(uint256, address) external pure override returns (uint256) { return 0; }\n\n    // -------------------------------------------------------------------------\n    // Withdrawal lifecycle stub\n    // -------------------------------------------------------------------------\n\n    function claimWithdraw(uint256) external pure override returns (uint256) { return 0; }\n\n    // -------------------------------------------------------------------------\n    // Pausing stubs\n    // -------------------------------------------------------------------------\n\n    function paused() external pure override returns (bool) { return false; }\n    function pause() external override {}\n    function unpause() external override {}\n\n    // -------------------------------------------------------------------------\n    // Admin / configuration stubs\n    // -------------------------------------------------------------------------\n\n    function updateDepositLimit(uint256) external override {}\n    function addPeriodConfiguration(uint48, uint48) external override {}\n    function checkpointTotalAssets() external override {}\n\n    // -------------------------------------------------------------------------\n    // Blocklist stubs\n    // -------------------------------------------------------------------------\n\n    function addToBlocklist(address) external override {}\n    function removeFromBlocklist(address) external override {}\n\n    // -------------------------------------------------------------------------\n    // Payout allowlist stubs\n    // -------------------------------------------------------------------------\n\n    function addToPayoutAllowlist(address) external override {}\n    function removeFromPayoutAllowlist(address) external override {}\n\n    // -------------------------------------------------------------------------\n    // Rescue stubs\n    // -------------------------------------------------------------------------\n\n    function rescueSharesFromBlocklisted(address, address) external override {}\n    function rescueWithdrawFromBlocklisted(address, address, uint256[] calldata) external override {}\n\n\n    // -------------------------------------------------------------------------\n    // Incident stubs\n    // -------------------------------------------------------------------------\n    \n    function hasActiveIncident(uint256) external pure override returns (bool) {return false;}\n    function setActiveIncident(uint256, bool) external override {}\n\n}\n"},{"file_path":"@openzeppelin/contracts/utils/Errors.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Collection of common custom errors used in multiple contracts\n *\n * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.\n * It is recommended to avoid relying on the error API for critical functionality.\n *\n * _Available since v5.1._\n */\nlibrary Errors {\n    /**\n     * @dev The ETH balance of the account is not enough to perform the operation.\n     */\n    error InsufficientBalance(uint256 balance, uint256 needed);\n\n    /**\n     * @dev A call to an address target failed. The target may have reverted.\n     */\n    error FailedCall();\n\n    /**\n     * @dev The deployment failed.\n     */\n    error FailedDeployment();\n\n    /**\n     * @dev A necessary precompile is missing.\n     */\n    error MissingPrecompile(address);\n}\n"},{"file_path":"contracts/imports/Imports.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.28;\n\n// Force Hardhat to compile external contracts that we deploy by short name\n// (via ethers.getContractFactory / getContractAt). Without these imports the\n// artifacts are not produced and deploy/verify/storage-check scripts fail.\n\nimport {TimelockController} from \"@openzeppelin/contracts/governance/TimelockController.sol\";\n"},{"file_path":"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC20} from \"../IERC20.sol\";\nimport {IERC1363} from \"../../../interfaces/IERC1363.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC-20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n    /**\n     * @dev An operation with an ERC-20 token failed.\n     */\n    error SafeERC20FailedOperation(address token);\n\n    /**\n     * @dev Indicates a failed `decreaseAllowance` request.\n     */\n    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);\n\n    /**\n     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeTransfer(IERC20 token, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));\n    }\n\n    /**\n     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\n     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\n     */\n    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));\n    }\n\n    /**\n     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     *\n     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the \"client\"\n     * smart contract uses ERC-7674 to set temporary allowances, then the \"client\" smart contract should avoid using\n     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract\n     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.\n     */\n    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 oldAllowance = token.allowance(address(this), spender);\n        forceApprove(token, spender, oldAllowance + value);\n    }\n\n    /**\n     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no\n     * value, non-reverting calls are assumed to be successful.\n     *\n     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the \"client\"\n     * smart contract uses ERC-7674 to set temporary allowances, then the \"client\" smart contract should avoid using\n     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract\n     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.\n     */\n    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {\n        unchecked {\n            uint256 currentAllowance = token.allowance(address(this), spender);\n            if (currentAllowance < requestedDecrease) {\n                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);\n            }\n            forceApprove(token, spender, currentAllowance - requestedDecrease);\n        }\n    }\n\n    /**\n     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\n     * to be set to zero before setting it to a non-zero value, such as USDT.\n     *\n     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function\n     * only sets the \"standard\" allowance. Any temporary allowance will remain active, in addition to the value being\n     * set here.\n     */\n    function forceApprove(IERC20 token, address spender, uint256 value) internal {\n        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));\n\n        if (!_callOptionalReturnBool(token, approvalCall)) {\n            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));\n            _callOptionalReturn(token, approvalCall);\n        }\n    }\n\n    /**\n     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no\n     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when\n     * targeting contracts.\n     *\n     * Reverts if the returned value is other than `true`.\n     */\n    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {\n        if (to.code.length == 0) {\n            safeTransfer(token, to, value);\n        } else if (!token.transferAndCall(to, value, data)) {\n            revert SafeERC20FailedOperation(address(token));\n        }\n    }\n\n    /**\n     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target\n     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when\n     * targeting contracts.\n     *\n     * Reverts if the returned value is other than `true`.\n     */\n    function transferFromAndCallRelaxed(\n        IERC1363 token,\n        address from,\n        address to,\n        uint256 value,\n        bytes memory data\n    ) internal {\n        if (to.code.length == 0) {\n            safeTransferFrom(token, from, to, value);\n        } else if (!token.transferFromAndCall(from, to, value, data)) {\n            revert SafeERC20FailedOperation(address(token));\n        }\n    }\n\n    /**\n     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no\n     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when\n     * targeting contracts.\n     *\n     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.\n     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}\n     * once without retrying, and relies on the returned value to be true.\n     *\n     * Reverts if the returned value is other than `true`.\n     */\n    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {\n        if (to.code.length == 0) {\n            forceApprove(token, to, value);\n        } else if (!token.approveAndCall(to, value, data)) {\n            revert SafeERC20FailedOperation(address(token));\n        }\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     *\n     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.\n     */\n    function _callOptionalReturn(IERC20 token, bytes memory data) private {\n        uint256 returnSize;\n        uint256 returnValue;\n        assembly (\"memory-safe\") {\n            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)\n            // bubble errors\n            if iszero(success) {\n                let ptr := mload(0x40)\n                returndatacopy(ptr, 0, returndatasize())\n                revert(ptr, returndatasize())\n            }\n            returnSize := returndatasize()\n            returnValue := mload(0)\n        }\n\n        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {\n            revert SafeERC20FailedOperation(address(token));\n        }\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     *\n     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.\n     */\n    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {\n        bool success;\n        uint256 returnSize;\n        uint256 returnValue;\n        assembly (\"memory-safe\") {\n            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)\n            returnSize := returndatasize()\n            returnValue := mload(0)\n        }\n        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Enumerable.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC721} from \"../IERC721.sol\";\n\n/**\n * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension\n * @dev See https://eips.ethereum.org/EIPS/eip-721\n */\ninterface IERC721Enumerable is IERC721 {\n    /**\n     * @dev Returns the total amount of tokens stored by the contract.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.\n     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.\n     */\n    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);\n\n    /**\n     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.\n     * Use along with {totalSupply} to enumerate all tokens.\n     */\n    function tokenByIndex(uint256 index) external view returns (uint256);\n}\n"},{"file_path":"contracts/test/FirelightVaultUpgradeTest.sol","source_code":"/* SPDX-License-Identifier: UNLICENSED */\n\npragma solidity 0.8.28;\n\nimport {FirelightVault} from  \"../core/FirelightVault.sol\";\nimport {Checkpoints} from \"../core/lib/Checkpoints.sol\";\nimport {SafeERC20} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract FirelightVaultUpgradeTest is FirelightVault {\n    using Checkpoints for Checkpoints.Trace256;\n    using SafeERC20 for IERC20;\n\n    function updateVersion(uint256 version) public {\n        contractVersion = version;\n    }\n}"},{"file_path":"contracts/core/FirelightVault.sol","source_code":"// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.28;\n\nimport {ERC4626Upgradeable} from \"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol\";\nimport {ERC20Upgradeable} from \"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {AccessControlUpgradeable} from \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport {PausableUpgradeable} from \"@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol\";\nimport {ReentrancyGuardUpgradeable} from \"@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol\";\nimport {SafeERC20} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {Math} from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport {Time} from \"@openzeppelin/contracts/utils/types/Time.sol\";\n\nimport {FirelightVaultStorage} from \"./FirelightVaultStorage.sol\";\nimport {Checkpoints} from \"./lib/Checkpoints.sol\";\n\n/**\n * @title FirelightVault\n * @notice Upgradeable ERC4626-compatible vault with delayed withdrawals.\n *\n * @dev FirelightVault is an ERC4626 vault that intentionally deviates from the standard.\n * It overrides `withdraw` and `redeem` to implement delayed withdrawals.\n * Instead of transferring assets immediately, these functions create a withdrawal request,\n * which must be completed later via `claimWithdraw` after a set delay.\n *\n * The standard `Withdraw` event is not emitted. Instead, `WithdrawRequest` and `CompleteWithdraw`\n * are used to track the withdrawal process.\n *\n * Off-chain and on-chain tools must account for this custom flow and event structure.\n *\n * @custom:security-contact securityreport@firelight.finance\n */\ncontract FirelightVault is\n    FirelightVaultStorage,\n    ERC4626Upgradeable,\n    AccessControlUpgradeable,\n    PausableUpgradeable,\n    ReentrancyGuardUpgradeable\n{\n    using Checkpoints for Checkpoints.Trace256;\n    using SafeERC20 for IERC20;\n    using Math for uint256;\n\n    /**\n     * @notice Initial parameters needed for the vault's deployment.\n     * @param defaultAdmin Vault's admin that grants and revokes roles.\n     * @param limitUpdater Address assigned the DEPOSIT_LIMIT_UPDATE_ROLE at initialization.\n     * @param blocklister Address assigned the BLOCKLIST_ROLE at initialization.\n     * @param pauser Address assigned the PAUSE_ROLE at initialization.\n     * @param periodConfigurationUpdater Address assigned the PERIOD_CONFIGURATION_UPDATE_ROLE at initialization.\n     * @param rescuer Address assigned the RESCUER_ROLE at initialization.\n     * @param depositLimit Initial total deposit limit.\n     * @param periodConfigurationDuration Initial period duration of the vault.\n     */\n    struct InitParams {\n        address defaultAdmin;\n        address limitUpdater;\n        address blocklister;\n        address pauser;\n        address periodConfigurationUpdater;\n        address rescuer;\n        uint256 depositLimit;\n        uint48 periodConfigurationDuration;\n    }\n\n    /**\n     * @notice Emitted when the vault's deposit limit is updated.\n     * @param limit The new maximum amount of assets allowed in the vault.\n     */\n    event DepositLimitUpdated(uint256 limit);\n\n    /**\n     * @notice Emitted when a new periodConfiguration is added.\n     * @param periodConfiguration The details of the newly added periodConfiguration.\n     */\n    event PeriodConfigurationAdded(PeriodConfiguration periodConfiguration);\n\n    /**\n     * @notice Emitted when a withdrawal request is created by a user.\n     * @param sender The caller who initiated the withdrawal request.\n     * @param receiver The address that will receive the assets in the next period.\n     * @param owner The address whose shares are being redeemed, using allowance.\n     * @param period The period when the withdrawal will be available.\n     * @param assets The amount of assets to be withdrawn.\n     * @param shares The number of shares burned for the withdrawal.\n     */\n    event WithdrawRequest(\n        address indexed sender,\n        address indexed receiver,\n        address indexed owner,\n        uint256 period,\n        uint256 assets,\n        uint256 shares\n    );\n\n    /**\n     * @notice Emitted when a user successfully claims a withdrawal for a given period.\n     * @param receiver The address that received the withdrawn assets.\n     * @param assets The amount of assets withdrawn.\n     * @param period The period for which the withdrawal was claimed.\n     */\n    event CompleteWithdraw(address indexed receiver, uint256 assets, uint256 period);\n\n    /**\n     * @notice Emitted when a user with RESCUER_ROLE successfully rescues shares from a blocklisted address.\n     * @param from The blocklisted address.\n     * @param to The beneficiary of the rescued shares.\n     * @param rescuedShares The amount of shares rescued.\n     */\n    event SharesRescuedFromBlocklisted(address from, address to, uint256 rescuedShares);\n\n    /**\n     * @notice Emitted when a user with RESCUER_ROLE successfully rescues pending withdrawals\n     * from a blocklisted address.\n     * @param from The blocklisted address.\n     * @param to The beneficiary of the rescued withdrawals.\n     * @param periods The array of periods rescued.\n     * @param rescuedShares The array of pending shares from withdrawals rescued for each period.\n     */\n    event WithdrawRescuedFromBlocklisted(address from, address to, uint256[] periods, uint256[] rescuedShares);\n\n    /**\n     * @notice Emitted when an incident payout is processed, including when no assets can be paid.\n     * @param to The allowlisted payout receiver.\n     * @param requestedAmount The requested payout amount, in vault asset units.\n     * @param paidAmount The amount actually transferred, which may be less than requested or zero.\n     * @param captureTimestamp The incident capture timestamp that determines the covered period and payout window.\n     */\n    event PayoutExecuted(address indexed to, uint256 requestedAmount, uint256 paidAmount, uint48 captureTimestamp);\n\n    /**\n     * @notice Emitted when a period's active incident flag is set.\n     * @param period The period whose active incident flag was set.\n     * @param active Whether the period has an active incident.\n     */\n    event ActiveIncidentUpdated(uint256 indexed period, bool active);\n\n    /**\n     * @notice Emitted when an address is added to the blocklist.\n     * @param account The blocklisted address.\n     */\n    event AddedToBlocklist(address indexed account);\n\n    /**\n     * @notice Emitted when an address is removed from the blocklist.\n     * @param account The address removed from the blocklist.\n     */\n    event RemovedFromBlocklist(address indexed account);\n\n    /**\n     * @notice Emitted when an address is added to the payout allowlist.\n     * @param account The allowlisted address.\n     */\n    event AddedToPayoutAllowlist(address indexed account);\n\n    /**\n     * @notice Emitted when an address is removed from the payout allowlist.\n     * @param account The address removed from the payout allowlist.\n     */\n    event RemovedFromPayoutAllowlist(address indexed account);\n\n    /**\n     * @notice Emitted when a total-assets checkpoint is recorded outside deposit/withdraw flows.\n     * @param totalAssets The total assets recorded, excluding assets pending withdrawal.\n     */\n    event TotalAssetsCheckpointed(uint256 totalAssets);\n\n    /// @notice An account involved in the operation is blocklisted.\n    error BlocklistedAddress();\n\n    /// @notice The account is not blocklisted.\n    error NotBlocklistedAddress();\n\n    /// @notice A deposit or mint would increase total assets above the deposit limit.\n    error DepositLimitExceeded();\n\n    /// @notice The deposit limit is zero.\n    error InvalidDepositLimit();\n\n    /// @notice The period configuration epoch is too early or not aligned with a period boundary.\n    error InvalidPeriodConfigurationEpoch();\n\n    /// @notice The period duration is outside the allowed range or not a multiple of SMALLEST_PERIOD_DURATION.\n    error InvalidPeriodConfigurationDuration();\n\n    /// @notice The account has insufficient shares for the withdrawal or no shares to rescue.\n    error InsufficientShares();\n\n    /// @notice The underlying asset address is zero.\n    error InvalidAssetAddress();\n\n    /// @notice The default admin address is zero.\n    error InvalidAdminAddress();\n\n    /// @notice An account address required by the operation is zero.\n    error InvalidAddress();\n\n    /// @notice The requested amount is zero or converts to zero shares or assets.\n    error InvalidAmount();\n\n    /// @notice No period configuration applies to the query, or the withdrawal period has not ended.\n    error InvalidPeriod();\n\n    /// @notice A future period configuration is already scheduled.\n    error CurrentPeriodConfigurationNotLast();\n\n    /// @notice The array of withdrawal periods to rescue is empty.\n    error InvalidArrayLength();\n\n    /// @notice A withdrawal for the account and period has already been claimed.\n    /// @param period The period whose withdrawal has already been claimed.\n    error AlreadyClaimedPeriod(uint256 period);\n\n    /// @notice The account has no withdrawal shares or claimable assets for the period.\n    /// @param period The period with no withdrawal shares or claimable assets.\n    error NoWithdrawalAmount(uint256 period);\n\n    /// @notice The payout receiver is not on the payout allowlist.\n    error AccountNotAllowlisted();\n\n    /// @notice The capture period is neither the current period nor the previous period.\n    error InvalidCapturePeriod();\n\n    /// @notice An active incident in the current or previous period blocks deposits and mints.\n    error CurrentPeriodHasActiveIncident();\n\n    modifier notBlocklisted(address account) {\n        _requireNotBlocklisted(account);\n        _;\n    }\n\n    function _requireNotBlocklisted(address account) private view {\n        if (isBlocklisted[account]) {\n            revert BlocklistedAddress();\n        }\n    }\n\n    modifier onlyBlocklisted(address account) {\n        if (!isBlocklisted[account]) {\n            revert NotBlocklistedAddress();\n        }\n        _;\n    }\n\n    /**\n     * @notice Prevents unauthorized direct deployment via the constructor.\n     */\n    /// @custom:oz-upgrades-unsafe-allow constructor\n    constructor() {\n        _disableInitializers();\n    }\n\n    /**\n     * @notice Initializes the FirelightVault contract with given parameters\n     * @param _asset The underlying collateral ERC20 token.\n     * @param _name The name of the vault token.\n     * @param _symbol The symbol of the vault token.\n     * @param _initParams Initial parameters.\n     */\n    function initialize(\n        IERC20 _asset,\n        string memory _name,\n        string memory _symbol,\n        bytes memory _initParams\n    ) public initializer {\n        InitParams memory initParams = abi.decode(_initParams, (InitParams));\n        __ERC20_init(_name, _symbol);\n        __ERC4626_init(_asset);\n        __Pausable_init();\n        __ReentrancyGuard_init();\n        __AccessControl_init();\n\n        if (address(_asset) == address(0)) {\n            revert InvalidAssetAddress();\n        }\n\n        _updateDepositLimit(initParams.depositLimit);\n\n        if (initParams.periodConfigurationDuration == 0) {\n            revert InvalidPeriodConfigurationDuration();\n        }\n\n        if (initParams.defaultAdmin == address(0)) {\n            revert InvalidAdminAddress();\n        }\n\n        _addPeriodConfiguration(Time.timestamp(), initParams.periodConfigurationDuration);\n        contractVersion = 2;\n\n        _grantRole(DEFAULT_ADMIN_ROLE, initParams.defaultAdmin);\n\n        if (initParams.limitUpdater != address(0)) {\n            _grantRole(DEPOSIT_LIMIT_UPDATE_ROLE, initParams.limitUpdater);\n        }\n\n        if (initParams.blocklister != address(0)) {\n            _grantRole(BLOCKLIST_ROLE, initParams.blocklister);\n        }\n\n        if (initParams.pauser != address(0)) {\n            _grantRole(PAUSE_ROLE, initParams.pauser);\n        }\n\n        if (initParams.periodConfigurationUpdater != address(0)) {\n            _grantRole(PERIOD_CONFIGURATION_UPDATE_ROLE, initParams.periodConfigurationUpdater);\n        }\n\n        if (initParams.rescuer != address(0)) {\n            _grantRole(RESCUER_ROLE, initParams.rescuer);\n        }\n    }\n\n    /**\n     * @notice Intended to be called atomically during upgrade via `upgradeToAndCall`.\n     */\n    function initializeV2() external reinitializer(2) {\n        contractVersion = 2;\n    }\n\n    /**\n     * @notice Returns the period configuration corresponding to a given timestamp.\n     * @dev Return value may be unreliable if timestamp given is far away in the future\n     * @dev given that new period configurations can be added after nextPeriodEnd().\n     * @param timestamp The timestamp to find the period configuration for.\n     * @return The period configuration corresponding to the given timestamp.\n     */\n    function periodConfigurationAtTimestamp(uint48 timestamp) public view returns (PeriodConfiguration memory) {\n        uint256 length = periodConfigurations.length;\n        if (length == 0) revert InvalidPeriod();\n\n        PeriodConfiguration memory periodConfiguration;\n        for (uint256 i = 0; i < length; i++) {\n            if (timestamp < periodConfigurations[i].epoch)\n                break;\n            periodConfiguration = periodConfigurations[i];\n        }\n        if (periodConfiguration.epoch == 0) revert InvalidPeriod();\n        return periodConfiguration;\n    }\n\n    /**\n     * @notice Returns the period configuration corresponding to a given period number.\n     * @dev Return value may be unreliable if period number given is far away in the future\n     * @dev given that new period configurations can be added after nextPeriodEnd().\n     * @param periodNumber The period number to find the period configuration for.\n     * @return The period configuration corresponding to the given period number.\n     */\n    function periodConfigurationAtNumber(uint256 periodNumber) public view returns (PeriodConfiguration memory) {\n        uint256 length = periodConfigurations.length;\n        if (length == 0) revert InvalidPeriod();\n\n        PeriodConfiguration memory periodConfiguration;\n        for (uint256 i = 0; i < length; i++) {\n            if (periodNumber < periodConfigurations[i].startingPeriod)\n                break;\n            periodConfiguration = periodConfigurations[i];\n        }\n        if (periodConfiguration.epoch == 0) revert InvalidPeriod();\n        return periodConfiguration;\n    }\n\n    /**\n     * @notice Returns the period number for the timestamp given.\n     * @dev Return value may be unreliable if period number given is far away in the future\n     * @dev given that new period configurations can be added after nextPeriodEnd().\n     * @return The period number corresponding to the given timestamp.\n     */\n    function periodAtTimestamp(uint48 timestamp) public view returns (uint256) {\n        PeriodConfiguration memory periodConfiguration = periodConfigurationAtTimestamp(timestamp);\n        // solhint-disable-next-line max-line-length\n        return periodConfiguration.startingPeriod + _timestampSinceEpoch(timestamp, periodConfiguration.epoch) / periodConfiguration.duration;\n    }\n\n    /**\n     * @notice Returns the period configuration for the current period.\n     * @return The period configuration corresponding to the current period.\n     */\n    function currentPeriodConfiguration() public view returns (PeriodConfiguration memory) {\n        return periodConfigurationAtTimestamp(Time.timestamp());\n    }\n\n    /**\n     * @notice Returns the current active period.\n     * @return The current period number since contract deployment.\n     */\n    function currentPeriod() public view returns (uint256) {\n        return periodAtTimestamp(Time.timestamp());\n    }\n\n    /**\n     * @notice Returns the start timestamp of the current period.\n     * @return Timestamp of the current period start.\n     */\n    function currentPeriodStart() external view returns (uint48) {\n        PeriodConfiguration memory currentPC = currentPeriodConfiguration();\n        return currentPC.epoch + (_nowSinceEpoch(currentPC.epoch) / currentPC.duration) * currentPC.duration;\n    }\n\n    /**\n     * @notice Returns the end timestamp of the current period.\n     * @return Timestamp of the current period end.\n     */\n    function currentPeriodEnd() public view returns (uint48) {\n        PeriodConfiguration memory currentPC = currentPeriodConfiguration();\n        return currentPC.epoch + (_nowSinceEpoch(currentPC.epoch) / currentPC.duration + 1) * currentPC.duration;\n    }\n\n    /**\n     * @notice Returns the end timestamp of the period following the current period.\n     * @return Timestamp of the next period end.\n     */\n    function nextPeriodEnd() public view returns (uint48) {\n        uint48 currentEnd = currentPeriodEnd();\n        return currentEnd + periodConfigurationAtTimestamp(currentEnd).duration;\n    }\n\n    /**\n     * @notice Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,\n     * through a deposit call.\n     * @param receiver The address of the deposit receiver.\n     * @return amount Maximum amount of assets that can be deposited.\n     */\n    function maxDeposit(address receiver) public view override returns (uint256 amount) {\n        uint256 assets = totalAssets();\n        if (isBlocklisted[receiver] || paused() || _hasActiveIncident() || assets > depositLimit) {\n            return 0;\n        }\n\n        uint256 remaining = depositLimit - assets;\n        if (previewDeposit(remaining) == 0) return 0;\n        return remaining;\n    }\n\n    /**\n     * @notice Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.\n     * @param receiver The address of the mint receiver.\n     * @return amount Maximum amount of shares that can be minted.\n     */\n    function maxMint(address receiver) public view override returns (uint256 amount) {\n        return previewDeposit(maxDeposit(receiver));\n    }\n\n    /**\n     * @notice Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the\n     * Vault, through a withdraw call.\n     * @param owner The owner of the assets.\n     * @return amount Maximum amount of assets that can be withdrawn.\n     */\n    function maxWithdraw(address owner) public view override returns (uint256 amount) {\n        if (isBlocklisted[owner] || paused()) {\n            return 0;\n        } else {\n            return _convertToAssets(balanceOf(owner), Math.Rounding.Floor);\n        }\n    }\n\n    /**\n     * @notice Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,\n     * through a redeem call.\n     * @param owner The owner of the shares.\n     * @return amount Maximum amount of shares that can be redeemed.\n     */\n    function maxRedeem(address owner) public view override returns (uint256 amount) {\n        if (isBlocklisted[owner] || paused()) {\n            return 0;\n        } else {\n            uint256 shares = balanceOf(owner);\n            return _convertToAssets(shares, Math.Rounding.Floor) == 0 ? 0 : shares;\n        }\n    }\n\n    /**\n     * @notice Returns the total assets in the vault excluding those marked for withdrawal.\n     * @return The total assets held by the vault.\n     */\n    function totalAssets() public view override returns (uint256) {\n        return super.totalAssets() - pendingWithdrawAssets;\n    }\n\n    /**\n     * @notice Returns the effective total shares for `account` at a specific `timestamp`.\n     * @param account The address whose share balance is being queried.\n     * @param timestamp The point in time for which the balance is being checked.\n     * @return The shares owned by `account` at the specified time.\n     */\n    function balanceOfAt(address account, uint48 timestamp) external view returns (uint256) {\n        return _traceBalanceOf[account].upperLookupRecent(timestamp);\n    }\n\n    /**\n     * @notice Returns the total supply of shares at a specific `timestamp`.\n     * @param timestamp The point in time for which the total supply is being checked.\n     * @return The total shares in existence at the specified time.\n     */\n    function totalSupplyAt(uint48 timestamp) external view returns (uint256) {\n        return _traceTotalSupply.upperLookupRecent(timestamp);\n    }\n\n    /**\n     * @notice Returns the total underlying assets held by the vault at a specific `timestamp`, excluding any assets\n     * marked for withdrawal.\n     * @param timestamp The point in time for which the total assets are being checked.\n     * @return The total underlying assets held by the vault at the specified time.\n     */\n    function totalAssetsAt(uint48 timestamp) public view returns (uint256) {\n        return _traceTotalAssets.upperLookupRecent(timestamp);\n    }\n\n    /**\n     * @notice Returns the amount that was made withdrawable for the given period and account, whether claimed or not.\n     * @param period Period number to check.\n     * @param account Account address.\n     * @return Amount of assets claimable for that period.\n     */\n    function withdrawalsOf(uint256 period, address account) external view returns (uint256) {\n        return\n            _convertToAssetsTotals(\n                withdrawSharesOf[period][account],\n                withdrawShares[period],\n                withdrawAssets[period],\n                Math.Rounding.Floor\n            );\n    }\n\n    /**\n     * @notice Returns whether a period is inside the payout window.\n     * @param period Vault period to check.\n     * @return True if the period can still be used for payout.\n     */\n    function isPeriodInPayoutWindow(uint256 period) external view returns (bool) {\n        return _isPeriodInPayoutWindow(period, currentPeriod());\n    }\n\n    /**\n     * @notice Returns the length of the periodConfigurations array.\n     * @return Length of the periodConfigurations array.\n     */\n    function periodConfigurationsLength() public view returns (uint256) {\n        return periodConfigurations.length;\n    }\n\n    /**\n     * @notice Pauses the contract. Requires PAUSE_ROLE.\n     */\n    function pause() external onlyRole(PAUSE_ROLE) {\n        _pause();\n    }\n\n    /**\n     * @notice Unpauses the contract. Requires PAUSE_ROLE.\n     */\n    function unpause() external onlyRole(PAUSE_ROLE) {\n        _unpause();\n    }\n\n    /**\n     * @notice Sets whether deposits are blocked for a period due to an active incident.\n     */\n    function setActiveIncident(uint256 period, bool active) external onlyRole(INCIDENT_ROLE) {\n        hasActiveIncident[period] = active;\n        emit ActiveIncidentUpdated(period, active);\n    }\n\n    /**\n     * @notice Records a checkpoint of the current total assets. Requires CHECKPOINT_ROLE.\n     * @dev Assets forwarded directly to the vault (e.g. reward distribution) do not trigger\n     * the deposit/withdraw checkpoints, so historical `totalAssetsAt` lookups would miss them\n     * until the next flow. The reward distributor calls this atomically after forwarding so\n     * period-start snapshots include the forwarded assets. Restricted to a role because\n     * checkpoint growth on a low-fee chain would otherwise be spammable.\n     */\n    function checkpointTotalAssets() external onlyRole(CHECKPOINT_ROLE) {\n        uint256 assets = totalAssets();\n        _traceTotalAssets.push(Time.timestamp(), assets);\n        emit TotalAssetsCheckpointed(assets);\n    }\n\n    /**\n     * @notice Updates the maximum deposit limit for the vault. Requires DEPOSIT_LIMIT_UPDATE_ROLE.\n     * @param newLimit The new deposit limit.\n     */\n    function updateDepositLimit(uint256 newLimit) external onlyRole(DEPOSIT_LIMIT_UPDATE_ROLE) {\n        _updateDepositLimit(newLimit);\n    }\n\n    /**\n     * @notice Adds a period configuration. Requires PERIOD_CONFIGURATION_UPDATE_ROLE.\n     * @param epoch The epoch timestamp.\n     * @param duration The period duration.\n     */\n    function addPeriodConfiguration(uint48 epoch, uint48 duration) external onlyRole(PERIOD_CONFIGURATION_UPDATE_ROLE) {\n        _addPeriodConfiguration(epoch, duration);\n    }\n\n    /**\n     * @notice Adds an address to the blocklist. Requires BLOCKLIST_ROLE.\n     * @param account Address to blocklist. Cannot be zero address nor blocklisted.\n     */\n    function addToBlocklist(address account) external onlyRole(BLOCKLIST_ROLE) notBlocklisted(account) {\n        if (account == address(0)) revert InvalidAddress();\n        isBlocklisted[account] = true;\n        emit AddedToBlocklist(account);\n    }\n\n    /**\n     * @notice Removes an address from the blocklist. Requires BLOCKLIST_ROLE.\n     * @param account Address to remove from blocklist. Must be blocklisted.\n     */\n    function removeFromBlocklist(address account) external onlyRole(BLOCKLIST_ROLE) onlyBlocklisted(account) {\n        isBlocklisted[account] = false;\n        emit RemovedFromBlocklist(account);\n    }\n\n    /**\n     * @notice Adds an address to the payout allowlist. Requires PAYOUT_ALLOWLIST_ROLE.\n     * @param account Address to add. Cannot be zero address.\n     */\n    function addToPayoutAllowlist(address account) external onlyRole(PAYOUT_ALLOWLIST_ROLE) {\n        if (account == address(0)) revert InvalidAddress();\n        isPayoutAllowlisted[account] = true;\n        emit AddedToPayoutAllowlist(account);\n    }\n\n    /**\n     * @notice Removes an address from the payout allowlist. Requires PAYOUT_ALLOWLIST_ROLE.\n     * @param account Address to remove.\n     */\n    function removeFromPayoutAllowlist(address account) external onlyRole(PAYOUT_ALLOWLIST_ROLE) {\n        isPayoutAllowlisted[account] = false;\n        emit RemovedFromPayoutAllowlist(account);\n    }\n\n    /**\n     * @notice Transfers shares to an address, with blocklist and pause checks.\n     * @param to Recipient address.\n     * @param shares Number of shares to transfer.\n     * @return Boolean indicating transfer success.\n     */\n    function transfer(\n        address to,\n        uint256 shares\n    )\n        public\n        override(ERC20Upgradeable, IERC20)\n        whenNotPaused\n        notBlocklisted(_msgSender())\n        notBlocklisted(to)\n        returns (bool)\n    {\n        super.transfer(to, shares);\n\n        uint48 ts = Time.timestamp();\n        address sender = _msgSender();\n        _traceBalanceOf[sender].push(ts, balanceOf(sender));\n        _traceBalanceOf[to].push(ts, balanceOf(to));\n\n        return true;\n    }\n\n    /**\n     * @notice Transfers shares from one account to another using allowance, with blocklist and pause checks.\n     * @param from Address sending the shares.\n     * @param to Address receiving the shares.\n     * @param shares Number of shares to transfer.\n     * @return Boolean indicating transfer success.\n     */\n    function transferFrom(\n        address from,\n        address to,\n        uint256 shares\n    )\n        public\n        override(ERC20Upgradeable, IERC20)\n        whenNotPaused\n        notBlocklisted(_msgSender())\n        notBlocklisted(from)\n        notBlocklisted(to)\n        returns (bool)\n    {\n        super.transferFrom(from, to, shares);\n\n        uint48 ts = Time.timestamp();\n        _traceBalanceOf[from].push(ts, balanceOf(from));\n        _traceBalanceOf[to].push(ts, balanceOf(to));\n\n        return true;\n    }\n\n    /**\n     * @notice Deposits assets into the vault and receives shares, with blocklist, active incident and pause checks.\n     * @param assets Amount of assets to deposit.\n     * @param receiver Address receiving the shares.\n     * @return Amount of shares received.\n     */\n    function deposit(\n        uint256 assets,\n        address receiver\n    )\n        public\n        override\n        whenNotPaused\n        notBlocklisted(_msgSender())\n        notBlocklisted(receiver)\n        nonReentrant\n        returns (uint256)\n    {\n        if (assets == 0) revert InvalidAmount();\n\n        if (_hasActiveIncident()) revert CurrentPeriodHasActiveIncident();\n\n        (uint256 shares, uint256 _totalSupply, uint256 _totalAssets) = _previewTotals(\n            assets,\n            true,\n            Math.Rounding.Floor\n        );\n        // A dust deposit can floor to zero shares once the share price exceeds 1;\n        // reject it instead of pulling assets in exchange for nothing.\n        if (shares == 0) revert InvalidAmount();\n\n        _depositFunds(_msgSender(), receiver, assets, shares, _totalSupply, _totalAssets);\n\n        return shares;\n    }\n\n    /**\n     * @notice Mints shares by depositing the required amount of assets into the vault,\n     * with blocklist, active incident and pause checks.\n     * @param shares Amount of shares to mint.\n     * @param receiver Address receiving the shares.\n     * @return Amount of assets deposited.\n     */\n    function mint(\n        uint256 shares,\n        address receiver\n    )\n        public\n        override\n        whenNotPaused\n        notBlocklisted(_msgSender())\n        notBlocklisted(receiver)\n        nonReentrant\n        returns (uint256)\n    {\n        if (shares == 0) revert InvalidAmount();\n\n        if (_hasActiveIncident()) revert CurrentPeriodHasActiveIncident();\n\n        (uint256 assets, uint256 _totalSupply, uint256 _totalAssets) = _previewTotals(\n            shares,\n            false,\n            Math.Rounding.Ceil\n        );\n\n        _depositFunds(_msgSender(), receiver, assets, shares, _totalSupply, _totalAssets);\n\n        return assets;\n    }\n\n    /**\n     * @notice Redeems shares from the vault and receives underlying assets, with blocklist and pause checks.\n     * Creates a withdrawal request, which will be available in the next period. Shares are burned.\n     * @param shares Amount of shares to redeem.\n     * @param receiver Address to receive the assets in the next period.\n     * @param owner Address whose shares are being redeemed.\n     * @return Amount of assets that will be received in the next period.\n     */\n    function redeem(\n        uint256 shares,\n        address receiver,\n        address owner\n    )\n        public\n        override\n        whenNotPaused\n        notBlocklisted(_msgSender())\n        notBlocklisted(owner)\n        notBlocklisted(receiver)\n        nonReentrant\n        returns (uint256)\n    {\n        if (shares == 0) revert InvalidAmount();\n\n        (uint256 assets, uint256 _totalSupply, uint256 _totalAssets) = _previewTotals(\n            shares,\n            false,\n            Math.Rounding.Floor\n        );\n\n        if (assets == 0) revert InvalidAmount();\n\n        uint256 ownerBalance = _requestWithdraw(assets, shares, receiver, owner);\n\n        _logTrace(owner, ownerBalance, _totalSupply - shares, _totalAssets - assets, true);\n\n        return assets;\n    }\n\n    /**\n     * @notice Initiates a withdrawal request from the vault, with blocklist and pause checks.\n     * The request becomes claimable starting from the period after the next full period.\n     * The calculated shares are burned.\n     * @param assets The amount of assets to withdraw.\n     * @param receiver The address to receive the assets in the next period.\n     * @param owner The address whose shares are being withdrawn.\n     * @return The amount of shares that were burned.\n     */\n    function withdraw(\n        uint256 assets,\n        address receiver,\n        address owner\n    )\n        public\n        override\n        whenNotPaused\n        notBlocklisted(_msgSender())\n        notBlocklisted(owner)\n        notBlocklisted(receiver)\n        nonReentrant\n        returns (uint256)\n    {\n        if (assets == 0) revert InvalidAmount();\n\n        (uint256 shares, uint256 _totalSupply, uint256 _totalAssets) = _previewTotals(assets, true, Math.Rounding.Ceil);\n\n        uint256 ownerBalance = _requestWithdraw(assets, shares, receiver, owner);\n\n        _logTrace(owner, ownerBalance, _totalSupply - shares, _totalAssets - assets, true);\n\n        return shares;\n    }\n\n    /**\n     * @notice Claims a pending withdrawal for a given period.\n     * Transfers the corresponding assets to the caller if not already claimed.\n     * Can only be called after the specified period has ended.\n     * Reverts if the withdrawal has already been claimed or if no withdrawal amount is available for the period.\n     * @param period The period number for which to claim the withdrawal.\n     * @return assets The amount of assets transferred to the caller.\n     */\n    function claimWithdraw(\n        uint256 period\n    ) external whenNotPaused notBlocklisted(_msgSender()) nonReentrant returns (uint256 assets) {\n        if (period >= currentPeriod()) revert InvalidPeriod();\n\n        address sender = _msgSender();\n        if (isWithdrawClaimed[period][sender]) revert AlreadyClaimedPeriod(period);\n\n        assets = _convertToAssetsTotals(\n            withdrawSharesOf[period][sender],\n            withdrawShares[period],\n            withdrawAssets[period],\n            Math.Rounding.Floor\n        );\n\n        if (assets == 0) revert NoWithdrawalAmount(period);\n\n        pendingWithdrawAssets -= assets;\n        isWithdrawClaimed[period][sender] = true;\n\n        IERC20(asset()).safeTransfer(sender, assets);\n\n        emit CompleteWithdraw(sender, assets, period);\n    }\n\n    /**\n     * @notice Rescues shares from a blocklisted address. Requires RESCUER_ROLE.\n     * @param from The blocklisted address.\n     * @param to The address to transfer the shares. Must not be blocklisted.\n     */\n    function rescueSharesFromBlocklisted(\n        address from,\n        address to\n    )\n        external\n        onlyRole(RESCUER_ROLE)\n        onlyBlocklisted(from)\n        notBlocklisted(to)\n    {\n        uint256 rescuedShares = balanceOf(from);\n        if( rescuedShares == 0) revert InsufficientShares();\n\n        _transfer(from, to, rescuedShares);\n\n        uint48 ts = Time.timestamp();\n        _traceBalanceOf[from].push(ts, 0);\n        _traceBalanceOf[to].push(ts, balanceOf(to));\n\n        emit SharesRescuedFromBlocklisted(from, to, rescuedShares);\n    }\n\n    /**\n    * @notice Rescues pending withdrawals from a blocklisted address. Requires RESCUER_ROLE.\n    * @param from The blocklisted address.\n    * @param to The address to transfer the shares to. Must not be blocklisted.\n    * @param periods An array of periods to rescue.\n    */\n    function rescueWithdrawFromBlocklisted(\n        address from,\n        address to,\n        uint256[] calldata periods\n    )\n        external\n        onlyRole(RESCUER_ROLE)\n        onlyBlocklisted(from)\n        notBlocklisted(to)\n    {\n        if (to == address(0)) revert InvalidAddress();\n\n        uint256 len = periods.length;\n        if(len == 0 ) revert InvalidArrayLength();\n\n        uint256[] memory rescuedShares = new uint256[](len);\n        for (uint256 i = 0; i < len; i++) {\n            uint256 _withdrawOf = withdrawSharesOf[periods[i]][from];\n\n            if (isWithdrawClaimed[periods[i]][from]) revert AlreadyClaimedPeriod(periods[i]);\n            if (isWithdrawClaimed[periods[i]][to]) revert AlreadyClaimedPeriod(periods[i]);\n            if (_withdrawOf == 0) revert NoWithdrawalAmount(periods[i]);\n\n            withdrawSharesOf[periods[i]][to] += _withdrawOf;\n            withdrawSharesOf[periods[i]][from] = 0;\n\n            rescuedShares[i] = _withdrawOf;\n        }\n\n        emit WithdrawRescuedFromBlocklisted(from, to, periods, rescuedShares);\n    }\n\n    /**\n     * @notice Pays an incident claim to an allowlisted receiver from the assets exposed to the incident's\n     * capture period. Callable only by PAYOUT_ROLE.\n     * @dev Allowed only during the capture period or the one after it. The amount is capped by the active assets\n     * at the capture-period start (an exposure cap, not a segregated bucket) and by currently payable assets, and\n     * is drawn proportionally from active assets and still-exposed withdrawals. `paidAmount` may be zero.\n     *\n     * The vault applies only these per-call limits; ensuring aggregate payouts stay within underwritten exposure\n     * is the responsibility of the curator and assessor roles.\n     * @param to Allowlisted payout receiver.\n     * @param amount Requested payout amount, in vault asset units.\n     * @param captureTimestamp Incident capture timestamp; sets the covered period and payout window.\n     * @return paidAmount Amount actually transferred; may be less than `amount`, including zero.\n     */\n    function payout(\n        address to,\n        uint256 amount,\n        uint48 captureTimestamp\n    ) external onlyRole(PAYOUT_ROLE) nonReentrant returns (uint256 paidAmount) {\n        if (!isPayoutAllowlisted[to]) revert AccountNotAllowlisted();\n        if (amount == 0) revert InvalidAmount();\n\n        (uint256 capturePeriod, uint48 capturePeriodStart) = _periodAndStartAt(captureTimestamp);\n        uint256 _currentPeriod = currentPeriod();\n\n        // Payout can execute during the incident period or the following period.\n        // After that, withdrawals for the incident period can become claimable.\n        if (!_isPeriodInPayoutWindow(capturePeriod, _currentPeriod)) {\n            revert InvalidCapturePeriod();\n        }\n\n        // Cap by the active assets committed at the start of the covered period.\n        // This is an exposure cap, not a segregated asset bucket.\n        // Use capturePeriodStart - 1 so the inclusive lookup selects timestamps < capturePeriodStart.\n        // This excludes deposits and withdrawals at the period start from the payout snapshot.\n        // capturePeriodStart is always > 0 (deploy epoch), so the subtraction cannot underflow.\n        uint256 assetsAtCapturePeriod = totalAssetsAt(capturePeriodStart - 1);\n        uint256 currentActiveAssets = totalAssets();\n\n        // Withdrawals requested during the capture period are assigned to capturePeriod + 1\n        // and remain payable while payout is allowed.\n        uint256 capturePeriodWithdrawals = withdrawAssets[capturePeriod + 1];\n\n        // If payout happens in the following period, withdrawals requested before payout\n        // during that following period are assigned to capturePeriod + 2 and should also\n        // remain payable; otherwise exposed assets could escape by withdrawing first.\n        uint256 nextPeriodWithdrawals;\n        if (_currentPeriod == capturePeriod + 1) {\n            nextPeriodWithdrawals = withdrawAssets[capturePeriod + 2];\n        }\n\n        uint256 activePayableAmount = currentActiveAssets + capturePeriodWithdrawals + nextPeriodWithdrawals;\n        paidAmount = Math.min(amount, Math.min(assetsAtCapturePeriod, activePayableAmount));\n\n        // The incident must be processed with the available amount, even if paidAmount is zero.\n        if (paidAmount > 0) {\n            uint256 paidFromActive;\n            uint256 paidFromNextWithdrawals;\n            uint256 paidFromCaptureWithdrawals;\n\n            if (_currentPeriod == capturePeriod) {\n                uint256 payableAmount = currentActiveAssets + capturePeriodWithdrawals;\n                paidFromActive = paidAmount.mulDiv(currentActiveAssets, payableAmount);\n                paidFromCaptureWithdrawals = paidAmount - paidFromActive;\n            } else {\n                uint256 payableAmount = currentActiveAssets + capturePeriodWithdrawals + nextPeriodWithdrawals;\n                paidFromActive = paidAmount.mulDiv(currentActiveAssets, payableAmount);\n                paidFromNextWithdrawals = paidAmount.mulDiv(nextPeriodWithdrawals, payableAmount);\n                paidFromCaptureWithdrawals = paidAmount - paidFromActive - paidFromNextWithdrawals;\n\n                if (capturePeriodWithdrawals < paidFromCaptureWithdrawals) {\n                    uint256 overflow = paidFromCaptureWithdrawals - capturePeriodWithdrawals;\n                    paidFromCaptureWithdrawals = capturePeriodWithdrawals;\n                    paidFromNextWithdrawals += overflow;\n                }\n            }\n\n            _traceTotalAssets.push(Time.timestamp(), currentActiveAssets - paidFromActive);\n\n            withdrawAssets[capturePeriod + 1] = capturePeriodWithdrawals - paidFromCaptureWithdrawals;\n\n            if (paidFromNextWithdrawals > 0) {\n                withdrawAssets[capturePeriod + 2] = nextPeriodWithdrawals - paidFromNextWithdrawals;\n            }\n\n            pendingWithdrawAssets -= paidFromCaptureWithdrawals + paidFromNextWithdrawals;\n\n            IERC20(asset()).safeTransfer(to, paidAmount);\n        }\n\n        emit PayoutExecuted(to, amount, paidAmount, captureTimestamp);\n    }\n\n    function _updateDepositLimit(uint256 newLimit) internal {\n        if (newLimit == 0) {\n            revert InvalidDepositLimit();\n        }\n        depositLimit = newLimit;\n        emit DepositLimitUpdated(newLimit);\n    }\n\n    function _depositFunds(\n        address caller,\n        address receiver,\n        uint256 assets,\n        uint256 shares,\n        uint256 _totalSupply,\n        uint256 _totalAssets\n    ) private {\n        _totalSupply += shares;\n        _totalAssets += assets;\n\n        if (_totalAssets > depositLimit) revert DepositLimitExceeded();\n\n        _deposit(caller, receiver, assets, shares);\n\n        _logTrace(receiver, balanceOf(receiver), _totalSupply, _totalAssets, true);\n    }\n\n    function _requestWithdraw(\n        uint256 assets,\n        uint256 shares,\n        address receiver,\n        address owner\n    ) private returns (uint256 ownerBalance) {\n        if (receiver == address(0) || owner == address(0)) revert InvalidAddress();\n\n        ownerBalance = balanceOf(owner);\n        if (shares > ownerBalance) revert InsufficientShares();\n        ownerBalance -= shares;\n\n        address sender = _msgSender();\n\n        uint256 period = currentPeriod() + 1;\n        uint256 sharesWithdraw = _convertToSharesTotals(\n            assets,\n            withdrawShares[period],\n            withdrawAssets[period],\n            Math.Rounding.Floor\n        );\n        withdrawAssets[period] += assets;\n        withdrawShares[period] += sharesWithdraw;\n        withdrawSharesOf[period][receiver] += sharesWithdraw;\n\n        pendingWithdrawAssets += assets;\n\n        if (sender != owner) {\n            _spendAllowance(owner, sender, shares);\n        }\n\n        _update(owner, address(0), shares);\n\n        emit WithdrawRequest(sender, receiver, owner, period, assets, shares);\n    }\n\n    function _previewTotals(\n        uint256 assetsOrShares,\n        bool isAssets,\n        Math.Rounding rounding\n    ) private view returns (uint256 amount, uint256 _totalSupply, uint256 _totalAssets) {\n        _totalSupply = totalSupply();\n        _totalAssets = totalAssets();\n        if (isAssets) {\n            amount = _convertToSharesTotals(assetsOrShares, _totalSupply, _totalAssets, rounding);\n        } else {\n            amount = _convertToAssetsTotals(assetsOrShares, _totalSupply, _totalAssets, rounding);\n        }\n    }\n\n    function _logTrace(\n        address owner,\n        uint256 balance,\n        uint256 _totalSupply,\n        uint256 _totalAssets,\n        bool isLogAssets\n    ) private {\n        uint48 ts = Time.timestamp();\n        _traceBalanceOf[owner].push(ts, balance);\n        _traceTotalSupply.push(ts, _totalSupply);\n\n        if (isLogAssets) _traceTotalAssets.push(ts, _totalAssets);\n    }\n\n    function _convertToSharesTotals(\n        uint256 assets,\n        uint256 totSupply,\n        uint256 totAssets,\n        Math.Rounding rounding\n    ) private view returns (uint256) {\n        return assets.mulDiv(totSupply + 10 ** _decimalsOffset(), totAssets + 1, rounding);\n    }\n\n    function _convertToAssetsTotals(\n        uint256 shares,\n        uint256 totSupply,\n        uint256 totAssets,\n        Math.Rounding rounding\n    ) private view returns (uint256) {\n        return shares.mulDiv(totAssets + 1, totSupply + 10 ** _decimalsOffset(), rounding);\n    }\n\n    function _nowSinceEpoch(uint48 epoch) private view returns (uint48) {\n        return _timestampSinceEpoch(Time.timestamp(), epoch);\n    }\n\n    function _timestampSinceEpoch(uint48 timestamp, uint48 epoch) private pure returns (uint48) {\n        return timestamp - epoch;\n    }\n\n    function _addPeriodConfiguration(uint48 newEpoch, uint48 newDuration) private {\n        if (\n            newDuration < SMALLEST_PERIOD_DURATION ||\n            newDuration > MAX_PERIOD_DURATION ||\n            newDuration % SMALLEST_PERIOD_DURATION != 0\n        ) revert InvalidPeriodConfigurationDuration();\n\n        uint256 startingPeriod;\n        if (periodConfigurations.length > 0) {\n            PeriodConfiguration memory currentPC = currentPeriodConfiguration();\n            if (currentPC.epoch != periodConfigurations[periodConfigurations.length - 1].epoch)\n                revert CurrentPeriodConfigurationNotLast();\n            if (newEpoch < nextPeriodEnd() || (newEpoch - currentPC.epoch) % currentPC.duration != 0)\n                revert InvalidPeriodConfigurationEpoch();\n\n            startingPeriod = currentPC.startingPeriod + (newEpoch - currentPC.epoch) / currentPC.duration;\n        } else {\n            if (newEpoch < Time.timestamp()) revert InvalidPeriodConfigurationEpoch();\n\n            startingPeriod = 0;\n        }\n\n        PeriodConfiguration memory newPeriod = PeriodConfiguration({\n            epoch: newEpoch,\n            duration: newDuration,\n            startingPeriod: startingPeriod\n        });\n        periodConfigurations.push(newPeriod);\n        emit PeriodConfigurationAdded(newPeriod);\n    }\n\n    /// @dev True while an incident is active in the current period or the previous one. The previous period is\n    /// included because a claim captured then is still payable this period, so deposits stay blocked through the\n    /// following period, until that payout window closes; otherwise fresh deposits would back unresolved exposure.\n    function _hasActiveIncident() internal view returns (bool) {\n        uint256 period = currentPeriod();\n        return hasActiveIncident[period] || (period > 0 && hasActiveIncident[period - 1]);\n    }\n\n    /// @dev Resolves a timestamp's period number and period start from a single scan of the\n    ///      configuration history: the configuration governing the timestamp also governs\n    ///      its period, so both derive from one lookup.\n    function _periodAndStartAt(uint48 timestamp) internal view returns (uint256 period, uint48 start) {\n        PeriodConfiguration memory pc = periodConfigurationAtTimestamp(timestamp);\n        uint48 periodsSinceEpoch = _timestampSinceEpoch(timestamp, pc.epoch) / pc.duration;\n        period = pc.startingPeriod + periodsSinceEpoch;\n        start = pc.epoch + periodsSinceEpoch * pc.duration;\n    }\n\n    function _isPeriodInPayoutWindow(uint256 period, uint256 current) internal pure returns (bool) {\n        return current == period || current == period + 1;\n    }\n}\n"},{"file_path":"@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.3.0) (utils/Pausable.sol)\n\npragma solidity ^0.8.20;\n\nimport {ContextUpgradeable} from \"../utils/ContextUpgradeable.sol\";\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract PausableUpgradeable is Initializable, ContextUpgradeable {\n    /// @custom:storage-location erc7201:openzeppelin.storage.Pausable\n    struct PausableStorage {\n        bool _paused;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.Pausable\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;\n\n    function _getPausableStorage() private pure returns (PausableStorage storage $) {\n        assembly {\n            $.slot := PausableStorageLocation\n        }\n    }\n\n    /**\n     * @dev Emitted when the pause is triggered by `account`.\n     */\n    event Paused(address account);\n\n    /**\n     * @dev Emitted when the pause is lifted by `account`.\n     */\n    event Unpaused(address account);\n\n    /**\n     * @dev The operation failed because the contract is paused.\n     */\n    error EnforcedPause();\n\n    /**\n     * @dev The operation failed because the contract is not paused.\n     */\n    error ExpectedPause();\n\n    /**\n     * @dev Modifier to make a function callable only when the contract is not paused.\n     *\n     * Requirements:\n     *\n     * - The contract must not be paused.\n     */\n    modifier whenNotPaused() {\n        _requireNotPaused();\n        _;\n    }\n\n    /**\n     * @dev Modifier to make a function callable only when the contract is paused.\n     *\n     * Requirements:\n     *\n     * - The contract must be paused.\n     */\n    modifier whenPaused() {\n        _requirePaused();\n        _;\n    }\n\n    function __Pausable_init() internal onlyInitializing {\n    }\n\n    function __Pausable_init_unchained() internal onlyInitializing {\n    }\n    /**\n     * @dev Returns true if the contract is paused, and false otherwise.\n     */\n    function paused() public view virtual returns (bool) {\n        PausableStorage storage $ = _getPausableStorage();\n        return $._paused;\n    }\n\n    /**\n     * @dev Throws if the contract is paused.\n     */\n    function _requireNotPaused() internal view virtual {\n        if (paused()) {\n            revert EnforcedPause();\n        }\n    }\n\n    /**\n     * @dev Throws if the contract is not paused.\n     */\n    function _requirePaused() internal view virtual {\n        if (!paused()) {\n            revert ExpectedPause();\n        }\n    }\n\n    /**\n     * @dev Triggers stopped state.\n     *\n     * Requirements:\n     *\n     * - The contract must not be paused.\n     */\n    function _pause() internal virtual whenNotPaused {\n        PausableStorage storage $ = _getPausableStorage();\n        $._paused = true;\n        emit Paused(_msgSender());\n    }\n\n    /**\n     * @dev Returns to normal state.\n     *\n     * Requirements:\n     *\n     * - The contract must be paused.\n     */\n    function _unpause() internal virtual whenPaused {\n        PausableStorage storage $ = _getPausableStorage();\n        $._paused = false;\n        emit Unpaused(_msgSender());\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/utils/introspection/ERC165.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC165} from \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n */\nabstract contract ERC165 is IERC165 {\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {\n        return interfaceId == type(IERC165).interfaceId;\n    }\n}\n"},{"file_path":"contracts/test/ReentrantVaultAsset.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.28;\n\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @dev Malicious ERC20 used as a FirelightVault underlying asset. On every token\n *      movement it re-enters the vault with a configurable call, letting tests hit\n *      the `nonReentrant` revert branches of deposit/mint/claimWithdraw/payout.\n *      The inner revert is bubbled up so assertions can match the vault error.\n */\ncontract ReentrantVaultAsset is ERC20 {\n    address public vault;\n    bytes public reentrantCall;\n    bool private _entered;\n\n    constructor() ERC20(\"Reentrant Asset\", \"rASSET\") {}\n\n    function setVault(address vault_) external {\n        vault = vault_;\n    }\n\n    /// @dev Empty bytes disables the attack.\n    function setReentrantCall(bytes calldata data) external {\n        reentrantCall = data;\n    }\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n\n    function _update(address from, address to, uint256 value) internal override {\n        super._update(from, to, value);\n\n        if (vault != address(0) && reentrantCall.length > 0 && !_entered) {\n            _entered = true;\n            (bool success, bytes memory returndata) = vault.call(reentrantCall);\n            _entered = false;\n            if (!success) {\n                assembly {\n                    revert(add(returndata, 0x20), mload(returndata))\n                }\n            }\n        }\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/utils/structs/Checkpoints.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/Checkpoints.sol)\n// This file was procedurally generated from scripts/generate/templates/Checkpoints.js.\n\npragma solidity ^0.8.20;\n\nimport {Math} from \"../math/Math.sol\";\n\n/**\n * @dev This library defines the `Trace*` struct, for checkpointing values as they change at different points in\n * time, and later looking up past values by block number. See {Votes} as an example.\n *\n * To create a history of checkpoints define a variable type `Checkpoints.Trace*` in your contract, and store a new\n * checkpoint for the current transaction block using the {push} function.\n */\nlibrary Checkpoints {\n    /**\n     * @dev A value was attempted to be inserted on a past checkpoint.\n     */\n    error CheckpointUnorderedInsertion();\n\n    struct Trace224 {\n        Checkpoint224[] _checkpoints;\n    }\n\n    struct Checkpoint224 {\n        uint32 _key;\n        uint224 _value;\n    }\n\n    /**\n     * @dev Pushes a (`key`, `value`) pair into a Trace224 so that it is stored as the checkpoint.\n     *\n     * Returns previous value and new value.\n     *\n     * IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint32).max` key set will disable the\n     * library.\n     */\n    function push(\n        Trace224 storage self,\n        uint32 key,\n        uint224 value\n    ) internal returns (uint224 oldValue, uint224 newValue) {\n        return _insert(self._checkpoints, key, value);\n    }\n\n    /**\n     * @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if\n     * there is none.\n     */\n    function lowerLookup(Trace224 storage self, uint32 key) internal view returns (uint224) {\n        uint256 len = self._checkpoints.length;\n        uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);\n        return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value;\n    }\n\n    /**\n     * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero\n     * if there is none.\n     */\n    function upperLookup(Trace224 storage self, uint32 key) internal view returns (uint224) {\n        uint256 len = self._checkpoints.length;\n        uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);\n        return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;\n    }\n\n    /**\n     * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero\n     * if there is none.\n     *\n     * NOTE: This is a variant of {upperLookup} that is optimised to find \"recent\" checkpoint (checkpoints with high\n     * keys).\n     */\n    function upperLookupRecent(Trace224 storage self, uint32 key) internal view returns (uint224) {\n        uint256 len = self._checkpoints.length;\n\n        uint256 low = 0;\n        uint256 high = len;\n\n        if (len > 5) {\n            uint256 mid = len - Math.sqrt(len);\n            if (key < _unsafeAccess(self._checkpoints, mid)._key) {\n                high = mid;\n            } else {\n                low = mid + 1;\n            }\n        }\n\n        uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);\n\n        return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;\n    }\n\n    /**\n     * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.\n     */\n    function latest(Trace224 storage self) internal view returns (uint224) {\n        uint256 pos = self._checkpoints.length;\n        return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;\n    }\n\n    /**\n     * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value\n     * in the most recent checkpoint.\n     */\n    function latestCheckpoint(Trace224 storage self) internal view returns (bool exists, uint32 _key, uint224 _value) {\n        uint256 pos = self._checkpoints.length;\n        if (pos == 0) {\n            return (false, 0, 0);\n        } else {\n            Checkpoint224 storage ckpt = _unsafeAccess(self._checkpoints, pos - 1);\n            return (true, ckpt._key, ckpt._value);\n        }\n    }\n\n    /**\n     * @dev Returns the number of checkpoint.\n     */\n    function length(Trace224 storage self) internal view returns (uint256) {\n        return self._checkpoints.length;\n    }\n\n    /**\n     * @dev Returns checkpoint at given position.\n     */\n    function at(Trace224 storage self, uint32 pos) internal view returns (Checkpoint224 memory) {\n        return self._checkpoints[pos];\n    }\n\n    /**\n     * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,\n     * or by updating the last one.\n     */\n    function _insert(\n        Checkpoint224[] storage self,\n        uint32 key,\n        uint224 value\n    ) private returns (uint224 oldValue, uint224 newValue) {\n        uint256 pos = self.length;\n\n        if (pos > 0) {\n            Checkpoint224 storage last = _unsafeAccess(self, pos - 1);\n            uint32 lastKey = last._key;\n            uint224 lastValue = last._value;\n\n            // Checkpoint keys must be non-decreasing.\n            if (lastKey > key) {\n                revert CheckpointUnorderedInsertion();\n            }\n\n            // Update or push new checkpoint\n            if (lastKey == key) {\n                last._value = value;\n            } else {\n                self.push(Checkpoint224({_key: key, _value: value}));\n            }\n            return (lastValue, value);\n        } else {\n            self.push(Checkpoint224({_key: key, _value: value}));\n            return (0, value);\n        }\n    }\n\n    /**\n     * @dev Return the index of the first (oldest) checkpoint with key strictly bigger than the search key, or `high`\n     * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive\n     * `high`.\n     *\n     * WARNING: `high` should not be greater than the array's length.\n     */\n    function _upperBinaryLookup(\n        Checkpoint224[] storage self,\n        uint32 key,\n        uint256 low,\n        uint256 high\n    ) private view returns (uint256) {\n        while (low < high) {\n            uint256 mid = Math.average(low, high);\n            if (_unsafeAccess(self, mid)._key > key) {\n                high = mid;\n            } else {\n                low = mid + 1;\n            }\n        }\n        return high;\n    }\n\n    /**\n     * @dev Return the index of the first (oldest) checkpoint with key greater or equal than the search key, or `high`\n     * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive\n     * `high`.\n     *\n     * WARNING: `high` should not be greater than the array's length.\n     */\n    function _lowerBinaryLookup(\n        Checkpoint224[] storage self,\n        uint32 key,\n        uint256 low,\n        uint256 high\n    ) private view returns (uint256) {\n        while (low < high) {\n            uint256 mid = Math.average(low, high);\n            if (_unsafeAccess(self, mid)._key < key) {\n                low = mid + 1;\n            } else {\n                high = mid;\n            }\n        }\n        return high;\n    }\n\n    /**\n     * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.\n     */\n    function _unsafeAccess(\n        Checkpoint224[] storage self,\n        uint256 pos\n    ) private pure returns (Checkpoint224 storage result) {\n        assembly {\n            mstore(0, self.slot)\n            result.slot := add(keccak256(0, 0x20), pos)\n        }\n    }\n\n    struct Trace208 {\n        Checkpoint208[] _checkpoints;\n    }\n\n    struct Checkpoint208 {\n        uint48 _key;\n        uint208 _value;\n    }\n\n    /**\n     * @dev Pushes a (`key`, `value`) pair into a Trace208 so that it is stored as the checkpoint.\n     *\n     * Returns previous value and new value.\n     *\n     * IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint48).max` key set will disable the\n     * library.\n     */\n    function push(\n        Trace208 storage self,\n        uint48 key,\n        uint208 value\n    ) internal returns (uint208 oldValue, uint208 newValue) {\n        return _insert(self._checkpoints, key, value);\n    }\n\n    /**\n     * @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if\n     * there is none.\n     */\n    function lowerLookup(Trace208 storage self, uint48 key) internal view returns (uint208) {\n        uint256 len = self._checkpoints.length;\n        uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);\n        return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value;\n    }\n\n    /**\n     * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero\n     * if there is none.\n     */\n    function upperLookup(Trace208 storage self, uint48 key) internal view returns (uint208) {\n        uint256 len = self._checkpoints.length;\n        uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);\n        return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;\n    }\n\n    /**\n     * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero\n     * if there is none.\n     *\n     * NOTE: This is a variant of {upperLookup} that is optimised to find \"recent\" checkpoint (checkpoints with high\n     * keys).\n     */\n    function upperLookupRecent(Trace208 storage self, uint48 key) internal view returns (uint208) {\n        uint256 len = self._checkpoints.length;\n\n        uint256 low = 0;\n        uint256 high = len;\n\n        if (len > 5) {\n            uint256 mid = len - Math.sqrt(len);\n            if (key < _unsafeAccess(self._checkpoints, mid)._key) {\n                high = mid;\n            } else {\n                low = mid + 1;\n            }\n        }\n\n        uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);\n\n        return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;\n    }\n\n    /**\n     * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.\n     */\n    function latest(Trace208 storage self) internal view returns (uint208) {\n        uint256 pos = self._checkpoints.length;\n        return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;\n    }\n\n    /**\n     * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value\n     * in the most recent checkpoint.\n     */\n    function latestCheckpoint(Trace208 storage self) internal view returns (bool exists, uint48 _key, uint208 _value) {\n        uint256 pos = self._checkpoints.length;\n        if (pos == 0) {\n            return (false, 0, 0);\n        } else {\n            Checkpoint208 storage ckpt = _unsafeAccess(self._checkpoints, pos - 1);\n            return (true, ckpt._key, ckpt._value);\n        }\n    }\n\n    /**\n     * @dev Returns the number of checkpoint.\n     */\n    function length(Trace208 storage self) internal view returns (uint256) {\n        return self._checkpoints.length;\n    }\n\n    /**\n     * @dev Returns checkpoint at given position.\n     */\n    function at(Trace208 storage self, uint32 pos) internal view returns (Checkpoint208 memory) {\n        return self._checkpoints[pos];\n    }\n\n    /**\n     * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,\n     * or by updating the last one.\n     */\n    function _insert(\n        Checkpoint208[] storage self,\n        uint48 key,\n        uint208 value\n    ) private returns (uint208 oldValue, uint208 newValue) {\n        uint256 pos = self.length;\n\n        if (pos > 0) {\n            Checkpoint208 storage last = _unsafeAccess(self, pos - 1);\n            uint48 lastKey = last._key;\n            uint208 lastValue = last._value;\n\n            // Checkpoint keys must be non-decreasing.\n            if (lastKey > key) {\n                revert CheckpointUnorderedInsertion();\n            }\n\n            // Update or push new checkpoint\n            if (lastKey == key) {\n                last._value = value;\n            } else {\n                self.push(Checkpoint208({_key: key, _value: value}));\n            }\n            return (lastValue, value);\n        } else {\n            self.push(Checkpoint208({_key: key, _value: value}));\n            return (0, value);\n        }\n    }\n\n    /**\n     * @dev Return the index of the first (oldest) checkpoint with key strictly bigger than the search key, or `high`\n     * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive\n     * `high`.\n     *\n     * WARNING: `high` should not be greater than the array's length.\n     */\n    function _upperBinaryLookup(\n        Checkpoint208[] storage self,\n        uint48 key,\n        uint256 low,\n        uint256 high\n    ) private view returns (uint256) {\n        while (low < high) {\n            uint256 mid = Math.average(low, high);\n            if (_unsafeAccess(self, mid)._key > key) {\n                high = mid;\n            } else {\n                low = mid + 1;\n            }\n        }\n        return high;\n    }\n\n    /**\n     * @dev Return the index of the first (oldest) checkpoint with key greater or equal than the search key, or `high`\n     * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive\n     * `high`.\n     *\n     * WARNING: `high` should not be greater than the array's length.\n     */\n    function _lowerBinaryLookup(\n        Checkpoint208[] storage self,\n        uint48 key,\n        uint256 low,\n        uint256 high\n    ) private view returns (uint256) {\n        while (low < high) {\n            uint256 mid = Math.average(low, high);\n            if (_unsafeAccess(self, mid)._key < key) {\n                low = mid + 1;\n            } else {\n                high = mid;\n            }\n        }\n        return high;\n    }\n\n    /**\n     * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.\n     */\n    function _unsafeAccess(\n        Checkpoint208[] storage self,\n        uint256 pos\n    ) private pure returns (Checkpoint208 storage result) {\n        assembly {\n            mstore(0, self.slot)\n            result.slot := add(keccak256(0, 0x20), pos)\n        }\n    }\n\n    struct Trace160 {\n        Checkpoint160[] _checkpoints;\n    }\n\n    struct Checkpoint160 {\n        uint96 _key;\n        uint160 _value;\n    }\n\n    /**\n     * @dev Pushes a (`key`, `value`) pair into a Trace160 so that it is stored as the checkpoint.\n     *\n     * Returns previous value and new value.\n     *\n     * IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint96).max` key set will disable the\n     * library.\n     */\n    function push(\n        Trace160 storage self,\n        uint96 key,\n        uint160 value\n    ) internal returns (uint160 oldValue, uint160 newValue) {\n        return _insert(self._checkpoints, key, value);\n    }\n\n    /**\n     * @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if\n     * there is none.\n     */\n    function lowerLookup(Trace160 storage self, uint96 key) internal view returns (uint160) {\n        uint256 len = self._checkpoints.length;\n        uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);\n        return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value;\n    }\n\n    /**\n     * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero\n     * if there is none.\n     */\n    function upperLookup(Trace160 storage self, uint96 key) internal view returns (uint160) {\n        uint256 len = self._checkpoints.length;\n        uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);\n        return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;\n    }\n\n    /**\n     * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero\n     * if there is none.\n     *\n     * NOTE: This is a variant of {upperLookup} that is optimised to find \"recent\" checkpoint (checkpoints with high\n     * keys).\n     */\n    function upperLookupRecent(Trace160 storage self, uint96 key) internal view returns (uint160) {\n        uint256 len = self._checkpoints.length;\n\n        uint256 low = 0;\n        uint256 high = len;\n\n        if (len > 5) {\n            uint256 mid = len - Math.sqrt(len);\n            if (key < _unsafeAccess(self._checkpoints, mid)._key) {\n                high = mid;\n            } else {\n                low = mid + 1;\n            }\n        }\n\n        uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);\n\n        return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;\n    }\n\n    /**\n     * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.\n     */\n    function latest(Trace160 storage self) internal view returns (uint160) {\n        uint256 pos = self._checkpoints.length;\n        return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;\n    }\n\n    /**\n     * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value\n     * in the most recent checkpoint.\n     */\n    function latestCheckpoint(Trace160 storage self) internal view returns (bool exists, uint96 _key, uint160 _value) {\n        uint256 pos = self._checkpoints.length;\n        if (pos == 0) {\n            return (false, 0, 0);\n        } else {\n            Checkpoint160 storage ckpt = _unsafeAccess(self._checkpoints, pos - 1);\n            return (true, ckpt._key, ckpt._value);\n        }\n    }\n\n    /**\n     * @dev Returns the number of checkpoint.\n     */\n    function length(Trace160 storage self) internal view returns (uint256) {\n        return self._checkpoints.length;\n    }\n\n    /**\n     * @dev Returns checkpoint at given position.\n     */\n    function at(Trace160 storage self, uint32 pos) internal view returns (Checkpoint160 memory) {\n        return self._checkpoints[pos];\n    }\n\n    /**\n     * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,\n     * or by updating the last one.\n     */\n    function _insert(\n        Checkpoint160[] storage self,\n        uint96 key,\n        uint160 value\n    ) private returns (uint160 oldValue, uint160 newValue) {\n        uint256 pos = self.length;\n\n        if (pos > 0) {\n            Checkpoint160 storage last = _unsafeAccess(self, pos - 1);\n            uint96 lastKey = last._key;\n            uint160 lastValue = last._value;\n\n            // Checkpoint keys must be non-decreasing.\n            if (lastKey > key) {\n                revert CheckpointUnorderedInsertion();\n            }\n\n            // Update or push new checkpoint\n            if (lastKey == key) {\n                last._value = value;\n            } else {\n                self.push(Checkpoint160({_key: key, _value: value}));\n            }\n            return (lastValue, value);\n        } else {\n            self.push(Checkpoint160({_key: key, _value: value}));\n            return (0, value);\n        }\n    }\n\n    /**\n     * @dev Return the index of the first (oldest) checkpoint with key strictly bigger than the search key, or `high`\n     * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive\n     * `high`.\n     *\n     * WARNING: `high` should not be greater than the array's length.\n     */\n    function _upperBinaryLookup(\n        Checkpoint160[] storage self,\n        uint96 key,\n        uint256 low,\n        uint256 high\n    ) private view returns (uint256) {\n        while (low < high) {\n            uint256 mid = Math.average(low, high);\n            if (_unsafeAccess(self, mid)._key > key) {\n                high = mid;\n            } else {\n                low = mid + 1;\n            }\n        }\n        return high;\n    }\n\n    /**\n     * @dev Return the index of the first (oldest) checkpoint with key greater or equal than the search key, or `high`\n     * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive\n     * `high`.\n     *\n     * WARNING: `high` should not be greater than the array's length.\n     */\n    function _lowerBinaryLookup(\n        Checkpoint160[] storage self,\n        uint96 key,\n        uint256 low,\n        uint256 high\n    ) private view returns (uint256) {\n        while (low < high) {\n            uint256 mid = Math.average(low, high);\n            if (_unsafeAccess(self, mid)._key < key) {\n                low = mid + 1;\n            } else {\n                high = mid;\n            }\n        }\n        return high;\n    }\n\n    /**\n     * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.\n     */\n    function _unsafeAccess(\n        Checkpoint160[] storage self,\n        uint256 pos\n    ) private pure returns (Checkpoint160 storage result) {\n        assembly {\n            mstore(0, self.slot)\n            result.slot := add(keccak256(0, 0x20), pos)\n        }\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/interfaces/IERC1363.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC20} from \"./IERC20.sol\";\nimport {IERC165} from \"./IERC165.sol\";\n\n/**\n * @title IERC1363\n * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].\n *\n * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract\n * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.\n */\ninterface IERC1363 is IERC20, IERC165 {\n    /*\n     * Note: the ERC-165 identifier for this interface is 0xb0202a11.\n     * 0xb0202a11 ===\n     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^\n     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^\n     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^\n     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^\n     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^\n     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))\n     */\n\n    /**\n     * @dev Moves a `value` amount of tokens from the caller's account to `to`\n     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.\n     * @param to The address which you want to transfer to.\n     * @param value The amount of tokens to be transferred.\n     * @return A boolean value indicating whether the operation succeeded unless throwing.\n     */\n    function transferAndCall(address to, uint256 value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from the caller's account to `to`\n     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.\n     * @param to The address which you want to transfer to.\n     * @param value The amount of tokens to be transferred.\n     * @param data Additional data with no specified format, sent in call to `to`.\n     * @return A boolean value indicating whether the operation succeeded unless throwing.\n     */\n    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism\n     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.\n     * @param from The address which you want to send tokens from.\n     * @param to The address which you want to transfer to.\n     * @param value The amount of tokens to be transferred.\n     * @return A boolean value indicating whether the operation succeeded unless throwing.\n     */\n    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism\n     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.\n     * @param from The address which you want to send tokens from.\n     * @param to The address which you want to transfer to.\n     * @param value The amount of tokens to be transferred.\n     * @param data Additional data with no specified format, sent in call to `to`.\n     * @return A boolean value indicating whether the operation succeeded unless throwing.\n     */\n    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);\n\n    /**\n     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.\n     * @param spender The address which will spend the funds.\n     * @param value The amount of tokens to be spent.\n     * @return A boolean value indicating whether the operation succeeded unless throwing.\n     */\n    function approveAndCall(address spender, uint256 value) external returns (bool);\n\n    /**\n     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.\n     * @param spender The address which will spend the funds.\n     * @param value The amount of tokens to be spent.\n     * @param data Additional data with no specified format, sent in call to `spender`.\n     * @return A boolean value indicating whether the operation succeeded unless throwing.\n     */\n    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);\n}\n"},{"file_path":"@openzeppelin/contracts/interfaces/IERC5313.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5313.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface for the Light Contract Ownership Standard.\n *\n * A standardized minimal interface required to identify an account that controls a contract\n */\ninterface IERC5313 {\n    /**\n     * @dev Gets the address of the owner.\n     */\n    function owner() external view returns (address);\n}\n"},{"file_path":"@openzeppelin/contracts/utils/Strings.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.2.0) (utils/Strings.sol)\n\npragma solidity ^0.8.20;\n\nimport {Math} from \"./math/Math.sol\";\nimport {SafeCast} from \"./math/SafeCast.sol\";\nimport {SignedMath} from \"./math/SignedMath.sol\";\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n    using SafeCast for *;\n\n    bytes16 private constant HEX_DIGITS = \"0123456789abcdef\";\n    uint8 private constant ADDRESS_LENGTH = 20;\n\n    /**\n     * @dev The `value` string doesn't fit in the specified `length`.\n     */\n    error StringsInsufficientHexLength(uint256 value, uint256 length);\n\n    /**\n     * @dev The string being parsed contains characters that are not in scope of the given base.\n     */\n    error StringsInvalidChar();\n\n    /**\n     * @dev The string being parsed is not a properly formatted address.\n     */\n    error StringsInvalidAddressFormat();\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n     */\n    function toString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            uint256 length = Math.log10(value) + 1;\n            string memory buffer = new string(length);\n            uint256 ptr;\n            assembly (\"memory-safe\") {\n                ptr := add(buffer, add(32, length))\n            }\n            while (true) {\n                ptr--;\n                assembly (\"memory-safe\") {\n                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))\n                }\n                value /= 10;\n                if (value == 0) break;\n            }\n            return buffer;\n        }\n    }\n\n    /**\n     * @dev Converts a `int256` to its ASCII `string` decimal representation.\n     */\n    function toStringSigned(int256 value) internal pure returns (string memory) {\n        return string.concat(value < 0 ? \"-\" : \"\", toString(SignedMath.abs(value)));\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n     */\n    function toHexString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            return toHexString(value, Math.log256(value) + 1);\n        }\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n     */\n    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n        uint256 localValue = value;\n        bytes memory buffer = new bytes(2 * length + 2);\n        buffer[0] = \"0\";\n        buffer[1] = \"x\";\n        for (uint256 i = 2 * length + 1; i > 1; --i) {\n            buffer[i] = HEX_DIGITS[localValue & 0xf];\n            localValue >>= 4;\n        }\n        if (localValue != 0) {\n            revert StringsInsufficientHexLength(value, length);\n        }\n        return string(buffer);\n    }\n\n    /**\n     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal\n     * representation.\n     */\n    function toHexString(address addr) internal pure returns (string memory) {\n        return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);\n    }\n\n    /**\n     * @dev Converts an `address` with fixed length of 20 bytes to its checksummed ASCII `string` hexadecimal\n     * representation, according to EIP-55.\n     */\n    function toChecksumHexString(address addr) internal pure returns (string memory) {\n        bytes memory buffer = bytes(toHexString(addr));\n\n        // hash the hex part of buffer (skip length + 2 bytes, length 40)\n        uint256 hashValue;\n        assembly (\"memory-safe\") {\n            hashValue := shr(96, keccak256(add(buffer, 0x22), 40))\n        }\n\n        for (uint256 i = 41; i > 1; --i) {\n            // possible values for buffer[i] are 48 (0) to 57 (9) and 97 (a) to 102 (f)\n            if (hashValue & 0xf > 7 && uint8(buffer[i]) > 96) {\n                // case shift by xoring with 0x20\n                buffer[i] ^= 0x20;\n            }\n            hashValue >>= 4;\n        }\n        return string(buffer);\n    }\n\n    /**\n     * @dev Returns true if the two strings are equal.\n     */\n    function equal(string memory a, string memory b) internal pure returns (bool) {\n        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));\n    }\n\n    /**\n     * @dev Parse a decimal string and returns the value as a `uint256`.\n     *\n     * Requirements:\n     * - The string must be formatted as `[0-9]*`\n     * - The result must fit into an `uint256` type\n     */\n    function parseUint(string memory input) internal pure returns (uint256) {\n        return parseUint(input, 0, bytes(input).length);\n    }\n\n    /**\n     * @dev Variant of {parseUint} that parses a substring of `input` located between position `begin` (included) and\n     * `end` (excluded).\n     *\n     * Requirements:\n     * - The substring must be formatted as `[0-9]*`\n     * - The result must fit into an `uint256` type\n     */\n    function parseUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {\n        (bool success, uint256 value) = tryParseUint(input, begin, end);\n        if (!success) revert StringsInvalidChar();\n        return value;\n    }\n\n    /**\n     * @dev Variant of {parseUint-string} that returns false if the parsing fails because of an invalid character.\n     *\n     * NOTE: This function will revert if the result does not fit in a `uint256`.\n     */\n    function tryParseUint(string memory input) internal pure returns (bool success, uint256 value) {\n        return _tryParseUintUncheckedBounds(input, 0, bytes(input).length);\n    }\n\n    /**\n     * @dev Variant of {parseUint-string-uint256-uint256} that returns false if the parsing fails because of an invalid\n     * character.\n     *\n     * NOTE: This function will revert if the result does not fit in a `uint256`.\n     */\n    function tryParseUint(\n        string memory input,\n        uint256 begin,\n        uint256 end\n    ) internal pure returns (bool success, uint256 value) {\n        if (end > bytes(input).length || begin > end) return (false, 0);\n        return _tryParseUintUncheckedBounds(input, begin, end);\n    }\n\n    /**\n     * @dev Implementation of {tryParseUint} that does not check bounds. Caller should make sure that\n     * `begin <= end <= input.length`. Other inputs would result in undefined behavior.\n     */\n    function _tryParseUintUncheckedBounds(\n        string memory input,\n        uint256 begin,\n        uint256 end\n    ) private pure returns (bool success, uint256 value) {\n        bytes memory buffer = bytes(input);\n\n        uint256 result = 0;\n        for (uint256 i = begin; i < end; ++i) {\n            uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));\n            if (chr > 9) return (false, 0);\n            result *= 10;\n            result += chr;\n        }\n        return (true, result);\n    }\n\n    /**\n     * @dev Parse a decimal string and returns the value as a `int256`.\n     *\n     * Requirements:\n     * - The string must be formatted as `[-+]?[0-9]*`\n     * - The result must fit in an `int256` type.\n     */\n    function parseInt(string memory input) internal pure returns (int256) {\n        return parseInt(input, 0, bytes(input).length);\n    }\n\n    /**\n     * @dev Variant of {parseInt-string} that parses a substring of `input` located between position `begin` (included) and\n     * `end` (excluded).\n     *\n     * Requirements:\n     * - The substring must be formatted as `[-+]?[0-9]*`\n     * - The result must fit in an `int256` type.\n     */\n    function parseInt(string memory input, uint256 begin, uint256 end) internal pure returns (int256) {\n        (bool success, int256 value) = tryParseInt(input, begin, end);\n        if (!success) revert StringsInvalidChar();\n        return value;\n    }\n\n    /**\n     * @dev Variant of {parseInt-string} that returns false if the parsing fails because of an invalid character or if\n     * the result does not fit in a `int256`.\n     *\n     * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.\n     */\n    function tryParseInt(string memory input) internal pure returns (bool success, int256 value) {\n        return _tryParseIntUncheckedBounds(input, 0, bytes(input).length);\n    }\n\n    uint256 private constant ABS_MIN_INT256 = 2 ** 255;\n\n    /**\n     * @dev Variant of {parseInt-string-uint256-uint256} that returns false if the parsing fails because of an invalid\n     * character or if the result does not fit in a `int256`.\n     *\n     * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.\n     */\n    function tryParseInt(\n        string memory input,\n        uint256 begin,\n        uint256 end\n    ) internal pure returns (bool success, int256 value) {\n        if (end > bytes(input).length || begin > end) return (false, 0);\n        return _tryParseIntUncheckedBounds(input, begin, end);\n    }\n\n    /**\n     * @dev Implementation of {tryParseInt} that does not check bounds. Caller should make sure that\n     * `begin <= end <= input.length`. Other inputs would result in undefined behavior.\n     */\n    function _tryParseIntUncheckedBounds(\n        string memory input,\n        uint256 begin,\n        uint256 end\n    ) private pure returns (bool success, int256 value) {\n        bytes memory buffer = bytes(input);\n\n        // Check presence of a negative sign.\n        bytes1 sign = begin == end ? bytes1(0) : bytes1(_unsafeReadBytesOffset(buffer, begin)); // don't do out-of-bound (possibly unsafe) read if sub-string is empty\n        bool positiveSign = sign == bytes1(\"+\");\n        bool negativeSign = sign == bytes1(\"-\");\n        uint256 offset = (positiveSign || negativeSign).toUint();\n\n        (bool absSuccess, uint256 absValue) = tryParseUint(input, begin + offset, end);\n\n        if (absSuccess && absValue < ABS_MIN_INT256) {\n            return (true, negativeSign ? -int256(absValue) : int256(absValue));\n        } else if (absSuccess && negativeSign && absValue == ABS_MIN_INT256) {\n            return (true, type(int256).min);\n        } else return (false, 0);\n    }\n\n    /**\n     * @dev Parse a hexadecimal string (with or without \"0x\" prefix), and returns the value as a `uint256`.\n     *\n     * Requirements:\n     * - The string must be formatted as `(0x)?[0-9a-fA-F]*`\n     * - The result must fit in an `uint256` type.\n     */\n    function parseHexUint(string memory input) internal pure returns (uint256) {\n        return parseHexUint(input, 0, bytes(input).length);\n    }\n\n    /**\n     * @dev Variant of {parseHexUint} that parses a substring of `input` located between position `begin` (included) and\n     * `end` (excluded).\n     *\n     * Requirements:\n     * - The substring must be formatted as `(0x)?[0-9a-fA-F]*`\n     * - The result must fit in an `uint256` type.\n     */\n    function parseHexUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {\n        (bool success, uint256 value) = tryParseHexUint(input, begin, end);\n        if (!success) revert StringsInvalidChar();\n        return value;\n    }\n\n    /**\n     * @dev Variant of {parseHexUint-string} that returns false if the parsing fails because of an invalid character.\n     *\n     * NOTE: This function will revert if the result does not fit in a `uint256`.\n     */\n    function tryParseHexUint(string memory input) internal pure returns (bool success, uint256 value) {\n        return _tryParseHexUintUncheckedBounds(input, 0, bytes(input).length);\n    }\n\n    /**\n     * @dev Variant of {parseHexUint-string-uint256-uint256} that returns false if the parsing fails because of an\n     * invalid character.\n     *\n     * NOTE: This function will revert if the result does not fit in a `uint256`.\n     */\n    function tryParseHexUint(\n        string memory input,\n        uint256 begin,\n        uint256 end\n    ) internal pure returns (bool success, uint256 value) {\n        if (end > bytes(input).length || begin > end) return (false, 0);\n        return _tryParseHexUintUncheckedBounds(input, begin, end);\n    }\n\n    /**\n     * @dev Implementation of {tryParseHexUint} that does not check bounds. Caller should make sure that\n     * `begin <= end <= input.length`. Other inputs would result in undefined behavior.\n     */\n    function _tryParseHexUintUncheckedBounds(\n        string memory input,\n        uint256 begin,\n        uint256 end\n    ) private pure returns (bool success, uint256 value) {\n        bytes memory buffer = bytes(input);\n\n        // skip 0x prefix if present\n        bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(buffer, begin)) == bytes2(\"0x\"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty\n        uint256 offset = hasPrefix.toUint() * 2;\n\n        uint256 result = 0;\n        for (uint256 i = begin + offset; i < end; ++i) {\n            uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));\n            if (chr > 15) return (false, 0);\n            result *= 16;\n            unchecked {\n                // Multiplying by 16 is equivalent to a shift of 4 bits (with additional overflow check).\n                // This guaratees that adding a value < 16 will not cause an overflow, hence the unchecked.\n                result += chr;\n            }\n        }\n        return (true, result);\n    }\n\n    /**\n     * @dev Parse a hexadecimal string (with or without \"0x\" prefix), and returns the value as an `address`.\n     *\n     * Requirements:\n     * - The string must be formatted as `(0x)?[0-9a-fA-F]{40}`\n     */\n    function parseAddress(string memory input) internal pure returns (address) {\n        return parseAddress(input, 0, bytes(input).length);\n    }\n\n    /**\n     * @dev Variant of {parseAddress} that parses a substring of `input` located between position `begin` (included) and\n     * `end` (excluded).\n     *\n     * Requirements:\n     * - The substring must be formatted as `(0x)?[0-9a-fA-F]{40}`\n     */\n    function parseAddress(string memory input, uint256 begin, uint256 end) internal pure returns (address) {\n        (bool success, address value) = tryParseAddress(input, begin, end);\n        if (!success) revert StringsInvalidAddressFormat();\n        return value;\n    }\n\n    /**\n     * @dev Variant of {parseAddress-string} that returns false if the parsing fails because the input is not a properly\n     * formatted address. See {parseAddress} requirements.\n     */\n    function tryParseAddress(string memory input) internal pure returns (bool success, address value) {\n        return tryParseAddress(input, 0, bytes(input).length);\n    }\n\n    /**\n     * @dev Variant of {parseAddress-string-uint256-uint256} that returns false if the parsing fails because input is not a properly\n     * formatted address. See {parseAddress} requirements.\n     */\n    function tryParseAddress(\n        string memory input,\n        uint256 begin,\n        uint256 end\n    ) internal pure returns (bool success, address value) {\n        if (end > bytes(input).length || begin > end) return (false, address(0));\n\n        bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(bytes(input), begin)) == bytes2(\"0x\"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty\n        uint256 expectedLength = 40 + hasPrefix.toUint() * 2;\n\n        // check that input is the correct length\n        if (end - begin == expectedLength) {\n            // length guarantees that this does not overflow, and value is at most type(uint160).max\n            (bool s, uint256 v) = _tryParseHexUintUncheckedBounds(input, begin, end);\n            return (s, address(uint160(v)));\n        } else {\n            return (false, address(0));\n        }\n    }\n\n    function _tryParseChr(bytes1 chr) private pure returns (uint8) {\n        uint8 value = uint8(chr);\n\n        // Try to parse `chr`:\n        // - Case 1: [0-9]\n        // - Case 2: [a-f]\n        // - Case 3: [A-F]\n        // - otherwise not supported\n        unchecked {\n            if (value > 47 && value < 58) value -= 48;\n            else if (value > 96 && value < 103) value -= 87;\n            else if (value > 64 && value < 71) value -= 55;\n            else return type(uint8).max;\n        }\n\n        return value;\n    }\n\n    /**\n     * @dev Reads a bytes32 from a bytes array without bounds checking.\n     *\n     * NOTE: making this function internal would mean it could be used with memory unsafe offset, and marking the\n     * assembly block as such would prevent some optimizations.\n     */\n    function _unsafeReadBytesOffset(bytes memory buffer, uint256 offset) private pure returns (bytes32 value) {\n        // This is not memory safe in the general case, but all calls to this private function are within bounds.\n        assembly (\"memory-safe\") {\n            value := mload(add(buffer, add(0x20, offset)))\n        }\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/utils/types/Time.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/types/Time.sol)\n\npragma solidity ^0.8.20;\n\nimport {Math} from \"../math/Math.sol\";\nimport {SafeCast} from \"../math/SafeCast.sol\";\n\n/**\n * @dev This library provides helpers for manipulating time-related objects.\n *\n * It uses the following types:\n * - `uint48` for timepoints\n * - `uint32` for durations\n *\n * While the library doesn't provide specific types for timepoints and duration, it does provide:\n * - a `Delay` type to represent duration that can be programmed to change value automatically at a given point\n * - additional helper functions\n */\nlibrary Time {\n    using Time for *;\n\n    /**\n     * @dev Get the block timestamp as a Timepoint.\n     */\n    function timestamp() internal view returns (uint48) {\n        return SafeCast.toUint48(block.timestamp);\n    }\n\n    /**\n     * @dev Get the block number as a Timepoint.\n     */\n    function blockNumber() internal view returns (uint48) {\n        return SafeCast.toUint48(block.number);\n    }\n\n    // ==================================================== Delay =====================================================\n    /**\n     * @dev A `Delay` is a uint32 duration that can be programmed to change value automatically at a given point in the\n     * future. The \"effect\" timepoint describes when the transitions happens from the \"old\" value to the \"new\" value.\n     * This allows updating the delay applied to some operation while keeping some guarantees.\n     *\n     * In particular, the {update} function guarantees that if the delay is reduced, the old delay still applies for\n     * some time. For example if the delay is currently 7 days to do an upgrade, the admin should not be able to set\n     * the delay to 0 and upgrade immediately. If the admin wants to reduce the delay, the old delay (7 days) should\n     * still apply for some time.\n     *\n     *\n     * The `Delay` type is 112 bits long, and packs the following:\n     *\n     * ```\n     *   | [uint48]: effect date (timepoint)\n     *   |           | [uint32]: value before (duration)\n     *   ↓           ↓       ↓ [uint32]: value after (duration)\n     * 0xAAAAAAAAAAAABBBBBBBBCCCCCCCC\n     * ```\n     *\n     * NOTE: The {get} and {withUpdate} functions operate using timestamps. Block number based delays are not currently\n     * supported.\n     */\n    type Delay is uint112;\n\n    /**\n     * @dev Wrap a duration into a Delay to add the one-step \"update in the future\" feature\n     */\n    function toDelay(uint32 duration) internal pure returns (Delay) {\n        return Delay.wrap(duration);\n    }\n\n    /**\n     * @dev Get the value at a given timepoint plus the pending value and effect timepoint if there is a scheduled\n     * change after this timepoint. If the effect timepoint is 0, then the pending value should not be considered.\n     */\n    function _getFullAt(\n        Delay self,\n        uint48 timepoint\n    ) private pure returns (uint32 valueBefore, uint32 valueAfter, uint48 effect) {\n        (valueBefore, valueAfter, effect) = self.unpack();\n        return effect <= timepoint ? (valueAfter, 0, 0) : (valueBefore, valueAfter, effect);\n    }\n\n    /**\n     * @dev Get the current value plus the pending value and effect timepoint if there is a scheduled change. If the\n     * effect timepoint is 0, then the pending value should not be considered.\n     */\n    function getFull(Delay self) internal view returns (uint32 valueBefore, uint32 valueAfter, uint48 effect) {\n        return _getFullAt(self, timestamp());\n    }\n\n    /**\n     * @dev Get the current value.\n     */\n    function get(Delay self) internal view returns (uint32) {\n        (uint32 delay, , ) = self.getFull();\n        return delay;\n    }\n\n    /**\n     * @dev Update a Delay object so that it takes a new duration after a timepoint that is automatically computed to\n     * enforce the old delay at the moment of the update. Returns the updated Delay object and the timestamp when the\n     * new delay becomes effective.\n     */\n    function withUpdate(\n        Delay self,\n        uint32 newValue,\n        uint32 minSetback\n    ) internal view returns (Delay updatedDelay, uint48 effect) {\n        uint32 value = self.get();\n        uint32 setback = uint32(Math.max(minSetback, value > newValue ? value - newValue : 0));\n        effect = timestamp() + setback;\n        return (pack(value, newValue, effect), effect);\n    }\n\n    /**\n     * @dev Split a delay into its components: valueBefore, valueAfter and effect (transition timepoint).\n     */\n    function unpack(Delay self) internal pure returns (uint32 valueBefore, uint32 valueAfter, uint48 effect) {\n        uint112 raw = Delay.unwrap(self);\n\n        valueAfter = uint32(raw);\n        valueBefore = uint32(raw >> 32);\n        effect = uint48(raw >> 64);\n\n        return (valueBefore, valueAfter, effect);\n    }\n\n    /**\n     * @dev pack the components into a Delay object.\n     */\n    function pack(uint32 valueBefore, uint32 valueAfter, uint48 effect) internal pure returns (Delay) {\n        return Delay.wrap((uint112(effect) << 64) | (uint112(valueBefore) << 32) | uint112(valueAfter));\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC20} from \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC-20 standard.\n */\ninterface IERC20Metadata is IERC20 {\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() external view returns (string memory);\n\n    /**\n     * @dev Returns the symbol of the token.\n     */\n    function symbol() external view returns (string memory);\n\n    /**\n     * @dev Returns the decimals places of the token.\n     */\n    function decimals() external view returns (uint8);\n}\n"},{"file_path":"@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {IERC20Metadata} from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport {ContextUpgradeable} from \"../../utils/ContextUpgradeable.sol\";\nimport {IERC20Errors} from \"@openzeppelin/contracts/interfaces/draft-IERC6093.sol\";\nimport {Initializable} from \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * The default value of {decimals} is 18. To change this, you should override\n * this function so it returns a different value.\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC-20\n * applications.\n */\nabstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors {\n    /// @custom:storage-location erc7201:openzeppelin.storage.ERC20\n    struct ERC20Storage {\n        mapping(address account => uint256) _balances;\n\n        mapping(address account => mapping(address spender => uint256)) _allowances;\n\n        uint256 _totalSupply;\n\n        string _name;\n        string _symbol;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.ERC20\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00;\n\n    function _getERC20Storage() private pure returns (ERC20Storage storage $) {\n        assembly {\n            $.slot := ERC20StorageLocation\n        }\n    }\n\n    /**\n     * @dev Sets the values for {name} and {symbol}.\n     *\n     * Both values are immutable: they can only be set once during construction.\n     */\n    function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {\n        __ERC20_init_unchained(name_, symbol_);\n    }\n\n    function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {\n        ERC20Storage storage $ = _getERC20Storage();\n        $._name = name_;\n        $._symbol = symbol_;\n    }\n\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() public view virtual returns (string memory) {\n        ERC20Storage storage $ = _getERC20Storage();\n        return $._name;\n    }\n\n    /**\n     * @dev Returns the symbol of the token, usually a shorter version of the\n     * name.\n     */\n    function symbol() public view virtual returns (string memory) {\n        ERC20Storage storage $ = _getERC20Storage();\n        return $._symbol;\n    }\n\n    /**\n     * @dev Returns the number of decimals used to get its user representation.\n     * For example, if `decimals` equals `2`, a balance of `505` tokens should\n     * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n     *\n     * Tokens usually opt for a value of 18, imitating the relationship between\n     * Ether and Wei. This is the default value returned by this function, unless\n     * it's overridden.\n     *\n     * NOTE: This information is only used for _display_ purposes: it in\n     * no way affects any of the arithmetic of the contract, including\n     * {IERC20-balanceOf} and {IERC20-transfer}.\n     */\n    function decimals() public view virtual returns (uint8) {\n        return 18;\n    }\n\n    /// @inheritdoc IERC20\n    function totalSupply() public view virtual returns (uint256) {\n        ERC20Storage storage $ = _getERC20Storage();\n        return $._totalSupply;\n    }\n\n    /// @inheritdoc IERC20\n    function balanceOf(address account) public view virtual returns (uint256) {\n        ERC20Storage storage $ = _getERC20Storage();\n        return $._balances[account];\n    }\n\n    /**\n     * @dev See {IERC20-transfer}.\n     *\n     * Requirements:\n     *\n     * - `to` cannot be the zero address.\n     * - the caller must have a balance of at least `value`.\n     */\n    function transfer(address to, uint256 value) public virtual returns (bool) {\n        address owner = _msgSender();\n        _transfer(owner, to, value);\n        return true;\n    }\n\n    /// @inheritdoc IERC20\n    function allowance(address owner, address spender) public view virtual returns (uint256) {\n        ERC20Storage storage $ = _getERC20Storage();\n        return $._allowances[owner][spender];\n    }\n\n    /**\n     * @dev See {IERC20-approve}.\n     *\n     * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on\n     * `transferFrom`. This is semantically equivalent to an infinite approval.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     */\n    function approve(address spender, uint256 value) public virtual returns (bool) {\n        address owner = _msgSender();\n        _approve(owner, spender, value);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-transferFrom}.\n     *\n     * Skips emitting an {Approval} event indicating an allowance update. This is not\n     * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].\n     *\n     * NOTE: Does not update the allowance if the current allowance\n     * is the maximum `uint256`.\n     *\n     * Requirements:\n     *\n     * - `from` and `to` cannot be the zero address.\n     * - `from` must have a balance of at least `value`.\n     * - the caller must have allowance for ``from``'s tokens of at least\n     * `value`.\n     */\n    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {\n        address spender = _msgSender();\n        _spendAllowance(from, spender, value);\n        _transfer(from, to, value);\n        return true;\n    }\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to`.\n     *\n     * This internal function is equivalent to {transfer}, and can be used to\n     * e.g. implement automatic token fees, slashing mechanisms, etc.\n     *\n     * Emits a {Transfer} event.\n     *\n     * NOTE: This function is not virtual, {_update} should be overridden instead.\n     */\n    function _transfer(address from, address to, uint256 value) internal {\n        if (from == address(0)) {\n            revert ERC20InvalidSender(address(0));\n        }\n        if (to == address(0)) {\n            revert ERC20InvalidReceiver(address(0));\n        }\n        _update(from, to, value);\n    }\n\n    /**\n     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`\n     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding\n     * this function.\n     *\n     * Emits a {Transfer} event.\n     */\n    function _update(address from, address to, uint256 value) internal virtual {\n        ERC20Storage storage $ = _getERC20Storage();\n        if (from == address(0)) {\n            // Overflow check required: The rest of the code assumes that totalSupply never overflows\n            $._totalSupply += value;\n        } else {\n            uint256 fromBalance = $._balances[from];\n            if (fromBalance < value) {\n                revert ERC20InsufficientBalance(from, fromBalance, value);\n            }\n            unchecked {\n                // Overflow not possible: value <= fromBalance <= totalSupply.\n                $._balances[from] = fromBalance - value;\n            }\n        }\n\n        if (to == address(0)) {\n            unchecked {\n                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.\n                $._totalSupply -= value;\n            }\n        } else {\n            unchecked {\n                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.\n                $._balances[to] += value;\n            }\n        }\n\n        emit Transfer(from, to, value);\n    }\n\n    /**\n     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).\n     * Relies on the `_update` mechanism\n     *\n     * Emits a {Transfer} event with `from` set to the zero address.\n     *\n     * NOTE: This function is not virtual, {_update} should be overridden instead.\n     */\n    function _mint(address account, uint256 value) internal {\n        if (account == address(0)) {\n            revert ERC20InvalidReceiver(address(0));\n        }\n        _update(address(0), account, value);\n    }\n\n    /**\n     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.\n     * Relies on the `_update` mechanism.\n     *\n     * Emits a {Transfer} event with `to` set to the zero address.\n     *\n     * NOTE: This function is not virtual, {_update} should be overridden instead\n     */\n    function _burn(address account, uint256 value) internal {\n        if (account == address(0)) {\n            revert ERC20InvalidSender(address(0));\n        }\n        _update(account, address(0), value);\n    }\n\n    /**\n     * @dev Sets `value` as the allowance of `spender` over the `owner`'s tokens.\n     *\n     * This internal function is equivalent to `approve`, and can be used to\n     * e.g. set automatic allowances for certain subsystems, etc.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `owner` cannot be the zero address.\n     * - `spender` cannot be the zero address.\n     *\n     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.\n     */\n    function _approve(address owner, address spender, uint256 value) internal {\n        _approve(owner, spender, value, true);\n    }\n\n    /**\n     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.\n     *\n     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by\n     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any\n     * `Approval` event during `transferFrom` operations.\n     *\n     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to\n     * true using the following override:\n     *\n     * ```solidity\n     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {\n     *     super._approve(owner, spender, value, true);\n     * }\n     * ```\n     *\n     * Requirements are the same as {_approve}.\n     */\n    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {\n        ERC20Storage storage $ = _getERC20Storage();\n        if (owner == address(0)) {\n            revert ERC20InvalidApprover(address(0));\n        }\n        if (spender == address(0)) {\n            revert ERC20InvalidSpender(address(0));\n        }\n        $._allowances[owner][spender] = value;\n        if (emitEvent) {\n            emit Approval(owner, spender, value);\n        }\n    }\n\n    /**\n     * @dev Updates `owner`'s allowance for `spender` based on spent `value`.\n     *\n     * Does not update the allowance value in case of infinite allowance.\n     * Revert if not enough allowance is available.\n     *\n     * Does not emit an {Approval} event.\n     */\n    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {\n        uint256 currentAllowance = allowance(owner, spender);\n        if (currentAllowance < type(uint256).max) {\n            if (currentAllowance < value) {\n                revert ERC20InsufficientAllowance(spender, currentAllowance, value);\n            }\n            unchecked {\n                _approve(owner, spender, currentAllowance - value, false);\n            }\n        }\n    }\n}\n"},{"file_path":"contracts/oracle/FtsoChainlinkAdapter.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.28;\n\nimport {AccessControlDefaultAdminRules} from\n    \"@openzeppelin/contracts/access/extensions/AccessControlDefaultAdminRules.sol\";\nimport {IFlareContractRegistry} from\n    \"flare-smart-contracts/contracts/userInterfaces/IFlareContractRegistry.sol\";\n\nimport {IAggregatorV3} from \"../core/interfaces/IAggregatorV3.sol\";\n\n/**\n * @title  IFtsoV2View\n * @notice View-shaped slice of FTSO v2's `getFeedByIdInWei`.\n *\n *         The upstream `FtsoV2Interface` declares the function `payable`. We\n *         redeclare the same selector here as `view` so the Solidity\n *         compiler emits a `STATICCALL` instead of a `CALL`. While the\n *         wrapped feed carries a zero fee — FTSO v2's default today — the\n *         call is state-neutral and the `STATICCALL` succeeds, letting this\n *         contract serve a fully-`view` Chainlink read surface.\n */\ninterface IFtsoV2View {\n    function getFeedByIdInWei(bytes21 _feedId)\n        external\n        view\n        returns (uint256 _value, uint64 _timestamp);\n}\n\n/**\n * @title  FtsoChainlinkAdapter\n * @notice Chainlink `AggregatorV3`-shaped, fully-`view` aggregator backed\n *         by Flare FTSO v2.\n *\n *         **Read-surface semantics**\n *\n *         Every read pulls live from FTSO over a `STATICCALL` — no keeper,\n *         no cached round, no storage writes on the hot path. Consumers\n *         that only call `latestRoundData()` (the vast majority of\n *         Chainlink integrations) plug in unchanged.\n *\n *         - `decimals() == 18` — `getFeedByIdInWei` returns 1e18-scaled\n *           values, so `int256 answer` is the 1e18-wei price.\n *         - `roundId` is the FTSO publication timestamp cast to `uint80`.\n *           Within a single publication window every read returns the\n *           same round id; across a feed rotation, monotonicity is *not*\n *           guaranteed. Consumers that rely on monotone round ids must\n *           guard themselves.\n *         - `getRoundData(roundId)` reverts `NotImplemented()` — only the\n *           currently-published round exists on-chain. For verified\n *           history, use the source FTSO + a Merkle proof.\n *         - `answer == 0` is passed through unchanged. Downstream\n *           consumers must check freshness (`updatedAt`) and zero-value\n *           handling per their own policy.\n *\n *         **Zero-fee feeds only.** This contract only works while FTSO's\n *         `getFeedByIdInWei` is callable without paying a fee. When Flare\n *         turns fees on for the wrapped pair, reads start reverting and\n *         operators must migrate consumers to a fee-aware variant.\n *\n *         **Governance — role-based, no timelock**\n *\n *         Two roles, both managed via {AccessControlDefaultAdminRules}:\n *\n *         - `DEFAULT_ADMIN_ROLE` — single-holder meta-role. The only role\n *           that may grant or revoke `FEED_ADMIN_ROLE`. Transferring the\n *           admin role is itself two-step and delayed (the {ACDAR} guarantee),\n *           which removes the \"instant admin takeover\" failure mode that\n *           plain {AccessControl} carries.\n *         - `FEED_ADMIN_ROLE` — gates {setFeedConfig}, the single mutating\n *           administrative function. Holders may rotate the feed\n *           atomically with its description.\n *\n *         **Compromise model**\n *\n *         By design, this contract has no execution delay. A stolen\n *         `FEED_ADMIN_ROLE` key can rotate the feed in a single transaction\n *         — accept this risk explicitly by holding the role on a\n *         multisig. The admin's remediation is to call `revokeRole` on\n *         the compromised holder; ACDAR's delay on the admin role itself\n *         prevents an attacker from locking the legitimate admin out.\n *\n *         If you want execution delays as well, deploy this adapter\n *         downstream of an {OpenZeppelin TimelockController} that holds\n *         `FEED_ADMIN_ROLE`.\n */\ncontract FtsoChainlinkAdapter is AccessControlDefaultAdminRules, IAggregatorV3 {\n    // ------------------------------------------------------------------\n    // Roles\n    // ------------------------------------------------------------------\n\n    /// @notice Holder may rotate the feed id + description via\n    ///         {setFeedConfig}. Granted and revoked by `DEFAULT_ADMIN_ROLE`.\n    bytes32 public constant FEED_ADMIN_ROLE = keccak256(\"FEED_ADMIN_ROLE\");\n\n    // ------------------------------------------------------------------\n    // Errors\n    // ------------------------------------------------------------------\n\n    /// @notice The supplied feed identifier is zero.\n    error FeedIdEmpty();\n\n    /// @notice The supplied feed identifier matches the currently configured feed.\n    error FeedIdUnchanged();\n\n    /// @notice The FTSO feed value cannot be represented as a signed Chainlink answer.\n    /// @param value The feed value that exceeds type(int256).max.\n    error FeedValueExceedsInt256(uint256 value);\n\n    /// @notice Historical round lookup is unsupported; use latestRoundData for the current value.\n    error NotImplemented();\n\n    // ------------------------------------------------------------------\n    // Events\n    // ------------------------------------------------------------------\n\n    /// @notice Emitted when the feed identifier and description are configured.\n    /// @dev Also emitted during construction, with a zero old feed identifier and an empty old description.\n    /// @param oldFeedId The previous FTSO feed identifier.\n    /// @param newFeedId The newly configured FTSO feed identifier.\n    /// @param oldDescription The previous feed description.\n    /// @param newDescription The newly configured feed description.\n    event FeedConfigUpdated(\n        bytes21 indexed oldFeedId,\n        bytes21 indexed newFeedId,\n        string oldDescription,\n        string newDescription\n    );\n\n    // ------------------------------------------------------------------\n    // Constants — Chainlink conventions\n    // ------------------------------------------------------------------\n\n    uint256 public constant override version = 1;\n    uint8 public constant override decimals = 18;\n\n    /// @notice Canonical `FlareContractRegistry`, identical address on every\n    ///         Flare-family network (Flare, Songbird, Coston, Coston2).\n    IFlareContractRegistry public constant REGISTRY =\n        IFlareContractRegistry(0xaD67FE66660Fb8dFE9d6b1b4240d8650e30F6019);\n\n    // ------------------------------------------------------------------\n    // Storage\n    // ------------------------------------------------------------------\n\n    /// @notice FTSO feed id this aggregator wraps.\n    bytes21 public feedId;\n\n    /// @notice Aggregator description (e.g. `\"XRP / USD\"`). Rotated\n    ///         atomically with {feedId} via {setFeedConfig}.\n    string public override description;\n\n    // ------------------------------------------------------------------\n    // Constructor\n    // ------------------------------------------------------------------\n\n    /**\n     * @param admin              Initial `DEFAULT_ADMIN_ROLE` holder\n     *                           (multisig / governance). Must be non-zero.\n     * @param adminTransferDelay Cooldown enforced on\n     *                           `DEFAULT_ADMIN_ROLE` transfers (see\n     *                           {AccessControlDefaultAdminRules}). Three\n     *                           days is a sensible default — long enough\n     *                           to react to a hostile transfer, short\n     *                           enough to recover from operational\n     *                           mistakes.\n     * @param feedAdmin          Initial `FEED_ADMIN_ROLE` holder\n     *                           (multisig / hot wallet that may rotate\n     *                           the feed).\n     * @param initialFeedId      FTSO v2 feed id to wrap.\n     * @param initialDescription Human-readable description, Chainlink-style.\n     */\n    constructor(\n        address admin,\n        uint48 adminTransferDelay,\n        address feedAdmin,\n        bytes21 initialFeedId,\n        string memory initialDescription\n    ) AccessControlDefaultAdminRules(adminTransferDelay, admin) {\n        // Skip the grant when `feedAdmin == 0` so deployers can spin up a\n        // role-frozen adapter and have the admin appoint a holder later.\n        // Avoids the AccessControl footgun of `hasRole(role, address(0)) == true`.\n        if (feedAdmin != address(0)) _grantRole(FEED_ADMIN_ROLE, feedAdmin);\n        _setFeedId(initialFeedId, initialDescription);\n    }\n\n    // ------------------------------------------------------------------\n    // Admin — atomic feed rotation\n    // ------------------------------------------------------------------\n\n    /**\n     * @notice Rotate the FTSO feed id and the human-readable description\n     *         atomically. Callable only by `FEED_ADMIN_ROLE`.\n     * @param newFeedId The FTSO feed identifier. Must be nonzero and different from the current feed.\n     * @param newDescription The human-readable description of the new feed.\n     */\n    function setFeedConfig(bytes21 newFeedId, string calldata newDescription)\n        external\n        onlyRole(FEED_ADMIN_ROLE)\n    {\n        _setFeedId(newFeedId, newDescription);\n    }\n\n    /// @dev Shared write path for both the constructor and {setFeedConfig}.\n    ///      Validates `newFeedId`, persists the pair, and emits the event.\n    ///      Reads the previous values straight from storage so the\n    ///      constructor naturally emits the `(0, \"\")` baseline.\n    function _setFeedId(bytes21 newFeedId, string memory newDescription) internal {\n        if (newFeedId == bytes21(0)) revert FeedIdEmpty();\n        if (newFeedId == feedId) revert FeedIdUnchanged();\n\n        bytes21 oldFeedId = feedId;\n        string memory oldDescription = description;\n\n        feedId = newFeedId;\n        description = newDescription;\n\n        emit FeedConfigUpdated(oldFeedId, newFeedId, oldDescription, newDescription);\n    }\n\n    // ------------------------------------------------------------------\n    // IAggregatorV3 — read surface\n    // ------------------------------------------------------------------\n\n    /// @inheritdoc IAggregatorV3\n    /// @dev Historical lookup is not supported — only the currently\n    ///      published round exists on-chain. Mutability is narrowed to\n    ///      `pure` (a permitted Solidity override).\n    function getRoundData(uint80)\n        external\n        pure\n        override\n        returns (uint80, int256, uint256, uint256, uint80)\n    {\n        revert NotImplemented();\n    }\n\n    /// @inheritdoc IAggregatorV3\n    function latestRoundData()\n        external\n        view\n        override\n        returns (\n            uint80 roundId,\n            int256 answer,\n            uint256 startedAt,\n            uint256 updatedAt,\n            uint80 answeredInRound\n        )\n    {\n        (uint256 priceWei, uint64 ftsoTs) = _ftsoV2View().getFeedByIdInWei(feedId);\n        if (priceWei > uint256(type(int256).max)) revert FeedValueExceedsInt256(priceWei);\n\n        roundId = uint80(ftsoTs);\n        answer = int256(priceWei); // safe: bounded by the check immediately above.\n        startedAt = uint256(ftsoTs);\n        updatedAt = uint256(ftsoTs);\n        answeredInRound = roundId;\n    }\n\n    // ------------------------------------------------------------------\n    // Internals\n    // ------------------------------------------------------------------\n\n    function _ftsoV2View() internal view returns (IFtsoV2View) {\n        return IFtsoV2View(REGISTRY.getContractAddressByName(\"FtsoV2\"));\n    }\n}\n"},{"file_path":"contracts/core/interfaces/ICoverOrderAllocator.sol","source_code":"// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.28;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {IFirelightVault} from \"./IFirelightVault.sol\";\nimport {IAggregatorV3} from \"./IAggregatorV3.sol\";\nimport {CoverNFT} from \"../CoverNFT.sol\";\n\n/**\n * @title ICoverOrderAllocator\n * @notice Cover order lifecycle, off-chain matching commit/settle, capacity, and\n *         protocol-concentration configuration for the Firelight cover market.\n *\n * ## Decimal convention\n *\n * All on-chain monetary values (cover amounts, premiums, capacity, allocations,\n * protocolConcentration caps × capacity, etc.) are expressed in a **canonical USD unit** with\n * `CANONICAL_DECIMALS` decimals (currently 18), regardless of the underlying premium /\n * first-loss-buffer / vault-asset token decimals. Throughout this interface, \"canonical USD\"\n * always means `CANONICAL_DECIMALS`-decimal USD. Conversion to/from native token decimals\n * happens only at the boundaries:\n *\n *   - Off-chain: callers MUST normalize inputs (e.g. `MarketAllocationInput.coverAmount`,\n *     merkle leaf `allocatedCover`, `commitAllocation` totalAllocated)\n *     from native decimals to canonical decimals before submitting.\n *   - On-chain transfers: premium `safeTransferFrom`, first-loss-buffer transfer, and\n *     `vault.payout()` are denormalized from canonical decimals back to each token's native\n *     decimals at the transfer call site.\n *\n * Supported tokens must implement `decimals() <= CANONICAL_DECIMALS` (validated on registration).\n *\n * ## Off-chain matching flow\n *\n * The matching algorithm runs off-chain (TypeScript engine). The on-chain contract\n * only validates and applies results via a Merkle commit + settle pattern:\n *\n *   1. Read all PENDING orders for the target period via `getCoverOrdersIdByPeriod(period)`\n *   2. For each orderId, read `getCoverOrder(orderId)` and `getCoverOrderMarkets(orderId)`\n *   3. Read protocolConcentration caps via `getSupportedProtocolConcentrationHashes()` + `getEffectiveProtocolConcentration(hash)\n *   4. Compute capacity: `(config.firstLossBufferToken.balanceOf(config.firstLossBuffer)\n *      + vault.totalAssets() * assetPriceUSD / 10**priceFeedDecimals) * config.effectiveLeverage / config.minCAR`\n *   5. Run the matching algorithm → produces per-order `allocatedCoverPerMarket[]`\n *   6. Build a StandardMerkleTree with leaves: `(uint256 orderId, (bytes32,uint256)[] marketCoverAllocations)`\n *      (premiums are recomputed on-chain at settlement, pro-rata from each order's stored terms)\n *   7. Call `commitAllocation(commitmentPeriod, merkleRoot, totalAllocated, matchingCapacity)`\n *      to commit, where `matchingCapacity` is the capacity of step 4 the matching ran against\n *   8. Call `batchSettleCoverOrder(params)` or `settleCoverOrder(...)` for each order with its Merkle proof\n *\n * ## Leaf encoding\n *\n * Double-hash per OpenZeppelin standard:\n *   `keccak256(bytes.concat(keccak256(abi.encode(orderId, marketCoverAllocations))))`\n *\n * ## ProtocolConcentration cap\n *\n * A market belongs to a protocolConcentration group identified by\n *   `protocolConcentrationHash = keccak256(abi.encode(chainId, protocol))`\n * Multiple markets sharing the same (chainId, protocol) share a single cap. During\n * settlement, cumulative cover per protocolConcentration group must not exceed:\n *   `protocolConcentrationBps * totalAvailableCapacity / 10000`\n *\n * ProtocolConcentration caps are governed independently from market registration. A market\n * whose protocolConcentration has no cap configured is effectively disabled.\n */\ninterface ICoverOrderAllocator {\n    // --- Enums ---\n\n    /// @notice Whether a cover order is a new purchase or a renewal of existing cover.\n    enum CoverOrderType {\n        NEW,\n        RENEWAL\n    }\n\n    /// @notice Lifecycle status of a cover order.\n    enum CoverOrderStatus {\n        PENDING,\n        /// Fully settled: allocated cover equals the requested amount.\n        MATCHED,\n        /// Settled with allocated cover below the requested amount.\n        PARTIAL,\n        CANCELLED\n    }\n\n    // --- Structs ---\n\n    /// @notice A protected market on a specific chain and protocol.\n    struct Market {\n        uint64 chainId;\n        string protocol;\n        /// Protocol-specific market identifier.\n        bytes32 market;\n    }\n\n    /// @notice Input to set a (protocol, chainId) concentration group's cap.\n    struct ProtocolConcentrationInput {\n        uint64 chainId;\n        string protocol;\n        /// Maximum share of capacity the group may take, in bps.\n        uint256 maxProtocolConcentrationBps;\n    }\n\n    /// @notice The (protocol, chainId) tuple a concentration hash was derived from.\n    struct ProtocolConcentration {\n        uint64 chainId;\n        string protocol;\n    }\n\n    /// @notice Initialization parameters for the allocator.\n    struct InitParams {\n        IFirelightVault vault;\n        address premiumCollector;\n        CoverNFT coverNFT;\n        IAggregatorV3 priceFeedAdapter;\n        /// Maximum tolerated price age, in seconds.\n        uint48 maxPriceAge;\n        address[] premiumTokens;\n        /// Receives DEFAULT_ADMIN_ROLE.\n        address admin;\n        /// Receives ADMIN_ROLE.\n        address adminRole;\n        address curatorRole;\n        address allocatorRole;\n        address configAdminRole;\n        ProtocolConcentrationInput[] initialProtocolConcentrations;\n        Market[] newMarkets;\n        /// Initial capacity configuration (effective from period 0).\n        CapacityConfig capacityConfig;\n    }\n\n    /// @notice Per-market cover request submitted when creating an order.\n    struct MarketAllocationInput {\n        bytes32 marketId;\n        /// Annual cover rate, in bps.\n        uint32 coverRateAnnual;\n        /// Requested cover in canonical USD, pre-normalized by the caller\n        /// (e.g. `nativeAmount * 10**(CANONICAL_DECIMALS - premiumTokenDecimals)`).\n        uint256 coverAmount;\n    }\n\n    /// @notice Per-market allocation stored for an order (request + settled result).\n    struct MarketAllocation {\n        bytes32 marketId;\n        /// Annual cover rate, in bps.\n        uint32 coverRateAnnual;\n        /// Requested cover, in canonical USD.\n        uint256 coverAmount;\n        /// Cover actually allocated on settle, in canonical USD.\n        uint256 allocatedCoverAmount;\n    }\n\n    /// @notice Full cover order record.\n    struct CoverOrder {\n        // Slot: buyer (20) + orderType (1) + status (1).\n        address buyer;\n        CoverOrderType orderType;\n        CoverOrderStatus status;\n        address payoutRecipient;\n        /// Off-chain beneficiary reference.\n        string beneficiaryAddress;\n        address premiumToken;\n        /// Total requested cover across markets, in canonical USD.\n        uint256 totalCoverAmount;\n        /// Total premium owed, in canonical USD.\n        uint256 totalPremiumAmount;\n        /// Cover-amount-weighted average annual rate, in bps.\n        uint256 weightedAvgRate;\n        /// Total cover allocated on settle, in canonical USD.\n        uint256 allocatedCoverAmount;\n        /// Premium charged for the allocated cover, in canonical USD.\n        uint256 allocatedPremiumAmount;\n        /// Target period the order is matched and settled in.\n        uint256 period;\n    }\n\n    /// @notice Per-period capacity configuration (checkpointed; effective at currentPeriod() + 1).\n    struct CapacityConfig {\n        /// Minimum capital adequacy ratio, in bps (1.2x to 5x, inclusive).\n        uint256 minCAR;\n        /// ERC20 (decimals ≤ CANONICAL_DECIMALS) used as first-loss-buffer collateral.\n        IERC20 firstLossBufferToken;\n        /// Upper margin (bps) tolerated between the submitted `totalAllocated` and the recomputed\n        /// capacity at commit/recommit, to absorb price/FLB drift. 0 = strict (legacy behavior).\n        uint16 divergenceToleranceBps;\n        /// Custody wallet holding the first-loss-buffer balance.\n        address firstLossBuffer;\n        /// Leverage applied to collateral when computing capacity, in bps\n        /// (> 0, <= MAX_LEVERAGE_FACTOR * minCAR).\n        uint256 effectiveLeverage;\n        /// Minimum per-market cover amount allowed on an order, in canonical USD.\n        uint256 minOrderMarketCoverAmount;\n    }\n\n    /// @notice Committed off-chain matching results and settlement accounting for a period.\n    struct AllocationCommitment {\n        /// Root of the StandardMerkleTree of settlement leaves.\n        bytes32 root;\n        /// Effective period capacity (tolerance included), in canonical USD.\n        uint256 totalAvailableCapacity;\n        /// Declared sum of allocated cover across the tree, in canonical USD.\n        uint256 totalDeclaredAllocated;\n        /// Cumulative cover settled so far, in canonical USD.\n        uint256 totalSettledCover;\n        /// Cumulative premium settled so far, in canonical USD.\n        uint256 totalSettledPremium;\n        /// Timestamp at which the grace elapses and settlement can begin. Frozen at\n        /// commit/recommit time from the then-current `settlementGracePeriod`; later\n        /// grace updates never alter an already-published window.\n        uint48 graceExpiresAt;\n    }\n\n    /// @notice Per-market allocation encoded in a settlement leaf and passed to settle.\n    struct MarketCoverAllocation {\n        bytes32 marketId;\n        /// Allocated cover for the market, in canonical USD.\n        uint256 allocatedCover;\n    }\n\n    /// @notice Parameters to settle a single order in a batch.\n    struct SettleParams {\n        uint256 orderId;\n        MarketCoverAllocation[] marketCoverAllocations;\n        /// Merkle proof of the order's leaf against the period commitment root.\n        bytes32[] proof;\n    }\n\n    // --- Events ---\n\n    /**\n     * @notice Emitted when a cover order is created for the next period.\n     * @param coverOrderId New cover order id.\n     * @param buyer Account that pays the premium.\n     * @param payoutRecipient Account that receives the payout if a covered incident occurs.\n     * @param beneficiaryAddress Off-chain beneficiary reference recorded with the order.\n     * @param premiumToken ERC20 used to pay the premium.\n     * @param totalCoverAmount Total requested cover across markets, in canonical USD.\n     * @param totalPremiumAmount Total premium owed for the order, in canonical USD.\n     * @param weightedAvgRate Cover-amount-weighted average annual rate, in bps.\n     * @param orderType Whether the order is NEW or a RENEWAL.\n     * @param period Target period the order will be matched and settled in.\n     */\n    event CoverOrderCreated(\n        uint256 coverOrderId,\n        address buyer,\n        address payoutRecipient,\n        string beneficiaryAddress,\n        address premiumToken,\n        uint256 totalCoverAmount,\n        uint256 totalPremiumAmount,\n        uint256 weightedAvgRate,\n        CoverOrderType orderType,\n        uint256 period\n    );\n\n    /**\n     * @notice Emitted when an order is settled against the committed Merkle root.\n     * @param coverOrderId Settled cover order id.\n     * @param status Resulting status (MATCHED or PARTIAL).\n     * @param allocatedCoverAmount Total cover allocated to the order, in canonical USD.\n     * @param allocatedPremiumAmount Premium charged for the allocated cover, in canonical USD.\n     */\n    event CoverOrderSettled(\n        uint256 coverOrderId,\n        CoverOrderStatus status,\n        uint256 allocatedCoverAmount,\n        uint256 allocatedPremiumAmount\n    );\n\n    /**\n     * @notice Emitted when a pending order is cancelled.\n     * @param coverOrderId Cancelled cover order id.\n     */\n    event CoverOrderCancelled(uint256 coverOrderId);\n\n    /**\n     * @notice Emitted on each commit or recommit of a period's matching results.\n     * @param period Period the commitment applies to.\n     * @param merkleRoot Root of the StandardMerkleTree of settlement leaves.\n     * @param totalAvailableCapacity Effective period capacity (tolerance included), in canonical USD.\n     * @param totalAllocated Declared sum of allocated cover across the tree, in canonical USD.\n     */\n    event AllocationCommitted(\n        uint256 indexed period,\n        bytes32 merkleRoot,\n        uint256 totalAvailableCapacity,\n        uint256 totalAllocated\n    );\n\n    /**\n     * @notice Emitted when a period's allocation commitment is cancelled before any settlement.\n     * @param period Period whose commitment was cancelled.\n     * @param merkleRoot Root of the cancelled commitment.\n     */\n    event AllocationCommitmentCancelled(uint256 indexed period, bytes32 merkleRoot);\n\n    /**\n     * @notice Emitted when the premium collector address changes.\n     * @param oldCollector Previous premium collector.\n     * @param newCollector New premium collector.\n     */\n    event PremiumCollectorUpdated(address oldCollector, address newCollector);\n\n    /**\n     * @notice Emitted when a market is registered.\n     * @param marketId Derived market id.\n     * @param chainId Chain the market lives on.\n     * @param protocol Protocol name the market belongs to.\n     * @param market Protocol-specific market identifier.\n     */\n    event MarketAdded(bytes32 marketId, uint64 chainId, string protocol, bytes32 market);\n\n    /**\n     * @notice Emitted the first time a (protocol, chainId) concentration group is seen.\n     * @param protocolConcentrationHash Hash identifying the concentration group.\n     * @param protocol Protocol name of the group.\n     * @param chainId Chain id of the group.\n     */\n    event ProtocolConcentrationRegistered(bytes32 indexed protocolConcentrationHash, string protocol, uint64 chainId);\n\n    /**\n     * @notice Emitted when a concentration group's cap is set or updated.\n     * @param protocolConcentrationHash Hash identifying the concentration group.\n     * @param oldShareBps Previous cap, in bps.\n     * @param newShareBps New cap, in bps, effective at currentPeriod() + 1.\n     */\n    event ProtocolConcentrationUpdated(\n        bytes32 indexed protocolConcentrationHash,\n        uint256 oldShareBps,\n        uint256 newShareBps\n    );\n\n    /**\n     * @notice Emitted when a premium token is whitelisted.\n     * @param token Added premium token.\n     */\n    event PremiumTokenAdded(address token);\n\n    /**\n     * @notice Emitted when a premium token is removed from the whitelist.\n     * @param token Removed premium token.\n     */\n    event PremiumTokenRemoved(address token);\n\n    /**\n     * @notice Emitted when a new capacity configuration checkpoint is pushed.\n     * @param config The capacity configuration that becomes effective (currentPeriod() + 1, or 0 for the first one).\n     */\n    event CapacityConfigUpdated(CapacityConfig config);\n\n    /**\n     * @notice Emitted when the price feed adapter is replaced.\n     * @param oldPriceFeedAdapter Previous adapter.\n     * @param oldPriceFeedDecimals Previous adapter decimals.\n     * @param newPriceFeedAdapter New adapter.\n     * @param newPriceFeedDecimals New adapter decimals.\n     */\n    event PriceFeedUpdated(\n        address oldPriceFeedAdapter,\n        uint8 oldPriceFeedDecimals,\n        address newPriceFeedAdapter,\n        uint8 newPriceFeedDecimals\n    );\n\n    /**\n     * @notice Emitted when the maximum tolerated price age changes.\n     * @param oldMaxPriceAge Previous max age, in seconds.\n     * @param newMaxPriceAge New max age, in seconds.\n     */\n    event MaxPriceAgeUpdated(uint48 oldMaxPriceAge, uint48 newMaxPriceAge);\n\n    /**\n     * @notice Emitted when the settlement grace period changes.\n     * @param oldGracePeriod Previous grace period, in seconds.\n     * @param newGracePeriod New grace period, in seconds.\n     */\n    event SettlementGracePeriodUpdated(uint48 oldGracePeriod, uint48 newGracePeriod);\n\n    // --- Errors ---\n\n    // -- order creation / market configuration --\n    /// @notice Thrown when an order is created with no markets.\n    error InvalidMarketsLength();\n    /// @notice Thrown when a market entry has a zero cover amount or zero rate.\n    error InvalidMarketsZeroValue();\n    /// @notice Thrown when an order lists the same market more than once.\n    error DuplicateMarket();\n    /// @notice Thrown when registering a market that already exists.\n    error MarketAlreadyExists();\n    /// @notice Thrown when ordering on a market whose concentration cap is unset (disabled).\n    error ZeroProtocolConcentrationForMarket();\n    /// @notice Thrown when referencing a market that is not registered.\n    error MarketNotFound();\n    /// @notice Thrown when an order with a zero chainId is referenced.\n    error InvalidChainId();\n    /// @notice Thrown when a market's cover amount is below the configured minimum.\n    /// @param amount Provided cover amount, in canonical USD.\n    /// @param minimum Configured minimum, in canonical USD.\n    error OrderMarketCoverAmountTooLow(uint256 amount, uint256 minimum);\n\n    // -- commit / settle --\n    /// @notice Thrown when an order is not in PENDING status for the attempted action.\n    error OrderNotPending();\n    /// @notice Thrown when committing to a period that already has a commitment.\n    error PeriodAlreadyCommitted();\n    /// @notice Thrown when a commit or recommit is so late in the period that its grace window\n    ///         would reach the period end, leaving no instant at which settlement is possible.\n    error CommitTooCloseToPeriodEnd();\n    /// @notice Thrown when the commit/recommit period is not the current vault period.\n    /// @param commitmentPeriod Period the caller passed.\n    /// @param currentPeriod Current vault period at execution.\n    error InvalidCommitmentPeriod(uint256 commitmentPeriod, uint256 currentPeriod);\n    /// @notice Thrown when the Merkle root is the zero hash.\n    error InvalidMerkleRoot();\n    /// @notice Thrown when the declared/settled cover exceeds the effective capacity.\n    /// @param allocation Requested allocation, in canonical USD.\n    /// @param capacity Effective capacity ceiling, in canonical USD.\n    error TotalAllocationOverflow(uint256 allocation, uint256 capacity);\n    /// @notice Thrown when the caller's matching capacity exceeds the live capacity\n    ///         widened by the period's divergence tolerance.\n    /// @param matchingCapacity Capacity the matcher ran against, in canonical USD.\n    /// @param maxCapacity Live capacity ceiling (incl. tolerance), in canonical USD.\n    error MatchingCapacityOverflow(uint256 matchingCapacity, uint256 maxCapacity);\n    /// @notice Thrown when a market's allocated cover exceeds its requested cover.\n    /// @param allocation Allocated cover, in canonical USD.\n    /// @param capacity Requested cover for that market, in canonical USD.\n    error MarketAllocationOverflow(uint256 allocation, uint256 capacity);\n    /// @notice Thrown when cumulative settled cover for a concentration group exceeds its cap.\n    /// @param allocation Cumulative cover for the group, in canonical USD.\n    /// @param capacity Group cap, in canonical USD.\n    error ProtocolConcentrationOverflow(uint256 allocation, uint256 capacity);\n    /// @notice Thrown when cumulative settled cover exceeds the declared allocation.\n    /// @param allocation Cumulative settled cover, in canonical USD.\n    /// @param capacity Declared allocation, in canonical USD.\n    error TotalSettledOverflow(uint256 allocation, uint256 capacity);\n    /// @notice Thrown when the settle market list length differs from the order's markets.\n    error InvalidAllocationMarketsLength();\n    /// @notice Thrown when a settle market id does not match the order's market at that index.\n    error MarketIdMismatch();\n    /// @notice Thrown when an order settles with zero total allocated cover.\n    error ZeroAllocation();\n    /// @notice Thrown when there is no commitment for the period being settled/recommitted.\n    error NoCommitForPeriod();\n    /// @notice Thrown when the supplied Merkle proof does not verify against the commitment root.\n    error InvalidProof();\n    /// @notice Thrown when recommitting after settlement of the period has already started.\n    error SettlementsAlreadyStarted();\n    /// @notice Thrown when settling before the commitment's grace period has elapsed.\n    /// @param expiresAt Timestamp at which settlement becomes allowed.\n    error GracePeriodActive(uint48 expiresAt);\n    /// @notice Thrown when settling outside the order's settlement window (period advanced).\n    error SettleWindowExpired();\n    /// @notice Thrown when cancelling a non-expired order via the permissionless path.\n    error OrderNotExpired();\n    /// @notice Thrown when an order/orderId is otherwise invalid for the operation.\n    error InvalidOrder();\n\n    // -- admin / configuration --\n    /// @notice Thrown when a required address argument is the zero address.\n    error InvalidZeroAddress();\n    /// @notice Thrown when the configured leverage is zero.\n    error InvalidLeverage();\n    /// @notice Thrown when the configured minimum CAR is outside the supported 1.2x to 5x range.\n    error InvalidMinCAR();\n    /// @notice Thrown when the configured maximum price age is zero.\n    error InvalidMaxPriceAge();\n    /// @notice Thrown when the price feed adapter decimals are outside [6, 18].\n    /// @param decimals Reported adapter decimals.\n    error InvalidPriceFeedDecimals(uint8 decimals);\n    /// @notice Thrown when a concentration input is invalid (empty protocol or bps > 100%).\n    error InvalidProtocolConcentration();\n    /// @notice Thrown when the configured minimum order-market cover amount is zero.\n    error InvalidMinOrderMarketCoverAmount();\n    /// @notice Thrown when the configured divergence tolerance exceeds the hard ceiling.\n    /// @param bps Provided tolerance, in bps.\n    error InvalidDivergenceTolerance(uint16 bps);\n    /// @notice Thrown when the settlement grace period exceeds its ceiling relative to the\n    ///         current period duration.\n    /// @param gracePeriod Provided grace period, in seconds.\n    /// @param maxGracePeriod Maximum allowed grace period, in seconds.\n    error InvalidGracePeriod(uint48 gracePeriod, uint48 maxGracePeriod);\n    /// @notice Thrown when referencing a premium token that is not whitelisted.\n    error UnsupportedPremiumToken();\n    /// @notice Thrown when a token reports decimals greater than the canonical 18.\n    /// @param decimals Reported token decimals.\n    error UnsupportedDecimals(uint8 decimals);\n    /// @notice Thrown when adding a premium token that is already whitelisted.\n    error PremiumTokenAlreadySupported();\n    /// @notice Reserved: thrown when an action is blocked because matching is in progress.\n    error MatchingInProgress();\n    /// @notice Reserved: thrown when a period duration is invalid.\n    error InvalidPeriodDuration();\n\n    // =========================================================================\n    // Write functions\n    // =========================================================================\n\n    /// @notice Creates a cover order for the next period. Only CURATOR_ROLE.\n    /// @param buyer Account that will pay the premium.\n    /// @param payoutRecipient Account that receives the payout if a covered incident occurs.\n    /// @param beneficiaryAddress Off-chain beneficiary reference stored with the order.\n    /// @param premiumToken Whitelisted ERC20 used to pay the premium.\n    /// @param markets Per-market cover requests; `coverAmount` pre-normalized to canonical decimals.\n    /// @param orderType NEW or RENEWAL.\n    /// @return coverOrderId Id assigned to the new order.\n    function createCoverOrder(\n        address buyer,\n        address payoutRecipient,\n        string calldata beneficiaryAddress,\n        address premiumToken,\n        MarketAllocationInput[] calldata markets,\n        CoverOrderType orderType\n    ) external returns (uint256 coverOrderId);\n\n    /// @notice Commits a Merkle root with matching results. Only ALLOCATOR_ROLE.\n    /// @dev The vault asset price is read from the registered `priceFeedAdapter` oracle.\n    ///      Reverts if the grace window (`block.timestamp + settlementGracePeriod`) would\n    ///      reach the period end, since no order could ever settle against the commitment.\n    ///      Stores `matchingCapacity` — the capacity the off-chain matcher ran against —\n    ///      as the commitment's `totalAvailableCapacity`, so the committed tree can be\n    ///      re-derived exactly; the live recompute (widened by `divergenceToleranceBps`)\n    ///      only bounds it.\n    /// @param commitmentPeriod The period the caller intends to match. Must equal\n    ///        `vault.currentPeriod()` at execution time, otherwise the call reverts.\n    /// @param merkleRoot Root of the StandardMerkleTree containing settlement leaves.\n    ///        Each leaf encodes `(orderId, MarketCoverAllocation[])` with\n    ///        `allocatedCover` in canonical USD.\n    /// @param totalAllocated Sum of all allocated cover across all orders in the tree,\n    ///        in canonical USD. Must not exceed `matchingCapacity`.\n    /// @param matchingCapacity Capacity the matching ran against, in canonical USD. Must\n    ///        not exceed the live capacity widened by the period's divergence tolerance.\n    function commitAllocation(\n        uint256 commitmentPeriod,\n        bytes32 merkleRoot,\n        uint256 totalAllocated,\n        uint256 matchingCapacity\n    ) external;\n\n    /// @notice Settles a single order against the committed Merkle root. Only ALLOCATOR_ROLE.\n    /// @dev The premium is computed on-chain, pro-rata per market from the order's stored\n    ///      rates and the vault's period duration — never taken from the caller or the leaf.\n    /// @param orderId Order to settle.\n    /// @param marketCoverAllocations Per-market allocated cover, in canonical USD;\n    ///        order and market ids must match the order's markets.\n    /// @param proof Merkle proof of the leaf for this order against the period commitment root.\n    function settleCoverOrder(\n        uint256 orderId,\n        MarketCoverAllocation[] calldata marketCoverAllocations,\n        bytes32[] calldata proof\n    ) external;\n\n    /// @notice Settles multiple orders in a single transaction. Only ALLOCATOR_ROLE.\n    /// @param params Per-order settle parameters (orderId, allocations, proof).\n    function batchSettleCoverOrder(SettleParams[] calldata params) external;\n\n    /// @notice Cancels a pending cover order. Only CURATOR_ROLE.\n    /// @param coverOrderId Order to cancel; must still be PENDING.\n    function cancelCoverOrder(uint256 coverOrderId) external;\n\n    /// @notice Permissionlessly cancels PENDING orders whose cover period has already elapsed.\n    /// @dev Once `currentPeriod() > order.period` the settle window has closed and the order can\n    ///      never be settled; anyone may clean up the dangling PENDING state in batch.\n    /// @param coverOrderIds Ids of expired PENDING orders to cancel.\n    function cancelExpiredOrders(uint256[] calldata coverOrderIds) external;\n\n    /// @notice Replaces a committed Merkle root if no orders have been settled yet. Only CONFIG_ADMIN_ROLE.\n    /// @dev Restricted to the current period; the new matching capacity is bound to the\n    ///      period's real collateral within tolerance, recomputed from live inputs.\n    ///      Resets the commitment's `graceExpiresAt` from the current `settlementGracePeriod`,\n    ///      under the same period-end proximity check as `commitAllocation` — no commitment can\n    ///      ever store a window past its period end. Recommitting the same root is the supported\n    ///      way to re-derive a published window after a grace update; for a swap late in the\n    ///      period, lower the grace first so the fresh window fits, or use\n    ///      `cancelCommitAllocation` to withdraw the root without a replacement.\n    /// @param period Period whose commitment is replaced; must equal the current vault period.\n    /// @param newMerkleRoot New StandardMerkleTree root of settlement leaves.\n    /// @param newTotalAllocated New declared sum of allocated cover, in canonical USD.\n    /// @param newMatchingCapacity Capacity the new matching ran against, in canonical USD.\n    function recommitAllocation(\n        uint256 period,\n        bytes32 newMerkleRoot,\n        uint256 newTotalAllocated,\n        uint256 newMatchingCapacity\n    ) external;\n\n    /// @notice Cancels a period's commitment if no orders have been settled yet. Only CONFIG_ADMIN_ROLE.\n    /// @dev Emergency path to withdraw a bad Merkle root without providing a replacement and\n    ///      without touching the price feed (unlike `recommitAllocation`, it works while the\n    ///      oracle is down or stale). After cancelling, `commitAllocation` can be called again\n    ///      for the period; if no new commit lands, pending orders expire unsettled and can be\n    ///      cleaned up via `cancelExpiredOrders`.\n    /// @param period Period whose commitment is cancelled; must equal the current vault period.\n    function cancelCommitAllocation(uint256 period) external;\n\n    // =========================================================================\n    // Admin functions\n    // =========================================================================\n\n    /// @notice Sets the address that receives collected premiums. Only ADMIN_ROLE.\n    /// @param newCollector New premium collector; must be non-zero.\n    function setPremiumCollector(address newCollector) external;\n\n    /// @notice Whitelists a premium token and caches its decimals. Only CONFIG_ADMIN_ROLE.\n    /// @param token ERC20 to whitelist; must report `decimals() <= CANONICAL_DECIMALS`.\n    function addSupportedPremiumToken(address token) external;\n\n    /// @notice Removes a premium token from the whitelist. Only CONFIG_ADMIN_ROLE.\n    /// @param token ERC20 to remove.\n    function removeSupportedPremiumToken(address token) external;\n\n    /// @notice Pushes a new capacity configuration checkpoint. Only CONFIG_ADMIN_ROLE.\n    /// @dev Effective at currentPeriod() + 1 (or period 0 for the first configuration).\n    /// @param config New capacity configuration.\n    function setCapacityConfig(CapacityConfig calldata config) external;\n\n    /// @notice Replaces the vault-asset USD price feed adapter. Only ADMIN_ROLE.\n    /// @param newPriceFeedAdapter New adapter; must report decimals in [6, 18].\n    function setPriceFeedAdapter(IAggregatorV3 newPriceFeedAdapter) external;\n\n    /// @notice Sets the maximum tolerated price age. Only ADMIN_ROLE.\n    /// @param newMaxPriceAge New max age in seconds; must be non-zero.\n    function setMaxPriceAge(uint48 newMaxPriceAge) external;\n\n    /// @notice Minimum delay between `commitAllocation`/`recommitAllocation` and `settleCoverOrder`.\n    ///         Gives operators a grace window to swap a bad merkle root via\n    ///         `recommitAllocation` before any settle finalizes. Defaults to 0 (disabled).\n    ///         Hard-capped at 8 hours — at most a third of any period duration, since the\n    ///         vault enforces periods to be multiples of its SMALLEST_PERIOD_DURATION (1 day) —\n    ///         so the grace can never consume the settle window.\n    ///         Only affects future commitments: each commitment freezes its own\n    ///         `graceExpiresAt` at commit/recommit time, so no update can move (or kill) an\n    ///         already-published window. To re-derive a published window under a new grace,\n    ///         recommit the same root via `recommitAllocation`.\n    function setSettlementGracePeriod(uint48 newGracePeriod) external;\n\n    /// @notice Registers a new market. Only CONFIG_ADMIN_ROLE.\n    /// @param newMarket Market to register (chainId, protocol, market).\n    /// @return marketId Derived id of the registered market.\n    function addSupportedMarket(Market calldata newMarket) external returns (bytes32 marketId);\n\n    /// @notice Sets the maxProtocolConcentrationBps cap for a protocolConcentration group derived from (protocol, chainId).\n    /// @dev Affects ALL markets sharing the same (protocol, chainId). Effective from currentPeriod() + 1.\n    /// @param protocolConcentration Group key (chainId, protocol) and its new cap in bps.\n    function setProtocolConcentration(ProtocolConcentrationInput calldata protocolConcentration) external;\n\n    /// @notice Batch variant of {setProtocolConcentration}. Only CONFIG_ADMIN_ROLE.\n    /// @param protocolConcentrations Concentration inputs to apply.\n    function batchSetProtocolConcentration(ProtocolConcentrationInput[] calldata protocolConcentrations) external;\n\n    // =========================================================================\n    // View functions — all data needed for off-chain matching\n    // =========================================================================\n\n    /// @notice The internal canonical decimals used for all on-chain monetary values.\n    function CANONICAL_DECIMALS() external view returns (uint8);\n\n    /// @notice Returns the vault contract (use to call currentPeriod(), totalAssets(), periodConfigurationAtNumber()).\n    function vault() external view returns (IFirelightVault);\n\n    /// @notice Address receiving premium payments.\n    function premiumCollector() external view returns (address);\n\n    /// @notice The CoverNFT contract minted on settle (immutable, set in constructor).\n    function coverNFT() external view returns (CoverNFT);\n\n    /// @notice Chainlink-compatible oracle queried to price the vault asset against USD.\n    function priceFeedAdapter() external view returns (IAggregatorV3);\n\n    /// @notice Max age (seconds) tolerated on `priceFeedAdapter.latestRoundData().updatedAt`.\n    function maxPriceAge() external view returns (uint48);\n\n    /// @notice Grace period (seconds) enforced between commit and settlement.\n    function settlementGracePeriod() external view returns (uint48);\n\n    /// @notice Cached `decimals()` of the registered `priceFeedAdapter`.\n    function priceFeedDecimals() external view returns (uint8);\n\n    /// @notice Next orderId that will be assigned.\n    function nextCoverOrderId() external view returns (uint256);\n\n    /// @notice Full order struct for a given orderId.\n    function getCoverOrder(uint256 coverOrderId) external view returns (CoverOrder memory);\n\n    /// @notice Per-market allocations for an order (marketId, coverRateAnnual, coverAmount).\n    ///         `coverAmount` and `allocatedCoverAmount` are in canonical USD.\n    function getCoverOrderMarkets(uint256 coverOrderId) external view returns (MarketAllocation[] memory);\n\n    /// @notice Period, allocated cover, and payoutRecipient for a given (orderId, marketId) pair.\n    /// @return period Period the order belongs to. Zero when not found.\n    /// @return allocatedCoverAmount Amount allocated to that market on settle, in canonical\n    ///         USD. Zero when not found.\n    /// @return payoutRecipient EVM address that will receive the payout for this cover if a\n    ///         covered incident occurs. `address(0)` means the pair was not found.\n    function getCoverOrderMarketInfo(\n        uint256 coverOrderId,\n        bytes32 marketId\n    ) external view returns (uint256 period, uint256 allocatedCoverAmount, address payoutRecipient);\n\n    /// @notice All orderIds created for a given period.\n    function getCoverOrdersIdByPeriod(uint256 period) external view returns (uint256[] memory);\n\n    /// @notice Market configuration by marketId.\n    function getSupportedMarket(bytes32 marketId) external view returns (Market memory);\n\n    /// @notice All registered marketIds (including markets whose protocolConcentration cap is 0).\n    function getSupportedMarketIds() external view returns (bytes32[] memory);\n\n    /// @notice Whether a premium token is whitelisted.\n    function isPremiumTokenSupported(address token) external view returns (bool);\n\n    /// @notice All currently whitelisted premium tokens.\n    /// @return The set of supported premium token addresses.\n    function getSupportedPremiumTokens() external view returns (address[] memory);\n\n    /// @notice Effective capacity configuration at currentPeriod() + 1.\n    function getEffectiveCapacityConfig() external view returns (CapacityConfig memory);\n\n    /// @notice get active capacity config at a given period\n    function getCapacityConfigAt(uint256 period) external view returns (CapacityConfig memory);\n\n    /// @notice Committed matching data for a given period.\n    function getAllocationCommitment(uint256 period) external view returns (AllocationCommitment memory);\n\n    /// @notice All protocolConcentration hashes ever registered via setProtocolConcentration / initialize.\n    ///         May include entries currently set to 0 bps (disabled).\n    function getSupportedProtocolConcentrationHashes() external view returns (bytes32[] memory);\n\n    /// @notice Returns the (protocol, chainId) tuple that originated the protocolConcentration hash.\n    function getProtocolConcentrationFromHash(\n        bytes32 protocolConcentrationHash\n    ) external view returns (ProtocolConcentration memory);\n\n    /// @notice ProtocolConcentration cap (bps) effective at currentPeriod() + 1.\n    function getEffectiveProtocolConcentration(bytes32 protocolConcentrationHash) external view returns (uint256);\n\n    /// @notice ProtocolConcentration cap (bps) active at the given `period`. Reads from the\n    ///         checkpoint history — useful for auditing past settlements.\n    function getProtocolConcentrationAt(\n        bytes32 protocolConcentrationHash,\n        uint256 period\n    ) external view returns (uint256);\n\n    /// @notice Cumulative settled cover for a specific protocolConcentration group in a given period.\n    function getProtocolConcentrationSettledCover(\n        uint256 period,\n        bytes32 protocolConcentrationHash\n    ) external view returns (uint256);\n}\n"},{"file_path":"@openzeppelin/contracts/utils/Panic.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Helper library for emitting standardized panic codes.\n *\n * ```solidity\n * contract Example {\n *      using Panic for uint256;\n *\n *      // Use any of the declared internal constants\n *      function foo() { Panic.GENERIC.panic(); }\n *\n *      // Alternatively\n *      function foo() { Panic.panic(Panic.GENERIC); }\n * }\n * ```\n *\n * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].\n *\n * _Available since v5.1._\n */\n// slither-disable-next-line unused-state\nlibrary Panic {\n    /// @dev generic / unspecified error\n    uint256 internal constant GENERIC = 0x00;\n    /// @dev used by the assert() builtin\n    uint256 internal constant ASSERT = 0x01;\n    /// @dev arithmetic underflow or overflow\n    uint256 internal constant UNDER_OVERFLOW = 0x11;\n    /// @dev division or modulo by zero\n    uint256 internal constant DIVISION_BY_ZERO = 0x12;\n    /// @dev enum conversion error\n    uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;\n    /// @dev invalid encoding in storage\n    uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;\n    /// @dev empty array pop\n    uint256 internal constant EMPTY_ARRAY_POP = 0x31;\n    /// @dev array out of bounds access\n    uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;\n    /// @dev resource error (too large allocation or too large array)\n    uint256 internal constant RESOURCE_ERROR = 0x41;\n    /// @dev calling invalid internal function\n    uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;\n\n    /// @dev Reverts with a panic code. Recommended to use with\n    /// the internal constants with predefined codes.\n    function panic(uint256 code) internal pure {\n        assembly (\"memory-safe\") {\n            mstore(0x00, 0x4e487b71)\n            mstore(0x20, code)\n            revert(0x1c, 0x24)\n        }\n    }\n}\n"},{"file_path":"contracts/test/MockPriceFeed.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.28;\n\nimport {IAggregatorV3} from \"../core/interfaces/IAggregatorV3.sol\";\n\n/**\n * @dev Minimal Chainlink-shaped aggregator mock. Lets tests control decimals,\n *      latest round answer/timestamps and round id.\n */\ncontract MockPriceFeed is IAggregatorV3 {\n    uint8 private _decimals;\n    uint80 private _roundId;\n    int256 private _answer;\n    uint256 private _startedAt;\n    uint256 private _updatedAt;\n    uint80 private _answeredInRound;\n\n    constructor(uint8 decimals_, int256 initialAnswer) {\n        _decimals = decimals_;\n        _answer = initialAnswer;\n        _roundId = 1;\n        _answeredInRound = 1;\n        _startedAt = block.timestamp;\n        _updatedAt = block.timestamp;\n    }\n\n    function setDecimals(uint8 d) external { _decimals = d; }\n\n    function setLatestRoundData(\n        uint80 roundId_,\n        int256 answer_,\n        uint256 startedAt_,\n        uint256 updatedAt_,\n        uint80 answeredInRound_\n    ) external {\n        _roundId = roundId_;\n        _answer = answer_;\n        _startedAt = startedAt_;\n        _updatedAt = updatedAt_;\n        _answeredInRound = answeredInRound_;\n    }\n\n    function decimals() external view override returns (uint8) { return _decimals; }\n    function description() external pure override returns (string memory) { return \"MockPriceFeed\"; }\n    function version() external pure override returns (uint256) { return 1; }\n\n    function getRoundData(uint80)\n        external\n        view\n        override\n        returns (uint80, int256, uint256, uint256, uint80)\n    {\n        return (_roundId, _answer, _startedAt, _updatedAt, _answeredInRound);\n    }\n\n    function latestRoundData()\n        external\n        view\n        override\n        returns (uint80, int256, uint256, uint256, uint80)\n    {\n        return (_roundId, _answer, _startedAt, _updatedAt, _answeredInRound);\n    }\n}\n"},{"file_path":"flare-smart-contracts/contracts/userInterfaces/IFlareContractRegistry.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity >=0.7.6 <0.9;\npragma abicoder v2;\n\n/**\n * Interface for the `FlareContractRegistry`.\n *\n * Entry point for all external dapps that need the latest contract addresses deployed by Flare.\n */\ninterface IFlareContractRegistry {\n    /**\n     * Returns the address of a given contract name.\n     * @param _name Name of the contract.\n     * @return Address of the contract, or `address(0)` if not found.\n     */\n    function getContractAddressByName(string calldata _name) external view returns(address);\n\n    /**\n     * Returns the address of a given contract hash.\n     * @param _nameHash Hash of the contract name as: `keccak256(abi.encode(name))`.\n     * @return Address of the contract, or `address(0)` if not found.\n     */\n    function getContractAddressByHash(bytes32 _nameHash) external view returns(address);\n\n    /**\n     * Returns the addresses of a list of contract names.\n     * @param _names Array of contract names.\n     * @return Array of addresses of the contracts.\n     * Any of them might be `address(0)` if not found.\n     */\n    function getContractAddressesByName(string[] calldata _names) external view returns(address[] memory);\n\n    /**\n     * Returns the addresses of a list of contract hashes.\n     * @param _nameHashes Array of contract name hashes as: `keccak256(abi.encode(name))`.\n     * @return Array of addresses of the contracts.\n     * Any of them might be `address(0)` if not found.\n     */\n    function getContractAddressesByHash(bytes32[] calldata _nameHashes) external view returns(address[] memory);\n\n    /**\n     * Returns all contract names and their corresponding addresses.\n     * @return _names Array of contract names.\n     * @return _addresses Array of corresponding contract addresses.\n     */\n    function getAllContracts() external view returns(string[] memory _names, address[] memory _addresses);\n}\n"},{"file_path":"contracts/core/CoverNFT.sol","source_code":"// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.28;\n\nimport {AccessControlUpgradeable} from \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport {ERC721Upgradeable} from \"@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol\";\nimport {ERC721EnumerableUpgradeable} from \"@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol\";\nimport {ERC721PausableUpgradeable} from \"@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721PausableUpgradeable.sol\";\n\n/**\n * @title CoverNFT\n * @notice Upgradeable ERC-721 token representing covers created by the CoverOrderAllocator.\n * The CoverOrderAllocator is expected to mint one NFT per settled cover order.\n *\n * @custom:security-contact securityreport@firelight.finance\n */\ncontract CoverNFT is\n    ERC721Upgradeable,\n    ERC721EnumerableUpgradeable,\n    ERC721PausableUpgradeable,\n    AccessControlUpgradeable\n{\n    /// @notice Role allowed to pause and unpause the contract.\n    bytes32 public constant PAUSER_ROLE = keccak256(\"PAUSER_ROLE\");\n\n    /// @notice Role allowed to mint new NFTs.\n    bytes32 public constant MINTER_ROLE = keccak256(\"MINTER_ROLE\");\n\n    /// @notice Role allowed to update the collection base URI.\n    bytes32 public constant URI_MANAGER_ROLE = keccak256(\"URI_MANAGER_ROLE\");\n\n    // --- ERC-7201 Namespaced Storage ---\n    /// @custom:storage-location erc7201:firelight.covernft.storage\n    struct CoverNFTStorage {\n        string baseURI;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"firelight.covernft.storage\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant STORAGE_LOCATION = 0x426682ee43cf77999495f05d99a85f0091c30c5f2afdd681b11771286cb00b00;\n\n    function _getStorage() private pure returns (CoverNFTStorage storage $) {\n        assembly {\n            $.slot := STORAGE_LOCATION\n        }\n    }\n\n    /**\n     * @notice Emitted when the base URI is updated.\n     * @param oldBaseURI Previous base URI.\n     * @param newBaseURI New base URI.\n     */\n    event BaseURIUpdated(string oldBaseURI, string newBaseURI);\n\n    /// @notice Thrown when admin is zero address.\n    error InvalidAdmin();\n\n    /// @custom:oz-upgrades-unsafe-allow constructor\n    constructor() {\n        _disableInitializers();\n    }\n\n    /**\n     * @notice Initializes the CoverNFT contract.\n     * @param name_ ERC-721 token name.\n     * @param symbol_ ERC-721 token symbol.\n     * @param baseURI_ Initial base URI for token metadata. May be empty.\n     * @param admin Address granted DEFAULT_ADMIN_ROLE.\n     * @param minter Address granted MINTER_ROLE.\n     * @param pauser Address granted PAUSER_ROLE. May be zero address.\n     * @param uriManager Address granted URI_MANAGER_ROLE. May be zero address.\n     */\n    function initialize(\n        string memory name_,\n        string memory symbol_,\n        string memory baseURI_,\n        address admin,\n        address minter,\n        address pauser,\n        address uriManager\n    ) public initializer {\n        __ERC721_init(name_, symbol_);\n        __ERC721Enumerable_init();\n        __ERC721Pausable_init();\n        __AccessControl_init();\n\n        if (admin == address(0)) revert InvalidAdmin();\n\n        _grantRole(DEFAULT_ADMIN_ROLE, admin);\n\n        if (minter != address(0)) _grantRole(MINTER_ROLE, minter);\n        if (pauser != address(0)) _grantRole(PAUSER_ROLE, pauser);\n        if (uriManager != address(0)) _grantRole(URI_MANAGER_ROLE, uriManager);\n\n        _setBaseURI(baseURI_);\n    }\n\n    /**\n     * @notice Safely mints a new cover order NFT.\n     * @param to Address receiving the NFT.\n     * @param tokenId Token id to mint.\n     */\n    function safeMint(address to, uint256 tokenId) public onlyRole(MINTER_ROLE) {\n        _safeMint(to, tokenId);\n    }\n\n    /**\n     * @notice Updates the base URI used for token metadata.\n     * @param baseURI_ New base URI for token metadata. May be empty.\n     */\n    function setBaseURI(string memory baseURI_) public onlyRole(URI_MANAGER_ROLE) {\n        _setBaseURI(baseURI_);\n    }\n\n    /**\n     * @notice Pauses token transfers, minting, and burning.\n     */\n    function pause() public onlyRole(PAUSER_ROLE) {\n        _pause();\n    }\n\n    /**\n     * @notice Unpauses token transfers, minting, and burning.\n     */\n    function unpause() public onlyRole(PAUSER_ROLE) {\n        _unpause();\n    }\n\n    /**\n     * @dev Stores a new base URI for token metadata.\n     * @param baseURI_ New base URI for token metadata.\n     */\n    function _setBaseURI(string memory baseURI_) private {\n        CoverNFTStorage storage $ = _getStorage();\n        string memory oldBaseURI = $.baseURI;\n        $.baseURI = baseURI_;\n        emit BaseURIUpdated(oldBaseURI, baseURI_);\n    }\n\n    /**\n     * @notice Returns the base URI used by ERC-721 metadata functions to construct token URIs.\n     * @return Base URI for token metadata.\n     */\n    function _baseURI() internal view override returns (string memory) {\n        return _getStorage().baseURI;\n    }\n\n    // --- Required overrides --- //\n\n    /// @dev Resolves the {ERC721}, {ERC721Enumerable}, and {ERC721Pausable} `_update` hooks.\n    function _update(\n        address to,\n        uint256 tokenId,\n        address auth\n    ) internal override(ERC721Upgradeable, ERC721EnumerableUpgradeable, ERC721PausableUpgradeable) returns (address) {\n        return super._update(to, tokenId, auth);\n    }\n\n    /// @dev Resolves the {ERC721} and {ERC721Enumerable} `_increaseBalance` hooks.\n    function _increaseBalance(\n        address account,\n        uint128 value\n    ) internal override(ERC721Upgradeable, ERC721EnumerableUpgradeable) {\n        super._increaseBalance(account, value);\n    }\n\n    /// @notice Returns whether the contract implements `interfaceId` (ERC-165).\n    /// @dev Resolves the {ERC721}, {ERC721Enumerable}, and {AccessControl} implementations.\n    /// @param interfaceId Interface identifier to query.\n    /// @return True if the interface is supported.\n    function supportsInterface(\n        bytes4 interfaceId\n    ) public view override(ERC721Upgradeable, ERC721EnumerableUpgradeable, AccessControlUpgradeable) returns (bool) {\n        return super.supportsInterface(interfaceId);\n    }\n}\n"},{"file_path":"@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/ERC721.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC721} from \"@openzeppelin/contracts/token/ERC721/IERC721.sol\";\nimport {IERC721Metadata} from \"@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol\";\nimport {ERC721Utils} from \"@openzeppelin/contracts/token/ERC721/utils/ERC721Utils.sol\";\nimport {ContextUpgradeable} from \"../../utils/ContextUpgradeable.sol\";\nimport {Strings} from \"@openzeppelin/contracts/utils/Strings.sol\";\nimport {IERC165} from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\nimport {ERC165Upgradeable} from \"../../utils/introspection/ERC165Upgradeable.sol\";\nimport {IERC721Errors} from \"@openzeppelin/contracts/interfaces/draft-IERC6093.sol\";\nimport {Initializable} from \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC-721] Non-Fungible Token Standard, including\n * the Metadata extension, but not including the Enumerable extension, which is available separately as\n * {ERC721Enumerable}.\n */\nabstract contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721, IERC721Metadata, IERC721Errors {\n    using Strings for uint256;\n\n    /// @custom:storage-location erc7201:openzeppelin.storage.ERC721\n    struct ERC721Storage {\n        // Token name\n        string _name;\n\n        // Token symbol\n        string _symbol;\n\n        mapping(uint256 tokenId => address) _owners;\n\n        mapping(address owner => uint256) _balances;\n\n        mapping(uint256 tokenId => address) _tokenApprovals;\n\n        mapping(address owner => mapping(address operator => bool)) _operatorApprovals;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.ERC721\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant ERC721StorageLocation = 0x80bb2b638cc20bc4d0a60d66940f3ab4a00c1d7b313497ca82fb0b4ab0079300;\n\n    function _getERC721Storage() private pure returns (ERC721Storage storage $) {\n        assembly {\n            $.slot := ERC721StorageLocation\n        }\n    }\n\n    /**\n     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.\n     */\n    function __ERC721_init(string memory name_, string memory symbol_) internal onlyInitializing {\n        __ERC721_init_unchained(name_, symbol_);\n    }\n\n    function __ERC721_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {\n        ERC721Storage storage $ = _getERC721Storage();\n        $._name = name_;\n        $._symbol = symbol_;\n    }\n\n    /// @inheritdoc IERC165\n    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165) returns (bool) {\n        return\n            interfaceId == type(IERC721).interfaceId ||\n            interfaceId == type(IERC721Metadata).interfaceId ||\n            super.supportsInterface(interfaceId);\n    }\n\n    /// @inheritdoc IERC721\n    function balanceOf(address owner) public view virtual returns (uint256) {\n        ERC721Storage storage $ = _getERC721Storage();\n        if (owner == address(0)) {\n            revert ERC721InvalidOwner(address(0));\n        }\n        return $._balances[owner];\n    }\n\n    /// @inheritdoc IERC721\n    function ownerOf(uint256 tokenId) public view virtual returns (address) {\n        return _requireOwned(tokenId);\n    }\n\n    /// @inheritdoc IERC721Metadata\n    function name() public view virtual returns (string memory) {\n        ERC721Storage storage $ = _getERC721Storage();\n        return $._name;\n    }\n\n    /// @inheritdoc IERC721Metadata\n    function symbol() public view virtual returns (string memory) {\n        ERC721Storage storage $ = _getERC721Storage();\n        return $._symbol;\n    }\n\n    /// @inheritdoc IERC721Metadata\n    function tokenURI(uint256 tokenId) public view virtual returns (string memory) {\n        _requireOwned(tokenId);\n\n        string memory baseURI = _baseURI();\n        return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : \"\";\n    }\n\n    /**\n     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each\n     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty\n     * by default, can be overridden in child contracts.\n     */\n    function _baseURI() internal view virtual returns (string memory) {\n        return \"\";\n    }\n\n    /// @inheritdoc IERC721\n    function approve(address to, uint256 tokenId) public virtual {\n        _approve(to, tokenId, _msgSender());\n    }\n\n    /// @inheritdoc IERC721\n    function getApproved(uint256 tokenId) public view virtual returns (address) {\n        _requireOwned(tokenId);\n\n        return _getApproved(tokenId);\n    }\n\n    /// @inheritdoc IERC721\n    function setApprovalForAll(address operator, bool approved) public virtual {\n        _setApprovalForAll(_msgSender(), operator, approved);\n    }\n\n    /// @inheritdoc IERC721\n    function isApprovedForAll(address owner, address operator) public view virtual returns (bool) {\n        ERC721Storage storage $ = _getERC721Storage();\n        return $._operatorApprovals[owner][operator];\n    }\n\n    /// @inheritdoc IERC721\n    function transferFrom(address from, address to, uint256 tokenId) public virtual {\n        if (to == address(0)) {\n            revert ERC721InvalidReceiver(address(0));\n        }\n        // Setting an \"auth\" arguments enables the `_isAuthorized` check which verifies that the token exists\n        // (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.\n        address previousOwner = _update(to, tokenId, _msgSender());\n        if (previousOwner != from) {\n            revert ERC721IncorrectOwner(from, tokenId, previousOwner);\n        }\n    }\n\n    /// @inheritdoc IERC721\n    function safeTransferFrom(address from, address to, uint256 tokenId) public {\n        safeTransferFrom(from, to, tokenId, \"\");\n    }\n\n    /// @inheritdoc IERC721\n    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {\n        transferFrom(from, to, tokenId);\n        ERC721Utils.checkOnERC721Received(_msgSender(), from, to, tokenId, data);\n    }\n\n    /**\n     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist\n     *\n     * IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the\n     * core ERC-721 logic MUST be matched with the use of {_increaseBalance} to keep balances\n     * consistent with ownership. The invariant to preserve is that for any address `a` the value returned by\n     * `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`.\n     */\n    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {\n        ERC721Storage storage $ = _getERC721Storage();\n        return $._owners[tokenId];\n    }\n\n    /**\n     * @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted.\n     */\n    function _getApproved(uint256 tokenId) internal view virtual returns (address) {\n        ERC721Storage storage $ = _getERC721Storage();\n        return $._tokenApprovals[tokenId];\n    }\n\n    /**\n     * @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in\n     * particular (ignoring whether it is owned by `owner`).\n     *\n     * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this\n     * assumption.\n     */\n    function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) {\n        return\n            spender != address(0) &&\n            (owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender);\n    }\n\n    /**\n     * @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner.\n     * Reverts if:\n     * - `spender` does not have approval from `owner` for `tokenId`.\n     * - `spender` does not have approval to manage all of `owner`'s assets.\n     *\n     * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this\n     * assumption.\n     */\n    function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual {\n        if (!_isAuthorized(owner, spender, tokenId)) {\n            if (owner == address(0)) {\n                revert ERC721NonexistentToken(tokenId);\n            } else {\n                revert ERC721InsufficientApproval(spender, tokenId);\n            }\n        }\n    }\n\n    /**\n     * @dev Unsafe write access to the balances, used by extensions that \"mint\" tokens using an {ownerOf} override.\n     *\n     * NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that\n     * a uint256 would ever overflow from increments when these increments are bounded to uint128 values.\n     *\n     * WARNING: Increasing an account's balance using this function tends to be paired with an override of the\n     * {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership\n     * remain consistent with one another.\n     */\n    function _increaseBalance(address account, uint128 value) internal virtual {\n        ERC721Storage storage $ = _getERC721Storage();\n        unchecked {\n            $._balances[account] += value;\n        }\n    }\n\n    /**\n     * @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner\n     * (or `to`) is the zero address. Returns the owner of the `tokenId` before the update.\n     *\n     * The `auth` argument is optional. If the value passed is non 0, then this function will check that\n     * `auth` is either the owner of the token, or approved to operate on the token (by the owner).\n     *\n     * Emits a {Transfer} event.\n     *\n     * NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}.\n     */\n    function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) {\n        ERC721Storage storage $ = _getERC721Storage();\n        address from = _ownerOf(tokenId);\n\n        // Perform (optional) operator check\n        if (auth != address(0)) {\n            _checkAuthorized(from, auth, tokenId);\n        }\n\n        // Execute the update\n        if (from != address(0)) {\n            // Clear approval. No need to re-authorize or emit the Approval event\n            _approve(address(0), tokenId, address(0), false);\n\n            unchecked {\n                $._balances[from] -= 1;\n            }\n        }\n\n        if (to != address(0)) {\n            unchecked {\n                $._balances[to] += 1;\n            }\n        }\n\n        $._owners[tokenId] = to;\n\n        emit Transfer(from, to, tokenId);\n\n        return from;\n    }\n\n    /**\n     * @dev Mints `tokenId` and transfers it to `to`.\n     *\n     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible\n     *\n     * Requirements:\n     *\n     * - `tokenId` must not exist.\n     * - `to` cannot be the zero address.\n     *\n     * Emits a {Transfer} event.\n     */\n    function _mint(address to, uint256 tokenId) internal {\n        if (to == address(0)) {\n            revert ERC721InvalidReceiver(address(0));\n        }\n        address previousOwner = _update(to, tokenId, address(0));\n        if (previousOwner != address(0)) {\n            revert ERC721InvalidSender(address(0));\n        }\n    }\n\n    /**\n     * @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must not exist.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function _safeMint(address to, uint256 tokenId) internal {\n        _safeMint(to, tokenId, \"\");\n    }\n\n    /**\n     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is\n     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.\n     */\n    function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {\n        _mint(to, tokenId);\n        ERC721Utils.checkOnERC721Received(_msgSender(), address(0), to, tokenId, data);\n    }\n\n    /**\n     * @dev Destroys `tokenId`.\n     * The approval is cleared when the token is burned.\n     * This is an internal function that does not check if the sender is authorized to operate on the token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     *\n     * Emits a {Transfer} event.\n     */\n    function _burn(uint256 tokenId) internal {\n        address previousOwner = _update(address(0), tokenId, address(0));\n        if (previousOwner == address(0)) {\n            revert ERC721NonexistentToken(tokenId);\n        }\n    }\n\n    /**\n     * @dev Transfers `tokenId` from `from` to `to`.\n     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.\n     *\n     * Requirements:\n     *\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must be owned by `from`.\n     *\n     * Emits a {Transfer} event.\n     */\n    function _transfer(address from, address to, uint256 tokenId) internal {\n        if (to == address(0)) {\n            revert ERC721InvalidReceiver(address(0));\n        }\n        address previousOwner = _update(to, tokenId, address(0));\n        if (previousOwner == address(0)) {\n            revert ERC721NonexistentToken(tokenId);\n        } else if (previousOwner != from) {\n            revert ERC721IncorrectOwner(from, tokenId, previousOwner);\n        }\n    }\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients\n     * are aware of the ERC-721 standard to prevent tokens from being forever locked.\n     *\n     * `data` is additional data, it has no specified format and it is sent in call to `to`.\n     *\n     * This internal function is like {safeTransferFrom} in the sense that it invokes\n     * {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g.\n     * implement alternative mechanisms to perform token transfer, such as signature-based.\n     *\n     * Requirements:\n     *\n     * - `tokenId` token must exist and be owned by `from`.\n     * - `to` cannot be the zero address.\n     * - `from` cannot be the zero address.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function _safeTransfer(address from, address to, uint256 tokenId) internal {\n        _safeTransfer(from, to, tokenId, \"\");\n    }\n\n    /**\n     * @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is\n     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.\n     */\n    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {\n        _transfer(from, to, tokenId);\n        ERC721Utils.checkOnERC721Received(_msgSender(), from, to, tokenId, data);\n    }\n\n    /**\n     * @dev Approve `to` to operate on `tokenId`\n     *\n     * The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is\n     * either the owner of the token, or approved to operate on all tokens held by this owner.\n     *\n     * Emits an {Approval} event.\n     *\n     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.\n     */\n    function _approve(address to, uint256 tokenId, address auth) internal {\n        _approve(to, tokenId, auth, true);\n    }\n\n    /**\n     * @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not\n     * emitted in the context of transfers.\n     */\n    function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual {\n        ERC721Storage storage $ = _getERC721Storage();\n        // Avoid reading the owner unless necessary\n        if (emitEvent || auth != address(0)) {\n            address owner = _requireOwned(tokenId);\n\n            // We do not use _isAuthorized because single-token approvals should not be able to call approve\n            if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) {\n                revert ERC721InvalidApprover(auth);\n            }\n\n            if (emitEvent) {\n                emit Approval(owner, to, tokenId);\n            }\n        }\n\n        $._tokenApprovals[tokenId] = to;\n    }\n\n    /**\n     * @dev Approve `operator` to operate on all of `owner` tokens\n     *\n     * Requirements:\n     * - operator can't be the address zero.\n     *\n     * Emits an {ApprovalForAll} event.\n     */\n    function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {\n        ERC721Storage storage $ = _getERC721Storage();\n        if (operator == address(0)) {\n            revert ERC721InvalidOperator(operator);\n        }\n        $._operatorApprovals[owner][operator] = approved;\n        emit ApprovalForAll(owner, operator, approved);\n    }\n\n    /**\n     * @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned).\n     * Returns the owner.\n     *\n     * Overrides to ownership logic should be done to {_ownerOf}.\n     */\n    function _requireOwned(uint256 tokenId) internal view returns (address) {\n        address owner = _ownerOf(tokenId);\n        if (owner == address(0)) {\n            revert ERC721NonexistentToken(tokenId);\n        }\n        return owner;\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC721} from \"../IERC721.sol\";\n\n/**\n * @title ERC-721 Non-Fungible Token Standard, optional metadata extension\n * @dev See https://eips.ethereum.org/EIPS/eip-721\n */\ninterface IERC721Metadata is IERC721 {\n    /**\n     * @dev Returns the token collection name.\n     */\n    function name() external view returns (string memory);\n\n    /**\n     * @dev Returns the token collection symbol.\n     */\n    function symbol() external view returns (string memory);\n\n    /**\n     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.\n     */\n    function tokenURI(uint256 tokenId) external view returns (string memory);\n}\n"},{"file_path":"@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)\n\npragma solidity ^0.8.20;\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,\n * consider using {ReentrancyGuardTransient} instead.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuardUpgradeable is Initializable {\n    // Booleans are more expensive than uint256 or any type that takes up a full\n    // word because each write operation emits an extra SLOAD to first read the\n    // slot's contents, replace the bits taken up by the boolean, and then write\n    // back. This is the compiler's defense against contract upgrades and\n    // pointer aliasing, and it cannot be disabled.\n\n    // The values being non-zero value makes deployment a bit more expensive,\n    // but in exchange the refund on every call to nonReentrant will be lower in\n    // amount. Since refunds are capped to a percentage of the total\n    // transaction's gas, it is best to keep them low in cases like this one, to\n    // increase the likelihood of the full refund coming into effect.\n    uint256 private constant NOT_ENTERED = 1;\n    uint256 private constant ENTERED = 2;\n\n    /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard\n    struct ReentrancyGuardStorage {\n        uint256 _status;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.ReentrancyGuard\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;\n\n    function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {\n        assembly {\n            $.slot := ReentrancyGuardStorageLocation\n        }\n    }\n\n    /**\n     * @dev Unauthorized reentrant call.\n     */\n    error ReentrancyGuardReentrantCall();\n\n    function __ReentrancyGuard_init() internal onlyInitializing {\n        __ReentrancyGuard_init_unchained();\n    }\n\n    function __ReentrancyGuard_init_unchained() internal onlyInitializing {\n        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();\n        $._status = NOT_ENTERED;\n    }\n\n    /**\n     * @dev Prevents a contract from calling itself, directly or indirectly.\n     * Calling a `nonReentrant` function from another `nonReentrant`\n     * function is not supported. It is possible to prevent this from happening\n     * by making the `nonReentrant` function external, and making it call a\n     * `private` function that does the actual work.\n     */\n    modifier nonReentrant() {\n        _nonReentrantBefore();\n        _;\n        _nonReentrantAfter();\n    }\n\n    function _nonReentrantBefore() private {\n        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();\n        // On the first call to nonReentrant, _status will be NOT_ENTERED\n        if ($._status == ENTERED) {\n            revert ReentrancyGuardReentrantCall();\n        }\n\n        // Any calls to nonReentrant after this point will fail\n        $._status = ENTERED;\n    }\n\n    function _nonReentrantAfter() private {\n        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();\n        // By storing the original value once again, a refund is triggered (see\n        // https://eips.ethereum.org/EIPS/eip-2200)\n        $._status = NOT_ENTERED;\n    }\n\n    /**\n     * @dev Returns true if the reentrancy guard is currently set to \"entered\", which indicates there is a\n     * `nonReentrant` function in the call stack.\n     */\n    function _reentrancyGuardEntered() internal view returns (bool) {\n        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();\n        return $._status == ENTERED;\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/utils/Context.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n\n    function _contextSuffixLength() internal view virtual returns (uint256) {\n        return 0;\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/access/extensions/AccessControlDefaultAdminRules.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/extensions/AccessControlDefaultAdminRules.sol)\n\npragma solidity ^0.8.20;\n\nimport {IAccessControlDefaultAdminRules} from \"./IAccessControlDefaultAdminRules.sol\";\nimport {AccessControl, IAccessControl} from \"../AccessControl.sol\";\nimport {SafeCast} from \"../../utils/math/SafeCast.sol\";\nimport {Math} from \"../../utils/math/Math.sol\";\nimport {IERC5313} from \"../../interfaces/IERC5313.sol\";\n\n/**\n * @dev Extension of {AccessControl} that allows specifying special rules to manage\n * the `DEFAULT_ADMIN_ROLE` holder, which is a sensitive role with special permissions\n * over other roles that may potentially have privileged rights in the system.\n *\n * If a specific role doesn't have an admin role assigned, the holder of the\n * `DEFAULT_ADMIN_ROLE` will have the ability to grant it and revoke it.\n *\n * This contract implements the following risk mitigations on top of {AccessControl}:\n *\n * * Only one account holds the `DEFAULT_ADMIN_ROLE` since deployment until it's potentially renounced.\n * * Enforces a 2-step process to transfer the `DEFAULT_ADMIN_ROLE` to another account.\n * * Enforces a configurable delay between the two steps, with the ability to cancel before the transfer is accepted.\n * * The delay can be changed by scheduling, see {changeDefaultAdminDelay}.\n * * It is not possible to use another role to manage the `DEFAULT_ADMIN_ROLE`.\n *\n * Example usage:\n *\n * ```solidity\n * contract MyToken is AccessControlDefaultAdminRules {\n *   constructor() AccessControlDefaultAdminRules(\n *     3 days,\n *     msg.sender // Explicit initial `DEFAULT_ADMIN_ROLE` holder\n *    ) {}\n * }\n * ```\n */\nabstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRules, IERC5313, AccessControl {\n    // pending admin pair read/written together frequently\n    address private _pendingDefaultAdmin;\n    uint48 private _pendingDefaultAdminSchedule; // 0 == unset\n\n    uint48 private _currentDelay;\n    address private _currentDefaultAdmin;\n\n    // pending delay pair read/written together frequently\n    uint48 private _pendingDelay;\n    uint48 private _pendingDelaySchedule; // 0 == unset\n\n    /**\n     * @dev Sets the initial values for {defaultAdminDelay} and {defaultAdmin} address.\n     */\n    constructor(uint48 initialDelay, address initialDefaultAdmin) {\n        if (initialDefaultAdmin == address(0)) {\n            revert AccessControlInvalidDefaultAdmin(address(0));\n        }\n        _currentDelay = initialDelay;\n        _grantRole(DEFAULT_ADMIN_ROLE, initialDefaultAdmin);\n    }\n\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n        return interfaceId == type(IAccessControlDefaultAdminRules).interfaceId || super.supportsInterface(interfaceId);\n    }\n\n    /**\n     * @dev See {IERC5313-owner}.\n     */\n    function owner() public view virtual returns (address) {\n        return defaultAdmin();\n    }\n\n    ///\n    /// Override AccessControl role management\n    ///\n\n    /**\n     * @dev See {AccessControl-grantRole}. Reverts for `DEFAULT_ADMIN_ROLE`.\n     */\n    function grantRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {\n        if (role == DEFAULT_ADMIN_ROLE) {\n            revert AccessControlEnforcedDefaultAdminRules();\n        }\n        super.grantRole(role, account);\n    }\n\n    /**\n     * @dev See {AccessControl-revokeRole}. Reverts for `DEFAULT_ADMIN_ROLE`.\n     */\n    function revokeRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {\n        if (role == DEFAULT_ADMIN_ROLE) {\n            revert AccessControlEnforcedDefaultAdminRules();\n        }\n        super.revokeRole(role, account);\n    }\n\n    /**\n     * @dev See {AccessControl-renounceRole}.\n     *\n     * For the `DEFAULT_ADMIN_ROLE`, it only allows renouncing in two steps by first calling\n     * {beginDefaultAdminTransfer} to the `address(0)`, so it's required that the {pendingDefaultAdmin} schedule\n     * has also passed when calling this function.\n     *\n     * After its execution, it will not be possible to call `onlyRole(DEFAULT_ADMIN_ROLE)` functions.\n     *\n     * NOTE: Renouncing `DEFAULT_ADMIN_ROLE` will leave the contract without a {defaultAdmin},\n     * thereby disabling any functionality that is only available for it, and the possibility of reassigning a\n     * non-administrated role.\n     */\n    function renounceRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {\n        if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {\n            (address newDefaultAdmin, uint48 schedule) = pendingDefaultAdmin();\n            if (newDefaultAdmin != address(0) || !_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {\n                revert AccessControlEnforcedDefaultAdminDelay(schedule);\n            }\n            delete _pendingDefaultAdminSchedule;\n        }\n        super.renounceRole(role, account);\n    }\n\n    /**\n     * @dev See {AccessControl-_grantRole}.\n     *\n     * For `DEFAULT_ADMIN_ROLE`, it only allows granting if there isn't already a {defaultAdmin} or if the\n     * role has been previously renounced.\n     *\n     * NOTE: Exposing this function through another mechanism may make the `DEFAULT_ADMIN_ROLE`\n     * assignable again. Make sure to guarantee this is the expected behavior in your implementation.\n     */\n    function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {\n        if (role == DEFAULT_ADMIN_ROLE) {\n            if (defaultAdmin() != address(0)) {\n                revert AccessControlEnforcedDefaultAdminRules();\n            }\n            _currentDefaultAdmin = account;\n        }\n        return super._grantRole(role, account);\n    }\n\n    /**\n     * @dev See {AccessControl-_revokeRole}.\n     */\n    function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {\n        if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {\n            delete _currentDefaultAdmin;\n        }\n        return super._revokeRole(role, account);\n    }\n\n    /**\n     * @dev See {AccessControl-_setRoleAdmin}. Reverts for `DEFAULT_ADMIN_ROLE`.\n     */\n    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual override {\n        if (role == DEFAULT_ADMIN_ROLE) {\n            revert AccessControlEnforcedDefaultAdminRules();\n        }\n        super._setRoleAdmin(role, adminRole);\n    }\n\n    ///\n    /// AccessControlDefaultAdminRules accessors\n    ///\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function defaultAdmin() public view virtual returns (address) {\n        return _currentDefaultAdmin;\n    }\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function pendingDefaultAdmin() public view virtual returns (address newAdmin, uint48 schedule) {\n        return (_pendingDefaultAdmin, _pendingDefaultAdminSchedule);\n    }\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function defaultAdminDelay() public view virtual returns (uint48) {\n        uint48 schedule = _pendingDelaySchedule;\n        return (_isScheduleSet(schedule) && _hasSchedulePassed(schedule)) ? _pendingDelay : _currentDelay;\n    }\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function pendingDefaultAdminDelay() public view virtual returns (uint48 newDelay, uint48 schedule) {\n        schedule = _pendingDelaySchedule;\n        return (_isScheduleSet(schedule) && !_hasSchedulePassed(schedule)) ? (_pendingDelay, schedule) : (0, 0);\n    }\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function defaultAdminDelayIncreaseWait() public view virtual returns (uint48) {\n        return 5 days;\n    }\n\n    ///\n    /// AccessControlDefaultAdminRules public and internal setters for defaultAdmin/pendingDefaultAdmin\n    ///\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function beginDefaultAdminTransfer(address newAdmin) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {\n        _beginDefaultAdminTransfer(newAdmin);\n    }\n\n    /**\n     * @dev See {beginDefaultAdminTransfer}.\n     *\n     * Internal function without access restriction.\n     */\n    function _beginDefaultAdminTransfer(address newAdmin) internal virtual {\n        uint48 newSchedule = SafeCast.toUint48(block.timestamp) + defaultAdminDelay();\n        _setPendingDefaultAdmin(newAdmin, newSchedule);\n        emit DefaultAdminTransferScheduled(newAdmin, newSchedule);\n    }\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function cancelDefaultAdminTransfer() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {\n        _cancelDefaultAdminTransfer();\n    }\n\n    /**\n     * @dev See {cancelDefaultAdminTransfer}.\n     *\n     * Internal function without access restriction.\n     */\n    function _cancelDefaultAdminTransfer() internal virtual {\n        _setPendingDefaultAdmin(address(0), 0);\n    }\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function acceptDefaultAdminTransfer() public virtual {\n        (address newDefaultAdmin, ) = pendingDefaultAdmin();\n        if (_msgSender() != newDefaultAdmin) {\n            // Enforce newDefaultAdmin explicit acceptance.\n            revert AccessControlInvalidDefaultAdmin(_msgSender());\n        }\n        _acceptDefaultAdminTransfer();\n    }\n\n    /**\n     * @dev See {acceptDefaultAdminTransfer}.\n     *\n     * Internal function without access restriction.\n     */\n    function _acceptDefaultAdminTransfer() internal virtual {\n        (address newAdmin, uint48 schedule) = pendingDefaultAdmin();\n        if (!_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {\n            revert AccessControlEnforcedDefaultAdminDelay(schedule);\n        }\n        _revokeRole(DEFAULT_ADMIN_ROLE, defaultAdmin());\n        _grantRole(DEFAULT_ADMIN_ROLE, newAdmin);\n        delete _pendingDefaultAdmin;\n        delete _pendingDefaultAdminSchedule;\n    }\n\n    ///\n    /// AccessControlDefaultAdminRules public and internal setters for defaultAdminDelay/pendingDefaultAdminDelay\n    ///\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function changeDefaultAdminDelay(uint48 newDelay) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {\n        _changeDefaultAdminDelay(newDelay);\n    }\n\n    /**\n     * @dev See {changeDefaultAdminDelay}.\n     *\n     * Internal function without access restriction.\n     */\n    function _changeDefaultAdminDelay(uint48 newDelay) internal virtual {\n        uint48 newSchedule = SafeCast.toUint48(block.timestamp) + _delayChangeWait(newDelay);\n        _setPendingDelay(newDelay, newSchedule);\n        emit DefaultAdminDelayChangeScheduled(newDelay, newSchedule);\n    }\n\n    /**\n     * @inheritdoc IAccessControlDefaultAdminRules\n     */\n    function rollbackDefaultAdminDelay() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {\n        _rollbackDefaultAdminDelay();\n    }\n\n    /**\n     * @dev See {rollbackDefaultAdminDelay}.\n     *\n     * Internal function without access restriction.\n     */\n    function _rollbackDefaultAdminDelay() internal virtual {\n        _setPendingDelay(0, 0);\n    }\n\n    /**\n     * @dev Returns the amount of seconds to wait after the `newDelay` will\n     * become the new {defaultAdminDelay}.\n     *\n     * The value returned guarantees that if the delay is reduced, it will go into effect\n     * after a wait that honors the previously set delay.\n     *\n     * See {defaultAdminDelayIncreaseWait}.\n     */\n    function _delayChangeWait(uint48 newDelay) internal view virtual returns (uint48) {\n        uint48 currentDelay = defaultAdminDelay();\n\n        // When increasing the delay, we schedule the delay change to occur after a period of \"new delay\" has passed, up\n        // to a maximum given by defaultAdminDelayIncreaseWait, by default 5 days. For example, if increasing from 1 day\n        // to 3 days, the new delay will come into effect after 3 days. If increasing from 1 day to 10 days, the new\n        // delay will come into effect after 5 days. The 5 day wait period is intended to be able to fix an error like\n        // using milliseconds instead of seconds.\n        //\n        // When decreasing the delay, we wait the difference between \"current delay\" and \"new delay\". This guarantees\n        // that an admin transfer cannot be made faster than \"current delay\" at the time the delay change is scheduled.\n        // For example, if decreasing from 10 days to 3 days, the new delay will come into effect after 7 days.\n        return\n            newDelay > currentDelay\n                ? uint48(Math.min(newDelay, defaultAdminDelayIncreaseWait())) // no need to safecast, both inputs are uint48\n                : currentDelay - newDelay;\n    }\n\n    ///\n    /// Private setters\n    ///\n\n    /**\n     * @dev Setter of the tuple for pending admin and its schedule.\n     *\n     * May emit a DefaultAdminTransferCanceled event.\n     */\n    function _setPendingDefaultAdmin(address newAdmin, uint48 newSchedule) private {\n        (, uint48 oldSchedule) = pendingDefaultAdmin();\n\n        _pendingDefaultAdmin = newAdmin;\n        _pendingDefaultAdminSchedule = newSchedule;\n\n        // An `oldSchedule` from `pendingDefaultAdmin()` is only set if it hasn't been accepted.\n        if (_isScheduleSet(oldSchedule)) {\n            // Emit for implicit cancellations when another default admin was scheduled.\n            emit DefaultAdminTransferCanceled();\n        }\n    }\n\n    /**\n     * @dev Setter of the tuple for pending delay and its schedule.\n     *\n     * May emit a DefaultAdminDelayChangeCanceled event.\n     */\n    function _setPendingDelay(uint48 newDelay, uint48 newSchedule) private {\n        uint48 oldSchedule = _pendingDelaySchedule;\n\n        if (_isScheduleSet(oldSchedule)) {\n            if (_hasSchedulePassed(oldSchedule)) {\n                // Materialize a virtual delay\n                _currentDelay = _pendingDelay;\n            } else {\n                // Emit for implicit cancellations when another delay was scheduled.\n                emit DefaultAdminDelayChangeCanceled();\n            }\n        }\n\n        _pendingDelay = newDelay;\n        _pendingDelaySchedule = newSchedule;\n    }\n\n    ///\n    /// Private helpers\n    ///\n\n    /**\n     * @dev Defines if an `schedule` is considered set. For consistency purposes.\n     */\n    function _isScheduleSet(uint48 schedule) private pure returns (bool) {\n        return schedule != 0;\n    }\n\n    /**\n     * @dev Defines if an `schedule` is considered passed. For consistency purposes.\n     */\n    function _hasSchedulePassed(uint48 schedule) private view returns (bool) {\n        return schedule < block.timestamp;\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/interfaces/draft-IERC6093.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol)\npragma solidity ^0.8.20;\n\n/**\n * @dev Standard ERC-20 Errors\n * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.\n */\ninterface IERC20Errors {\n    /**\n     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.\n     * @param sender Address whose tokens are being transferred.\n     * @param balance Current balance for the interacting account.\n     * @param needed Minimum amount required to perform a transfer.\n     */\n    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);\n\n    /**\n     * @dev Indicates a failure with the token `sender`. Used in transfers.\n     * @param sender Address whose tokens are being transferred.\n     */\n    error ERC20InvalidSender(address sender);\n\n    /**\n     * @dev Indicates a failure with the token `receiver`. Used in transfers.\n     * @param receiver Address to which tokens are being transferred.\n     */\n    error ERC20InvalidReceiver(address receiver);\n\n    /**\n     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.\n     * @param spender Address that may be allowed to operate on tokens without being their owner.\n     * @param allowance Amount of tokens a `spender` is allowed to operate with.\n     * @param needed Minimum amount required to perform a transfer.\n     */\n    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);\n\n    /**\n     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.\n     * @param approver Address initiating an approval operation.\n     */\n    error ERC20InvalidApprover(address approver);\n\n    /**\n     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.\n     * @param spender Address that may be allowed to operate on tokens without being their owner.\n     */\n    error ERC20InvalidSpender(address spender);\n}\n\n/**\n * @dev Standard ERC-721 Errors\n * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.\n */\ninterface IERC721Errors {\n    /**\n     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.\n     * Used in balance queries.\n     * @param owner Address of the current owner of a token.\n     */\n    error ERC721InvalidOwner(address owner);\n\n    /**\n     * @dev Indicates a `tokenId` whose `owner` is the zero address.\n     * @param tokenId Identifier number of a token.\n     */\n    error ERC721NonexistentToken(uint256 tokenId);\n\n    /**\n     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.\n     * @param sender Address whose tokens are being transferred.\n     * @param tokenId Identifier number of a token.\n     * @param owner Address of the current owner of a token.\n     */\n    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);\n\n    /**\n     * @dev Indicates a failure with the token `sender`. Used in transfers.\n     * @param sender Address whose tokens are being transferred.\n     */\n    error ERC721InvalidSender(address sender);\n\n    /**\n     * @dev Indicates a failure with the token `receiver`. Used in transfers.\n     * @param receiver Address to which tokens are being transferred.\n     */\n    error ERC721InvalidReceiver(address receiver);\n\n    /**\n     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.\n     * @param operator Address that may be allowed to operate on tokens without being their owner.\n     * @param tokenId Identifier number of a token.\n     */\n    error ERC721InsufficientApproval(address operator, uint256 tokenId);\n\n    /**\n     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.\n     * @param approver Address initiating an approval operation.\n     */\n    error ERC721InvalidApprover(address approver);\n\n    /**\n     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.\n     * @param operator Address that may be allowed to operate on tokens without being their owner.\n     */\n    error ERC721InvalidOperator(address operator);\n}\n\n/**\n * @dev Standard ERC-1155 Errors\n * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.\n */\ninterface IERC1155Errors {\n    /**\n     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.\n     * @param sender Address whose tokens are being transferred.\n     * @param balance Current balance for the interacting account.\n     * @param needed Minimum amount required to perform a transfer.\n     * @param tokenId Identifier number of a token.\n     */\n    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);\n\n    /**\n     * @dev Indicates a failure with the token `sender`. Used in transfers.\n     * @param sender Address whose tokens are being transferred.\n     */\n    error ERC1155InvalidSender(address sender);\n\n    /**\n     * @dev Indicates a failure with the token `receiver`. Used in transfers.\n     * @param receiver Address to which tokens are being transferred.\n     */\n    error ERC1155InvalidReceiver(address receiver);\n\n    /**\n     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.\n     * @param operator Address that may be allowed to operate on tokens without being their owner.\n     * @param owner Address of the current owner of a token.\n     */\n    error ERC1155MissingApprovalForAll(address operator, address owner);\n\n    /**\n     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.\n     * @param approver Address initiating an approval operation.\n     */\n    error ERC1155InvalidApprover(address approver);\n\n    /**\n     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.\n     * @param operator Address that may be allowed to operate on tokens without being their owner.\n     */\n    error ERC1155InvalidOperator(address operator);\n\n    /**\n     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.\n     * Used in batch transfers.\n     * @param idsLength Length of the array of token identifiers\n     * @param valuesLength Length of the array of token amounts\n     */\n    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);\n}\n"},{"file_path":"contracts/core/lib/Checkpoints.sol","source_code":"// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.28;\n\nimport {Checkpoints as OZCheckpoints} from \"@openzeppelin/contracts/utils/structs/Checkpoints.sol\";\nimport {Math} from \"@openzeppelin/contracts/utils/math/Math.sol\";\n\n/**\n * @title Checkpoints\n * @notice Checkpoints values as they change over time and looks up past values by key.\n * @dev Defines the `Trace208` and `Trace256` structs. `Trace256` stores 256-bit values by\n *      indexing into a side array from an underlying OpenZeppelin `Trace208`, so index 0 acts as\n *      a permanent zero sentinel (real values start at index 1).\n * @custom:security-contact securityreport@firelight.finance\n */\nlibrary Checkpoints {\n    using OZCheckpoints for OZCheckpoints.Trace208;\n\n    /// @notice Thrown when popping a `Trace256` that holds no real checkpoint (only the sentinel).\n    error SystemCheckpoint();\n\n    /// @notice Checkpoint trace storing 208-bit values keyed by a 48-bit key.\n    struct Trace208 {\n        OZCheckpoints.Trace208 _trace;\n    }\n\n    /// @notice A single (key, value) checkpoint for {Trace208}.\n    struct Checkpoint208 {\n        uint48 _key;\n        uint208 _value;\n    }\n\n    /// @notice Checkpoint trace storing 256-bit values; `_trace` holds indices into `_values`.\n    struct Trace256 {\n        OZCheckpoints.Trace208 _trace;\n        uint256[] _values;\n    }\n\n    /// @notice A single (key, value) checkpoint for {Trace256}.\n    struct Checkpoint256 {\n        uint48 _key;\n        uint256 _value;\n    }\n\n    /**\n     * @dev Pushes a (`key`, `value`) pair into a Trace208 so that it is stored as the checkpoint.\n     *\n     * Returns previous value and new value.\n     */\n    function push(Trace208 storage self, uint48 key, uint208 value) internal returns (uint208, uint208) {\n        return self._trace.push(key, value);\n    }\n\n    /**\n     * @dev Returns the value in the last (most recent) checkpoint with a key lower or equal than the search key, or \n     * zero if there is none.\n     */\n    function upperLookupRecent(Trace208 storage self, uint48 key) internal view returns (uint208) {\n        return self._trace.upperLookupRecent(key);\n    }\n\n    /**\n     * @dev Returns the value in the last (most recent) checkpoint with a key lower or equal than the search key, or \n     * zero if there is none.\n     *\n     * NOTE: This is a variant of {upperLookupRecent} that can be optimized by getting the hint\n     * (index of the checkpoint with a key lower or equal than the search key).\n     */\n    function upperLookupRecent(Trace208 storage self, uint48 key, bytes memory hint_) internal view returns (uint208) {\n        if (hint_.length == 0) {\n            return upperLookupRecent(self, key);\n        }\n\n        uint32 hint = abi.decode(hint_, (uint32));\n        Checkpoint208 memory checkpoint = at(self, hint);\n        if (checkpoint._key == key) {\n            return checkpoint._value;\n        }\n\n        if (checkpoint._key < key && (hint == length(self) - 1 || at(self, hint + 1)._key > key)) {\n            return checkpoint._value;\n        }\n\n        return upperLookupRecent(self, key);\n    }\n\n    /**\n     * @dev Returns whether there is a checkpoint with a key lower or equal than the search key in the structure \n     * (i.e. it is not empty), and if so the key and value in the checkpoint, and its position in the trace.\n     */\n    function upperLookupRecentCheckpoint(\n        Trace208 storage self,\n        uint48 key\n    ) internal view returns (bool, uint48, uint208, uint32) {\n        uint256 len = self._trace._checkpoints.length;\n\n        uint256 low = 0;\n        uint256 high = len;\n\n        if (len > 5) {\n            uint256 mid = len - Math.sqrt(len);\n            if (key < _unsafeAccess(self._trace._checkpoints, mid)._key) {\n                high = mid;\n            } else {\n                low = mid + 1;\n            }\n        }\n\n        uint256 pos = _upperBinaryLookup(self._trace._checkpoints, key, low, high);\n\n        if (pos == 0) {\n            return (false, 0, 0, 0);\n        }\n\n        OZCheckpoints.Checkpoint208 memory checkpoint = _unsafeAccess(self._trace._checkpoints, pos - 1);\n        return (true, checkpoint._key, checkpoint._value, uint32(pos - 1));\n    }\n\n    /**\n     * @dev Returns whether there is a checkpoint with a key lower or equal than the search key in the structure \n     * (i.e. it is not empty), and if so the key and value in the checkpoint, and its position in the trace.\n     *\n     * NOTE: This is a variant of {upperLookupRecentCheckpoint} that can be optimized by getting the hint\n     * (index of the checkpoint with a key lower or equal than the search key).\n     */\n    function upperLookupRecentCheckpoint(\n        Trace208 storage self,\n        uint48 key,\n        bytes memory hint_\n    ) internal view returns (bool, uint48, uint208, uint32) {\n        if (hint_.length == 0) {\n            return upperLookupRecentCheckpoint(self, key);\n        }\n\n        uint32 hint = abi.decode(hint_, (uint32));\n        Checkpoint208 memory checkpoint = at(self, hint);\n        if (checkpoint._key == key) {\n            return (true, checkpoint._key, checkpoint._value, hint);\n        }\n\n        if (checkpoint._key < key && (hint == length(self) - 1 || at(self, hint + 1)._key > key)) {\n            return (true, checkpoint._key, checkpoint._value, hint);\n        }\n\n        return upperLookupRecentCheckpoint(self, key);\n    }\n\n    /**\n     * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.\n     */\n    function latest(\n        Trace208 storage self\n    ) internal view returns (uint208) {\n        return self._trace.latest();\n    }\n\n    /**\n     * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value\n     * in the most recent checkpoint.\n     */\n    function latestCheckpoint(\n        Trace208 storage self\n    ) internal view returns (bool, uint48, uint208) {\n        return self._trace.latestCheckpoint();\n    }\n\n    /**\n     * @dev Returns a total number of checkpoints.\n     */\n    function length(\n        Trace208 storage self\n    ) internal view returns (uint256) {\n        return self._trace.length();\n    }\n\n    /**\n     * @dev Returns checkpoint at a given position.\n     */\n    function at(Trace208 storage self, uint32 pos) internal view returns (Checkpoint208 memory) {\n        OZCheckpoints.Checkpoint208 memory checkpoint = self._trace.at(pos);\n        return Checkpoint208({_key: checkpoint._key, _value: checkpoint._value});\n    }\n\n    /**\n     * @dev Pops the last (most recent) checkpoint.\n     */\n    function pop(\n        Trace208 storage self\n    ) internal returns (uint208 value) {\n        value = self._trace.latest();\n        self._trace._checkpoints.pop();\n    }\n\n    /**\n     * @dev Pushes a (`key`, `value`) pair into a Trace256 so that it is stored as the checkpoint.\n     *\n     * Returns previous value and new value.\n     */\n    function push(Trace256 storage self, uint48 key, uint256 value) internal returns (uint256, uint256) {\n        if (self._values.length == 0) {\n            self._values.push(0);\n        }\n\n        (bool exists, uint48 lastKey,) = self._trace.latestCheckpoint();\n\n        uint256 len = self._values.length;\n        uint256 lastValue = latest(self);\n        if (exists && key == lastKey) {\n            self._values[len - 1] = value;\n        } else {\n            self._trace.push(key, uint208(len));\n            self._values.push(value);\n        }\n\n        return (lastValue, value);\n    }\n\n    /**\n     * @dev Returns the value in the last (most recent) checkpoint with a key lower or equal than the search key, \n     * or zero if there is none.\n     */\n    function upperLookupRecent(Trace256 storage self, uint48 key) internal view returns (uint256) {\n        uint208 idx = self._trace.upperLookupRecent(key);\n        return idx > 0 ? self._values[idx] : 0;\n    }\n\n    /**\n     * @dev Returns the value in the last (most recent) checkpoint with a key lower or equal than the search key, \n     * or zero if there is none.\n     *\n     * NOTE: This is a variant of {upperLookupRecent} that can be optimized by getting the hint\n     * (index of the checkpoint with a key lower or equal than the search key).\n     */\n    function upperLookupRecent(Trace256 storage self, uint48 key, bytes memory hint_) internal view returns (uint256) {\n        if (hint_.length == 0) {\n            return upperLookupRecent(self, key);\n        }\n\n        uint32 hint = abi.decode(hint_, (uint32));\n        Checkpoint256 memory checkpoint = at(self, hint);\n        if (checkpoint._key == key) {\n            return checkpoint._value;\n        }\n\n        if (checkpoint._key < key && (hint == length(self) - 1 || at(self, hint + 1)._key > key)) {\n            return checkpoint._value;\n        }\n\n        return upperLookupRecent(self, key);\n    }\n\n    /**\n     * @dev Returns whether there is a checkpoint with a key lower or equal than the search key in the structure \n     * (i.e. it is not empty), and if so the key and value in the checkpoint, and its position in the trace.\n     */\n    function upperLookupRecentCheckpoint(\n        Trace256 storage self,\n        uint48 key\n    ) internal view returns (bool, uint48, uint256, uint32) {\n        uint256 len = self._trace._checkpoints.length;\n\n        uint256 low = 0;\n        uint256 high = len;\n\n        if (len > 5) {\n            uint256 mid = len - Math.sqrt(len);\n            if (key < _unsafeAccess(self._trace._checkpoints, mid)._key) {\n                high = mid;\n            } else {\n                low = mid + 1;\n            }\n        }\n\n        uint256 pos = _upperBinaryLookup(self._trace._checkpoints, key, low, high);\n\n        if (pos == 0) {\n            return (false, 0, 0, 0);\n        }\n\n        OZCheckpoints.Checkpoint208 memory checkpoint = _unsafeAccess(self._trace._checkpoints, pos - 1);\n        return (true, checkpoint._key, self._values[checkpoint._value], uint32(pos - 1));\n    }\n\n    /**\n     * @dev Returns whether there is a checkpoint with a key lower or equal than the search key in the structure \n     * (i.e. it is not empty), and if so the key and value in the checkpoint, and its position in the trace.\n     *\n     * NOTE: This is a variant of {upperLookupRecentCheckpoint} that can be optimized by getting the hint\n     * (index of the checkpoint with a key lower or equal than the search key).\n     */\n    function upperLookupRecentCheckpoint(\n        Trace256 storage self,\n        uint48 key,\n        bytes memory hint_\n    ) internal view returns (bool, uint48, uint256, uint32) {\n        if (hint_.length == 0) {\n            return upperLookupRecentCheckpoint(self, key);\n        }\n\n        uint32 hint = abi.decode(hint_, (uint32));\n        Checkpoint256 memory checkpoint = at(self, hint);\n        if (checkpoint._key == key) {\n            return (true, checkpoint._key, checkpoint._value, hint);\n        }\n\n        if (checkpoint._key < key && (hint == length(self) - 1 || at(self, hint + 1)._key > key)) {\n            return (true, checkpoint._key, checkpoint._value, hint);\n        }\n\n        return upperLookupRecentCheckpoint(self, key);\n    }\n\n    /**\n     * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.\n     */\n    function latest(\n        Trace256 storage self\n    ) internal view returns (uint256) {\n        uint208 idx = self._trace.latest();\n        return idx > 0 ? self._values[idx] : 0;\n    }\n\n    /**\n     * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value\n     * in the most recent checkpoint.\n     */\n    function latestCheckpoint(\n        Trace256 storage self\n    ) internal view returns (bool exists, uint48 _key, uint256 _value) {\n        uint256 idx;\n        (exists, _key, idx) = self._trace.latestCheckpoint();\n        _value = exists ? self._values[idx] : 0;\n    }\n\n    /**\n     * @dev Returns a total number of checkpoints.\n     */\n    function length(\n        Trace256 storage self\n    ) internal view returns (uint256) {\n        return self._trace.length();\n    }\n\n    /**\n     * @dev Returns checkpoint at a given position.\n     */\n    function at(Trace256 storage self, uint32 pos) internal view returns (Checkpoint256 memory) {\n        OZCheckpoints.Checkpoint208 memory checkpoint = self._trace.at(pos);\n        return Checkpoint256({_key: checkpoint._key, _value: self._values[checkpoint._value]});\n    }\n\n    /**\n     * @dev Pops the last (most recent) checkpoint.\n     */\n    function pop(\n        Trace256 storage self\n    ) internal returns (uint256 value) {\n        uint208 idx = self._trace.latest();\n        if (idx == 0) {\n            revert SystemCheckpoint();\n        }\n        value = self._values[idx];\n        self._trace._checkpoints.pop();\n        self._values.pop();\n    }\n\n    /**\n     * @dev Return the index of the last (most recent) checkpoint with a key lower or equal than the search key, or \n     * `high` if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and \n     * exclusive `high`.\n     *\n     * WARNING: `high` should not be greater than the array's length.\n     */\n    function _upperBinaryLookup(\n        OZCheckpoints.Checkpoint208[] storage self,\n        uint48 key,\n        uint256 low,\n        uint256 high\n    ) private view returns (uint256) {\n        while (low < high) {\n            uint256 mid = Math.average(low, high);\n            if (_unsafeAccess(self, mid)._key > key) {\n                high = mid;\n            } else {\n                low = mid + 1;\n            }\n        }\n        return high;\n    }\n\n    /**\n     * @dev Access an element of the array without performing a bounds check. The position is assumed to be within \n     * bounds.\n     */\n    function _unsafeAccess(\n        OZCheckpoints.Checkpoint208[] storage self,\n        uint256 pos\n    ) private pure returns (OZCheckpoints.Checkpoint208 storage result) {\n        // solhint-disable-next-line no-inline-assembly\n        assembly {\n            mstore(0, self.slot)\n            result.slot := add(keccak256(0, 0x20), pos)\n        }\n    }\n}"},{"file_path":"@openzeppelin/contracts/token/ERC20/IERC20.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC-20 standard as defined in the ERC.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the value of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the value of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves a `value` amount of tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 value) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n     * caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the\n     * allowance mechanism. `value` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 value) external returns (bool);\n}\n"},{"file_path":"@openzeppelin/contracts/utils/math/SignedMath.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SignedMath.sol)\n\npragma solidity ^0.8.20;\n\nimport {SafeCast} from \"./SafeCast.sol\";\n\n/**\n * @dev Standard signed math utilities missing in the Solidity language.\n */\nlibrary SignedMath {\n    /**\n     * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.\n     *\n     * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.\n     * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute\n     * one branch when needed, making this function more expensive.\n     */\n    function ternary(bool condition, int256 a, int256 b) internal pure returns (int256) {\n        unchecked {\n            // branchless ternary works because:\n            // b ^ (a ^ b) == a\n            // b ^ 0 == b\n            return b ^ ((a ^ b) * int256(SafeCast.toUint(condition)));\n        }\n    }\n\n    /**\n     * @dev Returns the largest of two signed numbers.\n     */\n    function max(int256 a, int256 b) internal pure returns (int256) {\n        return ternary(a > b, a, b);\n    }\n\n    /**\n     * @dev Returns the smallest of two signed numbers.\n     */\n    function min(int256 a, int256 b) internal pure returns (int256) {\n        return ternary(a < b, a, b);\n    }\n\n    /**\n     * @dev Returns the average of two signed numbers without overflow.\n     * The result is rounded towards zero.\n     */\n    function average(int256 a, int256 b) internal pure returns (int256) {\n        // Formula from the book \"Hacker's Delight\"\n        int256 x = (a & b) + ((a ^ b) >> 1);\n        return x + (int256(uint256(x) >> 255) & (a ^ b));\n    }\n\n    /**\n     * @dev Returns the absolute unsigned value of a signed value.\n     */\n    function abs(int256 n) internal pure returns (uint256) {\n        unchecked {\n            // Formula from the \"Bit Twiddling Hacks\" by Sean Eron Anderson.\n            // Since `n` is a signed integer, the generated bytecode will use the SAR opcode to perform the right shift,\n            // taking advantage of the most significant (or \"sign\" bit) in two's complement representation.\n            // This opcode adds new most significant bits set to the value of the previous most significant bit. As a result,\n            // the mask will either be `bytes32(0)` (if n is positive) or `~bytes32(0)` (if n is negative).\n            int256 mask = n >> 255;\n\n            // A `bytes32(0)` mask leaves the input unchanged, while a `~bytes32(0)` mask complements it.\n            return uint256((n + mask) ^ mask);\n        }\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/interfaces/IERC4626.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC4626.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC20} from \"../token/ERC20/IERC20.sol\";\nimport {IERC20Metadata} from \"../token/ERC20/extensions/IERC20Metadata.sol\";\n\n/**\n * @dev Interface of the ERC-4626 \"Tokenized Vault Standard\", as defined in\n * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].\n */\ninterface IERC4626 is IERC20, IERC20Metadata {\n    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);\n\n    event Withdraw(\n        address indexed sender,\n        address indexed receiver,\n        address indexed owner,\n        uint256 assets,\n        uint256 shares\n    );\n\n    /**\n     * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.\n     *\n     * - MUST be an ERC-20 token contract.\n     * - MUST NOT revert.\n     */\n    function asset() external view returns (address assetTokenAddress);\n\n    /**\n     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.\n     *\n     * - SHOULD include any compounding that occurs from yield.\n     * - MUST be inclusive of any fees that are charged against assets in the Vault.\n     * - MUST NOT revert.\n     */\n    function totalAssets() external view returns (uint256 totalManagedAssets);\n\n    /**\n     * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal\n     * scenario where all the conditions are met.\n     *\n     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n     * - MUST NOT show any variations depending on the caller.\n     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n     * - MUST NOT revert.\n     *\n     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n     * from.\n     */\n    function convertToShares(uint256 assets) external view returns (uint256 shares);\n\n    /**\n     * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal\n     * scenario where all the conditions are met.\n     *\n     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.\n     * - MUST NOT show any variations depending on the caller.\n     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.\n     * - MUST NOT revert.\n     *\n     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the\n     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and\n     * from.\n     */\n    function convertToAssets(uint256 shares) external view returns (uint256 assets);\n\n    /**\n     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,\n     * through a deposit call.\n     *\n     * - MUST return a limited value if receiver is subject to some deposit limit.\n     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.\n     * - MUST NOT revert.\n     */\n    function maxDeposit(address receiver) external view returns (uint256 maxAssets);\n\n    /**\n     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given\n     * current on-chain conditions.\n     *\n     * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit\n     *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called\n     *   in the same transaction.\n     * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the\n     *   deposit would be accepted, regardless if the user has enough tokens approved, etc.\n     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n     * - MUST NOT revert.\n     *\n     * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in\n     * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n     */\n    function previewDeposit(uint256 assets) external view returns (uint256 shares);\n\n    /**\n     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.\n     *\n     * - MUST emit the Deposit event.\n     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n     *   deposit execution, and are accounted for during deposit.\n     * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not\n     *   approving enough underlying tokens to the Vault contract, etc).\n     *\n     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n     */\n    function deposit(uint256 assets, address receiver) external returns (uint256 shares);\n\n    /**\n     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.\n     * - MUST return a limited value if receiver is subject to some mint limit.\n     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.\n     * - MUST NOT revert.\n     */\n    function maxMint(address receiver) external view returns (uint256 maxShares);\n\n    /**\n     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given\n     * current on-chain conditions.\n     *\n     * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call\n     *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the\n     *   same transaction.\n     * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint\n     *   would be accepted, regardless if the user has enough tokens approved, etc.\n     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.\n     * - MUST NOT revert.\n     *\n     * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in\n     * share price or some other type of condition, meaning the depositor will lose assets by minting.\n     */\n    function previewMint(uint256 shares) external view returns (uint256 assets);\n\n    /**\n     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.\n     *\n     * - MUST emit the Deposit event.\n     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint\n     *   execution, and are accounted for during mint.\n     * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not\n     *   approving enough underlying tokens to the Vault contract, etc).\n     *\n     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.\n     */\n    function mint(uint256 shares, address receiver) external returns (uint256 assets);\n\n    /**\n     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the\n     * Vault, through a withdraw call.\n     *\n     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n     * - MUST NOT revert.\n     */\n    function maxWithdraw(address owner) external view returns (uint256 maxAssets);\n\n    /**\n     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,\n     * given current on-chain conditions.\n     *\n     * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw\n     *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if\n     *   called\n     *   in the same transaction.\n     * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though\n     *   the withdrawal would be accepted, regardless if the user has enough shares, etc.\n     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n     * - MUST NOT revert.\n     *\n     * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in\n     * share price or some other type of condition, meaning the depositor will lose assets by depositing.\n     */\n    function previewWithdraw(uint256 assets) external view returns (uint256 shares);\n\n    /**\n     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.\n     *\n     * - MUST emit the Withdraw event.\n     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n     *   withdraw execution, and are accounted for during withdraw.\n     * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner\n     *   not having enough shares, etc).\n     *\n     * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n     * Those methods should be performed separately.\n     */\n    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);\n\n    /**\n     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,\n     * through a redeem call.\n     *\n     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.\n     * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.\n     * - MUST NOT revert.\n     */\n    function maxRedeem(address owner) external view returns (uint256 maxShares);\n\n    /**\n     * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,\n     * given current on-chain conditions.\n     *\n     * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call\n     *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the\n     *   same transaction.\n     * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the\n     *   redemption would be accepted, regardless if the user has enough shares, etc.\n     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.\n     * - MUST NOT revert.\n     *\n     * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in\n     * share price or some other type of condition, meaning the depositor will lose assets by redeeming.\n     */\n    function previewRedeem(uint256 shares) external view returns (uint256 assets);\n\n    /**\n     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.\n     *\n     * - MUST emit the Withdraw event.\n     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the\n     *   redeem execution, and are accounted for during redeem.\n     * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner\n     *   not having enough shares, etc).\n     *\n     * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.\n     * Those methods should be performed separately.\n     */\n    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);\n}\n"},{"file_path":"contracts/test/MockIncidentManagerCoverOrderAllocator.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.28;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport {ICoverOrderAllocator} from \"../core/interfaces/ICoverOrderAllocator.sol\";\nimport {IFirelightVault} from \"../core/interfaces/IFirelightVault.sol\";\nimport {IAggregatorV3} from \"../core/interfaces/IAggregatorV3.sol\";\nimport {CoverNFT} from \"../core/CoverNFT.sol\";\n\n/**\n *\n *      IncidentManager only consumes a small slice of the CoverOrderAllocator surface:\n *        - `vault()`                        (read once in initialize)\n *        - `CANONICAL_DECIMALS()`           (read once in initialize)\n *        - `getCoverOrderMarketInfo(...)`    (read per assessment loss)\n *        - `getCapacityConfigAt(period)`    (read inside _executePayout — FLB token + payer\n *                                          come from the period's CapacityConfig)\n *\n *      Everything else in ICoverOrderAllocator is stubbed to safe zero / no-op so the\n *      mock satisfies the interface but is not exercised beyond IncidentManager's needs.\n */\ncontract MockIncidentManagerCoverOrderAllocator is ICoverOrderAllocator {\n    struct OrderMarket {\n        uint256 period;\n        uint256 allocatedCoverAmount;\n        address payoutRecipient;\n    }\n\n    IFirelightVault private _vault;\n    uint8 private _canonicalDecimals = 18;\n    CapacityConfig private _capacityConfig;\n    mapping(uint256 coverTokenId => mapping(bytes32 marketId => OrderMarket)) private _orderMarkets;\n\n    // -------------------------------------------------------------------------\n    // Test setters\n    // -------------------------------------------------------------------------\n\n    function setVault(IFirelightVault v) external { _vault = v; }\n    function setCanonicalDecimals(uint8 d) external { _canonicalDecimals = d; }\n    function setMockCapacityConfig(CapacityConfig calldata c) external { _capacityConfig = c; }\n\n    function setOrderMarket(\n        uint256 coverTokenId,\n        bytes32 marketId,\n        uint256 period,\n        uint256 allocatedCoverAmount,\n        address payoutRecipient\n    ) external {\n        _orderMarkets[coverTokenId][marketId] = OrderMarket(period, allocatedCoverAmount, payoutRecipient);\n    }\n\n    // -------------------------------------------------------------------------\n    // Functions exercised by IncidentManager\n    // -------------------------------------------------------------------------\n\n    function CANONICAL_DECIMALS() external view override returns (uint8) { return _canonicalDecimals; }\n    function vault() external view override returns (IFirelightVault) { return _vault; }\n\n    function getCapacityConfigAt(uint256) external view override returns (CapacityConfig memory) {\n        return _capacityConfig;\n    }\n\n    function getCoverOrderMarketInfo(\n        uint256 coverTokenId,\n        bytes32 marketId\n    ) external view override returns (uint256 period, uint256 allocatedCoverAmount, address payoutRecipient) {\n        OrderMarket storage m = _orderMarkets[coverTokenId][marketId];\n        return (m.period, m.allocatedCoverAmount, m.payoutRecipient);\n    }\n\n    // -------------------------------------------------------------------------\n    // Interface stubs (not used by IncidentManager)\n    // -------------------------------------------------------------------------\n\n    function createCoverOrder(\n        address,\n        address,\n        string calldata,\n        address,\n        MarketAllocationInput[] calldata,\n        CoverOrderType\n    ) external pure override returns (uint256) {\n        return 0;\n    }\n\n    function commitAllocation(uint256, bytes32, uint256, uint256) external pure override {}\n    function setPriceFeedAdapter(IAggregatorV3) external pure override {}\n    function setMaxPriceAge(uint48) external pure override {}\n    function setSettlementGracePeriod(uint48) external pure override {}\n    function priceFeedAdapter() external pure override returns (IAggregatorV3) { return IAggregatorV3(address(0)); }\n    function maxPriceAge() external pure override returns (uint48) { return 0; }\n    function settlementGracePeriod() external pure override returns (uint48) { return 0; }\n    function priceFeedDecimals() external pure override returns (uint8) { return 0; }\n    function settleCoverOrder(uint256, MarketCoverAllocation[] calldata, bytes32[] calldata) external pure override {}\n    function batchSettleCoverOrder(SettleParams[] calldata) external pure override {}\n    function cancelCoverOrder(uint256) external pure override {}\n    function cancelExpiredOrders(uint256[] calldata) external pure override {}\n    function recommitAllocation(uint256, bytes32, uint256, uint256) external pure override {}\n    function cancelCommitAllocation(uint256) external pure override {}\n\n    function setPremiumCollector(address) external pure override {}\n    function addSupportedPremiumToken(address) external pure override {}\n    function removeSupportedPremiumToken(address) external pure override {}\n    function setCapacityConfig(CapacityConfig calldata) external pure override {}\n    function addSupportedMarket(Market calldata) external pure override returns (bytes32) {\n        return bytes32(0);\n    }\n    function setProtocolConcentration(ProtocolConcentrationInput calldata) external pure override {}\n    function batchSetProtocolConcentration(ProtocolConcentrationInput[] calldata) external pure override {}\n\n    function premiumCollector() external pure override returns (address) {\n        return address(0);\n    }\n    function coverNFT() external pure override returns (CoverNFT) {\n        return CoverNFT(address(0));\n    }\n    function nextCoverOrderId() external pure override returns (uint256) {\n        return 0;\n    }\n    function getCoverOrder(uint256) external pure override returns (CoverOrder memory o) {\n        return o;\n    }\n    function getCoverOrderMarkets(uint256) external pure override returns (MarketAllocation[] memory m) {\n        return m;\n    }\n    function getCoverOrdersIdByPeriod(uint256) external pure override returns (uint256[] memory ids) {\n        return ids;\n    }\n    function getSupportedMarket(bytes32) external pure override returns (Market memory m) {\n        return m;\n    }\n    function getSupportedMarketIds() external pure override returns (bytes32[] memory ids) {\n        return ids;\n    }\n    function isPremiumTokenSupported(address) external pure override returns (bool) {\n        return false;\n    }\n    function getSupportedPremiumTokens() external pure override returns (address[] memory tokens) {\n        return tokens;\n    }\n    function getEffectiveCapacityConfig() external view override returns (CapacityConfig memory) {\n        return _capacityConfig;\n    }\n    function getAllocationCommitment(uint256) external pure override returns (AllocationCommitment memory c) {\n        return c;\n    }\n    function getSupportedProtocolConcentrationHashes() external pure override returns (bytes32[] memory h) {\n        return h;\n    }\n    function getProtocolConcentrationFromHash(bytes32) external pure override returns (ProtocolConcentration memory c) {\n        return c;\n    }\n    function getEffectiveProtocolConcentration(bytes32) external pure override returns (uint256) {\n        return 0;\n    }\n    function getProtocolConcentrationAt(bytes32, uint256) external pure override returns (uint256) {\n        return 0;\n    }\n    function getProtocolConcentrationSettledCover(uint256, bytes32) external pure override returns (uint256) {\n        return 0;\n    }\n}\n"},{"file_path":"@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/extensions/ERC721Enumerable.sol)\n\npragma solidity ^0.8.20;\n\nimport {ERC721Upgradeable} from \"../ERC721Upgradeable.sol\";\nimport {IERC721Enumerable} from \"@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol\";\nimport {IERC165} from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\nimport {Initializable} from \"../../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev This implements an optional extension of {ERC721} defined in the ERC that adds enumerability\n * of all the token ids in the contract as well as all token ids owned by each account.\n *\n * CAUTION: {ERC721} extensions that implement custom `balanceOf` logic, such as {ERC721Consecutive},\n * interfere with enumerability and should not be used together with {ERC721Enumerable}.\n */\nabstract contract ERC721EnumerableUpgradeable is Initializable, ERC721Upgradeable, IERC721Enumerable {\n    /// @custom:storage-location erc7201:openzeppelin.storage.ERC721Enumerable\n    struct ERC721EnumerableStorage {\n        mapping(address owner => mapping(uint256 index => uint256)) _ownedTokens;\n        mapping(uint256 tokenId => uint256) _ownedTokensIndex;\n\n        uint256[] _allTokens;\n        mapping(uint256 tokenId => uint256) _allTokensIndex;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.ERC721Enumerable\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant ERC721EnumerableStorageLocation = 0x645e039705490088daad89bae25049a34f4a9072d398537b1ab2425f24cbed00;\n\n    function _getERC721EnumerableStorage() private pure returns (ERC721EnumerableStorage storage $) {\n        assembly {\n            $.slot := ERC721EnumerableStorageLocation\n        }\n    }\n\n    /**\n     * @dev An `owner`'s token query was out of bounds for `index`.\n     *\n     * NOTE: The owner being `address(0)` indicates a global out of bounds index.\n     */\n    error ERC721OutOfBoundsIndex(address owner, uint256 index);\n\n    /**\n     * @dev Batch mint is not allowed.\n     */\n    error ERC721EnumerableForbiddenBatchMint();\n\n    function __ERC721Enumerable_init() internal onlyInitializing {\n    }\n\n    function __ERC721Enumerable_init_unchained() internal onlyInitializing {\n    }\n    /// @inheritdoc IERC165\n    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721Upgradeable) returns (bool) {\n        return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);\n    }\n\n    /// @inheritdoc IERC721Enumerable\n    function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual returns (uint256) {\n        ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();\n        if (index >= balanceOf(owner)) {\n            revert ERC721OutOfBoundsIndex(owner, index);\n        }\n        return $._ownedTokens[owner][index];\n    }\n\n    /// @inheritdoc IERC721Enumerable\n    function totalSupply() public view virtual returns (uint256) {\n        ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();\n        return $._allTokens.length;\n    }\n\n    /// @inheritdoc IERC721Enumerable\n    function tokenByIndex(uint256 index) public view virtual returns (uint256) {\n        ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();\n        if (index >= totalSupply()) {\n            revert ERC721OutOfBoundsIndex(address(0), index);\n        }\n        return $._allTokens[index];\n    }\n\n    /// @inheritdoc ERC721Upgradeable\n    function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) {\n        address previousOwner = super._update(to, tokenId, auth);\n\n        if (previousOwner == address(0)) {\n            _addTokenToAllTokensEnumeration(tokenId);\n        } else if (previousOwner != to) {\n            _removeTokenFromOwnerEnumeration(previousOwner, tokenId);\n        }\n        if (to == address(0)) {\n            _removeTokenFromAllTokensEnumeration(tokenId);\n        } else if (previousOwner != to) {\n            _addTokenToOwnerEnumeration(to, tokenId);\n        }\n\n        return previousOwner;\n    }\n\n    /**\n     * @dev Private function to add a token to this extension's ownership-tracking data structures.\n     * @param to address representing the new owner of the given token ID\n     * @param tokenId uint256 ID of the token to be added to the tokens list of the given address\n     */\n    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {\n        ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();\n        uint256 length = balanceOf(to) - 1;\n        $._ownedTokens[to][length] = tokenId;\n        $._ownedTokensIndex[tokenId] = length;\n    }\n\n    /**\n     * @dev Private function to add a token to this extension's token tracking data structures.\n     * @param tokenId uint256 ID of the token to be added to the tokens list\n     */\n    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {\n        ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();\n        $._allTokensIndex[tokenId] = $._allTokens.length;\n        $._allTokens.push(tokenId);\n    }\n\n    /**\n     * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that\n     * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for\n     * gas optimizations e.g. when performing a transfer operation (avoiding double writes).\n     * This has O(1) time complexity, but alters the order of the _ownedTokens array.\n     * @param from address representing the previous owner of the given token ID\n     * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address\n     */\n    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {\n        ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();\n        // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and\n        // then delete the last slot (swap and pop).\n\n        uint256 lastTokenIndex = balanceOf(from);\n        uint256 tokenIndex = $._ownedTokensIndex[tokenId];\n\n        mapping(uint256 index => uint256) storage _ownedTokensByOwner = $._ownedTokens[from];\n\n        // When the token to delete is the last token, the swap operation is unnecessary\n        if (tokenIndex != lastTokenIndex) {\n            uint256 lastTokenId = _ownedTokensByOwner[lastTokenIndex];\n\n            _ownedTokensByOwner[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token\n            $._ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index\n        }\n\n        // This also deletes the contents at the last position of the array\n        delete $._ownedTokensIndex[tokenId];\n        delete _ownedTokensByOwner[lastTokenIndex];\n    }\n\n    /**\n     * @dev Private function to remove a token from this extension's token tracking data structures.\n     * This has O(1) time complexity, but alters the order of the _allTokens array.\n     * @param tokenId uint256 ID of the token to be removed from the tokens list\n     */\n    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {\n        ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();\n        // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and\n        // then delete the last slot (swap and pop).\n\n        uint256 lastTokenIndex = $._allTokens.length - 1;\n        uint256 tokenIndex = $._allTokensIndex[tokenId];\n\n        // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so\n        // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding\n        // an 'if' statement (like in _removeTokenFromOwnerEnumeration)\n        uint256 lastTokenId = $._allTokens[lastTokenIndex];\n\n        $._allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token\n        $._allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index\n\n        // This also deletes the contents at the last position of the array\n        delete $._allTokensIndex[tokenId];\n        $._allTokens.pop();\n    }\n\n    /**\n     * See {ERC721-_increaseBalance}. We need that to account tokens that were minted in batch\n     */\n    function _increaseBalance(address account, uint128 amount) internal virtual override {\n        if (amount > 0) {\n            revert ERC721EnumerableForbiddenBatchMint();\n        }\n        super._increaseBalance(account, amount);\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/utils/ERC1155Holder.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC165, ERC165} from \"../../../utils/introspection/ERC165.sol\";\nimport {IERC1155Receiver} from \"../IERC1155Receiver.sol\";\n\n/**\n * @dev Simple implementation of `IERC1155Receiver` that will allow a contract to hold ERC-1155 tokens.\n *\n * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be\n * stuck.\n */\nabstract contract ERC1155Holder is ERC165, IERC1155Receiver {\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {\n        return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);\n    }\n\n    function onERC1155Received(\n        address,\n        address,\n        uint256,\n        uint256,\n        bytes memory\n    ) public virtual override returns (bytes4) {\n        return this.onERC1155Received.selector;\n    }\n\n    function onERC1155BatchReceived(\n        address,\n        address,\n        uint256[] memory,\n        uint256[] memory,\n        bytes memory\n    ) public virtual override returns (bytes4) {\n        return this.onERC1155BatchReceived.selector;\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/access/AccessControl.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)\n\npragma solidity ^0.8.20;\n\nimport {IAccessControl} from \"./IAccessControl.sol\";\nimport {Context} from \"../utils/Context.sol\";\nimport {ERC165} from \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```solidity\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```solidity\n * function foo() public {\n *     require(hasRole(MY_ROLE, msg.sender));\n *     ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}\n * to enforce additional security measures for this role.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n    struct RoleData {\n        mapping(address account => bool) hasRole;\n        bytes32 adminRole;\n    }\n\n    mapping(bytes32 role => RoleData) private _roles;\n\n    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n    /**\n     * @dev Modifier that checks that an account has a specific role. Reverts\n     * with an {AccessControlUnauthorizedAccount} error including the required role.\n     */\n    modifier onlyRole(bytes32 role) {\n        _checkRole(role);\n        _;\n    }\n\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n    }\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) public view virtual returns (bool) {\n        return _roles[role].hasRole[account];\n    }\n\n    /**\n     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`\n     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.\n     */\n    function _checkRole(bytes32 role) internal view virtual {\n        _checkRole(role, _msgSender());\n    }\n\n    /**\n     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`\n     * is missing `role`.\n     */\n    function _checkRole(bytes32 role, address account) internal view virtual {\n        if (!hasRole(role, account)) {\n            revert AccessControlUnauthorizedAccount(account, role);\n        }\n    }\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role's admin, use {_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {\n        return _roles[role].adminRole;\n    }\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     *\n     * May emit a {RoleGranted} event.\n     */\n    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {\n        _grantRole(role, account);\n    }\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {\n        _revokeRole(role, account);\n    }\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function's\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been revoked `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `callerConfirmation`.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function renounceRole(bytes32 role, address callerConfirmation) public virtual {\n        if (callerConfirmation != _msgSender()) {\n            revert AccessControlBadConfirmation();\n        }\n\n        _revokeRole(role, callerConfirmation);\n    }\n\n    /**\n     * @dev Sets `adminRole` as ``role``'s admin role.\n     *\n     * Emits a {RoleAdminChanged} event.\n     */\n    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n        bytes32 previousAdminRole = getRoleAdmin(role);\n        _roles[role].adminRole = adminRole;\n        emit RoleAdminChanged(role, previousAdminRole, adminRole);\n    }\n\n    /**\n     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.\n     *\n     * Internal function without access restriction.\n     *\n     * May emit a {RoleGranted} event.\n     */\n    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {\n        if (!hasRole(role, account)) {\n            _roles[role].hasRole[account] = true;\n            emit RoleGranted(role, account, _msgSender());\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.\n     *\n     * Internal function without access restriction.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {\n        if (hasRole(role, account)) {\n            _roles[role].hasRole[account] = false;\n            emit RoleRevoked(role, account, _msgSender());\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n"},{"file_path":"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```solidity\n * contract MyToken is ERC20Upgradeable {\n *     function initialize() initializer public {\n *         __ERC20_init(\"MyToken\", \"MTK\");\n *     }\n * }\n *\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n *     function initializeV2() reinitializer(2) public {\n *         __ERC20Permit_init(\"MyToken\");\n *     }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n *     _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract Initializable {\n    /**\n     * @dev Storage of the initializable contract.\n     *\n     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions\n     * when using with upgradeable contracts.\n     *\n     * @custom:storage-location erc7201:openzeppelin.storage.Initializable\n     */\n    struct InitializableStorage {\n        /**\n         * @dev Indicates that the contract has been initialized.\n         */\n        uint64 _initialized;\n        /**\n         * @dev Indicates that the contract is in the process of being initialized.\n         */\n        bool _initializing;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.Initializable\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;\n\n    /**\n     * @dev The contract is already initialized.\n     */\n    error InvalidInitialization();\n\n    /**\n     * @dev The contract is not initializing.\n     */\n    error NotInitializing();\n\n    /**\n     * @dev Triggered when the contract has been initialized or reinitialized.\n     */\n    event Initialized(uint64 version);\n\n    /**\n     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n     * `onlyInitializing` functions can be used to initialize parent contracts.\n     *\n     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any\n     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in\n     * production.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier initializer() {\n        // solhint-disable-next-line var-name-mixedcase\n        InitializableStorage storage $ = _getInitializableStorage();\n\n        // Cache values to avoid duplicated sloads\n        bool isTopLevelCall = !$._initializing;\n        uint64 initialized = $._initialized;\n\n        // Allowed calls:\n        // - initialSetup: the contract is not in the initializing state and no previous version was\n        //                 initialized\n        // - construction: the contract is initialized at version 1 (no reinitialization) and the\n        //                 current contract is just being deployed\n        bool initialSetup = initialized == 0 && isTopLevelCall;\n        bool construction = initialized == 1 && address(this).code.length == 0;\n\n        if (!initialSetup && !construction) {\n            revert InvalidInitialization();\n        }\n        $._initialized = 1;\n        if (isTopLevelCall) {\n            $._initializing = true;\n        }\n        _;\n        if (isTopLevelCall) {\n            $._initializing = false;\n            emit Initialized(1);\n        }\n    }\n\n    /**\n     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n     * used to initialize parent contracts.\n     *\n     * A reinitializer may be used after the original initialization step. This is essential to configure modules that\n     * are added through upgrades and that require initialization.\n     *\n     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\n     * cannot be nested. If one is invoked in the context of another, execution will revert.\n     *\n     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n     * a contract, executing them in the right order is up to the developer or operator.\n     *\n     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier reinitializer(uint64 version) {\n        // solhint-disable-next-line var-name-mixedcase\n        InitializableStorage storage $ = _getInitializableStorage();\n\n        if ($._initializing || $._initialized >= version) {\n            revert InvalidInitialization();\n        }\n        $._initialized = version;\n        $._initializing = true;\n        _;\n        $._initializing = false;\n        emit Initialized(version);\n    }\n\n    /**\n     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n     * {initializer} and {reinitializer} modifiers, directly or indirectly.\n     */\n    modifier onlyInitializing() {\n        _checkInitializing();\n        _;\n    }\n\n    /**\n     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.\n     */\n    function _checkInitializing() internal view virtual {\n        if (!_isInitializing()) {\n            revert NotInitializing();\n        }\n    }\n\n    /**\n     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n     * through proxies.\n     *\n     * Emits an {Initialized} event the first time it is successfully executed.\n     */\n    function _disableInitializers() internal virtual {\n        // solhint-disable-next-line var-name-mixedcase\n        InitializableStorage storage $ = _getInitializableStorage();\n\n        if ($._initializing) {\n            revert InvalidInitialization();\n        }\n        if ($._initialized != type(uint64).max) {\n            $._initialized = type(uint64).max;\n            emit Initialized(type(uint64).max);\n        }\n    }\n\n    /**\n     * @dev Returns the highest version that has been initialized. See {reinitializer}.\n     */\n    function _getInitializedVersion() internal view returns (uint64) {\n        return _getInitializableStorage()._initialized;\n    }\n\n    /**\n     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.\n     */\n    function _isInitializing() internal view returns (bool) {\n        return _getInitializableStorage()._initializing;\n    }\n\n    /**\n     * @dev Pointer to storage slot. Allows integrators to override it with a custom storage location.\n     *\n     * NOTE: Consider following the ERC-7201 formula to derive storage locations.\n     */\n    function _initializableStorageSlot() internal pure virtual returns (bytes32) {\n        return INITIALIZABLE_STORAGE;\n    }\n\n    /**\n     * @dev Returns a pointer to the storage namespace.\n     */\n    // solhint-disable-next-line var-name-mixedcase\n    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {\n        bytes32 slot = _initializableStorageSlot();\n        assembly {\n            $.slot := slot\n        }\n    }\n}\n"},{"file_path":"contracts/test/MockAggregatorV3.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.28;\n\nimport {IAggregatorV3} from \"../core/interfaces/IAggregatorV3.sol\";\n\n/// @dev Minimal Chainlink V3-compatible aggregator mock for CoverOrderAllocator tests.\ncontract MockAggregatorV3 is IAggregatorV3 {\n    uint8 private _decimals;\n    int256 private _answer;\n    uint256 private _updatedAt;\n    uint80 private _roundId = 1;\n    uint80 private _answeredInRound = 1;\n\n    constructor(uint8 decimals_, int256 initialAnswer) {\n        _decimals = decimals_;\n        _answer = initialAnswer;\n        _updatedAt = block.timestamp;\n    }\n\n    function setAnswer(int256 newAnswer) external {\n        _answer = newAnswer;\n        _updatedAt = block.timestamp;\n    }\n\n    function setUpdatedAt(uint256 newUpdatedAt) external { _updatedAt = newUpdatedAt; }\n    function setRoundIds(uint80 roundId_, uint80 answeredInRound_) external {\n        _roundId = roundId_;\n        _answeredInRound = answeredInRound_;\n    }\n\n    function decimals() external view override returns (uint8) { return _decimals; }\n    function description() external pure override returns (string memory) { return \"Mock/USD\"; }\n    function version() external pure override returns (uint256) { return 3; }\n\n    function getRoundData(uint80)\n        external\n        view\n        override\n        returns (uint80, int256, uint256, uint256, uint80)\n    {\n        return (_roundId, _answer, _updatedAt, _updatedAt, _answeredInRound);\n    }\n\n    function latestRoundData()\n        external\n        view\n        override\n        returns (uint80, int256, uint256, uint256, uint80)\n    {\n        return (_roundId, _answer, _updatedAt, _updatedAt, _answeredInRound);\n    }\n}\n"},{"file_path":"contracts/core/IncidentManager.sol","source_code":"// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.28;\n\nimport {AccessControlUpgradeable} from \"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol\";\nimport {ReentrancyGuardUpgradeable} from \"@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {IERC20Metadata} from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport {SafeERC20} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {Math} from \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport {ICoverOrderAllocator} from \"./interfaces/ICoverOrderAllocator.sol\";\nimport {IFirelightVault} from \"./interfaces/IFirelightVault.sol\";\nimport {IIncidentManager} from \"./interfaces/IIncidentManager.sol\";\nimport {PriceFeed} from \"./lib/PriceFeed.sol\";\nimport {Decimals} from \"./lib/Decimals.sol\";\nimport {IAggregatorV3} from \"./interfaces/IAggregatorV3.sol\";\n\n/**\n * @title IncidentManager\n * @notice Coordinates protocol incident reporting, assessment, approval, and payout execution.\n * Incidents are assigned to vault periods using their `captureTimestamp` and assessed against settled cover-market\n * allocations. Incident approvals prioritize unresolved incidents from the previous vault period, then use\n * capture-timestamp order within the current period. Approved assessments execute a payout waterfall that uses the\n * first-loss buffer before requesting any remaining amount from the vault. Payouts are sent to the configured\n * `payoutReceiver`, and any per-recipient distribution is handled off-chain.\n *\n * Incident status flow:\n * - `OPEN` -> `CONFIRMED` -> `UNDER_EVALUATION` -> `CLOSED`\n * - `OPEN` or `CONFIRMED` -> `CANCELED` before assessment starts.\n * - `UNDER_EVALUATION` -> `CANCELED` when an incident is invalidated during assessment.\n *\n * Assessment round status flow:\n * - `DRAFT` -> `UNDER_EVALUATION` -> `APPROVED`\n * - `DRAFT` -> `UNDER_EVALUATION` -> `REJECTED`\n * - `DRAFT` or `UNDER_EVALUATION` -> `CANCELED`\n * - After a round is `REJECTED` or `CANCELED`, the incident remains `UNDER_EVALUATION`, and a new `DRAFT`\n *   assessment round can be opened.\n *\n * Role responsibilities:\n * - `DEFAULT_ADMIN_ROLE`: manages role assignments.\n * - `CURATOR_ROLE`: creates incidents and assessed losses.\n * - `ASSESSMENT_APPROVER_ROLE`: approves assessments and executes payout.\n * - `ASSESSMENT_REJECTER_ROLE`: rejects submitted assessment rounds.\n * - `INCIDENT_INVALIDATOR_ROLE`: cancels incidents that are already under assessment.\n * - `CONFIG_ADMIN_ROLE`: updates incident report URIs.\n * - `PAYOUT_ADMIN_ROLE`: updates the payout receiver.\n * - `PRICE_FEED_ADMIN_ROLE`: updates the price feed adapter and freshness threshold.\n *\n * `CURATOR_ROLE` and `ASSESSMENT_APPROVER_ROLE` are responsible for ensuring assessments do not overstate claims\n * across related incidents.\n * @custom:security-contact securityreport@firelight.finance\n */\ncontract IncidentManager is IIncidentManager, AccessControlUpgradeable, ReentrancyGuardUpgradeable {\n    using SafeERC20 for IERC20;\n    using Math for uint256;\n    using PriceFeed for IAggregatorV3;\n\n    /// @notice Role allowed to create incidents and assessed losses.\n    bytes32 public constant CURATOR_ROLE = keccak256(\"CURATOR_ROLE\");\n    /// @notice Role allowed to approve assessments and execute payout.\n    bytes32 public constant ASSESSMENT_APPROVER_ROLE = keccak256(\"ASSESSMENT_APPROVER_ROLE\");\n    /// @notice Role allowed to reject submitted assessment rounds.\n    bytes32 public constant ASSESSMENT_REJECTER_ROLE = keccak256(\"ASSESSMENT_REJECTER_ROLE\");\n    /// @notice Role allowed to cancel incidents that are already under assessment.\n    bytes32 public constant INCIDENT_INVALIDATOR_ROLE = keccak256(\"INCIDENT_INVALIDATOR_ROLE\");\n    /// @notice Role allowed to update incident report URIs.\n    bytes32 public constant CONFIG_ADMIN_ROLE = keccak256(\"CONFIG_ADMIN_ROLE\");\n    /// @notice Role allowed to update the payout receiver.\n    bytes32 public constant PAYOUT_ADMIN_ROLE = keccak256(\"PAYOUT_ADMIN_ROLE\");\n    /// @notice Role allowed to update the price feed adapter and freshness threshold.\n    bytes32 public constant PRICE_FEED_ADMIN_ROLE = keccak256(\"PRICE_FEED_ADMIN_ROLE\");\n\n    /// @notice Maximum length of an incident title.\n    uint256 public constant MAX_INCIDENT_TITLE_LENGTH = 64;\n    /// @notice Maximum length of an incident cancel reason.\n    uint256 public constant MAX_CANCEL_REASON_LENGTH = 256;\n\n    // --- ERC-7201 Namespaced Storage ---\n    /// @custom:storage-location erc7201:firelight.incidentmanager.storage\n    struct IncidentManagerStorage {\n        ICoverOrderAllocator coverOrderAllocator;\n        IFirelightVault vault;\n        IAggregatorV3 priceFeedAdapter;\n        address payoutReceiver;\n        uint256 nextIncidentId;\n        uint48 maxPriceAge;\n        uint8 priceFeedDecimals;\n        uint8 vaultAssetDecimals;\n        uint8 canonicalDecimals;\n        mapping(uint256 incidentId => Incident) incidents;\n        mapping(bytes32 incidentRef => uint256 incidentId) incidentRefs;\n        mapping(uint256 incidentId => mapping(uint256 assessmentRoundId => AssessmentRound)) assessmentRounds;\n        mapping(uint256 incidentId => mapping(uint256 assessmentRoundId => AssessmentLoss[])) assessmentLosses;\n        mapping(uint256 incidentId => mapping(uint256 assessmentRoundId => mapping(uint256 coverTokenId => mapping(bytes32 marketId => bool)))) assessmentLossExists;\n        mapping(uint256 incidentId => mapping(uint256 assessmentRoundId => mapping(address payoutRecipient => uint256 amount))) payoutRecipientAssessmentLoss;\n        mapping(uint256 period => uint256 count) activeIncidentCountByPeriod;\n        // Used to enforce FIFO priority by capture timestamp within each period.\n        mapping(uint256 period => uint48[] timestamps) incidentTimestamps;\n        mapping(uint256 period => mapping(uint48 timestamp => uint256 incidentId)) incidentIdByTimestamp;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"firelight.incidentmanager.storage\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant STORAGE_LOCATION = 0xb6f563f2177a2296ef417ede66b7ea0704c1a671ddd9f25275dcd155f1fd2a00;\n\n    function _getStorage() private pure returns (IncidentManagerStorage storage $) {\n        assembly {\n            $.slot := STORAGE_LOCATION\n        }\n    }\n\n    /// @custom:oz-upgrades-unsafe-allow constructor\n    constructor() {\n        _disableInitializers();\n    }\n\n    /**\n     * @notice Initializes the incident manager dependencies, roles, payout receiver, and price feed settings.\n     * @param admin Address receiving `DEFAULT_ADMIN_ROLE`.\n     * @param curator Address receiving `CURATOR_ROLE`.\n     * @param assessmentApprover Address receiving `ASSESSMENT_APPROVER_ROLE`.\n     * @param assessmentRejecter Address receiving `ASSESSMENT_REJECTER_ROLE`.\n     * @param incidentInvalidator Address receiving `INCIDENT_INVALIDATOR_ROLE`.\n     * @param configAdmin Optional address receiving `CONFIG_ADMIN_ROLE`.\n     * @param payoutAdmin Optional address receiving `PAYOUT_ADMIN_ROLE`.\n     * @param priceFeedAdmin Optional address receiving `PRICE_FEED_ADMIN_ROLE`.\n     * @param _payoutReceiver Address receiving approved incident payouts.\n     * @param _coverOrderAllocator Cover order allocator used for settled allocation checks.\n     * @param _priceFeedAdapter Price feed used to value first-loss buffer payouts.\n     * @param _maxPriceAge Maximum allowed age for price feed answers.\n     */\n    function initialize(\n        address admin,\n        address curator,\n        address assessmentApprover,\n        address assessmentRejecter,\n        address incidentInvalidator,\n        address configAdmin,\n        address payoutAdmin,\n        address priceFeedAdmin,\n        address _payoutReceiver,\n        ICoverOrderAllocator _coverOrderAllocator,\n        IAggregatorV3 _priceFeedAdapter,\n        uint48 _maxPriceAge\n    ) external initializer {\n        if (\n            admin == address(0) ||\n            curator == address(0) ||\n            assessmentApprover == address(0) ||\n            assessmentRejecter == address(0) ||\n            incidentInvalidator == address(0) ||\n            address(_coverOrderAllocator) == address(0)\n        ) {\n            revert InvalidZeroAddress();\n        }\n\n        __AccessControl_init();\n        __ReentrancyGuard_init();\n\n        IncidentManagerStorage storage $ = _getStorage();\n        $.coverOrderAllocator = _coverOrderAllocator;\n        _setPayoutReceiver(_payoutReceiver);\n        $.vault = _coverOrderAllocator.vault();\n        $.nextIncidentId = 1;\n        $.vaultAssetDecimals = IERC20Metadata($.vault.asset()).decimals();\n        $.canonicalDecimals = _coverOrderAllocator.CANONICAL_DECIMALS();\n\n        _setMaxPriceAge(_maxPriceAge);\n        _setPriceFeedAdapter(_priceFeedAdapter);\n\n        _grantRole(DEFAULT_ADMIN_ROLE, admin);\n        _grantRole(CURATOR_ROLE, curator);\n        _grantRole(ASSESSMENT_APPROVER_ROLE, assessmentApprover);\n        _grantRole(ASSESSMENT_REJECTER_ROLE, assessmentRejecter);\n        _grantRole(INCIDENT_INVALIDATOR_ROLE, incidentInvalidator);\n        if (configAdmin != address(0)) _grantRole(CONFIG_ADMIN_ROLE, configAdmin);\n        if (payoutAdmin != address(0)) _grantRole(PAYOUT_ADMIN_ROLE, payoutAdmin);\n        if (priceFeedAdmin != address(0)) _grantRole(PRICE_FEED_ADMIN_ROLE, priceFeedAdmin);\n    }\n\n    /**\n     * @inheritdoc IIncidentManager\n     * @dev `incidentRef` only prevents exact reference reuse and cannot detect different refs for the same\n     * real-world event. Curators should use a consistent external report id, and a versioned ref when replacing a\n     * canceled incident.\n     */\n    function createIncident(\n        uint48 captureTimestamp,\n        string calldata title,\n        bytes32 incidentRef\n    ) external onlyRole(CURATOR_ROLE) returns (uint256 incidentId) {\n        uint256 titleLength = bytes(title).length;\n        if (titleLength == 0) revert InvalidIncidentTitle();\n        if (titleLength > MAX_INCIDENT_TITLE_LENGTH) revert IncidentTitleTooLong();\n\n        IncidentManagerStorage storage $ = _getStorage();\n\n        if (incidentRef == bytes32(0)) revert InvalidIncidentRef();\n\n        uint256 existingRefIncidentId = $.incidentRefs[incidentRef];\n        if (existingRefIncidentId != 0) {\n            revert IncidentReferenceAlreadyExists(incidentRef, existingRefIncidentId);\n        }\n\n        uint256 period = $.vault.periodAtTimestamp(captureTimestamp);\n        if (_isPeriodExpired(period) || captureTimestamp > block.timestamp) {\n            revert InvalidCaptureTimestamp(captureTimestamp, period, $.vault.currentPeriod());\n        }\n\n        incidentId = $.nextIncidentId++;\n\n        _insertIncidentTimestamp($, period, captureTimestamp, incidentId);\n\n        $.incidents[incidentId] = Incident({\n            title: title,\n            reportURI: \"\",\n            period: period,\n            incidentRef: incidentRef,\n            currentAssessmentRoundId: 0,\n            vaultPaidAmount: 0,\n            captureTimestamp: captureTimestamp,\n            status: IncidentStatus.OPEN,\n            cancelReason: \"\"\n        });\n\n        $.incidentRefs[incidentRef] = incidentId;\n\n        _increaseIncident(period);\n\n        emit IncidentCreated(incidentId, period, incidentRef, captureTimestamp, title);\n    }\n\n    /// @inheritdoc IIncidentManager\n    function confirmIncident(uint256 incidentId, string calldata reportURI) external onlyRole(CURATOR_ROLE) {\n        Incident storage incident = _activeIncidentWithStatus(incidentId, IncidentStatus.OPEN);\n        _updateIncidentReportURI(incidentId, incident, reportURI);\n        incident.status = IncidentStatus.CONFIRMED;\n        emit IncidentConfirmed(incidentId);\n    }\n\n    /// @inheritdoc IIncidentManager\n    function cancelPreAssessmentIncident(\n        uint256 incidentId,\n        string calldata cancelReason\n    ) external onlyRole(CURATOR_ROLE) {\n        _validateCancelReason(cancelReason);\n\n        Incident storage incident = _incident(incidentId);\n\n        _validateIncidentPeriod(incidentId, incident.period);\n\n        IncidentStatus status = incident.status;\n        if (status != IncidentStatus.OPEN && status != IncidentStatus.CONFIRMED) {\n            revert InvalidIncidentStatus(incidentId, status);\n        }\n\n        incident.status = IncidentStatus.CANCELED;\n        incident.cancelReason = cancelReason;\n\n        _decreaseIncident(incident.period);\n\n        emit IncidentCanceledPreAssessment(incidentId, cancelReason);\n    }\n\n    /// @inheritdoc IIncidentManager\n    function cancelIncident(\n        uint256 incidentId,\n        string calldata cancelReason\n    ) external onlyRole(INCIDENT_INVALIDATOR_ROLE) {\n        _validateCancelReason(cancelReason);\n\n        Incident storage incident = _activeIncidentWithStatus(incidentId, IncidentStatus.UNDER_EVALUATION);\n\n        // Keep the assessment round lifecycle aligned with the canceled incident.\n        uint256 roundId = incident.currentAssessmentRoundId;\n        AssessmentRound storage assessmentRound = _getStorage().assessmentRounds[incidentId][roundId];\n        AssessmentRoundStatus roundStatus = assessmentRound.status;\n        if (roundStatus == AssessmentRoundStatus.DRAFT || roundStatus == AssessmentRoundStatus.UNDER_EVALUATION) {\n            assessmentRound.status = AssessmentRoundStatus.CANCELED;\n            emit AssessmentRoundCanceled(incidentId, roundId);\n        }\n\n        incident.status = IncidentStatus.CANCELED;\n        incident.cancelReason = cancelReason;\n\n        _decreaseIncident(incident.period);\n\n        emit IncidentInvalidated(incidentId, cancelReason);\n    }\n\n    /// @inheritdoc IIncidentManager\n    function addAssessmentLosses(\n        uint256 incidentId,\n        AssessmentLoss[] calldata assessmentLosses\n    ) external onlyRole(CURATOR_ROLE) returns (uint256 assessmentRoundId) {\n        if (assessmentLosses.length == 0) revert InvalidAssessmentLosses();\n\n        Incident storage incident = _incident(incidentId);\n\n        _validateIncidentPeriod(incidentId, incident.period);\n\n        IncidentStatus status = incident.status;\n        if (status != IncidentStatus.CONFIRMED && status != IncidentStatus.UNDER_EVALUATION) {\n            revert InvalidIncidentStatus(incidentId, status);\n        }\n\n        if (status == IncidentStatus.CONFIRMED) {\n            incident.status = IncidentStatus.UNDER_EVALUATION;\n            emit IncidentUnderEvaluation(incidentId);\n        }\n\n        IncidentManagerStorage storage $ = _getStorage();\n\n        assessmentRoundId = incident.currentAssessmentRoundId;\n        AssessmentRound storage assessmentRound = $.assessmentRounds[incidentId][assessmentRoundId];\n\n        AssessmentRoundStatus roundStatus = assessmentRound.status;\n        if (\n            assessmentRoundId == 0 ||\n            roundStatus == AssessmentRoundStatus.REJECTED ||\n            roundStatus == AssessmentRoundStatus.CANCELED\n        ) {\n            assessmentRoundId = ++incident.currentAssessmentRoundId;\n            assessmentRound = $.assessmentRounds[incidentId][assessmentRoundId];\n            assessmentRound.status = AssessmentRoundStatus.DRAFT;\n\n            emit AssessmentRoundOpened(incidentId, assessmentRoundId);\n        } else if (roundStatus != AssessmentRoundStatus.DRAFT) {\n            revert InvalidAssessmentRoundStatus(incidentId, assessmentRoundId, roundStatus);\n        }\n\n        AssessmentLoss[] storage roundAssessmentLoss = $.assessmentLosses[incidentId][assessmentRoundId];\n\n        uint256 addedAssessmentLossAmount;\n        uint256 len = assessmentLosses.length;\n\n        for (uint256 i = 0; i < len; i++) {\n            uint256 coverTokenId = assessmentLosses[i].coverTokenId;\n            bytes32 marketId = assessmentLosses[i].marketId;\n            uint256 amount = assessmentLosses[i].amount;\n\n            if ($.assessmentLossExists[incidentId][assessmentRoundId][coverTokenId][marketId]) {\n                revert AssessmentLossAlreadyExists(i);\n            }\n\n            if (amount == 0) revert AssessmentLossZeroAmount(coverTokenId, marketId);\n\n            // Validate that the order-market allocation exists and the assessment loss amount does not exceed it.\n            (uint256 orderPeriod, uint256 allocatedCoverAmount, address payoutRecipient) = $\n                .coverOrderAllocator\n                .getCoverOrderMarketInfo(coverTokenId, marketId);\n            if (payoutRecipient == address(0)) revert OrderNotFound(coverTokenId, marketId);\n            if (amount > allocatedCoverAmount) {\n                revert AssessmentLossAmountTooBig(coverTokenId, marketId, allocatedCoverAmount, amount);\n            }\n            uint256 incidentPeriod = incident.period;\n            if (orderPeriod != incidentPeriod) revert InvalidOrderPeriod(coverTokenId, orderPeriod, incidentPeriod);\n\n            addedAssessmentLossAmount += amount;\n            $.assessmentLossExists[incidentId][assessmentRoundId][coverTokenId][marketId] = true;\n            roundAssessmentLoss.push(assessmentLosses[i]);\n\n            // Accumulate the payout recipient's total assessed loss for this assessment round.\n            $.payoutRecipientAssessmentLoss[incidentId][assessmentRoundId][payoutRecipient] += amount;\n        }\n\n        assessmentRound.totalAssessmentLoss += addedAssessmentLossAmount;\n\n        emit AssessmentLossesAdded(incidentId, assessmentRoundId, len, addedAssessmentLossAmount);\n    }\n\n    /// @inheritdoc IIncidentManager\n    function submitCurrentAssessment(uint256 incidentId) external onlyRole(CURATOR_ROLE) {\n        Incident storage incident = _activeIncidentWithStatus(incidentId, IncidentStatus.UNDER_EVALUATION);\n\n        (uint256 assessmentRoundId, AssessmentRound storage assessmentRound) = _setCurrentAssessmentStatus(\n            incidentId,\n            incident,\n            AssessmentRoundStatus.DRAFT,\n            AssessmentRoundStatus.UNDER_EVALUATION\n        );\n\n        emit AssessmentRoundSubmitted(incidentId, assessmentRoundId, assessmentRound.totalAssessmentLoss);\n    }\n\n    /// @inheritdoc IIncidentManager\n    function cancelCurrentAssessment(uint256 incidentId) external onlyRole(CURATOR_ROLE) {\n        Incident storage incident = _activeIncidentWithStatus(incidentId, IncidentStatus.UNDER_EVALUATION);\n\n        uint256 assessmentRoundId = incident.currentAssessmentRoundId;\n        AssessmentRound storage assessmentRound = _getStorage().assessmentRounds[incidentId][assessmentRoundId];\n\n        AssessmentRoundStatus status = assessmentRound.status;\n        if (status != AssessmentRoundStatus.DRAFT && status != AssessmentRoundStatus.UNDER_EVALUATION) {\n            revert InvalidAssessmentRoundStatus(incidentId, assessmentRoundId, status);\n        }\n\n        assessmentRound.status = AssessmentRoundStatus.CANCELED;\n\n        emit AssessmentRoundCanceled(incidentId, assessmentRoundId);\n    }\n\n    /// @inheritdoc IIncidentManager\n    function approveAssessment(\n        uint256 incidentId,\n        uint256 assessmentRoundId\n    ) external onlyRole(ASSESSMENT_APPROVER_ROLE) nonReentrant {\n        Incident storage incident = _activeIncidentWithStatus(incidentId, IncidentStatus.UNDER_EVALUATION);\n\n        IncidentManagerStorage storage $ = _getStorage();\n\n        uint256 expectedIncidentId = _earliestPayableIncidentId($);\n        if (incidentId != expectedIncidentId) {\n            revert IncidentApprovalOutOfOrder(incidentId, expectedIncidentId);\n        }\n\n        (uint256 currentAssessmentRoundId, AssessmentRound storage assessmentRound) = _setCurrentAssessmentStatus(\n            incidentId,\n            incident,\n            AssessmentRoundStatus.UNDER_EVALUATION,\n            AssessmentRoundStatus.APPROVED\n        );\n\n        if (assessmentRoundId != currentAssessmentRoundId) {\n            revert InvalidAssessmentRoundId(incidentId, assessmentRoundId, currentAssessmentRoundId);\n        }\n\n        uint256 totalAssessmentLoss = assessmentRound.totalAssessmentLoss;\n        emit AssessmentRoundApproved(incidentId, currentAssessmentRoundId, totalAssessmentLoss);\n\n        uint256 vaultPaidAmount = _executePayout(\n            $,\n            incidentId,\n            currentAssessmentRoundId,\n            incident.period,\n            incident.captureTimestamp,\n            totalAssessmentLoss\n        );\n\n        if (vaultPaidAmount > 0) incident.vaultPaidAmount = vaultPaidAmount;\n\n        incident.status = IncidentStatus.CLOSED;\n\n        _decreaseIncident(incident.period);\n\n        emit IncidentClosed(incidentId, vaultPaidAmount, totalAssessmentLoss);\n    }\n\n    /// @inheritdoc IIncidentManager\n    function rejectAssessment(\n        uint256 incidentId,\n        uint256 assessmentRoundId\n    ) external onlyRole(ASSESSMENT_REJECTER_ROLE) {\n        Incident storage incident = _activeIncidentWithStatus(incidentId, IncidentStatus.UNDER_EVALUATION);\n\n        (uint256 currentAssessmentRoundId, ) = _setCurrentAssessmentStatus(\n            incidentId,\n            incident,\n            AssessmentRoundStatus.UNDER_EVALUATION,\n            AssessmentRoundStatus.REJECTED\n        );\n\n        if (assessmentRoundId != currentAssessmentRoundId) {\n            revert InvalidAssessmentRoundId(incidentId, assessmentRoundId, currentAssessmentRoundId);\n        }\n\n        emit AssessmentRoundRejected(incidentId, currentAssessmentRoundId);\n    }\n\n    // -- admin functions --\n\n    /// @inheritdoc IIncidentManager\n    function updateIncidentReportURI(\n        uint256 incidentId,\n        string calldata reportURI\n    ) external onlyRole(CONFIG_ADMIN_ROLE) {\n        Incident storage incident = _incident(incidentId);\n\n        _validateIncidentPeriod(incidentId, incident.period);\n\n        IncidentStatus status = incident.status;\n        if (status == IncidentStatus.CLOSED || status == IncidentStatus.CANCELED) {\n            revert ReportURIUpdateNotAllowed(incidentId, status);\n        }\n        _updateIncidentReportURI(incidentId, incident, reportURI);\n    }\n\n    /// @inheritdoc IIncidentManager\n    function setPayoutReceiver(address newPayoutReceiver) external onlyRole(PAYOUT_ADMIN_ROLE) {\n        _setPayoutReceiver(newPayoutReceiver);\n    }\n\n    /// @inheritdoc IIncidentManager\n    function setPriceFeedAdapter(IAggregatorV3 newPriceFeedAdapter) external onlyRole(PRICE_FEED_ADMIN_ROLE) {\n        _setPriceFeedAdapter(newPriceFeedAdapter);\n    }\n\n    /// @inheritdoc IIncidentManager\n    function setMaxPriceAge(uint48 newMaxPriceAge) external onlyRole(PRICE_FEED_ADMIN_ROLE) {\n        _setMaxPriceAge(newMaxPriceAge);\n    }\n\n    // -- view functions --\n\n    /// @inheritdoc IIncidentManager\n    function coverOrderAllocator() external view returns (ICoverOrderAllocator) {\n        return _getStorage().coverOrderAllocator;\n    }\n\n    /// @inheritdoc IIncidentManager\n    function vault() external view returns (IFirelightVault) {\n        return _getStorage().vault;\n    }\n\n    /// @inheritdoc IIncidentManager\n    function getAssessmentLosses(\n        uint256 incidentId,\n        uint256 assessmentRoundId\n    ) external view returns (AssessmentLoss[] memory) {\n        return _getStorage().assessmentLosses[incidentId][assessmentRoundId];\n    }\n\n    /// @inheritdoc IIncidentManager\n    function getAssessmentLoss(\n        uint256 incidentId,\n        uint256 assessmentRoundId,\n        uint256 index\n    ) external view returns (AssessmentLoss memory) {\n        return _getStorage().assessmentLosses[incidentId][assessmentRoundId][index];\n    }\n\n    /// @inheritdoc IIncidentManager\n    function getAssessmentLossesLength(uint256 incidentId, uint256 assessmentRoundId) external view returns (uint256) {\n        return _getStorage().assessmentLosses[incidentId][assessmentRoundId].length;\n    }\n\n    /// @inheritdoc IIncidentManager\n    function getIncident(uint256 incidentId) external view returns (Incident memory incident, bool exists) {\n        incident = _getStorage().incidents[incidentId];\n        exists = incident.status != IncidentStatus.NONE;\n        if (\n            exists &&\n            incident.status != IncidentStatus.CLOSED &&\n            incident.status != IncidentStatus.CANCELED &&\n            _isPeriodExpired(incident.period)\n        ) {\n            incident.status = IncidentStatus.EXPIRED;\n        }\n    }\n\n    /// @inheritdoc IIncidentManager\n    function incidentIdByRef(bytes32 incidentRef) external view returns (uint256) {\n        return _getStorage().incidentRefs[incidentRef];\n    }\n\n    /// @inheritdoc IIncidentManager\n    function getAssessmentRound(\n        uint256 incidentId,\n        uint256 assessmentRoundId\n    ) public view returns (AssessmentRound memory assessmentRound, bool exists) {\n        assessmentRound = _getStorage().assessmentRounds[incidentId][assessmentRoundId];\n        exists = assessmentRound.status != AssessmentRoundStatus.NONE;\n    }\n\n    /// @inheritdoc IIncidentManager\n    function getCurrentAssessmentRound(\n        uint256 incidentId\n    ) external view returns (AssessmentRound memory assessmentRound, bool exists) {\n        Incident storage incident = _getStorage().incidents[incidentId];\n        if (incident.status == IncidentStatus.NONE) return (assessmentRound, false);\n        (assessmentRound, exists) = getAssessmentRound(incidentId, incident.currentAssessmentRoundId);\n    }\n\n    /// @inheritdoc IIncidentManager\n    function getPayoutRecipientAssessmentLoss(\n        uint256 incidentId,\n        uint256 assessmentRoundId,\n        address payoutRecipient\n    ) public view returns (uint256 amount, bool roundExists) {\n        IncidentManagerStorage storage $ = _getStorage();\n\n        AssessmentRound storage assessmentRound = $.assessmentRounds[incidentId][assessmentRoundId];\n        roundExists = assessmentRound.status != AssessmentRoundStatus.NONE;\n\n        if (!roundExists) return (0, false);\n\n        amount = $.payoutRecipientAssessmentLoss[incidentId][assessmentRoundId][payoutRecipient];\n    }\n\n    /// @inheritdoc IIncidentManager\n    function getCurrentPayoutRecipientAssessmentLoss(\n        uint256 incidentId,\n        address payoutRecipient\n    ) external view returns (uint256 amount, bool roundExists) {\n        Incident storage incident = _getStorage().incidents[incidentId];\n        if (incident.status == IncidentStatus.NONE) return (0, false);\n        uint256 assessmentRoundId = incident.currentAssessmentRoundId;\n        (amount, roundExists) = getPayoutRecipientAssessmentLoss(incidentId, assessmentRoundId, payoutRecipient);\n    }\n\n    /// @inheritdoc IIncidentManager\n    function payoutReceiver() external view returns (address) {\n        return _getStorage().payoutReceiver;\n    }\n\n    /// @inheritdoc IIncidentManager\n    function nextIncidentId() external view returns (uint256) {\n        return _getStorage().nextIncidentId;\n    }\n\n    /// @inheritdoc IIncidentManager\n    function maxPriceAge() external view returns (uint48) {\n        return _getStorage().maxPriceAge;\n    }\n\n    /// @inheritdoc IIncidentManager\n    function priceFeedAdapter() external view returns (IAggregatorV3) {\n        return _getStorage().priceFeedAdapter;\n    }\n\n    /**\n     * @inheritdoc IIncidentManager\n     * @dev The raw counter may remain nonzero for old periods, but it does not affect payout ordering or vault\n     * deposit blocking once the payout window has passed.\n     */\n    function activeIncidentCount(uint256 period) external view returns (uint256) {\n        if (_isPeriodExpired(period)) return 0;\n        return _getStorage().activeIncidentCountByPeriod[period];\n    }\n\n    // -- internal functions --\n\n    // Trust assumption: the per-period firstLossBuffer account in CoverOrderAllocator has approved\n    // IncidentManager to pull its buffer balance. A funded buffer without that allowance makes the\n    // first-loss transfer (and therefore the whole payout) revert.\n    function _executePayout(\n        IncidentManagerStorage storage $,\n        uint256 incidentId,\n        uint256 assessmentRoundId,\n        uint256 period,\n        uint48 captureTimestamp,\n        uint256 totalAssessmentLoss\n    ) internal returns (uint256 vaultPaidAmount) {\n        uint8 canonicalDecimals = $.canonicalDecimals;\n        address receiver = $.payoutReceiver;\n\n        // Token + custody travel together inside the per-period CapacityConfig, so a\n        // mid-life buffer-token rotation never desynchronizes from the wallet that\n        // holds (and pre-approved) the funds for the incident's period.\n        // Assumes the firstLossBufferToken is a stablecoin pegged to the same asset as the orders.\n        ICoverOrderAllocator.CapacityConfig memory periodConfig = $.coverOrderAllocator.getCapacityConfigAt(period);\n        address flbPayer = periodConfig.firstLossBuffer;\n        IERC20 flbToken = periodConfig.firstLossBufferToken;\n\n        // Intentionally let safeTransferFrom revert if a funded FLB payer has not approved IncidentManager.\n        uint256 flbPayerBalance = flbToken.balanceOf(flbPayer);\n        uint256 flbAmount;\n        uint256 remainderAmount = totalAssessmentLoss;\n\n        // Take from buffer account first\n        if (flbPayerBalance > 0) {\n            uint8 flbTokenDecimals = IERC20Metadata(address(flbToken)).decimals();\n            uint256 totalAssessmentLossFlb = Decimals.convert(\n                totalAssessmentLoss,\n                canonicalDecimals,\n                flbTokenDecimals,\n                Math.Rounding.Floor\n            );\n\n            flbAmount = Math.min(totalAssessmentLossFlb, flbPayerBalance);\n\n            if (flbAmount > 0) {\n                flbToken.safeTransferFrom(flbPayer, receiver, flbAmount);\n            }\n\n            // Convert the transferred flbAmount back to canonical decimals\n            uint256 flbAmountCanonical = Decimals.convert(\n                flbAmount,\n                flbTokenDecimals,\n                canonicalDecimals,\n                Math.Rounding.Floor\n            );\n\n            remainderAmount = flbAmountCanonical >= totalAssessmentLoss ? 0 : totalAssessmentLoss - flbAmountCanonical;\n        }\n\n        uint256 vaultRequestedAmount;\n        if (remainderAmount > 0) {\n            uint256 assetPriceUSD = $.priceFeedAdapter.getPrice($.maxPriceAge);\n\n            // Convert remaining USD loss into vault asset units.\n            vaultRequestedAmount = remainderAmount.mulDiv(10 ** $.priceFeedDecimals, assetPriceUSD).mulDiv(\n                10 ** $.vaultAssetDecimals,\n                10 ** canonicalDecimals\n            );\n\n            // In case of rounding dust\n            if (vaultRequestedAmount > 0) {\n                vaultPaidAmount = $.vault.payout(receiver, vaultRequestedAmount, captureTimestamp);\n            } else if (flbAmount == 0) {\n                // Revert only if the approved loss rounds to zero for both payout sources.\n                // A zero vault payout is still allowed when vault slashable capacity is exhausted.\n                revert PayoutRoundsToZero(remainderAmount, totalAssessmentLoss);\n            }\n        }\n\n        uint256 executionPeriod = $.vault.currentPeriod();\n        emit IncidentPayoutExecuted(\n            incidentId,\n            assessmentRoundId,\n            period,\n            executionPeriod,\n            receiver,\n            flbPayer,\n            address(flbToken),\n            flbAmount,\n            totalAssessmentLoss,\n            vaultRequestedAmount,\n            vaultPaidAmount\n        );\n    }\n\n    function _incident(uint256 incidentId) internal view returns (Incident storage incident) {\n        incident = _getStorage().incidents[incidentId];\n        if (incident.status == IncidentStatus.NONE) revert IncidentNotFound(incidentId);\n    }\n\n    // Loads an incident and guarantees it exists, its payout window has not expired, and it matches the\n    // expected status; reverts otherwise.\n    function _activeIncidentWithStatus(\n        uint256 incidentId,\n        IncidentStatus expectedStatus\n    ) internal view returns (Incident storage incident) {\n        incident = _incident(incidentId);\n        _validateIncidentPeriod(incidentId, incident.period);\n        IncidentStatus currentStatus = incident.status;\n        if (currentStatus != expectedStatus) revert InvalidIncidentStatus(incidentId, currentStatus);\n    }\n\n    function _setCurrentAssessmentStatus(\n        uint256 incidentId,\n        Incident storage incident,\n        AssessmentRoundStatus expectedStatus,\n        AssessmentRoundStatus newStatus\n    ) internal returns (uint256 assessmentRoundId, AssessmentRound storage assessmentRound) {\n        assessmentRoundId = incident.currentAssessmentRoundId;\n        assessmentRound = _getStorage().assessmentRounds[incidentId][assessmentRoundId];\n\n        AssessmentRoundStatus currentStatus = assessmentRound.status;\n        if (currentStatus != expectedStatus) {\n            revert InvalidAssessmentRoundStatus(incidentId, assessmentRoundId, currentStatus);\n        }\n\n        assessmentRound.status = newStatus;\n    }\n\n    function _setPayoutReceiver(address newPayoutReceiver) internal {\n        if (newPayoutReceiver == address(0)) revert InvalidZeroAddress();\n\n        IncidentManagerStorage storage $ = _getStorage();\n        address oldPayoutReceiver = $.payoutReceiver;\n        if (oldPayoutReceiver == newPayoutReceiver) return;\n        $.payoutReceiver = newPayoutReceiver;\n\n        emit PayoutReceiverUpdated(oldPayoutReceiver, newPayoutReceiver);\n    }\n\n    function _setMaxPriceAge(uint48 newMaxPriceAge) internal {\n        if (newMaxPriceAge == 0) revert InvalidMaxPriceAge();\n\n        IncidentManagerStorage storage $ = _getStorage();\n        uint48 oldMaxPriceAge = $.maxPriceAge;\n        if (oldMaxPriceAge == newMaxPriceAge) return;\n        $.maxPriceAge = newMaxPriceAge;\n\n        emit MaxPriceAgeUpdated(oldMaxPriceAge, newMaxPriceAge);\n    }\n\n    function _setPriceFeedAdapter(IAggregatorV3 newPriceFeedAdapter) internal {\n        if (address(newPriceFeedAdapter) == address(0)) revert InvalidZeroAddress();\n\n        IncidentManagerStorage storage $ = _getStorage();\n\n        IAggregatorV3 oldPriceFeedAdapter = $.priceFeedAdapter;\n        uint8 oldPriceFeedDecimals = $.priceFeedDecimals;\n        if (oldPriceFeedAdapter == newPriceFeedAdapter) return;\n        uint8 newPriceFeedDecimals = newPriceFeedAdapter.decimals();\n\n        // Bound feed decimals to a sane range so the payout conversion math stays well-scaled and cannot\n        // under/overflow on unusual feeds.\n        if (newPriceFeedDecimals < 6 || newPriceFeedDecimals > 18) {\n            revert InvalidPriceFeedDecimals(newPriceFeedDecimals);\n        }\n\n        $.priceFeedAdapter = newPriceFeedAdapter;\n        $.priceFeedDecimals = newPriceFeedDecimals;\n\n        emit PriceFeedAdapterUpdated(\n            address(oldPriceFeedAdapter),\n            oldPriceFeedDecimals,\n            address(newPriceFeedAdapter),\n            newPriceFeedDecimals\n        );\n    }\n\n    function _updateIncidentReportURI(\n        uint256 incidentId,\n        Incident storage incident,\n        string calldata reportURI\n    ) internal {\n        if (bytes(reportURI).length == 0) revert InvalidIncidentReportURI();\n\n        string memory oldReportURI = incident.reportURI;\n        if (keccak256(bytes(oldReportURI)) == keccak256(bytes(reportURI))) return;\n\n        incident.reportURI = reportURI;\n        emit IncidentReportURIUpdated(incidentId, oldReportURI, reportURI);\n    }\n\n    // Tracks active incidents per period so the vault can block deposits only while\n    // at least one incident for that period is unresolved. The vault flag is updated\n    // only on the 0 -> 1 transition to avoid redundant external calls.\n    function _increaseIncident(uint256 period) internal {\n        IncidentManagerStorage storage $ = _getStorage();\n        uint256 count = ++$.activeIncidentCountByPeriod[period];\n        if (count == 1) {\n            // First active incident for this period: tell the vault to block deposits.\n            $.vault.setActiveIncident(period, true);\n        }\n    }\n\n    // Decrements the active incident counter for the period. The vault deposit block\n    // is cleared only when the last unresolved incident for that period is closed or canceled.\n    function _decreaseIncident(uint256 period) internal {\n        IncidentManagerStorage storage $ = _getStorage();\n        uint256 count = $.activeIncidentCountByPeriod[period];\n        // Terminal incident transitions should not call this with a zero count.\n        if (count == 0) return;\n\n        --count;\n        $.activeIncidentCountByPeriod[period] = count;\n        if (count == 0) {\n            // Last active incident for this period resolved: allow deposits again.\n            $.vault.setActiveIncident(period, false);\n        }\n    }\n\n    function _validateIncidentPeriod(uint256 incidentId, uint256 incidentPeriod) internal view {\n        if (_isPeriodExpired(incidentPeriod)) {\n            revert IncidentPayoutWindowExpired(incidentId, incidentPeriod);\n        }\n    }\n\n    function _isPeriodExpired(uint256 incidentPeriod) internal view returns (bool) {\n        return !_getStorage().vault.isPeriodInPayoutWindow(incidentPeriod);\n    }\n\n    // Insert incident in captureTimestamp order and reject duplicate timestamps for the period.\n    function _insertIncidentTimestamp(\n        IncidentManagerStorage storage $,\n        uint256 period,\n        uint48 captureTimestamp,\n        uint256 incidentId\n    ) internal {\n        uint256 existingIncidentId = $.incidentIdByTimestamp[period][captureTimestamp];\n        if (existingIncidentId != 0) {\n            revert IncidentTimestampAlreadyExists(period, captureTimestamp, existingIncidentId);\n        }\n\n        $.incidentIdByTimestamp[period][captureTimestamp] = incidentId;\n\n        uint48[] storage timestamps = $.incidentTimestamps[period];\n        timestamps.push(captureTimestamp);\n\n        uint256 i = timestamps.length - 1;\n        while (i > 0 && timestamps[i - 1] > captureTimestamp) {\n            timestamps[i] = timestamps[i - 1];\n            --i;\n        }\n\n        timestamps[i] = captureTimestamp;\n    }\n\n    // Returns the incident that must be approved next, or 0 when none is payable. Unresolved incidents from the\n    // previous period take priority over the current period; within a period the earliest capture timestamp wins.\n    function _earliestPayableIncidentId(IncidentManagerStorage storage $) internal view returns (uint256) {\n        uint256 currentPeriod = $.vault.currentPeriod();\n        uint256 previousId = currentPeriod > 0 ? _earliestActiveIncidentIdAt($, currentPeriod - 1) : 0;\n\n        if (previousId > 0) return previousId;\n\n        uint256 currentId = _earliestActiveIncidentIdAt($, currentPeriod);\n\n        return currentId;\n    }\n\n    // Timestamps are maintained in sorted order by _insertIncidentTimestamp, so the first incident that is not\n    // closed or canceled is the earliest payable one by capture time. Returns 0 when none is active.\n    function _earliestActiveIncidentIdAt(\n        IncidentManagerStorage storage $,\n        uint256 period\n    ) internal view returns (uint256) {\n        uint48[] storage timestamps = $.incidentTimestamps[period];\n        uint256 len = timestamps.length;\n        for (uint256 i; i < len; ++i) {\n            uint256 incidentId = $.incidentIdByTimestamp[period][timestamps[i]];\n            IncidentStatus status = $.incidents[incidentId].status;\n\n            if (status != IncidentStatus.CLOSED && status != IncidentStatus.CANCELED) {\n                return incidentId;\n            }\n        }\n\n        return 0;\n    }\n\n    function _validateCancelReason(string calldata cancelReason) internal pure {\n        uint256 len = bytes(cancelReason).length;\n        if (len > MAX_CANCEL_REASON_LENGTH) {\n            revert CancelReasonTooLong(len, MAX_CANCEL_REASON_LENGTH);\n        }\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/utils/math/Math.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.20;\n\nimport {Panic} from \"../Panic.sol\";\nimport {SafeCast} from \"./SafeCast.sol\";\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n    enum Rounding {\n        Floor, // Toward negative infinity\n        Ceil, // Toward positive infinity\n        Trunc, // Toward zero\n        Expand // Away from zero\n    }\n\n    /**\n     * @dev Returns the addition of two unsigned integers, with an success flag (no overflow).\n     */\n    function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n        unchecked {\n            uint256 c = a + b;\n            if (c < a) return (false, 0);\n            return (true, c);\n        }\n    }\n\n    /**\n     * @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow).\n     */\n    function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n        unchecked {\n            if (b > a) return (false, 0);\n            return (true, a - b);\n        }\n    }\n\n    /**\n     * @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).\n     */\n    function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n        unchecked {\n            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n            // benefit is lost if 'b' is also tested.\n            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n            if (a == 0) return (true, 0);\n            uint256 c = a * b;\n            if (c / a != b) return (false, 0);\n            return (true, c);\n        }\n    }\n\n    /**\n     * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).\n     */\n    function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n        unchecked {\n            if (b == 0) return (false, 0);\n            return (true, a / b);\n        }\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).\n     */\n    function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {\n        unchecked {\n            if (b == 0) return (false, 0);\n            return (true, a % b);\n        }\n    }\n\n    /**\n     * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.\n     *\n     * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.\n     * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute\n     * one branch when needed, making this function more expensive.\n     */\n    function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {\n        unchecked {\n            // branchless ternary works because:\n            // b ^ (a ^ b) == a\n            // b ^ 0 == b\n            return b ^ ((a ^ b) * SafeCast.toUint(condition));\n        }\n    }\n\n    /**\n     * @dev Returns the largest of two numbers.\n     */\n    function max(uint256 a, uint256 b) internal pure returns (uint256) {\n        return ternary(a > b, a, b);\n    }\n\n    /**\n     * @dev Returns the smallest of two numbers.\n     */\n    function min(uint256 a, uint256 b) internal pure returns (uint256) {\n        return ternary(a < b, a, b);\n    }\n\n    /**\n     * @dev Returns the average of two numbers. The result is rounded towards\n     * zero.\n     */\n    function average(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b) / 2 can overflow.\n        return (a & b) + (a ^ b) / 2;\n    }\n\n    /**\n     * @dev Returns the ceiling of the division of two numbers.\n     *\n     * This differs from standard division with `/` in that it rounds towards infinity instead\n     * of rounding towards zero.\n     */\n    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n        if (b == 0) {\n            // Guarantee the same behavior as in a regular Solidity division.\n            Panic.panic(Panic.DIVISION_BY_ZERO);\n        }\n\n        // The following calculation ensures accurate ceiling division without overflow.\n        // Since a is non-zero, (a - 1) / b will not overflow.\n        // The largest possible result occurs when (a - 1) / b is type(uint256).max,\n        // but the largest value we can obtain is type(uint256).max - 1, which happens\n        // when a = type(uint256).max and b = 1.\n        unchecked {\n            return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);\n        }\n    }\n\n    /**\n     * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or\n     * denominator == 0.\n     *\n     * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by\n     * Uniswap Labs also under MIT license.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n        unchecked {\n            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use\n            // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n            // variables such that product = prod1 * 2²⁵⁶ + prod0.\n            uint256 prod0 = x * y; // Least significant 256 bits of the product\n            uint256 prod1; // Most significant 256 bits of the product\n            assembly {\n                let mm := mulmod(x, y, not(0))\n                prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n            }\n\n            // Handle non-overflow cases, 256 by 256 division.\n            if (prod1 == 0) {\n                // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n                // The surrounding unchecked block does not change this fact.\n                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n                return prod0 / denominator;\n            }\n\n            // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.\n            if (denominator <= prod1) {\n                Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));\n            }\n\n            ///////////////////////////////////////////////\n            // 512 by 256 division.\n            ///////////////////////////////////////////////\n\n            // Make division exact by subtracting the remainder from [prod1 prod0].\n            uint256 remainder;\n            assembly {\n                // Compute remainder using mulmod.\n                remainder := mulmod(x, y, denominator)\n\n                // Subtract 256 bit number from 512 bit number.\n                prod1 := sub(prod1, gt(remainder, prod0))\n                prod0 := sub(prod0, remainder)\n            }\n\n            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.\n            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.\n\n            uint256 twos = denominator & (0 - denominator);\n            assembly {\n                // Divide denominator by twos.\n                denominator := div(denominator, twos)\n\n                // Divide [prod1 prod0] by twos.\n                prod0 := div(prod0, twos)\n\n                // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.\n                twos := add(div(sub(0, twos), twos), 1)\n            }\n\n            // Shift in bits from prod1 into prod0.\n            prod0 |= prod1 * twos;\n\n            // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such\n            // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for\n            // four bits. That is, denominator * inv ≡ 1 mod 2⁴.\n            uint256 inverse = (3 * denominator) ^ 2;\n\n            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also\n            // works in modular arithmetic, doubling the correct bits in each step.\n            inverse *= 2 - denominator * inverse; // inverse mod 2⁸\n            inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶\n            inverse *= 2 - denominator * inverse; // inverse mod 2³²\n            inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴\n            inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸\n            inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶\n\n            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n            // This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is\n            // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and prod1\n            // is no longer required.\n            result = prod0 * inverse;\n            return result;\n        }\n    }\n\n    /**\n     * @dev Calculates x * y / denominator with full precision, following the selected rounding direction.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n        return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);\n    }\n\n    /**\n     * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.\n     *\n     * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.\n     * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.\n     *\n     * If the input value is not inversible, 0 is returned.\n     *\n     * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the\n     * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.\n     */\n    function invMod(uint256 a, uint256 n) internal pure returns (uint256) {\n        unchecked {\n            if (n == 0) return 0;\n\n            // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)\n            // Used to compute integers x and y such that: ax + ny = gcd(a, n).\n            // When the gcd is 1, then the inverse of a modulo n exists and it's x.\n            // ax + ny = 1\n            // ax = 1 + (-y)n\n            // ax ≡ 1 (mod n) # x is the inverse of a modulo n\n\n            // If the remainder is 0 the gcd is n right away.\n            uint256 remainder = a % n;\n            uint256 gcd = n;\n\n            // Therefore the initial coefficients are:\n            // ax + ny = gcd(a, n) = n\n            // 0a + 1n = n\n            int256 x = 0;\n            int256 y = 1;\n\n            while (remainder != 0) {\n                uint256 quotient = gcd / remainder;\n\n                (gcd, remainder) = (\n                    // The old remainder is the next gcd to try.\n                    remainder,\n                    // Compute the next remainder.\n                    // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd\n                    // where gcd is at most n (capped to type(uint256).max)\n                    gcd - remainder * quotient\n                );\n\n                (x, y) = (\n                    // Increment the coefficient of a.\n                    y,\n                    // Decrement the coefficient of n.\n                    // Can overflow, but the result is casted to uint256 so that the\n                    // next value of y is \"wrapped around\" to a value between 0 and n - 1.\n                    x - y * int256(quotient)\n                );\n            }\n\n            if (gcd != 1) return 0; // No inverse exists.\n            return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.\n        }\n    }\n\n    /**\n     * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.\n     *\n     * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is\n     * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that\n     * `a**(p-2)` is the modular multiplicative inverse of a in Fp.\n     *\n     * NOTE: this function does NOT check that `p` is a prime greater than `2`.\n     */\n    function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {\n        unchecked {\n            return Math.modExp(a, p - 2, p);\n        }\n    }\n\n    /**\n     * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)\n     *\n     * Requirements:\n     * - modulus can't be zero\n     * - underlying staticcall to precompile must succeed\n     *\n     * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make\n     * sure the chain you're using it on supports the precompiled contract for modular exponentiation\n     * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,\n     * the underlying function will succeed given the lack of a revert, but the result may be incorrectly\n     * interpreted as 0.\n     */\n    function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {\n        (bool success, uint256 result) = tryModExp(b, e, m);\n        if (!success) {\n            Panic.panic(Panic.DIVISION_BY_ZERO);\n        }\n        return result;\n    }\n\n    /**\n     * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).\n     * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying\n     * to operate modulo 0 or if the underlying precompile reverted.\n     *\n     * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain\n     * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in\n     * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack\n     * of a revert, but the result may be incorrectly interpreted as 0.\n     */\n    function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {\n        if (m == 0) return (false, 0);\n        assembly (\"memory-safe\") {\n            let ptr := mload(0x40)\n            // | Offset    | Content    | Content (Hex)                                                      |\n            // |-----------|------------|--------------------------------------------------------------------|\n            // | 0x00:0x1f | size of b  | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n            // | 0x20:0x3f | size of e  | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n            // | 0x40:0x5f | size of m  | 0x0000000000000000000000000000000000000000000000000000000000000020 |\n            // | 0x60:0x7f | value of b | 0x<.............................................................b> |\n            // | 0x80:0x9f | value of e | 0x<.............................................................e> |\n            // | 0xa0:0xbf | value of m | 0x<.............................................................m> |\n            mstore(ptr, 0x20)\n            mstore(add(ptr, 0x20), 0x20)\n            mstore(add(ptr, 0x40), 0x20)\n            mstore(add(ptr, 0x60), b)\n            mstore(add(ptr, 0x80), e)\n            mstore(add(ptr, 0xa0), m)\n\n            // Given the result < m, it's guaranteed to fit in 32 bytes,\n            // so we can use the memory scratch space located at offset 0.\n            success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)\n            result := mload(0x00)\n        }\n    }\n\n    /**\n     * @dev Variant of {modExp} that supports inputs of arbitrary length.\n     */\n    function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {\n        (bool success, bytes memory result) = tryModExp(b, e, m);\n        if (!success) {\n            Panic.panic(Panic.DIVISION_BY_ZERO);\n        }\n        return result;\n    }\n\n    /**\n     * @dev Variant of {tryModExp} that supports inputs of arbitrary length.\n     */\n    function tryModExp(\n        bytes memory b,\n        bytes memory e,\n        bytes memory m\n    ) internal view returns (bool success, bytes memory result) {\n        if (_zeroBytes(m)) return (false, new bytes(0));\n\n        uint256 mLen = m.length;\n\n        // Encode call args in result and move the free memory pointer\n        result = abi.encodePacked(b.length, e.length, mLen, b, e, m);\n\n        assembly (\"memory-safe\") {\n            let dataPtr := add(result, 0x20)\n            // Write result on top of args to avoid allocating extra memory.\n            success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)\n            // Overwrite the length.\n            // result.length > returndatasize() is guaranteed because returndatasize() == m.length\n            mstore(result, mLen)\n            // Set the memory pointer after the returned data.\n            mstore(0x40, add(dataPtr, mLen))\n        }\n    }\n\n    /**\n     * @dev Returns whether the provided byte array is zero.\n     */\n    function _zeroBytes(bytes memory byteArray) private pure returns (bool) {\n        for (uint256 i = 0; i < byteArray.length; ++i) {\n            if (byteArray[i] != 0) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded\n     * towards zero.\n     *\n     * This method is based on Newton's method for computing square roots; the algorithm is restricted to only\n     * using integer operations.\n     */\n    function sqrt(uint256 a) internal pure returns (uint256) {\n        unchecked {\n            // Take care of easy edge cases when a == 0 or a == 1\n            if (a <= 1) {\n                return a;\n            }\n\n            // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a\n            // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between\n            // the current value as `ε_n = | x_n - sqrt(a) |`.\n            //\n            // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root\n            // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is\n            // bigger than any uint256.\n            //\n            // By noticing that\n            // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`\n            // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar\n            // to the msb function.\n            uint256 aa = a;\n            uint256 xn = 1;\n\n            if (aa >= (1 << 128)) {\n                aa >>= 128;\n                xn <<= 64;\n            }\n            if (aa >= (1 << 64)) {\n                aa >>= 64;\n                xn <<= 32;\n            }\n            if (aa >= (1 << 32)) {\n                aa >>= 32;\n                xn <<= 16;\n            }\n            if (aa >= (1 << 16)) {\n                aa >>= 16;\n                xn <<= 8;\n            }\n            if (aa >= (1 << 8)) {\n                aa >>= 8;\n                xn <<= 4;\n            }\n            if (aa >= (1 << 4)) {\n                aa >>= 4;\n                xn <<= 2;\n            }\n            if (aa >= (1 << 2)) {\n                xn <<= 1;\n            }\n\n            // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).\n            //\n            // We can refine our estimation by noticing that the middle of that interval minimizes the error.\n            // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).\n            // This is going to be our x_0 (and ε_0)\n            xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)\n\n            // From here, Newton's method give us:\n            // x_{n+1} = (x_n + a / x_n) / 2\n            //\n            // One should note that:\n            // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a\n            //              = ((x_n² + a) / (2 * x_n))² - a\n            //              = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a\n            //              = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)\n            //              = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)\n            //              = (x_n² - a)² / (2 * x_n)²\n            //              = ((x_n² - a) / (2 * x_n))²\n            //              ≥ 0\n            // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n\n            //\n            // This gives us the proof of quadratic convergence of the sequence:\n            // ε_{n+1} = | x_{n+1} - sqrt(a) |\n            //         = | (x_n + a / x_n) / 2 - sqrt(a) |\n            //         = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |\n            //         = | (x_n - sqrt(a))² / (2 * x_n) |\n            //         = | ε_n² / (2 * x_n) |\n            //         = ε_n² / | (2 * x_n) |\n            //\n            // For the first iteration, we have a special case where x_0 is known:\n            // ε_1 = ε_0² / | (2 * x_0) |\n            //     ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))\n            //     ≤ 2**(2*e-4) / (3 * 2**(e-1))\n            //     ≤ 2**(e-3) / 3\n            //     ≤ 2**(e-3-log2(3))\n            //     ≤ 2**(e-4.5)\n            //\n            // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:\n            // ε_{n+1} = ε_n² / | (2 * x_n) |\n            //         ≤ (2**(e-k))² / (2 * 2**(e-1))\n            //         ≤ 2**(2*e-2*k) / 2**e\n            //         ≤ 2**(e-2*k)\n            xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5)  -- special case, see above\n            xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9)    -- general case with k = 4.5\n            xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18)   -- general case with k = 9\n            xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36)   -- general case with k = 18\n            xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72)   -- general case with k = 36\n            xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144)  -- general case with k = 72\n\n            // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision\n            // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either\n            // sqrt(a) or sqrt(a) + 1.\n            return xn - SafeCast.toUint(xn > a / xn);\n        }\n    }\n\n    /**\n     * @dev Calculates sqrt(a), following the selected rounding direction.\n     */\n    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = sqrt(a);\n            return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 2 of a positive value rounded towards zero.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        uint256 exp;\n        unchecked {\n            exp = 128 * SafeCast.toUint(value > (1 << 128) - 1);\n            value >>= exp;\n            result += exp;\n\n            exp = 64 * SafeCast.toUint(value > (1 << 64) - 1);\n            value >>= exp;\n            result += exp;\n\n            exp = 32 * SafeCast.toUint(value > (1 << 32) - 1);\n            value >>= exp;\n            result += exp;\n\n            exp = 16 * SafeCast.toUint(value > (1 << 16) - 1);\n            value >>= exp;\n            result += exp;\n\n            exp = 8 * SafeCast.toUint(value > (1 << 8) - 1);\n            value >>= exp;\n            result += exp;\n\n            exp = 4 * SafeCast.toUint(value > (1 << 4) - 1);\n            value >>= exp;\n            result += exp;\n\n            exp = 2 * SafeCast.toUint(value > (1 << 2) - 1);\n            value >>= exp;\n            result += exp;\n\n            result += SafeCast.toUint(value > 1);\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log2(value);\n            return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 10 of a positive value rounded towards zero.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >= 10 ** 64) {\n                value /= 10 ** 64;\n                result += 64;\n            }\n            if (value >= 10 ** 32) {\n                value /= 10 ** 32;\n                result += 32;\n            }\n            if (value >= 10 ** 16) {\n                value /= 10 ** 16;\n                result += 16;\n            }\n            if (value >= 10 ** 8) {\n                value /= 10 ** 8;\n                result += 8;\n            }\n            if (value >= 10 ** 4) {\n                value /= 10 ** 4;\n                result += 4;\n            }\n            if (value >= 10 ** 2) {\n                value /= 10 ** 2;\n                result += 2;\n            }\n            if (value >= 10 ** 1) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log10(value);\n            return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 256 of a positive value rounded towards zero.\n     * Returns 0 if given 0.\n     *\n     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n     */\n    function log256(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        uint256 isGt;\n        unchecked {\n            isGt = SafeCast.toUint(value > (1 << 128) - 1);\n            value >>= isGt * 128;\n            result += isGt * 16;\n\n            isGt = SafeCast.toUint(value > (1 << 64) - 1);\n            value >>= isGt * 64;\n            result += isGt * 8;\n\n            isGt = SafeCast.toUint(value > (1 << 32) - 1);\n            value >>= isGt * 32;\n            result += isGt * 4;\n\n            isGt = SafeCast.toUint(value > (1 << 16) - 1);\n            value >>= isGt * 16;\n            result += isGt * 2;\n\n            result += SafeCast.toUint(value > (1 << 8) - 1);\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log256(value);\n            return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);\n        }\n    }\n\n    /**\n     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.\n     */\n    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {\n        return uint8(rounding) % 2 == 1;\n    }\n}\n"},{"file_path":"contracts/test/MockCoverOrderAllocatorVault.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.28;\n\nimport {IFirelightVault} from \"../core/interfaces/IFirelightVault.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @dev Minimal mock of IFirelightVault for CoverOrderAllocator tests.\n *\n *      CoverOrderAllocator only consumes `currentPeriod`, `totalAssetsAt`, `currentPeriodStart`,\n *      `currentPeriodEnd` and `periodConfigurationAtNumber`. All other interface members are stubbed with\n *      safe zero / no-op defaults so the mock satisfies the full interface but is\n *      not exercised beyond the CoverOrderAllocator's needs.\n */\ncontract MockCoverOrderAllocatorVault is IFirelightVault {\n    uint256 private _currentPeriod;\n    uint256 private _totalAssets;\n    address private _asset;\n    uint48 private _currentPeriodStart;\n    // Far-future default keeps the commit period-end proximity check inert unless a test opts in.\n    uint48 private _currentPeriodEnd = type(uint48).max;\n    bool private _hasSnapshot;\n    uint256 private _totalAssetsAtSnapshot;\n    mapping(uint256 => PeriodConfiguration) private _periodConfigs;\n    mapping(uint48 => uint256) private _periodAtTimestamp;\n\n    // -------------------------------------------------------------------------\n    // Test setters\n    // -------------------------------------------------------------------------\n\n    constructor() {\n        // Mirror FirelightVault semantics: a period is always \"in progress\", so\n        // its start is never in the future and never zero on a live chain.\n        _currentPeriodStart = uint48(block.timestamp);\n    }\n\n    function setCurrentPeriod(uint256 p) external {\n        _currentPeriod = p;\n        // Mirror FirelightVault semantics for the off-chain matcher: the current\n        // period started the moment it rolled. The off-chain engine pins its\n        // matching reads to the first block at/after this timestamp. Tests that\n        // need a different start can still override via setCurrentPeriodStart.\n        _currentPeriodStart = uint48(block.timestamp);\n    }\n    function setTotalAssets(uint256 a) external { _totalAssets = a; }\n    function setAsset(address a) external { _asset = a; }\n    function setCurrentPeriodStart(uint48 ts) external { _currentPeriodStart = ts; }\n    function setCurrentPeriodEnd(uint48 ts) external { _currentPeriodEnd = ts; }\n    function setTotalAssetsAtSnapshot(uint256 a) external {\n        _totalAssetsAtSnapshot = a;\n        _hasSnapshot = true;\n    }\n    function setPeriodConfiguration(uint256 periodNumber, PeriodConfiguration calldata cfg) external {\n        _periodConfigs[periodNumber] = cfg;\n    }\n    function setPeriodAtTimestamp(uint48 timestamp, uint256 period) external {\n        _periodAtTimestamp[timestamp] = period;\n    }\n\n    // -------------------------------------------------------------------------\n    // Functions exercised by CoverOrderAllocator\n    // -------------------------------------------------------------------------\n\n    function currentPeriod() external view override returns (uint256) { return _currentPeriod; }\n    function isPeriodInPayoutWindow(uint256 period) external view override returns (bool) {\n        return _currentPeriod == period || _currentPeriod == period + 1;\n    }\n    function totalAssets() external view override returns (uint256) { return _totalAssets; }\n\n    function periodConfigurationAtNumber(uint256 periodNumber)\n        external\n        view\n        override\n        returns (PeriodConfiguration memory)\n    {\n        return _periodConfigs[periodNumber];\n    }\n\n    // -------------------------------------------------------------------------\n    // IERC20 / IERC20Metadata stubs\n    // -------------------------------------------------------------------------\n\n    function name() external pure override returns (string memory) { return \"\"; }\n    function symbol() external pure override returns (string memory) { return \"\"; }\n    function decimals() external pure override returns (uint8) { return 18; }\n    function totalSupply() external pure override returns (uint256) { return 0; }\n    function balanceOf(address) external pure override returns (uint256) { return 0; }\n    function transfer(address, uint256) external pure override returns (bool) { return false; }\n    function allowance(address, address) external pure override returns (uint256) { return 0; }\n    function approve(address, uint256) external pure override returns (bool) { return false; }\n    function transferFrom(address, address, uint256) external pure override returns (bool) { return false; }\n\n    // -------------------------------------------------------------------------\n    // IERC4626 stubs\n    // -------------------------------------------------------------------------\n\n    function asset() external view override returns (address) { return _asset; }\n    function convertToShares(uint256) external pure override returns (uint256) { return 0; }\n    function convertToAssets(uint256) external pure override returns (uint256) { return 0; }\n    function maxDeposit(address) external pure override returns (uint256) { return 0; }\n    function previewDeposit(uint256) external pure override returns (uint256) { return 0; }\n    function deposit(uint256, address) external pure override returns (uint256) { return 0; }\n    function maxMint(address) external pure override returns (uint256) { return 0; }\n    function previewMint(uint256) external pure override returns (uint256) { return 0; }\n    function mint(uint256, address) external pure override returns (uint256) { return 0; }\n    function maxWithdraw(address) external pure override returns (uint256) { return 0; }\n    function previewWithdraw(uint256) external pure override returns (uint256) { return 0; }\n    function withdraw(uint256, address, address) external pure override returns (uint256) { return 0; }\n    function maxRedeem(address) external pure override returns (uint256) { return 0; }\n    function previewRedeem(uint256) external pure override returns (uint256) { return 0; }\n    function redeem(uint256, address, address) external pure override returns (uint256) { return 0; }\n\n    // -------------------------------------------------------------------------\n    // IAccessControl stubs\n    // -------------------------------------------------------------------------\n\n    function hasRole(bytes32, address) external pure override returns (bool) { return false; }\n    function getRoleAdmin(bytes32) external pure override returns (bytes32) { return bytes32(0); }\n    function grantRole(bytes32, address) external override {}\n    function revokeRole(bytes32, address) external override {}\n    function renounceRole(bytes32, address) external override {}\n\n    // -------------------------------------------------------------------------\n    // Role / constant getters\n    // -------------------------------------------------------------------------\n\n    function DEPOSIT_LIMIT_UPDATE_ROLE() external pure override returns (bytes32) { return bytes32(0); }\n    function RESCUER_ROLE() external pure override returns (bytes32) { return bytes32(0); }\n    function BLOCKLIST_ROLE() external pure override returns (bytes32) { return bytes32(0); }\n    function PAUSE_ROLE() external pure override returns (bytes32) { return bytes32(0); }\n    function PERIOD_CONFIGURATION_UPDATE_ROLE() external pure override returns (bytes32) { return bytes32(0); }\n    function SMALLEST_PERIOD_DURATION() external pure override returns (uint48) { return 0; }\n    function MAX_PERIOD_DURATION() external pure override returns (uint48) { return 0; }\n    function PAYOUT_ALLOWLIST_ROLE() external pure override returns (bytes32) { return bytes32(0); }\n    function PAYOUT_ROLE() external pure override returns (bytes32) { return bytes32(0); }\n    function INCIDENT_ROLE() external pure override returns (bytes32) { return bytes32(0); }\n    function CHECKPOINT_ROLE() external pure override returns (bytes32) { return bytes32(0); }\n\n    // -------------------------------------------------------------------------\n    // Storage-getter stubs\n    // -------------------------------------------------------------------------\n\n    function depositLimit() external pure override returns (uint256) { return 0; }\n    function contractVersion() external pure override returns (uint256) { return 0; }\n    function pendingWithdrawAssets() external pure override returns (uint256) { return 0; }\n\n    function periodConfigurations(uint256)\n        external\n        pure\n        override\n        returns (uint48, uint48, uint256)\n    {\n        return (0, 0, 0);\n    }\n\n    function withdrawShares(uint256) external pure override returns (uint256) { return 0; }\n    function withdrawAssets(uint256) external pure override returns (uint256) { return 0; }\n    function withdrawSharesOf(uint256, address) external pure override returns (uint256) { return 0; }\n    function isWithdrawClaimed(uint256, address) external pure override returns (bool) { return false; }\n    function isBlocklisted(address) external pure override returns (bool) { return false; }\n    function isPayoutAllowlisted(address) external pure override returns (bool) { return false; }\n\n    // -------------------------------------------------------------------------\n    // Initialization stub\n    // -------------------------------------------------------------------------\n\n    function initialize(IERC20, string memory, string memory, bytes memory) external override {}\n\n    // -------------------------------------------------------------------------\n    // Period query stubs\n    // -------------------------------------------------------------------------\n\n    function periodConfigurationAtTimestamp(uint48)\n        external\n        pure\n        override\n        returns (PeriodConfiguration memory pc)\n    {\n        return pc;\n    }\n\n    function periodAtTimestamp(uint48 timestamp) external view override returns (uint256) {\n        return _periodAtTimestamp[timestamp];\n    }\n\n    function currentPeriodConfiguration()\n        external\n        pure\n        override\n        returns (PeriodConfiguration memory pc)\n    {\n        return pc;\n    }\n\n    function currentPeriodStart() external view override returns (uint48) { return _currentPeriodStart; }\n    function currentPeriodEnd() external view override returns (uint48) { return _currentPeriodEnd; }\n    function nextPeriodEnd() external pure override returns (uint48) { return 0; }\n    function periodConfigurationsLength() external pure override returns (uint256) { return 0; }\n\n    // -------------------------------------------------------------------------\n    // Historical query stubs\n    // -------------------------------------------------------------------------\n\n    function balanceOfAt(address, uint48) external pure override returns (uint256) { return 0; }\n    function totalSupplyAt(uint48) external pure override returns (uint256) { return 0; }\n    function totalAssetsAt(uint48) external view override returns (uint256) {\n        return _hasSnapshot ? _totalAssetsAtSnapshot : _totalAssets;\n    }\n    function withdrawalsOf(uint256, address) external pure override returns (uint256) { return 0; }\n\n    // -------------------------------------------------------------------------\n    // Withdrawal lifecycle stub\n    // -------------------------------------------------------------------------\n\n    function claimWithdraw(uint256) external pure override returns (uint256) { return 0; }\n\n    // -------------------------------------------------------------------------\n    // Pausing stubs\n    // -------------------------------------------------------------------------\n\n    function paused() external pure override returns (bool) { return false; }\n    function pause() external override {}\n    function unpause() external override {}\n\n    // -------------------------------------------------------------------------\n    // Incident stubs\n    // -------------------------------------------------------------------------\n\n    function hasActiveIncident(uint256) external pure override returns (bool) { return false; }\n    function setActiveIncident(uint256, bool) external override {}\n    function checkpointTotalAssets() external override {}\n\n    // -------------------------------------------------------------------------\n    // Admin / configuration stubs\n    // -------------------------------------------------------------------------\n\n    function updateDepositLimit(uint256) external override {}\n    function addPeriodConfiguration(uint48, uint48) external override {}\n\n    // -------------------------------------------------------------------------\n    // Blocklist stubs\n    // -------------------------------------------------------------------------\n\n    function addToBlocklist(address) external override {}\n    function removeFromBlocklist(address) external override {}\n\n    // -------------------------------------------------------------------------\n    // Payout allowlist stubs\n    // -------------------------------------------------------------------------\n\n    function addToPayoutAllowlist(address) external override {}\n    function removeFromPayoutAllowlist(address) external override {}\n\n    // -------------------------------------------------------------------------\n    // Rescue stubs\n    // -------------------------------------------------------------------------\n\n    function rescueSharesFromBlocklisted(address, address) external override {}\n    function rescueWithdrawFromBlocklisted(address, address, uint256[] calldata) external override {}\n\n\n    // -------------------------------------------------------------------------\n    // Firelight V2 \n    // -------------------------------------------------------------------------\n\n    function payout(address, uint256 amount, uint48) external pure override returns (uint256 paidAmount) {\n        return amount;\n    }\n}\n"},{"file_path":"@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (access/AccessControl.sol)\n\npragma solidity ^0.8.20;\n\nimport {IAccessControl} from \"@openzeppelin/contracts/access/IAccessControl.sol\";\nimport {ContextUpgradeable} from \"../utils/ContextUpgradeable.sol\";\nimport {IERC165} from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\nimport {ERC165Upgradeable} from \"../utils/introspection/ERC165Upgradeable.sol\";\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```solidity\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```solidity\n * function foo() public {\n *     require(hasRole(MY_ROLE, msg.sender));\n *     ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}\n * to enforce additional security measures for this role.\n */\nabstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {\n    struct RoleData {\n        mapping(address account => bool) hasRole;\n        bytes32 adminRole;\n    }\n\n    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n\n    /// @custom:storage-location erc7201:openzeppelin.storage.AccessControl\n    struct AccessControlStorage {\n        mapping(bytes32 role => RoleData) _roles;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.AccessControl\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;\n\n    function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {\n        assembly {\n            $.slot := AccessControlStorageLocation\n        }\n    }\n\n    /**\n     * @dev Modifier that checks that an account has a specific role. Reverts\n     * with an {AccessControlUnauthorizedAccount} error including the required role.\n     */\n    modifier onlyRole(bytes32 role) {\n        _checkRole(role);\n        _;\n    }\n\n    function __AccessControl_init() internal onlyInitializing {\n    }\n\n    function __AccessControl_init_unchained() internal onlyInitializing {\n    }\n    /// @inheritdoc IERC165\n    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n    }\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) public view virtual returns (bool) {\n        AccessControlStorage storage $ = _getAccessControlStorage();\n        return $._roles[role].hasRole[account];\n    }\n\n    /**\n     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`\n     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.\n     */\n    function _checkRole(bytes32 role) internal view virtual {\n        _checkRole(role, _msgSender());\n    }\n\n    /**\n     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`\n     * is missing `role`.\n     */\n    function _checkRole(bytes32 role, address account) internal view virtual {\n        if (!hasRole(role, account)) {\n            revert AccessControlUnauthorizedAccount(account, role);\n        }\n    }\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role's admin, use {_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {\n        AccessControlStorage storage $ = _getAccessControlStorage();\n        return $._roles[role].adminRole;\n    }\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     *\n     * May emit a {RoleGranted} event.\n     */\n    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {\n        _grantRole(role, account);\n    }\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {\n        _revokeRole(role, account);\n    }\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function's\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been revoked `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `callerConfirmation`.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function renounceRole(bytes32 role, address callerConfirmation) public virtual {\n        if (callerConfirmation != _msgSender()) {\n            revert AccessControlBadConfirmation();\n        }\n\n        _revokeRole(role, callerConfirmation);\n    }\n\n    /**\n     * @dev Sets `adminRole` as ``role``'s admin role.\n     *\n     * Emits a {RoleAdminChanged} event.\n     */\n    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n        AccessControlStorage storage $ = _getAccessControlStorage();\n        bytes32 previousAdminRole = getRoleAdmin(role);\n        $._roles[role].adminRole = adminRole;\n        emit RoleAdminChanged(role, previousAdminRole, adminRole);\n    }\n\n    /**\n     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.\n     *\n     * Internal function without access restriction.\n     *\n     * May emit a {RoleGranted} event.\n     */\n    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {\n        AccessControlStorage storage $ = _getAccessControlStorage();\n        if (!hasRole(role, account)) {\n            $._roles[role].hasRole[account] = true;\n            emit RoleGranted(role, account, _msgSender());\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.\n     *\n     * Internal function without access restriction.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {\n        AccessControlStorage storage $ = _getAccessControlStorage();\n        if (hasRole(role, account)) {\n            $._roles[role].hasRole[account] = false;\n            emit RoleRevoked(role, account, _msgSender());\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n"},{"file_path":"@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721PausableUpgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/extensions/ERC721Pausable.sol)\n\npragma solidity ^0.8.20;\n\nimport {ERC721Upgradeable} from \"../ERC721Upgradeable.sol\";\nimport {PausableUpgradeable} from \"../../../utils/PausableUpgradeable.sol\";\nimport {Initializable} from \"../../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev ERC-721 token with pausable token transfers, minting and burning.\n *\n * Useful for scenarios such as preventing trades until the end of an evaluation\n * period, or having an emergency switch for freezing all token transfers in the\n * event of a large bug.\n *\n * IMPORTANT: This contract does not include public pause and unpause functions. In\n * addition to inheriting this contract, you must define both functions, invoking the\n * {Pausable-_pause} and {Pausable-_unpause} internal functions, with appropriate\n * access control, e.g. using {AccessControl} or {Ownable}. Not doing so will\n * make the contract pause mechanism of the contract unreachable, and thus unusable.\n */\nabstract contract ERC721PausableUpgradeable is Initializable, ERC721Upgradeable, PausableUpgradeable {\n    function __ERC721Pausable_init() internal onlyInitializing {\n    }\n\n    function __ERC721Pausable_init_unchained() internal onlyInitializing {\n    }\n    /**\n     * @dev See {ERC721-_update}.\n     *\n     * Requirements:\n     *\n     * - the contract must not be paused.\n     */\n    function _update(\n        address to,\n        uint256 tokenId,\n        address auth\n    ) internal virtual override whenNotPaused returns (address) {\n        return super._update(to, tokenId, auth);\n    }\n}\n"},{"file_path":"contracts/legacy/FirelightVaultStoragePredeposits.sol","source_code":"// SPDX-License-Identifier: BUSL-1.1 \npragma solidity 0.8.28;\n\nimport {Checkpoints} from \"../core/lib/Checkpoints.sol\";\n\n/**\n * @title FirelightVaultStorage\n * @notice Storage layout for FirelightVault.\n * @custom:security-contact securityreport@firelight.finance\n */\nabstract contract FirelightVaultStoragePredeposits {\n    /// @notice Role for updating deposit limits.\n    bytes32 public constant DEPOSIT_LIMIT_UPDATE_ROLE = keccak256(\"DEPOSIT_LIMIT_UPDATE_ROLE\");\n\n    /// @notice Role for rescuing funds from blocklisted addresses.\n    bytes32 public constant RESCUER_ROLE = keccak256(\"RESCUER_ROLE\");\n\n    /// @notice Role for managing the blocklist.\n    bytes32 public constant BLOCKLIST_ROLE = keccak256(\"BLOCKLIST_ROLE\");\n\n    /// @notice Role for pausing and unpausing contract operations.\n    bytes32 public constant PAUSE_ROLE = keccak256(\"PAUSE_ROLE\");\n\n    /// @notice Role for updating the period configurations.\n    bytes32 public constant PERIOD_CONFIGURATION_UPDATE_ROLE = keccak256(\"PERIOD_CONFIGURATION_UPDATE_ROLE\");\n\n    /// @notice Minimum period duration in seconds.\n    uint48 public constant SMALLEST_PERIOD_DURATION = 1 days;\n\n    /// @notice The maximum total amount of assets that can be deposited into the vault.\n    uint256 public depositLimit;\n\n    /// @notice The current version of the contract.\n    uint256 public contractVersion;\n\n    /// @notice The total amount of assets pending withdrawal across all periods.\n    uint256 public pendingWithdrawAssets;\n\n    struct PeriodConfiguration {\n        uint48 epoch;\n        uint48 duration;\n        uint256 startingPeriod;\n    }\n\n    // solhint-disable-next-line max-line-length\n    /// @notice Array of period configurations consisting of an starting timestamp (epoch), starting period number (startingPeriod) and period duration (duration).\n    PeriodConfiguration[] public periodConfigurations;\n\n    /// @notice Total shares allocated for withdrawals in a given period.\n    mapping(uint256 period => uint256 shares) public withdrawShares;\n\n    /// @notice Total assets allocated for withdrawals in a given period.\n    mapping(uint256 period => uint256 assets) public withdrawAssets;\n\n    /// @notice Total shares allocated for withdrawals in a given period and a given account.\n    mapping(uint256 period => mapping(address account => uint256 shares)) public withdrawSharesOf;\n\n    /// @notice Indicates whether an account has claimed their withdrawal for a given period.\n    mapping(uint256 period => mapping(address account => bool value)) public isWithdrawClaimed;\n\n    /// @notice Indicates whether an account is blocklisted.\n    mapping(address account => bool) public isBlocklisted;\n\n    /// @notice Checkpoints for assets and shares.\n    mapping(address account => Checkpoints.Trace256 shares) internal _traceBalanceOf;\n    Checkpoints.Trace256 internal _traceTotalSupply;\n    Checkpoints.Trace256 internal _traceTotalAssets;\n\n    uint256[50] private __gap;\n}\n"},{"file_path":"@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/utils/ERC721Holder.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC721Receiver} from \"../IERC721Receiver.sol\";\n\n/**\n * @dev Implementation of the {IERC721Receiver} interface.\n *\n * Accepts all token transfers.\n * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or\n * {IERC721-setApprovalForAll}.\n */\nabstract contract ERC721Holder is IERC721Receiver {\n    /**\n     * @dev See {IERC721Receiver-onERC721Received}.\n     *\n     * Always returns `IERC721Receiver.onERC721Received.selector`.\n     */\n    function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {\n        return this.onERC721Received.selector;\n    }\n}\n"},{"file_path":"contracts/test/MockFtsoV2.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.28;\n\n/**\n * @notice Test-only mock of the subset of `FtsoV2Interface` that the\n *         `PriceReaderChainlink*` family depends on.\n *\n *         Per-feed configuration:\n *           - `setPrice(id, value, timestamp)` — sets what the next read\n *             will return.\n *           - `setFee(id, fee)` — sets the fee that `calculateFeeById`\n *             reports and that `getFeedByIdInWei` requires.\n *\n *         `setMutating(true)` flips the read into a state-modifying path\n *         (bumps a counter on every read). This is used by the View-side\n *         tests to verify that `STATICCALL` fails the moment FTSO starts\n *         touching storage — i.e. the moment Flare turns fees on.\n */\ncontract MockFtsoV2 {\n    mapping(bytes21 => uint256) public feeOf;\n    mapping(bytes21 => uint256) public priceOf;\n    mapping(bytes21 => uint64) public tsOf;\n\n    bool public mutating;\n    uint256 public mutationCounter;\n\n    function setFee(bytes21 id, uint256 newFee) external {\n        feeOf[id] = newFee;\n    }\n\n    function setPrice(bytes21 id, uint256 priceWei, uint64 timestamp) external {\n        priceOf[id] = priceWei;\n        tsOf[id] = timestamp;\n    }\n\n    function setMutating(bool v) external {\n        mutating = v;\n    }\n\n    function calculateFeeById(bytes21 id) external view returns (uint256) {\n        return feeOf[id];\n    }\n\n    function getFeedByIdInWei(bytes21 id)\n        external\n        payable\n        returns (uint256 value, uint64 timestamp)\n    {\n        if (mutating) {\n            // Force a storage write so STATICCALL paths revert. Simulates\n            // FTSO turning fee bookkeeping on.\n            mutationCounter += 1;\n        }\n        require(msg.value >= feeOf[id], \"MockFtsoV2: fee\");\n        return (priceOf[id], tsOf[id]);\n    }\n}\n"},{"file_path":"contracts/test/IncidentManagerHarness.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.28;\n\nimport {IncidentManager} from \"../core/IncidentManager.sol\";\n\n/**\n * @dev Exposes IncidentManager internal functions so tests can exercise defensive\n *      branches unreachable through the external ABI (e.g. `_decreaseIncident` with\n *      a zero active incident count).\n */\ncontract IncidentManagerHarness is IncidentManager {\n    function exposedDecreaseIncident(uint256 period) external {\n        _decreaseIncident(period);\n    }\n}\n"},{"file_path":"contracts/core/interfaces/IIncidentManager.sol","source_code":"// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.28;\n\nimport {ICoverOrderAllocator} from \"./ICoverOrderAllocator.sol\";\nimport {IFirelightVault} from \"./IFirelightVault.sol\";\nimport {IAggregatorV3} from \"./IAggregatorV3.sol\";\n\ninterface IIncidentManager {\n    /// @notice Lifecycle status of an incident.\n    enum IncidentStatus {\n        /// @notice Sentinel value for a non-existent incident.\n        NONE,\n        /// @notice Incident was created but has not been confirmed.\n        OPEN,\n        /// @notice Incident was confirmed and can enter assessment.\n        CONFIRMED,\n        /// @notice Incident has an active assessment round.\n        UNDER_EVALUATION,\n        /// @notice Incident was approved, paid, and closed.\n        CLOSED,\n        /// @notice Incident was canceled before payout.\n        CANCELED,\n        /// @notice Incident payout window has passed without closure; virtual status not written to storage.\n        EXPIRED\n    }\n\n    /// @notice Lifecycle status of an incident assessment round.\n    enum AssessmentRoundStatus {\n        /// @notice Sentinel value for a non-existent assessment round.\n        NONE,\n        /// @notice Round is being assembled and can receive additional losses.\n        DRAFT,\n        /// @notice Round was submitted for approval or rejection.\n        UNDER_EVALUATION,\n        /// @notice Round was approved and used for payout execution.\n        APPROVED,\n        /// @notice Round was rejected; a new round may be created for the incident.\n        REJECTED,\n        /// @notice Round was canceled before approval.\n        CANCELED\n    }\n\n    /// @notice Incident record.\n    struct Incident {\n        /// @notice Short human-readable incident title.\n        string title;\n        /// @notice External report URI for the incident.\n        string reportURI;\n        /// @notice Vault period affected by the incident.\n        uint256 period;\n        /// @notice Curator-provided duplicate-prevention reference.\n        bytes32 incidentRef;\n        /// @notice Latest assessment round id for the incident.\n        uint256 currentAssessmentRoundId;\n        /// @notice Amount actually paid by the vault for the approved assessment, in vault asset units.\n        uint256 vaultPaidAmount;\n        /// @notice Incident capture timestamp used to determine the affected vault period and payout window.\n        uint48 captureTimestamp;\n        /// @notice Current incident status.\n        IncidentStatus status;\n        /// @notice Reason recorded when the incident is canceled.\n        string cancelReason;\n    }\n\n    /// @notice Aggregate state for one assessment round.\n    struct AssessmentRound {\n        /// @notice Total assessed loss in CoverOrderAllocator canonical decimals.\n        uint256 totalAssessmentLoss;\n        /// @notice Current assessment round status.\n        AssessmentRoundStatus status;\n    }\n\n    /// @notice Loss attributed to one settled cover allocation and market.\n    struct AssessmentLoss {\n        /// @notice Cover NFT token id identifying the settled cover order.\n        uint256 coverTokenId;\n        /// @notice Market id within the settled cover order.\n        bytes32 marketId;\n        /// @notice Assessed loss amount in CoverOrderAllocator canonical decimals.\n        uint256 amount;\n    }\n\n    /**\n     * @notice Emitted when an incident is created. OPEN is its initial status.\n     * @param incidentId New incident id.\n     * @param period Vault period affected by the incident.\n     * @param incidentRef Curator-provided duplicate-prevention reference.\n     * @param captureTimestamp Incident capture timestamp.\n     * @param title Short human-readable incident title.\n     */\n    event IncidentCreated(\n        uint256 indexed incidentId,\n        uint256 indexed period,\n        bytes32 incidentRef,\n        uint48 captureTimestamp,\n        string title\n    );\n\n    /**\n     * @notice Emitted when an incident moves from OPEN to CONFIRMED.\n     * @param incidentId Confirmed incident id.\n     */\n    event IncidentConfirmed(uint256 indexed incidentId);\n\n    /**\n     * @notice Emitted when an incident moves from CONFIRMED to UNDER_EVALUATION.\n     * @param incidentId Incident id that entered assessment.\n     */\n    event IncidentUnderEvaluation(uint256 indexed incidentId);\n\n    /**\n     * @notice Emitted when an incident moves from UNDER_EVALUATION to CLOSED after payout execution.\n     * @param incidentId Closed incident id.\n     * @param vaultPaidAmount Amount actually paid by the vault, in vault asset units.\n     * @param totalAssessmentLoss Total approved assessment loss in CoverOrderAllocator canonical decimals.\n     */\n    event IncidentClosed(uint256 indexed incidentId, uint256 vaultPaidAmount, uint256 totalAssessmentLoss);\n\n    /**\n     * @notice Emitted when an incident is canceled before assessment begins.\n     * The incident moves from OPEN or CONFIRMED to CANCELED.\n     * @param incidentId Canceled incident id.\n     * @param cancelReason Cancellation reason.\n     */\n    event IncidentCanceledPreAssessment(uint256 indexed incidentId, string cancelReason);\n\n    /**\n     * @notice Emitted when an incident is invalidated after assessment begins.\n     * The incident moves from UNDER_EVALUATION to CANCELED.\n     * @param incidentId Invalidated incident id.\n     * @param cancelReason Cancellation reason.\n     */\n    event IncidentInvalidated(uint256 indexed incidentId, string cancelReason);\n\n    /**\n     * @notice Emitted when a new assessment round is opened. DRAFT is its initial status.\n     * @param incidentId Incident id the round belongs to.\n     * @param assessmentRoundId New assessment round id.\n     */\n    event AssessmentRoundOpened(uint256 indexed incidentId, uint256 indexed assessmentRoundId);\n\n    /**\n     * @notice Emitted when assessment losses are appended to a DRAFT assessment round.\n     * @param incidentId Incident id the round belongs to.\n     * @param assessmentRoundId Assessment round id receiving the losses.\n     * @param count Number of assessment losses added.\n     * @param addedAssessmentLossAmount Sum of losses added in this call, in CoverOrderAllocator canonical decimals.\n     */\n    event AssessmentLossesAdded(\n        uint256 indexed incidentId,\n        uint256 indexed assessmentRoundId,\n        uint256 count,\n        uint256 addedAssessmentLossAmount\n    );\n\n    /**\n     * @notice Emitted when an assessment round moves from DRAFT to UNDER_EVALUATION.\n     * The round is ready for approval or rejection and can no longer receive losses.\n     * @param incidentId Incident id the round belongs to.\n     * @param assessmentRoundId Submitted assessment round id.\n     * @param totalAssessmentLoss Total submitted assessment loss in CoverOrderAllocator canonical decimals.\n     */\n    event AssessmentRoundSubmitted(\n        uint256 indexed incidentId,\n        uint256 indexed assessmentRoundId,\n        uint256 totalAssessmentLoss\n    );\n\n    /**\n     * @notice Emitted when an assessment round moves from UNDER_EVALUATION to APPROVED.\n     * Approval triggers payout execution and closes the incident.\n     * @param incidentId Incident id the round belongs to.\n     * @param assessmentRoundId Approved assessment round id.\n     * @param totalAssessmentLoss Total approved assessment loss in CoverOrderAllocator canonical decimals.\n     */\n    event AssessmentRoundApproved(\n        uint256 indexed incidentId,\n        uint256 indexed assessmentRoundId,\n        uint256 totalAssessmentLoss\n    );\n\n    /**\n     * @notice Emitted when an assessment round moves from UNDER_EVALUATION to REJECTED.\n     * The incident remains UNDER_EVALUATION and a new assessment round may be opened.\n     * @param incidentId Incident id the round belongs to.\n     * @param assessmentRoundId Rejected assessment round id.\n     */\n    event AssessmentRoundRejected(uint256 indexed incidentId, uint256 indexed assessmentRoundId);\n\n    /**\n     * @notice Emitted when an assessment round moves from DRAFT or UNDER_EVALUATION to CANCELED.\n     * The incident remains UNDER_EVALUATION unless the incident itself is also canceled.\n     * @param incidentId Incident id the round belongs to.\n     * @param assessmentRoundId Canceled assessment round id.\n     */\n    event AssessmentRoundCanceled(uint256 indexed incidentId, uint256 indexed assessmentRoundId);\n\n    /**\n     * @notice Emitted after an approved assessment payout is executed and the incident moves to CLOSED.\n     * Payouts are sent to the configured payout receiver. Any per-recipient\n     * distribution is handled off-chain.\n     * @param incidentId Incident id paid.\n     * @param assessmentRoundId Approved assessment round id.\n     * @param incidentPeriod Vault period affected by the incident.\n     * @param executionPeriod Vault period when payout execution occurred.\n     * @param payoutReceiver Address receiving first-loss-buffer and vault payouts.\n     * @param firstLossBufferPayer Address funding the first-loss-buffer payment.\n     * @param firstLossBufferToken Token used for the first-loss-buffer payment.\n     * @param firstLossTokenAmount Amount paid from the first-loss-buffer token.\n     * @param totalAssessmentLoss Total approved assessment loss in CoverOrderAllocator canonical decimals.\n     * @param vaultRequestedAmount Vault asset amount requested from the vault after first-loss-buffer payment.\n     * @param vaultPaidAmount Vault asset amount actually paid by the vault.\n     */\n    event IncidentPayoutExecuted(\n        uint256 indexed incidentId,\n        uint256 indexed assessmentRoundId,\n        uint256 indexed incidentPeriod,\n        uint256 executionPeriod,\n        address payoutReceiver,\n        address firstLossBufferPayer,\n        address firstLossBufferToken,\n        uint256 firstLossTokenAmount,\n        uint256 totalAssessmentLoss,\n        uint256 vaultRequestedAmount,\n        uint256 vaultPaidAmount\n    );\n\n    /**\n     * @notice Emitted when the price feed adapter is updated.\n     * @param oldPriceFeedAdapter Previous price feed adapter address.\n     * @param oldPriceFeedDecimals Previous price feed decimals.\n     * @param newPriceFeedAdapter New price feed adapter address.\n     * @param newPriceFeedDecimals New price feed decimals.\n     */\n    event PriceFeedAdapterUpdated(\n        address oldPriceFeedAdapter,\n        uint8 oldPriceFeedDecimals,\n        address newPriceFeedAdapter,\n        uint8 newPriceFeedDecimals\n    );\n\n    /**\n     * @notice Emitted when the maximum accepted price age is updated.\n     * @param oldMaxPriceAge Previous maximum price age, in seconds.\n     * @param newMaxPriceAge New maximum price age, in seconds.\n     */\n    event MaxPriceAgeUpdated(uint48 oldMaxPriceAge, uint48 newMaxPriceAge);\n\n    /**\n     * @notice Emitted when the receiver for incident payouts is updated.\n     * @param oldReceiver Previous payout receiver.\n     * @param newReceiver New payout receiver.\n     */\n    event PayoutReceiverUpdated(address oldReceiver, address newReceiver);\n\n    /**\n     * @notice Emitted when an incident report URI is updated.\n     * @param incidentId Incident id whose report URI was updated.\n     * @param oldReportURI Previous report URI.\n     * @param newReportURI New report URI.\n     */\n    event IncidentReportURIUpdated(uint256 indexed incidentId, string oldReportURI, string newReportURI);\n\n    /// @notice Reverts when an incident title is empty.\n    error InvalidIncidentTitle();\n\n    /// @notice Reverts when an incident title exceeds the configured maximum length.\n    error IncidentTitleTooLong();\n\n    /// @notice Reverts when an incident reference is zero.\n    error InvalidIncidentRef();\n\n    /**\n     * @notice Reverts when the vault resolves the capture timestamp to a period outside the\n     * payout window, or when the capture timestamp is later than the current block timestamp.\n     * @param captureTimestamp Incident capture timestamp.\n     * @param capturePeriod Vault period derived from the capture timestamp.\n     * @param currentPeriod Current vault period.\n     */\n    error InvalidCaptureTimestamp(uint48 captureTimestamp, uint256 capturePeriod, uint256 currentPeriod);\n\n    /**\n     * @notice Reverts when an incident reference was already used.\n     * @param incidentRef Duplicate incident reference.\n     * @param existingIncidentId Incident id already using the reference.\n     */\n    error IncidentReferenceAlreadyExists(bytes32 incidentRef, uint256 existingIncidentId);\n\n    /**\n     * @notice Reverts when another incident already uses the same capture timestamp in the same vault period.\n     * Capture timestamps must be unique because incident approvals are ordered by timestamp.\n     * @param period Vault period containing the duplicate timestamp.\n     * @param captureTimestamp Duplicate incident capture timestamp.\n     * @param existingIncidentId Incident id already using the timestamp.\n     */\n    error IncidentTimestampAlreadyExists(uint256 period, uint48 captureTimestamp, uint256 existingIncidentId);\n\n    /**\n     * @notice Reverts when an incident id does not exist.\n     * @param incidentId Missing incident id.\n     */\n    error IncidentNotFound(uint256 incidentId);\n\n    /**\n     * @notice Reverts when an incident is not in the required status for the operation.\n     * @param incidentId Incident id with the invalid status.\n     * @param currentStatus Current incident status.\n     */\n    error InvalidIncidentStatus(uint256 incidentId, IncidentStatus currentStatus);\n\n    /**\n     * @notice Reverts when an incident's payout window has expired. Blocks every state\n     * transition on the incident, including confirmation, assessment, all three\n     * cancellation paths, and report URI updates.\n     * @param incidentId Expired incident id.\n     * @param incidentPeriod Vault period affected by the incident.\n     */\n    error IncidentPayoutWindowExpired(uint256 incidentId, uint256 incidentPeriod);\n\n    /**\n     * @notice Reverts when a cancellation reason exceeds the maximum length.\n     * @param cancelReasonLength Cancellation reason length.\n     * @param maxLength Maximum allowed cancellation reason length.\n     */\n    error CancelReasonTooLong(uint256 cancelReasonLength, uint256 maxLength);\n\n    /**\n     * @notice Reverts when an assessment round is not in the required status for the operation.\n     * @param incidentId Incident id the round belongs to.\n     * @param assessmentRoundId Assessment round id with the invalid status.\n     * @param currentStatus Current assessment round status.\n     */\n    error InvalidAssessmentRoundStatus(\n        uint256 incidentId,\n        uint256 assessmentRoundId,\n        AssessmentRoundStatus currentStatus\n    );\n\n    /// @notice Reverts when an assessment loss payload is empty.\n    error InvalidAssessmentLosses();\n\n    /**\n     * @notice Reverts when a cover-market loss already exists in the current assessment round.\n     * @param index Index of the duplicate loss in the submitted assessment loss payload.\n     */\n    error AssessmentLossAlreadyExists(uint256 index);\n\n    /**\n     * @notice Reverts when an assessed loss exceeds the allocated cover for the cover-market.\n     * @param coverTokenId Cover NFT token id identifying the settled cover order.\n     * @param marketId Market id within the settled cover order.\n     * @param allocatedCoverAmount Allocated cover amount for the cover-market.\n     * @param assessmentLossAmount Submitted assessment loss amount.\n     */\n    error AssessmentLossAmountTooBig(\n        uint256 coverTokenId,\n        bytes32 marketId,\n        uint256 allocatedCoverAmount,\n        uint256 assessmentLossAmount\n    );\n\n    /**\n     * @notice Reverts when an assessment loss amount is zero.\n     * @param coverTokenId Cover NFT token id identifying the settled cover order.\n     * @param marketId Market id within the settled cover order.\n     */\n    error AssessmentLossZeroAmount(uint256 coverTokenId, bytes32 marketId);\n\n    /**\n     * @notice Reverts when the submitted loss does not match a settled cover-market allocation in CoverOrderAllocator.\n     * @param coverTokenId Cover NFT token id identifying the settled cover order.\n     * @param marketId Market id within the settled cover order.\n     */\n    error OrderNotFound(uint256 coverTokenId, bytes32 marketId);\n\n    /**\n     * @notice Reverts when a cover-market allocation period does not match the incident period.\n     * @param coverTokenId Cover NFT token id identifying the settled cover order.\n     * @param orderPeriod Vault period of the cover-market allocation.\n     * @param incidentPeriod Vault period affected by the incident.\n     */\n    error InvalidOrderPeriod(uint256 coverTokenId, uint256 orderPeriod, uint256 incidentPeriod);\n\n    /**\n     * @notice Reverts when an incident is approved before an earlier payable incident.\n     * Incident approvals follow FIFO ordering by capture timestamp.\n     * @param incidentId Incident id submitted for approval.\n     * @param expectedIncidentId Earliest incident id that must be resolved first.\n     */\n    error IncidentApprovalOutOfOrder(uint256 incidentId, uint256 expectedIncidentId);\n\n    /**\n     * @notice Reverts when an approved loss rounds to zero for both payout sources.\n     * @param remainderAmount Remaining approved loss after first-loss-buffer payment.\n     * @param totalAssessmentLoss Total approved assessment loss in CoverOrderAllocator canonical decimals.\n     */\n    error PayoutRoundsToZero(uint256 remainderAmount, uint256 totalAssessmentLoss);\n\n    /// @notice Reverts when a required address argument is zero.\n    error InvalidZeroAddress();\n\n    /// @notice Reverts when an incident report URI is empty.\n    error InvalidIncidentReportURI();\n\n    /**\n     * @notice Reverts when an incident report URI cannot be updated in the current incident status.\n     * @param incidentId Incident id whose report URI update was rejected.\n     * @param status Current incident status.\n     */\n    error ReportURIUpdateNotAllowed(uint256 incidentId, IncidentStatus status);\n\n    /// @notice Reverts when the maximum accepted price age is zero.\n    error InvalidMaxPriceAge();\n\n    /**\n     * @notice Reverts when a price feed uses unsupported decimals.\n     * @param decimals Price feed decimals.\n     */\n    error InvalidPriceFeedDecimals(uint8 decimals);\n\n    /**\n     * @notice Reverts when the specified assessment round is not the incident's current assessment round.\n     * @param incidentId Incident id associated with the assessment round.\n     * @param assessmentRoundId Assessment round id supplied by the caller.\n     * @param currentAssessmentRoundId Incident's current assessment round id.\n     */\n    error InvalidAssessmentRoundId(uint256 incidentId, uint256 assessmentRoundId, uint256 currentAssessmentRoundId);\n\n    /**\n     * @notice Creates an incident in OPEN status.\n     * The vault determines the affected period from `captureTimestamp`.\n     * `captureTimestamp` is a Unix timestamp in seconds. If multiple incidents occur at the same second within one\n     * period, curators must provide distinct timestamps because approvals follow timestamp FIFO order.\n     * @param captureTimestamp Incident capture timestamp.\n     * @param title Short human-readable incident title.\n     * @param incidentRef Curator-provided duplicate-prevention reference.\n     * @return incidentId New incident id.\n     */\n    function createIncident(\n        uint48 captureTimestamp,\n        string calldata title,\n        bytes32 incidentRef\n    ) external returns (uint256 incidentId);\n\n    /**\n     * @notice Confirms an OPEN incident and moves it to CONFIRMED.\n     * The report URI can be updated later with `updateIncidentReportURI` by `CONFIG_ADMIN_ROLE`.\n     * @param incidentId Incident id to confirm.\n     * @param reportURI External report URI for the incident.\n     */\n    function confirmIncident(uint256 incidentId, string calldata reportURI) external;\n\n    /**\n     * @notice Cancels an incident before assessment begins.\n     * Use this for OPEN or CONFIRMED incidents that should not enter assessment.\n     * The incident moves from OPEN or CONFIRMED to CANCELED.\n     * @param incidentId Incident id to cancel.\n     * @param cancelReason Cancellation reason.\n     */\n    function cancelPreAssessmentIncident(uint256 incidentId, string calldata cancelReason) external;\n\n    /**\n     * @notice Invalidates an incident after assessment begins.\n     * Use this for UNDER_EVALUATION incidents that must be stopped during assessment.\n     * The incident moves from UNDER_EVALUATION to CANCELED.\n     * If the current assessment round is DRAFT or UNDER_EVALUATION, it is also canceled.\n     * @param incidentId Incident id to invalidate.\n     * @param cancelReason Cancellation reason.\n     */\n    function cancelIncident(uint256 incidentId, string calldata cancelReason) external;\n\n    /**\n     * @notice Adds losses to the current DRAFT assessment round.\n     * Opens a new DRAFT round if the incident has no current round or the latest round was rejected or canceled.\n     * Can be called multiple times while the round remains DRAFT, including to split large loss payloads across\n     * transactions.\n     * Losses are checked against each cover-market allocation, but cumulative\n     * loss across different incidents is not capped on-chain. Curators and\n     * approvers must prevent duplicate or excessive claims for the same period,\n     * cover token, and market.\n     * @param incidentId Incident id receiving assessment losses.\n     * @param assessmentLosses Losses to append, expressed in CoverOrderAllocator canonical decimals.\n     * @return assessmentRoundId Assessment round id that received the losses.\n     */\n    function addAssessmentLosses(\n        uint256 incidentId,\n        AssessmentLoss[] calldata assessmentLosses\n    ) external returns (uint256 assessmentRoundId);\n\n    /**\n     * @notice Submits the current DRAFT assessment round for approval or rejection.\n     * The round moves to UNDER_EVALUATION and can no longer receive losses.\n     * @param incidentId Incident id whose current assessment round is submitted.\n     */\n    function submitCurrentAssessment(uint256 incidentId) external;\n\n    /**\n     * @notice Cancels the current DRAFT or UNDER_EVALUATION assessment round.\n     * Use this for upload mistakes discovered while losses are loading or the round is being evaluated.\n     * The incident remains UNDER_EVALUATION and may receive a new assessment round.\n     * @param incidentId Incident id whose current assessment round is canceled.\n     */\n    function cancelCurrentAssessment(uint256 incidentId) external;\n\n    /**\n     * @notice Approves the specified UNDER_EVALUATION assessment round and executes the payout waterfall.\n     * The assessment round moves to APPROVED and the incident moves to CLOSED.\n     * The payout uses the first-loss buffer first and requests any remaining amount from the vault.\n     * This contract must be authorized to call vault payouts, and the configured payout receiver must be allowlisted\n     * by the vault. A valid incident can close even if the vault pays less than requested, including zero, when\n     * slashable vault capacity is exhausted.\n     * The specified `assessmentRoundId` must be the incident's current assessment round when the transaction executes.\n     * Approvers are responsible for confirming the assessment is valid and does not overstate claims across\n     * related incidents.\n     * @param incidentId Incident id associated with the assessment round.\n     * @param assessmentRoundId Assessment round id reviewed and authorized for approval.\n     */\n    function approveAssessment(uint256 incidentId, uint256 assessmentRoundId) external;\n\n    /**\n     * @notice Rejects the specified UNDER_EVALUATION assessment round.\n     * The specified `assessmentRoundId` must be the incident's current assessment round when the transaction executes.\n     * The incident remains UNDER_EVALUATION and may receive a new assessment round.\n     * The next call to `addAssessmentLosses` opens a new DRAFT assessment round.\n     * @param incidentId Incident id associated with the assessment round.\n     * @param assessmentRoundId Assessment round id reviewed and authorized for rejection.\n     */\n    function rejectAssessment(uint256 incidentId, uint256 assessmentRoundId) external;\n\n    /**\n     * @notice Updates the report URI for an active incident.\n     * The report URI cannot be updated after the incident is CLOSED or CANCELED, or after its\n     * payout window has expired.\n     * @param incidentId Incident id whose report URI is updated.\n     * @param reportURI New external report URI.\n     */\n    function updateIncidentReportURI(uint256 incidentId, string calldata reportURI) external;\n\n    /**\n     * @notice Updates the receiver used for future incident payouts.\n     * @param newPayoutReceiver New payout receiver address.\n     */\n    function setPayoutReceiver(address newPayoutReceiver) external;\n\n    /**\n     * @notice Updates the maximum accepted age for oracle prices.\n     * @param newMaxPriceAge New maximum price age, in seconds.\n     */\n    function setMaxPriceAge(uint48 newMaxPriceAge) external;\n\n    /**\n     * @notice Updates the price feed adapter used to price the vault asset.\n     * @param newPriceFeedAdapter New price feed adapter.\n     */\n    function setPriceFeedAdapter(IAggregatorV3 newPriceFeedAdapter) external;\n\n    /**\n     * @notice Returns the cover order allocator used to validate assessed losses.\n     * @return Cover order allocator contract.\n     */\n    function coverOrderAllocator() external view returns (ICoverOrderAllocator);\n\n    /**\n     * @notice Returns the price feed adapter used to price the vault asset.\n     * @return Price feed adapter.\n     */\n    function priceFeedAdapter() external view returns (IAggregatorV3);\n\n    /**\n     * @notice Returns the Firelight vault used for active incident tracking and payout execution.\n     * @return Firelight vault contract.\n     */\n    function vault() external view returns (IFirelightVault);\n\n    /**\n     * @notice Returns all assessment losses recorded for an assessment round.\n     * @param incidentId Incident id the round belongs to.\n     * @param assessmentRoundId Assessment round id.\n     * @return Assessment losses recorded for the round.\n     */\n    function getAssessmentLosses(\n        uint256 incidentId,\n        uint256 assessmentRoundId\n    ) external view returns (AssessmentLoss[] memory);\n\n    /**\n     * @notice Returns one assessment loss from an assessment round.\n     * @param incidentId Incident id the round belongs to.\n     * @param assessmentRoundId Assessment round id.\n     * @param index Assessment loss index.\n     * @return Assessment loss at `index`.\n     */\n    function getAssessmentLoss(\n        uint256 incidentId,\n        uint256 assessmentRoundId,\n        uint256 index\n    ) external view returns (AssessmentLoss memory);\n\n    /**\n     * @notice Returns the number of losses recorded for an assessment round.\n     * @param incidentId Incident id the round belongs to.\n     * @param assessmentRoundId Assessment round id.\n     * @return Number of assessment losses recorded for the round.\n     */\n    function getAssessmentLossesLength(uint256 incidentId, uint256 assessmentRoundId) external view returns (uint256);\n\n    /**\n     * @notice Returns an incident and whether it exists.\n     * @dev The returned status may be `EXPIRED` when the payout window has passed without closure, even though that\n     * status is never written to storage.\n     * @param incidentId Incident id to query.\n     * @return incident Incident record.\n     * @return exists True if the incident exists.\n     */\n    function getIncident(uint256 incidentId) external view returns (Incident memory incident, bool exists);\n\n    /**\n     * @notice Returns the incident id registered for an incident reference.\n     * @param incidentRef Curator-provided duplicate-prevention reference.\n     * @return Incident id for the reference, or zero if none exists.\n     */\n    function incidentIdByRef(bytes32 incidentRef) external view returns (uint256);\n\n    /**\n     * @notice Returns an assessment round and whether it exists.\n     * @param incidentId Incident id the round belongs to.\n     * @param assessmentRoundId Assessment round id.\n     * @return assessmentRound Assessment round record.\n     * @return exists True if the assessment round exists.\n     */\n    function getAssessmentRound(\n        uint256 incidentId,\n        uint256 assessmentRoundId\n    ) external view returns (AssessmentRound memory assessmentRound, bool exists);\n\n    /**\n     * @notice Returns the current assessment round for an incident.\n     * @param incidentId Incident id to query.\n     * @return assessmentRound Current assessment round record.\n     * @return exists True if the current assessment round exists.\n     */\n    function getCurrentAssessmentRound(\n        uint256 incidentId\n    ) external view returns (AssessmentRound memory assessmentRound, bool exists);\n\n    /**\n     * @notice Returns the assessed loss attributed to a payout recipient for one assessment round.\n     * @param incidentId Incident id the round belongs to.\n     * @param assessmentRoundId Assessment round id.\n     * @param payoutRecipient Payout recipient to query.\n     * @return amount Assessed loss attributed to the payout recipient.\n     * @return roundExists True if the assessment round exists.\n     */\n    function getPayoutRecipientAssessmentLoss(\n        uint256 incidentId,\n        uint256 assessmentRoundId,\n        address payoutRecipient\n    ) external view returns (uint256 amount, bool roundExists);\n\n    /**\n     * @notice Returns the assessed loss attributed to a payout recipient for the current assessment round.\n     * @param incidentId Incident id to query.\n     * @param payoutRecipient Payout recipient to query.\n     * @return amount Assessed loss attributed to the payout recipient.\n     * @return roundExists True if the current assessment round exists.\n     */\n    function getCurrentPayoutRecipientAssessmentLoss(\n        uint256 incidentId,\n        address payoutRecipient\n    ) external view returns (uint256 amount, bool roundExists);\n\n    /**\n     * @notice Returns the receiver used for incident payouts.\n     * @return Payout receiver address.\n     */\n    function payoutReceiver() external view returns (address);\n\n    /**\n     * @notice Returns the maximum accepted age for oracle prices.\n     * @return Maximum price age, in seconds.\n     */\n    function maxPriceAge() external view returns (uint48);\n\n    /**\n     * @notice Returns the next incident id to be assigned.\n     * @return Next incident id.\n     */\n    function nextIncidentId() external view returns (uint256);\n\n    /**\n     * @notice Returns the active incident count for a vault period.\n     * Expired periods are reported as zero without mutating storage.\n     * @param period Vault period to query.\n     * @return Active incident count for the period.\n     */\n    function activeIncidentCount(uint256 period) external view returns (uint256);\n}\n"},{"file_path":"contracts/core/interfaces/IFirelightVault.sol","source_code":"// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.28;\n\nimport {IERC4626} from \"@openzeppelin/contracts/interfaces/IERC4626.sol\";\nimport {IAccessControl} from \"@openzeppelin/contracts/access/IAccessControl.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title IFirelightVault\n * @notice Full external interface of {FirelightVault}, an ERC4626-compatible vault with\n *         delayed withdrawals, role-based access control, blocklisting, pausing,\n *         and historical balance/supply/assets checkpoints.\n *\n * @dev FirelightVault intentionally deviates from ERC4626: `withdraw` and `redeem` create a\n *      pending withdrawal request rather than transferring assets immediately. The shares\n *      are burned on request, and assets become claimable in a later period via\n *      `claimWithdraw`. The standard ERC4626 `Withdraw` event is NOT emitted; instead the\n *      lifecycle is tracked by `WithdrawRequest` and `CompleteWithdraw` events.\n *\n * @custom:security-contact securityreport@firelight.finance\n */\ninterface IFirelightVault is IERC4626, IAccessControl {\n    // -------------------------------------------------------------------------\n    // Structs\n    // -------------------------------------------------------------------------\n\n    /**\n     * @notice Configuration of a vault period.\n     * @param epoch Starting timestamp of this configuration.\n     * @param duration Period length in seconds. Must be a multiple of {SMALLEST_PERIOD_DURATION}.\n     * @param startingPeriod Period number assigned to `epoch`.\n     */\n    struct PeriodConfiguration {\n        uint48 epoch;\n        uint48 duration;\n        uint256 startingPeriod;\n    }\n\n    /**\n     * @notice Initial parameters for vault deployment, ABI-encoded as the `bytes` argument\n     *         of {initialize}.\n     * @param defaultAdmin Receives `DEFAULT_ADMIN_ROLE`.\n     * @param limitUpdater Receives `DEPOSIT_LIMIT_UPDATE_ROLE`.\n     * @param blocklister Receives `BLOCKLIST_ROLE`.\n     * @param pauser Receives `PAUSE_ROLE`.\n     * @param periodConfigurationUpdater Receives `PERIOD_CONFIGURATION_UPDATE_ROLE`.\n     * @param rescuer Receives `RESCUER_ROLE`.\n     * @param depositLimit Initial maximum total assets allowed in the vault.\n     * @param periodConfigurationDuration Initial period duration.\n     */\n    struct InitParams {\n        address defaultAdmin;\n        address limitUpdater;\n        address blocklister;\n        address pauser;\n        address periodConfigurationUpdater;\n        address rescuer;\n        uint256 depositLimit;\n        uint48 periodConfigurationDuration;\n    }\n\n    // -------------------------------------------------------------------------\n    // Events\n    // -------------------------------------------------------------------------\n\n    /// @notice Emitted when the vault's deposit limit is updated.\n    event DepositLimitUpdated(uint256 limit);\n\n    /// @notice Emitted when a new period configuration is added.\n    event PeriodConfigurationAdded(PeriodConfiguration periodConfiguration);\n\n    /// @notice Emitted when a withdrawal request is created.\n    event WithdrawRequest(\n        address indexed sender,\n        address indexed receiver,\n        address indexed owner,\n        uint256 period,\n        uint256 assets,\n        uint256 shares\n    );\n\n    /// @notice Emitted when a withdrawal is successfully claimed for a given period.\n    event CompleteWithdraw(address indexed receiver, uint256 assets, uint256 period);\n\n    /// @notice Emitted when a `RESCUER_ROLE` holder rescues shares from a blocklisted address.\n    event SharesRescuedFromBlocklisted(address from, address to, uint256 rescuedShares);\n\n    /// @notice Emitted when a `RESCUER_ROLE` holder rescues pending withdrawals from a blocklisted address.\n    event WithdrawRescuedFromBlocklisted(address from, address to, uint256[] periods, uint256[] rescuedShares);\n\n    /// @notice Emitted when an incident payout is processed, including when no assets can be paid.\n    /// @param to The allowlisted payout receiver.\n    /// @param requestedAmount The requested payout amount, in vault asset units.\n    /// @param paidAmount The amount actually transferred, which may be less than requested or zero.\n    /// @param captureTimestamp The incident capture timestamp that determines the covered period and payout window.\n    event PayoutExecuted(address indexed to, uint256 requestedAmount, uint256 paidAmount, uint48 captureTimestamp);\n\n    /// @notice Emitted when a period's active incident flag is set.\n    /// @param period The period whose active incident flag was set.\n    /// @param active Whether the period has an active incident.\n    event ActiveIncidentUpdated(uint256 indexed period, bool active);\n\n    /// @notice Emitted when an address is added to the blocklist.\n    event AddedToBlocklist(address indexed account);\n\n    /// @notice Emitted when an address is removed from the blocklist.\n    event RemovedFromBlocklist(address indexed account);\n\n    /// @notice Emitted when an address is added to the payout allowlist.\n    event AddedToPayoutAllowlist(address indexed account);\n\n    /// @notice Emitted when an address is removed from the payout allowlist.\n    event RemovedFromPayoutAllowlist(address indexed account);\n\n    /// @notice Emitted when a total-assets checkpoint is recorded outside deposit/withdraw flows.\n    event TotalAssetsCheckpointed(uint256 totalAssets);\n\n    // -------------------------------------------------------------------------\n    // Errors\n    // -------------------------------------------------------------------------\n\n    /// @notice An account involved in the operation is blocklisted.\n    error BlocklistedAddress();\n\n    /// @notice The account is not blocklisted.\n    error NotBlocklistedAddress();\n\n    /// @notice A deposit or mint would increase total assets above the deposit limit.\n    error DepositLimitExceeded();\n\n    /// @notice The deposit limit is zero.\n    error InvalidDepositLimit();\n\n    /// @notice The period configuration epoch is too early or not aligned with a period boundary.\n    error InvalidPeriodConfigurationEpoch();\n\n    /// @notice The period duration is outside the allowed range or not a multiple of SMALLEST_PERIOD_DURATION.\n    error InvalidPeriodConfigurationDuration();\n\n    /// @notice The account has insufficient shares for the withdrawal or no shares to rescue.\n    error InsufficientShares();\n\n    /// @notice The underlying asset address is zero.\n    error InvalidAssetAddress();\n\n    /// @notice The default admin address is zero.\n    error InvalidAdminAddress();\n\n    /// @notice An account address required by the operation is zero.\n    error InvalidAddress();\n\n    /// @notice The requested amount is zero or converts to zero shares or assets.\n    error InvalidAmount();\n\n    /// @notice No period configuration applies to the query, or the withdrawal period has not ended.\n    error InvalidPeriod();\n\n    /// @notice A future period configuration is already scheduled.\n    error CurrentPeriodConfigurationNotLast();\n\n    /// @notice The array of withdrawal periods to rescue is empty.\n    error InvalidArrayLength();\n\n    /// @notice A withdrawal for the account and period has already been claimed.\n    /// @param period The period whose withdrawal has already been claimed.\n    error AlreadyClaimedPeriod(uint256 period);\n\n    /// @notice The account has no withdrawal shares or claimable assets for the period.\n    /// @param period The period with no withdrawal shares or claimable assets.\n    error NoWithdrawalAmount(uint256 period);\n\n    /// @notice The payout receiver is not on the payout allowlist.\n    error AccountNotAllowlisted();\n\n    /// @notice The capture period is neither the current period nor the previous period.\n    error InvalidCapturePeriod();\n\n    /// @notice An active incident in the current or previous period blocks deposits and mints.\n    error CurrentPeriodHasActiveIncident();\n\n    // -------------------------------------------------------------------------\n    // Role identifiers and protocol constants (auto-generated public getters)\n    // -------------------------------------------------------------------------\n\n    function DEPOSIT_LIMIT_UPDATE_ROLE() external view returns (bytes32);\n\n    function RESCUER_ROLE() external view returns (bytes32);\n\n    function BLOCKLIST_ROLE() external view returns (bytes32);\n\n    function PAUSE_ROLE() external view returns (bytes32);\n\n    function PAYOUT_ALLOWLIST_ROLE() external view returns (bytes32);\n\n    function PAYOUT_ROLE() external view returns (bytes32);\n\n    function INCIDENT_ROLE() external view returns (bytes32);\n\n    function CHECKPOINT_ROLE() external view returns (bytes32);\n\n    function PERIOD_CONFIGURATION_UPDATE_ROLE() external view returns (bytes32);\n\n    function SMALLEST_PERIOD_DURATION() external view returns (uint48);\n\n    function MAX_PERIOD_DURATION() external view returns (uint48);\n\n    // -------------------------------------------------------------------------\n    // Storage getters (auto-generated from public state variables)\n    // -------------------------------------------------------------------------\n\n    function depositLimit() external view returns (uint256);\n\n    function contractVersion() external view returns (uint256);\n\n    function pendingWithdrawAssets() external view returns (uint256);\n\n    function periodConfigurations(\n        uint256 index\n    ) external view returns (uint48 epoch, uint48 duration, uint256 startingPeriod);\n\n    function withdrawShares(uint256 period) external view returns (uint256);\n\n    function withdrawAssets(uint256 period) external view returns (uint256);\n\n    function withdrawSharesOf(uint256 period, address account) external view returns (uint256);\n\n    function isWithdrawClaimed(uint256 period, address account) external view returns (bool);\n\n    function isBlocklisted(address account) external view returns (bool);\n\n    // -------------------------------------------------------------------------\n    // Initialization\n    // -------------------------------------------------------------------------\n\n    /**\n     * @notice Initializes the FirelightVault contract.\n     * @param asset_ The underlying ERC20 token.\n     * @param name_ The name of the vault share token.\n     * @param symbol_ The symbol of the vault share token.\n     * @param initParams_ ABI-encoded {InitParams}.\n     */\n    function initialize(IERC20 asset_, string memory name_, string memory symbol_, bytes memory initParams_) external;\n\n    // -------------------------------------------------------------------------\n    // Period queries\n    // -------------------------------------------------------------------------\n\n    function periodConfigurationAtTimestamp(uint48 timestamp) external view returns (PeriodConfiguration memory);\n\n    function periodConfigurationAtNumber(uint256 periodNumber) external view returns (PeriodConfiguration memory);\n\n    function periodAtTimestamp(uint48 timestamp) external view returns (uint256);\n\n    function currentPeriodConfiguration() external view returns (PeriodConfiguration memory);\n\n    function currentPeriod() external view returns (uint256);\n\n    function currentPeriodStart() external view returns (uint48);\n\n    function currentPeriodEnd() external view returns (uint48);\n\n    function nextPeriodEnd() external view returns (uint48);\n\n    function periodConfigurationsLength() external view returns (uint256);\n\n    // -------------------------------------------------------------------------\n    // Historical (checkpointed) queries\n    // -------------------------------------------------------------------------\n\n    function balanceOfAt(address account, uint48 timestamp) external view returns (uint256);\n\n    function totalSupplyAt(uint48 timestamp) external view returns (uint256);\n\n    function totalAssetsAt(uint48 timestamp) external view returns (uint256);\n\n    function withdrawalsOf(uint256 period, address account) external view returns (uint256);\n\n    function isPeriodInPayoutWindow(uint256 period) external view returns (bool);\n\n    // -------------------------------------------------------------------------\n    // Withdrawal lifecycle\n    // -------------------------------------------------------------------------\n\n    /**\n     * @notice Claims a pending withdrawal for `period`.\n     * @dev Reverts if `period` has not yet ended, has already been claimed, or has no balance.\n     */\n    function claimWithdraw(uint256 period) external returns (uint256 assets);\n\n    // -------------------------------------------------------------------------\n    // Pausing (PausableUpgradeable)\n    // -------------------------------------------------------------------------\n\n    function paused() external view returns (bool);\n\n    function pause() external;\n\n    function unpause() external;\n\n    function hasActiveIncident(uint256 period) external view returns (bool);\n\n    function setActiveIncident(uint256 period, bool active) external;\n\n    // -------------------------------------------------------------------------\n    // Admin / configuration\n    // -------------------------------------------------------------------------\n\n    function updateDepositLimit(uint256 newLimit) external;\n\n    function addPeriodConfiguration(uint48 epoch, uint48 duration) external;\n\n    /**\n     * @notice Records a checkpoint of the current total assets. Requires `CHECKPOINT_ROLE`.\n     * @dev Called by the reward distributor after forwarding assets so historical\n     * `totalAssetsAt` lookups include them.\n     */\n    function checkpointTotalAssets() external;\n\n    // -------------------------------------------------------------------------\n    // Blocklist\n    // -------------------------------------------------------------------------\n\n    function addToBlocklist(address account) external;\n\n    function removeFromBlocklist(address account) external;\n\n    // -------------------------------------------------------------------------\n    // Payout allowlist\n    // -------------------------------------------------------------------------\n\n    /// @notice Adds an address to the payout allowlist. Requires PAYOUT_ALLOWLIST_ROLE.\n    /// @param account The address to add. Cannot be the zero address.\n    function addToPayoutAllowlist(address account) external;\n\n    /// @notice Removes an address from the payout allowlist. Requires PAYOUT_ALLOWLIST_ROLE.\n    /// @param account The address to remove.\n    function removeFromPayoutAllowlist(address account) external;\n\n    /// @notice Returns whether an address is on the payout allowlist.\n    /// @param account The address to query.\n    /// @return True if the address is allowlisted to receive payouts.\n    function isPayoutAllowlisted(address account) external view returns (bool);\n\n    // -------------------------------------------------------------------------\n    // Rescue\n    // -------------------------------------------------------------------------\n\n    function rescueSharesFromBlocklisted(address from, address to) external;\n\n    function rescueWithdrawFromBlocklisted(address from, address to, uint256[] calldata periods) external;\n\n    // -------------------------------------------------------------------------\n    // Payout\n    // -------------------------------------------------------------------------\n\n    function payout(address to, uint256 amount, uint48 captureTimestamp) external returns (uint256 paidAmount);\n}\n"},{"file_path":"@openzeppelin/contracts/interfaces/IERC165.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC165} from \"../utils/introspection/IERC165.sol\";\n"},{"file_path":"@openzeppelin/contracts/utils/introspection/IERC165.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC-165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[ERC].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n    /**\n     * @dev Returns true if this contract implements the interface defined by\n     * `interfaceId`. See the corresponding\n     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]\n     * to learn more about how these ids are created.\n     *\n     * This function call must use less than 30 000 gas.\n     */\n    function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"}],"certified":false,"conflicting_implementations":null,"abi":[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidSwapTimestamp","type":"error"},{"inputs":[],"name":"InvalidZeroAddress","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vaultPeriod","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"incentiveRef","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"vaultAssetAmount","type":"uint256"}],"name":"IncentiveDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"vaultPeriod","type":"uint256"},{"indexed":true,"internalType":"address","name":"premiumToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"vaultAssetAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"premiumTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint48","name":"premiumSwapTimestamp","type":"uint48"}],"name":"RewardsDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenSwept","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DISTRIBUTOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SWEEPER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"vaultAssetAmount","type":"uint256"},{"internalType":"bytes32","name":"incentiveRef","type":"bytes32"}],"name":"distributeIncentive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vaultAssetAmount","type":"uint256"},{"internalType":"address","name":"premiumToken","type":"address"},{"internalType":"uint256","name":"premiumTokenAmount","type":"uint256"},{"internalType":"uint48","name":"premiumSwapTimestamp","type":"uint48"}],"name":"distributeRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IFirelightVault","name":"_vault","type":"address"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"distributor","type":"address"},{"internalType":"address","name":"sweeper","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"sweep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"contract IFirelightVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultAsset","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"}],"is_changed_bytecode":false,"is_partially_verified":false,"constructor_args":null}