{"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":"a3707b79-51b8-4d04-9e32-3e786d91c1d9","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"361d5dd664bcc2c0e7478f47dd66aa81d014555b81797943c1e9a248f9b38b91","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"9ba9df1d206aacaf46ef75113eb59a8d9610e6b12afafec9d85edc64c2d39e36","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"f5f60da18f2339ab8bbd71b9457faefaa5013f75bfb0f6381cf45dd972e63392","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"5faa84b5f7861ab7f3b4f4ae2a5e991fe7a6043118aef88e140fee70668ccfc7","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"eb7a1889d02479302d23ff22a4cbc7d03656530fb08377d4695be65fb74f6904","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"d3ea1f9f9304aef872ef6fea395c4a7224508435ac929f0faf409c099becca01","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"},{"acceptedSubmissionHash":"1c2628524b1c64afac28e49c6883d8d14248765b9d81ba97c28d0a3edd0dfe3f","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"7114905b598a995d4924bec189605ee2ca6474b2759f24f86c0187acd24f788c","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Build Sorphera smart contracts on Ethereum Sepolia.\nTagline: Weekly ETH & NFT lottery ball jackpots. Powered by FWA.\nUse Sorphera branding throughout. Deliver contracts, Foundry tests and docs; website later. No new ERC20 or liquidity pool.\n\nGAMES\n\nSorphera ETH Jackpot and Sorphera NFT Jackpot each draw weekly (two draws total), with separate rounds, tickets, schedules and accounting.\nEach nontransferable ticket enters one game/round. Default: 0.005 Sepolia ETH. Players pick 3 distinct unordered numbers from 1-20 and 1 bonus from 1-5. Duplicate combinations and multiple tickets per wallet are allowed.\nUncapped sales; bounded transaction batches. Configure seven-day windows, separate initial cutoffs and earliest draw-request times. Freeze price, fees, number format and deadlines before first sale; changes affect future rounds only.\n\nMONEY\n\nSplit new sales: 10% operator fee, 90% round-specific FWA acquisition budget, including FWA acquisition/VRF charges. Operator resources separately fund lottery VRF and gas.\nIsolate fees, budgets, pending requests, refunds, carryovers and winner liabilities by game/round. No cross-subsidies or spending reserved assets. Charge fees once on new sales, never on prizes, refunds or carryovers.\nBuilder rewards belong to the company. Purchaser rewards/refunds are prizes. Late recoveries follow the originating round's winner, rollover or refund entitlements. Operator withdrawals cannot consume player assets.\n\nFWA\n\nVerify Sepolia addresses, ABIs and dependencies using:\n[Sepolia](https://www.fwa.fun/docs/v2-sepolia)\n[Builder rewards](https://www.fwa.fun/docs/builder-revenue)\n[Settlement](https://www.fwa.fun/docs/winning)\n[Reference contracts](https://github.com/adamlizek/fwa-examples)\nMake a builder router the immediate caller of FWAV2.acquire and a separate prize vault the purchaser. Preserve builder attribution and purchaser rights.\nRead live quotes, fees, settings and settlement windows; never hardcode 0.06 ETH/spin. Enforce budgets, slippage limits and deadlines. Attribute all requests, allocations, settlements and recoveries to game/round.\nRecover expired requests and overpayments. Claim builder rewards only against actual allowances; assume no fixed emissions or guaranteed revenue.\nPermissionless settlement enforces the game's fixed outcome. Document keepers, deadlines, missed windows and pending delivery. Verify NFT custody, not just notifications.\n\nETH JACKPOT\n\nUse its acquisition budget for FWA pulls; settle successful allocations to ETH.\nPrize = actual cashouts + unused budget + refunds + ETH carryover. Prize value is not guaranteed.\nAfter cutoff and reconciliation, draw numbers. Match all 3 main numbers and the bonus to qualify.\nOne match wins everything. Multiple matches split equally per winning ticket, including repeats per wallet. No match rolls the prize into the next ETH round. Old tickets expire; never buy pulls with carryover.\nUse claim-based payouts, reserve unpaid winnings and carry division dust forward.\n\nNFT JACKPOT\n\nUse its acquisition budget for FWA pulls; always choose \"keep the NFT\" and secure assets promptly in the vault. No voluntary cashouts, sales, relisting, substitution or cherry-picking. Track collection, token ID, acquisition, round and custody.\nUse the same selected numbers and exact-match rule:\n\n- No match: roll all inventory and residual prize funds into the next NFT round; old tickets expire.\n- One match: that ticket wins all inventory and residual prize funds.\n- Multiple matches: uniformly select ONE matching ticket to win everything, using a domain-separated stream of verified round randomness. Each matching ticket has equal probability, including repeats per wallet. Disclose the tie-break.\n\nAdd acquisitions to carryover; never respin or re-fee carryover. Freeze secured inventory before requesting lottery randomness.\nAllow individual/batched claims to the winner's nominated compatible recipient. Isolate transfer failures, prevent double claims and reserve unclaimed assets.\nUnexpected ETH recoveries remain incidental NFT-round prize funds; never label them NFTs or guarantee resale value.\nIf no NFT, including carryover, is secured by the published settlement deadline, cancel BEFORE requesting lottery randomness. Refund available unspent/recovered funds plus reserved operator fees pro rata to ticket holders; preserve late-recovery entitlements. Do not guarantee full refunds after third-party charges/losses. Hold this round's operator fees until success/cancellation is established.\n\nRANDOMNESS AND REPLAY\n\nUse independent Chainlink VRF for lottery draws, separate from FWA randomness. Bind callbacks to game, round and request ID. Uniformly sample 3 distinct main balls and 1 bonus. Store ordered balls for replay and normalized combinations for matching.\nClose sales and reconcile required prize accounting before requesting randomness, never before the published earliest request time. Specify settlement/VRF delay handling.\nNo rerolls, replacement requests, admin-selected results, post-cutoff purchases or outcome-dependent cancellation. Store randomness in callbacks; finalize and claim separately.\nExpose persistent state/events for schedules, closure, prizes, randomness, ordered results, match counts, tie-breaks, rollovers and claims for replay/reconnection.\nFrontend globe-ball animations show recorded results, never determine outcomes. Document that onchain results may be readable before animation ends; no secret per-ball releases.\nWith zero tickets, skip randomness and retain carryover.\n\nSECURITY AND DELIVERY\n\nIndex combinations/matching tickets so finalization and selection never scan all sales. Never require transferring the full NFT inventory in one transaction.\nPrevent reentrancy, stale/duplicate callbacks, double claims, rejected-transfer losses and cross-round accounting errors. Admins cannot seize prizes or change active-round rules. Pausing sales preserves claims.\nTest zero/one/multiple matches, duplicates, both rollovers, NFT tie-break, large ticket/inventory counts, failed acquisitions, delayed settlement, custody recovery, late refunds, partial claims, rounding and conservation of funds.\nProvide reproducible mocks and separate FWA Sepolia integration tests. Distinguish verified behavior, assumptions and mocks; never silently substitute mocks.\nDeploy with sales disabled until dependencies, configuration and randomness funding are validated.\nExport source, ABIs, addresses, deployment blocks, configuration and frontend interfaces; document setup, remaining dependencies and both lifecycles.","parentJobId":null,"planHash":"4caad3e22742323b8bb0d041ca74456d94aa70a879eb0c42b012bcaf1fcd51e8","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":"51168","feedbackHash":"f35f9d0ca18354d862faed90cc731e300609532a9a0b5a36556fe883d413ed08","nodeKey":"audit_economics","submissionHash":"361d5dd664bcc2c0e7478f47dd66aa81d014555b81797943c1e9a248f9b38b91","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51737","feedbackHash":"64e5123000de0a77d37e09293cd1071c6f5b66902a4ec27d741bc1532bd1eacb","nodeKey":"audit_flow","submissionHash":"9ba9df1d206aacaf46ef75113eb59a8d9610e6b12afafec9d85edc64c2d39e36","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52230","feedbackHash":"621ebfd9f03122f52f9023b13e870862eb3ed5499a56bd4e7128860e7d05e6cb","nodeKey":"audit_judge","submissionHash":"f5f60da18f2339ab8bbd71b9457faefaa5013f75bfb0f6381cf45dd972e63392","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52262","feedbackHash":"2017bd5c90fa8f5b90f209b0ad7f4fd03e12fd91c461e19fd516883bf655bb3a","nodeKey":"audit_judge","submissionHash":"2fbabc475d7894864598a0ae1fef19084f0a31f64b519550a75c63208cd422b8","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51729","feedbackHash":"2b28bc532e5f3be586641f5a87f97fbdb310e80d9e2d42dfd5f440400c84f1e1","nodeKey":"audit_math","submissionHash":"5faa84b5f7861ab7f3b4f4ae2a5e991fe7a6043118aef88e140fee70668ccfc7","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51459","feedbackHash":"6fdeddcab5873d1a489513f498eec0e63f1bda5ef7cf8f16a613d6942cbf8e7b","nodeKey":"audit_permissions","submissionHash":"eb7a1889d02479302d23ff22a4cbc7d03656530fb08377d4695be65fb74f6904","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50985","feedbackHash":"75634537db77a88a201e4dca7278a7c54045c3fddf93396d956c02fefa34fb54","nodeKey":"build_contract_project","submissionHash":"e50343191d2c718790e8a3c397829ed8dd04956f9ec9ed50617efb2c042e0106","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51021","feedbackHash":"cc79120ea664ac4449548734ea25c74581fa9c83720662fc2b6adb69564401b0","nodeKey":"build_contract_project","submissionHash":"d3ea1f9f9304aef872ef6fea395c4a7224508435ac929f0faf409c099becca01","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51508","feedbackHash":"b11e900ddbbb7a1616fd9c6a08016423cc0664d942cdef5017852be1e7ec63de","nodeKey":"manifest","submissionHash":"1c2628524b1c64afac28e49c6883d8d14248765b9d81ba97c28d0a3edd0dfe3f","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51757","feedbackHash":"9dbb8eb568bd5060267e415bd264069b5b11b15c8cde4862123ae14bc5222f69","nodeKey":"manifest","submissionHash":"97396903ac5b18707422a4739823a680743fccf6caa915736f46062b96d1e461","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51486","feedbackHash":"f5bd916d1d3c1875c1582af41691698700fd8c3c54b7e0ee099cd8e26fe39a5d","nodeKey":"write_foundry_tests","submissionHash":"7114905b598a995d4924bec189605ee2ca6474b2759f24f86c0187acd24f788c","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51533","feedbackHash":"0d8d8d74f9ca1a6186d0595c9534733d1e3e62e956d4e6152c12d3fa1280e89a","nodeKey":"write_foundry_tests","submissionHash":"617af1407ac1e431405b372336a3c95aeff275598f777a7814a9221cef1e0661","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"e040d08d9d8f588959fca7e247ff271ad98a12a23ba69b0e845228ced42eff27","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"d93b4adc7e2e5435a4aac9dc1d27afc63d00ac8bf03476ad426f5b40d144b313","device":"3516474d8a268bd8","findings":[],"hash":"1c2628524b1c64afac28e49c6883d8d14248765b9d81ba97c28d0a3edd0dfe3f","nodeId":"0a3c4b7e-cf2c-44ce-9af0-07fb70f7dd74","outcome":"completed","summary":"The manifest is written, validated against the schema rules, and verified with a build and the full test suite.\n\n**Outcome**\n\n`launch.json` describes the three accepted application contracts in constructor dependency order, with arguments matching the accepted source exactly:\n\n- **SorpheraRouter** takes `$owner`. It is the builder router that calls FWAV2.acquire.\n- **SorpheraVaultFactory** takes the router reference then `$owner`. Its constructor requires the router to already have code, so the order is forced.\n- **Sorphera** takes `$owner`, the factory reference, and the static Chainlink VRF v2.5 Sepolia coordinator address recorded in docs/DEPENDENCIES.md. That address is a verified public dependency, not a requester value, so it is not a stand-in.\n\nThe per-round SorpheraVault is created at runtime by the factory and is correctly not listed. Every contract uses an explicit owner argument, so nothing is trapped at the deploying factory. The notes record the post-deploy owner steps the README already documents: bind the factory to the lottery, register a compatible FWA pool and helper, configure VRF, validate, then enable sales.\n\n**Verification**\n\n- The build succeeds and every runtime is under the EIP-170 limit. The largest is Sorphera at about 17.5 KB.\n- A scratch test deployed the three contracts in manifest order through a CREATE2 probe, confirmed owners and backward references, and scanned runtimes for forbidden opcodes. It passed alongside the 40 accepted tests, 41 total, 0 failures.\n- A script checked the manifest keys, identifier pattern, backward-only references, and notes length. Notes are 1639 characters.\n\n**On the prior failure**\n\nThe earlier rejection was clone_failed during restoration, not a manifest content fault. The current checkout has lib/forge-std committed as 30 ordinary tracked files with no submodule, and the only changed path is launch.json.\n\n**One caveat for the deployer**\n\nThe Sorphera constructor requires the coordinator address to have code. The protected rehearsal will only pass against Sepolia chain state where the Chainlink coordinator is deployed. That is a property of the accepted source, which this task cannot change.","treeHash":"c80501263d092f3b2d26d3ec83a4666bfe4c2ce7","usage":{"cachedInputTokens":271230,"inputTokens":194,"model":"claude-fable-5-1","outputTokens":7704,"runtime":"claude","turns":13,"wallClockMs":180653}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"28e346843ec15530","findings":[{"citation":"resolved","description":"Merged from audit_economics (high). SorpheraVault._reconcile marks an allocated acquisition terminal only when the listing is Settled (status 4) AND FWA does not record the vault as stuckNFTRecipient. FWA's non-keepNFT resolutions (depositorReclaimBacking after settlementWindow, finalizeUnsettled after finalizeWindow; ABI events NFTDeliveryFailed/StuckNFTRecovered, function deliverNFT) deliver best-effort and, on failure, set stuckNFTRecipient = purchaser; the only retry is recoverStuckNFT, which SorpheraVault.recoverNFT (line 198) calls and which reverts for as long as the collection rejects transfers to the vault (paused collection, blocklist, operator filter, abandoned contract). Sorphera.requestDraw (src/Sorphera.sol:347-350) requires v.pending() == 0 with no time bound and no alternative path, and for the ETH game (or an NFT round that already has an eligible NFT) there is no cancellation path either. Consequences: the round stays Closed forever; the ETH already exported to the group (90% of sales minus FWA charges) is unclaimable because entitlement() needs a terminal group; the round's 10% fees are never released; Sorphera.openRound reverts 'previous unsettled' for every later round of that game. No owner, keeper or player action can unblock it. This violates 'Isolate transfer failures' and 'Prevent ... rejected-transfer losses'. The adjacent ownerOf check already enforces 'a Settled flag is not custody'; keeping the request pending adds only the unbounded freeze. Note also that the stuck check overrides terminal even when `held` is true in the same call, so the fix should not depend on FWA clearing stuckNFTRecipient. Minimal fix preserving the design: treat a stuck delivery as terminal for pending accounting (custody is still recorded only when ownerOf == vault, and a later recoverNFT/reconcile records it as a late in-kind recovery that follows the originating round's entitlements), or bound the wait with a published grace period after which requestDraw may proceed.","line":184,"path":"src/SorpheraVault.sol","reproduction":"ETH game: openRound(0); alice buys 4 tickets (0.02 ETH; vault budget 0.018, fees 0.002); vault.acquire(1, now+1); FWA allocates listing 1 to the vault; keeper does not settle; the collection blocks transfers to the vault; after finalizeWindow anyone calls FWA.finalizeUnsettled(1) -> listing Settled, stuckNFTRecipient(1) == vault; vault.reconcile([1]) leaves pending == 1; vault.recoverNFT(1) reverts while the collection blocks; warp to cutoff + 365 days; reconcile again (still pending 1). EXPECTED: requestDraw(0,1) proceeds to Requested and the stuck asset remains a late recovery. ACTUAL: requestDraw(0,1) reverts 'Sorphera: reconciliation/time' indefinitely; 0.018 ETH exported prize and 0.002 ETH fees are frozen; openRound(0) reverts 'previous unsettled'. Proof test/scratch/StuckNFTFreezesGame.t.sol fails on this tree with 'Sorphera: reconciliation/time' and passes when line 184 no longer forces terminal = false (verified by a temporary local patch, then restored).","severity":"high","snippet":"                if (pool.stuckNFTRecipient(a.listingId) == address(this)) terminal = false;","title":"One permanently undeliverable (stuck) FWA NFT keeps the vault pending forever, which blocks requestDraw, freezes the round's prize and fees and halts every future round of that game"},{"citation":"resolved","description":"Merged from write_foundry_tests (reported medium; recalibrated to low because it needs no attacker, only nobody calling the permissionless reconcile between delivery and the deadline, causes no loss of funds, and docs/OPERATIONS.md line 5 already discloses it: 'External deliveries must be reconciled before that deadline; an unrecorded earlier transfer ... can result in cancellation'). recordNFT counts an asset toward active.eligibleNFTs only when the reconciliation transaction runs before settlementDeadline. FWA can deliver the NFT to the vault without SorpheraVault.settle (depositorReclaimBacking after settlementWindow, finalizeUnsettled after finalizeWindow). If that delivery happens before the deadline but reconcile([id]) runs after it, securedCount becomes 1 and pending 0, yet eligibleNFTs stays 0, so requestDraw(1, id) takes the cancel branch (src/Sorphera.sol:343-346) instead of drawing. The secured NFT then belongs to the refund cohort as a shared asset behind the unanimous-vote mechanism, and ticket holders get pro-rata refunds instead of a jackpot. The brief's condition is 'no NFT ... secured by the published settlement deadline'; here one was in custody before the deadline. Options that keep the design: let recordNFT count the asset while the round is still Closed and not yet cancelled (the cancel decision is only taken inside requestDraw, so no outcome dependence is introduced), or keep the current rule and state the reconciliation deadline in the published schedule and frontend.","line":327,"path":"src/Sorphera.sol","reproduction":"Mock setup: open NFT round 1, alice buys 3 tickets, vault.acquire(1, now+1), pool.allocate(1, 0.02 ether); warp to settlementDeadline - 1 and call pool.finalizeUnsettled(1): nft.ownerOf(1) == vault before the deadline. Warp to settlementDeadline + 1, vault.reconcile([1]): securedCount == 1, pending == 0, getRound(1,1).eligibleNFTs == 0. lottery.requestDraw(1,1). EXPECTED: Requested (3) with one VRF request. ACTUAL: Cancelled (7), vrf.requests() == 0. Reproduced in test/scratch/ReproOthers.t.sol::testCustodyBeforeDeadlineReconciledAfterCancels (passes as a demonstration of the actual behaviour).","severity":"low","snippet":"        if (g.terminalRound == 0 && block.timestamp < active.settlementDeadline) ++active.eligibleNFTs;","title":"An NFT physically delivered to the vault before the settlement deadline but reconciled after it is not counted as eligible, so requestDraw cancels a round that holds a secured NFT"},{"citation":"resolved","description":"Merged from audit_economics (low). create() has no caller restriction: it deploys a real SorpheraVault whose immutable lottery is msg.sender and emits VaultCreated(msg.sender, game, round, vault). docs/OPERATIONS.md step 1, frontend/README.md ('RoundOpened plus VaultCreated discovers every vault') and frontend/deployment.json (roundVaultDiscoveryEvent = VaultCreated) make this event the discovery index. An attacker can pre-mint VaultCreated(attacker, game, N, fakeVault) for a round that has not opened yet; an indexer keyed on (game, round) or trusting the factory's event stream will route keeper calls (acquire/settle/reconcile) and UI reads to the attacker's vault. No funds inside Sorphera are at risk (rounds[game][id].vault is set only by openRound and credit/recordNFT are bound to it), so the impact is spoofing of the on-chain discovery index and wasted keeper gas. Fix: require msg.sender to be a lottery address fixed at factory construction (or a registered lottery), and have discovery filter VaultCreated by the indexed lottery argument and cross-check RoundOpened.vault.","line":21,"path":"src/SorpheraVaultFactory.sol","reproduction":"vm.prank(alice); factory.create(0, 1, Settings(1,1,1,1,cutoff)). EXPECTED: revert (only the lottery may create round vaults). ACTUAL: succeeds; fake.lottery() == alice, fake.game() == 0, fake.round() == 1 and VaultCreated(alice, 0, 1, fake) is emitted. Reproduced in test/scratch/ReproOthers.t.sol::testFactoryCreatePermissionless.","severity":"low","snippet":"        vault = new SorpheraVault(msg.sender, router, game, round, settings);\n        emit VaultCreated(msg.sender, game, round, address(vault));","title":"SorpheraVaultFactory.create is permissionless, so anyone can emit VaultCreated events for arbitrary (game, round) that the documented keeper and frontend discovery flow consumes"},{"citation":"resolved","description":"Merged from audit_economics (low). settle() chooses the outcome purely by game: game 1 always calls keepNFT, even when the lottery round is already Status.Cancelled. requestDraw cancels an NFT round at settlementDeadline before checking pending requests (src/Sorphera.sol:343-346), explicitly leaving Pending/Ready/Fulfilled requests to resolve later. When such a request allocates, the only path is keepNFT; the NFT is recorded into a terminal group with divisor = sold, and claimNFTs (lines 255-263) then requires every sold ticket to nominate the identical recipient, with no fallback, timeout or sale (docs/OPERATIONS.md discloses the holdout risk). With tickets spread over several wallets the asset is unclaimable in practice, whereas acceptDepositorBid would have put settlementDiscountBps of the listing backing in ETH into the vault, exported by syncETH into the cancelled group's cash and distributed pro rata through the existing _distribute/claimETH path. The 'always keep the NFT' rule is a jackpot rule; a cancelled round has no jackpot, and the brief's cancellation clause asks for refunds of recovered funds. This is a documented tradeoff, so it is reported low; the author should decide whether a cancelled round may settle to ETH (read the round status from the lottery in settle) or whether a majority/deadline rule should replace unanimity.","line":144,"path":"src/SorpheraVault.sol","reproduction":"NFT round 1: alice 2 tickets, bob 1, carol 1; vault.acquire(1, now+1) still Pending at settlementDeadline; requestDraw(1,1) -> Cancelled, no VRF, perTicket = 0.0025 ETH. Later pool.allocate(1, 0.02 ether) and anyone calls vault.settle(1) -> keepNFT -> vault holds token 1. alice votes with tickets 1 and 2 for alice, bob votes for alice, carol never votes. EXPECTED (refund intent): about 0.018 ETH cashout credited to group cash and claimable pro rata by all four tickets. ACTUAL: nft.ownerOf(1) == vault indefinitely, releaseVotes[0][alice] == 3 != 4, group cash == 0 and perTicket unchanged at 0.0025 ETH. Reproduced in test/scratch/ReproOthers.t.sol::testLateNFTAfterCancellationNeedsUnanimity.","severity":"low","snippet":"            else pool.keepNFT(a.listingId);","title":"After an NFT round is cancelled, a late FWA allocation is still settled with keepNFT, so the refund cohort receives an indivisible NFT behind a unanimous vote instead of the ETH bid that the same coho"}],"hash":"2fbabc475d7894864598a0ae1fef19084f0a31f64b519550a75c63208cd422b8","nodeId":"dc5722bd-9711-4c64-b677-3dfe90755d42","outcome":"completed","summary":"Review complete. The findings file is written at `.imd-findings.json` with 4 findings and a 41-row coverage record, and the working tree has no tracked changes.\n\n**What I kept from the specialists**\n\n- **High, with proof.** A single FWA NFT that cannot be delivered to a round vault keeps the vault's request pending forever. The draw gate in `requestDraw` has no time bound, so the round never finalizes, its exported prize and held fees become unclaimable, and every future round of that game is blocked. The proof at `test/scratch/StuckNFTFreezesGame.t.sol` fails on the current code with the reconciliation revert and passes with a one-line fix that I verified by a temporary patch, then restored.\n- **Low.** An NFT physically delivered before the settlement deadline but reconciled after it does not count as eligible, so the round cancels while holding a secured asset. I lowered this from the tester's medium because the author documents it and it only needs nobody to call the permissionless reconcile in time.\n- **Low.** The vault factory is permissionless, so anyone can emit spoofed discovery events that the documented keeper and frontend flow consumes. No funds are reachable.\n- **Low.** A late FWA allocation after an NFT-round cancellation is still kept as an NFT, leaving the refund cohort an indivisible asset behind a unanimous vote instead of ETH. Documented tradeoff, reported for the author's decision.\n\n**What I dropped.** Nothing was dropped; all four specialist findings reproduced in scratch tests. Nothing new surfaced in my own pass beyond those. The static analysis leads were benign on inspection, and the slither send-ETH warning targets an immutable address.\n\n**Coverage.** All 37 entry points are answered. Three are tied to the high finding, two to the low findings, and the rest hold. One assumption is marked unreached: offline mocks cannot confirm that the live FWA pool keeps the listing purchaser and uses status 4 after settlement and depositor reclaims. If it does not, reconciliation would never become terminal on the real pool, so that should be verified on the compatible Sepolia deployment before activation.","treeHash":null,"usage":{"cachedInputTokens":1533876,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":57581,"runtime":"claude","turns":31,"wallClockMs":834544}},{"artifacts":[],"attempt":2,"bundleHash":null,"device":"6208734cdf5317a1","findings":[{"citation":"resolved","description":"SorpheraVault._reconcile refuses to mark an acquisition terminal while FWA records the vault as stuckNFTRecipient for its listing (src/SorpheraVault.sol:184), and Sorphera.requestDraw requires v.pending() == 0 with no time bound or fallback (src/Sorphera.sol:347-350). In the verified FWAV2 source, every non-keepNFT resolution (depositorReclaimBacking after settlementWindow, finalizeUnsettled after finalizeWindow) delivers the NFT best-effort with transferFrom and, on failure, sets stuckNFTRecipient = purchaser; the only retry is recoverStuckNFT, which does the same transferFrom and reverts while the collection keeps rejecting. So if an FWA-whitelisted collection blocks transfers to the vault (pausable collection, blocklist/operator filter, abandoned/paused contract), the acquisition can never become terminal: pending stays 1, requestDraw reverts 'Sorphera: reconciliation/time' forever, the round never reaches Requested/Won/Rolled, the ETH already exported into the group (90% of sales minus FWA charges) can never be claimed because entitlement() needs a terminal group, the 10% fees of that round are never released, and openRound reverts 'previous unsettled' for every future round of that game. This hits the ETH game even though it never wants NFTs (one missed acceptDepositorBid within FWA's settlementWindow, 1 day on the observed Sepolia pool, is enough), and the NFT game whenever any other NFT was already eligible (otherwise the round cancels first). The code's stated purpose for the line, 'a notification or Settled flag is not custody', is already enforced by the ownerOf check before recordNFT; keeping the request pending only adds the unbounded freeze. Violates 'Prevent ... rejected-transfer losses' and 'Isolate transfer failures'. Suggested fix preserving the design: treat a stuck delivery as terminal for pending accounting (custody is still only recorded once ownerOf == vault, and recoverNFT/reconcile later records it as a late in-kind recovery following the originating round's entitlements), or bound the wait with a published grace period after which requestDraw may proceed.","line":184,"path":"src/SorpheraVault.sol","reproduction":"ETH game: openRound(0); buy 4 tickets (0.02 ETH, budget 0.018); vault.acquire(1, now+1); FWA allocates listing 1 to the vault; keeper does not settle; the collection blocks transfers to the vault; after finalizeWindow anyone calls FWA.finalizeUnsettled(1) -> listing Settled, stuckNFTRecipient(1) == vault; vault.reconcile([1]) leaves pending == 1; vault.recoverNFT(1) reverts while blocked; warp to cutoff + 365 days; EXPECTED: requestDraw(0,1) proceeds and the stuck asset remains a late recovery; ACTUAL: requestDraw reverts 'Sorphera: reconciliation/time' indefinitely, 0.008 ETH exported prize plus 0.002 ETH fees are frozen, openRound(0) reverts 'previous unsettled' forever. Proof: test/scratch/StuckNFTBlocksDraw.t.sol fails on this tree and passes when the stuck check no longer forces terminal = false.","severity":"high","snippet":"                if (pool.stuckNFTRecipient(a.listingId) == address(this)) terminal = false;","title":"A single undeliverable (stuck) NFT keeps the vault's request pending forever, which permanently blocks requestDraw, freezes the round's prize ETH and halts the whole game"},{"citation":"resolved","description":"create() has no caller restriction; it deploys a real SorpheraVault whose immutable lottery is msg.sender and emits VaultCreated(msg.sender, game, round, vault). docs/OPERATIONS.md step 1 and frontend/README.md tell keepers and the frontend to discover vaults from RoundOpened plus VaultCreated, and frontend/deployment.json names VaultCreated as the roundVaultDiscoveryEvent. An attacker can mint VaultCreated(game=0, round=N, vault=attackerVault) for a not-yet-opened round; an indexer that keys on (game, round) without also checking the indexed lottery address, or that trusts the factory's event list, will route keeper calls (acquire/settle/reconcile) and UI reads to the attacker's vault. No funds are at risk inside Sorphera because rounds[game][id].vault is set only by openRound and credit/recordNFT are bound to it, but the spoofed vault calls back into the attacker's 'lottery', so the spoof costs nothing and pollutes the only on-chain discovery index the docs name. Fix: restrict create() to a lottery address fixed at deployment (or require msg.sender == a configured lottery), and document that discovery must filter VaultCreated by the indexed lottery argument and cross-check RoundOpened.vault.","line":21,"path":"src/SorpheraVaultFactory.sol","reproduction":"Any EOA calls factory.create(0, 1, Settings(1,1,1,1,cutoff)) before or after the real round 1 opens. EXPECTED: revert (only the lottery may create round vaults). ACTUAL: succeeds; VaultCreated(attacker, 0, 1, fakeVault) is emitted; fakeVault.lottery() == attacker, fakeVault.game() == 0, fakeVault.round() == 1. Verified with a scratch test (testFactoryCreateIsPermissionless).","severity":"low","snippet":"        vault = new SorpheraVault(msg.sender, router, game, round, settings);","title":"SorpheraVaultFactory.create is permissionless, so anyone can emit VaultCreated events with arbitrary lottery/game/round values that the documented keeper and frontend discovery flow consumes"},{"citation":"resolved","description":"settle() picks the outcome purely by game: game 1 always calls keepNFT, even when the lottery round is already Status.Cancelled. requestDraw cancels an NFT round at settlementDeadline before checking pending FWA requests (src/Sorphera.sol:343-346), so a request that is Pending/Ready/Fulfilled at that moment is explicitly left to resolve later. When it then allocates, the only path is keepNFT; the NFT lands in the vault and is recorded via recordNFT into a terminal group with divisor = sold. claimNFTs then requires every one of the sold tickets to nominate the identical recipient (releaseVotes[index][recipient] == count) before delivery, with no fallback, timeout, sale or admin path (docs/OPERATIONS.md calls this out as a holdout risk). With tickets spread over more than a handful of wallets the asset is unclaimable in practice, while acceptDepositorBid at that point would have returned settlementDiscountBps (90%) of the listing backing in ETH to the vault, exported by syncETH into the cancelled group's cash and distributed pro rata to the same cohort through the existing _distribute/claimETH path. The 'always keep the NFT' rule is a jackpot rule; a cancelled round has no jackpot and the brief's cancellation clause asks for refunds of recovered funds plus preserved late-recovery entitlements. This is documented as a tradeoff, but the end state contradicts the refund intent in substance. Suggested fix preserving the design: in settle(), for game 1 when the originating round is Cancelled (read via the lottery), call acceptDepositorBid instead of keepNFT; alternatively allow the cohort to resolve a shared asset by majority or after a deadline.","line":144,"path":"src/SorpheraVault.sol","reproduction":"NFT game round 1: 4 tickets from three wallets (alice 2, bob 1, carol 1); vault.acquire(1, ...) pending at settlementDeadline; requestDraw(1,1) -> Cancelled (no VRF), refunds 0.0025 ETH/ticket from 0.008 remaining budget + 0.002 fees; later FWA allocates listing 1 (backing 0.02 ETH) and anyone calls vault.settle(1) -> keepNFT -> vault holds token 1, recordNFT increments nftCount. alice claims with tickets 1 and 2 naming alice, bob names alice, carol never calls or names carol: EXPECTED (refund intent): 0.018 ETH cashout added to group cash, +0.0045 ETH per ticket claimable by all four; ACTUAL: NFT stays in the vault indefinitely (releaseVotes[0][alice] == 3 != 4), no ETH value reaches the cohort. The existing test testLateNFTDoesNotRescueCancelledRoundAndSharedRecovery shows the unanimous-vote path with a single cooperating pair; the holdout case has no test.","severity":"low","snippet":"            else pool.keepNFT(a.listingId);","title":"After an NFT round is cancelled, a late FWA allocation is still settled with keepNFT, so the refund cohort receives an indivisible co-owned NFT behind a unanimous vote instead of ETH that would have j"}],"hash":"361d5dd664bcc2c0e7478f47dd66aa81d014555b81797943c1e9a248f9b38b91","nodeId":"13bebbb2-689c-4083-b2d4-a5e04ad64ca3","outcome":"completed","summary":"The review is complete. The findings file is written, all snippets match the tree, every listed entry point has a coverage row, the project suite still passes, and no tracked file was changed.\n\n**Outcome.** I audited the Sorphera contracts against the Economic Security, Invariant and Flow Gap guides and recorded three findings plus a 39-row coverage record in `.imd-findings.json`. To ground the FWA-boundary analysis I pulled the Sourcify-verified mainnet FWAV2 source and confirmed its listing and acquisition state transitions match what the vault assumes, except for one path.\n\n**Findings**\n\n- **High. A stuck NFT delivery freezes a round and its whole game forever.** The vault keeps an acquisition pending while FWA records it as the stuck recipient, and the lottery's draw request demands zero pending requests with no time bound. In the verified FWA source, forced deliveries use a plain `transferFrom` and the only retry does the same, so a whitelisted collection that blocks or pauses transfers to the vault makes the request permanently non-terminal. The round's exported prize ETH can never be claimed, fees are never released, and no later round can open. This reaches the ETH game through one missed cashout within FWA's one-day Sepolia window. The proof under `test/scratch/StuckNFTBlocksDraw.t.sol` fails on the current tree and passes once a stuck delivery no longer forces the request to stay pending.\n- **Low. The vault factory is permissionless.** Anyone can emit `VaultCreated` events with arbitrary lottery, game and round values, which is the discovery index the keeper and frontend docs tell operators to consume. No funds are exposed inside Sorphera.\n- **Low. A cancelled NFT round still keeps a late-allocated NFT rather than cashing out.** The refund cohort then needs a unanimous nomination from every sold ticket to move an indivisible asset, so in practice the value is stranded. A cashout would have joined their pro-rata refunds. This is documented as a tradeoff, so I rated it low.\n\n**What held.** Fee accounting is charged once on sales only, operator withdrawals are bounded by released fees, group cash distribution conserves funds including cancelled rounds with late credits, the dust path and rollover entitlements behave as specified, the VRF binding and sampler are sound, and the Permit2 reward queue matches FWA's builder reference. Slither's arbitrary-send lead on the vault sync is a false positive since the target is immutable.\n\n**Not reached.** I did not verify the live Sepolia transfer helper interface or Chainlink billing behaviour; both are external and already flagged as unverified in the project's own docs.","treeHash":null,"usage":{"cachedInputTokens":4157628,"inputTokens":738,"model":"claude-fable-5-1","outputTokens":88211,"runtime":"claude","turns":51,"wallClockMs":1311432}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"b312209aac1edeae","findings":[],"hash":"5faa84b5f7861ab7f3b4f4ae2a5e991fe7a6043118aef88e140fee70668ccfc7","nodeId":"d877388b-cfbf-4876-b0b8-f358aa2015c0","outcome":"completed","summary":"Saved `.imd-findings.json` with no substantiated findings and coverage for all 37 entry points.\n\nAll 34 existing tests and 5 temporary math/boundary checks passed. Temporary tests were removed; contracts and configuration are unchanged. Live integration remains unverified.","treeHash":null,"usage":{"cachedInputTokens":1456896,"inputTokens":130531,"model":"gpt-6-astra","outputTokens":13011,"runtime":"codex","turns":6,"wallClockMs":484541}},{"artifacts":[],"attempt":1,"bundleHash":"f21c5d42a7ba512f530b646b574065a5ff869f5941162312d2a41f3fcc66af3d","device":"9eee291846dcb046","findings":[{"description":"recordNFT credits eligibleNFTs only when the reconciliation transaction timestamp is before the active settlementDeadline. FWA can permissionlessly finalize delivery directly to the vault before the deadline, without calling SorpheraVault.settle. If reconcile is called later, the NFT is held and recorded but eligibleNFTs remains zero; requestDraw cancels the round and refunds tickets rather than drawing for the secured inventory. This contradicts cancellation only when no NFT is secured by the published deadline and can strand the delivered NFT behind unanimous refund-holder voting. Actual custody and its acquisition timing must be reconciled with the deadline policy; bookkeeping delay must not be treated as absence of custody.","line":327,"path":"src/Sorphera.sol","reproduction":"Run the self-contained proof with forge test --match-path test/scratch/SorpheraCustodyDeadlineProof.t.sol. On Sepolia mock setup: open NFT round, buy 3 tickets at 0.005 ETH, acquire request 1, allocate listing 1, warp to settlementDeadline-1 and call pool.finalizeUnsettled(1). ownerOf(1) is the round vault before the deadline. Warp to deadline+1, call vault.reconcile([1]); securedCount=1 and pending=0. Call lottery.requestDraw(1,1). Expected Requested (3) and a VRF request; actual Cancelled (7), no lottery randomness. The proof fails with 7 != 3. FWA is modeled offline; no live deployment behavior is claimed.","severity":"medium","title":"NFT secured before settlement deadline is ignored when custody is reconciled later"}],"hash":"617af1407ac1e431405b372336a3c95aeff275598f777a7814a9221cef1e0661","nodeId":"649c953b-5b8f-4756-a086-ff711bc30bbf","outcome":"completed","summary":"Added adversarial tests and five stateful invariants. `forge build` and `forge test` pass: 49 tests, including 16,384 invariant calls.\n\nReported one medium-severity NFT custody/deadline defect in `.imd-findings.json`, with a reproduced failing proof.\n\nProduction contracts and configuration are unchanged. Live Sepolia integration remains unverified by this work.","treeHash":"caf2caa18029bc621d62e04bee4224525831df89","usage":{"cachedInputTokens":2867200,"inputTokens":128487,"model":"gpt-6.1-sol","outputTokens":23062,"runtime":"codex","turns":6,"wallClockMs":725646}},{"artifacts":[],"attempt":1,"bundleHash":"d1abcf11853a477abf6b472a930f7d983b5256e93b3c3ef4c865e77e99b81cc7","device":"0e78515c4d95885a","findings":[],"hash":"7114905b598a995d4924bec189605ee2ca6474b2759f24f86c0187acd24f788c","nodeId":"649c953b-5b8f-4756-a086-ff711bc30bbf","outcome":"completed","summary":"The full suite passes: 59 tests across three suites, 0 failures, and only paths under `test/` changed. No findings file is needed this round.\n\n**What I found**\n\nThe implementer's revision (commit 5ce3812) changed four things my previous suite depended on: the factory now takes an owner and must be bound to the lottery once with `setLottery`, the vault's ERC721 hook stamps a receipt time that reconciliation passes to `recordNFT`, settlement after an NFT-round cancellation takes the ETH bid instead of keeping the NFT, and a stuck FWA delivery is now terminal rather than pending. My fixture and one adversarial test no longer compiled against that code. The custody-deadline defect I reported last round is fixed by the receipt stamp.\n\nI read the revised paths adversarially and did not find a reportable defect. The hook is callable by anyone, but the stamp is keyed by caller, so a forged stamp cannot reach the real collection's entry. Stale stamps are not reachable with a compliant collection because a token cannot leave a vault and return to it within one round.\n\n**What I changed**\n\n- `test/helpers/SorpheraFixture.sol`: new factory constructor and the one-time lottery binding.\n- `test/SorpheraAdversarial.t.sol`: fixed the `recordNFT` caller test and added one vault impersonating another round. New tests cover a forged receipt stamp, the strict deadline boundary (receipt exactly at the deadline cancels), a 512-run property that receipt time rather than reconcile time decides cancellation, a stuck asset recovered into a rolled round that belongs only to the next winner while expired tickets get nothing, and a second lottery that cannot validate against or create vaults through the bound factory.\n- `test/SorpheraInvariant.t.sol`: the handler now drives forced FWA cashouts, forced NFT deliveries, rejected deliveries parked as stuck, and their recovery. A new invariant checks the fixed outcome rules on every round: cancelled rounds hold no VRF request and release no fees, drawn rounds hold exactly one bound request and release held fees once, zero-ticket rounds skip VRF, NFT draws always freeze inventory, the tie-break winner is an indexed matching ticket, eligible inventory never exceeds group inventory, and every recorded asset carries a receipt stamp. The pinned lifecycle now also drives a cancellation with a pending request, the late forced delivery into that cancelled round, and the refund claims.\n- `test/README.md`: describes the new coverage and records that last round's finding is fixed and nothing is open.\n\n**Still owed outside this suite**\n\nLive Sepolia runs remain blocked by the published pool's missing builder attribution, as `docs/DEPENDENCIES.md` records. Everything here runs against the local mocks, and the receipt-stamp coverage assumes FWA's forced deliveries use `safeTransferFrom`.","treeHash":"7efe572c96655a5677e984e256d23479be7dfa48","usage":{"cachedInputTokens":1743939,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":44735,"runtime":"claude","turns":43,"wallClockMs":877577}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"f494611affb5524c","findings":[],"hash":"80d03b0eacda1a29379f5299e3147ec422b4e5ca627f94bed3e97b0998b51ffa","nodeId":"0a3c4b7e-cf2c-44ce-9af0-07fb70f7dd74","outcome":"failed","summary":"could not restore accepted work: accepted work conflicts with a dependency, which plan validation should have prevented: ","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"claude","turns":0,"wallClockMs":221}},{"artifacts":[],"attempt":1,"bundleHash":"71cbe6bd470b94f1342d241291708e16f2d8f9b8235a50d4b99ed8587517310e","device":"ad944e1953b3faa2","findings":[],"hash":"97396903ac5b18707422a4739823a680743fccf6caa915736f46062b96d1e461","nodeId":"0a3c4b7e-cf2c-44ce-9af0-07fb70f7dd74","outcome":"completed","summary":"Updated [launch.json](/home/imdc1/.identitymd/work/a3707b79-51b8-4d04-9e32-3e786d91c1d9/0a3c4b7e-cf2c-44ce-9af0-07fb70f7dd74/launch.json) with three accepted contracts, matching constructor arguments, dependency order, and activation notes.\n\nValidated schema constraints and compiled constructor ABIs. `forge build` succeeded; all 34 tests passed.\n\nOnly `launch.json` changed outside permitted scratch output. Sales remain disabled pending compatible FWA dependencies, owner configuration, and VRF funding.","treeHash":"956708440425fef4e2da02ea826d99c880ef5e98","usage":{"cachedInputTokens":449536,"inputTokens":48403,"model":"gpt-6-astra","outputTokens":4146,"runtime":"codex","turns":3,"wallClockMs":146787}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"bc30da9213bc5d7a","findings":[],"hash":"9ba9df1d206aacaf46ef75113eb59a8d9610e6b12afafec9d85edc64c2d39e36","nodeId":"b9846440-8440-473b-90b8-63f9ce65c667","outcome":"completed","summary":"Saved [`.imd-findings.json`](./.imd-findings.json) with no substantiated findings and coverage for all 37 entry points.\n\nAll 34 offline tests passed. Live Sepolia end-to-end integration was not run. Implementation files remain unchanged.","treeHash":null,"usage":{"cachedInputTokens":1250688,"inputTokens":125059,"model":"gpt-6-astra","outputTokens":11809,"runtime":"codex","turns":5,"wallClockMs":486739}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"b94d8a9fa9123fb1","findings":[],"hash":"a81d7724bdac581708df1192e22cc60c42cd7ecc8e84e2b6b16c3cc907a1409f","nodeId":"13bebbb2-689c-4083-b2d4-a5e04ad64ca3","outcome":"failed","summary":"This content was flagged for possible cybersecurity risk. If this seems wrong, try rephrasing your request. If you’re doing authorized security work that requires more cyber permissive safeguards, apply for Daybreak access via https://platform.openai.com/settings/organization/status-and-access before retrying.","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":"gpt-6-astra","outputTokens":0,"runtime":"codex","turns":1,"wallClockMs":94404}},{"artifacts":[],"attempt":1,"bundleHash":"1e8e7568048292eafa30cc64934d80d9b3a48522ecef4d9c431d1a2df584e6e2","device":"72ae9b5bbd1a54b6","findings":[],"hash":"d3ea1f9f9304aef872ef6fea395c4a7224508435ac929f0faf409c099becca01","nodeId":"03e09382-8b7a-47f6-aae1-0247d0a4bdfb","outcome":"completed","summary":"All four findings reproduced on the accepted tree, and all four are fixed. The suite now has 40 passing tests, `forge build` and `forge fmt --check` pass, ABIs and manifest are regenerated, and `.imd-responses.json` answers each finding.\n\n**High: stuck NFT froze the game** (`src/SorpheraVault.sol`). The reviewer's scenario reproduced exactly: pending stayed 1 and `requestDraw` reverted a year later. The cause was the line that forced the request back to pending whenever FWA named the vault as stuck recipient. A Settled listing whose NFT the vault does not hold is now terminal for pending accounting. The vault emits `DeliveryStuck`, records no custody, and `recoverNFT` stays callable forever. A later recovery is recorded as a late in-kind recovery routed by the originating round's entitlements. Two new tests cover the ETH game proceeding to a winner with the asset recoverable only by that winner, and an NFT round with nothing else secured cancelling at its deadline.\n\n**Low: custody before deadline, reconciled after.** Reproduced and judged real. The vault's ERC-721 receive hook now stamps a receipt time per token, keyed by the calling collection. Reconciliation passes that time to the lottery, so delivery before the deadline counts even when recorded after it. Delivery after the deadline still cancels, and the cancel decision still happens only inside `requestDraw`.\n\n**Low: permissionless factory.** Reproduced and fixed. The factory is now owned and the owner binds the lottery once with `setLottery`. Only that lottery can create vaults, and `validateLaunch` refuses an unbound factory. The factory constructor gained an owner argument, so launch.json now passes `$owner` to it. The README lists the setter under \"After launch\".\n\n**Low: late allocation after cancellation kept as NFT.** Reproduced and judged real. Settlement after a round is Cancelled now takes the ETH depositor bid, read from round state, never from the caller. The test shows the per-ticket refund rising and conservation holding. Assets FWA forces into the vault after cancellation still follow the documented shared-cohort path.\n\nDocs updated in README, docs/OPERATIONS.md, docs/TESTING.md, frontend/README.md, and the frontend JSON exports. The scratch reproduction file was removed, so no stray files remain outside the delivered paths.","treeHash":"121bd48a29bd9e893d6750a1d7a801484d392e9f","usage":{"cachedInputTokens":1478921,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":40316,"runtime":"claude","turns":56,"wallClockMs":759777}},{"artifacts":[],"attempt":1,"bundleHash":"6f6cd55e79c3564b476c70c7d5ae9ff9c974861f2fd9f480f2c4f9d61a070353","device":"410247426ec5a6df","findings":[],"hash":"e50343191d2c718790e8a3c397829ed8dd04956f9ec9ed50617efb2c042e0106","nodeId":"03e09382-8b7a-47f6-aae1-0247d0a4bdfb","outcome":"completed","summary":"Delivered Sorphera contracts, tests, documentation, deployment manifest, and ABI exports. See [README.md](/home/imd/.identitymd/work/a3707b79-51b8-4d04-9e32-3e786d91c1d9/03e09382-8b7a-47f6-aae1-0247d0a4bdfb/README.md).\n\nChecks passed: `forge build`, 34 local tests, 2 Sepolia read-only checks, and `forge fmt --check`.\n\n**Launch remains gated:** the verified public FWA Sepolia deployment lacks the required builder attribution. Sales default to disabled; compatible dependencies and funded VRF configuration are required. No deployment was performed.","treeHash":"db9b163db28d0b58cc8ef4532bdd2274ebe78f1a","usage":{"cachedInputTokens":2715008,"inputTokens":146533,"model":"gpt-6-astra","outputTokens":50830,"runtime":"codex","turns":8,"wallClockMs":1502587}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"b5370e603753d2ab","findings":[],"hash":"eb7a1889d02479302d23ff22a4cbc7d03656530fb08377d4695be65fb74f6904","nodeId":"48a3d359-6c36-4b70-b6a2-e87eb09508a8","outcome":"completed","summary":"Saved [.imd-findings.json](.imd-findings.json): no substantiated defects in the assigned area, with coverage for all 37 entry points.\n\nAll 34 existing Foundry tests passed. Live integration was not rerun. Production files remain unchanged.","treeHash":null,"usage":{"cachedInputTokens":1195648,"inputTokens":133468,"model":"gpt-6-astra","outputTokens":9636,"runtime":"codex","turns":5,"wallClockMs":442512}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"04f946173ab09bec","findings":[{"citation":"resolved","description":"Settlement of prior finding 4854c268 (low). The author's fix stamps receivedAt[collection][tokenId] in SorpheraVault.onERC721Received (src/SorpheraVault.sol:302-305) and passes it to Sorphera.recordNFT, which counts the asset as eligible when securedAt < settlementDeadline (src/Sorphera.sol:330). The fix only works if FWA's forced deliveries invoke the ERC721 receiver hook. They do not. In the Sourcify-verified FWAV2 source (mainnet 0x958C41181182e76F221331b2755b77D9e1426A98, src/FWAV2/FWAV2.sol at the commit the pinned fwa-v2 reference was read from), depositorReclaimBacking and finalizeUnsettled call _settleNFTToPurchaser, which uses safeTransferFrom ONLY when msg.sig == keepNFT.selector and otherwise calls _deliverNFT -> deliverNFT -> _transferNFT, i.e. `ERC721(collection).transferFrom(from, to, tokenId); if (ERC721(collection).ownerOf(tokenId) != to) revert NFTTransferFailed();` (source comment: 'Non-safe transfers have no standard ERC-721 receiver callback; require recipient ownership'). recoverStuckNFT uses the same _transferNFT. So for exactly the two external-delivery paths the original finding named, receivedAt stays 0, _reconcile falls back to block.timestamp (the reconciliation time), and an NFT physically in the vault before the deadline but reconciled after it is still not eligible; requestDraw(1, id) then takes the cancel branch (src/Sorphera.sol:350-353) although the brief's condition 'no NFT secured by the published settlement deadline' is not met. The author's test testCustodyBeforeDeadlineCountsEvenWhenReconciledAfter passes only because test/mocks/ExternalMocks.sol:352 implements finalizeUnsettled with nft.safeTransferFrom, which diverges from the verified contract; docs/TESTING.md line 40 states the assumption ('FWA's forced deliveries are assumed to use safeTransferFrom (its stuck-recipient bookkeeping implies a receiver check)') and that assumption is contradicted by the source: the 'receiver check' is an ownerOf comparison, not the hook. Severity stays low (same as before): no attacker is needed beyond anyone calling requestDraw at the deadline before the keeper reconciles, there is no loss of funds (the cohort receives pro-rata refunds and the NFT as a shared asset), and on the readback Sepolia pool finalizeWindow is 7 days and settlementWindow 1 day, so the window arises only after the keeper has already missed its own settle. Fix options that keep the design: (a) count a held asset as eligible when it is reconciled while the round is still Closed and not yet cancelled (the cancel decision is already taken only inside requestDraw from the recorded count, so no outcome dependence is introduced), or (b) keep the current rule but remove the safeTransferFrom claim from docs/TESTING.md and docs/OPERATIONS.md, make the mock's finalizeUnsettled/depositorReclaimBacking use a plain transfer like FWAV2, and publish 'reconcile before the deadline' as part of the schedule. Either way the mock should not model a hook that FWA does not trigger.","line":190,"path":"src/SorpheraVault.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\nimport {Test} from \"forge-std/Test.sol\";\nimport {Sorphera} from \"src/Sorphera.sol\";\nimport {SorpheraVault} from \"src/SorpheraVault.sol\";\nimport {SorpheraRouter} from \"src/SorpheraRouter.sol\";\nimport {SorpheraVaultFactory} from \"src/SorpheraVaultFactory.sol\";\nimport {Balls} from \"src/lib/Balls.sol\";\nimport {\n    MockNFT, MockToken, MockRewards, MockFWA, MockHelper, MockPermit2, MockVRF\n} from \"test/mocks/ExternalMocks.sol\";\n\n/// The verified FWAV2 (`_settleNFTToPurchaser` -> `_deliverNFT` -> `deliverNFT` -> `_transferNFT`) delivers\n/// `finalizeUnsettled` and `depositorReclaimBacking` with plain `transferFrom` plus an `ownerOf` check, so the\n/// vault's `onERC721Received` never runs and `receivedAt` stays 0. This test leaves the chain in exactly that\n/// post-delivery state (listing Settled, token owned by the vault, no hook) and shows the round still cancels.\ncontract PlainDeliveryCancels is Test {\n    Sorphera lottery;\n    SorpheraRouter router;\n    SorpheraVaultFactory factory;\n    MockNFT nft;\n    MockToken token;\n    MockRewards rewards;\n    MockFWA pool;\n    MockHelper helper;\n    MockVRF vrf;\n    address alice = makeAddr(\"alice\");\n    uint256 cutoff;\n\n    function setUp() public {\n        vm.chainId(11155111);\n        vm.warp(10 days);\n        vm.roll(100);\n        MockPermit2 permit = new MockPermit2();\n        vm.etch(0x000000000022D473030F116dDEE9F6B43aC78BA3, address(permit).code);\n        nft = new MockNFT();\n        token = new MockToken();\n        rewards = new MockRewards(token);\n        pool = new MockFWA(rewards, nft);\n        rewards.setFWA(address(pool));\n        helper = new MockHelper(address(token));\n        token.setDistributor(address(helper));\n        token.setDistributor(address(rewards));\n        router = new SorpheraRouter(address(this));\n        router.configure(address(pool), address(helper));\n        factory = new SorpheraVaultFactory(address(router), address(this));\n        vrf = new MockVRF();\n        lottery = new Sorphera(address(this), address(factory), address(vrf));\n        factory.setLottery(address(lottery));\n        vrf.setConsumer(address(lottery));\n        cutoff = vm.getBlockTimestamp() + 7 days;\n        lottery.configureRules(0, Sorphera.Rules(0.005 ether, cutoff, 1 hours, 2 days, 0.02 ether, 0.03 ether, 1 ether, 500));\n        lottery.configureRules(1, Sorphera.Rules(0.005 ether, cutoff + 1 hours, 1 hours, 2 days, 0.02 ether, 0.03 ether, 1 ether, 500));\n        lottery.configureRandomness(Sorphera.RandomConfig(123, keccak256(\"mock key\"), 3, 200000, true));\n        lottery.validateLaunch();\n        lottery.setSalesEnabled(true);\n        vm.deal(alice, 100 ether);\n        vm.deal(address(pool), 1000 ether);\n    }\n\n    function _pick(uint8 game, uint256 id, uint256 word) internal view returns (Sorphera.Pick memory p) {\n        (p.main, p.bonus,) = Balls.draw(lottery.seedFor(game, id, word));\n    }\n\n    function _one(uint256 id) internal pure returns (uint256[] memory a) {\n        a = new uint256[](1);\n        a[0] = id;\n    }\n\n    /// Emulates FWAV2's plain-transferFrom delivery: the mock pool's `finalizeUnsettled` marks the listing\n    /// Settled; the token's owner is then set to the vault directly, exactly as `ERC721.transferFrom` would,\n    /// without the receiver hook.\n    function _finalizeUnsettledLikeFWAV2(uint256 listingId, address vault) internal {\n        nft.setRejected(listingId, true); // keep the mock's safeTransferFrom (and its hook) out of the path\n        pool.finalizeUnsettled(listingId); // listing.status = 4\n        nft.setRejected(listingId, false);\n        vm.store(address(nft), keccak256(abi.encode(listingId, uint256(0))), bytes32(uint256(uint160(vault))));\n        assertEq(nft.ownerOf(listingId), vault);\n    }\n\n    function testPlainTransferDeliveryBeforeDeadlineReconciledAfterStillCancels() public {\n        vm.warp(cutoff + 1 hours - 7 days);\n        uint256 id = lottery.openRound(1);\n        SorpheraVault v = SorpheraVault(payable(lottery.getRound(1, id).vault));\n        Sorphera.Pick[] memory picks = new Sorphera.Pick[](3);\n        picks[0] = _pick(1, 1, 73);\n        picks[1] = picks[0];\n        picks[2] = picks[0];\n        vm.prank(alice);\n        lottery.buy{value: 0.015 ether}(1, 1, picks);\n        v.acquire(1, vm.getBlockTimestamp() + 1);\n        pool.allocate(1, 0.02 ether);\n        uint256 deadline = lottery.getRound(1, 1).settlementDeadline;\n        vm.warp(deadline - 1);\n        _finalizeUnsettledLikeFWAV2(1, address(v));\n        // Physical custody one second before the deadline, but no receiver hook ran.\n        assertEq(v.receivedAt(address(nft), 1), 0);\n        vm.warp(deadline + 1);\n        v.reconcile(_one(1));\n        assertEq(v.securedCount(), 1);\n        assertEq(v.pending(), 0);\n        // EXPECTED: the asset secured before the deadline counts and the draw is requested.\n        lottery.requestDraw(1, 1);\n        assertEq(uint256(lottery.getRound(1, 1).status), uint256(Sorphera.Status.Requested), \"round cancelled\");\n        assertEq(vrf.requests(), 1);\n    }\n}","reproduction":"State a real finalizeUnsettled leaves: listing Settled (status 4), ownerOf(tokenId) == vault, no receiver hook. Mock setup as in test/helpers/SorpheraFixture.sol: open NFT round 1; alice buys 3 tickets (0.015 ETH); vault.acquire(1, now+1); pool.allocate(1, 0.02 ether); warp to settlementDeadline - 1; produce the FWAV2 delivery state (in the scratch test: nft.setRejected(1,true) so the mock's safeTransferFrom path is skipped, pool.finalizeUnsettled(1) sets status 4, then the token owner is set to the vault exactly as transferFrom would, with no hook). Assert v.receivedAt(nft, 1) == 0 and nft.ownerOf(1) == vault before the deadline. Warp to settlementDeadline + 1; v.reconcile([1]) -> securedCount == 1, pending == 0, getRound(1,1).eligibleNFTs == 0. lottery.requestDraw(1,1). EXPECTED: status Requested (3), vrf.requests() == 1. ACTUAL: status Cancelled (7), vrf.requests() == 0. test/scratch/PlainDeliveryCancels.t.sol fails on this tree with 'round cancelled: 7 != 3'. Source evidence: verified FWAV2.sol _settleNFTToPurchaser (`if (msg.sig == this.keepNFT.selector) { ERC721(listing.collection).safeTransferFrom(...) } else { _deliverNFT(...) }`), deliverNFT -> _transferNFT (`ERC721(collection).transferFrom(from, to, tokenId);`).","severity":"low","snippet":"                    uint256 securedAt = receivedAt[l.collection][l.tokenId];\n                    if (securedAt == 0) securedAt = block.timestamp;\n                    ISorphera(lottery).recordNFT(game, round, securedAt);","title":"Receipt-stamp fix for 'custody before deadline, reconciled after' does not take effect against the verified FWAV2: its forced deliveries use plain transferFrom, so onERC721Received never runs and the "}],"hash":"f5f60da18f2339ab8bbd71b9457faefaa5013f75bfb0f6381cf45dd972e63392","nodeId":"dc5722bd-9711-4c64-b677-3dfe90755d42","outcome":"completed","summary":"Review complete. The findings file is written and validated; no source or tracked files were changed.\n\n**Outcome of settling last round's four findings**\n\n- **High, stuck NFT freezes the game (a16d5bf3): fixed.** Reconcile now treats a Settled-but-undelivered listing as terminal and emits DeliveryStuck. I re-ran the original scenario and the draw, claims, fee release and the next round all proceed, and a recovery a year later is claimable only through round-1 entitlements. I also checked the verified FWAV2 source from Sourcify: every exit from an Allocated listing sets status Settled, and Withdrawn is used only for Active or Staged listings, so the new terminal rule has no gap.\n- **Low, permissionless factory.create (3b6f5940): fixed.** The factory is owner-bound once via setLottery, create is lottery-only, and validateLaunch cross-checks the binding.\n- **Low, keepNFT after cancellation (d3c3fcea): fixed.** settle reads the round's cancelled status and takes the ETH bid; the cohort's per-ticket refund rose from 0.0025 to 0.007 ETH in my re-run.\n- **Low, custody before deadline reconciled after (4854c268): not fixed against the real dependency.** This is the one finding I kept. The fix stamps receipt time in the vault's ERC721 receiver hook, but verified FWAV2 delivers `finalizeUnsettled` and `depositorReclaimBacking` with plain `transferFrom` plus an ownerOf check. The hook never runs on exactly those paths, so the stamp is zero, the fallback is reconciliation time, and the round still cancels. The author's test passes only because the mock's `finalizeUnsettled` uses `safeTransferFrom`, and docs/TESTING.md states that assumption explicitly. Severity stays low. My scratch test leaves the chain in the real post-delivery state and fails on this tree with \"round cancelled: 7 != 3\"; its source is attached as the proof.\n\n**Second pass.** I read all changed lines as new code and the rest of the contracts once more. Nothing else rose to a reproducible defect. Two notes went into coverage rows rather than findings: the router cannot re-register the same pool after an FWA rewards-module swap, which is fail-closed by design, and anyone can route their own FWA purchase through the router, which only adds builder credit to the company.\n\n**Coverage.** All 41 entry points are answered, plus four rows for the prior findings: 41 holds, 4 finding, 0 unreached.","treeHash":null,"usage":{"cachedInputTokens":2402792,"inputTokens":610,"model":"claude-fable-5-1","outputTokens":45638,"runtime":"claude","turns":46,"wallClockMs":803365}}],"verification":[{"checks":[{"durationMs":20204,"exitCode":0,"name":"build","output":"Compiling 29 files with Solc 0.8.26\nSolc 0.8.26 finished in 20.11s\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:102:9\n    │\n102 │         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:80:9\n   │\n80 │         address lottery_,\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/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[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[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[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SorpheraVaultFactory.sol:21:25\n   │\n21 │     function setLottery(address lottery_) external onlyOwner {\n   │                         ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\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:36:9\n   │\n36 │         emit VaultCreated(msg.sender, game, round, address(vault));\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/SorpheraRouter.sol:22:24\n   │\n22 │     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:22:39\n   │\n22 │     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:75:15\n   │\n75 │         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:71:13\n   │\n71 │             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:79:9\n   │\n79 │         emit AcquisitionForwarded(msg.sender, p, ids, refund);\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:107:64\n    │\n107 │             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:111:44\n    │\n111 │         require(count > 0 && count <= 8 && block.timestamp <= deadline, \"Sorphera: acquisition batch\");\n    │                                            ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:130:51\n    │\n130 │             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:134:13\n    │\n134 │             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:129:13\n    │\n129 │             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:131:13\n    │\n131 │             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:150:17\n    │\n150 │                 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:152:17\n    │\n152 │                 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:191:21\n    │\n191 │                     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:192:21\n    │\n192 │                     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:198:21\n    │\n198 │                     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:205:13\n    │\n205 │             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:160:9\n    │\n160 │         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:160:9\n    │\n160 │         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:172:37\n    │\n172 │         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:176:37\n    │\n176 │             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:181:21\n    │\n181 │                 try IERC721(l.collection).ownerOf(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:191:21\n    │\n191 │                     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:193:28\n    │\n193 │                 } 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:171:9\n    │\n171 │         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:173:9\n    │\n173 │         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:177:13\n    │\n177 │             require(l.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 `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/SorpheraRouter.sol:92:26\n   │\n92 │         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:85:9\n   │\n85 │         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:88:31\n   │\n88 │         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:95:9\n   │\n95 │         emit BuilderRewardClaimed(address(r), allowance, amount);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SorpheraVault.sol:184:21\n    │\n184 │                 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:213:9\n    │\n213 │         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:223:9\n    │\n223 │         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:224:9\n    │\n224 │         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:223:9\n    │\n223 │         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:232:13\n    │\n232 │         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:237:13\n    │\n237 │             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:244:9\n    │\n244 │         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:245:9\n    │\n245 │         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:244:9\n    │\n244 │         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/SorpheraVault.sol:281:17\n    │\n281 │             try this.deliver{gas: 500000}(index, recipient) {\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/Sorphera.sol:229:17\n    │\n229 │         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:223:13\n    │\n223 │             coordinator.getSubscription(c.subscription);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:281:17\n    │\n281 │             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:282:17\n    │\n282 │                 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:285:17\n    │\n285 │                 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:276:21\n    │\n276 │                     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:269:13\n    │\n269 │             require(!a.claimed, \"Sorphera: NFT claimed\");\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-no-eth]: external call can be reentered before `entered` is updated\n    ╭▸ src/SorpheraRouter.sol:101:9\n    │\n101 │         IRewards(rewardsForPool[p]).withdrawTokenBuyAllowanceAsETH();\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:98:87\n   │\n98 │     function recoverBuilderAllowance(address p, address recipient) external onlyOwner nonReentrant {\n   │                                                                                       ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SorpheraRouter.sol:101:9\n    │\n101 │         IRewards(rewardsForPool[p]).withdrawTokenBuyAllowanceAsETH();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `latestRound` is updated\n    ╭▸ src/Sorphera.sol:260:13\n    │\n260 │ ┏             factory.create(\n261 │ ┃                 game,\n262 │ ┃                 id,\n263 │ ┃                 SorpheraVault.Settings(rules.maxFee, rules.maxTotal, rules.minValue, rules.slippage, cutoff)\n264 │ ┃             )\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:250:17\n    │\n250 │         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:271:9\n    │\n271 │ ┏         emit RoundOpened(\n272 │ ┃             game, id, r.group, r.vault, r.start, cutoff, r.earliestDraw, r.settlementDeadline, r.price\n273 │ ┃         );\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:297:9\n    │\n297 │         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:279:57\n    │\n279 │             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:280:20\n    │\n280 │                 && 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:302:44\n    │\n302 │         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:307:45\n    │\n307 │         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 `entered` is updated\n    ╭▸ src/Sorphera.sol:344:9\n    │\n344 │         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:363:29\n    │\n363 │           uint256 requestId = coordinator.requestRandomWords(\n    │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n364 │ ┃             IVRF.Request(\n365 │ ┃                 c.keyHash,\n366 │ ┃                 c.subscription,\n    ‡ ┃\n372 │ ┃         );\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:350:49\n    │\n350 │         if (game == 1 && r.eligibleNFTs == 0 && block.timestamp >= r.settlementDeadline) {\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:355:13\n    │\n355 │             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[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Sorphera.sol:424:9\n    │\n424 │         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:436:9\n    │\n436 │         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:458:13\n    │\n458 │             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:376:9\n    │\n376 │         emit DrawRequested(game, id, 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:370:40\n    │\n370 │                 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[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/Sorphera.sol:452:19\n    │\n452 │         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:496:13\n    │\n496 │             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:501:9\n    │\n501 │         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:469:9\n    │\n469 │         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:472:9\n    │\n472 │         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:474:13\n    │\n474 │ ┏             require(\n475 │ ┃                 t.combination == r.winningKey && (game == 0 || ticket == r.winningTicket),\n476 │ ┃                 \"Sorphera: not winning ticket\"\n477 │ ┃             );\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:479:13\n    │\n479 │             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:504:85\n    │\n504 │     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:509:9\n    │\n509 │         emit OperatorWithdrawal(recipient, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":157,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 40 tests for test/Sorphera.t.sol:SorpheraTest\n[PASS] testApplicationSizeAndNoEscapeOpcodes() (gas: 15785328)\n[PASS] testBlackoutSlippageBudgetDeadlineAndFailedAcquireRollback() (gas: 4062242)\n[PASS] testBuilderAttributionOverpaymentAndPurchaserRewardsSeparated() (gas: 5824883)\n[PASS] testCallbackAuthenticationBindingDuplicateNoRerollOrCancellation() (gas: 4406996)\n[PASS] testCallbacksOutOfOrderAreBoundToSeparateGamesAndZeroWordValid() (gas: 9399167)\n[PASS] testCustodyBeforeDeadlineCountsEvenWhenReconciledAfter() (gas: 5601517)\n[PASS] testDelayedCashoutNFTGameRemainsIncidentalETHAndCancels() (gas: 4615185)\n[PASS] testETHForcedNFTCustodyIsReservedForWinners() (gas: 5661701)\n[PASS] testETHOneMatchFullPrizeAndOperatorReserve() (gas: 5585186)\n[PASS] testETHRolloverOldTicketsExpireCarryNeverSpent() (gas: 8848942)\n[PASS] testExpiredAcquisitionBlocksDrawUntilReconciled() (gas: 5092129)\n[PASS] testFactoryOnlyRegisteredLotteryCreatesVaults() (gas: 3421968)\n[PASS] testFixedETHSettlementEvenAfterExclusiveWindow() (gas: 4318086)\n[PASS] testForcedDeliveryAfterDeadlineStillCancels() (gas: 4914131)\n[PASS] testFuzzConservationAfterFWAChargesCashoutAndLateFunds(uint8,uint64) (runs: 256, μ: 6616225, ~: 6279874)\nLogs:\n  Bound result 28\n\n[PASS] testFuzzConservationUnusedBudget(uint8,uint64) (runs: 256, μ: 6669580, ~: 5712637)\nLogs:\n  Bound result 2\n\n[PASS] testFuzzSamplingRangeDistinctAndReplay(uint256) (runs: 256, μ: 33165, ~: 33214)\n[PASS] testLargeSalesConstantFinalizationAndPartialLargeInventory() (gas: 186045057)\n[PASS] testLateAllocationAfterCancellationSettlesToDivisibleETHRefund() (gas: 5037685)\n[PASS] testLateETHRecoveryGoesToOriginalWinnerNotNextRound() (gas: 7803080)\n[PASS] testLateNFTDoesNotRescueCancelledRoundAndSharedRecovery() (gas: 5269304)\n[PASS] testMultipleETHMatchesDuplicatesAndRounding() (gas: 4684554)\n[PASS] testNFTCancellationFeesPartialRefundAndLateRecovery() (gas: 5043249)\n[PASS] testNFTFeeReserveCannotBeWithdrawnUntilSuccess() (gas: 5473233)\n[PASS] testNFTOneWinnerAllAssetsAndFunds() (gas: 7608149)\n[PASS] testNFTRolloverInventoryAndResidualCannotRespin() (gas: 9666049)\n[PASS] testNFTTieBreakIncludesEveryDuplicateAndIsDomainSeparated() (gas: 6386276)\n[PASS] testNFTTransferFailuresAreIsolatedAndRetryable() (gas: 6843337)\n[PASS] testNoLateEntryAndBoundedTickets() (gas: 3499224)\n[PASS] testPermanentlyStuckNFTNeverFreezesETHGameAndLateRecoveryFollowsWinner() (gas: 9211576)\n[PASS] testRejectedETHAndReentrancyRetainLiabilities() (gas: 4711656)\n[PASS] testRewardHelperFailureRollsBackAllowanceAndClaims() (gas: 4843954)\n[PASS] testStartsDisabledUnconfiguredAndOwnerSettings() (gas: 880177)\n[PASS] testStuckNFTCustodyRequiredAndRecoveryAfterWindow() (gas: 6138929)\n[PASS] testStuckOnlyNFTCancelsRoundAndLateRecoveryIsSharedByRefundCohort() (gas: 8535891)\n[PASS] testUnclaimedWinningsStayReservedAcrossNewRoundAndWithdrawals() (gas: 8915784)\n[PASS] testUnorderedNumbersIndexedNoTicketTransfer() (gas: 3731176)\n[PASS] testUnwithdrawnFWARefundPreventsDraw() (gas: 4548240)\n[PASS] testWeeklyGamesIndependentFreezeAndPauseClaims() (gas: 8141062)\n[PASS] testZeroTicketsSkipRandomnessAndSeparateGameDoesNotBlock() (gas: 9709990)\nSuite result: ok. 40 passed; 0 failed; 0 skipped; finished in 86.04ms (283.91ms CPU time)\n\nRan 1 test suite in 86.98ms (86.04ms CPU time): 40 tests passed, 0 failed, 0 skipped (40 total tests)\n","passed":true},{"durationMs":34,"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.credit(uint8,uint256)\",\"Sorphera.finalize(uint8,uint256)\",\"Sorphera.openRound(uint8)\",\"Sorphera.proposeOwner(address)\",\"Sorphera.rawFulfillRandomWords(uint256,uint256[])\",\"Sorphera.recordNFT(uint8,uint256,uint256)\",\"Sorphera.requestDraw(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.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\":89,\"docs/DEPENDENCIES.md\":44,\"docs/OPERATIONS.md\":48,\"docs/TESTING.md\":44,\"docs/reference/FWAV2.sepolia.abi.json\":1,\"docs/reference/FWAV2Rewards.sepolia.abi.json\":1,\"docs/reference/sepolia-readback.json\":46,\"docs/reference/verification.json\":21,\"docs/validation.json\":11,\"foundry.toml\":20,\"frontend/README.md\":51,\"frontend/abi/Sorphera.json\":1849,\"frontend/abi/SorpheraRouter.json\":455,\"frontend/abi/SorpheraVault.json\":1020,\"frontend/abi/SorpheraVaultFactory.json\":229,\"frontend/bytecode-sizes.json\":18,\"frontend/configuration.json\":22,\"frontend/deployment.json\":15,\"integration/Sepolia.t.sol\":42,\"launch.json\":9,\"src/Sorphera.sol\":535,\"src/SorpheraRouter.sol\":105,\"src/SorpheraVault.sol\":307,\"src/SorpheraVaultFactory.sol\":38,\"src/interfaces/External.sol\":120,\"src/lib/Balls.sol\":46,\"src/lib/RewardTransfer.sol\":54,\"src/lib/Security.sol\":47,\"test/Sorphera.t.sol\":963,\"test/mocks/ExternalMocks.sol\":404,\"tools/export.py\":19},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"1c2628524b1c64afac28e49c6883d8d14248765b9d81ba97c28d0a3edd0dfe3f","verifiedTreeHash":"c80501263d092f3b2d26d3ec83a4666bfe4c2ce7","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":33303,"exitCode":0,"name":"build","output":"Compiling 32 files with Solc 0.8.26\nSolc 0.8.26 finished in 33.17s\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[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/SorpheraVaultFactory.sol:22:9\n   │\n22 │         emit VaultCreated(msg.sender, game, round, address(vault));\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\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[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-events-arithmetic]: `budget` is changed without an event but is used in arithmetic\n   ╭▸ src/SorpheraVault.sol:98:9\n   │\n98 │         budget += msg.value;\n   │         ━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\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:76:9\n   │\n76 │         address lottery_,\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/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[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[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[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SorpheraRouter.sol:22:39\n   │\n22 │     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:22:24\n   │\n22 │     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:75:15\n   │\n75 │         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:71:13\n   │\n71 │             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:79:9\n   │\n79 │         emit AcquisitionForwarded(msg.sender, p, ids, refund);\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:103:64\n    │\n103 │             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:107:44\n    │\n107 │         require(count > 0 && count <= 8 && block.timestamp <= deadline, \"Sorphera: acquisition batch\");\n    │                                            ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:126:51\n    │\n126 │             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:130:13\n    │\n130 │             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:125:13\n    │\n125 │             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:127:13\n    │\n127 │             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:143:28\n    │\n143 │             if (game == 0) 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:144:18\n    │\n144 │             else 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:180:21\n    │\n180 │                     ISorphera(lottery).recordNFT(game, round);\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:181:21\n    │\n181 │                     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:190:13\n    │\n190 │             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:151:9\n    │\n151 │         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:151:9\n    │\n151 │         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:163:37\n    │\n163 │         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:167:37\n    │\n167 │             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:172:21\n    │\n172 │                 try IERC721(l.collection).ownerOf(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:180:21\n    │\n180 │                     ISorphera(lottery).recordNFT(game, round);\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:184:21\n    │\n184 │                 if (pool.stuckNFTRecipient(a.listingId) == address(this)) terminal = false;\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:162:9\n    │\n162 │         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:164:9\n    │\n164 │         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:168:13\n    │\n168 │             require(l.purchaser == address(this), \"Sorphera: purchaser\");\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/SorpheraVault.sol:175:21\n    │\n175 │                 if (held) {\n    │                     ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\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/SorpheraRouter.sol:92:26\n   │\n92 │         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:85:9\n   │\n85 │         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:88:31\n   │\n88 │         require(minOut > 0 && block.timestamp <= deadline, \"Sorphera: reward bounds\");\n   │                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `requestState` is updated\n    ╭▸ src/SorpheraVault.sol:198:9\n    │\n198 │         pool.recoverStuckNFT(a.listingId);\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/SorpheraRouter.sol:95:9\n   │\n95 │         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 `entered` is updated\n    ╭▸ src/SorpheraVault.sol:208:9\n    │\n208 │         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:209:9\n    │\n209 │         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:208:9\n    │\n208 │         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:217:13\n    │\n217 │         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:222:13\n    │\n222 │             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:229:9\n    │\n229 │         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:230:9\n    │\n230 │         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:229:9\n    │\n229 │         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/SorpheraVault.sol:266:17\n    │\n266 │             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:266:17\n    │\n266 │             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:267:17\n    │\n267 │                 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:270:17\n    │\n270 │                 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:261:21\n    │\n261 │                     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:254:13\n    │\n254 │             require(!a.claimed, \"Sorphera: NFT claimed\");\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-no-eth]: external call can be reentered before `entered` is updated\n    ╭▸ src/SorpheraRouter.sol:101:9\n    │\n101 │         IRewards(rewardsForPool[p]).withdrawTokenBuyAllowanceAsETH();\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:98:87\n   │\n98 │     function recoverBuilderAllowance(address p, address recipient) external onlyOwner nonReentrant {\n   │                                                                                       ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SorpheraRouter.sol:101:9\n    │\n101 │         IRewards(rewardsForPool[p]).withdrawTokenBuyAllowanceAsETH();\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:228:17\n    │\n228 │         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:222:13\n    │\n222 │             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 `latestRound` is updated\n    ╭▸ src/Sorphera.sol:259:13\n    │\n259 │ ┏             factory.create(\n260 │ ┃                 game,\n261 │ ┃                 id,\n262 │ ┃                 SorpheraVault.Settings(rules.maxFee, rules.maxTotal, rules.minValue, rules.slippage, cutoff)\n263 │ ┃             )\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:249:17\n    │\n249 │         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:270:9\n    │\n270 │ ┏         emit RoundOpened(\n271 │ ┃             game, id, r.group, r.vault, r.start, cutoff, r.earliestDraw, r.settlementDeadline, r.price\n272 │ ┃         );\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:296:9\n    │\n296 │         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:278:57\n    │\n278 │             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:279:20\n    │\n279 │                 && 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:301:44\n    │\n301 │         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:306:45\n    │\n306 │         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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Sorphera.sol:327:37\n    │\n327 │         if (g.terminalRound == 0 && block.timestamp < active.settlementDeadline) ++active.eligibleNFTs;\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `entered` is updated\n    ╭▸ src/Sorphera.sol:337:9\n    │\n337 │         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:356:29\n    │\n356 │           uint256 requestId = coordinator.requestRandomWords(\n    │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n357 │ ┃             IVRF.Request(\n358 │ ┃                 c.keyHash,\n359 │ ┃                 c.subscription,\n    ‡ ┃\n365 │ ┃         );\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:343:49\n    │\n343 │         if (game == 1 && r.eligibleNFTs == 0 && block.timestamp >= r.settlementDeadline) {\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:348:13\n    │\n348 │             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[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Sorphera.sol:417:9\n    │\n417 │         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:429:9\n    │\n429 │         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:451:13\n    │\n451 │             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:369:9\n    │\n369 │         emit DrawRequested(game, id, 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:363:40\n    │\n363 │                 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[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/Sorphera.sol:445:19\n    │\n445 │         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:489:13\n    │\n489 │             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:494:9\n    │\n494 │         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:462:9\n    │\n462 │         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:465:9\n    │\n465 │         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:467:13\n    │\n467 │ ┏             require(\n468 │ ┃                 t.combination == r.winningKey && (game == 0 || ticket == r.winningTicket),\n469 │ ┃                 \"Sorphera: not winning ticket\"\n470 │ ┃             );\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:472:13\n    │\n472 │             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:497:85\n    │\n497 │     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:502:9\n    │\n502 │         emit OperatorWithdrawal(recipient, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":11417,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 13 tests for test/SorpheraAdversarial.t.sol:SorpheraAdversarialTest\n[PASS] testBadCallbackShapeUnknownRequestAndFutureRulesCannotMutateActiveRound() (gas: 7456639)\n[PASS] testCancellationAccumulatesSubTicketLateRecoveriesWithoutFees() (gas: 4474256)\n[PASS] testDuplicateClaimIdsNeverMultiplyPayoutAndMixedOwnerBatchRollsBack() (gas: 4573560)\n[PASS] testEmptyAndOversizedMaintenanceAndClaimBatchesReject() (gas: 3648851)\n[PASS] testEmptyAndUnderpaidAndOverpaidSalesAreAtomic() (gas: 3375738)\n[PASS] testEveryCombinationHasUniqueKeyAndAllSixOrdersMatch() (gas: 352960341)\n[PASS] testFuzzPriceEdgesKeepExactSplitAndFullBatchRefund(uint256,uint8) (runs: 1000, μ: 5781574, ~: 4879863)\nLogs:\n  Bound result 2\n  Bound result 55\n\n[PASS] testInvalidTicketAtEndRollsBackEntireBatchAndBothAccounts() (gas: 8555287)\n[PASS] testMaximumTicketPriceFullBatchRefundsAllUnspentFunds() (gas: 11699912)\nLogs:\n  Bound result 10000000000000000000\n  Bound result 100\n\n[PASS] testMinimumTicketPriceSingleEntryRefundsAllUnspentFunds() (gas: 3854585)\nLogs:\n  Bound result 1\n  Bound result 1\n\n[PASS] testNFTReceiverCannotReenterClaimAnotherAsset() (gas: 6687451)\n[PASS] testPauseBlocksSaleButPreservesPermissionlessSettlement() (gas: 5683573)\n[PASS] testVaultPrivilegeAndCrossRoundRequestBoundaries() (gas: 7183179)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 254.60ms (377.57ms CPU time)\n\nRan 34 tests for test/Sorphera.t.sol:SorpheraTest\n[PASS] testApplicationSizeAndNoEscapeOpcodes() (gas: 15208301)\n[PASS] testBlackoutSlippageBudgetDeadlineAndFailedAcquireRollback() (gas: 3984961)\n[PASS] testBuilderAttributionOverpaymentAndPurchaserRewardsSeparated() (gas: 5747476)\n[PASS] testCallbackAuthenticationBindingDuplicateNoRerollOrCancellation() (gas: 4329646)\n[PASS] testCallbacksOutOfOrderAreBoundToSeparateGamesAndZeroWordValid() (gas: 9221392)\n[PASS] testDelayedCashoutNFTGameRemainsIncidentalETHAndCancels() (gas: 4537856)\n[PASS] testETHForcedNFTCustodyIsReservedForWinners() (gas: 5562981)\n[PASS] testETHOneMatchFullPrizeAndOperatorReserve() (gas: 5507898)\n[PASS] testETHRolloverOldTicketsExpireCarryNeverSpent() (gas: 8694336)\n[PASS] testExpiredAcquisitionBlocksDrawUntilReconciled() (gas: 5014802)\n[PASS] testFixedETHSettlementEvenAfterExclusiveWindow() (gas: 4240768)\n[PASS] testFuzzConservationAfterFWAChargesCashoutAndLateFunds(uint8,uint64) (runs: 256, μ: 6623159, ~: 6206483)\nLogs:\n  Bound result 3\n\n[PASS] testFuzzConservationUnusedBudget(uint8,uint64) (runs: 256, μ: 6357893, ~: 5516539)\nLogs:\n  Bound result 1\n\n[PASS] testFuzzSamplingRangeDistinctAndReplay(uint256) (runs: 256, μ: 33047, ~: 33008)\n[PASS] testLargeSalesConstantFinalizationAndPartialLargeInventory() (gas: 183546456)\n[PASS] testLateETHRecoveryGoesToOriginalWinnerNotNextRound() (gas: 7648598)\n[PASS] testLateNFTDoesNotRescueCancelledRoundAndSharedRecovery() (gas: 5168631)\n[PASS] testMultipleETHMatchesDuplicatesAndRounding() (gas: 4607272)\n[PASS] testNFTCancellationFeesPartialRefundAndLateRecovery() (gas: 4966081)\n[PASS] testNFTFeeReserveCannotBeWithdrawnUntilSuccess() (gas: 5372663)\n[PASS] testNFTOneWinnerAllAssetsAndFunds() (gas: 7461005)\n[PASS] testNFTRolloverInventoryAndResidualCannotRespin() (gas: 9488221)\n[PASS] testNFTTieBreakIncludesEveryDuplicateAndIsDomainSeparated() (gas: 6262555)\n[PASS] testNFTTransferFailuresAreIsolatedAndRetryable() (gas: 6719379)\n[PASS] testNoLateEntryAndBoundedTickets() (gas: 3422050)\n[PASS] testRejectedETHAndReentrancyRetainLiabilities() (gas: 4634323)\n[PASS] testRewardHelperFailureRollsBackAllowanceAndClaims() (gas: 4766636)\n[PASS] testStartsDisabledUnconfiguredAndOwnerSettings() (gas: 235690)\n[PASS] testStuckNFTCustodyRequiredAndRecoveryAfterWindow() (gas: 5666221)\n[PASS] testUnclaimedWinningsStayReservedAcrossNewRoundAndWithdrawals() (gas: 8761318)\n[PASS] testUnorderedNumbersIndexedNoTicketTransfer() (gas: 3653914)\n[PASS] testUnwithdrawnFWARefundPreventsDraw() (gas: 4470727)\n[PASS] testWeeklyGamesIndependentFreezeAndPauseClaims() (gas: 7986505)\n[PASS] testZeroTicketsSkipRandomnessAndSeparateGameDoesNotBlock() (gas: 9478358)\nSuite result: ok. 34 passed; 0 failed; 0 skipped; finished in 254.64ms (413.46ms 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_vaultRequestsAndCustodyRemainBacked\n SorpheraInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------+---------------+-------+---------+----------╮\n| Contract        | Selector      | Calls | Reverts | Discards |\n+==============================================================+\n| SorpheraHandler | acquire       | 1206  | 0       | 0        |\n|-----------------+---------------+-------+---------+----------|\n| SorpheraHandler | advance       | 1139  | 0       | 0        |\n|-----------------+---------------+-------+---------+----------|\n| SorpheraHandler | builderReward | 1159  | 0       | 0        |\n|-----------------+---------------+-------+---------+----------|\n| SorpheraHandler | buy           | 2329  | 0       | 0        |\n|-----------------+---------------+-------+---------+----------|\n| SorpheraHandler | claim         | 1187  | 0       | 0        |\n|-----------------+---------------+-------+---------+----------|\n| SorpheraHandler | claimNFT      | 1218  | 0       | 0        |\n|-----------------+---------------+-------+---------+----------|\n| SorpheraHandler | pause         | 1148  | 0       | 0        |\n|-----------------+---------------+-------+---------+----------|\n| SorpheraHandler | queueTokens   | 1179  | 0       | 0        |\n|-----------------+---------------+-------+---------+----------|\n| SorpheraHandler | receiveTokens | 1168  | 0       | 0        |\n|-----------------+---------------+-------+---------+----------|\n| SorpheraHandler | resolve       | 1185  | 0       | 0        |\n|-----------------+---------------+-------+---------+----------|\n| SorpheraHandler | rewardEpoch   | 1180  | 0       | 0        |\n|-----------------+---------------+-------+---------+----------|\n| SorpheraHandler | syncOrDonate  | 1130  | 0       | 0        |\n|-----------------+---------------+-------+---------+----------|\n| SorpheraHandler | withdraw      | 1156  | 0       | 0        |\n╰-----------------+---------------+-------+---------+----------╯\n\nLogs:\n  Bound result 0\n  Bound result 4\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 1\n  Bound result 20000000000000000\n  Bound result 2\n  Bound result 20000000000000000\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 0\n  Bound result 1\n  Bound result 1\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 0\n  Bound result 2\n  Bound result 0\n  Bound result 1\n  Bound result 12\n  Bound result 2\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 4\n  Bound result 2\n  Bound result 1\n  Bound result 0\n  Bound result 3\n  Bound result 0\n  Bound result 2\n  Bound result 2\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 11089\n  Bound result 3\n  Bound result 353073666\n  Bound result 0\n  Bound result 1\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 1\n  Bound result 1\n  Bound result 1\n  Bound result 59663943782787764020\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 3\n  Bound result 0\n  Bound result 1\n  Bound result 14821\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 0\n  Bound result 2\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 500000000000000\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 21881\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 18488\n  Bound result 0\n  Bound result 1\n\n[PASS] testHandlerPinnedLifecycleExercisesClaimsAndLateRecoveries() (gas: 5434452)\nLogs:\n  Bound result 0\n  Bound result 4\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 1\n  Bound result 20000000000000000\n  Bound result 2\n  Bound result 20000000000000000\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 2000000000000000\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 0\n  Bound result 1\n  Bound result 1\n  Bound result 0\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 11.33s (11.33s CPU time)\n\nRan 3 test suites in 11.33s (11.84s CPU time): 49 tests passed, 0 failed, 0 skipped (49 total tests)\n","passed":true},{"durationMs":46,"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.credit(uint8,uint256)\",\"Sorphera.finalize(uint8,uint256)\",\"Sorphera.openRound(uint8)\",\"Sorphera.proposeOwner(address)\",\"Sorphera.rawFulfillRandomWords(uint256,uint256[])\",\"Sorphera.recordNFT(uint8,uint256)\",\"Sorphera.requestDraw(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.process(uint256)\",\"SorpheraVault.receive()\",\"SorpheraVault.reconcile(uint256[])\",\"SorpheraVault.recoverNFT(uint256)\",\"SorpheraVault.recoverRefund()\",\"SorpheraVault.settle(uint256)\",\"SorpheraVault.syncETH()\",\"SorpheraVaultFactory.create(uint8,uint256,(uint256,uint256,uint256,uint256,uint256))\"],\"files\":{\".gitignore\":3,\"README.md\":86,\"docs/DEPENDENCIES.md\":44,\"docs/OPERATIONS.md\":47,\"docs/TESTING.md\":40,\"docs/reference/FWAV2.sepolia.abi.json\":1,\"docs/reference/FWAV2Rewards.sepolia.abi.json\":1,\"docs/reference/sepolia-readback.json\":46,\"docs/reference/verification.json\":21,\"docs/validation.json\":11,\"foundry.toml\":20,\"frontend/README.md\":49,\"frontend/abi/Sorphera.json\":1820,\"frontend/abi/SorpheraRouter.json\":455,\"frontend/abi/SorpheraVault.json\":977,\"frontend/abi/SorpheraVaultFactory.json\":113,\"frontend/bytecode-sizes.json\":18,\"frontend/configuration.json\":21,\"frontend/deployment.json\":14,\"integration/Sepolia.t.sol\":42,\"launch.json\":9,\"src/Sorphera.sol\":528,\"src/SorpheraRouter.sol\":105,\"src/SorpheraVault.sol\":291,\"src/SorpheraVaultFactory.sol\":24,\"src/interfaces/External.sol\":119,\"src/lib/Balls.sol\":46,\"src/lib/RewardTransfer.sol\":54,\"src/lib/Security.sol\":47,\"test/README.md\":13,\"test/Sorphera.t.sol\":796,\"test/SorpheraAdversarial.t.sol\":308,\"test/SorpheraInvariant.t.sol\":516,\"test/helpers/SorpheraFixture.sol\":121,\"test/mocks/ExternalMocks.sol\":404,\"tools/export.py\":19},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"617af1407ac1e431405b372336a3c95aeff275598f777a7814a9221cef1e0661","verifiedTreeHash":"caf2caa18029bc621d62e04bee4224525831df89","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":39327,"exitCode":0,"name":"build","output":"Compiling 32 files with Solc 0.8.26\nSolc 0.8.26 finished in 39.20s\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[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[missing-events-arithmetic]: `budget` is changed without an event but is used in arithmetic\n    ╭▸ src/SorpheraVault.sol:102:9\n    │\n102 │         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: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:80:9\n   │\n80 │         address lottery_,\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/SorpheraVaultFactory.sol:21:25\n   │\n21 │     function setLottery(address lottery_) external onlyOwner {\n   │                         ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\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:36:9\n   │\n36 │         emit VaultCreated(msg.sender, game, round, address(vault));\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/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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SorpheraVault.sol:107:64\n    │\n107 │             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:111:44\n    │\n111 │         require(count > 0 && count <= 8 && block.timestamp <= deadline, \"Sorphera: acquisition batch\");\n    │                                            ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:130:51\n    │\n130 │             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:134:13\n    │\n134 │             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:129:13\n    │\n129 │             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:131:13\n    │\n131 │             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:150:17\n    │\n150 │                 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:152:17\n    │\n152 │                 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:191:21\n    │\n191 │                     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:192:21\n    │\n192 │                     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:198:21\n    │\n198 │                     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:205:13\n    │\n205 │             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:160:9\n    │\n160 │         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:160:9\n    │\n160 │         pool.processAcquisitions(count);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\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:22:39\n   │\n22 │     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:22:24\n   │\n22 │     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:75:15\n   │\n75 │         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[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:172:37\n    │\n172 │         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:176:37\n    │\n176 │             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:181:21\n    │\n181 │                 try IERC721(l.collection).ownerOf(l.tokenId) returns (address holder) {\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\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[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:191:21\n    │\n191 │                     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:193:28\n    │\n193 │                 } else if (pool.stuckNFTRecipient(a.listingId) == address(this)) {\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/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[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-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[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SorpheraVault.sol:171:9\n    │\n171 │         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:173:9\n    │\n173 │         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:177:13\n    │\n177 │             require(l.purchaser == address(this), \"Sorphera: purchaser\");\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/SorpheraRouter.sol:71:13\n   │\n71 │             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:79:9\n   │\n79 │         emit AcquisitionForwarded(msg.sender, p, ids, refund);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SorpheraVault.sol:184:21\n    │\n184 │                 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:213:9\n    │\n213 │         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:223:9\n    │\n223 │         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:224:9\n    │\n224 │         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:223:9\n    │\n223 │         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:232:13\n    │\n232 │         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:237:13\n    │\n237 │             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:244:9\n    │\n244 │         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:245:9\n    │\n245 │         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:244:9\n    │\n244 │         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:281:17\n    │\n281 │             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:281:17\n    │\n281 │             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:282:17\n    │\n282 │                 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:285:17\n    │\n285 │                 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:276:21\n    │\n276 │                     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:269:13\n    │\n269 │             require(!a.claimed, \"Sorphera: NFT claimed\");\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-no-eth]: external call can be reentered before `entered` is updated\n   ╭▸ src/SorpheraRouter.sol:92:26\n   │\n92 │         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:85:9\n   │\n85 │         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:88:31\n   │\n88 │         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:95:9\n   │\n95 │         emit BuilderRewardClaimed(address(r), allowance, amount);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/Sorphera.sol:229:17\n    │\n229 │         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:223:13\n    │\n223 │             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:101:9\n    │\n101 │         IRewards(rewardsForPool[p]).withdrawTokenBuyAllowanceAsETH();\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:98:87\n   │\n98 │     function recoverBuilderAllowance(address p, address recipient) external onlyOwner nonReentrant {\n   │                                                                                       ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SorpheraRouter.sol:101:9\n    │\n101 │         IRewards(rewardsForPool[p]).withdrawTokenBuyAllowanceAsETH();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `latestRound` is updated\n    ╭▸ src/Sorphera.sol:260:13\n    │\n260 │ ┏             factory.create(\n261 │ ┃                 game,\n262 │ ┃                 id,\n263 │ ┃                 SorpheraVault.Settings(rules.maxFee, rules.maxTotal, rules.minValue, rules.slippage, cutoff)\n264 │ ┃             )\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:250:17\n    │\n250 │         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:271:9\n    │\n271 │ ┏         emit RoundOpened(\n272 │ ┃             game, id, r.group, r.vault, r.start, cutoff, r.earliestDraw, r.settlementDeadline, r.price\n273 │ ┃         );\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:297:9\n    │\n297 │         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:279:57\n    │\n279 │             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:280:20\n    │\n280 │                 && 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:302:44\n    │\n302 │         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:307:45\n    │\n307 │         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 `entered` is updated\n    ╭▸ src/Sorphera.sol:344:9\n    │\n344 │         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:363:29\n    │\n363 │           uint256 requestId = coordinator.requestRandomWords(\n    │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n364 │ ┃             IVRF.Request(\n365 │ ┃                 c.keyHash,\n366 │ ┃                 c.subscription,\n    ‡ ┃\n372 │ ┃         );\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:350:49\n    │\n350 │         if (game == 1 && r.eligibleNFTs == 0 && block.timestamp >= r.settlementDeadline) {\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:355:13\n    │\n355 │             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[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Sorphera.sol:424:9\n    │\n424 │         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:436:9\n    │\n436 │         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:458:13\n    │\n458 │             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:376:9\n    │\n376 │         emit DrawRequested(game, id, 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:370:40\n    │\n370 │                 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[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/Sorphera.sol:452:19\n    │\n452 │         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:496:13\n    │\n496 │             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:501:9\n    │\n501 │         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:469:9\n    │\n469 │         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:472:9\n    │\n472 │         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:474:13\n    │\n474 │ ┏             require(\n475 │ ┃                 t.combination == r.winningKey && (game == 0 || ticket == r.winningTicket),\n476 │ ┃                 \"Sorphera: not winning ticket\"\n477 │ ┃             );\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:479:13\n    │\n479 │             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:504:85\n    │\n504 │     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:509:9\n    │\n509 │         emit OperatorWithdrawal(recipient, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":13662,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 40 tests for test/Sorphera.t.sol:SorpheraTest\n[PASS] testApplicationSizeAndNoEscapeOpcodes() (gas: 15785328)\n[PASS] testBlackoutSlippageBudgetDeadlineAndFailedAcquireRollback() (gas: 4062242)\n[PASS] testBuilderAttributionOverpaymentAndPurchaserRewardsSeparated() (gas: 5824883)\n[PASS] testCallbackAuthenticationBindingDuplicateNoRerollOrCancellation() (gas: 4406996)\n[PASS] testCallbacksOutOfOrderAreBoundToSeparateGamesAndZeroWordValid() (gas: 9399167)\n[PASS] testCustodyBeforeDeadlineCountsEvenWhenReconciledAfter() (gas: 5601517)\n[PASS] testDelayedCashoutNFTGameRemainsIncidentalETHAndCancels() (gas: 4615185)\n[PASS] testETHForcedNFTCustodyIsReservedForWinners() (gas: 5661701)\n[PASS] testETHOneMatchFullPrizeAndOperatorReserve() (gas: 5585186)\n[PASS] testETHRolloverOldTicketsExpireCarryNeverSpent() (gas: 8848942)\n[PASS] testExpiredAcquisitionBlocksDrawUntilReconciled() (gas: 5092129)\n[PASS] testFactoryOnlyRegisteredLotteryCreatesVaults() (gas: 3421968)\n[PASS] testFixedETHSettlementEvenAfterExclusiveWindow() (gas: 4318086)\n[PASS] testForcedDeliveryAfterDeadlineStillCancels() (gas: 4914131)\n[PASS] testFuzzConservationAfterFWAChargesCashoutAndLateFunds(uint8,uint64) (runs: 256, μ: 6613018, ~: 6141619)\nLogs:\n  Bound result 5\n\n[PASS] testFuzzConservationUnusedBudget(uint8,uint64) (runs: 256, μ: 6312727, ~: 5474805)\nLogs:\n  Bound result 2\n\n[PASS] testFuzzSamplingRangeDistinctAndReplay(uint256) (runs: 256, μ: 33173, ~: 33214)\n[PASS] testLargeSalesConstantFinalizationAndPartialLargeInventory() (gas: 186045057)\n[PASS] testLateAllocationAfterCancellationSettlesToDivisibleETHRefund() (gas: 5037685)\n[PASS] testLateETHRecoveryGoesToOriginalWinnerNotNextRound() (gas: 7803080)\n[PASS] testLateNFTDoesNotRescueCancelledRoundAndSharedRecovery() (gas: 5269304)\n[PASS] testMultipleETHMatchesDuplicatesAndRounding() (gas: 4684554)\n[PASS] testNFTCancellationFeesPartialRefundAndLateRecovery() (gas: 5043249)\n[PASS] testNFTFeeReserveCannotBeWithdrawnUntilSuccess() (gas: 5473233)\n[PASS] testNFTOneWinnerAllAssetsAndFunds() (gas: 7608149)\n[PASS] testNFTRolloverInventoryAndResidualCannotRespin() (gas: 9666049)\n[PASS] testNFTTieBreakIncludesEveryDuplicateAndIsDomainSeparated() (gas: 6386276)\n[PASS] testNFTTransferFailuresAreIsolatedAndRetryable() (gas: 6843337)\n[PASS] testNoLateEntryAndBoundedTickets() (gas: 3499224)\n[PASS] testPermanentlyStuckNFTNeverFreezesETHGameAndLateRecoveryFollowsWinner() (gas: 9211576)\n[PASS] testRejectedETHAndReentrancyRetainLiabilities() (gas: 4711656)\n[PASS] testRewardHelperFailureRollsBackAllowanceAndClaims() (gas: 4843954)\n[PASS] testStartsDisabledUnconfiguredAndOwnerSettings() (gas: 880177)\n[PASS] testStuckNFTCustodyRequiredAndRecoveryAfterWindow() (gas: 6138929)\n[PASS] testStuckOnlyNFTCancelsRoundAndLateRecoveryIsSharedByRefundCohort() (gas: 8535891)\n[PASS] testUnclaimedWinningsStayReservedAcrossNewRoundAndWithdrawals() (gas: 8915784)\n[PASS] testUnorderedNumbersIndexedNoTicketTransfer() (gas: 3731176)\n[PASS] testUnwithdrawnFWARefundPreventsDraw() (gas: 4548240)\n[PASS] testWeeklyGamesIndependentFreezeAndPauseClaims() (gas: 8141062)\n[PASS] testZeroTicketsSkipRandomnessAndSeparateGameDoesNotBlock() (gas: 9709990)\nSuite result: ok. 40 passed; 0 failed; 0 skipped; finished in 123.38ms (440.65ms CPU time)\n\nRan 17 tests for test/SorpheraAdversarial.t.sol:SorpheraAdversarialTest\n[PASS] testBadCallbackShapeUnknownRequestAndFutureRulesCannotMutateActiveRound() (gas: 7611163)\n[PASS] testCancellationAccumulatesSubTicketLateRecoveriesWithoutFees() (gas: 4551456)\n[PASS] testDuplicateClaimIdsNeverMultiplyPayoutAndMixedOwnerBatchRollsBack() (gas: 4650830)\n[PASS] testEmptyAndOversizedMaintenanceAndClaimBatchesReject() (gas: 3726162)\n[PASS] testEmptyAndUnderpaidAndOverpaidSalesAreAtomic() (gas: 3452999)\n[PASS] testEveryCombinationHasUniqueKeyAndAllSixOrdersMatch() (gas: 354288209)\n[PASS] testForgedReceiptStampCannotRescueLateDeliveryAndDeadlineBoundaryIsStrict() (gas: 5345495)\n[PASS] testFuzzPriceEdgesKeepExactSplitAndFullBatchRefund(uint256,uint8) (runs: 1000, μ: 6092509, ~: 5314320)\nLogs:\n  Bound result 124383573254415\n  Bound result 99\n\n[PASS] testFuzzReceiptTimeNotReconcileTimeDecidesCancellation(uint256,bool) (runs: 512, μ: 5093551, ~: 5158115)\nLogs:\n  Bound result 1716102\n\n[PASS] testInvalidTicketAtEndRollsBackEntireBatchAndBothAccounts() (gas: 8632531)\n[PASS] testMaximumTicketPriceFullBatchRefundsAllUnspentFunds() (gas: 11777152)\nLogs:\n  Bound result 10000000000000000000\n  Bound result 100\n\n[PASS] testMinimumTicketPriceSingleEntryRefundsAllUnspentFunds() (gas: 3931873)\nLogs:\n  Bound result 1\n  Bound result 1\n\n[PASS] testNFTReceiverCannotReenterClaimAnotherAsset() (gas: 6811356)\n[PASS] testPauseBlocksSaleButPreservesPermissionlessSettlement() (gas: 5783998)\n[PASS] testSecondLotteryCannotValidateAgainstBoundFactoryOrCreateVaults() (gas: 3897053)\n[PASS] testStuckAssetRecoveredIntoRolledRoundJoinsCarryoverForNextWinnerOnly() (gas: 11113347)\n[PASS] testVaultPrivilegeAndCrossRoundRequestBoundaries() (gas: 7392777)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 269.88ms (556.61ms 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         | 1034  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | advance         | 988   | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | builderReward   | 1034  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | buy             | 2029  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | claim           | 983   | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | claimNFT        | 1043  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | forceSettlement | 989   | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | pause           | 1056  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | queueTokens     | 1034  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | receiveTokens   | 1036  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | recoverStuck    | 1043  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | resolve         | 1057  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | rewardEpoch     | 1023  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | syncOrDonate    | 1042  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| SorpheraHandler | withdraw        | 993   | 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 2\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 0\n  Bound result 1\n  Bound result 244\n  Bound result 2\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 100000000000000000000\n  Bound result 1\n  Bound result 0\n  Bound result 0\n  Bound result 2\n  Bound result 3\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 2500000\n  Bound result 1\n  Bound result 3\n  Bound result 0\n  Bound result 1\n  Bound result 27504721603855484579\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 0\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 12\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 2\n  Bound result 1\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 2\n  Bound result 1\n  Bound result 1\n  Bound result 5166\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 19720\n  Bound result 123\n  Bound result 0\n  Bound result 1\n  Bound result 61\n  Bound result 0\n  Bound result 1\n  Bound result 6\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 0\n  Bound result 0\n  Bound result 1\n  Bound result 6\n  Bound result 0\n\n[PASS] testHandlerPinnedLifecycleExercisesClaimsAndLateRecoveries() (gas: 14178306)\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 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.58s (13.58s CPU time)\n\nRan 3 test suites in 13.58s (13.97s CPU time): 59 tests passed, 0 failed, 0 skipped (59 total tests)\n","passed":true},{"durationMs":56,"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.credit(uint8,uint256)\",\"Sorphera.finalize(uint8,uint256)\",\"Sorphera.openRound(uint8)\",\"Sorphera.proposeOwner(address)\",\"Sorphera.rawFulfillRandomWords(uint256,uint256[])\",\"Sorphera.recordNFT(uint8,uint256,uint256)\",\"Sorphera.requestDraw(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.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\":89,\"docs/DEPENDENCIES.md\":44,\"docs/OPERATIONS.md\":48,\"docs/TESTING.md\":44,\"docs/reference/FWAV2.sepolia.abi.json\":1,\"docs/reference/FWAV2Rewards.sepolia.abi.json\":1,\"docs/reference/sepolia-readback.json\":46,\"docs/reference/verification.json\":21,\"docs/validation.json\":11,\"foundry.toml\":20,\"frontend/README.md\":51,\"frontend/abi/Sorphera.json\":1849,\"frontend/abi/SorpheraRouter.json\":455,\"frontend/abi/SorpheraVault.json\":1020,\"frontend/abi/SorpheraVaultFactory.json\":229,\"frontend/bytecode-sizes.json\":18,\"frontend/configuration.json\":22,\"frontend/deployment.json\":15,\"integration/Sepolia.t.sol\":42,\"launch.json\":9,\"src/Sorphera.sol\":535,\"src/SorpheraRouter.sol\":105,\"src/SorpheraVault.sol\":307,\"src/SorpheraVaultFactory.sol\":38,\"src/interfaces/External.sol\":120,\"src/lib/Balls.sol\":46,\"src/lib/RewardTransfer.sol\":54,\"src/lib/Security.sol\":47,\"test/README.md\":17,\"test/Sorphera.t.sol\":963,\"test/SorpheraAdversarial.t.sol\":457,\"test/SorpheraInvariant.t.sol\":657,\"test/helpers/SorpheraFixture.sol\":122,\"test/mocks/ExternalMocks.sol\":404,\"tools/export.py\":19},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"7114905b598a995d4924bec189605ee2ca6474b2759f24f86c0187acd24f788c","verifiedTreeHash":"7efe572c96655a5677e984e256d23479be7dfa48","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":16581,"exitCode":0,"name":"build","output":"Compiling 29 files with Solc 0.8.26\nSolc 0.8.26 finished in 16.48s\nCompiler run successful!\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:22:9\n   │\n22 │         emit VaultCreated(msg.sender, game, round, address(vault));\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/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[missing-events-arithmetic]: `budget` is changed without an event but is used in arithmetic\n   ╭▸ src/SorpheraVault.sol:98:9\n   │\n98 │         budget += msg.value;\n   │         ━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\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[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:76:9\n   │\n76 │         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[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[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SorpheraRouter.sol:22:24\n   │\n22 │     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:22:39\n   │\n22 │     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:75:15\n   │\n75 │         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[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[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:71:13\n   │\n71 │             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:79:9\n   │\n79 │         emit AcquisitionForwarded(msg.sender, p, ids, refund);\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:103:64\n    │\n103 │             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:107:44\n    │\n107 │         require(count > 0 && count <= 8 && block.timestamp <= deadline, \"Sorphera: acquisition batch\");\n    │                                            ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:126:51\n    │\n126 │             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:130:13\n    │\n130 │             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:125:13\n    │\n125 │             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:127:13\n    │\n127 │             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:143:28\n    │\n143 │             if (game == 0) 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:144:18\n    │\n144 │             else 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:180:21\n    │\n180 │                     ISorphera(lottery).recordNFT(game, round);\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:181:21\n    │\n181 │                     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:190:13\n    │\n190 │             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:151:9\n    │\n151 │         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:151:9\n    │\n151 │         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:163:37\n    │\n163 │         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:167:37\n    │\n167 │             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:172:21\n    │\n172 │                 try IERC721(l.collection).ownerOf(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:180:21\n    │\n180 │                     ISorphera(lottery).recordNFT(game, round);\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:184:21\n    │\n184 │                 if (pool.stuckNFTRecipient(a.listingId) == address(this)) terminal = false;\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:162:9\n    │\n162 │         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:164:9\n    │\n164 │         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:168:13\n    │\n168 │             require(l.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 `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/SorpheraRouter.sol:92:26\n   │\n92 │         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:85:9\n   │\n85 │         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:88:31\n   │\n88 │         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:95:9\n   │\n95 │         emit BuilderRewardClaimed(address(r), allowance, amount);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/Sorphera.sol:228:17\n    │\n228 │         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:222:13\n    │\n222 │             coordinator.getSubscription(c.subscription);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SorpheraVault.sol:175:21\n    │\n175 │                 if (held) {\n    │                     ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-no-eth]: external call can be reentered before `entered` is updated\n    ╭▸ src/SorpheraRouter.sol:101:9\n    │\n101 │         IRewards(rewardsForPool[p]).withdrawTokenBuyAllowanceAsETH();\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:98:87\n   │\n98 │     function recoverBuilderAllowance(address p, address recipient) external onlyOwner nonReentrant {\n   │                                                                                       ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SorpheraRouter.sol:101:9\n    │\n101 │         IRewards(rewardsForPool[p]).withdrawTokenBuyAllowanceAsETH();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `requestState` is updated\n    ╭▸ src/SorpheraVault.sol:198:9\n    │\n198 │         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:208:9\n    │\n208 │         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:209:9\n    │\n209 │         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:208:9\n    │\n208 │         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:217:13\n    │\n217 │         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:222:13\n    │\n222 │             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:229:9\n    │\n229 │         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:230:9\n    │\n230 │         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:229:9\n    │\n229 │         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/SorpheraVault.sol:266:17\n    │\n266 │             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:266:17\n    │\n266 │             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:267:17\n    │\n267 │                 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:270:17\n    │\n270 │                 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:261:21\n    │\n261 │                     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:254:13\n    │\n254 │             require(!a.claimed, \"Sorphera: NFT claimed\");\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-no-eth]: external call can be reentered before `latestRound` is updated\n    ╭▸ src/Sorphera.sol:259:13\n    │\n259 │ ┏             factory.create(\n260 │ ┃                 game,\n261 │ ┃                 id,\n262 │ ┃                 SorpheraVault.Settings(rules.maxFee, rules.maxTotal, rules.minValue, rules.slippage, cutoff)\n263 │ ┃             )\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:249:17\n    │\n249 │         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:270:9\n    │\n270 │ ┏         emit RoundOpened(\n271 │ ┃             game, id, r.group, r.vault, r.start, cutoff, r.earliestDraw, r.settlementDeadline, r.price\n272 │ ┃         );\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:296:9\n    │\n296 │         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:278:57\n    │\n278 │             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:279:20\n    │\n279 │                 && 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:301:44\n    │\n301 │         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:306:45\n    │\n306 │         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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Sorphera.sol:327:37\n    │\n327 │         if (g.terminalRound == 0 && block.timestamp < active.settlementDeadline) ++active.eligibleNFTs;\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `entered` is updated\n    ╭▸ src/Sorphera.sol:337:9\n    │\n337 │         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:356:29\n    │\n356 │           uint256 requestId = coordinator.requestRandomWords(\n    │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n357 │ ┃             IVRF.Request(\n358 │ ┃                 c.keyHash,\n359 │ ┃                 c.subscription,\n    ‡ ┃\n365 │ ┃         );\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:343:49\n    │\n343 │         if (game == 1 && r.eligibleNFTs == 0 && block.timestamp >= r.settlementDeadline) {\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:348:13\n    │\n348 │             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[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Sorphera.sol:417:9\n    │\n417 │         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:429:9\n    │\n429 │         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:451:13\n    │\n451 │             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:369:9\n    │\n369 │         emit DrawRequested(game, id, 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:363:40\n    │\n363 │                 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[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/Sorphera.sol:445:19\n    │\n445 │         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:489:13\n    │\n489 │             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:494:9\n    │\n494 │         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:462:9\n    │\n462 │         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:465:9\n    │\n465 │         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:467:13\n    │\n467 │ ┏             require(\n468 │ ┃                 t.combination == r.winningKey && (game == 0 || ticket == r.winningTicket),\n469 │ ┃                 \"Sorphera: not winning ticket\"\n470 │ ┃             );\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:472:13\n    │\n472 │             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:497:85\n    │\n497 │     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:502:9\n    │\n502 │         emit OperatorWithdrawal(recipient, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":142,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 34 tests for test/Sorphera.t.sol:SorpheraTest\n[PASS] testApplicationSizeAndNoEscapeOpcodes() (gas: 15208301)\n[PASS] testBlackoutSlippageBudgetDeadlineAndFailedAcquireRollback() (gas: 3984961)\n[PASS] testBuilderAttributionOverpaymentAndPurchaserRewardsSeparated() (gas: 5747476)\n[PASS] testCallbackAuthenticationBindingDuplicateNoRerollOrCancellation() (gas: 4329646)\n[PASS] testCallbacksOutOfOrderAreBoundToSeparateGamesAndZeroWordValid() (gas: 9221392)\n[PASS] testDelayedCashoutNFTGameRemainsIncidentalETHAndCancels() (gas: 4537856)\n[PASS] testETHForcedNFTCustodyIsReservedForWinners() (gas: 5562981)\n[PASS] testETHOneMatchFullPrizeAndOperatorReserve() (gas: 5507898)\n[PASS] testETHRolloverOldTicketsExpireCarryNeverSpent() (gas: 8694336)\n[PASS] testExpiredAcquisitionBlocksDrawUntilReconciled() (gas: 5014802)\n[PASS] testFixedETHSettlementEvenAfterExclusiveWindow() (gas: 4240768)\n[PASS] testFuzzConservationAfterFWAChargesCashoutAndLateFunds(uint8,uint64) (runs: 256, μ: 6449670, ~: 5839329)\nLogs:\n  Bound result 3\n\n[PASS] testFuzzConservationUnusedBudget(uint8,uint64) (runs: 256, μ: 6235480, ~: 5238600)\nLogs:\n  Bound result 55\n\n[PASS] testFuzzSamplingRangeDistinctAndReplay(uint256) (runs: 256, μ: 33063, ~: 33104)\n[PASS] testLargeSalesConstantFinalizationAndPartialLargeInventory() (gas: 183546456)\n[PASS] testLateETHRecoveryGoesToOriginalWinnerNotNextRound() (gas: 7648598)\n[PASS] testLateNFTDoesNotRescueCancelledRoundAndSharedRecovery() (gas: 5168631)\n[PASS] testMultipleETHMatchesDuplicatesAndRounding() (gas: 4607272)\n[PASS] testNFTCancellationFeesPartialRefundAndLateRecovery() (gas: 4966081)\n[PASS] testNFTFeeReserveCannotBeWithdrawnUntilSuccess() (gas: 5372663)\n[PASS] testNFTOneWinnerAllAssetsAndFunds() (gas: 7461005)\n[PASS] testNFTRolloverInventoryAndResidualCannotRespin() (gas: 9488221)\n[PASS] testNFTTieBreakIncludesEveryDuplicateAndIsDomainSeparated() (gas: 6262555)\n[PASS] testNFTTransferFailuresAreIsolatedAndRetryable() (gas: 6719379)\n[PASS] testNoLateEntryAndBoundedTickets() (gas: 3422050)\n[PASS] testRejectedETHAndReentrancyRetainLiabilities() (gas: 4634323)\n[PASS] testRewardHelperFailureRollsBackAllowanceAndClaims() (gas: 4766636)\n[PASS] testStartsDisabledUnconfiguredAndOwnerSettings() (gas: 235690)\n[PASS] testStuckNFTCustodyRequiredAndRecoveryAfterWindow() (gas: 5666221)\n[PASS] testUnclaimedWinningsStayReservedAcrossNewRoundAndWithdrawals() (gas: 8761318)\n[PASS] testUnorderedNumbersIndexedNoTicketTransfer() (gas: 3653914)\n[PASS] testUnwithdrawnFWARefundPreventsDraw() (gas: 4470727)\n[PASS] testWeeklyGamesIndependentFreezeAndPauseClaims() (gas: 7986505)\n[PASS] testZeroTicketsSkipRandomnessAndSeparateGameDoesNotBlock() (gas: 9478358)\nSuite result: ok. 34 passed; 0 failed; 0 skipped; finished in 74.65ms (286.78ms CPU time)\n\nRan 1 test suite in 75.79ms (74.65ms CPU time): 34 tests passed, 0 failed, 0 skipped (34 total tests)\n","passed":true},{"durationMs":41,"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.credit(uint8,uint256)\",\"Sorphera.finalize(uint8,uint256)\",\"Sorphera.openRound(uint8)\",\"Sorphera.proposeOwner(address)\",\"Sorphera.rawFulfillRandomWords(uint256,uint256[])\",\"Sorphera.recordNFT(uint8,uint256)\",\"Sorphera.requestDraw(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.process(uint256)\",\"SorpheraVault.receive()\",\"SorpheraVault.reconcile(uint256[])\",\"SorpheraVault.recoverNFT(uint256)\",\"SorpheraVault.recoverRefund()\",\"SorpheraVault.settle(uint256)\",\"SorpheraVault.syncETH()\",\"SorpheraVaultFactory.create(uint8,uint256,(uint256,uint256,uint256,uint256,uint256))\"],\"files\":{\".gitignore\":3,\"README.md\":86,\"docs/DEPENDENCIES.md\":44,\"docs/OPERATIONS.md\":47,\"docs/TESTING.md\":40,\"docs/reference/FWAV2.sepolia.abi.json\":1,\"docs/reference/FWAV2Rewards.sepolia.abi.json\":1,\"docs/reference/sepolia-readback.json\":46,\"docs/reference/verification.json\":21,\"docs/validation.json\":11,\"foundry.toml\":20,\"frontend/README.md\":49,\"frontend/abi/Sorphera.json\":1820,\"frontend/abi/SorpheraRouter.json\":455,\"frontend/abi/SorpheraVault.json\":977,\"frontend/abi/SorpheraVaultFactory.json\":113,\"frontend/bytecode-sizes.json\":18,\"frontend/configuration.json\":21,\"frontend/deployment.json\":14,\"integration/Sepolia.t.sol\":42,\"launch.json\":22,\"src/Sorphera.sol\":528,\"src/SorpheraRouter.sol\":105,\"src/SorpheraVault.sol\":291,\"src/SorpheraVaultFactory.sol\":24,\"src/interfaces/External.sol\":119,\"src/lib/Balls.sol\":46,\"src/lib/RewardTransfer.sol\":54,\"src/lib/Security.sol\":47,\"test/Sorphera.t.sol\":796,\"test/mocks/ExternalMocks.sol\":404,\"tools/export.py\":19},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"97396903ac5b18707422a4739823a680743fccf6caa915736f46062b96d1e461","verifiedTreeHash":"956708440425fef4e2da02ea826d99c880ef5e98","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":21694,"exitCode":0,"name":"build","output":"Compiling 29 files with Solc 0.8.26\nSolc 0.8.26 finished in 21.60s\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[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-events-arithmetic]: `budget` is changed without an event but is used in arithmetic\n    ╭▸ src/SorpheraVault.sol:102:9\n    │\n102 │         budget += msg.value;\n    │         ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\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:80:9\n   │\n80 │         address lottery_,\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/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[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SorpheraVaultFactory.sol:21:25\n   │\n21 │     function setLottery(address lottery_) external onlyOwner {\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/SorpheraVaultFactory.sol:36:9\n   │\n36 │         emit VaultCreated(msg.sender, game, round, address(vault));\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/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[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[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SorpheraRouter.sol:22:39\n   │\n22 │     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:22:24\n   │\n22 │     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:75:15\n   │\n75 │         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:71:13\n   │\n71 │             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:79:9\n   │\n79 │         emit AcquisitionForwarded(msg.sender, p, ids, refund);\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:107:64\n    │\n107 │             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:111:44\n    │\n111 │         require(count > 0 && count <= 8 && block.timestamp <= deadline, \"Sorphera: acquisition batch\");\n    │                                            ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:130:51\n    │\n130 │             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:134:13\n    │\n134 │             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:129:13\n    │\n129 │             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:131:13\n    │\n131 │             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:150:17\n    │\n150 │                 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:152:17\n    │\n152 │                 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:191:21\n    │\n191 │                     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:192:21\n    │\n192 │                     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:198:21\n    │\n198 │                     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:205:13\n    │\n205 │             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:160:9\n    │\n160 │         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:160:9\n    │\n160 │         pool.processAcquisitions(count);\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/SorpheraRouter.sol:92:26\n   │\n92 │         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:85:9\n   │\n85 │         nonReentrant\n   │         ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:172:37\n    │\n172 │         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:176:37\n    │\n176 │             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:181:21\n    │\n181 │                 try IERC721(l.collection).ownerOf(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:191:21\n    │\n191 │                     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:193:28\n    │\n193 │                 } else if (pool.stuckNFTRecipient(a.listingId) == address(this)) {\n    │                            ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/SorpheraRouter.sol:88:31\n   │\n88 │         require(minOut > 0 && block.timestamp <= deadline, \"Sorphera: reward bounds\");\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/SorpheraVault.sol:171:9\n    │\n171 │         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:173:9\n    │\n173 │         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:177:13\n    │\n177 │             require(l.purchaser == address(this), \"Sorphera: purchaser\");\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-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/SorpheraRouter.sol:95:9\n   │\n95 │         emit BuilderRewardClaimed(address(r), allowance, amount);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SorpheraVault.sol:184:21\n    │\n184 │                 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:213:9\n    │\n213 │         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:223:9\n    │\n223 │         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:224:9\n    │\n224 │         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:223:9\n    │\n223 │         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:232:13\n    │\n232 │         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:237:13\n    │\n237 │             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:244:9\n    │\n244 │         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:245:9\n    │\n245 │         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:244:9\n    │\n244 │         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/SorpheraVault.sol:281:17\n    │\n281 │             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:281:17\n    │\n281 │             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:282:17\n    │\n282 │                 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:285:17\n    │\n285 │                 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:276:21\n    │\n276 │                     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:269:13\n    │\n269 │             require(!a.claimed, \"Sorphera: NFT claimed\");\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-no-eth]: external call can be reentered before `entered` is updated\n    ╭▸ src/SorpheraRouter.sol:101:9\n    │\n101 │         IRewards(rewardsForPool[p]).withdrawTokenBuyAllowanceAsETH();\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:98:87\n   │\n98 │     function recoverBuilderAllowance(address p, address recipient) external onlyOwner nonReentrant {\n   │                                                                                       ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SorpheraRouter.sol:101:9\n    │\n101 │         IRewards(rewardsForPool[p]).withdrawTokenBuyAllowanceAsETH();\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:229:17\n    │\n229 │         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:223:13\n    │\n223 │             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 `latestRound` is updated\n    ╭▸ src/Sorphera.sol:260:13\n    │\n260 │ ┏             factory.create(\n261 │ ┃                 game,\n262 │ ┃                 id,\n263 │ ┃                 SorpheraVault.Settings(rules.maxFee, rules.maxTotal, rules.minValue, rules.slippage, cutoff)\n264 │ ┃             )\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:250:17\n    │\n250 │         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:271:9\n    │\n271 │ ┏         emit RoundOpened(\n272 │ ┃             game, id, r.group, r.vault, r.start, cutoff, r.earliestDraw, r.settlementDeadline, r.price\n273 │ ┃         );\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:297:9\n    │\n297 │         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:279:57\n    │\n279 │             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:280:20\n    │\n280 │                 && 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:302:44\n    │\n302 │         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:307:45\n    │\n307 │         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 `entered` is updated\n    ╭▸ src/Sorphera.sol:344:9\n    │\n344 │         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:363:29\n    │\n363 │           uint256 requestId = coordinator.requestRandomWords(\n    │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n364 │ ┃             IVRF.Request(\n365 │ ┃                 c.keyHash,\n366 │ ┃                 c.subscription,\n    ‡ ┃\n372 │ ┃         );\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:350:49\n    │\n350 │         if (game == 1 && r.eligibleNFTs == 0 && block.timestamp >= r.settlementDeadline) {\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:355:13\n    │\n355 │             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[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Sorphera.sol:424:9\n    │\n424 │         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:436:9\n    │\n436 │         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:458:13\n    │\n458 │             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:376:9\n    │\n376 │         emit DrawRequested(game, id, 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:370:40\n    │\n370 │                 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[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/Sorphera.sol:452:19\n    │\n452 │         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:496:13\n    │\n496 │             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:501:9\n    │\n501 │         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:469:9\n    │\n469 │         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:472:9\n    │\n472 │         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:474:13\n    │\n474 │ ┏             require(\n475 │ ┃                 t.combination == r.winningKey && (game == 0 || ticket == r.winningTicket),\n476 │ ┃                 \"Sorphera: not winning ticket\"\n477 │ ┃             );\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:479:13\n    │\n479 │             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:504:85\n    │\n504 │     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:509:9\n    │\n509 │         emit OperatorWithdrawal(recipient, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":157,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 40 tests for test/Sorphera.t.sol:SorpheraTest\n[PASS] testApplicationSizeAndNoEscapeOpcodes() (gas: 15785328)\n[PASS] testBlackoutSlippageBudgetDeadlineAndFailedAcquireRollback() (gas: 4062242)\n[PASS] testBuilderAttributionOverpaymentAndPurchaserRewardsSeparated() (gas: 5824883)\n[PASS] testCallbackAuthenticationBindingDuplicateNoRerollOrCancellation() (gas: 4406996)\n[PASS] testCallbacksOutOfOrderAreBoundToSeparateGamesAndZeroWordValid() (gas: 9399167)\n[PASS] testCustodyBeforeDeadlineCountsEvenWhenReconciledAfter() (gas: 5601517)\n[PASS] testDelayedCashoutNFTGameRemainsIncidentalETHAndCancels() (gas: 4615185)\n[PASS] testETHForcedNFTCustodyIsReservedForWinners() (gas: 5661701)\n[PASS] testETHOneMatchFullPrizeAndOperatorReserve() (gas: 5585186)\n[PASS] testETHRolloverOldTicketsExpireCarryNeverSpent() (gas: 8848942)\n[PASS] testExpiredAcquisitionBlocksDrawUntilReconciled() (gas: 5092129)\n[PASS] testFactoryOnlyRegisteredLotteryCreatesVaults() (gas: 3421968)\n[PASS] testFixedETHSettlementEvenAfterExclusiveWindow() (gas: 4318086)\n[PASS] testForcedDeliveryAfterDeadlineStillCancels() (gas: 4914131)\n[PASS] testFuzzConservationAfterFWAChargesCashoutAndLateFunds(uint8,uint64) (runs: 256, μ: 6618328, ~: 6094874)\nLogs:\n  Bound result 4\n\n[PASS] testFuzzConservationUnusedBudget(uint8,uint64) (runs: 256, μ: 6235954, ~: 5303634)\nLogs:\n  Bound result 64\n\n[PASS] testFuzzSamplingRangeDistinctAndReplay(uint256) (runs: 256, μ: 33173, ~: 33214)\n[PASS] testLargeSalesConstantFinalizationAndPartialLargeInventory() (gas: 186045057)\n[PASS] testLateAllocationAfterCancellationSettlesToDivisibleETHRefund() (gas: 5037685)\n[PASS] testLateETHRecoveryGoesToOriginalWinnerNotNextRound() (gas: 7803080)\n[PASS] testLateNFTDoesNotRescueCancelledRoundAndSharedRecovery() (gas: 5269304)\n[PASS] testMultipleETHMatchesDuplicatesAndRounding() (gas: 4684554)\n[PASS] testNFTCancellationFeesPartialRefundAndLateRecovery() (gas: 5043249)\n[PASS] testNFTFeeReserveCannotBeWithdrawnUntilSuccess() (gas: 5473233)\n[PASS] testNFTOneWinnerAllAssetsAndFunds() (gas: 7608149)\n[PASS] testNFTRolloverInventoryAndResidualCannotRespin() (gas: 9666049)\n[PASS] testNFTTieBreakIncludesEveryDuplicateAndIsDomainSeparated() (gas: 6386276)\n[PASS] testNFTTransferFailuresAreIsolatedAndRetryable() (gas: 6843337)\n[PASS] testNoLateEntryAndBoundedTickets() (gas: 3499224)\n[PASS] testPermanentlyStuckNFTNeverFreezesETHGameAndLateRecoveryFollowsWinner() (gas: 9211576)\n[PASS] testRejectedETHAndReentrancyRetainLiabilities() (gas: 4711656)\n[PASS] testRewardHelperFailureRollsBackAllowanceAndClaims() (gas: 4843954)\n[PASS] testStartsDisabledUnconfiguredAndOwnerSettings() (gas: 880177)\n[PASS] testStuckNFTCustodyRequiredAndRecoveryAfterWindow() (gas: 6138929)\n[PASS] testStuckOnlyNFTCancelsRoundAndLateRecoveryIsSharedByRefundCohort() (gas: 8535891)\n[PASS] testUnclaimedWinningsStayReservedAcrossNewRoundAndWithdrawals() (gas: 8915784)\n[PASS] testUnorderedNumbersIndexedNoTicketTransfer() (gas: 3731176)\n[PASS] testUnwithdrawnFWARefundPreventsDraw() (gas: 4548240)\n[PASS] testWeeklyGamesIndependentFreezeAndPauseClaims() (gas: 8141062)\n[PASS] testZeroTicketsSkipRandomnessAndSeparateGameDoesNotBlock() (gas: 9709990)\nSuite result: ok. 40 passed; 0 failed; 0 skipped; finished in 82.67ms (348.35ms CPU time)\n\nRan 1 test suite in 83.79ms (82.67ms CPU time): 40 tests passed, 0 failed, 0 skipped (40 total tests)\n","passed":true},{"durationMs":42,"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.credit(uint8,uint256)\",\"Sorphera.finalize(uint8,uint256)\",\"Sorphera.openRound(uint8)\",\"Sorphera.proposeOwner(address)\",\"Sorphera.rawFulfillRandomWords(uint256,uint256[])\",\"Sorphera.recordNFT(uint8,uint256,uint256)\",\"Sorphera.requestDraw(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.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\":89,\"docs/DEPENDENCIES.md\":44,\"docs/OPERATIONS.md\":48,\"docs/TESTING.md\":44,\"docs/reference/FWAV2.sepolia.abi.json\":1,\"docs/reference/FWAV2Rewards.sepolia.abi.json\":1,\"docs/reference/sepolia-readback.json\":46,\"docs/reference/verification.json\":21,\"docs/validation.json\":11,\"foundry.toml\":20,\"frontend/README.md\":51,\"frontend/abi/Sorphera.json\":1849,\"frontend/abi/SorpheraRouter.json\":455,\"frontend/abi/SorpheraVault.json\":1020,\"frontend/abi/SorpheraVaultFactory.json\":229,\"frontend/bytecode-sizes.json\":18,\"frontend/configuration.json\":22,\"frontend/deployment.json\":15,\"integration/Sepolia.t.sol\":42,\"launch.json\":9,\"src/Sorphera.sol\":535,\"src/SorpheraRouter.sol\":105,\"src/SorpheraVault.sol\":307,\"src/SorpheraVaultFactory.sol\":38,\"src/interfaces/External.sol\":120,\"src/lib/Balls.sol\":46,\"src/lib/RewardTransfer.sol\":54,\"src/lib/Security.sol\":47,\"test/Sorphera.t.sol\":963,\"test/mocks/ExternalMocks.sol\":404,\"tools/export.py\":19},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":5948,"exitCode":0,"name":"slither","output":"[high/medium] arbitrary-send-eth at src/SorpheraVault.sol:231: SorpheraVault._sync() (src/SorpheraVault.sol#231-239) sends eth to arbitrary user\n[medium/medium] divide-before-multiply at src/Sorphera.sol:448: Sorphera._distribute(uint8,uint256) (src/Sorphera.sol#448-460) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/Sorphera.sol:334: Sorphera.isCancelled(uint8,uint256) (src/Sorphera.sol#334-336) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/Sorphera.sol:448: Sorphera._distribute(uint8,uint256) (src/Sorphera.sol#448-460) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/Sorphera.sol:339: Reentrancy in Sorphera.requestDraw(uint8,uint256) (src/Sorphera.sol#339-377):\n[medium/medium] reentrancy-no-eth at src/Sorphera.sol:339: Reentrancy in Sorphera.requestDraw(uint8,uint256) (src/Sorphera.sol#339-377):\n[medium/medium] reentrancy-no-eth at src/Sorphera.sol:238: Reentrancy in Sorphera.openRound(uint8) (src/Sorphera.sol#238-274):\n[medium/medium] reentrancy-no-eth at src/SorpheraVault.sol:209: Reentrancy in SorpheraVault.recoverNFT(uint256) (src/SorpheraVault.sol#209-215):\n[medium/medium] reentrancy-no-eth at src/SorpheraVault.sol:170: Reentrancy in SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#170-207):\n[medium/medium] reentrancy-no-eth at src/SorpheraVault.sol:142: Reentrancy in SorpheraVault.settle(uint256) (src/SorpheraVault.sol#142-156):\n[medium/medium] reentrancy-no-eth at src/SorpheraVault.sol:259: Reentrancy in SorpheraVault.claimNFTs(uint256,uint256[],address) (src/SorpheraVault.sol#259-288):\n[medium/medium] uninitialized-local at src/Sorphera.sol:491: Sorphera.claimETH(uint8,uint256,uint256[],address).amount (src/Sorphera.sol#491) is a local variable never initialized\n[medium/medium] uninitialized-local at src/SorpheraVault.sol:180: SorpheraVault._reconcile(uint256).held (src/SorpheraVault.sol#180) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Sorphera.sol:225: Sorphera._checkFunding(Sorphera.RandomConfig).present (src/Sorphera.sol#225) is a local variable never initialized\n[medium/medium] unused-return at src/SorpheraVault.sol:241: SorpheraVault.claimEpochRewards(uint256[]) (src/SorpheraVault.sol#241-246) ignores return value by rewards.claimEpochTokens(epochs) (src/SorpheraVault.sol#244)\n[medium/medium] unused-return at src/SorpheraRouter.sol:98: SorpheraRouter.recoverBuilderAllowance(address,address) (src/SorpheraRouter.sol#98-103) ignores return value by IRewards(rewardsForPool[p]).withdrawTokenBuyAllowanceAsETH() (src/SorpheraRouter.sol#101)\n[medium/medium] unused-return at src/SorpheraVault.sol:158: SorpheraVault.process(uint256) (src/SorpheraVault.sol#158-161) ignores return value by pool.processAcquisitions(count) (src/SorpheraVault.sol#160)\n[medium/medium] unused-return at src/Sorphera.sol:220: Sorphera._checkFunding(Sorphera.RandomConfig) (src/Sorphera.sol#220-230) ignores return value by (link,nativeBalance,None,None,consumers) = coordinator.getSubscription(c.subscription) (src/Sorphera.sol#222-223)\n[medium/medium] unused-return at src/SorpheraVault.sol:221: SorpheraVault.recoverRefund() (src/SorpheraVault.sol#221-225) ignores return value by pool.withdrawAcquisitionRefund() (src/SorpheraVault.sol#223)\n[low/medium] events-maths at src/SorpheraVault.sol:101: SorpheraVault.fund() (src/SorpheraVault.sol#101-103) should emit an event for: \n[low/medium] missing-zero-check at src/SorpheraVault.sol:80: SorpheraVault.constructor(address,SorpheraRouter,uint8,uint256,SorpheraVault.Settings).lottery_ (src/SorpheraVault.sol#80) lacks a zero-check on :\n[low/medium] calls-loop at src/SorpheraVault.sol:170: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#170-207) has external calls inside a loop: ISorphera(lottery).recordNFT(game,round,securedAt) (src/SorpheraVault.sol#191)\n[low/medium] calls-loop at src/SorpheraVault.sol:170: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#170-207) has external calls inside a loop: l = pool.listings(a.listingId) (src/SorpheraVault.sol#176)\n[low/medium] calls-loop at src/SorpheraVault.sol:105: SorpheraVault.acquire(uint256,uint256) (src/SorpheraVault.sol#105-137) has external calls inside a loop: acquisition = pool.acquisitions(ids[i]) (src/SorpheraVault.sol#130)\n[low/medium] calls-loop at src/SorpheraVault.sol:259: SorpheraVault.claimNFTs(uint256,uint256[],address) (src/SorpheraVault.sol#259-288) has external calls inside a loop: this.deliver{gas: 500000}(index,recipient) (src/SorpheraVault.sol#281-286)\n[low/medium] calls-loop at src/SorpheraVault.sol:170: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#170-207) has external calls inside a loop: a = pool.acquisitions(id) (src/SorpheraVault.sol#172)\n[low/medium] calls-loop at src/SorpheraVault.sol:170: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#170-207) has external calls inside a loop: pool.stuckNFTRecipient(a.listingId) == address(this) (src/SorpheraVault.sol#193)\n[low/medium] calls-loop at src/SorpheraVault.sol:170: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#170-207) has external calls inside a loop: holder = IERC721(l.collection).ownerOf(l.tokenId) (src/SorpheraVault.sol#181-183)\n[low/medium] reentrancy-benign at src/SorpheraVault.sol:170: Reentrancy in SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#170-207):\n[low/medium] reentrancy-benign at src/SorpheraVault.sol:105: Reentrancy in SorpheraVault.acquire(uint256,uint256) (src/SorpheraVault.sol#105-137):\n[low/medium] reentrancy-benign at src/Sorphera.sol:339: Reentrancy in Sorphera.requestDraw(uint8,uint256) (src/Sorphera.sol#339-377):\n[low/medium] reentrancy-benign at src/Sorphera.sol:339: Reentrancy in Sorphera.requestDraw(uint8,uint256) (src/Sorphera.sol#339-377):\n[low/medium] reentrancy-benign at src/Sorphera.sol:238: Reentrancy in Sorphera.openRound(uint8) (src/Sorphera.sol#238-274):\n[low/medium] timestamp at src/SorpheraVault.sol:105: SorpheraVault.acquire(uint256,uint256) (src/SorpheraVault.sol#105-137) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:334: Sorphera.isCancelled(uint8,uint256) (src/Sorphera.sol#334-336) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:324: Sorphera.recordNFT(uint8,uint256,uint256) (src/Sorphera.sol#324-332) uses timestamp for comparisons\n[low/medium] timestamp at src/SorpheraVault.sol:170: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#170-207) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:394: Sorphera.finalize(uint8,uint256) (src/Sorphera.sol#394-418) uses timestamp for comparisons\n[low/medium] timestamp at src/SorpheraVault.sol:231: SorpheraVault._sync() (src/SorpheraVault.sol#231-239) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:448: Sorphera._distribute(uint8,uint256) (src/Sorphera.sol#448-460) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:312: Sorphera.credit(uint8,uint256) (src/Sorphera.sol#312-320) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:238: Sorphera.openRound(uint8) (src/Sorphera.sol#238-274) uses timestamp for comparisons\n[low/medium] timestamp at src/SorpheraRouter.sol:60: SorpheraRouter.acquire(address,uint256,uint256,uint256,uint256,uint256) (src/SorpheraRouter.sol#60-80) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:300: Sorphera.acquisitionOpen(uint8,uint256) (src/Sorphera.sol#300-303) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:305: Sorphera.close(uint8,uint256) (src/Sorphera.sol#305-310) uses timestamp for comparisons\n[low/medium] timestamp at src/SorpheraRouter.sol:82: SorpheraRouter.claimBuilderRewards(address,address,uint256,uint256) (src/SorpheraRouter.sol#82-96) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:339: Sorphera.requestDraw(uint8,uint256) (src/Sorphera.sol#339-377) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:276: Sorphera.buy(uint8,uint256,Sorphera.Pick[]) (src/Sorphera.sol#276-298) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:379: Sorphera.rawFulfillRandomWords(uint256,uint256[]) (src/Sorphera.sol#379-388) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:462: Sorphera.entitlement(uint8,uint256,uint256,address) (src/Sorphera.sol#462-482) uses timestamp for comparisons","passed":true},{"durationMs":324,"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:212: Reentrancy: State change after external call (25 places)\n[high] storage-array-memory-edit at src/Sorphera.sol:213: Storage Array Edited with Memory (2 places)\n[low] centralization-risk at src/Sorphera.sol:11: Centralization Risk (12 places)\n[low] costly-loop at src/Sorphera.sol:290: Costly operations inside loop (5 places)\n[low] large-numeric-literal at src/Sorphera.sol:200: Large Numeric Literal (2 places)\n[low] literal-instead-of-constant at src/Sorphera.sol:181: Literal Instead of Constant (13 places)\n[low] modifier-used-only-once at src/SorpheraVault.sol:96: Modifier Invoked Only Once\n[low] non-reentrant-not-first at src/Sorphera.sol:504: `nonReentrant` is Not the First Modifier (3 places)\n[low] require-revert-in-loop at src/Sorphera.sol:290: Loop Contains `require`/`revert` (5 places)\n[low] state-change-without-event at src/SorpheraRouter.sol:98: State Change Without Event (4 places)\n[low] unchecked-return at src/Sorphera.sol:493: Unchecked Return (5 places)\n[low] uninitialized-local-variable at src/Sorphera.sol:226: Uninitialized Local Variable (4 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"d3ea1f9f9304aef872ef6fea395c4a7224508435ac929f0faf409c099becca01","verifiedTreeHash":"121bd48a29bd9e893d6750a1d7a801484d392e9f","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":16325,"exitCode":0,"name":"build","output":"Compiling 29 files with Solc 0.8.26\nSolc 0.8.26 finished in 16.23s\nCompiler run successful!\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:22:9\n   │\n22 │         emit VaultCreated(msg.sender, game, round, address(vault));\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/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:98:9\n   │\n98 │         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:76:9\n   │\n76 │         address lottery_,\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/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[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/SorpheraRouter.sol:22:24\n   │\n22 │     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:22:39\n   │\n22 │     function configure(address pool_, address helper_) 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[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n   ╭▸ src/SorpheraRouter.sol:75:15\n   │\n75 │         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:71:13\n   │\n71 │             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:79:9\n   │\n79 │         emit AcquisitionForwarded(msg.sender, p, ids, refund);\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:103:64\n    │\n103 │             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:107:44\n    │\n107 │         require(count > 0 && count <= 8 && block.timestamp <= deadline, \"Sorphera: acquisition batch\");\n    │                                            ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:126:51\n    │\n126 │             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:130:13\n    │\n130 │             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:125:13\n    │\n125 │             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:127:13\n    │\n127 │             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:143:28\n    │\n143 │             if (game == 0) 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:144:18\n    │\n144 │             else 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:180:21\n    │\n180 │                     ISorphera(lottery).recordNFT(game, round);\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:181:21\n    │\n181 │                     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:190:13\n    │\n190 │             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:151:9\n    │\n151 │         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:151:9\n    │\n151 │         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:163:37\n    │\n163 │         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:167:37\n    │\n167 │             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:172:21\n    │\n172 │                 try IERC721(l.collection).ownerOf(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:180:21\n    │\n180 │                     ISorphera(lottery).recordNFT(game, round);\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SorpheraVault.sol:184:21\n    │\n184 │                 if (pool.stuckNFTRecipient(a.listingId) == address(this)) terminal = false;\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:162:9\n    │\n162 │         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:164:9\n    │\n164 │         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:168:13\n    │\n168 │             require(l.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 `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/SorpheraRouter.sol:92:26\n   │\n92 │         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:85:9\n   │\n85 │         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:88:31\n   │\n88 │         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:95:9\n   │\n95 │         emit BuilderRewardClaimed(address(r), allowance, amount);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SorpheraVault.sol:175:21\n    │\n175 │                 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:198:9\n    │\n198 │         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:208:9\n    │\n208 │         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:209:9\n    │\n209 │         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:208:9\n    │\n208 │         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:217:13\n    │\n217 │         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:222:13\n    │\n222 │             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:229:9\n    │\n229 │         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:230:9\n    │\n230 │         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:229:9\n    │\n229 │         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/SorpheraVault.sol:266:17\n    │\n266 │             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:266:17\n    │\n266 │             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:267:17\n    │\n267 │                 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:270:17\n    │\n270 │                 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:261:21\n    │\n261 │                     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:254:13\n    │\n254 │             require(!a.claimed, \"Sorphera: NFT claimed\");\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/Sorphera.sol:228:17\n    │\n228 │         require(present, \"Sorphera: VRF consumer missing\");\n    │                 ━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-no-eth]: external call can be reentered before `entered` is updated\n    ╭▸ src/SorpheraRouter.sol:101:9\n    │\n101 │         IRewards(rewardsForPool[p]).withdrawTokenBuyAllowanceAsETH();\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/Sorphera.sol:222:13\n    │\n222 │             coordinator.getSubscription(c.subscription);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/SorpheraRouter.sol:98:87\n   │\n98 │     function recoverBuilderAllowance(address p, address recipient) external onlyOwner nonReentrant {\n   │                                                                                       ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SorpheraRouter.sol:101:9\n    │\n101 │         IRewards(rewardsForPool[p]).withdrawTokenBuyAllowanceAsETH();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `latestRound` is updated\n    ╭▸ src/Sorphera.sol:259:13\n    │\n259 │ ┏             factory.create(\n260 │ ┃                 game,\n261 │ ┃                 id,\n262 │ ┃                 SorpheraVault.Settings(rules.maxFee, rules.maxTotal, rules.minValue, rules.slippage, cutoff)\n263 │ ┃             )\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:249:17\n    │\n249 │         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:270:9\n    │\n270 │ ┏         emit RoundOpened(\n271 │ ┃             game, id, r.group, r.vault, r.start, cutoff, r.earliestDraw, r.settlementDeadline, r.price\n272 │ ┃         );\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:296:9\n    │\n296 │         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:278:57\n    │\n278 │             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:279:20\n    │\n279 │                 && 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:301:44\n    │\n301 │         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:306:45\n    │\n306 │         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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Sorphera.sol:327:37\n    │\n327 │         if (g.terminalRound == 0 && block.timestamp < active.settlementDeadline) ++active.eligibleNFTs;\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `entered` is updated\n    ╭▸ src/Sorphera.sol:337:9\n    │\n337 │         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:356:29\n    │\n356 │           uint256 requestId = coordinator.requestRandomWords(\n    │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n357 │ ┃             IVRF.Request(\n358 │ ┃                 c.keyHash,\n359 │ ┃                 c.subscription,\n    ‡ ┃\n365 │ ┃         );\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:343:49\n    │\n343 │         if (game == 1 && r.eligibleNFTs == 0 && block.timestamp >= r.settlementDeadline) {\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:348:13\n    │\n348 │             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[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Sorphera.sol:417:9\n    │\n417 │         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:429:9\n    │\n429 │         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:451:13\n    │\n451 │             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:369:9\n    │\n369 │         emit DrawRequested(game, id, 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:363:40\n    │\n363 │                 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[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/Sorphera.sol:445:19\n    │\n445 │         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:489:13\n    │\n489 │             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:494:9\n    │\n494 │         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:462:9\n    │\n462 │         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:465:9\n    │\n465 │         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:467:13\n    │\n467 │ ┏             require(\n468 │ ┃                 t.combination == r.winningKey && (game == 0 || ticket == r.winningTicket),\n469 │ ┃                 \"Sorphera: not winning ticket\"\n470 │ ┃             );\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:472:13\n    │\n472 │             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:497:85\n    │\n497 │     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:502:9\n    │\n502 │         emit OperatorWithdrawal(recipient, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":170,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 34 tests for test/Sorphera.t.sol:SorpheraTest\n[PASS] testApplicationSizeAndNoEscapeOpcodes() (gas: 15208301)\n[PASS] testBlackoutSlippageBudgetDeadlineAndFailedAcquireRollback() (gas: 3984961)\n[PASS] testBuilderAttributionOverpaymentAndPurchaserRewardsSeparated() (gas: 5747476)\n[PASS] testCallbackAuthenticationBindingDuplicateNoRerollOrCancellation() (gas: 4329646)\n[PASS] testCallbacksOutOfOrderAreBoundToSeparateGamesAndZeroWordValid() (gas: 9221392)\n[PASS] testDelayedCashoutNFTGameRemainsIncidentalETHAndCancels() (gas: 4537856)\n[PASS] testETHForcedNFTCustodyIsReservedForWinners() (gas: 5562981)\n[PASS] testETHOneMatchFullPrizeAndOperatorReserve() (gas: 5507898)\n[PASS] testETHRolloverOldTicketsExpireCarryNeverSpent() (gas: 8694336)\n[PASS] testExpiredAcquisitionBlocksDrawUntilReconciled() (gas: 5014802)\n[PASS] testFixedETHSettlementEvenAfterExclusiveWindow() (gas: 4240768)\n[PASS] testFuzzConservationAfterFWAChargesCashoutAndLateFunds(uint8,uint64) (runs: 256, μ: 6611959, ~: 6116971)\nLogs:\n  Bound result 23\n\n[PASS] testFuzzConservationUnusedBudget(uint8,uint64) (runs: 256, μ: 6470833, ~: 5714703)\nLogs:\n  Bound result 71\n\n[PASS] testFuzzSamplingRangeDistinctAndReplay(uint256) (runs: 256, μ: 33063, ~: 33104)\n[PASS] testLargeSalesConstantFinalizationAndPartialLargeInventory() (gas: 183546456)\n[PASS] testLateETHRecoveryGoesToOriginalWinnerNotNextRound() (gas: 7648598)\n[PASS] testLateNFTDoesNotRescueCancelledRoundAndSharedRecovery() (gas: 5168631)\n[PASS] testMultipleETHMatchesDuplicatesAndRounding() (gas: 4607272)\n[PASS] testNFTCancellationFeesPartialRefundAndLateRecovery() (gas: 4966081)\n[PASS] testNFTFeeReserveCannotBeWithdrawnUntilSuccess() (gas: 5372663)\n[PASS] testNFTOneWinnerAllAssetsAndFunds() (gas: 7461005)\n[PASS] testNFTRolloverInventoryAndResidualCannotRespin() (gas: 9488221)\n[PASS] testNFTTieBreakIncludesEveryDuplicateAndIsDomainSeparated() (gas: 6262555)\n[PASS] testNFTTransferFailuresAreIsolatedAndRetryable() (gas: 6719379)\n[PASS] testNoLateEntryAndBoundedTickets() (gas: 3422050)\n[PASS] testRejectedETHAndReentrancyRetainLiabilities() (gas: 4634323)\n[PASS] testRewardHelperFailureRollsBackAllowanceAndClaims() (gas: 4766636)\n[PASS] testStartsDisabledUnconfiguredAndOwnerSettings() (gas: 235690)\n[PASS] testStuckNFTCustodyRequiredAndRecoveryAfterWindow() (gas: 5666221)\n[PASS] testUnclaimedWinningsStayReservedAcrossNewRoundAndWithdrawals() (gas: 8761318)\n[PASS] testUnorderedNumbersIndexedNoTicketTransfer() (gas: 3653914)\n[PASS] testUnwithdrawnFWARefundPreventsDraw() (gas: 4470727)\n[PASS] testWeeklyGamesIndependentFreezeAndPauseClaims() (gas: 7986505)\n[PASS] testZeroTicketsSkipRandomnessAndSeparateGameDoesNotBlock() (gas: 9478358)\nSuite result: ok. 34 passed; 0 failed; 0 skipped; finished in 101.21ms (327.12ms CPU time)\n\nRan 1 test suite in 102.28ms (101.21ms CPU time): 34 tests passed, 0 failed, 0 skipped (34 total tests)\n","passed":true},{"durationMs":41,"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.credit(uint8,uint256)\",\"Sorphera.finalize(uint8,uint256)\",\"Sorphera.openRound(uint8)\",\"Sorphera.proposeOwner(address)\",\"Sorphera.rawFulfillRandomWords(uint256,uint256[])\",\"Sorphera.recordNFT(uint8,uint256)\",\"Sorphera.requestDraw(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.process(uint256)\",\"SorpheraVault.receive()\",\"SorpheraVault.reconcile(uint256[])\",\"SorpheraVault.recoverNFT(uint256)\",\"SorpheraVault.recoverRefund()\",\"SorpheraVault.settle(uint256)\",\"SorpheraVault.syncETH()\",\"SorpheraVaultFactory.create(uint8,uint256,(uint256,uint256,uint256,uint256,uint256))\"],\"files\":{\".gitignore\":3,\"README.md\":86,\"docs/DEPENDENCIES.md\":44,\"docs/OPERATIONS.md\":47,\"docs/TESTING.md\":40,\"docs/reference/FWAV2.sepolia.abi.json\":1,\"docs/reference/FWAV2Rewards.sepolia.abi.json\":1,\"docs/reference/sepolia-readback.json\":46,\"docs/reference/verification.json\":21,\"docs/validation.json\":11,\"foundry.toml\":20,\"frontend/README.md\":49,\"frontend/abi/Sorphera.json\":1820,\"frontend/abi/SorpheraRouter.json\":455,\"frontend/abi/SorpheraVault.json\":977,\"frontend/abi/SorpheraVaultFactory.json\":113,\"frontend/bytecode-sizes.json\":18,\"frontend/configuration.json\":21,\"frontend/deployment.json\":14,\"integration/Sepolia.t.sol\":42,\"launch.json\":9,\"src/Sorphera.sol\":528,\"src/SorpheraRouter.sol\":105,\"src/SorpheraVault.sol\":291,\"src/SorpheraVaultFactory.sol\":24,\"src/interfaces/External.sol\":119,\"src/lib/Balls.sol\":46,\"src/lib/RewardTransfer.sol\":54,\"src/lib/Security.sol\":47,\"test/Sorphera.t.sol\":796,\"test/mocks/ExternalMocks.sol\":404,\"tools/export.py\":19},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":5153,"exitCode":0,"name":"slither","output":"[high/medium] arbitrary-send-eth at src/SorpheraVault.sol:216: SorpheraVault._sync() (src/SorpheraVault.sol#216-224) sends eth to arbitrary user\n[medium/medium] divide-before-multiply at src/Sorphera.sol:441: Sorphera._distribute(uint8,uint256) (src/Sorphera.sol#441-453) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/Sorphera.sol:441: Sorphera._distribute(uint8,uint256) (src/Sorphera.sol#441-453) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/SorpheraVault.sol:161: Reentrancy in SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#161-192):\n[medium/medium] reentrancy-no-eth at src/Sorphera.sol:332: Reentrancy in Sorphera.requestDraw(uint8,uint256) (src/Sorphera.sol#332-370):\n[medium/medium] reentrancy-no-eth at src/Sorphera.sol:332: Reentrancy in Sorphera.requestDraw(uint8,uint256) (src/Sorphera.sol#332-370):\n[medium/medium] reentrancy-no-eth at src/SorpheraVault.sol:194: Reentrancy in SorpheraVault.recoverNFT(uint256) (src/SorpheraVault.sol#194-200):\n[medium/medium] reentrancy-no-eth at src/SorpheraVault.sol:136: Reentrancy in SorpheraVault.settle(uint256) (src/SorpheraVault.sol#136-147):\n[medium/medium] reentrancy-no-eth at src/Sorphera.sol:237: Reentrancy in Sorphera.openRound(uint8) (src/Sorphera.sol#237-273):\n[medium/medium] reentrancy-no-eth at src/SorpheraVault.sol:244: Reentrancy in SorpheraVault.claimNFTs(uint256,uint256[],address) (src/SorpheraVault.sol#244-273):\n[medium/medium] uninitialized-local at src/Sorphera.sol:484: Sorphera.claimETH(uint8,uint256,uint256[],address).amount (src/Sorphera.sol#484) is a local variable never initialized\n[medium/medium] uninitialized-local at src/SorpheraVault.sol:171: SorpheraVault._reconcile(uint256).held (src/SorpheraVault.sol#171) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Sorphera.sol:224: Sorphera._checkFunding(Sorphera.RandomConfig).present (src/Sorphera.sol#224) is a local variable never initialized\n[medium/medium] unused-return at src/SorpheraRouter.sol:98: SorpheraRouter.recoverBuilderAllowance(address,address) (src/SorpheraRouter.sol#98-103) ignores return value by IRewards(rewardsForPool[p]).withdrawTokenBuyAllowanceAsETH() (src/SorpheraRouter.sol#101)\n[medium/medium] unused-return at src/SorpheraVault.sol:149: SorpheraVault.process(uint256) (src/SorpheraVault.sol#149-152) ignores return value by pool.processAcquisitions(count) (src/SorpheraVault.sol#151)\n[medium/medium] unused-return at src/Sorphera.sol:219: Sorphera._checkFunding(Sorphera.RandomConfig) (src/Sorphera.sol#219-229) ignores return value by (link,nativeBalance,None,None,consumers) = coordinator.getSubscription(c.subscription) (src/Sorphera.sol#221-222)\n[medium/medium] unused-return at src/SorpheraVault.sol:206: SorpheraVault.recoverRefund() (src/SorpheraVault.sol#206-210) ignores return value by pool.withdrawAcquisitionRefund() (src/SorpheraVault.sol#208)\n[medium/medium] unused-return at src/SorpheraVault.sol:226: SorpheraVault.claimEpochRewards(uint256[]) (src/SorpheraVault.sol#226-231) ignores return value by rewards.claimEpochTokens(epochs) (src/SorpheraVault.sol#229)\n[low/medium] events-maths at src/SorpheraVault.sol:97: SorpheraVault.fund() (src/SorpheraVault.sol#97-99) should emit an event for: \n[low/medium] missing-zero-check at src/SorpheraVault.sol:76: SorpheraVault.constructor(address,SorpheraRouter,uint8,uint256,SorpheraVault.Settings).lottery_ (src/SorpheraVault.sol#76) lacks a zero-check on :\n[low/medium] calls-loop at src/SorpheraVault.sol:161: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#161-192) has external calls inside a loop: ISorphera(lottery).recordNFT(game,round) (src/SorpheraVault.sol#180)\n[low/medium] calls-loop at src/SorpheraVault.sol:244: SorpheraVault.claimNFTs(uint256,uint256[],address) (src/SorpheraVault.sol#244-273) has external calls inside a loop: this.deliver{gas: 500000}(index,recipient) (src/SorpheraVault.sol#266-271)\n[low/medium] calls-loop at src/SorpheraVault.sol:161: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#161-192) has external calls inside a loop: l = pool.listings(a.listingId) (src/SorpheraVault.sol#167)\n[low/medium] calls-loop at src/SorpheraVault.sol:101: SorpheraVault.acquire(uint256,uint256) (src/SorpheraVault.sol#101-133) has external calls inside a loop: acquisition = pool.acquisitions(ids[i]) (src/SorpheraVault.sol#126)\n[low/medium] calls-loop at src/SorpheraVault.sol:161: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#161-192) has external calls inside a loop: holder = IERC721(l.collection).ownerOf(l.tokenId) (src/SorpheraVault.sol#172-174)\n[low/medium] calls-loop at src/SorpheraVault.sol:161: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#161-192) has external calls inside a loop: a = pool.acquisitions(id) (src/SorpheraVault.sol#163)\n[low/medium] calls-loop at src/SorpheraVault.sol:161: SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#161-192) has external calls inside a loop: pool.stuckNFTRecipient(a.listingId) == address(this) (src/SorpheraVault.sol#184)\n[low/medium] reentrancy-benign at src/Sorphera.sol:237: Reentrancy in Sorphera.openRound(uint8) (src/Sorphera.sol#237-273):\n[low/medium] reentrancy-benign at src/SorpheraVault.sol:101: Reentrancy in SorpheraVault.acquire(uint256,uint256) (src/SorpheraVault.sol#101-133):\n[low/medium] reentrancy-benign at src/SorpheraVault.sol:161: Reentrancy in SorpheraVault._reconcile(uint256) (src/SorpheraVault.sol#161-192):\n[low/medium] reentrancy-benign at src/Sorphera.sol:332: Reentrancy in Sorphera.requestDraw(uint8,uint256) (src/Sorphera.sol#332-370):\n[low/medium] reentrancy-benign at src/Sorphera.sol:332: Reentrancy in Sorphera.requestDraw(uint8,uint256) (src/Sorphera.sol#332-370):\n[low/medium] timestamp at src/Sorphera.sol:275: Sorphera.buy(uint8,uint256,Sorphera.Pick[]) (src/Sorphera.sol#275-297) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:311: Sorphera.credit(uint8,uint256) (src/Sorphera.sol#311-319) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:332: Sorphera.requestDraw(uint8,uint256) (src/Sorphera.sol#332-370) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:321: Sorphera.recordNFT(uint8,uint256) (src/Sorphera.sol#321-329) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:372: Sorphera.rawFulfillRandomWords(uint256,uint256[]) (src/Sorphera.sol#372-381) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:299: Sorphera.acquisitionOpen(uint8,uint256) (src/Sorphera.sol#299-302) uses timestamp for comparisons\n[low/medium] timestamp at src/SorpheraVault.sol:101: SorpheraVault.acquire(uint256,uint256) (src/SorpheraVault.sol#101-133) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:455: Sorphera.entitlement(uint8,uint256,uint256,address) (src/Sorphera.sol#455-475) uses timestamp for comparisons\n[low/medium] timestamp at src/SorpheraVault.sol:216: SorpheraVault._sync() (src/SorpheraVault.sol#216-224) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:387: Sorphera.finalize(uint8,uint256) (src/Sorphera.sol#387-411) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:304: Sorphera.close(uint8,uint256) (src/Sorphera.sol#304-309) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:237: Sorphera.openRound(uint8) (src/Sorphera.sol#237-273) uses timestamp for comparisons\n[low/medium] timestamp at src/SorpheraRouter.sol:60: SorpheraRouter.acquire(address,uint256,uint256,uint256,uint256,uint256) (src/SorpheraRouter.sol#60-80) uses timestamp for comparisons\n[low/medium] timestamp at src/SorpheraRouter.sol:82: SorpheraRouter.claimBuilderRewards(address,address,uint256,uint256) (src/SorpheraRouter.sol#82-96) uses timestamp for comparisons\n[low/medium] timestamp at src/Sorphera.sol:441: Sorphera._distribute(uint8,uint256) (src/Sorphera.sol#441-453) uses timestamp for comparisons","passed":true},{"durationMs":314,"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:212: Reentrancy: State change after external call (24 places)\n[high] storage-array-memory-edit at src/Sorphera.sol:213: Storage Array Edited with Memory (2 places)\n[low] centralization-risk at src/Sorphera.sol:11: Centralization Risk (10 places)\n[low] costly-loop at src/Sorphera.sol:289: Costly operations inside loop (5 places)\n[low] large-numeric-literal at src/Sorphera.sol:200: Large Numeric Literal (2 places)\n[low] literal-instead-of-constant at src/Sorphera.sol:181: Literal Instead of Constant (13 places)\n[low] modifier-used-only-once at src/SorpheraVault.sol:92: Modifier Invoked Only Once\n[low] non-reentrant-not-first at src/Sorphera.sol:497: `nonReentrant` is Not the First Modifier (3 places)\n[low] require-revert-in-loop at src/Sorphera.sol:289: Loop Contains `require`/`revert` (5 places)\n[low] state-change-without-event at src/SorpheraRouter.sol:98: State Change Without Event (3 places)\n[low] unchecked-return at src/Sorphera.sol:486: Unchecked Return (5 places)\n[low] uninitialized-local-variable at src/Sorphera.sol:225: Uninitialized Local Variable (4 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"e50343191d2c718790e8a3c397829ed8dd04956f9ec9ed50617efb2c042e0106","verifiedTreeHash":"db9b163db28d0b58cc8ef4532bdd2274ebe78f1a","verifierVersion":"0.1.0+ad90ce4c"}]}