{"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":"85e58ec1-1ee5-4b04-8133-d8bb9ae15f1c","kind":"skill:build-contract-project","nodes":[{"acceptedSubmissionHash":"be411c73c79b378a975d4c42797d0e7ffe3375190833ac18e061bf1fa94447c4","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"587567a5ef71546b5973ceaf6ef1dbbd590f49503b5ac9a6973ed86b89584857","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"587567a5ef71546b5973ceaf6ef1dbbd590f49503b5ac9a6973ed86b89584857","skillId":"build-contract-project","state":"accepted"}],"objective":"Sorphera - contract review, executable testing and operations\n\nSelect CONTRACTS and continue the latest Sorphera project.\nRepo: Sorphera\nReviewed PR: #2, merged.\nReviewed commit: 6a6b3e57adcbfac8063aaa0d734c055c4fcff940\nLatest completed job: 76506bc8-6d8a-4fe0-95cb-6384a0801040\n\nName: Sorphera\nTagline: Weekly ETH & NFT lottery ball jackpots. Powered by FWA.\n\nUse the current project head. Implement fixes, tests and contract execution tools in this repo. Supporting docs belong with the code; this is not a website or standalone report assignment.\n\nMainnet is the production target. Do not broadcast mainnet transactions, spend real funds, open a new paid launch or enable public sales. Complete local/fork work despite missing external access.\n\nPRESERVE THE BASELINE\n\nRead the existing test evidence, deployment docs and interfaces before editing. Preserve the passing mainnet integration and separate NFT tie-break VRF.\n\nKeep two weekly games, uncapped total ticket sales, bounded transactions, three distinct unordered main numbers 1-20 plus bonus 1-5, default 0.005 ETH tickets and fixed 10% operator fee / 90% acquisition allocation. ETH matches split equally; no matches roll over. NFT no-match inventory rolls; one matching ticket wins all; multiple matches use a separate VRF request to select one ticket for all inventory. Separate company builder rewards from purchaser prizes/rewards/refunds.\n\nADVERSARIAL CONTRACT REVIEW AND FIXES\n\nReview the latest code, especially tie states, callbacks, accounting and external dependencies. Use a separate reviewer if supported and disclose reviewer roles. Automated review is not a professional audit.\n\nFor reproducible bugs, add a failing regression test and a minimal fix. Cover:\n\nRound isolation, immutable sold terms, fee release, rounding, rollovers and late recoveries.\n\nDraw/tie request binding, replay/out-of-order callbacks, subscription changes, funding depletion and callback gas.\n\nFWA settlement deadlines, forced outcomes, stuck NFTs, helper permissions and reward delivery.\n\nUnauthorized withdrawals, reentrancy and bounded processing at large ticket/inventory counts.\n\nPreserve economics and safeguards; avoid unrelated rewrites.\n\nHANDLE ORACLE FAILURE WITHOUT COMPROMISING FAIRNESS\n\nCurrent draw/tie VRF failure can lock funds indefinitely. Review against Chainlink security guidance.\n\nTest underfunding followed by top-up, delayed callbacks, insufficient gas, legitimate callback completion without external payouts and permanent nonfulfillment. Distinguish a reverted request transaction from an accepted request awaiting fulfillment.\n\nImplement compatible reserve checks, monitoring and safeguards where justified. Do not silently add cancellation, new randomness, fallback seeds, winner replacement or operator-selected results after a successful request. A timeout must not let anyone discard unfavorable randomness.\n\nIf a remedy changes refund/payout rights or trust assumptions, leave it out of the production candidate and present the exact proposed rule, attack analysis and decision required. Do not mark the lockup resolved merely because it is documented.\n\nDELIVER AN EXECUTABLE LIVE SEPOLIA VRF WORKFLOW\n\nSepoliaCanary.s.sol currently simulates deployment. Add a resumable CLI/script with explicit dry-run and live-Sepolia modes, controlled FWA test dependencies, the real coordinator and production lottery callbacks.\n\nSupport deployment, factory/router binding, company subscription creation or supplied ID, consumer registration, funding/configuration checks, test tickets, both draws, independent NFT tie-break, finalization and claims. Persist addresses/receipts for restart. Clearly label mock dependencies.\n\nUse a small pre-sale test ticket price and coverage of all 5,700 combinations when needed to guarantee winners/ties. Never assume the random result or impersonate the coordinator in a live test.\n\nExecute live transactions only with company test-wallet authorization/access and test funds supplied for this task. Use secure signers; never request, print or commit secrets. Otherwise finish/test the workflow locally and mark live execution NOT RUN with exact missing inputs. Prank-only simulation is not an executable live workflow.\n\nLive evidence must include actual coordinator request/fulfillment receipts, callback success, billing, distinct draw/tie request IDs and claims. Keep it separate from simulated evidence.\n\nBUILD A RUNNABLE CONTRACT KEEPER\n\nOperations.s.sol only returns unsigned calldata. Add a minimal restart-safe keeper CLI/worker, defaulting to dry-run with live submission explicitly gated.\n\nSupport opening rounds, bounded purchases, FWA processing, urgent settlement, reconciliation/refunds, closing, draw/tie requests and finalization. Prioritize settlement deadlines. Enforce frozen budgets/slippage bounds and never replace accepted VRF requests.\n\nReconcile chain state before retries; persist cursors/checkpoints; prevent unsafe duplicate-worker/nonce behavior. Handle RPC outages, pending/replaced/reverted transactions and chain reorganizations. Add gas/spend ceilings, funding alerts, structured logs and a supervisor/container configuration.\n\nSeparate permissionless maintenance from owner-only treasury/configuration actions; do not give the keeper unrestricted ownership. Test both game lifecycles on a local fork, including restart and delayed callbacks. Supply start/dry-run/stop/recovery commands. Hosting and real operating funds remain external prerequisites.\n\nADDRESS THE IMD GATE WITH AVAILABLE ACCESS\n\nLaunch 961 remains parked on the original code. Empty-chain constructor rejection and genuine-fork success are already documented in docs/REHEARSAL.md.\n\nIf the private runner is accessible, rerun against the latest source and correct chain/manifest. Capture resolved arguments, dependency order/code, chain/block and inner revert. Fix demonstrated application faults while preserving dependency checks.\n\nIf unavailable, retain the public reproduction and provide one concise support-request draft listing the necessary evidence. Do not repeatedly probe inaccessible endpoints or claim local tests cleared the private gate. Do not contact support on my behalf.\n\nKeep the historical Sepolia manifest separate from mainnet preparation. A continuation must not silently deploy or retarget the old launch.\n\nVERIFY AND HAND OFF THE CONTRACT VERSION\n\nRun build/format checks, regression/invariant tests, relevant mainnet/Sepolia fork tests and new tooling tests. Record exact commands, source commit, toolchain, fork blocks and pass/fail/skip counts. Distinguish live oracle results from simulation.\n\nUpdate ABIs, events/status mappings, configuration schemas and deployment/setup commands after fixes. Leave undeployed addresses null. Identify interface/policy decisions affecting future website integration.\n\nPublish through IMD's supported artifact mechanism to the existing repo/PR. Respect worker git restrictions; distinguish those from the platform's final publication status.\n\nFinish with a concise summary of code changes, actual test results, unresolved findings, private-gate status, live-VRF status and one external-action checklist naming who must supply each item. State whether interfaces are ready for website integration. Complete all unblocked implementation; do not stop at recommendations or claim public-launch readiness.","parentJobId":"76506bc8-6d8a-4fe0-95cb-6384a0801040","planHash":"ab72ef6252df50eeb28809b66a77975e0c1973295bc186914304f36fd114c585","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"a3707b79-51b8-4d04-9e32-3e786d91c1d9","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-961-build-sorphera-smart-contracts"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51197","feedbackHash":"a1399adb4cd40a5b9d97b0672dc6a6c677831dc9bcba82bf508d44162a2493f3","nodeKey":"build_contract_project","submissionHash":"be411c73c79b378a975d4c42797d0e7ffe3375190833ac18e061bf1fa94447c4","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"e8eb9177551a5320c38045689c501e30bac193a0cf188c31774087bbda156d04","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"e654cc42df4504348ba8f39eae589ee02c1de929d0cef02f4987fa6423166a8b","device":"f2d7eadae2a3cfdf","findings":[],"hash":"be411c73c79b378a975d4c42797d0e7ffe3375190833ac18e061bf1fa94447c4","nodeId":"45997df4-ec5e-4a74-8fbb-6ff122491b96","outcome":"completed","summary":"Implemented the contract fixes, resumable Sepolia workflow, keeper, tests and updated ABIs/docs. [Handoff and evidence](/home/seat/.identitymd/work/85e58ec1-1ee5-4b04-8133-d8bb9ae15f1c/45997df4-ec5e-4a74-8fbb-6ff122491b96/docs/CONTINUATION.md).\n\nFixed premature NFT cancellation and hostile ownership-read gas exhaustion; added frozen VRF reserve checks. Separate automated reviewers checked contracts and tooling—this is not a professional audit.\n\nVerification passed: build/format, **75 Foundry tests, 47 tooling tests, 14 fork tests**, constructor rejection and both keeper lifecycles. No failures or skips.\n\nPermanent accepted-VRF lockup remains unresolved. Live Sepolia VRF **NOT RUN**; private IMD gate **NOT RERUN**; platform publication unconfirmed. No mainnet transactions or public sales enabled.\n\nExternal actions:\n\n- **Company:** supply authorized signer/test funds and execute the live canary; decide remaining oracle policy.\n- **Operations:** supply hosting, RPC, keeper wallet and operating budget.\n- **Human reviewer:** complete independent security review.\n- **IMD:** publish artifacts and rerun the private gate.\n\nInterfaces are ready for website implementation against this source; live activation is not ready.","treeHash":"b05bab360ebf837db7731602ef2be797c1cc3ba3","usage":{"cachedInputTokens":5961088,"inputTokens":163062,"model":"gpt-6-astra","outputTokens":35158,"runtime":"codex","turns":9,"wallClockMs":2235837}}],"verification":[{"checks":[{"durationMs":82670,"exitCode":0,"name":"build","output":"Compiling 42 files with Solc 0.8.26\nSolc 0.8.26 finished in 82.53s\nCompiler run successful!\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n  ╭▸ src/lib/Balls.sol:9:13\n  │\n9 │             require(a[i] >= 1 && a[i] <= 20, \"Sorphera: ball\");\n  │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n  │\n  ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[uninitialized-local]: local variable is read before being initialized\n   ╭▸ src/lib/Balls.sol:23:68\n   │\n23 │             uint256 x = uint256(keccak256(abi.encode(seed, domain, counter)));\n   │                                                                    ━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[missing-events-arithmetic]: `budget` is changed without an event but is used in arithmetic\n    ╭▸ src/SorpheraVault.sol:104:9\n    │\n104 │         budget += msg.value;\n    │         ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/lib/Balls.sol:20:9\n   │\n20 │         require(n != 0, \"Sorphera: empty sample\");\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/lib/Balls.sol:26:9\n   │\n26 │         revert(\"unreachable\");\n   │         ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/lib/Balls.sol:43:17\n   │\n43 │         bonus = uint8(uniform(seed, keccak256(\"Sorphera bonus\"), 5) + 1);\n   │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint8' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SorpheraVault.sol:82:9\n   │\n82 │         address lottery_,\n   │         ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/lib/RewardTransfer.sol:19:32\n   │\n19 │         require(amount != 0 && block.timestamp <= deadline, \"Sorphera: reward bounds\");\n   │                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/lib/RewardTransfer.sol:45:9\n   │\n45 │         emit RewardQueued(token, recipient, amount, nonce);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SorpheraVaultFactory.sol:22:25\n   │\n22 │     function setLottery(address lottery_) external onlyOwner {\n   │                         ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unused-return]: return value of an external call is not used\n   ╭▸ src/lib/RewardTransfer.sol:36:9\n   │\n36 │ ┏         ITransferHelper(helper)\n37 │ ┃             .depositWithPermit2(\n38 │ ┃                 ITransferHelper.PermitTransferFrom(\n39 │ ┃                     ITransferHelper.TokenPermissions(token, amount), nonce, deadline\n   ‡ ┃\n42 │ ┃                 recipient\n43 │ ┃             );\n   │ ┗━━━━━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/SorpheraVaultFactory.sol:37:9\n   │\n37 │         emit VaultCreated(msg.sender, game, round, address(vault));\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SorpheraVault.sol:109:64\n    │\n109 │             ISorphera(lottery).acquisitionOpen(game, round) && block.timestamp < settings.cutoff\n    │                                                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SorpheraVault.sol:113:44\n    │\n113 │         require(count > 0 && count <= 8 && block.timestamp <= deadline, \"Sorphera: acquisition batch\");\n    │                                            ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n   ╭▸ src/lib/Networks.sol:21:17\n   │\n21 │         require(valid, \"Sorphera: network configuration\");\n   │                 ━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:132:51\n    │\n132 │             IFWA.Acquisition memory acquisition = pool.acquisitions(ids[i]);\n    │                                                   ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SorpheraVault.sol:136:13\n    │\n136 │             emit Acquisition(game, round, ids[i], acquisition.priceEscrowed, vrf, sw, fw);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SorpheraVault.sol:131:13\n    │\n131 │             require(ids[i] != 0 && requestState[ids[i]] == 0, \"Sorphera: duplicate request\");\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SorpheraVault.sol:133:13\n    │\n133 │             require(acquisition.purchaser == address(this), \"Sorphera: purchaser\");\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-no-eth]: external call can be reentered before `requestState` is updated\n    ╭▸ src/SorpheraVault.sol:152:17\n    │\n152 │                 pool.acceptDepositorBid(a.listingId);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `requestState` is updated\n    ╭▸ src/SorpheraVault.sol:154:17\n    │\n154 │                 pool.keepNFT(a.listingId);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `requestState` is updated\n    ╭▸ src/SorpheraVault.sol:209:21\n    │\n209 │                     ISorphera(lottery).recordNFT(game, round, securedAt);\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SorpheraVault.sol:210:21\n    │\n210 │                     emit CustodySecured(index, l.collection, l.tokenId, id, a.listingId);\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SorpheraVault.sol:216:21\n    │\n216 │                     emit DeliveryStuck(id, a.listingId);\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SorpheraVault.sol:223:13\n    │\n223 │             emit Reconciled(id, a.listingId, a.status);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `entered` is updated\n    ╭▸ src/SorpheraVault.sol:162:9\n    │\n162 │         pool.processAcquisitions(count);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SorpheraVault.sol:162:9\n    │\n162 │         pool.processAcquisitions(count);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:188:37\n    │\n188 │         IFWA.Acquisition memory a = pool.acquisitions(id);\n    │                                     ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:192:37\n    │\n192 │             IFWA.Listing memory l = pool.listings(a.listingId);\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:199:21\n    │\n199 │                 try IERC721(l.collection).ownerOf{gas: 50000}(l.tokenId) returns (address holder) {\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:209:21\n    │\n209 │                     ISorphera(lottery).recordNFT(game, round, securedAt);\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:211:28\n    │\n211 │                 } else if (pool.stuckNFTRecipient(a.listingId) == address(this)) {\n    │                            ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SorpheraVault.sol:187:9\n    │\n187 │         require(requestState[id] != 0, \"Sorphera: unknown acquisition\");\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SorpheraVault.sol:189:9\n    │\n189 │         require(a.purchaser == address(this), \"Sorphera: purchaser\");\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SorpheraVault.sol:193:13\n    │\n193 │             require(l.purchaser == address(this), \"Sorphera: purchaser\");\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n   ╭▸ src/lib/Security.sol:15:22\n   │\n15 │         (bool ok,) = to.call{value: amount}(\"\");\n   │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/SorpheraVault.sol:174:72\n    │\n174 │     function reviewCancellation(uint256 deadline) external onlyLottery nonReentrant returns (bool complete) {\n    │                                                                        ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SorpheraVault.sol:175:30\n    │\n175 │         require(game == 1 && block.timestamp >= deadline, \"Sorphera: cancellation review time\");\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SorpheraVault.sol:202:21\n    │\n202 │                 if (held) {\n    │                     ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-no-eth]: external call can be reentered before `requestState` is updated\n    ╭▸ src/SorpheraVault.sol:231:9\n    │\n231 │         pool.recoverStuckNFT(a.listingId);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `entered` is updated\n    ╭▸ src/SorpheraVault.sol:241:9\n    │\n241 │         pool.withdrawAcquisitionRefund();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SorpheraVault.sol:242:9\n    │\n242 │         emit RefundRecovered(address(this).balance - beforeBalance);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SorpheraVault.sol:241:9\n    │\n241 │         pool.withdrawAcquisitionRefund();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SorpheraVault.sol:250:13\n    │\n250 │         if (block.timestamp >= settings.cutoff) budget = 0;\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SorpheraVault.sol:255:13\n    │\n255 │             emit ETHExported(amount);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `entered` is updated\n    ╭▸ src/SorpheraVault.sol:262:9\n    │\n262 │         rewards.claimEpochTokens(epochs);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SorpheraVault.sol:263:9\n    │\n263 │         emit PurchaserRewardsClaimed(prizeToken.balanceOf(address(this)) - beforeBalance);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SorpheraVault.sol:262:9\n    │\n262 │         rewards.claimEpochTokens(epochs);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `entered` is updated\n   ╭▸ src/lib/RewardTransfer.sol:36:9\n   │\n36 │ ┏         ITransferHelper(helper)\n37 │ ┃             .depositWithPermit2(\n38 │ ┃                 ITransferHelper.PermitTransferFrom(\n39 │ ┃                     ITransferHelper.TokenPermissions(token, amount), nonce, deadline\n   ‡ ┃\n42 │ ┃                 recipient\n43 │ ┃             );\n   │ ┗━━━━━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `entered` is updated\n    ╭▸ src/SorpheraVault.sol:299:17\n    │\n299 │             try this.deliver{gas: 500000}(index, recipient) {\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:299:17\n    │\n299 │             try this.deliver{gas: 500000}(index, recipient) {\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SorpheraVault.sol:300:17\n    │\n300 │                 emit NFTClaimed(index, recipient);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SorpheraVault.sol:303:17\n    │\n303 │                 emit NFTClaimFailed(index, recipient);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SorpheraVault.sol:294:21\n    │\n294 │                     emit SharedAssetVote(index, ticket, recipient);\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SorpheraVault.sol:287:13\n    │\n287 │             require(!a.claimed, \"Sorphera: NFT claimed\");\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SorpheraRouter.sol:26:39\n   │\n26 │     function configure(address pool_, address helper_) external onlyOwner {\n   │                                       ━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SorpheraRouter.sol:26:24\n   │\n26 │     function configure(address pool_, address helper_) external onlyOwner {\n   │                        ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/SorpheraRouter.sol:106:15\n    │\n106 │         ids = IFWA(p).acquire{value: msg.value}(msg.sender, count, maxFee, minValue, slippage);\n    │               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[reentrancy-eth]: uncapped ETH transfer can be reentered before `entered` is updated\n   ╭▸ src/lib/Security.sol:15:22\n   │\n15 │         (bool ok,) = to.call{value: amount}(\"\");\n   │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SorpheraRouter.sol:102:13\n    │\n102 │             block.timestamp <= deadline && maxFee > 0 && minValue > 0 && slippage <= 1000,\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SorpheraRouter.sol:110:9\n    │\n110 │         emit AcquisitionForwarded(msg.sender, p, ids, refund);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/Sorphera.sol:258:26\n    │\n258 │         (bool active,) = coordinator.s_provingKeys(c.keyHash);\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/Sorphera.sol:259:67\n    │\n259 │         (uint16 minimumConfirmations, uint32 maximumGas,,,,,,,) = coordinator.s_config();\n    │                                                                   ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/Sorphera.sol:288:17\n    │\n288 │         require(present, \"Sorphera: VRF consumer missing\");\n    │                 ━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/Sorphera.sol:280:13\n    │\n280 │             coordinator.getSubscription(c.subscription);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `entered` is updated\n    ╭▸ src/SorpheraRouter.sol:123:26\n    │\n123 │         uint256 amount = r.claimAccruedTokens(minOut);\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/SorpheraRouter.sol:116:9\n    │\n116 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SorpheraRouter.sol:119:31\n    │\n119 │         require(minOut > 0 && block.timestamp <= deadline, \"Sorphera: reward bounds\");\n    │                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SorpheraRouter.sol:126:9\n    │\n126 │         emit BuilderRewardClaimed(address(r), allowance, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `latestRound` is updated\n    ╭▸ src/Sorphera.sol:320:13\n    │\n320 │ ┏             factory.create(\n321 │ ┃                 game,\n322 │ ┃                 id,\n323 │ ┃                 SorpheraVault.Settings(rules.maxFee, rules.maxTotal, rules.minValue, rules.slippage, cutoff)\n324 │ ┃             )\n    │ ┗━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Sorphera.sol:310:17\n    │\n310 │         require(block.timestamp >= cutoff - WEEK, \"Sorphera: window not started\");\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Sorphera.sol:332:9\n    │\n332 │ ┏         emit RoundOpened(\n333 │ ┃             game, id, r.group, r.vault, r.start, cutoff, r.earliestDraw, r.settlementDeadline, r.price\n334 │ ┃         );\n    │ ┗━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/Sorphera.sol:360:9\n    │\n360 │         SorpheraVault(payable(r.vault)).fund{value: msg.value - fee}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Sorphera.sol:340:57\n    │\n340 │             salesEnabled && r.status == Status.Sales && block.timestamp >= r.start\n    │                                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Sorphera.sol:341:20\n    │\n341 │                 && block.timestamp < r.cutoff,\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n  ╭▸ src/lib/Balls.sol:7:9\n  │\n7 │         require(bonus >= 1 && bonus <= 5, \"Sorphera: bonus\");\n  │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n  │\n  ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/lib/Balls.sol:14:9\n   │\n14 │         require(a[0] != a[1] && a[1] != a[2], \"Sorphera: distinct balls\");\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Sorphera.sol:365:44\n    │\n365 │         return r.status == Status.Sales && block.timestamp < r.cutoff;\n    │                                            ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Sorphera.sol:370:45\n    │\n370 │         require(r.status == Status.Sales && block.timestamp >= r.cutoff, \"Sorphera: cannot close\");\n    │                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `groups` is updated\n    ╭▸ src/Sorphera.sol:417:29\n    │\n417 │             bool reviewed = v.reviewCancellation(r.settlementDeadline);\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `entered` is updated\n    ╭▸ src/Sorphera.sol:407:9\n    │\n407 │         v.syncETH();\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `entered` is updated\n    ╭▸ src/Sorphera.sol:440:21\n    │\n440 │           requestId = coordinator.requestRandomWords(\n    │ ┏━━━━━━━━━━━━━━━━━━━━━┛\n441 │ ┃             IVRF.Request(\n442 │ ┃                 c.keyHash,\n443 │ ┃                 c.subscription,\n    ‡ ┃\n449 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `entered` is updated\n    ╭▸ src/SorpheraRouter.sol:132:9\n    │\n132 │         IRewards(rewardsForPool[p]).withdrawTokenBuyAllowanceAsETH();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Sorphera.sol:413:49\n    │\n413 │         if (game == 1 && r.eligibleNFTs == 0 && block.timestamp >= r.settlementDeadline) {\n    │                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/SorpheraRouter.sol:129:87\n    │\n129 │     function recoverBuilderAllowance(address p, address recipient) external onlyOwner nonReentrant {\n    │                                                                                       ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Sorphera.sol:425:13\n    │\n425 │             block.timestamp >= r.earliestDraw && v.pending() == 0 && v.budget() == 0 && v.refundCredit() == 0,\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SorpheraRouter.sol:132:9\n    │\n132 │         IRewards(rewardsForPool[p]).withdrawTokenBuyAllowanceAsETH();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Sorphera.sol:553:9\n    │\n553 │         emit Rollover(game, id, r.group, g.cash, g.nftCount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Sorphera.sol:565:9\n    │\n565 │         emit Cancelled(game, id, g.cash, r.sold);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Sorphera.sol:587:13\n    │\n587 │             emit DustCarried(game, currentGroup[game], dust);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Sorphera.sol:433:9\n    │\n433 │         emit DrawRequested(game, id, r.requestId, r.frozenNFTs);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Sorphera.sol:447:40\n    │\n447 │                 abi.encodeWithSelector(bytes4(keccak256(\"VRF ExtraArgsV1\")), c.nativePayment)\n    │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Sorphera.sol:460:9\n    │\n460 │         emit TieBreakRequested(game, id, r.tieBreakRequestId);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/Sorphera.sol:581:19\n    │\n581 │         g.cash -= share * g.divisor;\n    │                   ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/Sorphera.sol:625:13\n    │\n625 │             amount += delta;\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Sorphera.sol:630:9\n    │\n630 │         emit Claimed(game, g.terminalRound, ids[0], recipient, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/Sorphera.sol:598:9\n    │\n598 │         require(terminal != 0, \"Sorphera: prize not finalized\");\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/Sorphera.sol:601:9\n    │\n601 │         require(t.player == claimant && claimant != address(0), \"Sorphera: ticket owner\");\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/Sorphera.sol:603:13\n    │\n603 │ ┏             require(\n604 │ ┃                 t.combination == r.winningKey && (game == 0 || ticket == r.winningTicket),\n605 │ ┃                 \"Sorphera: not winning ticket\"\n606 │ ┃             );\n    │ ┗━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/Sorphera.sol:608:13\n    │\n608 │             require(r.status == Status.Cancelled, \"Sorphera: prize state\");\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/Sorphera.sol:633:85\n    │\n633 │     function withdrawOperator(address recipient, uint256 amount) external onlyOwner nonReentrant {\n    │                                                                                     ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Sorphera.sol:638:9\n    │\n638 │         emit OperatorWithdrawal(recipient, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":13369,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 8 tests for test/SorpheraReviewRegression.t.sol:SorpheraReviewRegressionTest\n[PASS] testFrozenReserveRejectsRequestBeforeAcceptanceThenTopupAllowsSameRound() (gas: 5189051)\n[PASS] testHostileOwnerOfCannotExhaustCancellationSweepGas() (gas: 5120692)\n[PASS] testInsufficientCallbackGasIsBilledAndCannotReplaceAcceptedDraw() (gas: 4311510)\n[PASS] testLargePendingCancellationReviewCommitsBoundedProgress() (gas: 21096668)\n[PASS] testPermanentTieNonfulfillmentLocksUntilOriginalDelayedCallback() (gas: 6554190)\n[PASS] testPredeadlineCustodyInLastCancellationBatchPreservesDraw() (gas: 24022019)\n[PASS] testReservePermissionsCurrencyAndConsumerRecheckedBeforeRequest() (gas: 4388171)\n[PASS] testUnreconciledPredeadlineCustodyCannotBeFrontRunByCancellation() (gas: 5463542)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 26.31ms (24.44ms CPU time)\n\nRan 6 tests for test/SorpheraHardening.t.sol:SorpheraHardeningTest\n[PASS] testChangedChainStopsSalesAndRequests() (gas: 3907292)\n[PASS] testChangedHelperPermissionsStopNewRiskButPreserveCashClaims() (gas: 5035732)\n[PASS] testConstructorDiagnosticsEmptyChainAndUnresolvedDependencies() (gas: 6162)\n[PASS] testIndependentTieBreakLocksPrizesFeesAndNextRound() (gas: 6895025)\n[PASS] testProductionRejectsSimulationAndIncorrectGasLane() (gas: 150279)\n[PASS] testUnavailableInventoryDoesNotConsumeBudget() (gas: 5120931)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 155.21ms (5.48ms CPU time)\n\nRan 40 tests for test/Sorphera.t.sol:SorpheraTest\n[PASS] testApplicationSizeAndNoEscapeOpcodes() (gas: 17882420)\n[PASS] testBlackoutSlippageBudgetDeadlineAndFailedAcquireRollback() (gas: 4258887)\n[PASS] testBuilderAttributionOverpaymentAndPurchaserRewardsSeparated() (gas: 6141753)\n[PASS] testCallbackAuthenticationBindingDuplicateNoRerollOrCancellation() (gas: 4609800)\n[PASS] testCallbacksOutOfOrderAreBoundToSeparateGamesAndZeroWordValid() (gas: 10275859)\nLogs:\n  tie-break finalize gas: 224081\n\n[PASS] testCustodyBeforeDeadlineCountsEvenWhenReconciledAfter() (gas: 6279664)\nLogs:\n  tie-break finalize gas: 224081\n\n[PASS] testDelayedCashoutNFTGameRemainsIncidentalETHAndCancels() (gas: 4935161)\n[PASS] testETHForcedNFTCustodyIsReservedForWinners() (gas: 5958457)\n[PASS] testETHOneMatchFullPrizeAndOperatorReserve() (gas: 5950401)\n[PASS] testETHRolloverOldTicketsExpireCarryNeverSpent() (gas: 9384043)\n[PASS] testExpiredAcquisitionBlocksDrawUntilReconciled() (gas: 5389494)\n[PASS] testFactoryOnlyRegisteredLotteryCreatesVaults() (gas: 3560174)\n[PASS] testFixedETHSettlementEvenAfterExclusiveWindow() (gas: 4550787)\n[PASS] testForcedDeliveryAfterDeadlineStillCancels() (gas: 5236103)\n[PASS] testFuzzConservationAfterFWAChargesCashoutAndLateFunds(uint8,uint64) (runs: 256, μ: 6973465, ~: 6585259)\nLogs:\n  Bound result 18\n\n[PASS] testFuzzConservationUnusedBudget(uint8,uint64) (runs: 256, μ: 6655800, ~: 5658445)\nLogs:\n  Bound result 62\n\n[PASS] testFuzzSamplingRangeDistinctAndReplay(uint256) (runs: 256, μ: 33325, ~: 33288)\n[PASS] testLargeSalesConstantFinalizationAndPartialLargeInventory() (gas: 188056746)\nLogs:\n  draw finalize gas, 2000 tickets / 104 NFTs: 138688\n  tie-break finalize gas: 224081\n\n[PASS] testLateAllocationAfterCancellationSettlesToDivisibleETHRefund() (gas: 5392703)\n[PASS] testLateETHRecoveryGoesToOriginalWinnerNotNextRound() (gas: 8265490)\n[PASS] testLateNFTDoesNotRescueCancelledRoundAndSharedRecovery() (gas: 5644771)\n[PASS] testMultipleETHMatchesDuplicatesAndRounding() (gas: 5012549)\n[PASS] testNFTCancellationFeesPartialRefundAndLateRecovery() (gas: 5397864)\n[PASS] testNFTFeeReserveCannotBeWithdrawnUntilSuccess() (gas: 6140014)\nLogs:\n  tie-break finalize gas: 224081\n\n[PASS] testNFTOneWinnerAllAssetsAndFunds() (gas: 7977135)\n[PASS] testNFTRolloverInventoryAndResidualCannotRespin() (gas: 10237588)\n[PASS] testNFTTieBreakIncludesEveryDuplicateAndIsDomainSeparated() (gas: 7113122)\n[PASS] testNFTTransferFailuresAreIsolatedAndRetryable() (gas: 7512514)\n[PASS] testNoLateEntryAndBoundedTickets() (gas: 3635338)\n[PASS] testPermanentlyStuckNFTNeverFreezesETHGameAndLateRecoveryFollowsWinner() (gas: 9714225)\n[PASS] testRejectedETHAndReentrancyRetainLiabilities() (gas: 4977446)\n[PASS] testRewardHelperFailureRollsBackAllowanceAndClaims() (gas: 5071872)\n[PASS] testStartsDisabledUnconfiguredAndOwnerSettings() (gas: 5716865)\n[PASS] testStuckNFTCustodyRequiredAndRecoveryAfterWindow() (gas: 6816668)\n[PASS] testStuckOnlyNFTCancelsRoundAndLateRecoveryIsSharedByRefundCohort() (gas: 8999634)\n[PASS] testUnclaimedWinningsStayReservedAcrossNewRoundAndWithdrawals() (gas: 9446984)\n[PASS] testUnorderedNumbersIndexedNoTicketTransfer() (gas: 4110411)\n[PASS] testUnwithdrawnFWARefundPreventsDraw() (gas: 4786111)\n[PASS] testWeeklyGamesIndependentFreezeAndPauseClaims() (gas: 8616524)\n[PASS] testZeroTicketsSkipRandomnessAndSeparateGameDoesNotBlock() (gas: 10133660)\nSuite result: ok. 40 passed; 0 failed; 0 skipped; finished in 157.75ms (473.78ms CPU time)\n\nRan 17 tests for test/SorpheraAdversarial.t.sol:SorpheraAdversarialTest\n[PASS] testBadCallbackShapeUnknownRequestAndFutureRulesCannotMutateActiveRound() (gas: 7974384)\n[PASS] testCancellationAccumulatesSubTicketLateRecoveriesWithoutFees() (gas: 4776016)\n[PASS] testDuplicateClaimIdsNeverMultiplyPayoutAndMixedOwnerBatchRollsBack() (gas: 4974642)\n[PASS] testEmptyAndOversizedMaintenanceAndClaimBatchesReject() (gas: 3865150)\n[PASS] testEmptyAndUnderpaidAndOverpaidSalesAreAtomic() (gas: 3596425)\n[PASS] testEveryCombinationHasUniqueKeyAndAllSixOrdersMatch() (gas: 355211609)\n[PASS] testForgedReceiptStampCannotRescueLateDeliveryAndDeadlineBoundaryIsStrict() (gas: 5667527)\n[PASS] testFuzzPriceEdgesKeepExactSplitAndFullBatchRefund(uint256,uint8) (runs: 1000, μ: 6360874, ~: 5426907)\nLogs:\n  Bound result 1\n  Bound result 9\n\n[PASS] testFuzzReceiptTimeNotReconcileTimeDecidesCancellation(uint256,bool) (runs: 512, μ: 5375217, ~: 5414908)\nLogs:\n  Bound result 1714542\n\n[PASS] testInvalidTicketAtEndRollsBackEntireBatchAndBothAccounts() (gas: 8835365)\n[PASS] testMaximumTicketPriceFullBatchRefundsAllUnspentFunds() (gas: 12006836)\nLogs:\n  Bound result 10000000000000000000\n  Bound result 100\n\n[PASS] testMinimumTicketPriceSingleEntryRefundsAllUnspentFunds() (gas: 4164222)\nLogs:\n  Bound result 1\n  Bound result 1\n\n[PASS] testNFTReceiverCannotReenterClaimAnotherAsset() (gas: 7176177)\n[PASS] testPauseBlocksSaleButPreservesPermissionlessSettlement() (gas: 6453166)\n[PASS] testSecondLotteryCannotValidateAgainstBoundFactoryOrCreateVaults() (gas: 8794940)\n[PASS] testStuckAssetRecoveredIntoRolledRoundJoinsCarryoverForNextWinnerOnly() (gas: 11704233)\n[PASS] testVaultPrivilegeAndCrossRoundRequestBoundaries() (gas: 7764256)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 286.57ms (619.11ms CPU time)\n\nRan 2 tests for test/SepoliaCanary.t.sol:SepoliaCanaryTest\n[PASS] testControlledDependenciesAndNoPublicSalesWindow() (gas: 4096878)\n[PASS] testFullCoverageTwoGamesDelayedDrawAndIndependentTieClaims() (gas: 10701817)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 570.47ms (571.44ms CPU time)\n\nRan 2 tests for test/SorpheraInvariant.t.sol:SorpheraInvariantTest\n[PASS]\nSorpheraInvariantTest invariants:\n[PASS] invariant_externalFlowsConserveETH\n[PASS] invariant_gameAssetsNeverCrossSubsidize\n[PASS] invariant_liabilitiesEqualReservedFeesCashAndUnpaidTickets\n[PASS] invariant_purchaserAndBuilderTokensStayConserved\n[PASS] invariant_roundOutcomesFollowFixedRules\n[PASS] invariant_vaultRequestsAndCustodyRemainBacked\n SorpheraInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------+-----------------+-------+---------+----------╮\n| Contract        | Selector        | Calls | Reverts | Discards |\n+================================================================+\n| SorpheraHandler | acquire         | 1009  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | advance         | 984   | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | builderReward   | 1036  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | buy             | 2027  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | claim           | 1011  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | claimNFT        | 1067  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | forceSettlement | 1034  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | pause           | 1008  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | queueTokens     | 995   | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | receiveTokens   | 1070  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | recoverStuck    | 1019  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | resolve         | 1034  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | rewardEpoch     | 1021  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | syncOrDonate    | 1042  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | withdraw        | 1027  | 0       | 0        |\n╰-----------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 0\n  Bound result 8\n  Bound result 0\n  Bound result 1\n  Bound result 4\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 20000000000000000\n  Bound result 2\n  Bound result 20000000000000000\n  Bound result 3\n  Bound result 2\n  Bound result 0\n  Bound result 1\n  Bound result 103\n  Bound result 1\n  Bound result 1\n  Bound result 101\n  Bound result 1\n  Bound result 1\n  Bound result 864000\n  Bound result 0\n  Bound result 2\n  Bound result 2\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 2\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 3\n  Bound result 2\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 2\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1706\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 4999999999999999\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 4\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 6\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 2226134084736095\n  Bound result 0\n  Bound result 1\n  Bound result 4116\n  Bound result 1\n  Bound result 1\n  Bound result 8330545749137105\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 2\n  Bound result 1\n  Bound result 1\n  Bound result 2\n  Bound result 1\n  Bound result 1\n  Bound result 3247843824347649\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 0\n\n[PASS] testHandlerPinnedLifecycleExercisesClaimsAndLateRecoveries() (gas: 15403156)\nLogs:\n  Bound result 0\n  Bound result 8\n  Bound result 0\n  Bound result 1\n  Bound result 4\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 20000000000000000\n  Bound result 2\n  Bound result 20000000000000000\n  Bound result 3\n  Bound result 2\n  Bound result 0\n  Bound result 1\n  Bound result 103\n  Bound result 1\n  Bound result 1\n  Bound result 101\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 17\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 4000000000000000\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 8\n  Bound result 2\n  Bound result 1\n  Bound result 1\n  Bound result 4\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 5\n  Bound result 1\n  Bound result 1\n  Bound result 2\n  Bound result 1\n  Bound result 1\n  Bound result 2\n  Bound result 1\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 13.29s (13.29s CPU time)\n\nRan 6 test suites in 13.29s (14.48s CPU time): 75 tests passed, 0 failed, 0 skipped (75 total tests)\n","passed":true},{"durationMs":60,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"Sorphera.acceptOwner()\",\"Sorphera.buy(uint8,uint256,(uint8[3],uint8)[])\",\"Sorphera.claimETH(uint8,uint256,uint256[],address)\",\"Sorphera.close(uint8,uint256)\",\"Sorphera.configureRandomness((uint256,bytes32,uint16,uint32,bool))\",\"Sorphera.configureRules(uint8,(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256))\",\"Sorphera.configureVRFReserve(uint96,uint96)\",\"Sorphera.credit(uint8,uint256)\",\"Sorphera.finalize(uint8,uint256)\",\"Sorphera.finalizeTieBreak(uint8,uint256)\",\"Sorphera.openRound(uint8)\",\"Sorphera.proposeOwner(address)\",\"Sorphera.rawFulfillRandomWords(uint256,uint256[])\",\"Sorphera.recordNFT(uint8,uint256,uint256)\",\"Sorphera.requestDraw(uint8,uint256)\",\"Sorphera.requestTieBreak(uint8,uint256)\",\"Sorphera.setSalesEnabled(bool)\",\"Sorphera.validateLaunch()\",\"Sorphera.withdrawOperator(address,uint256)\",\"SorpheraRouter.acceptOwner()\",\"SorpheraRouter.acquire(address,uint256,uint256,uint256,uint256,uint256)\",\"SorpheraRouter.claimBuilderRewards(address,address,uint256,uint256)\",\"SorpheraRouter.configure(address,address)\",\"SorpheraRouter.proposeOwner(address)\",\"SorpheraRouter.receive()\",\"SorpheraRouter.recoverBuilderAllowance(address,address)\",\"SorpheraVault.acquire(uint256,uint256)\",\"SorpheraVault.claimEpochRewards(uint256[])\",\"SorpheraVault.claimNFTs(uint256,uint256[],address)\",\"SorpheraVault.claimTokens(uint256,address,uint256)\",\"SorpheraVault.deliver(uint256,address)\",\"SorpheraVault.fund()\",\"SorpheraVault.onERC721Received(address,address,uint256,bytes)\",\"SorpheraVault.process(uint256)\",\"SorpheraVault.receive()\",\"SorpheraVault.reconcile(uint256[])\",\"SorpheraVault.recoverNFT(uint256)\",\"SorpheraVault.recoverRefund()\",\"SorpheraVault.reviewCancellation(uint256)\",\"SorpheraVault.settle(uint256)\",\"SorpheraVault.syncETH()\",\"SorpheraVaultFactory.acceptOwner()\",\"SorpheraVaultFactory.create(uint8,uint256,(uint256,uint256,uint256,uint256,uint256))\",\"SorpheraVaultFactory.proposeOwner(address)\",\"SorpheraVaultFactory.setLottery(address)\"],\"files\":{\".gitignore\":3,\"README.md\":95,\"deployments/canary.example.json\":26,\"deployments/canary.schema.json\":32,\"deployments/config.schema.json\":169,\"deployments/fork.json\":13,\"deployments/mainnet.json\":28,\"deployments/mainnet.launch.json\":27,\"deployments/sepolia.json\":28,\"docs/CANARY.md\":118,\"docs/CONTINUATION.md\":47,\"docs/DEPENDENCIES.md\":46,\"docs/DEPLOYMENT.md\":38,\"docs/IMD-SUPPORT-DRAFT.md\":7,\"docs/KEEPER.md\":72,\"docs/MAINNET.md\":46,\"docs/OPERATIONS.md\":60,\"docs/REHEARSAL.md\":43,\"docs/REVIEW-CONTINUATION.md\":79,\"docs/TESTING.md\":68,\"docs/TOOLING-REVIEW.md\":36,\"docs/continuation-validation.json\":174,\"docs/evidence/baseline.txt\":125,\"docs/evidence/build.txt\":834,\"docs/evidence/commands.json\":49,\"docs/evidence/compiler-settings.json\":8,\"docs/evidence/constructor-empty.txt\":31,\"docs/evidence/continuation/build.log\":858,\"docs/evidence/continuation/canary-dry-run.json\":30,\"docs/evidence/continuation/empty-constructor.log\":9,\"docs/evidence/continuation/forge-tests.log\":154,\"docs/evidence/continuation/keeper-fork-simulation.json\":2408,\"docs/evidence/continuation/keeper-fork-simulation.log\":83,\"docs/evidence/continuation/mainnet-fork.log\":22,\"docs/evidence/continuation/runtime-simulation.json\":17,\"docs/evidence/continuation/sepolia-fork.log\":14,\"docs/evidence/continuation/source-content.json\":97,\"docs/evidence/continuation/tooling-tests.log\":52,\"docs/evidence/fmt-check.txt\":0,\"docs/evidence/gas-scaling.txt\":252,\"docs/evidence/imd-launch-961.json\":168,\"docs/evidence/imd-launch-capabilities.json\":144,\"docs/evidence/integration-fmt-check.txt\":0,\"docs/evidence/mainnet-builder-trace.txt\":853,\"docs/evidence/mainnet-fork.txt\":22,\"docs/evidence/offline-tests.txt\":134,\"docs/evidence/sepolia-fork-and-constructor.txt\":121,\"docs/reference/FWAV2.sepolia.abi.json\":1,\"docs/reference/FWAV2Rewards.sepolia.abi.json\":1,\"docs/reference/mainnet/buyback.runtime.hex\":1,\"docs/reference/mainnet/coordinator.abi.json\":1750,\"docs/reference/mainnet/coordinator.runtime.hex\":1,\"docs/reference/mainnet/floorOracle.abi.json\":1439,\"docs/reference/mainnet/floorOracle.runtime.hex\":1,\"docs/reference/mainnet/helper.abi.json\":591,\"docs/reference/mainnet/helper.runtime.hex\":1,\"docs/reference/mainnet/hook.abi.json\":1352,\"docs/reference/mainnet/hook.runtime.hex\":1,\"docs/reference/mainnet/notifier.runtime.hex\":1,\"docs/reference/mainnet/permit2.abi.json\":901,\"docs/reference/mainnet/permit2.runtime.hex\":1,\"docs/reference/mainnet/pool.abi.json\":2979,\"docs/reference/mainnet/pool.runtime.hex\":1,\"docs/reference/mainnet/poolManager.abi.json\":1366,\"docs/reference/mainnet/poolManager.runtime.hex\":1,\"docs/reference/mainnet/rewards.abi.json\":1623,\"docs/reference/mainnet/rewards.runtime.hex\":1,\"docs/reference/mainnet/service.runtime.hex\":1,\"docs/reference/mainnet/snapshot.json\":956,\"docs/reference/mainnet/source-bindings.json\":29,\"docs/reference/mainnet/token.abi.json\":1214,\"docs/reference/mainnet/token.runtime.hex\":1,\"docs/reference/sepolia-readback.json\":46,\"docs/reference/verification.json\":21,\"docs/validation.json\":91,\"foundry.toml\":20,\"frontend/README.md\":11,\"frontend/abi/Sorphera.json\":2197,\"frontend/abi/SorpheraRouter.json\":468,\"frontend/abi/SorpheraVault.json\":1071,\"frontend/abi/SorpheraVaultFactory.json\":245,\"frontend/bytecode-sizes.json\":18,\"frontend/configuration.json\":62,\"frontend/deployment.json\":29,\"integration/Sepolia.t.sol\":42,\"integration/mainnet/Mainnet.t.sol\":425,\"integration/rehearsal/Constructor.t.sol\":61,\"launch.json\":9,\"ops/keeper.example.json\":27,\"ops/keeper.schema.json\":33,\"ops/sorphera-keeper.service\":33,\"script/CanaryDependencies.sol\":266,\"script/Operations.s.sol\":149,\"script/Rehearse.s.sol\":43,\"script/SepoliaCanary.s.sol\":32,\"src/Sorphera.sol\":664,\"src/SorpheraRouter.sol\":136,\"src/SorpheraVault.sol\":325,\"src/SorpheraVaultFactory.sol\":39,\"src/interfaces/External.sol\":126,\"src/lib/Balls.sol\":46,\"src/lib/Networks.sol\":23,\"src/lib/RewardTransfer.sol\":54,\"src/lib/Security.sol\":47,\"test/README.md\":7,\"test/SepoliaCanary.t.sol\":186,\"test/Sorphera.t.sol\":988,\"test/SorpheraAdversarial.t.sol\":458,\"test/SorpheraHardening.t.sol\":150,\"test/SorpheraInvariant.t.sol\":666,\"test/SorpheraReviewRegression.t.sol\":259,\"test/helpers/SimulatedSorphera.sol\":14,\"test/helpers/SorpheraFixture.sol\":128,\"test/mocks/ExternalMocks.sol\":416,\"tools/canary.py\":405,\"tools/contracts.py\":50,\"tools/export.py\":19,\"tools/keeper.py\":302,\"tools/keeper_smoke.py\":230,\"tools/preflight.py\":56,\"tools/read-only-rpc.py\":36,\"tools/runtime.py\":366,\"tools/snapshot-mainnet.py\":65,\"tools/source_manifest.py\":32,\"tools/test_canary.py\":221,\"tools/test_keeper.py\":302,\"tools/test_runtime.py\":394},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":6792,"exitCode":0,"name":"slither","output":"[high/medium] arbitrary-send-eth at src/SorpheraVault.sol:249: SorpheraVault._sync() (src/SorpheraVault.sol#249-257) sends eth to arbitrary user\n[medium/medium] divide-before-multiply at src/Sorphera.sol:577: Sorphera._distribute(uint8,uint256) (src/Sorphera.sol#577-589) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/Sorphera.sol:397: Sorphera.isCancelled(uint8,uint256) (src/Sorphera.sol#397-399) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/Sorphera.sol:577: Sorphera._distribute(uint8,uint256) (src/Sorphera.sol#577-589) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/Sorphera.sol:402: Reentrancy in Sorphera.requestDraw(uint8,uint256) (src/Sorphera.sol#402-434):\n[medium/medium] reentrancy-no-eth at src/Sorphera.sol:402: Reentrancy in Sorphera.requestDraw(uint8,uint256) (src/Sorphera.sol#402-434):\n[medium/medium] reentrancy-no-eth at src/Sorphera.sol:402: Reentrancy in Sorphera.requestDraw(uint8,uint256) (src/Sorphera.sol#402-434):\n[medium/medium] reentrancy-no-eth at src/SorpheraVault.sol:227: Reentrancy in SorpheraVault.recoverNFT(uint256) (src/SorpheraVault.sol#227-233):\n[medium/medium] reentrancy-no-eth at src/SorpheraVault.sol:277: Reentrancy in SorpheraVault.claimNFTs(uint256,uint256[],address) (src/SorpheraVault.sol#277-306):\n[medium/medium] reentrancy-no-eth at src/SorpheraVault.sol:186: Reentrancy in SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#186-225):\n[medium/medium] reentrancy-no-eth at src/Sorphera.sol:297: Reentrancy in Sorphera.openRound(uint8) (src/Sorphera.sol#297-335):\n[medium/medium] reentrancy-no-eth at src/SorpheraVault.sol:144: Reentrancy in SorpheraVault.settle(uint256) (src/SorpheraVault.sol#144-158):\n[medium/medium] reentrancy-no-eth at src/SorpheraVault.sol:174: Reentrancy in SorpheraVault.reviewCancellation(uint256) (src/SorpheraVault.sol#174-184):\n[medium/medium] uninitialized-local at src/Sorphera.sol:620: Sorphera.claimETH(uint8,uint256,uint256[],address).amount (src/Sorphera.sol#620) is a local variable never initialized\n[medium/medium] uninitialized-local at src/SorpheraVault.sol:196: SorpheraVault._reconcile(uint256).held (src/SorpheraVault.sol#196) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Sorphera.sol:284: Sorphera._checkFunding(Sorphera.RandomConfig,uint96).present (src/Sorphera.sol#284) is a local variable never initialized\n[medium/medium] unused-return at src/Sorphera.sol:255: Sorphera._validateNetwork(Sorphera.RandomConfig) (src/Sorphera.sol#255-267) ignores return value by (active,None) = coordinator.s_provingKeys(c.keyHash) (src/Sorphera.sol#258)\n[medium/medium] unused-return at src/Sorphera.sol:277: Sorphera._checkFunding(Sorphera.RandomConfig,uint96) (src/Sorphera.sol#277-289) ignores return value by (link,nativeBalance,None,None,consumers) = coordinator.getSubscription(c.subscription) (src/Sorphera.sol#279-280)\n[medium/medium] unused-return at src/SorpheraRouter.sol:129: SorpheraRouter.recoverBuilderAllowance(address,address) (src/SorpheraRouter.sol#129-134) ignores return value by IRewards(rewardsForPool[p]).withdrawTokenBuyAllowanceAsETH() (src/SorpheraRouter.sol#132)\n[medium/medium] unused-return at src/SorpheraVault.sol:160: SorpheraVault.process(uint256) (src/SorpheraVault.sol#160-163) ignores return value by pool.processAcquisitions(count) (src/SorpheraVault.sol#162)\n[medium/medium] unused-return at src/SorpheraVault.sol:259: SorpheraVault.claimEpochRewards(uint256[]) (src/SorpheraVault.sol#259-264) ignores return value by rewards.claimEpochTokens(epochs) (src/SorpheraVault.sol#262)\n[medium/medium] unused-return at src/SorpheraVault.sol:239: SorpheraVault.recoverRefund() (src/SorpheraVault.sol#239-243) ignores return value by pool.withdrawAcquisitionRefund() (src/SorpheraVault.sol#241)\n[medium/medium] unused-return at src/Sorphera.sol:255: Sorphera._validateNetwork(Sorphera.RandomConfig) (src/Sorphera.sol#255-267) ignores return value by (minimumConfirmations,maximumGas,None,None,None,None,None,None,None) = coordinator.s_config() (src/Sorphera.sol#259)\n[low/medium] events-maths at src/SorpheraVault.sol:103: SorpheraVault.fund() (src/SorpheraVault.sol#103-105) should emit an event for: \n[low/medium] missing-zero-check at src/SorpheraVault.sol:82: SorpheraVault.constructor(address,SorpheraRouter,uint8,uint256,SorpheraVault.Settings).lottery_ (src/SorpheraVault.sol#82) lacks a zero-check on :\n[low/medium] calls-loop at src/SorpheraVault.sol:186: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#186-225) has external calls inside a loop: l = pool.listings(a.listingId) (src/SorpheraVault.sol#192)\n[low/medium] calls-loop at src/SorpheraVault.sol:186: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#186-225) has external calls inside a loop: holder = IERC721(l.collection).ownerOf{gas: 50000}(l.tokenId) (src/SorpheraVault.sol#199-201)\n[low/medium] calls-loop at src/SorpheraVault.sol:186: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#186-225) has external calls inside a loop: ISorphera(lottery).recordNFT(game,round,securedAt) (src/SorpheraVault.sol#209)\n[low/medium] calls-loop at src/SorpheraVault.sol:186: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#186-225) has external calls inside a loop: l = pool.listings(a.listingId) (src/SorpheraVault.sol#192)\n[low/medium] calls-loop at src/SorpheraVault.sol:186: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#186-225) has external calls inside a loop: a = pool.acquisitions(id) (src/SorpheraVault.sol#188)\n[low/medium] calls-loop at src/SorpheraVault.sol:186: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#186-225) has external calls inside a loop: holder = IERC721(l.collection).ownerOf{gas: 50000}(l.tokenId) (src/SorpheraVault.sol#199-201)\n[low/medium] calls-loop at src/SorpheraVault.sol:186: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#186-225) has external calls inside a loop: pool.stuckNFTRecipient(a.listingId) == address(this) (src/SorpheraVault.sol#211)\n[low/medium] calls-loop at src/SorpheraVault.sol:186: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#186-225) has external calls inside a loop: a = pool.acquisitions(id) (src/SorpheraVault.sol#188)\n[low/medium] calls-loop at src/SorpheraVault.sol:277: SorpheraVault.claimNFTs(uint256,uint256[],address) (src/SorpheraVault.sol#277-306) has external calls inside a loop: this.deliver{gas: 500000}(index,recipient) (src/SorpheraVault.sol#299-304)\n[low/medium] calls-loop at src/SorpheraVault.sol:186: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#186-225) has external calls inside a loop: ISorphera(lottery).recordNFT(game,round,securedAt) (src/SorpheraVault.sol#209)\n[low/medium] calls-loop at src/SorpheraVault.sol:186: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#186-225) has external calls inside a loop: pool.stuckNFTRecipient(a.listingId) == address(this) (src/SorpheraVault.sol#211)\n[low/medium] calls-loop at src/SorpheraVault.sol:107: SorpheraVault.acquire(uint256,uint256) (src/SorpheraVault.sol#107-139) has external calls inside a loop: acquisition = pool.acquisitions(ids[i]) (src/SorpheraVault.sol#132)\n[low/medium] reentrancy-benign at src/Sorphera.sol:402: Reentrancy in Sorphera.requestDraw(uint8,uint256) (src/Sorphera.sol#402-434):\n[low/medium] reentrancy-benign at src/Sorphera.sol:436: Reentrancy in Sorphera._request(uint8,uint256,bool) (src/Sorphera.sol#436-452):\n[low/medium] reentrancy-benign at src/SorpheraVault.sol:107: Reentrancy in SorpheraVault.acquire(uint256,uint256) (src/SorpheraVault.sol#107-139):\n[low/medium] reentrancy-benign at src/Sorphera.sol:297: Reentrancy in Sorphera.openRound(uint8) (src/Sorphera.sol#297-335):\n[low/medium] reentrancy-benign at src/SorpheraVault.sol:186: Reentrancy in SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#186-225):\n[low/medium] timestamp at src/Sorphera.sol:297: Sorphera.openRound(uint8) (src/Sorphera.sol#297-335) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:591: Sorphera.entitlement(uint8,uint256,uint256,address) (src/Sorphera.sol#591-611) uses timestamp for comparisons\n[low/medium] timestamp at src/SorpheraRouter.sol:113: SorpheraRouter.claimBuilderRewards(address,address,uint256,uint256) (src/SorpheraRouter.sol#113-127) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:455: Sorphera.requestTieBreak(uint8,uint256) (src/Sorphera.sol#455-461) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:387: Sorphera.recordNFT(uint8,uint256,uint256) (src/Sorphera.sol#387-395) uses timestamp for comparisons\n[low/medium] timestamp at src/SorpheraVault.sol:249: SorpheraVault._sync() (src/SorpheraVault.sol#249-257) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:577: Sorphera._distribute(uint8,uint256) (src/Sorphera.sol#577-589) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:488: Sorphera.finalize(uint8,uint256) (src/Sorphera.sol#488-505) uses timestamp for comparisons\n[low/medium] timestamp at src/SorpheraVault.sol:186: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#186-225) uses timestamp for comparisons\n[low/medium] timestamp at src/SorpheraVault.sol:107: SorpheraVault.acquire(uint256,uint256) (src/SorpheraVault.sol#107-139) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:402: Sorphera.requestDraw(uint8,uint256) (src/Sorphera.sol#402-434) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:363: Sorphera.acquisitionOpen(uint8,uint256) (src/Sorphera.sol#363-366) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:397: Sorphera.isCancelled(uint8,uint256) (src/Sorphera.sol#397-399) uses timestamp for comparisons\n[low/medium] timestamp at src/SorpheraRouter.sol:91: SorpheraRouter.acquire(address,uint256,uint256,uint256,uint256,uint256) (src/SorpheraRouter.sol#91-111) uses timestamp for comparisons\n[low/medium] timestamp at src/SorpheraVault.sol:174: SorpheraVault.reviewCancellation(uint256) (src/SorpheraVault.sol#174-184) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:368: Sorphera.close(uint8,uint256) (src/Sorphera.sol#368-373) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:337: Sorphera.buy(uint8,uint256,Sorphera.Pick[]) (src/Sorphera.sol#337-361) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:525: Sorphera.finalizeTieBreak(uint8,uint256) (src/Sorphera.sol#525-536) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:375: Sorphera.credit(uint8,uint256) (src/Sorphera.sol#375-383) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:463: Sorphera.rawFulfillRandomWords(uint256,uint256[]) (src/Sorphera.sol#463-482) uses timestamp for comparisons","passed":true},{"durationMs":408,"exitCode":0,"name":"aderyn","output":"[high] contract-locks-ether at src/SorpheraVault.sol:10: Contract locks Ether without a withdraw function\n[high] reentrancy-state-change at src/Sorphera.sol:247: Reentrancy: State change after external call (26 places)\n[high] storage-array-memory-edit at src/Sorphera.sol:246: Storage Array Edited with Memory (7 places)\n[low] centralization-risk at src/Sorphera.sol:12: Centralization Risk (13 places)\n[low] costly-loop at src/Sorphera.sol:353: Costly operations inside loop (6 places)\n[low] large-numeric-literal at src/Sorphera.sol:227: Large Numeric Literal (2 places)\n[low] literal-instead-of-constant at src/Sorphera.sol:208: Literal Instead of Constant (14 places)\n[low] non-reentrant-not-first at src/Sorphera.sol:633: `nonReentrant` is Not the First Modifier (4 places)\n[low] require-revert-in-loop at src/Sorphera.sol:353: Loop Contains `require`/`revert` (6 places)\n[low] state-change-without-event at src/SorpheraRouter.sol:129: State Change Without Event (4 places)\n[low] unchecked-return at src/Sorphera.sol:622: Unchecked Return (5 places)\n[low] uninitialized-local-variable at src/Sorphera.sol:285: Uninitialized Local Variable (4 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"be411c73c79b378a975d4c42797d0e7ffe3375190833ac18e061bf1fa94447c4","verifiedTreeHash":"b05bab360ebf837db7731602ef2be797c1cc3ba3","verifierVersion":"0.1.0+ad90ce4c"}]}