creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "src/SharpFactsAggregator.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Initializable} from \"openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol\";\nimport {AccessControlUpgradeable} from \"openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol\";\n\nimport {IFactsRegistry} from \"./interfaces/IFactsRegistry.sol\";\nimport {Uint256Splitter} from \"./lib/Uint256Splitter.sol\";\n\n/// @title SharpFactsAggregator\n/// @dev Aggregator contract to handle SHARP job outputs and update the global aggregator state.\n/// @author Herodotus Dev\n/// ------------------\n/// Example:\n/// Blocks inside brackets are the ones processed during their SHARP job execution\n//  7 [8 9 10] 11\n/// n = 10\n/// r = 3\n/// `r` is the number of blocks processed on a single SHARP job execution\n/// `blockNMinusRPlusOneParentHash` = 8.parentHash (oldestHash)\n/// `blockNPlusOneParentHash`       = 11.parentHash (newestHash)\n/// ------------------\ncontract SharpFactsAggregator is Initializable, AccessControlUpgradeable {\n    // Using inline library for efficient splitting and joining of uint256 values\n    using Uint256Splitter for uint256;\n\n    // Role definitions for access control\n    bytes32 public constant OPERATOR_ROLE = keccak256(\"OPERATOR_ROLE\");\n    bytes32 public constant UNLOCKER_ROLE = keccak256(\"UNLOCKER_ROLE\");\n\n    uint256 public constant MINIMUM_BLOCKS_CONFIRMATIONS = 20;\n    uint256 public constant MAXIMUM_BLOCKS_CONFIRMATIONS = 255;\n\n    // Sharp Facts Registry\n    IFactsRegistry public immutable FACTS_REGISTRY;\n\n    // Cairo program hash (i.e., the off-chain block headers accumulator program)\n    bytes32 public constant PROGRAM_HASH =\n        bytes32(\n            uint256(\n                0x1eca36d586f5356fba096edbf7414017d51cd0ed24b8fde80f78b61a9216ed2\n            )\n        );\n\n    // Global aggregator state\n    struct AggregatorState {\n        bytes32 poseidonMmrRoot;\n        bytes32 keccakMmrRoot;\n        uint256 mmrSize;\n        bytes32 continuableParentHash;\n    }\n\n    // Current __global__ state of this aggregator\n    AggregatorState public aggregatorState;\n\n    // Mapping to keep track of block number to its parent hash\n    mapping(uint256 => bytes32) public blockNumberToParentHash;\n\n    // Flag to control operator role requirements\n    bool public isOperatorRequired;\n\n    // Representation of the Cairo program's output (raw unpacked)\n    struct JobOutput {\n        uint256 fromBlockNumberHigh;\n        uint256 toBlockNumberLow;\n        bytes32 blockNPlusOneParentHashLow;\n        bytes32 blockNPlusOneParentHashHigh;\n        bytes32 blockNMinusRPlusOneParentHashLow;\n        bytes32 blockNMinusRPlusOneParentHashHigh;\n        bytes32 mmrPreviousRootPoseidon;\n        bytes32 mmrPreviousRootKeccakLow;\n        bytes32 mmrPreviousRootKeccakHigh;\n        uint256 mmrPreviousSize;\n        bytes32 mmrNewRootPoseidon;\n        bytes32 mmrNewRootKeccakLow;\n        bytes32 mmrNewRootKeccakHigh;\n        uint256 mmrNewSize;\n    }\n\n    // Packed representation of the Cairo program's output (for gas efficiency)\n    struct JobOutputPacked {\n        uint256 blockNumbersPacked;\n        bytes32 blockNPlusOneParentHash;\n        bytes32 blockNMinusRPlusOneParentHash;\n        bytes32 mmrPreviousRootPoseidon;\n        bytes32 mmrPreviousRootKeccak;\n        bytes32 mmrNewRootPoseidon;\n        bytes32 mmrNewRootKeccak;\n        uint256 mmrSizesPacked;\n    }\n\n    // Custom errors for better error handling and clarity\n    error NotEnoughBlockConfirmations();\n    error TooManyBlocksConfirmations();\n    error NotEnoughJobs();\n    error UnknownParentHash();\n    error AggregationError(string message); // Generic error with a message\n    error AggregationBlockMismatch();\n    error GenesisBlockReached();\n    error InvalidFact();\n\n    // Event emitted when a new range is registered\n    // (i.e, when we want to allow aggregating from a more recent block)\n    event NewRangeRegistered(\n        uint256 targetBlock,\n        bytes32 targetBlockParentHash\n    );\n\n    // Event emitted when __at least__ one SHARP job is aggregated\n    event Aggregate(\n        uint256 fromBlockNumberHigh,\n        uint256 toBlockNumberLow,\n        bytes32 poseidonMmrRoot,\n        bytes32 keccakMmrRoot,\n        uint256 mmrSize,\n        bytes32 continuableParentHash\n    );\n\n    event OperatorRequirementChange(\n        bool newRequirement\n    );\n\n    constructor(IFactsRegistry factsRegistry) {\n        FACTS_REGISTRY = factsRegistry;\n    }\n\n    /**\n     * @notice Initializes the contract with given parameters.\n     * @param initialAggregatorState Initial state of the aggregator (i.e., initial trees state).\n     */\n    function initialize(\n        AggregatorState calldata initialAggregatorState\n    ) public initializer {\n        __AccessControl_init();\n\n        aggregatorState = initialAggregatorState;\n\n        _setRoleAdmin(OPERATOR_ROLE, OPERATOR_ROLE);\n        _setRoleAdmin(UNLOCKER_ROLE, OPERATOR_ROLE);\n\n        // Grant operator role to the contract deployer\n        // to be able to define new aggregate ranges\n        _grantRole(OPERATOR_ROLE, _msgSender());\n        _grantRole(UNLOCKER_ROLE, _msgSender());\n\n        // Set operator role requirement to true by default\n        isOperatorRequired = true;\n    }\n\n    /// @notice Reverts if the caller is not an operator and the operator role requirement is enabled\n    modifier onlyOperator() {\n        if (isOperatorRequired) {\n            require(\n                hasRole(OPERATOR_ROLE, _msgSender()),\n                \"Caller is not an operator\"\n            );\n        }\n        _;\n    }\n\n    /// @notice Reverts if the caller is not an unlocker\n    modifier onlyUnlocker() {\n        require(\n            hasRole(UNLOCKER_ROLE, _msgSender()),\n            \"Caller is not an unlocker\"\n        );\n        _;\n    }\n\n    /// @dev Modifies the contract's operator requirement\n    function setOperatorRequired(\n        bool _isOperatorRequired\n    ) external onlyUnlocker {\n        isOperatorRequired = _isOperatorRequired;\n        emit OperatorRequirementChange(_isOperatorRequired);\n    }\n\n    /// Registers a new range to aggregate from\n    /// @notice Caches a recent block hash (MINIMUM_BLOCKS_CONFIRMATIONS to -MAXIMUM_BLOCKS_CONFIRMATIONS from present), relying on the global `blockhash` Solidity function\n    /// @param blocksConfirmations Number of blocks preceding the current block\n    function registerNewRange(\n        uint256 blocksConfirmations\n    ) external onlyOperator {\n        // Minimum blocks confirmations to avoid reorgs\n        if (blocksConfirmations < MINIMUM_BLOCKS_CONFIRMATIONS) {\n            revert NotEnoughBlockConfirmations();\n        }\n\n        // Maximum MAXIMUM_BLOCKS_CONFIRMATIONS blocks confirmations to capture\n        // an available block hash with Solidity `blockhash()`\n        if (blocksConfirmations > MAXIMUM_BLOCKS_CONFIRMATIONS) {\n            revert TooManyBlocksConfirmations();\n        }\n\n        // Determine the target block number (i.e. the child block)\n        uint256 targetBlock = block.number - blocksConfirmations;\n\n        // Extract its parent hash.\n        bytes32 targetBlockParentHash = blockhash(targetBlock - 1);\n\n        // If the parent hash is not available, revert\n        // (This should never happen under the current EVM rules)\n        if (targetBlockParentHash == bytes32(0)) {\n            revert UnknownParentHash();\n        }\n\n        // Cache the parent hash so that we can later on continue accumlating from it\n        blockNumberToParentHash[targetBlock] = targetBlockParentHash;\n\n        // If we cannot aggregate further in the past (e.g., genesis block is reached or it's a new tree)\n        if (aggregatorState.continuableParentHash == bytes32(0)) {\n            // Set the aggregator state's `continuableParentHash` to the target block's parent hash\n            // so we can easily continue aggregating from it without specifying `rightBoundStartBlock` in `aggregateSharpJobs`\n            aggregatorState.continuableParentHash = targetBlockParentHash;\n        }\n\n        emit NewRangeRegistered(targetBlock, targetBlockParentHash);\n    }\n\n    /// @notice Aggregate SHARP jobs outputs (min. 1) to update the global aggregator state\n    /// @param rightBoundStartBlock The reference block to start from. Defaults to continuing from the global state if set to `0`\n    /// @param outputs Array of SHARP jobs outputs (packed for Solidity)\n    function aggregateSharpJobs(\n        uint256 rightBoundStartBlock,\n        JobOutputPacked[] calldata outputs\n    ) external onlyOperator {\n        // Ensuring at least one job output is provided\n        if (outputs.length < 1) {\n            revert NotEnoughJobs();\n        }\n\n        bytes32 rightBoundStartBlockParentHash = bytes32(0);\n\n        // Start from a different block than the current state if `rightBoundStartBlock` is specified\n        if (rightBoundStartBlock != 0) {\n            // Retrieve from cache the parent hash of the block to start from\n            rightBoundStartBlockParentHash = blockNumberToParentHash[\n                rightBoundStartBlock\n            ];\n\n            // If not present in the cache, hash is not authenticated and we cannot continue from it\n            if (rightBoundStartBlockParentHash == bytes32(0)) {\n                revert UnknownParentHash();\n            }\n        }\n\n        JobOutputPacked calldata firstOutput = outputs[0];\n        // Ensure the first job is continuable\n        ensureContinuable(rightBoundStartBlockParentHash, firstOutput);\n\n        if (rightBoundStartBlockParentHash != bytes32(0)) {\n            (uint256 fromBlockHighStart, ) = firstOutput\n                .blockNumbersPacked\n                .split128();\n\n            // We check that block numbers are consecutives\n            if (fromBlockHighStart != rightBoundStartBlock - 1) {\n                revert AggregationBlockMismatch();\n            }\n        }\n\n        uint256 limit = outputs.length - 1;\n        \n        // Iterate over the jobs outputs (aside from the last one)\n        // and ensure jobs are correctly linked and valid\n        for (uint256 i = 0; i < limit; ++i) {\n            JobOutputPacked calldata curOutput = outputs[i];\n            JobOutputPacked calldata nextOutput = outputs[i + 1];\n\n            ensureValidFact(curOutput);\n            ensureConsecutiveJobs(curOutput, nextOutput);\n        }\n\n        JobOutputPacked calldata lastOutput = outputs[limit];\n        ensureValidFact(lastOutput);\n\n        // We save the latest output in the contract state for future calls\n        (, uint256 mmrNewSize) = lastOutput.mmrSizesPacked.split128();\n        aggregatorState.poseidonMmrRoot = lastOutput.mmrNewRootPoseidon;\n        aggregatorState.keccakMmrRoot = lastOutput.mmrNewRootKeccak;\n        aggregatorState.mmrSize = mmrNewSize;\n        aggregatorState.continuableParentHash = lastOutput\n            .blockNMinusRPlusOneParentHash;\n\n        (uint256 fromBlock, ) = firstOutput.blockNumbersPacked.split128();\n        (, uint256 toBlock) = lastOutput.blockNumbersPacked.split128();\n\n        emit Aggregate(\n            fromBlock,\n            toBlock,\n            lastOutput.mmrNewRootPoseidon,\n            lastOutput.mmrNewRootKeccak,\n            mmrNewSize,\n            lastOutput.blockNMinusRPlusOneParentHash\n        );\n    }\n\n    /// @notice Ensures the fact is registered on SHARP Facts Registry\n    /// @param output SHARP job output (packed for Solidity)\n    function ensureValidFact(JobOutputPacked memory output) internal view {\n        (uint256 fromBlock, uint256 toBlock) = output\n            .blockNumbersPacked\n            .split128();\n\n        (uint256 mmrPreviousSize, uint256 mmrNewSize) = output\n            .mmrSizesPacked\n            .split128();\n        (\n            uint256 blockNPlusOneParentHashLow,\n            uint256 blockNPlusOneParentHashHigh\n        ) = uint256(output.blockNPlusOneParentHash).split128();\n\n        (\n            uint256 blockNMinusRPlusOneParentHashLow,\n            uint256 blockNMinusRPlusOneParentHashHigh\n        ) = uint256(output.blockNMinusRPlusOneParentHash).split128();\n\n        (\n            uint256 mmrPreviousRootKeccakLow,\n            uint256 mmrPreviousRootKeccakHigh\n        ) = uint256(output.mmrPreviousRootKeccak).split128();\n\n        (uint256 mmrNewRootKeccakLow, uint256 mmrNewRootKeccakHigh) = uint256(\n            output.mmrNewRootKeccak\n        ).split128();\n\n        // We assemble the outputs in a uint256 array\n        uint256[] memory outputs = new uint256[](14);\n        outputs[0] = fromBlock;\n        outputs[1] = toBlock;\n        outputs[2] = blockNPlusOneParentHashLow;\n        outputs[3] = blockNPlusOneParentHashHigh;\n        outputs[4] = blockNMinusRPlusOneParentHashLow;\n        outputs[5] = blockNMinusRPlusOneParentHashHigh;\n        outputs[6] = uint256(output.mmrPreviousRootPoseidon);\n        outputs[7] = mmrPreviousRootKeccakLow;\n        outputs[8] = mmrPreviousRootKeccakHigh;\n        outputs[9] = mmrPreviousSize;\n        outputs[10] = uint256(output.mmrNewRootPoseidon);\n        outputs[11] = mmrNewRootKeccakLow;\n        outputs[12] = mmrNewRootKeccakHigh;\n        outputs[13] = mmrNewSize;\n\n        // We hash the outputs\n        bytes32 outputHash = keccak256(abi.encodePacked(outputs));\n\n        // We compute the deterministic fact bytes32 value\n        bytes32 fact = keccak256(abi.encode(PROGRAM_HASH, outputHash));\n\n        // We ensure this fact has been registered on SHARP Facts Registry\n        if (!FACTS_REGISTRY.isValid(fact)) {\n            revert InvalidFact();\n        }\n    }\n\n    /// @notice Ensures the job output is cryptographically sound to continue from\n    /// @param rightBoundStartParentHash The parent hash of the block to start from\n    /// @param output The job output to check\n    function ensureContinuable(\n        bytes32 rightBoundStartParentHash,\n        JobOutputPacked memory output\n    ) internal view {\n        (uint256 mmrPreviousSize, ) = output.mmrSizesPacked.split128();\n\n        // Check that the job's previous Poseidon MMR root is the same as the one stored in the contract state\n        if (output.mmrPreviousRootPoseidon != aggregatorState.poseidonMmrRoot)\n            revert AggregationError(\"Poseidon root mismatch\");\n\n        // Check that the job's previous Keccak MMR root is the same as the one stored in the contract state\n        if (output.mmrPreviousRootKeccak != aggregatorState.keccakMmrRoot)\n            revert AggregationError(\"Keccak root mismatch\");\n\n        // Check that the job's previous MMR size is the same as the one stored in the contract state\n        if (mmrPreviousSize != aggregatorState.mmrSize)\n            revert AggregationError(\"MMR size mismatch\");\n\n        if (rightBoundStartParentHash == bytes32(0)) {\n            // If the right bound start parent hash __is not__ specified,\n            // we check that the job's `blockN + 1 parent hash` is matching with the previously stored parent hash\n            if (\n                output.blockNPlusOneParentHash !=\n                aggregatorState.continuableParentHash\n            ) {\n                revert AggregationError(\"Global state: Parent hash mismatch\");\n            }\n        } else {\n            // If the right bound start parent hash __is__ specified,\n            // we check that the job's `blockN + 1 parent hash` is matching with a previously stored parent hash\n            if (output.blockNPlusOneParentHash != rightBoundStartParentHash) {\n                revert AggregationError(\"Parent hash mismatch\");\n            }\n        }\n    }\n\n    /// @notice Ensures the job outputs are correctly linked\n    /// @param output The job output to check\n    /// @param nextOutput The next job output to check\n    function ensureConsecutiveJobs(\n        JobOutputPacked memory output,\n        JobOutputPacked memory nextOutput\n    ) internal pure {\n        (, uint256 toBlock) = output.blockNumbersPacked.split128();\n\n        // We cannot aggregate further past the genesis block\n        if (toBlock == 0) {\n            revert GenesisBlockReached();\n        }\n\n        (uint256 nextFromBlock, ) = nextOutput.blockNumbersPacked.split128();\n\n        // We check that the next job's `from block` is the same as the previous job's `to block + 1`\n        if (toBlock - 1 != nextFromBlock) revert AggregationBlockMismatch();\n\n        (, uint256 outputMmrNewSize) = output.mmrSizesPacked.split128();\n        (uint256 nextOutputMmrPreviousSize, ) = nextOutput\n            .mmrSizesPacked\n            .split128();\n\n        // We check that the previous job's new Poseidon MMR root matches the next job's previous Poseidon MMR root\n        if (output.mmrNewRootPoseidon != nextOutput.mmrPreviousRootPoseidon)\n            revert AggregationError(\"Poseidon root mismatch\");\n\n        // We check that the previous job's new Keccak MMR root matches the next job's previous Keccak MMR root\n        if (output.mmrNewRootKeccak != nextOutput.mmrPreviousRootKeccak)\n            revert AggregationError(\"Keccak root mismatch\");\n\n        // We check that the previous job's new MMR size matches the next job's previous MMR size\n        if (outputMmrNewSize != nextOutputMmrPreviousSize)\n            revert AggregationError(\"MMR size mismatch\");\n\n        // We check that the previous job's lowest block hash matches the next job's highest block hash\n        if (\n            output.blockNMinusRPlusOneParentHash !=\n            nextOutput.blockNPlusOneParentHash\n        ) revert AggregationError(\"Parent hash mismatch\");\n    }\n\n    /// @dev Helper function to verify a fact based on a job output\n    function verifyFact(uint256[] memory outputs) external view returns (bool) {\n        bytes32 outputHash = keccak256(abi.encodePacked(outputs));\n        bytes32 fact = keccak256(abi.encode(PROGRAM_HASH, outputHash));\n\n        return FACTS_REGISTRY.isValid(fact);\n    }\n\n    /// @notice Returns the current root hash of the Keccak Merkle Mountain Range (MMR) tree\n    function getMMRKeccakRoot() external view returns (bytes32) {\n        return aggregatorState.keccakMmrRoot;\n    }\n\n    /// @notice Returns the current root hash of the Poseidon Merkle Mountain Range (MMR) tree\n    function getMMRPoseidonRoot() external view returns (bytes32) {\n        return aggregatorState.poseidonMmrRoot;\n    }\n\n    /// @notice Returns the current size of the Merkle Mountain Range (MMR) trees\n    function getMMRSize() external view returns (uint256) {\n        return aggregatorState.mmrSize;\n    }\n}\n"
      },
      "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.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 Indicates that the contract has been initialized.\n     */\n    uint8 private _initialized;\n\n    /**\n     * @dev Indicates that the contract is in the process of being initialized.\n     */\n    bool private _initializing;\n\n    /**\n     * @dev The contract is already initialized.\n     */\n    error AlreadyInitialized();\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(uint8 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 functions marked with `initializer` can be nested in the context of a\n     * constructor.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier initializer() {\n        bool isTopLevelCall = !_initializing;\n        if (!(isTopLevelCall && _initialized < 1) && !(address(this).code.length == 0 && _initialized == 1)) {\n            revert AlreadyInitialized();\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 255 will prevent any future reinitialization.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier reinitializer(uint8 version) {\n        if (_initializing || _initialized >= version) {\n            revert AlreadyInitialized();\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 (!_initializing) {\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        if (_initializing) {\n            revert AlreadyInitialized();\n        }\n        if (_initialized != type(uint8).max) {\n            _initialized = type(uint8).max;\n            emit Initialized(type(uint8).max);\n        }\n    }\n\n    /**\n     * @dev Returns the highest version that has been initialized. See {reinitializer}.\n     */\n    function _getInitializedVersion() internal view returns (uint8) {\n        return _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 _initializing;\n    }\n}\n"
      },
      "lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)\n\npragma solidity ^0.8.20;\n\nimport { IAccessControlUpgradeable } from \"./IAccessControlUpgradeable.sol\";\nimport { ContextUpgradeable } from \"../utils/ContextUpgradeable.sol\";\nimport { ERC165Upgradeable } from \"../utils/introspection/ERC165Upgradeable.sol\";\nimport \"../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, IAccessControlUpgradeable, ERC165Upgradeable {\n    function __AccessControl_init() internal onlyInitializing {\n    }\n\n    function __AccessControl_init_unchained() internal onlyInitializing {\n    }\n    struct RoleData {\n        mapping(address => bool) members;\n        bytes32 adminRole;\n    }\n\n    mapping(bytes32 => 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(IAccessControlUpgradeable).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].members[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].members[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].members[account] = false;\n            emit RoleRevoked(role, account, _msgSender());\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev This empty reserved space is put in place to allow future versions to add new\n     * variables without shifting down storage in the inheritance chain.\n     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n     */\n    uint256[49] private __gap;\n}\n"
      },
      "src/interfaces/IFactsRegistry.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IFactsRegistry {\n    function isValid(bytes32 fact) external view returns (bool);\n}\n"
      },
      "src/lib/Uint256Splitter.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nlibrary Uint256Splitter {\n    uint256 constant _MASK = type(uint128).max;\n\n    /// @notice Splits a uint256 into two uint128s (low, high) represented as uint256s.\n    /// @param a The uint256 to split.\n    function split128(\n        uint256 a\n    ) internal pure returns (uint256 lower, uint256 upper) {\n        return (a & _MASK, a >> 128);\n    }\n\n    /// @notice Merges two uint128s (low, high) into one uint256.\n    /// @param lower The lower uint256. The caller is required to pass a value that is less than 2^128 - 1.\n    /// @param upper The upper uint256.\n    function merge128(\n        uint256 lower,\n        uint256 upper\n    ) internal pure returns (uint256 a) {\n        require(lower <= _MASK, \"Uint256Splitter: lower exceeds uint128\");\n        // return (upper << 128) | lower;\n        assembly {\n            a := or(shl(128, upper), lower)\n        }\n    }\n}\n"
      },
      "lib/openzeppelin-contracts-upgradeable/contracts/access/IAccessControlUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControlUpgradeable {\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, an admin role\n     * bearer except when using {AccessControl-_setupRole}.\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"
      },
      "lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.20;\nimport \"../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    /**\n     * @dev This empty reserved space is put in place to allow future versions to add new\n     * variables without shifting down storage in the inheritance chain.\n     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n     */\n    uint256[50] private __gap;\n}\n"
      },
      "lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/ERC165Upgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.20;\n\nimport { IERC165Upgradeable } from \"./IERC165Upgradeable.sol\";\nimport \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 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, IERC165Upgradeable {\n    function __ERC165_init() internal onlyInitializing {\n    }\n\n    function __ERC165_init_unchained() internal onlyInitializing {\n    }\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {\n        return interfaceId == type(IERC165Upgradeable).interfaceId;\n    }\n\n    /**\n     * @dev This empty reserved space is put in place to allow future versions to add new\n     * variables without shifting down storage in the inheritance chain.\n     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n     */\n    uint256[50] private __gap;\n}\n"
      },
      "lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/IERC165Upgradeable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\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 IERC165Upgradeable {\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[EIP 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"
      }
    },
    "settings": {
      "remappings": [
        "ds-test/=lib/forge-std/lib/ds-test/src/",
        "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
        "forge-std/=lib/forge-std/src/",
        "openzeppelin-contracts/=lib/openzeppelin-contracts/",
        "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
        "openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 20000
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs",
        "appendCBOR": true
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "paris",
      "libraries": {}
    }
  }}